pub struct ConfinedDir { /* private fields */ }Expand description
A held O_DIRECTORY handle beneath a trusted root. Every operation is
RELATIVE to this handle (openat/mkdirat/unlinkat), each component
is opened O_NOFOLLOW, and no path is ever re-resolved after a check —
the check IS the open, so ancestor-symlink plants and concurrent
component swaps fail closed (ELOOP/ENOTDIR) instead of escaping.
On Linux, multi-component descents additionally try openat2 with
RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS for kernel-side atomic
resolution, falling back to the component-by-component walk when the
syscall is unavailable (ENOSYS). Unix-only by design: confined export
fails closed on other platforms.
Implementations§
Source§impl ConfinedDir
impl ConfinedDir
Sourcepub fn create_root(path: &Path) -> Result<ConfinedDir, Error>
pub fn create_root(path: &Path) -> Result<ConfinedDir, Error>
Open the TRUSTED export root itself, creating it 0700 if missing.
The final component must not be a symlink (O_NOFOLLOW); the
root’s own ancestors are the caller’s trusted input.
Sourcepub fn open_root(path: &Path) -> Result<ConfinedDir, Error>
pub fn open_root(path: &Path) -> Result<ConfinedDir, Error>
Open an EXISTING trusted root without creating it.
Sourcepub fn open_absolute_no_symlinks(path: &Path) -> Result<ConfinedDir, Error>
pub fn open_absolute_no_symlinks(path: &Path) -> Result<ConfinedDir, Error>
Open an EXISTING absolute directory with EVERY component — ancestors
AND the final one — resolved handle-relatively and symlink-refusing
(fix2). Unlike Self::open_root, whose pathname open lets the
kernel follow symlinked ANCESTORS, this walks from / (which cannot
be a symlink): Linux tries one atomic openat2 with
RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS from the root handle, and
the fallback opens each component O_NOFOLLOW | O_DIRECTORY. There
is no check-then-open race — a component swapped to a symlink at any
point fails the open itself (ELOOP/ENOTDIR).
TRUSTED-ROOT SEMANTICS: nothing on the path is trusted; every
component must be a real directory. Operators whose base dir
legitimately sits behind ancestor symlinks (e.g. /home →
var/home) must point SYNAPS_BASE_DIR at the canonical path.
Sourcepub fn create_absolute_no_symlinks(path: &Path) -> Result<ConfinedDir, Error>
pub fn create_absolute_no_symlinks(path: &Path) -> Result<ConfinedDir, Error>
Like Self::open_absolute_no_symlinks, but creates missing
directory components (mode 0700) during the walk and repairs the
LEAF to 0700 via fchmod on the opened handle — the handle-relative
counterpart of ensure_private_dir. Every existing component,
ancestor or final, must be a real (non-symlink) directory.
Sourcepub fn create_absolute_no_symlinks_durable(
path: &Path,
) -> Result<ConfinedDir, Error>
pub fn create_absolute_no_symlinks_durable( path: &Path, ) -> Result<ConfinedDir, Error>
Durable variant: sync each parent handle so newly created directory entries (including on retry after a failed sync) survive a crash. The caller must still sync the leaf after publishing files in it.
Sourcepub fn sync_all(&self) -> Result<(), Error>
pub fn sync_all(&self) -> Result<(), Error>
Sync this opened directory, without re-resolving its pathname. An atomic file rename is not a durable commit until this succeeds.
Sourcepub fn child_dir(&self, name: &str) -> Result<ConfinedDir, Error>
pub fn child_dir(&self, name: &str) -> Result<ConfinedDir, Error>
Open-or-create one DIRECT child directory (validated single component), 0700, refusing symlinks even when swapped in between operations.
Sourcepub fn create_dirs(&self, components: &[String]) -> Result<ConfinedDir, Error>
pub fn create_dirs(&self, components: &[String]) -> Result<ConfinedDir, Error>
Descend through validated relative directory components, creating
each as needed (0700). Component-by-component O_NOFOLLOW opens.
Sourcepub fn open_dirs(&self, components: &[String]) -> Result<ConfinedDir, Error>
pub fn open_dirs(&self, components: &[String]) -> Result<ConfinedDir, Error>
Descend through EXISTING validated relative directory components
without creating anything. Tries Linux openat2 with
RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS for the whole descent;
falls back to the component walk when unavailable.
Sourcepub fn open_file(&self, components: &[String]) -> Result<File, Error>
pub fn open_file(&self, components: &[String]) -> Result<File, Error>
Open a descendant FILE for reading via handle-relative resolution
(CP-13 fix3): Linux tries openat2 with RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS over the whole validated relative path, falling
back to the component O_NOFOLLOW directory walk plus a final
openat(O_RDONLY|O_NOFOLLOW). The opened handle must be a regular
file by handle metadata. No full-path re-open ever happens — an
ancestor swapped to a symlink after discovery is refused here.
Sourcepub fn create_file(&self, name: &str) -> Result<File, Error>
pub fn create_file(&self, name: &str) -> Result<File, Error>
Create a file (validated single component) O_CREAT|O_EXCL| O_NOFOLLOW 0600 relative to this handle. An existing entry —
including any symlink, even one swapped in concurrently — fails.
Sourcepub fn write_atomic(&self, name: &str, data: &[u8]) -> Result<(), Error>
pub fn write_atomic(&self, name: &str, data: &[u8]) -> Result<(), Error>
Unlink a file (validated single component) relative to this handle.
Handle-relative atomic private write (fix2): <name>.tmp is created
O_CREAT|O_EXCL|O_NOFOLLOW 0600 relative to THIS handle, written,
fsynced, and renameated over <name> — renameat never follows
the target, so a planted symlink is replaced as a link, never
written through. A pre-existing symlink at the target is refused
with a typed error (policy parity with write_atomic_private);
one planted after the check merely gets atomically replaced.
Sourcepub fn append_file(&self, name: &str) -> Result<File, Error>
pub fn append_file(&self, name: &str) -> Result<File, Error>
Handle-relative private append (fix2): open <name> for appending
relative to THIS handle with O_NOFOLLOW, creating it 0600; a
pre-existing broader-mode file is repaired via the opened handle —
parity with open_private_append, minus the pathname resolution.
pub fn remove_file(&self, name: &str) -> Result<(), Error>
Source§impl ConfinedDir
impl ConfinedDir
Sourcepub fn entries(&self) -> Result<Vec<ConfinedEntry>, Error>
pub fn entries(&self) -> Result<Vec<ConfinedEntry>, Error>
Enumerate direct children via fdopendir(dup(dirfd)) + readdir and
classify each with handle-relative fstatat(AT_SYMLINK_NOFOLLOW).
Symlinks are returned as neither file nor directory. Names that are
non-UTF8 or invalid confined components are omitted.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for ConfinedDir
impl RefUnwindSafe for ConfinedDir
impl Send for ConfinedDir
impl Sync for ConfinedDir
impl Unpin for ConfinedDir
impl UnsafeUnpin for ConfinedDir
impl UnwindSafe for ConfinedDir
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more