Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions litebox/src/fs/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,21 @@ pub trait Backend: private::Sealed + Send + Sync + Any {
/// Status of an open file or directory handle.
fn status(&self, h: HandleRef<'_>) -> Result<FileStatus, FileStatusError>;

/// Create a new file at `parent` with the given `name` and `mode`.
/// Create a new file at `parent` with the given `name` and metadata.
fn create_file_at(
&self,
dir: DirHandle,
name: &str,
mode: Mode,
metadata: CreationMetadata,
) -> Result<FileHandle, OpenError>;
Comment thread
jaybosamiya-ms marked this conversation as resolved.

/// Create a new directory at `parent` with the given `name` and `mode`.
fn mkdir_at(&self, dir: DirHandle, name: &str, mode: Mode) -> Result<DirHandle, MkdirError>;
/// Create a new directory at `parent` with the given `name` and metadata.
fn mkdir_at(
&self,
dir: DirHandle,
name: &str,
metadata: CreationMetadata,
) -> Result<DirHandle, MkdirError>;

/// Remove the file `name` at `parent`.
fn unlink_at(&self, dir: DirHandle, name: &str) -> Result<(), UnlinkError>;
Expand Down Expand Up @@ -334,6 +339,16 @@ pub(super) enum WalkStopReason {
Continue,
}

/// The metadata a backend stamps onto a newly created file or directory.
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct CreationMetadata {
/// Permission bits for the new node.
pub mode: Mode,
/// Owner of the new node.
pub owner: UserInfo,
}

/// A backend item plus permission metadata for resolver-side checks.
pub struct Permissioned<H> {
pub(super) item: H,
Expand Down
17 changes: 11 additions & 6 deletions litebox/src/fs/composer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use alloc::vec;
use alloc::vec::Vec;

use super::backend::{
Backend, BackendHandles, DirHandle, FileHandle, HandleRef, PermissionCheck, Permissioned,
SeekBehavior, WalkOutcome, WalkStopReason, WalkedComponent, WalkingDirHandle,
Backend, BackendHandles, CreationMetadata, DirHandle, FileHandle, HandleRef, PermissionCheck,
Permissioned, SeekBehavior, WalkOutcome, WalkStopReason, WalkedComponent, WalkingDirHandle,
};
use super::errors::{
ChmodError, ChownError, FileStatusError, MkdirError, OpenError, PathError, ReadDirError,
Expand Down Expand Up @@ -701,7 +701,7 @@ impl Backend for Composer {
&self,
dir: DirHandle,
name: &str,
mode: Mode,
metadata: CreationMetadata,
) -> Result<FileHandle, OpenError> {
let dir = dir.into_typed::<Self>();
match dir.inner {
Expand All @@ -714,7 +714,7 @@ impl Backend for Composer {
self.checked_child_path(path, name, OpenError::ReadOnlyFileSystem)?;
self.mounts[mount_index]
.backend
.create_file_at(handle, name, mode)
.create_file_at(handle, name, metadata)
.map(|handle| {
FileHandle::from_typed::<Self>(ComposerFileHandle {
mount_index,
Expand All @@ -725,7 +725,12 @@ impl Backend for Composer {
}
}

fn mkdir_at(&self, dir: DirHandle, name: &str, mode: Mode) -> Result<DirHandle, MkdirError> {
fn mkdir_at(
&self,
dir: DirHandle,
name: &str,
metadata: CreationMetadata,
) -> Result<DirHandle, MkdirError> {
let dir = dir.into_typed::<Self>();
match dir.inner {
ComposerDirHandleInner::Virtual { .. } => Err(MkdirError::ReadOnlyFileSystem),
Expand All @@ -737,7 +742,7 @@ impl Backend for Composer {
let path = self.checked_child_path(path, name, MkdirError::ReadOnlyFileSystem)?;
self.mounts[mount_index]
.backend
.mkdir_at(handle, name, mode)
.mkdir_at(handle, name, metadata)
.map(|handle| {
DirHandle::from_typed::<Self>(
ComposerDirHandleInner::Mounted {
Expand Down
13 changes: 9 additions & 4 deletions litebox/src/fs/devices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::LiteBox;
use crate::sync::RawSyncPrimitivesProvider;

use super::backend::{
Backend, BackendHandles, DirHandle, FileHandle, HandleRef, PermissionCheck, Permissioned,
SeekBehavior, WalkOutcome, WalkStopReason, WalkingDirHandle,
Backend, BackendHandles, CreationMetadata, DirHandle, FileHandle, HandleRef, PermissionCheck,
Permissioned, SeekBehavior, WalkOutcome, WalkStopReason, WalkingDirHandle,
};
use super::errors::{
ChmodError, ChownError, FileStatusError, MkdirError, OpenError, PathError, ReadDirError,
Expand Down Expand Up @@ -359,12 +359,17 @@ where
&self,
_dir: DirHandle,
_name: &str,
_mode: Mode,
_metadata: CreationMetadata,
) -> Result<FileHandle, OpenError> {
Err(OpenError::ReadOnlyFileSystem)
}

fn mkdir_at(&self, _dir: DirHandle, _name: &str, _mode: Mode) -> Result<DirHandle, MkdirError> {
fn mkdir_at(
&self,
_dir: DirHandle,
_name: &str,
_metadata: CreationMetadata,
) -> Result<DirHandle, MkdirError> {
Err(MkdirError::ReadOnlyFileSystem)
}

Expand Down
63 changes: 6 additions & 57 deletions litebox/src/fs/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ pub struct InMem<Platform: sync::RawSyncPrimitivesProvider> {
// TODO: Possibly support a single-threaded variant that doesn't have the cost of requiring a
// sync-primitives platform, as well as cost of mutexes and such?
root: DirNode<Platform>,
// TODO(jayb): This duplicates the resolver's `Context::user_info`, which is supposed to own
// this. This exists as a transition until we update callers to either manage the perm checks or
// pass down the UserInfo.
current_user: UserInfo,
inode_allocator: InodeAllocator,
}

Expand All @@ -48,10 +44,6 @@ impl<Platform: sync::RawSyncPrimitivesProvider> InMem<Platform> {
}));
Self {
root,
current_user: UserInfo {
user: 1000,
group: 1000,
},
inode_allocator,
}
}
Expand Down Expand Up @@ -487,7 +479,7 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
&self,
dir: super::backend::DirHandle,
name: &str,
mode: Mode,
metadata: super::backend::CreationMetadata,
) -> Result<super::backend::FileHandle, OpenError> {
// TODO(jayb): Nothing checks write permission on the parent directory before creating;
// the resolver should do so before calling this.
Expand All @@ -498,8 +490,8 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
}
let file = Arc::new(sync::RwLock::new(FileData {
perms: Permissions {
mode,
userinfo: self.current_user,
mode: metadata.mode,
userinfo: metadata.owner,
},
data: Vec::new().into(),
node_info: self.inode_allocator.next(),
Expand All @@ -517,7 +509,7 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
&self,
dir: super::backend::DirHandle,
name: &str,
mode: Mode,
metadata: super::backend::CreationMetadata,
) -> Result<super::backend::DirHandle, MkdirError> {
// TODO(jayb): Nothing checks write permission on the parent directory before creating;
// the resolver should do so before calling this.
Expand All @@ -528,8 +520,8 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
}
let child = Arc::new(sync::RwLock::new(DirData {
perms: Permissions {
mode,
userinfo: self.current_user,
mode: metadata.mode,
userinfo: metadata.owner,
},
children: HashMap::default(),
node_info: self.inode_allocator.next(),
Expand Down Expand Up @@ -580,8 +572,6 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
}

fn chmod(&self, h: super::backend::HandleRef<'_>, mode: Mode) -> Result<(), ChmodError> {
// TODO(jayb): This checks ownership against the backend's own `current_user`, rather than
// the resolver's context user.
let mut perms = match h {
super::backend::HandleRef::File(h) => {
sync::RwLockWriteGuard::map(h.get_typed::<Self>().file.write(), |f| &mut f.perms)
Expand All @@ -590,11 +580,6 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
sync::RwLockWriteGuard::map(h.get_typed::<Self>().dir.write(), |d| &mut d.perms)
}
};
if !(self.current_user.user == UserInfo::ROOT.user
|| self.current_user.user == perms.userinfo.user)
{
return Err(ChmodError::NotTheOwner);
}
perms.mode = mode;
Ok(())
}
Expand All @@ -605,8 +590,6 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
user: Option<u16>,
group: Option<u16>,
) -> Result<(), ChownError> {
// TODO(jayb): This checks ownership against the backend's own `current_user`, rather than
// the resolver's context user.
let mut perms = match h {
super::backend::HandleRef::File(h) => {
sync::RwLockWriteGuard::map(h.get_typed::<Self>().file.write(), |f| &mut f.perms)
Expand All @@ -615,11 +598,6 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::backend::Backend for InMe
sync::RwLockWriteGuard::map(h.get_typed::<Self>().dir.write(), |d| &mut d.perms)
}
};
if !(self.current_user.user == UserInfo::ROOT.user
|| self.current_user.user == perms.userinfo.user)
{
return Err(ChownError::NotTheOwner);
}
if let Some(new_user) = user {
perms.userinfo.user = new_user;
}
Expand Down Expand Up @@ -694,32 +672,3 @@ struct Permissions {
mode: Mode,
userinfo: UserInfo,
}

/// Run `f` with the acting user set to root.
///
/// Non-test callers set up root-owned state via [`InMem::new_initialized`] instead; this exists so
/// that the tests can exercise operations that depend on the acting user.
#[cfg(test)]
pub(super) fn with_root_privileges<Platform: sync::RawSyncPrimitivesProvider>(
fs: &mut super::resolver::Resolver<Platform, InMem<Platform>>,
f: impl FnOnce(&mut super::resolver::Resolver<Platform, InMem<Platform>>),
) {
with_user(fs, UserInfo::ROOT.user, UserInfo::ROOT.group, f);
}

/// Run `f` with the acting user set to `user`/`group`. See [`with_root_privileges`].
#[cfg(test)]
pub(super) fn with_user<Platform: sync::RawSyncPrimitivesProvider>(
fs: &mut super::resolver::Resolver<Platform, InMem<Platform>>,
user: u16,
group: u16,
f: impl FnOnce(&mut super::resolver::Resolver<Platform, InMem<Platform>>),
) {
let user = UserInfo { user, group };
let original_user = fs.swap_acting_user(user);
fs.backend_mut().current_user = user;
f(fs);
let user_again = fs.swap_acting_user(original_user);
fs.backend_mut().current_user = original_user;
assert!(user_again.user == user.user && user_again.group == user.group);
}
25 changes: 19 additions & 6 deletions litebox/src/fs/nine_p/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,23 @@ where
&self,
dir: DirHandle,
name: &str,
mode: super::Mode,
metadata: super::backend::CreationMetadata,
) -> Result<FileHandle, OpenError> {
// `Tlcreate` turns the directory fid into the new file's fid server-side, so it must be
// handed a private clone rather than the caller's directory handle.
let fid = self.client.clone_fid(&dir.get_typed::<Self>().fid.fid)?;
// NOTE: 9P needs to commit to an access mode at creation time. The resolver still enforces
// the caller's read/write intent via its own `read_allowed`/`write_allowed`.
let (_, fid) = self
.client
.create(fid, name, fcall::LOpenFlags::O_RDWR, mode.bits(), 0)?;
//
// XXX: `Tlcreate` only carries a gid; the owning uid is whichever user the connection
// attached as, so `metadata.owner.user` cannot be honored here.
let (_, fid) = self.client.create(
fid,
name,
fcall::LOpenFlags::O_RDWR,
metadata.mode.bits(),
u32::from(metadata.owner.group),
)?;
Ok(FileHandle::from_typed::<Self>(NinePFileHandle {
fid: self.own(fid),
}))
Expand All @@ -520,10 +527,16 @@ where
&self,
dir: DirHandle,
name: &str,
mode: super::Mode,
metadata: super::backend::CreationMetadata,
) -> Result<DirHandle, MkdirError> {
let dir = dir.into_typed::<Self>();
self.client.mkdir(&dir.fid.fid, name, mode.bits(), 0)?;
// XXX: as in `create_file_at`, `Tmkdir` cannot set the owning uid.
self.client.mkdir(
&dir.fid.fid,
name,
metadata.mode.bits(),
u32::from(metadata.owner.group),
)?;
// `Tmkdir` only reports the new directory's qid, so a walk is needed to address it.
//
// TODO(jayb): the resolver discards this handle, so the walk is pure overhead, and worse, a
Expand Down
Loading