Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions DAV-requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ Renaming files, creating directories, removing files/directories
## For writing files.

Writing files is a delicate operation, we should take care to do it
correctly. Right now, the driver checks if we're talking to an Apache
or SabreDAV implementation because they are the only ones that implement
partial put.
correctly. The driver checks if we're talking to an Apache or SabreDAV
implementation because they are the only ones that implement partial updates.

- If-Match: * / If-None-Match: * support (RFC2616).
If-Match: * is used with the PUT method to prevent files being written
Expand All @@ -36,14 +35,20 @@ partial put.
if it already exists.
Though this is very basic, there are servers that do not implement this,
or not correctly.
- Partial PUT support.
- Partial PUT support (preferred).
This means writing just a part of a file, updating it in-place, instead
of replacing an existing file. webdavFS detects what webserver it is
talking to. If it's Apache it uses PUT + Content-Range, if it's
of replacing an existing file. webdavfs detects what webserver it is
talking to. If it's Apache it uses PUT + Content-Range, if it's
SabreDAV it uses PATCH + X-Update-Range. For more info, see:
https://blog.sphere.chronosempire.org.uk/2012/11/21/webdav-and-the-http-patch-nightmare
http://sabre.io/dav/http-patch/

- Fallback: full PUT on close.
If partial PUT is not available, webdavfs supports read–write by using a
per‑open in‑RAM buffer. The full file is uploaded with a normal PUT on
fsync/close or after a short inactivity period. Servers only need to support
standard `PUT`, `GET`, and conditional `If-Match`/`If-None-Match`.

## Partial PUT support as a standard

There seems to be some movement in this space. RFC9110 mentions
Expand Down
45 changes: 28 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

## A FUSE filesystem for WEBDAV shares.

Most filesystem drivers for Webdav shares act somewhat like a mirror;
if a file is read it's first downloaded then cached in its entirety
on a local drive, then read from there. Writing files is similar or
even worse- a partial update to a file might involve downloading it first,
modifying it, then uploading it again. In many cases that is not optimal.
Most filesystem drivers for WebDAV shares mirror files locally and operate on
on-disk caches. Partial updates often involve downloading the full file,
modifying it, then uploading it again.

This filesystem driver behaves like a network filesystem. It doesn't
cache anything locally, it just sends out partial reads/writes over the
network.
webdavfs behaves like a network filesystem and supports efficient partial I/O
when the server allows it. It also has a robust RAM-backed mode for servers
without partial write support.

For that to work, you need partial write support- and unfortunately,
there is no standard for that. See
Expand All @@ -22,13 +20,11 @@ used by e.g. NextCloud) for partial writes. So we detect if it's Apache or
SabreDav we're talking to and then use their specific methods to partially
update files.

If no support for partial writes is detected, mount.webdavfs will
print a warning and mount the filesystem read-only. In that case you can
also use the `rwdirops` mount option, this will make metadata writable
(i.e. you can use rm / mv / mkdir / rmdir) but you still won't be able
to write to files.

But if you only need to read files it's still way faster than davfs2 :)
If no support for partial writes is detected, webdavfs still allows full
read–write operation by buffering file content in RAM per open handle, and
flushing the entire file with a single PUT on close (or fsync). There is also
an inactivity auto‑flush (default 10s) to push changes even if the application
keeps the file open.

## What is working

Expand All @@ -38,6 +34,10 @@ Basic filesystem operations.
- directories: mkdir rmdir readdir
- query filesystem size (df / vfsstat)

Small files (by default ≤64 MiB) use a per‑open in‑RAM cache for reads/writes,
and upload on close/fsync/auto‑flush. Large files use ranged I/O when the
server supports partial updates.

## What is not yet working

- locking
Expand All @@ -47,7 +47,9 @@ Basic filesystem operations.
- change permissions (all files are 644, all dirs are 755)
- change user/group
- devices / fifos / chardev / blockdev etc
- truncate(2) / ftruncate(2) for lengths between 1 .. currentfilesize - 1
- truncate(2) / ftruncate(2) shrinking a file while using ranged I/O.
Shrinking is supported for handles using the RAM cache (small files or
when the server lacks partial write support).

This is basically because these are mostly just missing properties
from webdav.
Expand Down Expand Up @@ -96,6 +98,7 @@ Using it is simple as:
```
# mount -t webdavfs -ousername=you,password=pass https://webdav.where.ever/subdir /mnt
```
On exit (Ctrl+C or SIGTERM), webdavfs unmounts the mountpoint automatically.

## Command line options

Expand Down Expand Up @@ -133,6 +136,15 @@ Using it is simple as:
| maxidleconns | Maximum number of idle connections (default 8)
| sabredav_partialupdate | Use the sabredav partialupdate protocol even when
| | the remote server doesn't advertise support (DANGEROUS)
| cache_threshold | Size in bytes above which files use non‑cached ranged
| | I/O (if available). Default 67108864 (64 MiB).
| cache_threshold_mb | Same as cache_threshold but specified in MiB. |

Notes:
- If the server does not support partial writes, all files use the in‑RAM
per‑open cache regardless of size.
- The in‑RAM cache auto‑flushes after 10 seconds of inactivity per open handle,
or on fsync/close.

If the webdavfs program is called via `mount -t webdavfs` or as `mount.webdav`,
it will fork, re-exec and run in the background. In that case it will remove
Expand Down Expand Up @@ -168,4 +180,3 @@ experience and better performance, here are a few ideas:
- DELETE Depth 0 for collections (no delete if non-empty)
- return updated PROPSTAT information after operations
like PUT / DELETE / MKCOL / MOVE

3 changes: 1 addition & 2 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ func (nd *Node) statInfoFresh() bool {
return nd.LastStat.Add(statCacheTime).After(now)
}

func (nd* Node) statInfoTouch() {
func (nd *Node) statInfoTouch() {
nd.LastStat = time.Now()
}

8 changes: 3 additions & 5 deletions daemon.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package main

import (
Expand Down Expand Up @@ -83,8 +82,8 @@ func Daemonize() error {

// now re-exec ourselves.
attrs := os.ProcAttr{
Files: []*os.File{ devnull, wout, werr },
Sys: &syscall.SysProcAttr{ Setsid: true },
Files: []*os.File{devnull, wout, werr},
Sys: &syscall.SysProcAttr{Setsid: true},
}
os.Setenv(isDaemonEnv, "YES")
proc, err := os.StartProcess(binary, os.Args, &attrs)
Expand Down Expand Up @@ -126,7 +125,7 @@ func Daemonize() error {
}

// Returns true when this process is a daemonized child.
func IsDaemon() (bool) {
func IsDaemon() bool {
return os.Getenv(isDaemonEnv) != ""
}

Expand All @@ -141,4 +140,3 @@ func Detach() error {
fh.Close()
return nil
}

5 changes: 2 additions & 3 deletions debug.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

package main

import (
Expand All @@ -8,7 +7,8 @@ import (
)

var dbgChan = make(chan string, 8)
func init () {

func init() {
go func() {
for {
line := <-dbgChan
Expand All @@ -28,4 +28,3 @@ func dbgJson(obj interface{}) string {
}
return fmt.Sprintf("%+v", obj)
}

Loading