Revert "parser: remove unused cwd_fd field"

cwd_fd is crucial to maintain: long-term vision for fish is multiple
threads each with their own CWD with the process-wide CWD used for
nothing at all except transiently (e.g. during fork).

Note this fd is heightenized per open_dir.

This reverts commit 6701b7f6c8.
This commit is contained in:
Peter Ammon
2026-05-25 20:25:26 -07:00
parent 79bf53aaa5
commit 960b96750a
2 changed files with 46 additions and 22 deletions

View File

@@ -12,6 +12,7 @@
use errno::Errno;
use libc::{EACCES, ELOOP, ENOENT, ENOTDIR, EPERM};
use nix::unistd::fchdir;
use std::sync::Arc;
// The cd builtin. Changes the current directory to the one specified or to $HOME if none is
// specified. The directory can be relative to any directory in the CDPATH variable.
@@ -82,35 +83,42 @@ pub fn cd(parser: &mut Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
let res = wopen_dir(&norm_dir, BEST_O_SEARCH).map_err(|err| err as i32);
let res = res.and_then(|fd| {
fchdir(&fd).map_err(|_|
fchdir(&fd)
.map_err(|_|
// nix::Result::Err contains nix::errno::Errno, which does not offer an API for
// converting to a raw int.
errno::errno().0)
.map(|()| fd)
});
if let Err(err) = res {
// Some errors we skip and only report if nothing worked.
// ENOENT in particular is very low priority
// - if in another directory there was a *file* by the correct name
// we prefer *that* error because it's more specific
if err == ENOENT {
let tmp = wreadlink(&norm_dir);
// clippy doesn't like this is_some/unwrap pair, but using if let is harder to read IMO
// TODO: if-let-chains
if let Some(tmp) = tmp.filter(|_| broken_symlink.is_empty()) {
broken_symlink = norm_dir;
broken_symlink_target = tmp;
} else if best_errno == 0 {
best_errno = errno::errno().0;
let fd = match res {
Ok(fd) => fd,
Err(err) => {
// Some errors we skip and only report if nothing worked.
// ENOENT in particular is very low priority
// - if in another directory there was a *file* by the correct name
// we prefer *that* error because it's more specific
if err == ENOENT {
let tmp = wreadlink(&norm_dir);
if let Some(tmp) = tmp.filter(|_| broken_symlink.is_empty()) {
broken_symlink = norm_dir;
broken_symlink_target = tmp;
} else if best_errno == 0 {
best_errno = errno::errno().0;
}
continue;
} else if err == ENOTDIR {
best_errno = err;
continue;
}
continue;
} else if err == ENOTDIR {
best_errno = err;
continue;
break;
}
best_errno = err;
break;
}
};
// Stash the fd for the cwd in the parser so it stays open.
// Eventually fish will support distinct CWDs for different parsers in different threads.
parser.libdata_mut().cwd_fd = Some(Arc::new(fd));
parser.set_var_and_fire(
L!("PWD"),

View File

@@ -11,6 +11,7 @@
},
event::{self, Event},
expand::{ExpandFlags, ExpandResultCode, expand_string, replace_home_directory_with_tilde},
fds::{BEST_O_SEARCH, open_dir},
flog, flogf, function,
io::IoChain,
job_group::MaybeJobId,
@@ -25,6 +26,7 @@
proc::{InternalJobId, JobGroupRef, JobList, JobRef, Pid, ProcStatus, job_reap},
signal::{RawSignal, signal_check_cancel, signal_clear_cancel},
wait_handle::WaitHandleStore,
wutil::perror_nix,
};
use assert_matches::assert_matches;
use fish_common::{
@@ -38,6 +40,7 @@
use std::io::Write as _;
use std::num::NonZeroU32;
use std::ops::DerefMut;
use std::os::fd::OwnedFd;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Duration;
@@ -271,6 +274,10 @@ pub struct LibraryData {
/// the command line.
pub transient_commandline: ScopedRefCell<Option<WString>>,
/// A file descriptor holding the current working directory, for use in openat().
/// This is never null and never invalid.
pub cwd_fd: Option<Arc<OwnedFd>>,
/// Variables supporting the "status" builtin.
pub status_vars: StatusVars,
@@ -443,7 +450,7 @@ pub fn user(mode: EnvMode) -> Self {
impl Parser {
/// Create a parser.
pub fn new(variables: EnvStack, cancel_behavior: CancelBehavior) -> Parser {
let result = Self {
let mut result = Self {
interactive_initialized: false,
current_node: ScopedRefCell::new(None),
current_filename: ScopedRefCell::new(None),
@@ -462,6 +469,15 @@ pub fn new(variables: EnvStack, cancel_behavior: CancelBehavior) -> Parser {
test_only_suppress_stderr: false,
};
match open_dir(c".", BEST_O_SEARCH) {
Ok(fd) => {
result.libdata_mut().cwd_fd = Some(Arc::new(fd));
}
Err(err) => {
perror_nix("Unable to open the current working directory", err);
}
}
result
}