diff --git a/src/context/context.rs b/src/context/context.rs index 56e3e10f9331b67fdbb4ca558b7cec33317791ff..1cf5a900f8aa502d34a06e70f397995b1d7557ba 100644 --- a/src/context/context.rs +++ b/src/context/context.rs @@ -244,17 +244,28 @@ impl Context { /// Add a file to the lowest available slot. /// Return the file descriptor number or None if no slot was found pub fn add_file(&self, file: FileDescriptor) -> Option<FileHandle> { + self.add_file_min(file, 0) + } + + /// Add a file to the lowest available slot greater than or equal to min. + /// Return the file descriptor number or None if no slot was found + pub fn add_file_min(&self, file: FileDescriptor, min: usize) -> Option<FileHandle> { let mut files = self.files.lock(); for (i, mut file_option) in files.iter_mut().enumerate() { - if file_option.is_none() { + if file_option.is_none() && i >= min { *file_option = Some(file); return Some(FileHandle::from(i)); } } let len = files.len(); if len < super::CONTEXT_MAX_FILES { - files.push(Some(file)); - Some(FileHandle::from(len)) + if len >= min { + files.push(Some(file)); + Some(FileHandle::from(len)) + } else { + drop(files); + self.insert_file(FileHandle::from(min), file) + } } else { None } diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index 76fcfd6f9c09dde85de91a51205ec60ead9ddcff..168d165cebd9a9303b402489b141e6e9ffa0e50b 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -8,7 +8,7 @@ use scheme::{self, FileHandle}; use syscall; use syscall::data::{Packet, Stat}; use syscall::error::*; -use syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, O_ACCMODE, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC}; +use syscall::flag::{F_GETFD, F_SETFD, F_GETFL, F_SETFL, F_DUPFD, O_ACCMODE, O_RDONLY, O_WRONLY, MODE_DIR, MODE_FILE, O_CLOEXEC}; use context::file::{FileDescriptor, FileDescription}; pub fn file_op(a: usize, fd: FileHandle, c: usize, d: usize) -> Result<usize> { @@ -240,8 +240,7 @@ pub fn close(fd: FileHandle) -> Result<usize> { file.close(fd) } -/// Duplicate file descriptor -pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { +fn duplicate_file(fd: FileHandle, buf: &[u8]) -> Result<FileDescriptor> { let file = { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; @@ -251,15 +250,11 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { }; if buf.is_empty() { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - - context.add_file(FileDescriptor { + Ok(FileDescriptor { description: Arc::clone(&file.description), event: None, cloexec: false, - }).ok_or(Error::new(EMFILE)) + }) } else { let description = file.description.read(); @@ -273,11 +268,7 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { scheme.dup(description.number, buf)? }; - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - - context.add_file(FileDescriptor { + Ok(FileDescriptor { description: Arc::new(RwLock::new(FileDescription { scheme: description.scheme, number: new_id, @@ -285,61 +276,34 @@ pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { })), event: None, cloexec: false, - }).ok_or(Error::new(EMFILE)) + }) } } +/// Duplicate file descriptor +pub fn dup(fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { + let new_file = duplicate_file(fd, buf)?; + + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + + context.add_file(new_file).ok_or(Error::new(EMFILE)) +} + /// Duplicate file descriptor, replacing another pub fn dup2(fd: FileHandle, new_fd: FileHandle, buf: &[u8]) -> Result<FileHandle> { if fd == new_fd { Ok(new_fd) } else { let _ = close(new_fd); + let new_file = duplicate_file(fd, buf)?; - let file = { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - context.get_file(fd).ok_or(Error::new(EBADF))? - }; - - if buf.is_empty() { - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); - - context.insert_file(new_fd, FileDescriptor { - description: Arc::clone(&file.description), - event: None, - cloexec: false, - }).ok_or(Error::new(EBADF)) - } else { - let description = file.description.read(); - - let new_id = { - let scheme = { - let schemes = scheme::schemes(); - let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?; - - scheme.clone() - }; - scheme.dup(description.number, buf)? - }; - - let contexts = context::contexts(); - let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; - let context = context_lock.read(); + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); - context.insert_file(new_fd, FileDescriptor { - description: Arc::new(RwLock::new(FileDescription { - scheme: description.scheme, - number: new_id, - flags: description.flags, - })), - event: None, - cloexec: false, - }).ok_or(Error::new(EMFILE)) - } + context.insert_file(new_fd, new_file).ok_or(Error::new(EMFILE)) } } @@ -356,7 +320,7 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> { let description = file.description.read(); // Communicate fcntl with scheme - if cmd != F_GETFD && cmd != F_SETFD { + if cmd != F_DUPFD && cmd != F_GETFD && cmd != F_SETFD { let scheme = { let schemes = scheme::schemes(); let scheme = schemes.get(description.scheme).ok_or(Error::new(EBADF))?; @@ -367,9 +331,23 @@ pub fn fcntl(fd: FileHandle, cmd: usize, arg: usize) -> Result<usize> { // Perform kernel operation if scheme agrees { + if cmd == F_DUPFD { + // Not in match because 'files' cannot be locked + let new_file = duplicate_file(fd, &[])?; + + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + + return context.add_file_min(new_file, arg) + .ok_or(Error::new(EMFILE)) + .map(FileHandle::into); + } + let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); + let mut files = context.files.lock(); match *files.get_mut(fd.into()).ok_or(Error::new(EBADF))? { Some(ref mut file) => match cmd {