Skip to content
Draft
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
63 changes: 40 additions & 23 deletions litebox_shim_linux/src/syscalls/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1192,25 +1192,22 @@ impl<FS: ShimFS> Task<FS> {
.flatten()
}
FcntlArg::DUPFD { cloexec, min_fd } => {
let new_file = self.do_dup(
desc,
if cloexec {
OFlags::CLOEXEC
} else {
OFlags::empty()
},
)?;
let max_fd = self
.process()
.limits
.get_rlimit_cur(litebox_common_linux::RlimitResource::NOFILE);
if min_fd as usize >= max_fd {
return Err(Errno::EINVAL);
}
if new_file < min_fd as usize || new_file > max_fd {
self.do_close(new_file)?;
return Err(Errno::EMFILE);
}
let new_file = self.do_dup_inner(
desc,
if cloexec {
OFlags::CLOEXEC
} else {
OFlags::empty()
},
DupFdRequest::LowestAtOrAbove(min_fd as usize),
)?;
Ok(new_file.try_into().unwrap())
}
_ => unimplemented!(),
Expand Down Expand Up @@ -1916,21 +1913,21 @@ impl<FS: ShimFS> Task<FS> {
}

fn do_dup(&self, file: usize, flags: OFlags) -> Result<usize, Errno> {
self.do_dup_inner(file, flags, None)
self.do_dup_inner(file, flags, DupFdRequest::LowestAvailable)
}

fn do_dup_inner(
&self,
file: usize,
flags: OFlags,
target: Option<usize>,
target: DupFdRequest,
) -> Result<usize, Errno> {
fn dup<FS: ShimFS, S: FdEnabledSubsystem>(
global: &GlobalState<FS>,
files: &FilesState<FS>,
fd: &TypedFd<S>,
close_on_exec: bool,
target: Option<usize>,
target: DupFdRequest,
) -> Result<usize, Errno> {
let mut dt = global.litebox.descriptor_table_mut();
let fd: TypedFd<_> = dt.duplicate(fd).ok_or(Errno::EBADF)?;
Expand All @@ -1939,13 +1936,23 @@ impl<FS: ShimFS> Task<FS> {
assert!(old.is_none());
}
let mut rds = files.raw_descriptor_store.write();
if let Some(target) = target {
if !rds.fd_into_specific_raw_integer(fd, target) {
return Err(Errno::EBADF);
match target {
DupFdRequest::Exact(target) => {
if !rds.fd_into_specific_raw_integer(fd, target) {
return Err(Errno::EBADF);
}
Ok(target)
}
DupFdRequest::LowestAvailable => Ok(rds.fd_into_raw_integer(fd)),
DupFdRequest::LowestAtOrAbove(min_fd) => {
#[allow(clippy::maybe_infinite_iter)]
let raw_fd = (min_fd..)
.find(|&raw_fd| !rds.is_alive(raw_fd))
.expect("raw fd search should always find a slot");
let success = rds.fd_into_specific_raw_integer(fd, raw_fd);
assert!(success);
Ok(raw_fd)
}
Ok(target)
} else {
Ok(rds.fd_into_raw_integer(fd))
}
}
let close_on_exec = flags.contains(OFlags::CLOEXEC);
Expand All @@ -1959,7 +1966,10 @@ impl<FS: ShimFS> Task<FS> {
|fd| dup(&self.global, &files, fd, close_on_exec, target),
|fd| dup(&self.global, &files, fd, close_on_exec, target),
)??;
if target.is_none() {
if matches!(
target,
DupFdRequest::LowestAvailable | DupFdRequest::LowestAtOrAbove(_)
) {
let max_fd = self
.process()
.limits
Expand Down Expand Up @@ -2018,7 +2028,7 @@ impl<FS: ShimFS> Task<FS> {
self.do_dup_inner(
oldfd_usize,
flags.unwrap_or(OFlags::empty()),
Some(newfd_usize),
DupFdRequest::Exact(newfd_usize),
)?;
Ok(newfd)
} else {
Expand All @@ -2029,6 +2039,13 @@ impl<FS: ShimFS> Task<FS> {
}
}

#[derive(Clone, Copy)]
enum DupFdRequest {
LowestAvailable,
LowestAtOrAbove(usize),
Exact(usize),
}

#[derive(Clone, Copy, Debug, Default)]
struct Diroff(usize);

Expand Down
24 changes: 24 additions & 0 deletions litebox_shim_linux/src/syscalls/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,30 @@ fn test_dup() {
assert_eq!(fd2 + 10, fd4);
}

#[test]
fn test_fcntl_dupfd_respects_min_fd() {
let task = init_platform(None);

let fd = task
.sys_open("/dev/stdin", OFlags::RDONLY, Mode::empty())
.unwrap();
let fd = i32::try_from(fd).unwrap();

let min_fd = fd + 10;
let duplicated = task
.sys_fcntl(
fd,
FcntlArg::DUPFD {
cloexec: false,
min_fd: u32::try_from(min_fd).unwrap(),
},
)
.unwrap();
let duplicated = i32::try_from(duplicated).unwrap();

assert_eq!(duplicated, min_fd);
}

// Note the test was generated by copilot with minor fixes.
#[test]
fn test_getdent64() {
Expand Down
Loading