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
17 changes: 9 additions & 8 deletions litebox/src/fs/in_mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::FileSystem for FileSystem
unimplemented!("{flags:?}")
}
let path = self.absolute_path(path)?;
let entry = if flags.contains(OFlags::CREAT) {
let (entry, created) = if flags.contains(OFlags::CREAT) {
let mut root = self.root.write();
let (parent, entry) = root.parent_and_entry(&path, self.current_user)?;
if let Some(entry) = entry {
if flags.contains(OFlags::EXCL) {
return Err(OpenError::AlreadyExists);
}
entry
(entry, false)
} else {
let Some((_, parent)) = parent else {
// Only `/` does not have a parent; any other scenario (e.g., missing ancestor)
Expand Down Expand Up @@ -233,26 +233,27 @@ impl<Platform: sync::RawSyncPrimitivesProvider> super::FileSystem for FileSystem
})));
let old = root.entries.insert(path, entry.clone());
assert!(old.is_none());
entry
(entry, true)
}
} else {
let root = self.root.read();
let (_, entry) = root.parent_and_entry(&path, self.current_user)?;
let Some(entry) = entry else {
return Err(PathError::NoSuchFileOrDirectory)?;
};
entry
(entry, false)
};
let read_allowed = if flags.contains(OFlags::RDONLY) || flags.contains(OFlags::RDWR) {
if !self.current_user.can_read(&entry.perms()) {
let access_mode = flags & (OFlags::WRONLY | OFlags::RDWR);
let read_allowed = if access_mode == OFlags::RDONLY || access_mode == OFlags::RDWR {
if !created && !self.current_user.can_read(&entry.perms()) {
return Err(OpenError::AccessNotAllowed);
}
true
} else {
false
};
let write_allowed = if flags.contains(OFlags::WRONLY) || flags.contains(OFlags::RDWR) {
if !self.current_user.can_write(&entry.perms()) {
let write_allowed = if access_mode == OFlags::WRONLY || access_mode == OFlags::RDWR {
if !created && !self.current_user.can_write(&entry.perms()) {
return Err(OpenError::AccessNotAllowed);
}
true
Expand Down
5 changes: 2 additions & 3 deletions litebox/src/fs/layered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -740,9 +740,8 @@ impl<
.litebox
.descriptor_table()
.with_entry(fd, |descriptor| {
if !descriptor.entry.flags.contains(OFlags::RDONLY)
&& !descriptor.entry.flags.contains(OFlags::RDWR)
{
let access_mode = descriptor.entry.flags & (OFlags::WRONLY | OFlags::RDWR);
if access_mode == OFlags::WRONLY {
Err(ReadError::NotForReading)
} else {
Ok(Arc::clone(&descriptor.entry.entry))
Expand Down
52 changes: 52 additions & 0 deletions litebox/src/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,58 @@ mod in_mem {
});
}

#[test]
fn write_only_open_does_not_require_read_permission() {
let litebox = LiteBox::new(MockPlatform::new());
let mut fs = in_mem::FileSystem::new(&litebox);
fs.with_root_privileges(|fs| {
fs.mkdir("/tmp", Mode::RWXU | Mode::RWXG | Mode::RWXO)
.expect("Failed to create /tmp");
});

let path = "/tmp/write_only";
let fd = fs
.open(path, OFlags::CREAT | OFlags::WRONLY, Mode::WUSR)
.expect("Failed to create write-only file");
fs.write(&fd, b"x", None).expect("Failed to write file");

let mut buffer = [0];
assert!(matches!(
fs.read(&fd, &mut buffer, None),
Err(crate::fs::errors::ReadError::NotForReading)
));
fs.close(&fd).expect("Failed to close file");

assert!(matches!(
fs.open(path, OFlags::RDONLY, Mode::empty()),
Err(crate::fs::errors::OpenError::AccessNotAllowed)
));
}

#[test]
fn newly_created_file_does_not_require_its_own_permissions() {
let litebox = LiteBox::new(MockPlatform::new());
let mut fs = in_mem::FileSystem::new(&litebox);
fs.with_root_privileges(|fs| {
fs.mkdir("/tmp", Mode::RWXU | Mode::RWXG | Mode::RWXO)
.expect("Failed to create /tmp");
});

let path = "/tmp/zero_mode";
let fd = fs
.open(path, OFlags::CREAT | OFlags::WRONLY, Mode::empty())
.expect("Failed to create zero-mode file");
fs.write(&fd, b"x", None).expect("Failed to write file");
fs.close(&fd).expect("Failed to close file");

let status = fs.file_status(path).expect("Failed to stat file");
assert_eq!(status.mode, Mode::empty());
assert!(matches!(
fs.open(path, OFlags::WRONLY, Mode::empty()),
Err(crate::fs::errors::OpenError::AccessNotAllowed)
));
}

#[test]
fn root_directory_creation_and_removal() {
let litebox = LiteBox::new(MockPlatform::new());
Expand Down
14 changes: 14 additions & 0 deletions litebox_common_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ bitflags::bitflags! {
}

#[repr(u32)]
#[derive(IntEnum)]
pub enum InodeType {
/// FIFO (named pipe)
NamedPipe = 0o010000,
Expand Down Expand Up @@ -2136,6 +2137,12 @@ pub enum SyscallRequest<Platform: litebox::platform::RawPointerProvider> {
fd: i32,
length: usize,
},
Mknodat {
dirfd: i32,
pathname: Platform::RawConstPointer<i8>,
mode_and_type: u32,
dev: u32,
},
Unlinkat {
dirfd: i32,
pathname: Platform::RawConstPointer<i8>,
Expand Down Expand Up @@ -2723,6 +2730,13 @@ impl<Platform: litebox::platform::RawPointerProvider> SyscallRequest<Platform> {
mode: ctx.sys_req_arg(2),
}
}
Sysno::mknodat => sys_req!(Mknodat { dirfd,pathname:*,mode_and_type,dev }),
Sysno::mknod => SyscallRequest::Mknodat {
dirfd: AT_FDCWD,
pathname: ctx.sys_req_ptr(0),
mode_and_type: ctx.sys_req_arg(1),
dev: ctx.sys_req_arg(2),
},
Sysno::unlinkat => sys_req!(Unlinkat { dirfd,pathname:*,flags }),
Sysno::unlink => {
// unlink is equivalent to unlinkat with dirfd AT_FDCWD and flags 0
Expand Down
8 changes: 8 additions & 0 deletions litebox_shim_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,14 @@ impl<FS: ShimFS> Task<FS> {
syscall!(sys_openat(dirfd, path, flags, mode))
}),
SyscallRequest::Ftruncate { fd, length } => syscall!(sys_ftruncate(fd, length)),
SyscallRequest::Mknodat {
dirfd,
pathname,
mode_and_type,
dev,
} => pathname.to_cstring().map_or(Err(Errno::EFAULT), |path| {
syscall!(sys_mknodat(dirfd, path, mode_and_type, dev))
}),
SyscallRequest::Unlinkat {
dirfd,
pathname,
Expand Down
Loading
Loading