pub struct FsRoot { /* private fields */ }Expand description
What the filesystem API may touch.
Held by value in the app state; every filesystem path in the API is produced by one of these methods and by no other route.
Implementations§
Source§impl FsRoot
impl FsRoot
Sourcepub fn new(root: impl AsRef<Path>) -> Result<Self>
pub fn new(root: impl AsRef<Path>) -> Result<Self>
Anchor a jail at root, which must already exist.
Canonicalised once here so every later comparison is against a path with symlinks already resolved — otherwise a symlinked root would make every containment check compare unlike things.
Sourcepub fn machine_wide() -> Self
pub fn machine_wide() -> Self
Reach everything this account can, with no subtree restriction.
The default when --fs-root is not given. Anchors are enumerated once,
here, so a drive that appears later is not silently reachable by a
server that started before it existed.
Sourcepub fn jail_path(&self) -> Option<&Path>
pub fn jail_path(&self) -> Option<&Path>
The jail’s own path, or None when the scope is the whole machine.
Returns an Option rather than a bare Path because machine-wide scope
genuinely has no single path: on Windows there is nothing above C:\
and D:\ to name. A caller that needs one — the audit-log containment
check at startup, say — has to say what it does when there isn’t one.
Sourcepub fn describe(&self) -> String
pub fn describe(&self) -> String
One line naming the effective scope, for the startup banner.
The banner is the only thing standing between an operator and a scope wider than they assumed, now that the file API no longer needs a flag to exist — so this states what is reachable, not which flag was passed.
Sourcepub fn resolve_existing(&self, rel: &str) -> Result<PathBuf, FsError>
pub fn resolve_existing(&self, rel: &str) -> Result<PathBuf, FsError>
Resolve a path that must already exist.
Containment is decided by canonicalising the deepest part of the path that exists, never by the kind of error a full canonicalisation returned. Branching on the error kind is what leaks: a path whose parent is a file fails with ENOTDIR while a path whose parent is absent fails with NotFound, so answering differently tells the caller which files exist outside the jail. It also mishandles a symlink that points out of the root — the link resolves, the target does not exist, and a lexical check sees a path that never left.
Walking down instead means every real directory on the way is resolved
through its symlinks and checked, and the verdict never depends on an
errno. resolve_for_create uses the same discipline.
Sourcepub fn resolve_for_create(&self, rel: &str) -> Result<PathBuf, FsError>
pub fn resolve_for_create(&self, rel: &str) -> Result<PathBuf, FsError>
Resolve a path that does not exist yet (an upload target).
The target itself cannot be canonicalised, so the nearest existing
ancestor is canonicalised instead and the remaining segments are checked
lexically. Those segments may not contain ..: with nothing on disk to
resolve against, a traversal there would go unnoticed until the write.
Sourcepub fn relative(&self, abs: &Path) -> Option<String>
pub fn relative(&self, abs: &Path) -> Option<String>
Render an absolute path as the string the API names it by.
Inside a jail that is a root-relative POSIX string. Machine-wide it is
the absolute path itself, with \ normalised to / so one separator
style comes back regardless of which one went in — the value is echoed
in responses, used as the list cursor, and keyed on to detect two
uploads racing for one destination, so it has to be stable per file.
Returns None for anything outside the scope, so a caller cannot
accidentally publish a path it should not have.