Get rid of CanonPath::fromCwd

As discussed in the last Nix team meeting (2024-02-95), this method
doesn't belong because `CanonPath` is a virtual/ideal absolute path
format, not used in file systems beyond the native OS format for which a
"current working directory" is defined.

Progress towards #9205
This commit is contained in:
John Ericson 2024-02-06 16:23:58 -05:00
parent f2f54cf087
commit 4687beecef
30 changed files with 152 additions and 135 deletions

View file

@ -256,9 +256,9 @@ SV *
hashPath(char * algo, int base32, char * path) hashPath(char * algo, int base32, char * path)
PPCODE: PPCODE:
try { try {
PosixSourceAccessor accessor; auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path);
Hash h = hashPath( Hash h = hashPath(
accessor, CanonPath::fromCwd(path), accessor, canonPath,
FileIngestionMethod::Recursive, parseHashAlgo(algo)).first; FileIngestionMethod::Recursive, parseHashAlgo(algo)).first;
auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false);
XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0)));
@ -336,10 +336,10 @@ StoreWrapper::addToStore(char * srcPath, int recursive, char * algo)
PPCODE: PPCODE:
try { try {
auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat;
PosixSourceAccessor accessor; auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(srcPath);
auto path = THIS->store->addToStore( auto path = THIS->store->addToStore(
std::string(baseNameOf(srcPath)), std::string(baseNameOf(srcPath)),
accessor, CanonPath::fromCwd(srcPath), accessor, canonPath,
method, parseHashAlgo(algo)); method, parseHashAlgo(algo));
XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0))); XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0)));
} catch (Error & e) { } catch (Error & e) {

View file

@ -156,7 +156,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
for (auto & i : autoArgs) { for (auto & i : autoArgs) {
auto v = state.allocValue(); auto v = state.allocValue();
if (i.second[0] == 'E') if (i.second[0] == 'E')
state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath(CanonPath::fromCwd()))); state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath(".")));
else else
v->mkString(((std::string_view) i.second).substr(1)); v->mkString(((std::string_view) i.second).substr(1));
res.insert(state.symbols.create(i.first), v); res.insert(state.symbols.create(i.first), v);
@ -164,7 +164,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
return res.finish(); return res.finish();
} }
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir) SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir)
{ {
if (EvalSettings::isPseudoUrl(s)) { if (EvalSettings::isPseudoUrl(s)) {
auto storePath = fetchers::downloadTarball( auto storePath = fetchers::downloadTarball(
@ -185,7 +185,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDi
} }
else else
return state.rootPath(CanonPath(s, baseDir)); return state.rootPath(baseDir ? absPath(s, *baseDir) : absPath(s));
} }
} }

View file

@ -29,6 +29,6 @@ private:
std::map<std::string, std::string> autoArgs; std::map<std::string, std::string> autoArgs;
}; };
SourcePath lookupFileArg(EvalState & state, std::string_view s, CanonPath baseDir = CanonPath::fromCwd()); SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr);
} }

View file

@ -17,7 +17,7 @@ Strings editorFor(const SourcePath & file, uint32_t line)
editor.find("vim") != std::string::npos || editor.find("vim") != std::string::npos ||
editor.find("kak") != std::string::npos)) editor.find("kak") != std::string::npos))
args.push_back(fmt("+%d", line)); args.push_back(fmt("+%d", line));
args.push_back(path->abs()); args.push_back(path->string());
return args; return args;
} }

View file

@ -487,10 +487,11 @@ Installables SourceExprCommand::parseInstallables(
state->eval(e, *vFile); state->eval(e, *vFile);
} }
else if (file) { else if (file) {
state->evalFile(lookupFileArg(*state, *file, CanonPath::fromCwd(getCommandBaseDir())), *vFile); auto dir = absPath(getCommandBaseDir());
state->evalFile(lookupFileArg(*state, *file, &dir), *vFile);
} }
else { else {
CanonPath dir(CanonPath::fromCwd(getCommandBaseDir())); Path dir = absPath(getCommandBaseDir());
auto e = state->parseExprFromString(*expr, state->rootPath(dir)); auto e = state->parseExprFromString(*expr, state->rootPath(dir));
state->eval(e, *vFile); state->eval(e, *vFile);
} }

View file

@ -899,7 +899,7 @@ void NixRepl::addVarToScope(const Symbol name, Value & v)
Expr * NixRepl::parseString(std::string s) Expr * NixRepl::parseString(std::string s)
{ {
return state->parseExprFromString(std::move(s), state->rootPath(CanonPath::fromCwd()), staticEnv); return state->parseExprFromString(std::move(s), state->rootPath("."), staticEnv);
} }

View file

@ -434,14 +434,14 @@ EvalState::EvalState(
, emptyBindings(0) , emptyBindings(0)
, rootFS( , rootFS(
evalSettings.restrictEval || evalSettings.pureEval evalSettings.restrictEval || evalSettings.pureEval
? ref<InputAccessor>(AllowListInputAccessor::create(makeFSInputAccessor(CanonPath::root), {}, ? ref<InputAccessor>(AllowListInputAccessor::create(makeFSInputAccessor(), {},
[](const CanonPath & path) -> RestrictedPathError { [](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = evalSettings.pureEval auto modeInformation = evalSettings.pureEval
? "in pure evaluation mode (use '--impure' to override)" ? "in pure evaluation mode (use '--impure' to override)"
: "in restricted mode"; : "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
})) }))
: makeFSInputAccessor(CanonPath::root)) : makeFSInputAccessor())
, corepkgsFS(makeMemoryInputAccessor()) , corepkgsFS(makeMemoryInputAccessor())
, internalFS(makeMemoryInputAccessor()) , internalFS(makeMemoryInputAccessor())
, derivationInternal{corepkgsFS->addFile( , derivationInternal{corepkgsFS->addFile(
@ -2763,7 +2763,7 @@ Expr * EvalState::parseStdin()
// drainFD should have left some extra space for terminators // drainFD should have left some extra space for terminators
buffer.append("\0\0", 2); buffer.append("\0\0", 2);
auto s = make_ref<std::string>(std::move(buffer)); auto s = make_ref<std::string>(std::move(buffer));
return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath(CanonPath::fromCwd()), staticBaseEnv); return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath("."), staticBaseEnv);
} }

View file

@ -372,6 +372,11 @@ public:
*/ */
SourcePath rootPath(CanonPath path); SourcePath rootPath(CanonPath path);
/**
* Variant which accepts relative paths too.
*/
SourcePath rootPath(PathView path);
/** /**
* Allow access to a path. * Allow access to a path.
*/ */

View file

@ -1,5 +1,4 @@
#include "eval.hh" #include "eval.hh"
#include "fs-input-accessor.hh"
namespace nix { namespace nix {
@ -8,4 +7,9 @@ SourcePath EvalState::rootPath(CanonPath path)
return {rootFS, std::move(path)}; return {rootFS, std::move(path)};
} }
SourcePath EvalState::rootPath(PathView path)
{
return {rootFS, CanonPath(absPath(path))};
}
} }

View file

@ -6,72 +6,30 @@ namespace nix {
struct FSInputAccessor : InputAccessor, PosixSourceAccessor struct FSInputAccessor : InputAccessor, PosixSourceAccessor
{ {
CanonPath root; using PosixSourceAccessor::PosixSourceAccessor;
FSInputAccessor(const CanonPath & root)
: root(root)
{
displayPrefix = root.isRoot() ? "" : root.abs();
}
void readFile(
const CanonPath & path,
Sink & sink,
std::function<void(uint64_t)> sizeCallback) override
{
auto absPath = makeAbsPath(path);
PosixSourceAccessor::readFile(absPath, sink, sizeCallback);
}
bool pathExists(const CanonPath & path) override
{
return PosixSourceAccessor::pathExists(makeAbsPath(path));
}
std::optional<Stat> maybeLstat(const CanonPath & path) override
{
return PosixSourceAccessor::maybeLstat(makeAbsPath(path));
}
DirEntries readDirectory(const CanonPath & path) override
{
DirEntries res;
for (auto & entry : PosixSourceAccessor::readDirectory(makeAbsPath(path)))
res.emplace(entry);
return res;
}
std::string readLink(const CanonPath & path) override
{
return PosixSourceAccessor::readLink(makeAbsPath(path));
}
CanonPath makeAbsPath(const CanonPath & path)
{
return root / path;
}
std::optional<CanonPath> getPhysicalPath(const CanonPath & path) override
{
return makeAbsPath(path);
}
}; };
ref<InputAccessor> makeFSInputAccessor(const CanonPath & root) ref<InputAccessor> makeFSInputAccessor()
{ {
return make_ref<FSInputAccessor>(root); return make_ref<FSInputAccessor>();
}
ref<InputAccessor> makeFSInputAccessor(std::filesystem::path root)
{
return make_ref<FSInputAccessor>(std::move(root));
} }
ref<InputAccessor> makeStorePathAccessor( ref<InputAccessor> makeStorePathAccessor(
ref<Store> store, ref<Store> store,
const StorePath & storePath) const StorePath & storePath)
{ {
return makeFSInputAccessor(CanonPath(store->toRealPath(storePath))); // FIXME: should use `store->getFSAccessor()`
return makeFSInputAccessor(std::filesystem::path { store->toRealPath(storePath) });
} }
SourcePath getUnfilteredRootPath(CanonPath path) SourcePath getUnfilteredRootPath(CanonPath path)
{ {
static auto rootFS = makeFSInputAccessor(CanonPath::root); static auto rootFS = makeFSInputAccessor();
return {rootFS, path}; return {rootFS, path};
} }

View file

@ -8,8 +8,9 @@ namespace nix {
class StorePath; class StorePath;
class Store; class Store;
ref<InputAccessor> makeFSInputAccessor( ref<InputAccessor> makeFSInputAccessor();
const CanonPath & root);
ref<InputAccessor> makeFSInputAccessor(std::filesystem::path root);
ref<InputAccessor> makeStorePathAccessor( ref<InputAccessor> makeStorePathAccessor(
ref<Store> store, ref<Store> store,

View file

@ -140,15 +140,15 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type)
struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl> struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
{ {
/** Location of the repository on disk. */ /** Location of the repository on disk. */
CanonPath path; std::filesystem::path path;
Repository repo; Repository repo;
GitRepoImpl(CanonPath _path, bool create, bool bare) GitRepoImpl(std::filesystem::path _path, bool create, bool bare)
: path(std::move(_path)) : path(std::move(_path))
{ {
initLibGit2(); initLibGit2();
if (pathExists(path.abs())) { if (pathExists(path.native())) {
if (git_repository_open(Setter(repo), path.c_str())) if (git_repository_open(Setter(repo), path.c_str()))
throw Error("opening Git repository '%s': %s", path, git_error_last()->message); throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
} else { } else {
@ -221,10 +221,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
return toHash(*oid); return toHash(*oid);
} }
std::vector<Submodule> parseSubmodules(const CanonPath & configFile) std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
{ {
GitConfig config; GitConfig config;
if (git_config_open_ondisk(Setter(config), configFile.abs().c_str())) if (git_config_open_ondisk(Setter(config), configFile.c_str()))
throw Error("parsing .gitmodules file: %s", git_error_last()->message); throw Error("parsing .gitmodules file: %s", git_error_last()->message);
ConfigIterator it; ConfigIterator it;
@ -296,7 +296,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
/* Get submodule info. */ /* Get submodule info. */
auto modulesFile = path / ".gitmodules"; auto modulesFile = path / ".gitmodules";
if (pathExists(modulesFile.abs())) if (pathExists(modulesFile))
info.submodules = parseSubmodules(modulesFile); info.submodules = parseSubmodules(modulesFile);
return info; return info;
@ -389,10 +389,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
auto dir = this->path; auto dir = this->path;
Strings gitArgs; Strings gitArgs;
if (shallow) { if (shallow) {
gitArgs = { "-C", dir.abs(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec }; gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
} }
else { else {
gitArgs = { "-C", dir.abs(), "fetch", "--quiet", "--force", "--", url, refspec }; gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec };
} }
runProgram(RunOptions { runProgram(RunOptions {
@ -438,7 +438,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
.args = { .args = {
"-c", "-c",
"gpg.ssh.allowedSignersFile=" + allowedSignersFile, "gpg.ssh.allowedSignersFile=" + allowedSignersFile,
"-C", path.abs(), "-C", path,
"verify-commit", "verify-commit",
rev.gitRev() rev.gitRev()
}, },
@ -465,7 +465,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
} }
}; };
ref<GitRepo> GitRepo::openRepo(const CanonPath & path, bool create, bool bare) ref<GitRepo> GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare)
{ {
return make_ref<GitRepoImpl>(path, create, bare); return make_ref<GitRepoImpl>(path, create, bare);
} }
@ -781,7 +781,7 @@ std::vector<std::tuple<GitRepoImpl::Submodule, Hash>> GitRepoImpl::getSubmodules
auto rawAccessor = getRawAccessor(rev); auto rawAccessor = getRawAccessor(rev);
for (auto & submodule : parseSubmodules(CanonPath(pathTemp))) { for (auto & submodule : parseSubmodules(pathTemp)) {
auto rev = rawAccessor->getSubmoduleRev(submodule.path); auto rev = rawAccessor->getSubmoduleRev(submodule.path);
result.push_back({std::move(submodule), rev}); result.push_back({std::move(submodule), rev});
} }

View file

@ -12,7 +12,7 @@ struct GitRepo
virtual ~GitRepo() virtual ~GitRepo()
{ } { }
static ref<GitRepo> openRepo(const CanonPath & path, bool create = false, bool bare = false); static ref<GitRepo> openRepo(const std::filesystem::path & path, bool create = false, bool bare = false);
virtual uint64_t getRevCount(const Hash & rev) = 0; virtual uint64_t getRevCount(const Hash & rev) = 0;

View file

@ -415,7 +415,7 @@ struct GitInputScheme : InputScheme
// If this is a local directory and no ref or revision is // If this is a local directory and no ref or revision is
// given, then allow the use of an unclean working tree. // given, then allow the use of an unclean working tree.
if (!input.getRef() && !input.getRev() && repoInfo.isLocal) if (!input.getRef() && !input.getRev() && repoInfo.isLocal)
repoInfo.workdirInfo = GitRepo::openRepo(CanonPath(repoInfo.url))->getWorkdirInfo(); repoInfo.workdirInfo = GitRepo::openRepo(repoInfo.url)->getWorkdirInfo();
return repoInfo; return repoInfo;
} }
@ -429,7 +429,7 @@ struct GitInputScheme : InputScheme
if (auto res = cache->lookup(key)) if (auto res = cache->lookup(key))
return getIntAttr(*res, "lastModified"); return getIntAttr(*res, "lastModified");
auto lastModified = GitRepo::openRepo(CanonPath(repoDir))->getLastModified(rev); auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev);
cache->upsert(key, Attrs{{"lastModified", lastModified}}); cache->upsert(key, Attrs{{"lastModified", lastModified}});
@ -447,7 +447,7 @@ struct GitInputScheme : InputScheme
Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url)); Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url));
auto revCount = GitRepo::openRepo(CanonPath(repoDir))->getRevCount(rev); auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev);
cache->upsert(key, Attrs{{"revCount", revCount}}); cache->upsert(key, Attrs{{"revCount", revCount}});
@ -457,7 +457,7 @@ struct GitInputScheme : InputScheme
std::string getDefaultRef(const RepoInfo & repoInfo) const std::string getDefaultRef(const RepoInfo & repoInfo) const
{ {
auto head = repoInfo.isLocal auto head = repoInfo.isLocal
? GitRepo::openRepo(CanonPath(repoInfo.url))->getWorkdirRef() ? GitRepo::openRepo(repoInfo.url)->getWorkdirRef()
: readHeadCached(repoInfo.url); : readHeadCached(repoInfo.url);
if (!head) { if (!head) {
warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.url); warn("could not read HEAD ref from repo at '%s', using 'master'", repoInfo.url);
@ -510,7 +510,7 @@ struct GitInputScheme : InputScheme
if (repoInfo.isLocal) { if (repoInfo.isLocal) {
repoDir = repoInfo.url; repoDir = repoInfo.url;
if (!input.getRev()) if (!input.getRev())
input.attrs.insert_or_assign("rev", GitRepo::openRepo(CanonPath(repoDir))->resolveRef(ref).gitRev()); input.attrs.insert_or_assign("rev", GitRepo::openRepo(repoDir)->resolveRef(ref).gitRev());
} else { } else {
Path cacheDir = getCachePath(repoInfo.url, getShallowAttr(input)); Path cacheDir = getCachePath(repoInfo.url, getShallowAttr(input));
repoDir = cacheDir; repoDir = cacheDir;
@ -519,7 +519,7 @@ struct GitInputScheme : InputScheme
createDirs(dirOf(cacheDir)); createDirs(dirOf(cacheDir));
PathLocks cacheDirLock({cacheDir}); PathLocks cacheDirLock({cacheDir});
auto repo = GitRepo::openRepo(CanonPath(cacheDir), true, true); auto repo = GitRepo::openRepo(cacheDir, true, true);
Path localRefFile = Path localRefFile =
ref.compare(0, 5, "refs/") == 0 ref.compare(0, 5, "refs/") == 0
@ -588,7 +588,7 @@ struct GitInputScheme : InputScheme
// cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder // cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder
} }
auto repo = GitRepo::openRepo(CanonPath(repoDir)); auto repo = GitRepo::openRepo(repoDir);
auto isShallow = repo->isShallow(); auto isShallow = repo->isShallow();
@ -664,7 +664,7 @@ struct GitInputScheme : InputScheme
for (auto & submodule : repoInfo.workdirInfo.submodules) for (auto & submodule : repoInfo.workdirInfo.submodules)
repoInfo.workdirInfo.files.insert(submodule.path); repoInfo.workdirInfo.files.insert(submodule.path);
auto repo = GitRepo::openRepo(CanonPath(repoInfo.url), false, false); auto repo = GitRepo::openRepo(repoInfo.url, false, false);
auto exportIgnore = getExportIgnoreAttr(input); auto exportIgnore = getExportIgnoreAttr(input);
@ -703,7 +703,7 @@ struct GitInputScheme : InputScheme
} }
if (!repoInfo.workdirInfo.isDirty) { if (!repoInfo.workdirInfo.isDirty) {
auto repo = GitRepo::openRepo(CanonPath(repoInfo.url)); auto repo = GitRepo::openRepo(repoInfo.url);
if (auto ref = repo->getWorkdirRef()) if (auto ref = repo->getWorkdirRef())
input.attrs.insert_or_assign("ref", *ref); input.attrs.insert_or_assign("ref", *ref);

View file

@ -110,8 +110,8 @@ void SourceAccessor::dumpPath(
time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter) time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter)
{ {
PosixSourceAccessor accessor; auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path);
accessor.dumpPath(CanonPath::fromCwd(path), sink, filter); accessor.dumpPath(canonPath, sink, filter);
return accessor.mtime; return accessor.mtime;
} }

View file

@ -20,11 +20,6 @@ CanonPath::CanonPath(const std::vector<std::string> & elems)
push(s); push(s);
} }
CanonPath CanonPath::fromCwd(std::string_view path)
{
return CanonPath(unchecked_t(), absPath(path));
}
std::optional<CanonPath> CanonPath::parent() const std::optional<CanonPath> CanonPath::parent() const
{ {
if (isRoot()) return std::nullopt; if (isRoot()) return std::nullopt;

View file

@ -52,8 +52,6 @@ public:
*/ */
CanonPath(const std::vector<std::string> & elems); CanonPath(const std::vector<std::string> & elems);
static CanonPath fromCwd(std::string_view path = ".");
static CanonPath root; static CanonPath root;
/** /**

View file

@ -6,6 +6,33 @@
namespace nix { namespace nix {
PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root)
: root(std::move(root))
{
assert(root.empty() || root.is_absolute());
displayPrefix = root;
}
PosixSourceAccessor::PosixSourceAccessor()
: PosixSourceAccessor(std::filesystem::path {})
{ }
std::pair<PosixSourceAccessor, CanonPath> PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
{
std::filesystem::path path2 = absPath(path.native());
return {
PosixSourceAccessor { path2.root_path() },
CanonPath { static_cast<std::string>(path2.relative_path()) },
};
}
std::filesystem::path PosixSourceAccessor::makeAbsPath(const CanonPath & path)
{
return root.empty()
? (std::filesystem::path { path.abs() })
: root / path.rel();
}
void PosixSourceAccessor::readFile( void PosixSourceAccessor::readFile(
const CanonPath & path, const CanonPath & path,
Sink & sink, Sink & sink,
@ -13,9 +40,11 @@ void PosixSourceAccessor::readFile(
{ {
assertNoSymlinks(path); assertNoSymlinks(path);
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW); auto ap = makeAbsPath(path);
AutoCloseFD fd = open(ap.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
if (!fd) if (!fd)
throw SysError("opening file '%1%'", path); throw SysError("opening file '%1%'", ap.native());
struct stat st; struct stat st;
if (fstat(fd.get(), &st) == -1) if (fstat(fd.get(), &st) == -1)
@ -46,7 +75,7 @@ void PosixSourceAccessor::readFile(
bool PosixSourceAccessor::pathExists(const CanonPath & path) bool PosixSourceAccessor::pathExists(const CanonPath & path)
{ {
if (auto parent = path.parent()) assertNoSymlinks(*parent); if (auto parent = path.parent()) assertNoSymlinks(*parent);
return nix::pathExists(path.abs()); return nix::pathExists(makeAbsPath(path));
} }
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path) std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
@ -60,7 +89,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
} }
std::optional<struct stat> st{std::in_place}; std::optional<struct stat> st{std::in_place};
if (::lstat(path.c_str(), &*st)) { if (::lstat(makeAbsPath(path).c_str(), &*st)) {
if (errno == ENOENT || errno == ENOTDIR) if (errno == ENOENT || errno == ENOTDIR)
st.reset(); st.reset();
else else
@ -95,7 +124,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
{ {
assertNoSymlinks(path); assertNoSymlinks(path);
DirEntries res; DirEntries res;
for (auto & entry : nix::readDirectory(path.abs())) { for (auto & entry : nix::readDirectory(makeAbsPath(path))) {
std::optional<Type> type; std::optional<Type> type;
switch (entry.type) { switch (entry.type) {
case DT_REG: type = Type::tRegular; break; case DT_REG: type = Type::tRegular; break;
@ -110,12 +139,12 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
std::string PosixSourceAccessor::readLink(const CanonPath & path) std::string PosixSourceAccessor::readLink(const CanonPath & path)
{ {
if (auto parent = path.parent()) assertNoSymlinks(*parent); if (auto parent = path.parent()) assertNoSymlinks(*parent);
return nix::readLink(path.abs()); return nix::readLink(makeAbsPath(path));
} }
std::optional<CanonPath> PosixSourceAccessor::getPhysicalPath(const CanonPath & path) std::optional<std::filesystem::path> PosixSourceAccessor::getPhysicalPath(const CanonPath & path)
{ {
return path; return makeAbsPath(path);
} }
void PosixSourceAccessor::assertNoSymlinks(CanonPath path) void PosixSourceAccessor::assertNoSymlinks(CanonPath path)

View file

@ -9,6 +9,16 @@ namespace nix {
*/ */
struct PosixSourceAccessor : virtual SourceAccessor struct PosixSourceAccessor : virtual SourceAccessor
{ {
/**
* Optional root path to prefix all operations into the native file
* system. This allows prepending funny things like `C:\` that
* `CanonPath` intentionally doesn't support.
*/
const std::filesystem::path root;
PosixSourceAccessor();
PosixSourceAccessor(std::filesystem::path && root);
/** /**
* The most recent mtime seen by lstat(). This is a hack to * The most recent mtime seen by lstat(). This is a hack to
* support dumpPathAndGetMtime(). Should remove this eventually. * support dumpPathAndGetMtime(). Should remove this eventually.
@ -28,7 +38,22 @@ struct PosixSourceAccessor : virtual SourceAccessor
std::string readLink(const CanonPath & path) override; std::string readLink(const CanonPath & path) override;
std::optional<CanonPath> getPhysicalPath(const CanonPath & path) override; std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override;
/**
* Create a `PosixSourceAccessor` and `CanonPath` corresponding to
* some native path.
*
* The `PosixSourceAccessor` is rooted as far up the tree as
* possible, (e.g. on Windows it could scoped to a drive like
* `C:\`). This allows more `..` parent accessing to work.
*
* See
* [`std::filesystem::path::root_path`](https://en.cppreference.com/w/cpp/filesystem/path/root_path)
* and
* [`std::filesystem::path::relative_path`](https://en.cppreference.com/w/cpp/filesystem/path/relative_path).
*/
static std::pair<PosixSourceAccessor, CanonPath> createAtRoot(const std::filesystem::path & path);
private: private:
@ -38,6 +63,8 @@ private:
void assertNoSymlinks(CanonPath path); void assertNoSymlinks(CanonPath path);
std::optional<struct stat> cachedLstat(const CanonPath & path); std::optional<struct stat> cachedLstat(const CanonPath & path);
std::filesystem::path makeAbsPath(const CanonPath & path);
}; };
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <filesystem>
#include "canon-path.hh" #include "canon-path.hh"
#include "hash.hh" #include "hash.hh"
@ -119,7 +121,7 @@ struct SourceAccessor
* possible. This is only possible for filesystems that are * possible. This is only possible for filesystems that are
* materialized in the root filesystem. * materialized in the root filesystem.
*/ */
virtual std::optional<CanonPath> getPhysicalPath(const CanonPath & path) virtual std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path)
{ return std::nullopt; } { return std::nullopt; }
bool operator == (const SourceAccessor & x) const bool operator == (const SourceAccessor & x) const

View file

@ -35,7 +35,7 @@ void SourcePath::dumpPath(
PathFilter & filter) const PathFilter & filter) const
{ return accessor->dumpPath(path, sink, filter); } { return accessor->dumpPath(path, sink, filter); }
std::optional<CanonPath> SourcePath::getPhysicalPath() const std::optional<std::filesystem::path> SourcePath::getPhysicalPath() const
{ return accessor->getPhysicalPath(path); } { return accessor->getPhysicalPath(path); }
std::string SourcePath::to_string() const std::string SourcePath::to_string() const

View file

@ -82,7 +82,7 @@ struct SourcePath
* Return the location of this path in the "real" filesystem, if * Return the location of this path in the "real" filesystem, if
* it has a physical location. * it has a physical location.
*/ */
std::optional<CanonPath> getPhysicalPath() const; std::optional<std::filesystem::path> getPhysicalPath() const;
std::string to_string() const; std::string to_string() const;

View file

@ -299,7 +299,7 @@ static void main_nix_build(int argc, char * * argv)
else else
for (auto i : left) { for (auto i : left) {
if (fromArgs) if (fromArgs)
exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath(CanonPath::fromCwd()))); exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath(".")));
else { else {
auto absolute = i; auto absolute = i;
try { try {
@ -400,7 +400,7 @@ static void main_nix_build(int argc, char * * argv)
try { try {
auto expr = state->parseExprFromString( auto expr = state->parseExprFromString(
"(import <nixpkgs> {}).bashInteractive", "(import <nixpkgs> {}).bashInteractive",
state->rootPath(CanonPath::fromCwd())); state->rootPath("."));
Value v; Value v;
state->eval(expr, v); state->eval(expr, v);

View file

@ -413,7 +413,7 @@ static void queryInstSources(EvalState & state,
loadSourceExpr(state, *instSource.nixExprPath, vArg); loadSourceExpr(state, *instSource.nixExprPath, vArg);
for (auto & i : args) { for (auto & i : args) {
Expr * eFun = state.parseExprFromString(i, state.rootPath(CanonPath::fromCwd())); Expr * eFun = state.parseExprFromString(i, state.rootPath("."));
Value vFun, vTmp; Value vFun, vTmp;
state.eval(eFun, vFun); state.eval(eFun, vFun);
vTmp.mkApp(&vFun, &vArg); vTmp.mkApp(&vFun, &vArg);

View file

@ -168,7 +168,7 @@ static int main_nix_instantiate(int argc, char * * argv)
for (auto & i : files) { for (auto & i : files) {
auto p = state->findFile(i); auto p = state->findFile(i);
if (auto fn = p.getPhysicalPath()) if (auto fn = p.getPhysicalPath())
std::cout << fn->abs() << std::endl; std::cout << fn->native() << std::endl;
else else
throw Error("'%s' has no physical path", p); throw Error("'%s' has no physical path", p);
} }
@ -184,7 +184,7 @@ static int main_nix_instantiate(int argc, char * * argv)
for (auto & i : files) { for (auto & i : files) {
Expr * e = fromArgs Expr * e = fromArgs
? state->parseExprFromString(i, state->rootPath(CanonPath::fromCwd())) ? state->parseExprFromString(i, state->rootPath("."))
: state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, i))); : state->parseExprFromFile(resolveExprPath(lookupFileArg(*state, i)));
processExpr(*state, attrPaths, parseOnly, strict, autoArgs, processExpr(*state, attrPaths, parseOnly, strict, autoArgs,
evalOnly, outputKind, xmlOutputSourceLocation, e); evalOnly, outputKind, xmlOutputSourceLocation, e);

View file

@ -176,12 +176,11 @@ static void opAdd(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
PosixSourceAccessor accessor; for (auto & i : opArgs) {
for (auto & i : opArgs) auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i);
cout << fmt("%s\n", store->printStorePath(store->addToStore( cout << fmt("%s\n", store->printStorePath(store->addToStore(
std::string(baseNameOf(i)), std::string(baseNameOf(i)), accessor, canonPath)));
accessor, }
CanonPath::fromCwd(i))));
} }
@ -201,14 +200,15 @@ static void opAddFixed(Strings opFlags, Strings opArgs)
HashAlgorithm hashAlgo = parseHashAlgo(opArgs.front()); HashAlgorithm hashAlgo = parseHashAlgo(opArgs.front());
opArgs.pop_front(); opArgs.pop_front();
PosixSourceAccessor accessor; for (auto & i : opArgs) {
for (auto & i : opArgs) auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i);
std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow( std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow(
baseNameOf(i), baseNameOf(i),
accessor, accessor,
CanonPath::fromCwd(i), canonPath,
method, method,
hashAlgo).path)); hashAlgo).path));
}
} }

View file

@ -60,9 +60,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand
{ {
if (!namePart) namePart = baseNameOf(path); if (!namePart) namePart = baseNameOf(path);
PosixSourceAccessor accessor; auto [accessor, path2] = PosixSourceAccessor::createAtRoot(path);
auto path2 = CanonPath::fromCwd(path);
auto storePath = dryRun auto storePath = dryRun
? store->computeStorePath( ? store->computeStorePath(

View file

@ -66,7 +66,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption
if (apply) { if (apply) {
auto vApply = state->allocValue(); auto vApply = state->allocValue();
state->eval(state->parseExprFromString(*apply, state->rootPath(CanonPath::fromCwd())), *vApply); state->eval(state->parseExprFromString(*apply, state->rootPath(".")), *vApply);
auto vRes = state->allocValue(); auto vRes = state->allocValue();
state->callFunction(*vApply, *v, *vRes, noPos); state->callFunction(*vApply, *v, *vRes, noPos);
v = vRes; v = vRes;

View file

@ -89,8 +89,8 @@ struct CmdHashBase : Command
else else
hashSink = std::make_unique<HashSink>(ha); hashSink = std::make_unique<HashSink>(ha);
PosixSourceAccessor accessor; auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path);
dumpPath(accessor, CanonPath::fromCwd(path), *hashSink, mode); dumpPath(accessor, canonPath, *hashSink, mode);
Hash h = hashSink->finish().first; Hash h = hashSink->finish().first;
if (truncate && h.hashSize > 20) h = compressHash(h, 20); if (truncate && h.hashSize > 20) h = compressHash(h, 20);

View file

@ -123,10 +123,9 @@ std::tuple<StorePath, Hash> prefetchFile(
Activity act(*logger, lvlChatty, actUnknown, Activity act(*logger, lvlChatty, actUnknown,
fmt("adding '%s' to the store", url)); fmt("adding '%s' to the store", url));
PosixSourceAccessor accessor; auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(tmpFile);
auto info = store->addToStoreSlow( auto info = store->addToStoreSlow(
*name, *name, accessor, canonPath,
accessor, CanonPath::fromCwd(tmpFile),
ingestionMethod, hashAlgo, {}, expectedHash); ingestionMethod, hashAlgo, {}, expectedHash);
storePath = info.path; storePath = info.path;
assert(info.ca); assert(info.ca);