507 Commits
Author SHA1 Message Date
kevin9327 81e7372f6f serve s3: report the object's modtime as Last-Modified after a PUT - fixes #10128
gofakes3 adds the time of the request as "Last-Modified" to the
metadata it passes to PutObject, CopyObject and multipart uploads.
storeMeta cached it with the rest, so HeadObject and GetObject
replaced the object's modification time with the upload time until the
cache entry was evicted, while ListObjects reported the right time.

storeMeta now drops "Last-Modified" before caching the metadata.
2026-10-20 11:17:18 +01:00
Azharandbunnysayzz 0677b0ca1e serve restic: fix temporary errors confusing restic into leaving locks behind
deleteObject() replied 404 for any error from newObject(), while
serveObject() in the same file replies 404 only for
fs.ErrorObjectNotFound and 500 otherwise.

restic treats a 404 on DELETE as final and never retries, so a
transient lookup failure while removing a lock left the lock behind
and broke the next exclusive operation (eg forget --prune failing with
"repository is already locked").

This change mirrors serveObject() handling of 404/500 errors in
deleteObject().

Fixes #10100

Co-authored-by: bunnysayzz <bunnysayzz@users.noreply.github.com>
2026-10-18 17:39:27 +01:00
Nick Craig-Wood 5fe99911ca serve: fix errcheck lint errors from golangci-lint v2.15.0
errcheck now flags ignoring an error received from a channel, and
staticcheck rejects assigning it to _, so use nolint instead.
2026-10-17 15:48:14 +01:00
Nick Craig-Wood 8a91f15bb7 build: update all dependencies
This fixes the code to compile with the updated dependencies:

- dlna: pass a *slog.Logger to the ssdp server as required by
  anacrolix/dms v1.8.0

github.com/Files-com/files-sdk-go/v3 was not updated as the latest
version requires go 1.26.9.

The tcell and go-systemd pins are kept and github.com/hanwen/go-fuse/v2
is pinned to v2.11.0 as v2.12.0 fails to compile on 32 bit linux - see
https://github.com/hanwen/go-fuse/issues/628
2026-10-17 15:48:14 +01:00
Nick Craig-Wood d3db33c6b7 build: fix lint errors after golang.org/x/net update
golang.org/x/net v0.60.0 deprecates its HTTP/2 server and transport in
favour of net/http.

Make the h2c test use http.Transport with unencrypted HTTP/2. serve
restic --stdio still needs http2.Server to serve HTTP/2 on a single
connection as http.Server has no way of doing that.
2026-10-08 23:51:15 +01:00
Nick Craig-Wood c2ce9a0bfb serve s3: apply --metadata-max-objects to each user separately
The metadata of the objects of all users was kept in one store, so with
--auth-proxy or several --auth-key credentials one user storing the
metadata of many objects could make the server forget that of every
other user.

Each user (each access key) now has a store of their own.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood b1657092d3 serve s3: apply the multipart upload limits to each user separately
--multipart-max-uploads and --multipart-streaming-buffer-total limited
the uploads of all users together, so with --auth-proxy or several
--auth-key credentials one user could use up the limits and stop every
other user uploading.

They now limit the uploads of each user (each access key) separately.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 5379bd5409 serve s3: fix multipart upload corruption if the backend write of an early part fails
A multipart part which arrives before the parts ahead of it is buffered
and acknowledged to the client, then written to the backend when its
turn comes. If that write failed the part was dropped, possibly with
some of it already written, but the upload carried on as if nothing
had happened, so it could be completed with the part missing, or with
a resend of the part appended to the partial data.

Such an upload is now failed, so the client starts it again.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood c3b582ccdf serve s3: fix presigned URL being turned into a copy of any object GHSA-w3h7-jxxq-vfrp CVE-PENDING
The SigV4 verification in gofakes3 only checked the headers the
client listed as signed and never looked at the other headers which
arrived. A presigned PUT URL signs only the host header, so a holder
of one could add an unsigned x-amz-copy-source header and have the
server copy any object it can reach - in any bucket, and in any
remote when serving a cloud backend - into the object the URL names,
then read it back with the same URL. This affected both --auth-key
and --auth-proxy. AWS refuses such a request with AccessDenied, as
it requires every x-amz-* header to be signed.

This updates gofakes3 to a version which refuses requests carrying
an x-amz-* header not in their signed headers list with AccessDenied,
and adds tests that a presigned PUT URL can't be turned into a copy
in either auth mode. Real S3 clients always sign x-amz-* headers so
they are unaffected.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood d120b5fe0b serve s3: test multipart buffer limits can't be bypassed by overflowing part sizes GHSA-qjw8-8hmp-c3m6
In v1.75 the multipart reorder buffer admitted the next part the stream
needed however big, adding its client-declared length to the running
total unchecked. Two concurrent requests for the next part declaring
huge lengths overflowed the total negative, after which out-of-order
parts were admitted past --multipart-streaming-buffer-limit.

The next part is now streamed straight to the backend without being
charged to the buffer, and a buffered part is only admitted if it fits
in what is left of the limits, so this can no longer happen. Add a
test of the attack against both the per-upload and server-wide limits.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood da71a52714 serve s3: document memory use and bring the docs up to date
Add a section on the memory serve s3 uses and how to limit it, and
update the docs for the recent changes to multipart uploads, listings
and metadata.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 221398e2d0 serve s3: fix stale metadata being returned for objects changed elsewhere
serve s3 keeps the metadata of objects uploaded through it in memory.
If the object was then changed some other way - by another --auth-proxy
user sharing the backend, another server or directly on the backend -
serve s3 went on returning the old metadata, including the old
modification time, for the new object.

The size and modification time of the object are now stored with its
metadata, which is ignored if they no longer match.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 0edd92134e serve s3: fix uploads failing when the auth proxy expires the user's backend
With --auth-proxy the backend of each user is shut down once it has
been unused for 5 minutes. A request didn't count as using it, so a
long upload or download could have its backend shut down under it and
fail. A multipart upload used the backend it started on throughout, so
failed if the client paused for more than 5 minutes between parts, or
the auth proxy changed the user's secret.

Each request and multipart upload now holds the backend it uses until
it is finished with it. In-progress multipart uploads are aborted when
the server is shut down so their backends are released.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 32b08f0237 serve s3: stop auth proxy users listing or using each other's multipart uploads GHSA-35g6-9fjv-rmx5 CVE-PENDING
With --auth-proxy each access key ID may be mapped to a different
backend, but gofakes3 kept a single record of multipart uploads for
everyone. Any user could list the uploads of every other user with
ListMultipartUploads (including their upload IDs) and the parts of
them with ListParts.

With --disable-multipart-streaming the uploads are held by gofakes3,
whose upload IDs are sequential so easy to guess. Any user could
upload parts to, abort, or complete another user's upload - which
wrote it to the backend of the user completing it.

This updates gofakes3 to a version which can keep each user's uploads
private and uses the access key ID as the owner.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood a16a901de7 serve s3: stop auth proxy users using each other's multipart uploads and metadata GHSA-35g6-9fjv-rmx5 CVE-PENDING
With --auth-proxy each access key ID may be mapped to a different
backend, but serve s3 found a streaming multipart upload by its upload
ID alone and stored object metadata by path alone.

A user who learnt another user's upload ID (for example from
ListMultipartUploads on a bucket of the same name) could upload parts
to, complete or abort that upload on the other user's backend. A user
with an object at the same bucket and key as another user was returned
the other user's metadata by HeadObject and GetObject and could
overwrite it.

Keep the uploads and metadata of each access key ID apart.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 2385e27cfd serve s3: keep the state remembered for each user in one place
Refactor the state serve s3 keeps between requests - in-flight
multipart uploads and object metadata - so that it is reached through
a tenant struct for the user making the request. This makes it
possible to keep users' state apart when they are serving different
backends.

There is only one tenant for now, so there is no change in behaviour.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 77ed639805 serve s3: add --metadata-max-objects to limit the metadata kept in memory
serve s3 keeps the metadata of the objects uploaded to it in memory,
with no limit on the number of objects, so memory use grew with every
object uploaded, and a client could use a lot by uploading many empty
objects with large metadata.

The new --metadata-max-objects (default 100000, 0 for no limit) limits
the number of objects whose metadata is kept, forgetting the metadata of
the least recently used first. As the metadata is only kept in memory
it is already lost when the server restarts; the modification time is
stored on the object itself so is never lost.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 0ce74ffaac serve s3: forget expired multipart uploads completely
When an abandoned multipart upload expired after --multipart-expiry,
gofakes3's record of it was left behind, so it stayed in memory and in
ListMultipartUploads until the server was restarted.

That record is now removed too.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood f4c1268f33 serve s3: update gofakes3 to limit the memory requests can use
This picks up these fixes to serve s3 from gofakes3:

- XML request bodies (CompleteMultipartUpload, DeleteObjects) bigger
  than 10 MiB are rejected rather than read into memory.
- DeleteObjects requests with more than 1000 keys are rejected.
- CompleteMultipartUpload checks the upload exists before reading the
  request body.
- With --disable-multipart-streaming, parts are no longer allocated at
  their declared size before the data arrives.
- Browser POST uploads hold at most 1 MiB in memory and are limited to
  5 GB.

It also brings a SlowDown error, so the SlowDown replies serve s3 sends
when a part waits too long for buffer space, or too many multipart
uploads are in progress, now have status 503 as S3 does rather than
500.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood d24568e3e6 serve s3: add --multipart-max-uploads to limit uploads in progress
There was no limit on the number of multipart uploads in progress, and
each one holds server resources until it completes or expires - once it
has data, a VFS upload and the buffers of the remote's own upload - so a
client could use unbounded memory by starting many uploads.

The new --multipart-max-uploads (default 1000, 0 for no limit) limits
the streamed multipart uploads in progress. Starting another fails with
SlowDown, which S3 clients retry, before anything is created on the
remote.

With --multipart-expiry 0 nothing ever cleans up abandoned uploads, so
they go on counting towards the limit until no new upload can be
started. A warning is logged at startup when both are set.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 7d2c16269d serve s3: start the upload to the backend on the first multipart part
Starting a multipart upload opened its VFS upload straight away, which
allocates a 100 KiB buffer (--streaming-upload-cutoff) and starts a
goroutine and a transfer, all held until the upload finished or was
expired after --multipart-expiry (default 24h). A client could make the
server hold about 100 KiB per tiny CreateMultipartUpload request.

The VFS upload is now only opened when the first part is written to it,
so an upload with no parts holds none of these.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 87d81fcb2e serve s3: add --multipart-streaming-buffer-total to limit memory across uploads
--multipart-streaming-buffer-limit only limits the memory buffered for
parts arriving out of order in each upload, so a client could use as
much memory as it liked by starting more uploads.

The new --multipart-streaming-buffer-total (default 1G, 0 for no limit)
limits the memory buffered by all uploads together. A part which would
take either over its limit waits as before. Since the part the stream
needs next is never buffered, every upload can still make progress.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 7773bb6487 serve s3: stream in-order multipart parts without buffering them
Every multipart part was buffered in memory in full before being written
to the backend, even the part the stream needed next. There is no limit
on part size, and several concurrent requests for the current part were
each admitted, so a client could make the server hold as much memory as
it sent, whatever --multipart-streaming-buffer-limit was set to.

The part the stream needs next is now written straight through to the
backend as it arrives, so only parts which arrive ahead of their turn
are buffered, and those always within the limit - a part bigger than
the limit waits until it is the next part rather than being admitted
when the buffer is empty. Further requests for the part being streamed
wait, holding no memory, and are then checked against it.

Since what reaches the backend can't be taken back, a part whose body
fails part way records how far it got, and the client's retry of the
part, which must have the same content, carries on from there. Such an
upload can't be completed without the rest of the part.

The body is read to its end, so that an error it only reports there -
as the Content-MD5 check does - fails the part rather than being
missed, and a body longer than declared is rejected. A part rejected
once it has been streamed leaves data which can be neither completed
nor replaced, so the whole upload is failed and cleaned up and the
client starts again.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 603e6238eb serve s3: time out multipart parts waiting for buffer space
A part waiting for room in the out-of-order buffer waited forever,
ignoring the request being cancelled, and a waiting part stopped its
upload being expired, so a client could hold requests, goroutines and
uploads open indefinitely.

The wait now ends when the request is cancelled, and after a minute the
part fails with SlowDown so the client retries it later.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 122b4e8df2 serve s3: don't buffer re-uploads of multipart parts already streamed
A part uploaded again after it had been streamed to the backend (a
client retrying after a timeout) was buffered in memory in full just to
compare it with the original, and the buffer limit didn't apply to it.

Such a re-upload is now hashed as it is received and compared with the
original without being buffered.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 4aa3c41aa1 serve s3: count buffered multipart parts in whole memory pages
The out-of-order multipart buffer was charged the declared length of
each part, but a buffered part occupies whole 1 MiB pool pages however
small it is. A client could therefore send up to 10000 one-byte parts
ahead of the part the stream needed and pin about 10 GiB of memory in a
single upload while --multipart-streaming-buffer-limit saw only 10 KB.

Each buffered part is now charged the whole pages it occupies.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 3739ad9c35 vfs: fix serve nfs path traversal by refusing unsafe file names GHSA-44pm-2q5p-fm63 CVE-PENDING
Dir.Create, Dir.Mkdir and Dir.Rename joined whatever name they were
given onto the directory's path. A name of "..", "." or one containing
a "/" isn't a child of the directory, and at the VFS root
path.Join("", "..") preserves the ".." so the new node's path pointed
outside the root. Backends which join the Fs root with the remote then
resolved it to a location above the directory the VFS was created on.

serve nfs was exploitable through this. The go-nfs library refuses a
name of exactly "." or ".." in MKDIR only, doesn't check the name in
CREATE, RENAME or SYMLINK, and never refuses a name containing a "/".

serve webdav was exploitable in the same way and has its own fix. The
other serve front-ends rely on their own or their protocol library's
path cleaning to keep such names out of the VFS.

Reject such names with EINVAL in the VFS itself, so that every
front-end and mount is protected regardless of what the layer above it
checks. This is the fix for serve nfs and defence in depth for the
others. The serve webdav check is kept as the front-end is the trust
boundary and gives the client a clear error. The source name of a
rename is only used to look the node up so it isn't checked.

VFS.Mkdir of the root, which previously went through Dir.Mkdir with an
empty name and added a nameless child node to the root, now makes the
root directory on the backend directly, still honouring --read-only.
serve webdav and serve ftp made directories through Dir.Mkdir rather
than VFS.Mkdir, so they are switched to VFS.Mkdir to keep "MKCOL /" and
"MKD /" working.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood c266a63ced serve webdav: fix path traversal above the served directory GHSA-44pm-2q5p-fm63 CVE-PENDING
A request path with a ".." element, such as "MKCOL /..", was passed
straight through to the VFS. At the VFS root path.Join("", "..")
preserves the "..", so a directory node with the path ".." was added
under the root and every path built beneath it started "../". Backends
which join the Fs root with the remote before encoding it - such as
webdav, ftp, sftp, http and memory - resolved that "../" away, letting
an unauthenticated client create, overwrite, copy, move and delete
objects and directories outside the directory rclone was told to serve.
Moving a directory or file from outside the served tree into it also
made it readable.

Reject any request with a "." or ".." element in the request path, or
in the Destination header of COPY and MOVE, with 400 Bad Request. The
check is made once in ServeHTTP rather than in each webdav.FileSystem
method so that the client gets a consistent error. Rejected requests
are logged at INFO level like other requests.

The webdav handler strips --baseurl from the Destination as a string
prefix, so "/base../x" reaches the FileSystem as "../x". The Destination
is therefore checked after stripping the prefix in the same way.
2026-10-08 22:49:33 +01:00
Nick Craig-Wood 25707f2dc6 lib/http: require authentication for OPTIONS requests GHSA-gj73-fh6v-92fj CVE-PENDING
Before this change the basic, htpasswd and auth proxy authentication
middlewares let every OPTIONS request through without checking
credentials so that browser CORS preflights, which can't carry
credentials, would succeed.

However in serve webdav the OPTIONS request is answered by the WebDAV
handler whose Allow header depends on whether the path is an existing
file, an existing directory or doesn't exist, so an unauthenticated
client could discover the existence and type of any path on the served
remote even with --user/--pass or --htpasswd set.

After this change he authentication middlewares no longer special-case
OPTIONS. Instead the CORS middleware, which runs before authentication
and owns the --allow-origin setting, answers genuine CORS preflights
itself with an empty 200 carrying the CORS headers only when
--allow-origin is set.

All other OPTIONS requests need credentials like any other method, so
clients which probe with OPTIONS before authenticating will get a 401
challenge and must retry with credentials.
2026-10-08 22:49:33 +01:00
kaii9 9823661bb8 serve restic: fix repositories named data - fixes #6086 2026-10-08 15:18:14 +01:00
Nick Craig-Wood 2e9c06c84d serve: fix --auth-proxy backends never being shut down after simultaneous logins
With --auth-proxy, when several logins with the same credentials
arrived together and none was in the cache yet, as when an FTP client
opens several connections at once, each one ran the auth proxy program
and took its own reference to the backend. Only one of them was
remembered, so the others were never given back and the backend was
never shut down.

Logins which arrive together now share one run of the auth proxy
program and one reference to the backend.
2026-10-08 10:36:00 +01:00
Nick Craig-Wood 0caf24af50 serve: fix --auth-proxy stopping VFS cache uploads after 5 minutes
With --auth-proxy the backend of each user is shut down once it has
been unused for 5 minutes. With --vfs-cache-mode writes or full, files
are uploaded to the backend after the transfer which wrote them has
finished, which didn't count as a use. So an upload which hadn't
finished 5 minutes after the user's last command (or disconnection for
serve sftp) was stopped, leaving the file in the VFS cache but not on
the backend.

The backend is now kept until it has no files open for write and
nothing in the VFS cache waiting to be uploaded.

This was introduced in v1.75.1 by

f425f8d46 serve: refactor VFS and proxy handling into Provider
2026-10-08 10:36:00 +01:00
Nick Craig-Wood 7e6d6fa838 serve webdav: fix transfers failing after 5 minutes with --auth-proxy
With --auth-proxy the backend of each user is shut down once it has
been unused for 5 minutes. Only the start of each request counted as a
use, so an upload or download lasting longer than that had its backend
shut down under it and failed.

Each request now holds the backend it uses until it has been served.

This was introduced in v1.75.1 by

f425f8d46 serve: refactor VFS and proxy handling into Provider
2026-10-08 10:36:00 +01:00
Nick Craig-Wood 8457da3c8a serve http: fix downloads failing after 5 minutes with --auth-proxy
With --auth-proxy the backend of each user is shut down once it has
been unused for 5 minutes. Only the start of each request counted as a
use, so a download lasting longer than that had its backend shut down
under it and failed.

Each request now holds the backend it uses until it has been served.

This was introduced in v1.75.1 by

f425f8d46 serve: refactor VFS and proxy handling into Provider
2026-10-08 10:36:00 +01:00
Nick Craig-Wood ee0a70f875 serve ftp: fix transfers failing after 5 minutes with --auth-proxy
With --auth-proxy the backend of each user is shut down once it has
been unused for 5 minutes. Only the start of each FTP command counted
as a use, so an upload or download lasting longer than that had its
backend shut down under it and failed with "context canceled".

Each FTP command now holds the backend it uses until it has finished,
which for a download is when the file is closed.

This was introduced in v1.75.1 by

f425f8d46 serve: refactor VFS and proxy handling into Provider
2026-10-08 10:36:00 +01:00
Nick Craig-Wood 89521d875d lib/http: prefix links in directory listings with ./ so selfupdate works
downloads.rclone.org is about to be served from static listings
written by rclone index instead of by Caddy's file server. Released
versions of rclone selfupdate find the latest patch release of a minor
version by searching that listing for href="./vX.Y.Z/", as written by
Caddy. rclone's listings linked to "vX.Y.Z/" without the ./ so `rclone
selfupdate --version X.Y` would have failed with "could not find the
minor release" for every rclone already installed.

Caddy prefixes every link with ./ so that a name with a colon in its
first path segment is not read as an absolute URL with a scheme (RFC
3986 section 4.2). rclone was already safe from that as url.URL.String
adds the ./ but only to names which need it, so links to plain names
had no prefix.

This prefixes all the links with ./ as Caddy does. It changes the
output of serve http, serve webdav and the rc server as well as rclone
index, since they share the code. The links resolve identically, and
it keeps the listings consistent with each other.

Now every URL has the prefix, the caddy.json template no longer needs
to add it.
2026-09-30 12:12:33 +01:00
Nick Craig-Wood 9dc8b71ae9 serve s3: fix TestEtagHashAuto on Windows
The temporary directory contains a drive letter colon so it needs
quoting in the crypt connection string, otherwise the password
parameter is dropped.
2026-09-26 14:39:07 +01:00
Harsh Raj Singhania 14a1359c96 serve webdav: escape filename in zip download Content-Disposition header
Use mime.FormatMediaType instead of unescaped string concatenation so a
directory name containing a double quote cannot inject extra disposition
parameters.

Fixes #9962
2026-09-25 17:09:33 +01:00
Harsh Raj Singhania 661484f36c serve http: escape filename in zip download Content-Disposition header
Use mime.FormatMediaType instead of unescaped string concatenation so a
directory name containing a double quote cannot inject extra disposition
parameters.

Fixes #9962
2026-09-25 17:09:33 +01:00
Nick Craig-Wood ce5351c52e serve s3: fix CopyObject of a missing object not returning NoSuchKey
Copying an object onto itself, as clients do to replace its metadata,
reported success when the object didn't exist, and copying a missing
object to a different key failed with an internal error. Both now fail
with NoSuchKey as S3 does.
2026-09-24 10:36:10 +01:00
Nick Craig-Wood 746eac73ef serve s3: fix --etag-hash auto crashing or using the wrong hash with --auth-proxy
With --etag-hash auto the hash for the ETags was chosen once from the
remote serve s3 was started with. With --auth-proxy there is no such
remote so serve s3 crashed on startup, and when started by the rc it
was the wrong remote, so users whose backends lacked that hash got no
ETags.

The hash is now chosen from the backend of the user making each
request.
2026-09-24 10:36:06 +01:00
Nick Craig-Wood c350f8ebbf serve s3: forget the metadata of deleted objects
The metadata of every object uploaded was kept in memory for as long as
the server ran, even after the object was deleted, so it grew without
limit and the metadata of a deleted object reappeared on any object
later created at the same key other than through serve s3.

Deleting an object now forgets its metadata.
2026-09-24 10:35:30 +01:00
Nick Craig-Wood 04697cc02a serve s3: list objects lazily so paging a deep hierarchy is fast - fixes #9855
Listing a prefix with no delimiter walked the entire subtree below it into
memory and only then sliced out the requested page, so every page of a
listing cost a full traversal of the tree.

Walk the tree lazily instead, stopping as soon as the page is full and
skipping the subtrees an earlier page already returned. A page now costs a
number of directory reads proportional to the keys it returns rather than to
the size of the subtree, and ETags are computed only for the objects that
are actually returned.

Entries are emitted in the order their keys have in a flat keyspace, with a
directory sorting as if it carried its trailing slash, so that "a.txt" comes
before "a/b" as it does in a real S3 bucket. Without this a resumed listing
would silently skip keys across a page boundary.
2026-09-24 10:35:30 +01:00
phatlc 556940ea44 serve dlna: log unescaped paths - fixes #7370
The request log printed the escaped URL, so non-ASCII file names showed
up as long runs of %XX escapes. Log the unescaped URL path instead, as
the other serve commands do.
2026-09-21 18:11:07 +01:00
Nick Craig-Wood 1c3e432c3f box, serve s3: fix log messages with bad format strings
The box backend logged an int64 with %q which printed
%!q(int64=123) instead of the sequence ID.

serve s3 passed the message from gofakes3 as the format string, so
any % in it was interpreted as a formatting directive and the message
was mangled.
2026-09-20 14:23:50 +01:00
Vasek Sraier d632f8bb51 serve restic: prevent concurrent append-only overwrites
- Reject uploads when object lookup fails for reasons other than "not found".
- Serialize uploads to the same object to prevent races between the
  existence check and the write.
2026-09-19 10:16:32 +01:00
phatlc 9ac29e3b35 serve docker: fix volume path being lost when the plugin restarts
applyOptions consumes the "path" option into vol.Path rather than leaving
it in vol.Options, but restoreState rebuilt the options with only fs and
type. The explicit path was therefore dropped when the plugin restarted,
and since fsString is rebuilt from those options the volume was remounted
at the root of the remote instead of at its subpath.

Before this change a volume created with type + path lost its path
completely, and one created with remote + path silently fell back to the
path of the connection string. With a backend whose credentials are
scoped to the subpath the restored mount then failed every operation
rather than serving the wrong directory.

Feed the persisted path back like fs and type, so applyOptions applies
the same precedence on restore that it applies when the volume is
first created.

Fixes #9853
2026-09-08 17:05:23 +01:00
Nick Craig-Wood 9b9fd3f493 serve ftp: fix VFS leak when the server fails to start
The deferred cleanup in the constructor checked a local error variable
rather than the error being returned, so failures after the VFS was
created (such as an invalid --passive-port) never shut it down. Name
the error return so the cleanup sees the returned error.
2026-09-08 10:28:54 +01:00
Nick Craig-Wood f2a390b2d4 serve webdav,http: fix crash when the server fails to start - fixes #9882
When the HTTP server failed to initialise, for example because the
listen address was already in use, rclone panicked with a nil pointer
dereference instead of reporting the error.

The deferred cleanup in the constructor read the provider from the
named return value, but `return nil, err` sets that to nil before the
deferred function runs. Use a local variable for the server instead.
2026-09-08 10:28:54 +01:00
Nick Craig-Wood e855d2ed36 serve docker: fix tests leaving unkillable processes and stale FUSE mounts
Writing to the mount with os.WriteFile made the Go runtime register
the file with its poller so the kernel then polled the file from
epoll_ctl and epoll_wait, sending POLL requests to the FUSE server
running in this same process. A thread waiting inside epoll cannot be
preempted by the runtime, so a garbage collection starting while such
a POLL was outstanding stopped the world for good - the test binary
could not be killed even with SIGKILL and the mount was left behind,
wedging anything that touched it.

Now we write through the mount with a descriptor straight from
open(2), which os.NewFile keeps out of the poller, check that it
really is out of the poller with SetDeadline, and check at the end of
the test that the mountpoint is unmounted.

In this commit we fixed the same problem for mount by running in a
subprocess however changing one write file routine here was much
easier than re-arranging the tests.

4a382c09ec mount: run tests in a subprocess to fix deadlock - #3259

Note that go-fuse (and hence mount2) works around this problem it by
forcing an early POLL it can answer with ENOSYS.

See: https://github.com/golang/go/issues/21014
2026-09-05 12:15:32 +01:00