Keep an fd for the cwd in the parser

To support distinct parsers having different working directories, we need
to keep the working directory alive, and also retain a non-path reference
to it.
This commit is contained in:
ridiculousfish 2019-06-10 09:27:51 -07:00
parent 6ce85aebc6
commit 6637ccd3a2
3 changed files with 18 additions and 2 deletions

View File

@ -2,6 +2,7 @@
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "builtin.h" #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); 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; struct stat buffer;
int status; int status;
status = wstat(dir, &buffer); status = wstat(dir, &buffer);
if (!status && S_ISDIR(buffer.st_mode)) { if (!status && S_ISDIR(buffer.st_mode)) {
streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), cmd, dir_in.c_str()); streams.err.append_format(_(L"%ls: Permission denied: '%ls'\n"), cmd, dir_in.c_str());
} else { } else {
streams.err.append_format(_(L"%ls: '%ls' is not a directory\n"), cmd, dir_in.c_str()); 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; return STATUS_CMD_ERROR;
} }
parser.libdata().cwd_fd = std::make_shared<const autoclose_fd_t>(std::move(dir_fd));
std::vector<event_t> evts; std::vector<event_t> evts;
parser.vars().set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(norm_dir), &evts); parser.vars().set_one(L"PWD", ENV_EXPORT | ENV_GLOBAL, std::move(norm_dir), &evts);
for (const auto &evt : evts) { for (const auto &evt : evts) {

View File

@ -1,6 +1,7 @@
// The fish parser. Contains functions for parsing and evaluating code. // The fish parser. Contains functions for parsing and evaluating code.
#include "config.h" // IWYU pragma: keep #include "config.h" // IWYU pragma: keep
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <cwchar> #include <cwchar>
@ -102,6 +103,12 @@ wcstring parser_t::user_presentable_path(const wcstring &path) const {
parser_t::parser_t(std::shared_ptr<env_stack_t> vars) : variables(std::move(vars)) { parser_t::parser_t(std::shared_ptr<env_stack_t> vars) : variables(std::move(vars)) {
assert(variables.get() && "Null variables in parser initializer"); 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<const autoclose_fd_t>(cwd);
} }
parser_t::parser_t() : parser_t(env_stack_t::principal_ref()) {} parser_t::parser_t() : parser_t(env_stack_t::principal_ref()) {}

View File

@ -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 /// machinery when wrapping: e.g. if `tig` wraps `git` then git completions need to see git on
/// the command line. /// the command line.
wcstring_list_t transient_commandlines{}; 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<const autoclose_fd_t> cwd_fd{};
}; };
class parser_t : public std::enable_shared_from_this<parser_t> { class parser_t : public std::enable_shared_from_this<parser_t> {