Merge pull request #9269 from edolstra/unify-accessor

Unify `FSAccessor` and `SourceAccessor`
This commit is contained in:
Eelco Dolstra 2023-11-02 14:23:10 +01:00 committed by GitHub
commit 5223114c93
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 250 additions and 299 deletions

View file

@ -34,16 +34,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1695283060, "lastModified": 1698876495,
"narHash": "sha256-CJz71xhCLlRkdFUSQEL0pIAAfcnWFXMzd9vXhPrnrEg=", "narHash": "sha256-nsQo2/mkDUFeAjuu92p0dEqhRvHHiENhkKVIV1y0/Oo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "31ed632c692e6a36cfc18083b88ece892f863ed4", "rev": "9eb24edd6a0027fed010ccfe300a9734d029983c",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-23.05-small", "ref": "release-23.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }

View file

@ -1,7 +1,9 @@
{ {
description = "The purely functional package manager"; description = "The purely functional package manager";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; # FIXME go back to nixos-23.05-small once
# https://github.com/NixOS/nixpkgs/pull/264875 is included.
inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; }; inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };

View file

@ -1548,10 +1548,8 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
try { try {
auto checked = state.checkSourcePath(path); auto checked = state.checkSourcePath(path);
auto exists = checked.pathExists(); auto st = checked.maybeLstat();
if (exists && mustBeDir) { auto exists = st && (!mustBeDir || st->type == SourceAccessor::tDirectory);
exists = checked.lstat().type == InputAccessor::tDirectory;
}
v.mkBool(exists); v.mkBool(exists);
} catch (SysError & e) { } catch (SysError & e) {
/* Don't give away info from errors while canonicalising /* Don't give away info from errors while canonicalising

View file

@ -36,11 +36,11 @@ struct FSInputAccessorImpl : FSInputAccessor, PosixSourceAccessor
return isAllowed(absPath) && PosixSourceAccessor::pathExists(absPath); return isAllowed(absPath) && PosixSourceAccessor::pathExists(absPath);
} }
Stat lstat(const CanonPath & path) override std::optional<Stat> maybeLstat(const CanonPath & path) override
{ {
auto absPath = makeAbsPath(path); auto absPath = makeAbsPath(path);
checkAllowed(absPath); checkAllowed(absPath);
return PosixSourceAccessor::lstat(absPath); return PosixSourceAccessor::maybeLstat(absPath);
} }
DirEntries readDirectory(const CanonPath & path) override DirEntries readDirectory(const CanonPath & path) override

View file

@ -20,12 +20,12 @@ struct MemoryInputAccessorImpl : MemoryInputAccessor
return i != files.end(); return i != files.end();
} }
Stat lstat(const CanonPath & path) override std::optional<Stat> maybeLstat(const CanonPath & path) override
{ {
auto i = files.find(path); auto i = files.find(path);
if (i != files.end()) if (i != files.end())
return Stat { .type = tRegular, .isExecutable = false }; return Stat { .type = tRegular, .isExecutable = false };
throw Error("file '%s' does not exist", path); return std::nullopt;
} }
DirEntries readDirectory(const CanonPath & path) override DirEntries readDirectory(const CanonPath & path) override

View file

@ -2,7 +2,7 @@
#include "binary-cache-store.hh" #include "binary-cache-store.hh"
#include "compression.hh" #include "compression.hh"
#include "derivations.hh" #include "derivations.hh"
#include "fs-accessor.hh" #include "source-accessor.hh"
#include "globals.hh" #include "globals.hh"
#include "nar-info.hh" #include "nar-info.hh"
#include "sync.hh" #include "sync.hh"
@ -143,7 +143,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
write the compressed NAR to disk), into a HashSink (to get the write the compressed NAR to disk), into a HashSink (to get the
NAR hash), and into a NarAccessor (to get the NAR listing). */ NAR hash), and into a NarAccessor (to get the NAR listing). */
HashSink fileHashSink { htSHA256 }; HashSink fileHashSink { htSHA256 };
std::shared_ptr<FSAccessor> narAccessor; std::shared_ptr<SourceAccessor> narAccessor;
HashSink narHashSink { htSHA256 }; HashSink narHashSink { htSHA256 };
{ {
FdSink fileSink(fdTemp.get()); FdSink fileSink(fdTemp.get());
@ -195,7 +195,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
if (writeNARListing) { if (writeNARListing) {
nlohmann::json j = { nlohmann::json j = {
{"version", 1}, {"version", 1},
{"root", listNar(ref<FSAccessor>(narAccessor), "", true)}, {"root", listNar(ref<SourceAccessor>(narAccessor), CanonPath::root, true)},
}; };
upsertFile(std::string(info.path.hashPart()) + ".ls", j.dump(), "application/json"); upsertFile(std::string(info.path.hashPart()) + ".ls", j.dump(), "application/json");
@ -206,9 +206,9 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
specify the NAR file and member containing the debug info. */ specify the NAR file and member containing the debug info. */
if (writeDebugInfo) { if (writeDebugInfo) {
std::string buildIdDir = "/lib/debug/.build-id"; CanonPath buildIdDir("lib/debug/.build-id");
if (narAccessor->stat(buildIdDir).type == FSAccessor::tDirectory) { if (auto st = narAccessor->maybeLstat(buildIdDir); st && st->type == SourceAccessor::tDirectory) {
ThreadPool threadPool(25); ThreadPool threadPool(25);
@ -231,17 +231,17 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
std::regex regex1("^[0-9a-f]{2}$"); std::regex regex1("^[0-9a-f]{2}$");
std::regex regex2("^[0-9a-f]{38}\\.debug$"); std::regex regex2("^[0-9a-f]{38}\\.debug$");
for (auto & s1 : narAccessor->readDirectory(buildIdDir)) { for (auto & [s1, _type] : narAccessor->readDirectory(buildIdDir)) {
auto dir = buildIdDir + "/" + s1; auto dir = buildIdDir + s1;
if (narAccessor->stat(dir).type != FSAccessor::tDirectory if (narAccessor->lstat(dir).type != SourceAccessor::tDirectory
|| !std::regex_match(s1, regex1)) || !std::regex_match(s1, regex1))
continue; continue;
for (auto & s2 : narAccessor->readDirectory(dir)) { for (auto & [s2, _type] : narAccessor->readDirectory(dir)) {
auto debugPath = dir + "/" + s2; auto debugPath = dir + s2;
if (narAccessor->stat(debugPath).type != FSAccessor::tRegular if (narAccessor->lstat(debugPath).type != SourceAccessor::tRegular
|| !std::regex_match(s2, regex2)) || !std::regex_match(s2, regex2))
continue; continue;
@ -250,7 +250,7 @@ ref<const ValidPathInfo> BinaryCacheStore::addToStoreCommon(
std::string key = "debuginfo/" + buildId; std::string key = "debuginfo/" + buildId;
std::string target = "../" + narInfo->url; std::string target = "../" + narInfo->url;
threadPool.enqueue(std::bind(doFile, std::string(debugPath, 1), key, target)); threadPool.enqueue(std::bind(doFile, std::string(debugPath.rel()), key, target));
} }
} }
@ -503,9 +503,9 @@ void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
upsertFile(filePath, info.toJSON().dump(), "application/json"); upsertFile(filePath, info.toJSON().dump(), "application/json");
} }
ref<FSAccessor> BinaryCacheStore::getFSAccessor() ref<SourceAccessor> BinaryCacheStore::getFSAccessor(bool requireValidPath)
{ {
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), localNarCache); return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()), requireValidPath, localNarCache);
} }
void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSet & sigs) void BinaryCacheStore::addSignatures(const StorePath & storePath, const StringSet & sigs)

View file

@ -148,7 +148,7 @@ public:
void narFromPath(const StorePath & path, Sink & sink) override; void narFromPath(const StorePath & path, Sink & sink) override;
ref<FSAccessor> getFSAccessor() override; ref<SourceAccessor> getFSAccessor(bool requireValidPath) override;
void addSignatures(const StorePath & storePath, const StringSet & sigs) override; void addSignatures(const StorePath & storePath, const StringSet & sigs) override;

View file

@ -6,7 +6,6 @@
#include "split.hh" #include "split.hh"
#include "common-protocol.hh" #include "common-protocol.hh"
#include "common-protocol-impl.hh" #include "common-protocol-impl.hh"
#include "fs-accessor.hh"
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>

View file

@ -72,7 +72,7 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
Callback<std::shared_ptr<const Realisation>> callback) noexcept override Callback<std::shared_ptr<const Realisation>> callback) noexcept override
{ callback(nullptr); } { callback(nullptr); }
virtual ref<FSAccessor> getFSAccessor() override virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
{ unsupported("getFSAccessor"); } { unsupported("getFSAccessor"); }
}; };

View file

@ -1,52 +0,0 @@
#pragma once
///@file
#include "types.hh"
namespace nix {
/**
* An abstract class for accessing a filesystem-like structure, such
* as a (possibly remote) Nix store or the contents of a NAR file.
*/
class FSAccessor
{
public:
enum Type { tMissing, tRegular, tSymlink, tDirectory };
struct Stat
{
Type type = tMissing;
/**
* regular files only
*/
uint64_t fileSize = 0;
/**
* regular files only
*/
bool isExecutable = false; // regular files only
/**
* regular files only
*/
uint64_t narOffset = 0; // regular files only
};
virtual ~FSAccessor() { }
virtual Stat stat(const Path & path) = 0;
virtual StringSet readDirectory(const Path & path) = 0;
/**
* Read a file inside the store.
*
* If `requireValidPath` is set to `true` (the default), the path must be
* inside a valid store path, otherwise it just needs to be physically
* present (but not necessarily properly registered)
*/
virtual std::string readFile(const Path & path, bool requireValidPath = true) = 0;
virtual std::string readLink(const Path & path) = 0;
};
}

View file

@ -363,7 +363,7 @@ public:
void ensurePath(const StorePath & path) override void ensurePath(const StorePath & path) override
{ unsupported("ensurePath"); } { unsupported("ensurePath"); }
virtual ref<FSAccessor> getFSAccessor() override virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
{ unsupported("getFSAccessor"); } { unsupported("getFSAccessor"); }
/** /**

View file

@ -1,5 +1,5 @@
#include "archive.hh" #include "archive.hh"
#include "fs-accessor.hh" #include "posix-source-accessor.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "globals.hh" #include "globals.hh"
@ -13,69 +13,53 @@ LocalFSStore::LocalFSStore(const Params & params)
{ {
} }
struct LocalStoreAccessor : public FSAccessor struct LocalStoreAccessor : PosixSourceAccessor
{ {
ref<LocalFSStore> store; ref<LocalFSStore> store;
bool requireValidPath;
LocalStoreAccessor(ref<LocalFSStore> store) : store(store) { } LocalStoreAccessor(ref<LocalFSStore> store, bool requireValidPath)
: store(store)
, requireValidPath(requireValidPath)
{ }
Path toRealPath(const Path & path, bool requireValidPath = true) CanonPath toRealPath(const CanonPath & path)
{ {
auto storePath = store->toStorePath(path).first; auto [storePath, rest] = store->toStorePath(path.abs());
if (requireValidPath && !store->isValidPath(storePath)) if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath)); throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
return store->getRealStoreDir() + std::string(path, store->storeDir.size()); return CanonPath(store->getRealStoreDir()) + storePath.to_string() + CanonPath(rest);
} }
FSAccessor::Stat stat(const Path & path) override std::optional<Stat> maybeLstat(const CanonPath & path) override
{ {
auto realPath = toRealPath(path); return PosixSourceAccessor::maybeLstat(toRealPath(path));
struct stat st;
if (lstat(realPath.c_str(), &st)) {
if (errno == ENOENT || errno == ENOTDIR) return {Type::tMissing, 0, false};
throw SysError("getting status of '%1%'", path);
} }
if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode)) DirEntries readDirectory(const CanonPath & path) override
throw Error("file '%1%' has unsupported type", path);
return {
S_ISREG(st.st_mode) ? Type::tRegular :
S_ISLNK(st.st_mode) ? Type::tSymlink :
Type::tDirectory,
S_ISREG(st.st_mode) ? (uint64_t) st.st_size : 0,
S_ISREG(st.st_mode) && st.st_mode & S_IXUSR};
}
StringSet readDirectory(const Path & path) override
{ {
auto realPath = toRealPath(path); return PosixSourceAccessor::readDirectory(toRealPath(path));
auto entries = nix::readDirectory(realPath);
StringSet res;
for (auto & entry : entries)
res.insert(entry.name);
return res;
} }
std::string readFile(const Path & path, bool requireValidPath = true) override void readFile(
const CanonPath & path,
Sink & sink,
std::function<void(uint64_t)> sizeCallback) override
{ {
return nix::readFile(toRealPath(path, requireValidPath)); return PosixSourceAccessor::readFile(toRealPath(path), sink, sizeCallback);
} }
std::string readLink(const Path & path) override std::string readLink(const CanonPath & path) override
{ {
return nix::readLink(toRealPath(path)); return PosixSourceAccessor::readLink(toRealPath(path));
} }
}; };
ref<FSAccessor> LocalFSStore::getFSAccessor() ref<SourceAccessor> LocalFSStore::getFSAccessor(bool requireValidPath)
{ {
return make_ref<LocalStoreAccessor>(ref<LocalFSStore>( return make_ref<LocalStoreAccessor>(ref<LocalFSStore>(
std::dynamic_pointer_cast<LocalFSStore>(shared_from_this()))); std::dynamic_pointer_cast<LocalFSStore>(shared_from_this())),
requireValidPath);
} }
void LocalFSStore::narFromPath(const StorePath & path, Sink & sink) void LocalFSStore::narFromPath(const StorePath & path, Sink & sink)

View file

@ -43,7 +43,7 @@ public:
LocalFSStore(const Params & params); LocalFSStore(const Params & params);
void narFromPath(const StorePath & path, Sink & sink) override; void narFromPath(const StorePath & path, Sink & sink) override;
ref<FSAccessor> getFSAccessor() override; ref<SourceAccessor> getFSAccessor(bool requireValidPath) override;
/** /**
* Creates symlink from the `gcRoot` to the `storePath` and * Creates symlink from the `gcRoot` to the `storePath` and

View file

@ -11,13 +11,7 @@ namespace nix {
struct NarMember struct NarMember
{ {
FSAccessor::Type type = FSAccessor::Type::tMissing; SourceAccessor::Stat stat;
bool isExecutable = false;
/* If this is a regular file, position of the contents of this
file in the NAR. */
uint64_t start = 0, size = 0;
std::string target; std::string target;
@ -25,7 +19,7 @@ struct NarMember
std::map<std::string, NarMember> children; std::map<std::string, NarMember> children;
}; };
struct NarAccessor : public FSAccessor struct NarAccessor : public SourceAccessor
{ {
std::optional<const std::string> nar; std::optional<const std::string> nar;
@ -57,7 +51,7 @@ struct NarAccessor : public FSAccessor
acc.root = std::move(member); acc.root = std::move(member);
parents.push(&acc.root); parents.push(&acc.root);
} else { } else {
if (parents.top()->type != FSAccessor::Type::tDirectory) if (parents.top()->stat.type != Type::tDirectory)
throw Error("NAR file missing parent directory of path '%s'", path); throw Error("NAR file missing parent directory of path '%s'", path);
auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member)); auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member));
parents.push(&result.first->second); parents.push(&result.first->second);
@ -66,12 +60,12 @@ struct NarAccessor : public FSAccessor
void createDirectory(const Path & path) override void createDirectory(const Path & path) override
{ {
createMember(path, {FSAccessor::Type::tDirectory, false, 0, 0}); createMember(path, {Type::tDirectory, false, 0, 0});
} }
void createRegularFile(const Path & path) override void createRegularFile(const Path & path) override
{ {
createMember(path, {FSAccessor::Type::tRegular, false, 0, 0}); createMember(path, {Type::tRegular, false, 0, 0});
} }
void closeRegularFile() override void closeRegularFile() override
@ -79,14 +73,15 @@ struct NarAccessor : public FSAccessor
void isExecutable() override void isExecutable() override
{ {
parents.top()->isExecutable = true; parents.top()->stat.isExecutable = true;
} }
void preallocateContents(uint64_t size) override void preallocateContents(uint64_t size) override
{ {
assert(size <= std::numeric_limits<uint64_t>::max()); assert(size <= std::numeric_limits<uint64_t>::max());
parents.top()->size = (uint64_t) size; auto & st = parents.top()->stat;
parents.top()->start = pos; st.fileSize = (uint64_t) size;
st.narOffset = pos;
} }
void receiveContents(std::string_view data) override void receiveContents(std::string_view data) override
@ -95,7 +90,9 @@ struct NarAccessor : public FSAccessor
void createSymlink(const Path & path, const std::string & target) override void createSymlink(const Path & path, const std::string & target) override
{ {
createMember(path, createMember(path,
NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target}); NarMember{
.stat = {.type = Type::tSymlink},
.target = target});
} }
size_t read(char * data, size_t len) override size_t read(char * data, size_t len) override
@ -130,18 +127,20 @@ struct NarAccessor : public FSAccessor
std::string type = v["type"]; std::string type = v["type"];
if (type == "directory") { if (type == "directory") {
member.type = FSAccessor::Type::tDirectory; member.stat = {.type = Type::tDirectory};
for (auto i = v["entries"].begin(); i != v["entries"].end(); ++i) { for (auto i = v["entries"].begin(); i != v["entries"].end(); ++i) {
std::string name = i.key(); std::string name = i.key();
recurse(member.children[name], i.value()); recurse(member.children[name], i.value());
} }
} else if (type == "regular") { } else if (type == "regular") {
member.type = FSAccessor::Type::tRegular; member.stat = {
member.size = v["size"]; .type = Type::tRegular,
member.isExecutable = v.value("executable", false); .fileSize = v["size"],
member.start = v["narOffset"]; .isExecutable = v.value("executable", false),
.narOffset = v["narOffset"]
};
} else if (type == "symlink") { } else if (type == "symlink") {
member.type = FSAccessor::Type::tSymlink; member.stat = {.type = Type::tSymlink};
member.target = v.value("target", ""); member.target = v.value("target", "");
} else return; } else return;
}; };
@ -150,134 +149,122 @@ struct NarAccessor : public FSAccessor
recurse(root, v); recurse(root, v);
} }
NarMember * find(const Path & path) NarMember * find(const CanonPath & path)
{ {
Path canon = path == "" ? "" : canonPath(path);
NarMember * current = &root; NarMember * current = &root;
auto end = path.end();
for (auto it = path.begin(); it != end; ) {
// because it != end, the remaining component is non-empty so we need
// a directory
if (current->type != FSAccessor::Type::tDirectory) return nullptr;
// skip slash (canonPath above ensures that this is always a slash) for (auto & i : path) {
assert(*it == '/'); if (current->stat.type != Type::tDirectory) return nullptr;
it += 1; auto child = current->children.find(std::string(i));
// lookup current component
auto next = std::find(it, end, '/');
auto child = current->children.find(std::string(it, next));
if (child == current->children.end()) return nullptr; if (child == current->children.end()) return nullptr;
current = &child->second; current = &child->second;
it = next;
} }
return current; return current;
} }
NarMember & get(const Path & path) { NarMember & get(const CanonPath & path) {
auto result = find(path); auto result = find(path);
if (result == nullptr) if (!result)
throw Error("NAR file does not contain path '%1%'", path); throw Error("NAR file does not contain path '%1%'", path);
return *result; return *result;
} }
Stat stat(const Path & path) override std::optional<Stat> maybeLstat(const CanonPath & path) override
{ {
auto i = find(path); auto i = find(path);
if (i == nullptr) if (!i)
return {FSAccessor::Type::tMissing, 0, false}; return std::nullopt;
return {i->type, i->size, i->isExecutable, i->start}; return i->stat;
} }
StringSet readDirectory(const Path & path) override DirEntries readDirectory(const CanonPath & path) override
{ {
auto i = get(path); auto i = get(path);
if (i.type != FSAccessor::Type::tDirectory) if (i.stat.type != Type::tDirectory)
throw Error("path '%1%' inside NAR file is not a directory", path); throw Error("path '%1%' inside NAR file is not a directory", path);
StringSet res; DirEntries res;
for (auto & child : i.children) for (auto & child : i.children)
res.insert(child.first); res.insert_or_assign(child.first, std::nullopt);
return res; return res;
} }
std::string readFile(const Path & path, bool requireValidPath = true) override std::string readFile(const CanonPath & path) override
{ {
auto i = get(path); auto i = get(path);
if (i.type != FSAccessor::Type::tRegular) if (i.stat.type != Type::tRegular)
throw Error("path '%1%' inside NAR file is not a regular file", path); throw Error("path '%1%' inside NAR file is not a regular file", path);
if (getNarBytes) return getNarBytes(i.start, i.size); if (getNarBytes) return getNarBytes(*i.stat.narOffset, *i.stat.fileSize);
assert(nar); assert(nar);
return std::string(*nar, i.start, i.size); return std::string(*nar, *i.stat.narOffset, *i.stat.fileSize);
} }
std::string readLink(const Path & path) override std::string readLink(const CanonPath & path) override
{ {
auto i = get(path); auto i = get(path);
if (i.type != FSAccessor::Type::tSymlink) if (i.stat.type != Type::tSymlink)
throw Error("path '%1%' inside NAR file is not a symlink", path); throw Error("path '%1%' inside NAR file is not a symlink", path);
return i.target; return i.target;
} }
}; };
ref<FSAccessor> makeNarAccessor(std::string && nar) ref<SourceAccessor> makeNarAccessor(std::string && nar)
{ {
return make_ref<NarAccessor>(std::move(nar)); return make_ref<NarAccessor>(std::move(nar));
} }
ref<FSAccessor> makeNarAccessor(Source & source) ref<SourceAccessor> makeNarAccessor(Source & source)
{ {
return make_ref<NarAccessor>(source); return make_ref<NarAccessor>(source);
} }
ref<FSAccessor> makeLazyNarAccessor(const std::string & listing, ref<SourceAccessor> makeLazyNarAccessor(const std::string & listing,
GetNarBytes getNarBytes) GetNarBytes getNarBytes)
{ {
return make_ref<NarAccessor>(listing, getNarBytes); return make_ref<NarAccessor>(listing, getNarBytes);
} }
using nlohmann::json; using nlohmann::json;
json listNar(ref<FSAccessor> accessor, const Path & path, bool recurse) json listNar(ref<SourceAccessor> accessor, const CanonPath & path, bool recurse)
{ {
auto st = accessor->stat(path); auto st = accessor->lstat(path);
json obj = json::object(); json obj = json::object();
switch (st.type) { switch (st.type) {
case FSAccessor::Type::tRegular: case SourceAccessor::Type::tRegular:
obj["type"] = "regular"; obj["type"] = "regular";
obj["size"] = st.fileSize; if (st.fileSize)
obj["size"] = *st.fileSize;
if (st.isExecutable) if (st.isExecutable)
obj["executable"] = true; obj["executable"] = true;
if (st.narOffset) if (st.narOffset && *st.narOffset)
obj["narOffset"] = st.narOffset; obj["narOffset"] = *st.narOffset;
break; break;
case FSAccessor::Type::tDirectory: case SourceAccessor::Type::tDirectory:
obj["type"] = "directory"; obj["type"] = "directory";
{ {
obj["entries"] = json::object(); obj["entries"] = json::object();
json &res2 = obj["entries"]; json &res2 = obj["entries"];
for (auto & name : accessor->readDirectory(path)) { for (auto & [name, type] : accessor->readDirectory(path)) {
if (recurse) { if (recurse) {
res2[name] = listNar(accessor, path + "/" + name, true); res2[name] = listNar(accessor, path + name, true);
} else } else
res2[name] = json::object(); res2[name] = json::object();
} }
} }
break; break;
case FSAccessor::Type::tSymlink: case SourceAccessor::Type::tSymlink:
obj["type"] = "symlink"; obj["type"] = "symlink";
obj["target"] = accessor->readLink(path); obj["target"] = accessor->readLink(path);
break; break;
case FSAccessor::Type::tMissing: case SourceAccessor::Type::tMisc:
default: assert(false); // cannot happen for NARs
throw Error("path '%s' does not exist in NAR", path);
} }
return obj; return obj;
} }

View file

@ -1,10 +1,11 @@
#pragma once #pragma once
///@file ///@file
#include "source-accessor.hh"
#include <functional> #include <functional>
#include <nlohmann/json_fwd.hpp> #include <nlohmann/json_fwd.hpp>
#include "fs-accessor.hh"
namespace nix { namespace nix {
@ -14,9 +15,9 @@ struct Source;
* Return an object that provides access to the contents of a NAR * Return an object that provides access to the contents of a NAR
* file. * file.
*/ */
ref<FSAccessor> makeNarAccessor(std::string && nar); ref<SourceAccessor> makeNarAccessor(std::string && nar);
ref<FSAccessor> makeNarAccessor(Source & source); ref<SourceAccessor> makeNarAccessor(Source & source);
/** /**
* Create a NAR accessor from a NAR listing (in the format produced by * Create a NAR accessor from a NAR listing (in the format produced by
@ -26,7 +27,7 @@ ref<FSAccessor> makeNarAccessor(Source & source);
*/ */
typedef std::function<std::string(uint64_t, uint64_t)> GetNarBytes; typedef std::function<std::string(uint64_t, uint64_t)> GetNarBytes;
ref<FSAccessor> makeLazyNarAccessor( ref<SourceAccessor> makeLazyNarAccessor(
const std::string & listing, const std::string & listing,
GetNarBytes getNarBytes); GetNarBytes getNarBytes);
@ -34,6 +35,6 @@ ref<FSAccessor> makeLazyNarAccessor(
* Write a JSON representation of the contents of a NAR (except file * Write a JSON representation of the contents of a NAR (except file
* contents). * contents).
*/ */
nlohmann::json listNar(ref<FSAccessor> accessor, const Path & path, bool recurse); nlohmann::json listNar(ref<SourceAccessor> accessor, const CanonPath & path, bool recurse);
} }

View file

@ -8,8 +8,9 @@
namespace nix { namespace nix {
RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, const Path & cacheDir) RemoteFSAccessor::RemoteFSAccessor(ref<Store> store, bool requireValidPath, const Path & cacheDir)
: store(store) : store(store)
, requireValidPath(requireValidPath)
, cacheDir(cacheDir) , cacheDir(cacheDir)
{ {
if (cacheDir != "") if (cacheDir != "")
@ -22,7 +23,7 @@ Path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::strin
return fmt("%s/%s.%s", cacheDir, hashPart, ext); return fmt("%s/%s.%s", cacheDir, hashPart, ext);
} }
ref<FSAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar) ref<SourceAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar)
{ {
if (cacheDir != "") { if (cacheDir != "") {
try { try {
@ -38,7 +39,7 @@ ref<FSAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::str
if (cacheDir != "") { if (cacheDir != "") {
try { try {
nlohmann::json j = listNar(narAccessor, "", true); nlohmann::json j = listNar(narAccessor, CanonPath::root, true);
writeFile(makeCacheFile(hashPart, "ls"), j.dump()); writeFile(makeCacheFile(hashPart, "ls"), j.dump());
} catch (...) { } catch (...) {
ignoreException(); ignoreException();
@ -48,11 +49,10 @@ ref<FSAccessor> RemoteFSAccessor::addToCache(std::string_view hashPart, std::str
return narAccessor; return narAccessor;
} }
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath) std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPath & path)
{ {
auto path = canonPath(path_); auto [storePath, restPath_] = store->toStorePath(path.abs());
auto restPath = CanonPath(restPath_);
auto [storePath, restPath] = store->toStorePath(path);
if (requireValidPath && !store->isValidPath(storePath)) if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath)); throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
@ -63,7 +63,7 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
std::string listing; std::string listing;
Path cacheFile; Path cacheFile;
if (cacheDir != "" && pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) { if (cacheDir != "" && nix::pathExists(cacheFile = makeCacheFile(storePath.hashPart(), "nar"))) {
try { try {
listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls")); listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls"));
@ -101,25 +101,25 @@ std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, boo
return {addToCache(storePath.hashPart(), std::move(sink.s)), restPath}; return {addToCache(storePath.hashPart(), std::move(sink.s)), restPath};
} }
FSAccessor::Stat RemoteFSAccessor::stat(const Path & path) std::optional<SourceAccessor::Stat> RemoteFSAccessor::maybeLstat(const CanonPath & path)
{ {
auto res = fetch(path); auto res = fetch(path);
return res.first->stat(res.second); return res.first->maybeLstat(res.second);
} }
StringSet RemoteFSAccessor::readDirectory(const Path & path) SourceAccessor::DirEntries RemoteFSAccessor::readDirectory(const CanonPath & path)
{ {
auto res = fetch(path); auto res = fetch(path);
return res.first->readDirectory(res.second); return res.first->readDirectory(res.second);
} }
std::string RemoteFSAccessor::readFile(const Path & path, bool requireValidPath) std::string RemoteFSAccessor::readFile(const CanonPath & path)
{ {
auto res = fetch(path, requireValidPath); auto res = fetch(path);
return res.first->readFile(res.second); return res.first->readFile(res.second);
} }
std::string RemoteFSAccessor::readLink(const Path & path) std::string RemoteFSAccessor::readLink(const CanonPath & path)
{ {
auto res = fetch(path); auto res = fetch(path);
return res.first->readLink(res.second); return res.first->readLink(res.second);

View file

@ -1,40 +1,43 @@
#pragma once #pragma once
///@file ///@file
#include "fs-accessor.hh" #include "source-accessor.hh"
#include "ref.hh" #include "ref.hh"
#include "store-api.hh" #include "store-api.hh"
namespace nix { namespace nix {
class RemoteFSAccessor : public FSAccessor class RemoteFSAccessor : public SourceAccessor
{ {
ref<Store> store; ref<Store> store;
std::map<std::string, ref<FSAccessor>> nars; std::map<std::string, ref<SourceAccessor>> nars;
bool requireValidPath;
Path cacheDir; Path cacheDir;
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_, bool requireValidPath = true); std::pair<ref<SourceAccessor>, CanonPath> fetch(const CanonPath & path);
friend class BinaryCacheStore; friend class BinaryCacheStore;
Path makeCacheFile(std::string_view hashPart, const std::string & ext); Path makeCacheFile(std::string_view hashPart, const std::string & ext);
ref<FSAccessor> addToCache(std::string_view hashPart, std::string && nar); ref<SourceAccessor> addToCache(std::string_view hashPart, std::string && nar);
public: public:
RemoteFSAccessor(ref<Store> store, RemoteFSAccessor(ref<Store> store,
bool requireValidPath = true,
const /* FIXME: use std::optional */ Path & cacheDir = ""); const /* FIXME: use std::optional */ Path & cacheDir = "");
Stat stat(const Path & path) override; std::optional<Stat> maybeLstat(const CanonPath & path) override;
StringSet readDirectory(const Path & path) override; DirEntries readDirectory(const CanonPath & path) override;
std::string readFile(const Path & path, bool requireValidPath = true) override; std::string readFile(const CanonPath & path) override;
std::string readLink(const Path & path) override; std::string readLink(const CanonPath & path) override;
}; };
} }

View file

@ -970,7 +970,7 @@ void RemoteStore::narFromPath(const StorePath & path, Sink & sink)
copyNAR(conn->from, sink); copyNAR(conn->from, sink);
} }
ref<FSAccessor> RemoteStore::getFSAccessor() ref<SourceAccessor> RemoteStore::getFSAccessor(bool requireValidPath)
{ {
return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this())); return make_ref<RemoteFSAccessor>(ref<Store>(shared_from_this()));
} }

View file

@ -185,7 +185,7 @@ protected:
friend struct ConnectionHandle; friend struct ConnectionHandle;
virtual ref<FSAccessor> getFSAccessor() override; virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath) override;
virtual void narFromPath(const StorePath & path, Sink & sink) override; virtual void narFromPath(const StorePath & path, Sink & sink) override;

View file

@ -1,5 +1,5 @@
#include "crypto.hh" #include "crypto.hh"
#include "fs-accessor.hh" #include "source-accessor.hh"
#include "globals.hh" #include "globals.hh"
#include "derivations.hh" #include "derivations.hh"
#include "store-api.hh" #include "store-api.hh"
@ -1338,12 +1338,12 @@ Derivation Store::derivationFromPath(const StorePath & drvPath)
return readDerivation(drvPath); return readDerivation(drvPath);
} }
Derivation readDerivationCommon(Store& store, const StorePath& drvPath, bool requireValidPath) static Derivation readDerivationCommon(Store & store, const StorePath & drvPath, bool requireValidPath)
{ {
auto accessor = store.getFSAccessor(); auto accessor = store.getFSAccessor(requireValidPath);
try { try {
return parseDerivation(store, return parseDerivation(store,
accessor->readFile(store.printStorePath(drvPath), requireValidPath), accessor->readFile(CanonPath(store.printStorePath(drvPath))),
Derivation::nameFromPath(drvPath)); Derivation::nameFromPath(drvPath));
} catch (FormatError & e) { } catch (FormatError & e) {
throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg()); throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg());

View file

@ -70,7 +70,7 @@ MakeError(InvalidStoreURI, Error);
struct BasicDerivation; struct BasicDerivation;
struct Derivation; struct Derivation;
class FSAccessor; struct SourceAccessor;
class NarInfoDiskCache; class NarInfoDiskCache;
class Store; class Store;
@ -703,7 +703,7 @@ public:
/** /**
* @return An object to access files in the Nix store. * @return An object to access files in the Nix store.
*/ */
virtual ref<FSAccessor> getFSAccessor() = 0; virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath = true) = 0;
/** /**
* Repair the contents of the given path by redownloading it using * Repair the contents of the given path by redownloading it using

View file

@ -35,8 +35,8 @@ public:
static std::set<std::string> uriSchemes() static std::set<std::string> uriSchemes()
{ return {"unix"}; } { return {"unix"}; }
ref<FSAccessor> getFSAccessor() override ref<SourceAccessor> getFSAccessor(bool requireValidPath) override
{ return LocalFSStore::getFSAccessor(); } { return LocalFSStore::getFSAccessor(requireValidPath); }
void narFromPath(const StorePath & path, Sink & sink) override void narFromPath(const StorePath & path, Sink & sink) override
{ LocalFSStore::narFromPath(path, sink); } { LocalFSStore::narFromPath(path, sink); }

View file

@ -44,6 +44,11 @@ inline std::string fmt(const std::string & s)
return s; return s;
} }
inline std::string fmt(std::string_view s)
{
return std::string(s);
}
inline std::string fmt(const char * s) inline std::string fmt(const char * s)
{ {
return s; return s;

View file

@ -44,9 +44,13 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path)
return nix::pathExists(path.abs()); return nix::pathExists(path.abs());
} }
SourceAccessor::Stat PosixSourceAccessor::lstat(const CanonPath & path) std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonPath & path)
{ {
auto st = nix::lstat(path.abs()); struct stat st;
if (::lstat(path.c_str(), &st)) {
if (errno == ENOENT) return std::nullopt;
throw SysError("getting status of '%s'", showPath(path));
}
mtime = std::max(mtime, st.st_mtime); mtime = std::max(mtime, st.st_mtime);
return Stat { return Stat {
.type = .type =
@ -54,7 +58,8 @@ SourceAccessor::Stat PosixSourceAccessor::lstat(const CanonPath & path)
S_ISDIR(st.st_mode) ? tDirectory : S_ISDIR(st.st_mode) ? tDirectory :
S_ISLNK(st.st_mode) ? tSymlink : S_ISLNK(st.st_mode) ? tSymlink :
tMisc, tMisc,
.isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR .fileSize = S_ISREG(st.st_mode) ? std::optional<uint64_t>(st.st_size) : std::nullopt,
.isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR,
}; };
} }

View file

@ -22,7 +22,7 @@ struct PosixSourceAccessor : SourceAccessor
bool pathExists(const CanonPath & path) override; bool pathExists(const CanonPath & path) override;
Stat lstat(const CanonPath & path) override; std::optional<Stat> maybeLstat(const CanonPath & path) override;
DirEntries readDirectory(const CanonPath & path) override; DirEntries readDirectory(const CanonPath & path) override;

View file

@ -10,6 +10,11 @@ SourceAccessor::SourceAccessor()
{ {
} }
bool SourceAccessor::pathExists(const CanonPath & path)
{
return maybeLstat(path).has_value();
}
std::string SourceAccessor::readFile(const CanonPath & path) std::string SourceAccessor::readFile(const CanonPath & path)
{ {
StringSink sink; StringSink sink;
@ -42,12 +47,12 @@ Hash SourceAccessor::hashPath(
return sink.finish().first; return sink.finish().first;
} }
std::optional<SourceAccessor::Stat> SourceAccessor::maybeLstat(const CanonPath & path) SourceAccessor::Stat SourceAccessor::lstat(const CanonPath & path)
{ {
// FIXME: merge these into one operation. if (auto st = maybeLstat(path))
if (!pathExists(path)) return *st;
return {}; else
return lstat(path); throw Error("path '%s' does not exist", showPath(path));
} }
std::string SourceAccessor::showPath(const CanonPath & path) std::string SourceAccessor::showPath(const CanonPath & path)

View file

@ -40,7 +40,7 @@ struct SourceAccessor
Sink & sink, Sink & sink,
std::function<void(uint64_t)> sizeCallback = [](uint64_t size){}); std::function<void(uint64_t)> sizeCallback = [](uint64_t size){});
virtual bool pathExists(const CanonPath & path) = 0; virtual bool pathExists(const CanonPath & path);
enum Type { enum Type {
tRegular, tSymlink, tDirectory, tRegular, tSymlink, tDirectory,
@ -57,13 +57,29 @@ struct SourceAccessor
struct Stat struct Stat
{ {
Type type = tMisc; Type type = tMisc;
//uint64_t fileSize = 0; // regular files only
bool isExecutable = false; // regular files only /**
* For regular files only: the size of the file. Not all
* accessors return this since it may be too expensive to
* compute.
*/
std::optional<uint64_t> fileSize;
/**
* For regular files only: whether this is an executable.
*/
bool isExecutable = false;
/**
* For regular files only: the position of the contents of this
* file in the NAR. Only returned by NAR accessors.
*/
std::optional<uint64_t> narOffset;
}; };
virtual Stat lstat(const CanonPath & path) = 0; Stat lstat(const CanonPath & path);
std::optional<Stat> maybeLstat(const CanonPath & path); virtual std::optional<Stat> maybeLstat(const CanonPath & path) = 0;
typedef std::optional<Type> DirEntry; typedef std::optional<Type> DirEntry;

View file

@ -4,7 +4,6 @@
#include "shared.hh" #include "shared.hh"
#include "store-api.hh" #include "store-api.hh"
#include "local-fs-store.hh" #include "local-fs-store.hh"
#include "fs-accessor.hh"
#include "eval-inline.hh" #include "eval-inline.hh"
using namespace nix; using namespace nix;

View file

@ -1,6 +1,5 @@
#include "command.hh" #include "command.hh"
#include "store-api.hh" #include "store-api.hh"
#include "fs-accessor.hh"
#include "nar-accessor.hh" #include "nar-accessor.hh"
using namespace nix; using namespace nix;
@ -9,15 +8,12 @@ struct MixCat : virtual Args
{ {
std::string path; std::string path;
void cat(ref<FSAccessor> accessor) void cat(ref<SourceAccessor> accessor)
{ {
auto st = accessor->stat(path); auto st = accessor->lstat(CanonPath(path));
if (st.type == FSAccessor::Type::tMissing) if (st.type != SourceAccessor::Type::tRegular)
throw Error("path '%1%' does not exist", path);
if (st.type != FSAccessor::Type::tRegular)
throw Error("path '%1%' is not a regular file", path); throw Error("path '%1%' is not a regular file", path);
writeFull(STDOUT_FILENO, accessor->readFile(CanonPath(path)));
writeFull(STDOUT_FILENO, accessor->readFile(path));
} }
}; };

View file

@ -1,6 +1,5 @@
#include "command.hh" #include "command.hh"
#include "store-api.hh" #include "store-api.hh"
#include "fs-accessor.hh"
#include "nar-accessor.hh" #include "nar-accessor.hh"
#include "common-args.hh" #include "common-args.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -39,61 +38,58 @@ struct MixLs : virtual Args, MixJSON
}); });
} }
void listText(ref<FSAccessor> accessor) void listText(ref<SourceAccessor> accessor)
{ {
std::function<void(const FSAccessor::Stat &, const Path &, const std::string &, bool)> doPath; std::function<void(const SourceAccessor::Stat &, const CanonPath &, std::string_view, bool)> doPath;
auto showFile = [&](const Path & curPath, const std::string & relPath) { auto showFile = [&](const CanonPath & curPath, std::string_view relPath) {
if (verbose) { if (verbose) {
auto st = accessor->stat(curPath); auto st = accessor->lstat(curPath);
std::string tp = std::string tp =
st.type == FSAccessor::Type::tRegular ? st.type == SourceAccessor::Type::tRegular ?
(st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") : (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") :
st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx" : st.type == SourceAccessor::Type::tSymlink ? "lrwxrwxrwx" :
"dr-xr-xr-x"; "dr-xr-xr-x";
auto line = fmt("%s %20d %s", tp, st.fileSize, relPath); auto line = fmt("%s %20d %s", tp, st.fileSize.value_or(0), relPath);
if (st.type == FSAccessor::Type::tSymlink) if (st.type == SourceAccessor::Type::tSymlink)
line += " -> " + accessor->readLink(curPath); line += " -> " + accessor->readLink(curPath);
logger->cout(line); logger->cout(line);
if (recursive && st.type == FSAccessor::Type::tDirectory) if (recursive && st.type == SourceAccessor::Type::tDirectory)
doPath(st, curPath, relPath, false); doPath(st, curPath, relPath, false);
} else { } else {
logger->cout(relPath); logger->cout(relPath);
if (recursive) { if (recursive) {
auto st = accessor->stat(curPath); auto st = accessor->lstat(curPath);
if (st.type == FSAccessor::Type::tDirectory) if (st.type == SourceAccessor::Type::tDirectory)
doPath(st, curPath, relPath, false); doPath(st, curPath, relPath, false);
} }
} }
}; };
doPath = [&](const FSAccessor::Stat & st, const Path & curPath, doPath = [&](const SourceAccessor::Stat & st, const CanonPath & curPath,
const std::string & relPath, bool showDirectory) std::string_view relPath, bool showDirectory)
{ {
if (st.type == FSAccessor::Type::tDirectory && !showDirectory) { if (st.type == SourceAccessor::Type::tDirectory && !showDirectory) {
auto names = accessor->readDirectory(curPath); auto names = accessor->readDirectory(curPath);
for (auto & name : names) for (auto & [name, type] : names)
showFile(curPath + "/" + name, relPath + "/" + name); showFile(curPath + name, relPath + "/" + name);
} else } else
showFile(curPath, relPath); showFile(curPath, relPath);
}; };
auto st = accessor->stat(path); auto path2 = CanonPath(path);
if (st.type == FSAccessor::Type::tMissing) auto st = accessor->lstat(path2);
throw Error("path '%1%' does not exist", path); doPath(st, path2,
doPath(st, path, st.type == SourceAccessor::Type::tDirectory ? "." : path2.baseName().value_or(""),
st.type == FSAccessor::Type::tDirectory ? "." : std::string(baseNameOf(path)),
showDirectory); showDirectory);
} }
void list(ref<FSAccessor> accessor) void list(ref<SourceAccessor> accessor)
{ {
if (path == "/") path = "";
if (json) { if (json) {
if (showDirectory) if (showDirectory)
throw UsageError("'--directory' is useless with '--json'"); throw UsageError("'--directory' is useless with '--json'");
logger->cout("%s", listNar(accessor, path, recursive)); logger->cout("%s", listNar(accessor, CanonPath(path), recursive));
} else } else
listText(accessor); listText(accessor);
} }

View file

@ -6,7 +6,7 @@
#include "derivations.hh" #include "derivations.hh"
#include "local-store.hh" #include "local-store.hh"
#include "finally.hh" #include "finally.hh"
#include "fs-accessor.hh" #include "source-accessor.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
#include "eval.hh" #include "eval.hh"
#include "build/personality.hh" #include "build/personality.hh"
@ -119,9 +119,9 @@ struct CmdShell : InstallablesCommand, MixEnvironment
if (true) if (true)
unixPath.push_front(store->printStorePath(path) + "/bin"); unixPath.push_front(store->printStorePath(path) + "/bin");
auto propPath = store->printStorePath(path) + "/nix-support/propagated-user-env-packages"; auto propPath = CanonPath(store->printStorePath(path)) + "nix-support" + "propagated-user-env-packages";
if (accessor->stat(propPath).type == FSAccessor::tRegular) { if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) {
for (auto & p : tokenizeString<Paths>(readFile(propPath))) for (auto & p : tokenizeString<Paths>(accessor->readFile(propPath)))
todo.push(store->parseStorePath(p)); todo.push(store->parseStorePath(p));
} }
} }

View file

@ -1,7 +1,7 @@
#include "command.hh" #include "command.hh"
#include "store-api.hh" #include "store-api.hh"
#include "progress-bar.hh" #include "progress-bar.hh"
#include "fs-accessor.hh" #include "source-accessor.hh"
#include "shared.hh" #include "shared.hh"
#include <queue> #include <queue>
@ -175,7 +175,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
struct BailOut { }; struct BailOut { };
printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) { printNode = [&](Node & node, const std::string & firstPad, const std::string & tailPad) {
auto pathS = store->printStorePath(node.path); CanonPath pathS(store->printStorePath(node.path));
assert(node.dist != inf); assert(node.dist != inf);
if (precise) { if (precise) {
@ -183,7 +183,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
firstPad, firstPad,
node.visited ? "\e[38;5;244m" : "", node.visited ? "\e[38;5;244m" : "",
firstPad != "" ? "" : "", firstPad != "" ? "" : "",
pathS); pathS.abs());
} }
if (node.path == dependencyPath && !all if (node.path == dependencyPath && !all
@ -210,24 +210,25 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
contain the reference. */ contain the reference. */
std::map<std::string, Strings> hits; std::map<std::string, Strings> hits;
std::function<void(const Path &)> visitPath; std::function<void(const CanonPath &)> visitPath;
visitPath = [&](const Path & p) { visitPath = [&](const CanonPath & p) {
auto st = accessor->stat(p); auto st = accessor->maybeLstat(p);
assert(st);
auto p2 = p == pathS ? "/" : std::string(p, pathS.size() + 1); auto p2 = p == pathS ? "/" : p.abs().substr(pathS.abs().size() + 1);
auto getColour = [&](const std::string & hash) { auto getColour = [&](const std::string & hash) {
return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE; return hash == dependencyPathHash ? ANSI_GREEN : ANSI_BLUE;
}; };
if (st.type == FSAccessor::Type::tDirectory) { if (st->type == SourceAccessor::Type::tDirectory) {
auto names = accessor->readDirectory(p); auto names = accessor->readDirectory(p);
for (auto & name : names) for (auto & [name, type] : names)
visitPath(p + "/" + name); visitPath(p + name);
} }
else if (st.type == FSAccessor::Type::tRegular) { else if (st->type == SourceAccessor::Type::tRegular) {
auto contents = accessor->readFile(p); auto contents = accessor->readFile(p);
for (auto & hash : hashes) { for (auto & hash : hashes) {
@ -245,7 +246,7 @@ struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
} }
} }
else if (st.type == FSAccessor::Type::tSymlink) { else if (st->type == SourceAccessor::Type::tSymlink) {
auto target = accessor->readLink(p); auto target = accessor->readLink(p);
for (auto & hash : hashes) { for (auto & hash : hashes) {

View file

@ -1 +1 @@
error: getting status of '/pwd/lang/fnord': No such file or directory error: path '/pwd/lang/fnord' does not exist

View file

@ -1 +1 @@
error: getting status of '/pwd/lang/fnord': No such file or directory error: path '/pwd/lang/fnord' does not exist

View file

@ -25,6 +25,12 @@ diff -u baz.cat-nar $storePath/foo/baz
nix store cat $storePath/foo/baz > baz.cat-nar nix store cat $storePath/foo/baz > baz.cat-nar
diff -u baz.cat-nar $storePath/foo/baz diff -u baz.cat-nar $storePath/foo/baz
# Check that 'nix store cat' fails on invalid store paths.
invalidPath="$(dirname $storePath)/99999999999999999999999999999999-foo"
mv $storePath $invalidPath
expect 1 nix store cat $invalidPath/foo/baz
mv $invalidPath $storePath
# Test --json. # Test --json.
diff -u \ diff -u \
<(nix nar ls --json $narFile / | jq -S) \ <(nix nar ls --json $narFile / | jq -S) \
@ -46,7 +52,7 @@ diff -u \
<(echo '{"type":"regular","size":0}' | jq -S) <(echo '{"type":"regular","size":0}' | jq -S)
# Test missing files. # Test missing files.
expect 1 nix store ls --json -R $storePath/xyzzy 2>&1 | grep 'does not exist in NAR' expect 1 nix store ls --json -R $storePath/xyzzy 2>&1 | grep 'does not exist'
expect 1 nix store ls $storePath/xyzzy 2>&1 | grep 'does not exist' expect 1 nix store ls $storePath/xyzzy 2>&1 | grep 'does not exist'
# Test failure to dump. # Test failure to dump.