diff --git a/src/libos/src/fs/file_table.rs b/src/libos/src/fs/file_table.rs index 14f54241..0e60c108 100644 --- a/src/libos/src/fs/file_table.rs +++ b/src/libos/src/fs/file_table.rs @@ -11,12 +11,6 @@ pub struct FileTable { num_fds: usize, } -#[derive(Debug, Clone)] -struct FileTableEntry { - file: FileRef, - close_on_spawn: bool, -} - impl FileTable { pub fn new() -> FileTable { FileTable { @@ -25,12 +19,38 @@ impl FileTable { } } + pub fn dup(&mut self, fd: FileDesc, min_fd: FileDesc, close_on_spawn: bool) -> Result { + let file_ref = self.get(fd)?; + + let min_fd = min_fd as usize; + let min_free_fd = { + let mut table = &mut self.table; + + // Make sure that min_fd does not exceed the capacity of the table + if min_fd >= table.len() { + let expand_size = min_fd - table.len() + 1; + for _ in 0..expand_size { + table.push(None) + } + } + + table.iter() + .enumerate() + .skip(min_fd as usize) + .find(|&(idx, opt)| opt.is_none()) + .unwrap().0 + } as FileDesc; + + self.put_at(min_free_fd, file_ref, close_on_spawn); + + Ok(min_free_fd) + } + pub fn put(&mut self, file: FileRef, close_on_spawn: bool) -> FileDesc { let mut table = &mut self.table; let min_free_fd = if self.num_fds < table.len() { - table - .iter() + table.iter() .enumerate() .find(|&(idx, opt)| opt.is_none()) .unwrap() @@ -59,13 +79,30 @@ impl FileTable { } pub fn get(&self, fd: FileDesc) -> Result { + let entry = self.get_entry(fd)?; + Ok(entry.file.clone()) + } + + pub fn get_entry(&self, fd: FileDesc) -> Result<&FileTableEntry, Error> { if fd as usize >= self.table.len() { return errno!(EBADF, "Invalid file descriptor"); } let table = &self.table; match table[fd as usize].as_ref() { - Some(table_entry) => Ok(table_entry.file.clone()), + Some(table_entry) => Ok(table_entry), + None => errno!(EBADF, "Invalid file descriptor"), + } + } + + pub fn get_entry_mut(&mut self, fd: FileDesc) -> Result<&mut FileTableEntry, Error> { + if fd as usize >= self.table.len() { + return errno!(EBADF, "Invalid file descriptor"); + } + + let table = &mut self.table; + match table[fd as usize].as_mut() { + Some(table_entry) => Ok(table_entry), None => errno!(EBADF, "Invalid file descriptor"), } } @@ -114,11 +151,33 @@ impl Clone for FileTable { } } +#[derive(Debug, Clone)] +pub struct FileTableEntry { + file: FileRef, + close_on_spawn: bool, +} + impl FileTableEntry { - fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry { + pub fn new(file: FileRef, close_on_spawn: bool) -> FileTableEntry { FileTableEntry { file, close_on_spawn, } } + + pub fn get_file(&self) -> &FileRef { + &self.file + } + + pub fn is_close_on_spawn(&self) -> bool { + self.close_on_spawn + } + + pub fn get_file_mut(&mut self) -> &mut FileRef { + &mut self.file + } + + pub fn set_close_on_spawn(&mut self, close_on_spawn: bool) { + self.close_on_spawn = close_on_spawn; + } } diff --git a/src/libos/src/fs/mod.rs b/src/libos/src/fs/mod.rs index 8142f23a..2cddb5a3 100644 --- a/src/libos/src/fs/mod.rs +++ b/src/libos/src/fs/mod.rs @@ -390,7 +390,7 @@ fn split_path(path: &str) -> (&str, &str) { } bitflags! { - struct OpenFlags: u32 { + pub struct OpenFlags: u32 { /// read only const RDONLY = 0; /// write only @@ -614,3 +614,84 @@ pub unsafe fn write_cstr(ptr: *mut u8, s: &str) { ptr.copy_from(s.as_ptr(), s.len()); ptr.add(s.len()).write(0); } + + +#[derive(Debug)] +pub enum FcntlCmd { + /// Duplicate the file descriptor fd using the lowest-numbered available + /// file descriptor greater than or equal to arg. + DupFd(FileDesc), + /// As for `DupFd`, but additionally set the close-on-exec flag for the + /// duplicate file descriptor. + DupFdCloexec(FileDesc), + /// Return (as the function result) the file descriptor flags + GetFd(), + /// Set the file descriptor to be close-on-exec or not + SetFd(u32), + /// Get the file status flags + GetFl(), + /// Set the file status flags + SetFl(OpenFlags), +} + +pub const F_DUPFD : u32 = 0; +pub const F_GETFD : u32 = 1; +pub const F_SETFD : u32 = 2; +pub const F_GETFL : u32 = 3; +pub const F_SETFL : u32 = 4; +pub const F_DUPFD_CLOEXEC : u32 = 1030; + +pub const FD_CLOEXEC : u32 = 1; + +impl FcntlCmd { + #[deny(unreachable_patterns)] + pub fn from_raw(cmd: u32, arg: u64) -> Result { + Ok(match cmd { + F_DUPFD => FcntlCmd::DupFd(arg as FileDesc), + F_DUPFD_CLOEXEC => FcntlCmd::DupFdCloexec(arg as FileDesc), + F_GETFD => FcntlCmd::GetFd(), + F_SETFD => FcntlCmd::SetFd(arg as u32), + F_GETFL => FcntlCmd::GetFl(), + F_SETFL => FcntlCmd::SetFl(OpenFlags::from_bits_truncate(arg as u32)), + _ => return errno!(EINVAL, "invalid command"), + }) + } +} + +pub fn do_fcntl(fd: FileDesc, cmd: &FcntlCmd) -> Result { + info!("do_fcntl: {:?}, {:?}", &fd, cmd); + let current_ref = process::get_current(); + let mut current = current_ref.lock().unwrap(); + let files_ref = current.get_files(); + let mut files = files_ref.lock().unwrap(); + Ok(match cmd { + FcntlCmd::DupFd(min_fd) => { + let dup_fd = files.dup(fd, *min_fd, false)?; + dup_fd as isize + }, + FcntlCmd::DupFdCloexec(min_fd) => { + let dup_fd = files.dup(fd, *min_fd, true)?; + dup_fd as isize + }, + FcntlCmd::GetFd() => { + let entry = files.get_entry(fd)?; + let fd_flags = if entry.is_close_on_spawn() { + FD_CLOEXEC + } else { + 0 + }; + fd_flags as isize + }, + FcntlCmd::SetFd(fd_flags) => { + let entry = files.get_entry_mut(fd)?; + entry.set_close_on_spawn((fd_flags & FD_CLOEXEC) != 0); + 0 + }, + FcntlCmd::GetFl() => { + unimplemented!(); + }, + FcntlCmd::SetFl(flags) => { + unimplemented!(); + }, + }) +} diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index 7944ce36..0cd25ab4 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -1,5 +1,5 @@ use {fs, process, std, vm}; -use fs::{File, FileDesc, off_t, AccessModes, AccessFlags, AT_FDCWD}; +use fs::{File, FileDesc, off_t, AccessModes, AccessFlags, AT_FDCWD, FcntlCmd}; use prelude::*; use process::{ChildProcessFilter, FileAction, pid_t, CloneFlags, FutexFlags, FutexOp}; use std::ffi::{CStr, CString}; @@ -72,6 +72,7 @@ pub extern "C" fn dispatch_syscall( SYS_RMDIR => do_rmdir(arg0 as *const i8), SYS_LINK => do_link(arg0 as *const i8, arg1 as *const i8), SYS_UNLINK => do_unlink(arg0 as *const i8), + SYS_FCNTL => do_fcntl(arg0 as FileDesc, arg1 as u32, arg2 as u64), SYS_EXIT => do_exit(arg0 as i32), SYS_SPAWN => do_spawn( @@ -726,6 +727,11 @@ fn do_unlink(path: *const i8) -> Result { Ok(0) } +fn do_fcntl(fd: FileDesc, cmd: u32, arg: u64) -> Result { + let cmd = FcntlCmd::from_raw(cmd, arg)?; + fs::do_fcntl(fd, &cmd) +} + fn do_arch_prctl(code: u32, addr: *mut usize) -> Result { let code = process::ArchPrctlCode::from_u32(code)?; check_mut_ptr(addr)?;