A virtual folder mapping can now re-root the mount onto a sub-path of
the folder: the mount at the virtual path serves the folder starting
from the configured subpath, which stays invisible in the virtual tree.
The same folder can be mapped multiple times with distinct subpaths;
quota remains a property of the whole folder, so every mapping must use
the same limits.
Supported for local, encrypted, S3, GCS, Azure Blob and SFTP folders.
Fixes#2037
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Apply the fixes suggested by the modernize analyzers enabled by
default in gopls v0.23: range over int, errors.AsType, min/max,
slices.Backward/Contains, maps.Copy, strings.Cut/CutPrefix/CutSuffix/
SplitSeq, fmt.Appendf, WaitGroup.Go and b.Loop in tests, new(expr)
instead of to.Ptr, removal of ineffective omitempty tags on nested
struct fields.
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
The previous commit fixed the test cases failing on Windows, but which
cleanup loses the race is not deterministic: a client disconnection is
processed after the client returns, so removing the home dir before
deleting the user fails if the connection still holds the fs root when
os.RemoveAll runs. TestResume passed in one CI run and failed in the
next one on the same unchanged code.
Swap the order in all the remaining cleanups: deleting the user first
disconnects it and httpdtest.RemoveUser waits for its connections to be
removed, so the fs roots are released before the home dir removal.
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Confine all local filesystem access to an os.Root, with path resoultion
enforced by the underlying OS.
Behavior changes an upgrading deployment may notice:
- temp_path is removed and ignored if still configured. Its purpose
was to stage files (atomic uploads, scratch and streaming data)
outside the user's storage and rename them into place, and that is
fundamentally incompatible with the confinement: an os.Root cannot
rename a file in from outside the root, so every staged upload would
degrade to a full copy-then-delete even when temp_path is on the
same filesystem, defeating the very atomicity the option existed to
provide. It was not a great idea before this change either: a
temp_path on a different device already turned the final rename into
a silent full copy. Staged and scratch files (the atomic upload
temporary, the CryptFs download pipe backing file) now live in the
storage root itself, on the same filesystem as the data, so the
final rename stays atomic.
- Cross-folder / cross-root renames are no longer atomic. A rename
spanning the home and a local virtual folder, or two local folders,
falls back to copy-then-delete: slower for large data even on the
same device, and a midway failure can leave a partial tree at the
destination. Symlinks and other non-regular entries inside a moved
directory are skipped, not copied, and the rename still reports
success. File mode bits are not preserved; regular files keep their
modification time, directories do not.
- Absolute symlinks created outside SFTPGo, or by versions predating
this change, are no longer traversable and Readlink rejects them with
permission denied, even when the target is inside the root: os.Root
refuses an absolute link target regardless of where it points.
Symlinks created through SFTPGo are stored relative to the link
directory and keep working.
- Execute-only home directories (e.g. mode 0111) are unusable:
os.OpenRoot requires read permission, so every operation fails with
permission denied until the directory is made readable, where
previously only the listing failed. Since the root open is retried on
demand, fixing the permissions (or creating a missing home
externally) makes the same connection work again without
reconnecting.
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
The CryptFs passphrase and the S3 SSE-C key are used directly as
data-at-rest encryption key material, yet they were accepted with no
strength check, so an operator could protect encrypted data with a
trivially guessable secret.
Add common.secret_min_entropy (default 80): when one of these
secrets is submitted in plain text it must pass a minimum-entropy
check before it is encrypted and stored. The check runs on
plain-text input, so secrets that are already stored keep working even
if the threshold is later raised. The estimate is a heuristic used to
reject obviously weak input, not a measure of cryptographic strength:
always supply key material generated by a CSPRNG.
Thanks to Vishal Shukla (@shukla304) for the report.
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>