proc/fs: make dup2 refresh conservative (preserve original non-self dup2 path)
The previous backport rewrote the whole dup2 (duplicate-first, remove/insert) which perturbed ordinary dup2 used by process-spawn stdio setup and could break exec (ENOENT). Restrict the new behavior to the self-dup-with-buffer case (dup2(ft, ft, 'refresh')); the normal fd!=new_fd path is byte-for-byte the original.
This commit is contained in:
+33
-31
@@ -311,41 +311,43 @@ pub fn dup2(
|
||||
buf: UserSliceRo,
|
||||
token: &mut CleanLockToken,
|
||||
) -> Result<FileHandle> {
|
||||
// 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);
|
||||
}
|
||||
if fd == new_fd {
|
||||
// Self-dup: a no-op if no buffer is supplied. But a self-dup2 WITH a
|
||||
// buffer is a meaningful in-place operation — e.g. relibc issues
|
||||
// dup2(ft, ft, "refresh") to re-materialize the inherited filetable, so
|
||||
// it must NOT short-circuit. Duplicate first (fd is still valid), then
|
||||
// replace the descriptor in place. Only this self-dup path is new; the
|
||||
// normal path below is unchanged so ordinary dup2 (process-spawn stdio)
|
||||
// keeps its exact original behavior.
|
||||
if buf.is_empty() {
|
||||
return Ok(new_fd);
|
||||
}
|
||||
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();
|
||||
let old_file = context.remove_file(new_fd, &mut token);
|
||||
context
|
||||
.insert_file(new_fd, new_file, &mut token)
|
||||
.ok_or(Error::new(EMFILE))?;
|
||||
old_file
|
||||
};
|
||||
if let Some(old) = old_file {
|
||||
let _ = old.close(token);
|
||||
}
|
||||
Ok(new_fd)
|
||||
} else {
|
||||
let _ = close(new_fd, token);
|
||||
let new_file = duplicate_file(fd, buf, false, token)?;
|
||||
|
||||
// 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 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);
|
||||
let (context, mut token) = current.token_split();
|
||||
context
|
||||
.insert_file(new_fd, new_file, &mut token)
|
||||
.ok_or(Error::new(EMFILE))
|
||||
}
|
||||
|
||||
Ok(new_fd)
|
||||
}
|
||||
pub fn call(
|
||||
fd: FileHandle,
|
||||
|
||||
Reference in New Issue
Block a user