diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index a6a766b298..9989e17662 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -1228,25 +1228,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 e353184dac..f3677d0875 100644 --- a/src/syscall/fs.rs +++ b/src/syscall/fs.rs @@ -311,19 +311,41 @@ 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, false, token)?; + // A self-dup2 (fd == new_fd) with an EMPTY buffer is a no-op. But a + // self-dup2 WITH a buffer is meaningful — e.g. relibc issues dup2(ft, ft, + // "refresh") to re-materialize the inherited filetable — so it must fall + // through to duplicate_file() rather than short-circuiting. + if fd == new_fd && buf.is_empty() { + return Ok(new_fd); + } + // Duplicate FIRST (fd may equal new_fd), then replace new_fd's descriptor. + let new_file = duplicate_file(fd, buf, false, token)?; + + let old_file = { 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)) + let (context, mut split_token) = current.token_split(); + + let old_file = context.remove_file(new_fd, &mut split_token); + + if context + .insert_file(new_fd, new_file, &mut split_token) + .is_none() + { + if let Some(old) = old_file { + context.insert_file(new_fd, old, &mut split_token); + } + return Err(Error::new(EMFILE)); + } + old_file + }; + + if let Some(old) = old_file { + let _ = old.close(token); } + + Ok(new_fd) } pub fn call( fd: FileHandle,