diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index e347bf0d9..57adc84b4 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -2,6 +2,7 @@ #include "config.h" // IWYU pragma: keep #include +#include #include #include "builtin.h" @@ -68,14 +69,17 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wcstring norm_dir = normalize_path(dir); - if (wchdir(norm_dir) != 0) { + // We need to keep around the fd for this directory, in the parser. + autoclose_fd_t dir_fd(wopen_cloexec(norm_dir, O_RDONLY)); + bool success = dir_fd.valid() && fchdir(dir_fd.fd()) == 0; + + if (!success) { struct stat buffer; int status; status = wstat(dir, &buffer); if (!status && S_ISDIR(buffer.st_mode)) { streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), cmd, dir_in.c_str()); - } else { streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); } @@ -87,6 +91,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { return STATUS_CMD_ERROR; } + parser.libdata().cwd_fd = std::make_shared(std::move(dir_fd)); std::vector evts; parser.vars().set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(norm_dir), &evts); for (const auto &evt : evts) { diff --git a/src/parser.cpp b/src/parser.cpp index 100aa13ac..72b58612e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1,6 +1,7 @@ // The fish parser. Contains functions for parsing and evaluating code. #include "config.h" // IWYU pragma: keep +#include #include #include @@ -102,6 +103,12 @@ wcstring parser_t::user_presentable_path(const wcstring &path) const { parser_t::parser_t(std::shared_ptr vars) : variables(std::move(vars)) { assert(variables.get() && "Null variables in parser initializer"); + int cwd = open_cloexec(".", O_RDONLY); + if (cwd < 0) { + perror("Unable to open the current working directory"); + abort(); + } + libdata().cwd_fd = std::make_shared(cwd); } parser_t::parser_t() : parser_t(env_stack_t::principal_ref()) {} diff --git a/src/parser.h b/src/parser.h index bcd39214d..77d73cc10 100644 --- a/src/parser.h +++ b/src/parser.h @@ -169,6 +169,10 @@ struct library_data_t { /// machinery when wrapping: e.g. if `tig` wraps `git` then git completions need to see git on /// the command line. wcstring_list_t transient_commandlines{}; + + /// A file descriptor holding the current working directory, for use in openat(). + /// This is never null and never invalid. + std::shared_ptr cwd_fd{}; }; class parser_t : public std::enable_shared_from_this {