Skip to main content

ConfinedDir

Struct ConfinedDir 

Source
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

Source

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.

Source

pub fn open_root(path: &Path) -> Result<ConfinedDir, Error>

Open an EXISTING trusted root without creating it.

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.

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.

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn remove_file(&self, name: &str) -> Result<(), Error>

Source§

impl ConfinedDir

Source

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§

Source§

impl Debug for ConfinedDir

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert 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>

Convert 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)

Convert &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)

Convert &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
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ThreadSafetyMarker for T
where T: Send,

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more