diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index c9c8fea3f9..d8abeea4af 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -771,25 +771,59 @@ impl KernelScheme for ProcScheme { } => { // TODO: Maybe allow userspace to either copy or transfer recently dupped file // descriptors between file tables. - if buf != b"copy" { - return Err(Error::new(EINVAL)); - } - let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; + let new_handle = match buf { + b"copy" => { + let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; - let new_filetable = - Arc::new(RwLock::new(filetable.read(token.token()).clone())); + let new_filetable = + Arc::new(RwLock::new(filetable.read(token.token()).clone())); - handle( - Handle { - kind: ContextHandle::NewFiletable { - filetable: new_filetable, - binary_format, - data: data.clone(), - }, - context, - }, - true, - ) + Handle { + kind: ContextHandle::NewFiletable { + filetable: new_filetable, + binary_format, + data: data.clone(), + }, + context, + } + } + b"refresh" => { + let filetable = filetable.upgrade().ok_or(Error::new(EOWNERDEAD))?; + + let new_data = if binary_format { + let mut data = Vec::new(); + for index in filetable + .read(token.token()) + .enumerate() + .filter_map(|(idx, val)| val.as_ref().map(|_| idx)) + { + data.extend((index as u64).to_le_bytes()); + } + data.into_boxed_slice() + } else { + use core::fmt::Write; + let mut data = String::new(); + for index in filetable + .read(token.token()) + .enumerate() + .filter_map(|(idx, val)| val.as_ref().map(|_| idx)) + { + writeln!(data, "{}", index).unwrap(); + } + data.into_bytes().into_boxed_slice() + }; + Handle { + kind: ContextHandle::Filetable { + filetable: Arc::downgrade(&filetable), + binary_format, + data: new_data, + }, + context, + } + } + _ => return Err(Error::new(EINVAL)), + }; + handle(new_handle, true) } Handle { kind: ContextHandle::AddrSpace { ref addrspace }, diff --git a/src/syscall/fs.rs b/src/syscall/fs.rs index f796517c2e..35d6a49e4e 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -235,19 +235,36 @@ pub fn dup2( buf: UserSliceRo, token: &mut CleanLockToken, ) -> Result { - if fd == new_fd { - Ok(new_fd) - } else { - let _ = close(new_fd, token); - let new_file = duplicate_file(fd, buf, token)?; - - let current_lock = context::current(); - let mut current = current_lock.read(token.token()); - let (context, mut token) = current.token_split(); - context - .insert_file(new_fd, new_file, &mut token) - .ok_or(Error::new(EMFILE)) + if fd == new_fd && buf.is_empty() { + return Ok(new_fd); } + + let new_file = duplicate_file(fd, buf, token)?; + + let old_file = { + let current_lock = context::current(); + let mut current = current_lock.write(token.token()); + let (context, mut split_token) = current.token_split(); + + let old_file = context.remove_file(new_fd, &mut split_token.token()); + + if context + .insert_file(new_fd, new_file, &mut split_token.token()) + .is_none() + { + if let Some(old) = old_file { + context.insert_file(new_fd, old, &mut split_token.token()); + } + return Err(Error::new(EMFILE)); + } + old_file + }; + + if let Some(old) = old_file { + let _ = old.close(token); + } + + Ok(new_fd) } pub fn call( fds: &[usize],