Skip to content
6 changes: 3 additions & 3 deletions src/libcmd/common-eval-args.cc
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ EvalSettings evalSettings {
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath);
return state.rootPath(state.store->toRealPath(storePath));
return state.rootPath(state.store->printStorePath(storePath));
},
},
},
Expand Down Expand Up @@ -179,7 +179,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
state.fetchSettings,
EvalSettings::resolvePseudoUrl(s));
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy);
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
return state.rootPath(CanonPath(state.store->printStorePath(storePath)));
}

else if (hasPrefix(s, "flake:")) {
Expand All @@ -188,7 +188,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
auto storePath = nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
state.allowPath(storePath);
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
return state.rootPath(CanonPath(state.store->printStorePath(storePath)));
}

else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
Expand Down
63 changes: 40 additions & 23 deletions src/libexpr/eval.cc
Original file line number Diff line number Diff line change
Expand Up @@ -246,15 +246,42 @@ EvalState::EvalState(
, repair(NoRepair)
, emptyBindings(0)
, rootFS(
settings.restrictEval || settings.pureEval
? ref<SourceAccessor>(AllowListSourceAccessor::create(getFSSourceAccessor(), {},
[&settings](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = settings.pureEval
? "in pure evaluation mode (use '--impure' to override)"
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
}))
: getFSSourceAccessor())
({
/* In pure eval mode, we provide a filesystem that only
contains the Nix store.

If we have a chroot store and pure eval is not enabled,
use a union accessor to make the chroot store available
at its logical location while still having the
underlying directory available. This is necessary for
instance if we're evaluating a file from the physical
/nix/store while using a chroot store. */
auto accessor = getFSSourceAccessor();

auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy));
if (settings.pureEval || store->storeDir != realStoreDir) {
auto storeFS = makeMountedSourceAccessor(
{
{CanonPath::root, makeEmptySourceAccessor()},
{CanonPath(store->storeDir), makeFSSourceAccessor(realStoreDir)}
});
accessor = settings.pureEval
? storeFS
: makeUnionSourceAccessor({accessor, storeFS});
}

/* Apply access control if needed. */
if (settings.restrictEval || settings.pureEval)
accessor = AllowListSourceAccessor::create(accessor, {},
[&settings](const CanonPath & path) -> RestrictedPathError {
auto modeInformation = settings.pureEval
? "in pure evaluation mode (use '--impure' to override)"
: "in restricted mode";
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
});

accessor;
}))
, corepkgsFS(make_ref<MemorySourceAccessor>())
, internalFS(make_ref<MemorySourceAccessor>())
, derivationInternal{corepkgsFS->addFile(
Expand Down Expand Up @@ -344,7 +371,7 @@ void EvalState::allowPath(const Path & path)
void EvalState::allowPath(const StorePath & storePath)
{
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
rootFS2->allowPrefix(CanonPath(store->printStorePath(storePath)));
}

void EvalState::allowClosure(const StorePath & storePath)
Expand Down Expand Up @@ -422,16 +449,6 @@ void EvalState::checkURI(const std::string & uri)
}


Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
{
// FIXME: check whether 'path' is in 'context'.
return
!context.empty() && store->isInStore(path)
? store->toRealPath(path)
: path;
}


Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
{
Value * v2 = allocValue();
Expand Down Expand Up @@ -2051,7 +2068,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
else if (firstType == nPath) {
if (!context.empty())
state.error<EvalError>("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow();
v.mkPath(state.rootPath(CanonPath(canonPath(str()))));
v.mkPath(state.rootPath(CanonPath(str())));
} else
v.mkStringMove(c_str(), context);
}
Expand Down Expand Up @@ -2432,7 +2449,7 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
if (path == "" || path[0] != '/')
error<EvalError>("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow();
return rootPath(CanonPath(path));
return rootPath(path);
}


Expand Down Expand Up @@ -3086,7 +3103,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
fetchSettings,
EvalSettings::resolvePseudoUrl(value));
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
return finish(rootPath(store->toRealPath(storePath)));
return finish(rootPath(store->printStorePath(storePath)));
} catch (Error & e) {
logWarning({
.msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)
Expand Down
11 changes: 0 additions & 11 deletions src/libexpr/eval.hh
Original file line number Diff line number Diff line change
Expand Up @@ -412,17 +412,6 @@ public:

void checkURI(const std::string & uri);

/**
* When using a diverted store and 'path' is in the Nix store, map
* 'path' to the diverted location (e.g. /nix/store/foo is mapped
* to /home/alice/my-nix/nix/store/foo). However, this is only
* done if the context is not empty, since otherwise we're
* probably trying to read from the actual /nix/store. This is
* intended to distinguish between import-from-derivation and
* sources stored in the actual /nix/store.
*/
Path toRealPath(const Path & path, const NixStringContext & context);

/**
* Parse a Nix expression from the specified file.
*/
Expand Down
13 changes: 2 additions & 11 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ static SourcePath realisePath(EvalState & state, const PosIdx pos, Value & v, st
try {
if (!context.empty() && path.accessor == state.rootFS) {
auto rewrites = state.realiseContext(context);
auto realPath = state.toRealPath(rewriteStrings(path.path.abs(), rewrites), context);
path = {path.accessor, CanonPath(realPath)};
path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))};
}
return resolveSymlinks ? path.resolveSymlinks(*resolveSymlinks) : path;
} catch (Error & e) {
Expand Down Expand Up @@ -2485,15 +2484,7 @@ static void addPath(
// FIXME: handle CA derivation outputs (where path needs to
// be rewritten to the actual output).
auto rewrites = state.realiseContext(context);
path = {state.rootFS, CanonPath(state.toRealPath(rewriteStrings(path.path.abs(), rewrites), context))};

try {
auto [storePath, subPath] = state.store->toStorePath(path.path.abs());
// FIXME: we should scanForReferences on the path before adding it
refs = state.store->queryPathInfo(storePath)->references;
path = {state.rootFS, CanonPath(state.store->toRealPath(storePath) + subPath)};
} catch (Error &) { // FIXME: should be InvalidPathError
}
Comment on lines -2488 to -2496
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the idea that refs is unused? If so, let's also delete the variable above? If not... what's going on?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a regression in....ea38605?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this appears to be dead code. I think the idea was to propagate the references, but that "feature" was never really well thought out. Maybe we should warn/fail if the path has references...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can investigate what happened here later. IIRC there was some regressions with this too.

path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))};
}

std::unique_ptr<PathFilter> filter;
Expand Down
23 changes: 4 additions & 19 deletions src/libflake/flake/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ static Flake readFlake(
auto storePath = fetchToStore(*state.store, setting.value->path(), FetchMode::Copy);
flake.config.settings.emplace(
state.symbols[setting.name],
state.store->toRealPath(storePath));
state.store->printStorePath(storePath));
}
else if (setting.value->type() == nInt)
flake.config.settings.emplace(
Expand Down Expand Up @@ -423,7 +423,7 @@ static Flake getFlake(
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, accessor);

// Re-parse flake.nix from the store.
return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootAttrPath);
return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->printStorePath(storePath)), lockRootAttrPath);
}

Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries)
Expand Down Expand Up @@ -784,7 +784,7 @@ LockedFlake lockFlake(
// FIXME: allow input to be lazy.
auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, accessor);

return {state.rootPath(state.store->toRealPath(storePath)), lockedRef};
return {state.rootPath(state.store->printStorePath(storePath)), lockedRef};
}
}();

Expand Down Expand Up @@ -921,21 +921,6 @@ LockedFlake lockFlake(
}
}

std::pair<StorePath, Path> sourcePathToStorePath(
ref<Store> store,
const SourcePath & _path)
{
auto path = _path.path.abs();

if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
auto realStoreDir = store2->getRealStoreDir();
if (isInDir(path, realStoreDir))
path = store2->storeDir + path.substr(realStoreDir.size());
}

return store->toStorePath(path);
}

void callFlake(EvalState & state,
const LockedFlake & lockedFlake,
Value & vRes)
Expand All @@ -953,7 +938,7 @@ void callFlake(EvalState & state,

auto lockedNode = node.dynamic_pointer_cast<const LockedNode>();

auto [storePath, subdir] = sourcePathToStorePath(state.store, sourcePath);
auto [storePath, subdir] = state.store->toStorePath(sourcePath.path.abs());

emitTreeAttrs(
state,
Expand Down
10 changes: 0 additions & 10 deletions src/libflake/flake/flake.hh
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,6 @@ void callFlake(
const LockedFlake & lockedFlake,
Value & v);

/**
* Map a `SourcePath` to the corresponding store path. This is a
* temporary hack to support chroot stores while we don't have full
* lazy trees. FIXME: Remove this once we can pass a sourcePath rather
* than a storePath to call-flake.nix.
*/
std::pair<StorePath, Path> sourcePathToStorePath(
ref<Store> store,
const SourcePath & path);

}

void emitTreeAttrs(
Expand Down
1 change: 1 addition & 0 deletions src/libutil/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ sources = files(
'tarfile.cc',
'terminal.cc',
'thread-pool.cc',
'union-source-accessor.cc',
'unix-domain-socket.cc',
'url.cc',
'users.cc',
Expand Down
12 changes: 6 additions & 6 deletions src/libutil/mounted-source-accessor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ struct MountedSourceAccessor : SourceAccessor
return accessor->readFile(subpath);
}

bool pathExists(const CanonPath & path) override
{
auto [accessor, subpath] = resolve(path);
return accessor->pathExists(subpath);
}

std::optional<Stat> maybeLstat(const CanonPath & path) override
{
auto [accessor, subpath] = resolve(path);
Expand Down Expand Up @@ -69,6 +63,12 @@ struct MountedSourceAccessor : SourceAccessor
path.pop();
}
}

std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
{
auto [accessor, subpath] = resolve(path);
return accessor->getPhysicalPath(subpath);
}
};

ref<SourceAccessor> makeMountedSourceAccessor(std::map<CanonPath, ref<SourceAccessor>> mounts)
Expand Down
6 changes: 6 additions & 0 deletions src/libutil/source-accessor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,10 @@ ref<SourceAccessor> makeFSSourceAccessor(std::filesystem::path root);

ref<SourceAccessor> makeMountedSourceAccessor(std::map<CanonPath, ref<SourceAccessor>> mounts);

/**
* Construct an accessor that presents a "union" view of a vector of
* underlying accessors. Earlier accessors take precedence over later.
*/
ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> && accessors);

}
82 changes: 82 additions & 0 deletions src/libutil/union-source-accessor.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#include "source-accessor.hh"

namespace nix {

struct UnionSourceAccessor : SourceAccessor
{
std::vector<ref<SourceAccessor>> accessors;

UnionSourceAccessor(std::vector<ref<SourceAccessor>> _accessors)
: accessors(std::move(_accessors))
{
displayPrefix.clear();
}

std::string readFile(const CanonPath & path) override
{
for (auto & accessor : accessors) {
auto st = accessor->maybeLstat(path);
if (st)
return accessor->readFile(path);
}
throw FileNotFound("path '%s' does not exist", showPath(path));
}

std::optional<Stat> maybeLstat(const CanonPath & path) override
{
for (auto & accessor : accessors) {
auto st = accessor->maybeLstat(path);
if (st)
return st;
}
return std::nullopt;
}

DirEntries readDirectory(const CanonPath & path) override
{
DirEntries result;
for (auto & accessor : accessors) {
auto st = accessor->maybeLstat(path);
if (!st)
continue;
for (auto & entry : accessor->readDirectory(path))
// Don't override entries from previous accessors.
result.insert(entry);
}
return result;
}

std::string readLink(const CanonPath & path) override
{
for (auto & accessor : accessors) {
auto st = accessor->maybeLstat(path);
if (st)
return accessor->readLink(path);
}
throw FileNotFound("path '%s' does not exist", showPath(path));
}

std::string showPath(const CanonPath & path) override
{
for (auto & accessor : accessors)
return accessor->showPath(path);
return SourceAccessor::showPath(path);
}

std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
{
for (auto & accessor : accessors) {
auto p = accessor->getPhysicalPath(path);
if (p)
return p;
}
return std::nullopt;
}
};

ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> && accessors)
{
return make_ref<UnionSourceAccessor>(std::move(accessors));
}

}
4 changes: 2 additions & 2 deletions src/nix/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON
auto & flake = lockedFlake.flake;

// Currently, all flakes are in the Nix store via the rootFS accessor.
auto storePath = store->printStorePath(sourcePathToStorePath(store, flake.path).first);
auto storePath = store->printStorePath(store->toStorePath(flake.path.path.abs()).first);

if (json) {
nlohmann::json j;
Expand Down Expand Up @@ -1079,7 +1079,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun

StorePathSet sources;

auto storePath = sourcePathToStorePath(store, flake.flake.path).first;
auto storePath = store->toStorePath(flake.flake.path.path.abs()).first;

sources.insert(storePath);

Expand Down
Loading
Loading