Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WebDAV: dav://

rcp copy /data davs://cloud.example/remote.php/dav/files/me/backup speaks WebDAV (RFC 4918), the protocol behind Nextcloud, ownCloud, SharePoint, Apache mod_dav and most “map a network drive over HTTPS” appliances: PROPFIND with Depth: 1 walks one collection at a time (never infinity, which servers commonly refuse and which is unbounded in memory), GET with Range reads, PUT writes, MKCOL, DELETE and MOVE for rename. There is no hard link: COPY writes a second copy of the bytes, which is not a second name for one file, so hard_link is refused as unsupported and the transfer layer copies – the same bytes that COPY would have written, with the summary now saying copied rather than linked. The same is true of s3://, oci://, hdfs:// and sftp://. dav:// is plain HTTP and davs:// needs the rustls build. Credentials go in -dav.user USER:PASSWORD or RCP_DAV_USER with RCP_DAV_PASSWORD; a URL carrying user:password@ is refused by name rather than quietly stripped, because the endpoint lands in the job log, in every report and in ps. The refusal does not repeat the URL: quoting the endpoint back would put the password in the job log on the one path that exists to keep it out. The same holds for the http:// endpoint, for -s3.endpoint, and for rcp show, which takes an NFS host and would otherwise hand a mistyped URL to the resolver with the password inside the error. All four refuse without repeating the token.

Three things the protocol cannot hold, each refused by name rather than dropped: symlinks (there is no representation for one; -type f leaves them out), hard links, and any of the modes, owners and times this crate would set. That last one has a consequence for sync that is worth knowing: a server’s getlastmodified is when it wrote the file, and the property is read-only, so the destination cannot carry the source’s mtime. The planner’s “does the destination still hold this file” check used to compare size AND mtime, and read every file on a WebDAV destination as changed – measured as two files crossing for one edit before keeps_times existed. A destination that says it cannot keep times is now matched on size, which is rsync’s --size-only and the honest best such a store allows; the catalog still carries the source mtime, so a changed source is still noticed. What that cannot detect is a file edited in place on the server to the same length.

verify asks the same question, so the two commands do not contradict each other on exactly the stores keeps_times exists for. The flag’s other consumers are the relay’s HELLO, the AnyTree delegation, the planner and sync -dryrun -target; a verify that ignored it would see a correct, complete copy to a WebDAV destination held done by the planner and report an mtime difference on EVERY file. The comparison is skipped when the destination keeps its own times, and said out loud in a warning rather than dropped quietly – a check that silently stops running is how a suite goes green without measuring anything. The modes are still compared, so verify against a WebDAV destination still reports what the server cannot hold; -noattrs is the comparison this transport can make without any of that.

The XML reader is deliberately not a general one: it reads a 207 Multistatus and nothing else and refuses a DOCTYPE outright rather than defending against entity expansion – each stated in src/dav/xml.rs with the reason it is safe to omit here. It DOES resolve namespaces. It is tempting to strip prefixes and compare local names, on the reasoning that every server binds D: to DAV:, but that reasoning is about servers while the reader is fed by whatever answers on the port: an href in another namespace placed after the real one would be read as the entry’s path. Only DAV: elements are read, whatever their prefix, and a foreign element takes its subtree with it.

One consequence of the size-only match is worth its own sentence: isync – Mode::Mirror, no catalog – to a WebDAV destination skips a source file edited in place to the same length, because without a catalog there is no recorded source mtime to compare against and the destination cannot hold one. sync -id sees the edit through the catalog; isync does not. verify -noattrs after an isync is the check. S3 is the neighboring case: a bucket this crate wrote carries the source mtime in object metadata and keeps_times is honestly true, but a bucket written by another tool carries only the store’s Last-Modified, and against one of those isync recopies every file whose length matches and whose date does not. The contract suite runs the client against an in-process stand-in (src/dav/testserver.rs) that is deliberately awkward in the two ways real servers are: its listings include the collection’s own entry, in either position, and every entry carries a second propstat answering 404 for a property it lacks. An adapter that only meets a tidy server is one that has not been tested.

That was measured the hard way. Against Apache 2.4 with mod_dav, every run failed at open with HTTP 301, and the stand-in had never shown it. A collection named without its trailing slash, as an endpoint URL is written, gets a 301 from Apache to the same path with the slash. The adapter refuses redirects, and the stand-in answered 207 to both forms. Now the adapter follows exactly that redirect, when Location names the same resource on the same origin with a slash added, and refuses every other. The stand-in answers the way Apache does. Against Apache 2.4 httpd:2.4 (Debian), a tree of 10 files and 22 MB copies up and back down, including names with spaces, +, %, non-ASCII characters and a 200-character name. verify reports 0 differences, and diff -r against the server’s disk is clean.