Skip to content

Commit 782c63f

Browse files
authored
Merge pull request #12512 from DeterminateSystems/store-fs
Use a union source accessor to put chroot stores in the logical location
2 parents e5fdb4b + ec7dc56 commit 782c63f

File tree

12 files changed

+168
-87
lines changed

12 files changed

+168
-87
lines changed

src/libcmd/common-eval-args.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ EvalSettings evalSettings {
3737
auto [accessor, lockedRef] = flakeRef.resolve(state.store).lazyFetch(state.store);
3838
auto storePath = nix::fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy, lockedRef.input.getName());
3939
state.allowPath(storePath);
40-
return state.rootPath(state.store->toRealPath(storePath));
40+
return state.rootPath(state.store->printStorePath(storePath));
4141
},
4242
},
4343
},
@@ -179,7 +179,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas
179179
state.fetchSettings,
180180
EvalSettings::resolvePseudoUrl(s));
181181
auto storePath = fetchToStore(*state.store, SourcePath(accessor), FetchMode::Copy);
182-
return state.rootPath(CanonPath(state.store->toRealPath(storePath)));
182+
return state.rootPath(CanonPath(state.store->printStorePath(storePath)));
183183
}
184184

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

194194
else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {

src/libexpr/eval.cc

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -246,15 +246,42 @@ EvalState::EvalState(
246246
, repair(NoRepair)
247247
, emptyBindings(0)
248248
, rootFS(
249-
settings.restrictEval || settings.pureEval
250-
? ref<SourceAccessor>(AllowListSourceAccessor::create(getFSSourceAccessor(), {},
251-
[&settings](const CanonPath & path) -> RestrictedPathError {
252-
auto modeInformation = settings.pureEval
253-
? "in pure evaluation mode (use '--impure' to override)"
254-
: "in restricted mode";
255-
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
256-
}))
257-
: getFSSourceAccessor())
249+
({
250+
/* In pure eval mode, we provide a filesystem that only
251+
contains the Nix store.
252+
253+
If we have a chroot store and pure eval is not enabled,
254+
use a union accessor to make the chroot store available
255+
at its logical location while still having the
256+
underlying directory available. This is necessary for
257+
instance if we're evaluating a file from the physical
258+
/nix/store while using a chroot store. */
259+
auto accessor = getFSSourceAccessor();
260+
261+
auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy));
262+
if (settings.pureEval || store->storeDir != realStoreDir) {
263+
auto storeFS = makeMountedSourceAccessor(
264+
{
265+
{CanonPath::root, makeEmptySourceAccessor()},
266+
{CanonPath(store->storeDir), makeFSSourceAccessor(realStoreDir)}
267+
});
268+
accessor = settings.pureEval
269+
? storeFS
270+
: makeUnionSourceAccessor({accessor, storeFS});
271+
}
272+
273+
/* Apply access control if needed. */
274+
if (settings.restrictEval || settings.pureEval)
275+
accessor = AllowListSourceAccessor::create(accessor, {},
276+
[&settings](const CanonPath & path) -> RestrictedPathError {
277+
auto modeInformation = settings.pureEval
278+
? "in pure evaluation mode (use '--impure' to override)"
279+
: "in restricted mode";
280+
throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation);
281+
});
282+
283+
accessor;
284+
}))
258285
, corepkgsFS(make_ref<MemorySourceAccessor>())
259286
, internalFS(make_ref<MemorySourceAccessor>())
260287
, derivationInternal{corepkgsFS->addFile(
@@ -344,7 +371,7 @@ void EvalState::allowPath(const Path & path)
344371
void EvalState::allowPath(const StorePath & storePath)
345372
{
346373
if (auto rootFS2 = rootFS.dynamic_pointer_cast<AllowListSourceAccessor>())
347-
rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath)));
374+
rootFS2->allowPrefix(CanonPath(store->printStorePath(storePath)));
348375
}
349376

350377
void EvalState::allowClosure(const StorePath & storePath)
@@ -422,16 +449,6 @@ void EvalState::checkURI(const std::string & uri)
422449
}
423450

424451

425-
Path EvalState::toRealPath(const Path & path, const NixStringContext & context)
426-
{
427-
// FIXME: check whether 'path' is in 'context'.
428-
return
429-
!context.empty() && store->isInStore(path)
430-
? store->toRealPath(path)
431-
: path;
432-
}
433-
434-
435452
Value * EvalState::addConstant(const std::string & name, Value & v, Constant info)
436453
{
437454
Value * v2 = allocValue();
@@ -2051,7 +2068,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
20512068
else if (firstType == nPath) {
20522069
if (!context.empty())
20532070
state.error<EvalError>("a string that refers to a store path cannot be appended to a path").atPos(pos).withFrame(env, *this).debugThrow();
2054-
v.mkPath(state.rootPath(CanonPath(canonPath(str()))));
2071+
v.mkPath(state.rootPath(CanonPath(str())));
20552072
} else
20562073
v.mkStringMove(c_str(), context);
20572074
}
@@ -2432,7 +2449,7 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext
24322449
auto path = coerceToString(pos, v, context, errorCtx, false, false, true).toOwned();
24332450
if (path == "" || path[0] != '/')
24342451
error<EvalError>("string '%1%' doesn't represent an absolute path", path).withTrace(pos, errorCtx).debugThrow();
2435-
return rootPath(CanonPath(path));
2452+
return rootPath(path);
24362453
}
24372454

24382455

@@ -3086,7 +3103,7 @@ std::optional<SourcePath> EvalState::resolveLookupPathPath(const LookupPath::Pat
30863103
fetchSettings,
30873104
EvalSettings::resolvePseudoUrl(value));
30883105
auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy);
3089-
return finish(rootPath(store->toRealPath(storePath)));
3106+
return finish(rootPath(store->printStorePath(storePath)));
30903107
} catch (Error & e) {
30913108
logWarning({
30923109
.msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value)

src/libexpr/eval.hh

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -412,17 +412,6 @@ public:
412412

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

415-
/**
416-
* When using a diverted store and 'path' is in the Nix store, map
417-
* 'path' to the diverted location (e.g. /nix/store/foo is mapped
418-
* to /home/alice/my-nix/nix/store/foo). However, this is only
419-
* done if the context is not empty, since otherwise we're
420-
* probably trying to read from the actual /nix/store. This is
421-
* intended to distinguish between import-from-derivation and
422-
* sources stored in the actual /nix/store.
423-
*/
424-
Path toRealPath(const Path & path, const NixStringContext & context);
425-
426415
/**
427416
* Parse a Nix expression from the specified file.
428417
*/

src/libexpr/primops.cc

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ static SourcePath realisePath(EvalState & state, const PosIdx pos, Value & v, st
145145
try {
146146
if (!context.empty() && path.accessor == state.rootFS) {
147147
auto rewrites = state.realiseContext(context);
148-
auto realPath = state.toRealPath(rewriteStrings(path.path.abs(), rewrites), context);
149-
path = {path.accessor, CanonPath(realPath)};
148+
path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))};
150149
}
151150
return resolveSymlinks ? path.resolveSymlinks(*resolveSymlinks) : path;
152151
} catch (Error & e) {
@@ -2479,21 +2478,11 @@ static void addPath(
24792478
const NixStringContext & context)
24802479
{
24812480
try {
2482-
StorePathSet refs;
2483-
24842481
if (path.accessor == state.rootFS && state.store->isInStore(path.path.abs())) {
24852482
// FIXME: handle CA derivation outputs (where path needs to
24862483
// be rewritten to the actual output).
24872484
auto rewrites = state.realiseContext(context);
2488-
path = {state.rootFS, CanonPath(state.toRealPath(rewriteStrings(path.path.abs(), rewrites), context))};
2489-
2490-
try {
2491-
auto [storePath, subPath] = state.store->toStorePath(path.path.abs());
2492-
// FIXME: we should scanForReferences on the path before adding it
2493-
refs = state.store->queryPathInfo(storePath)->references;
2494-
path = {state.rootFS, CanonPath(state.store->toRealPath(storePath) + subPath)};
2495-
} catch (Error &) { // FIXME: should be InvalidPathError
2496-
}
2485+
path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))};
24972486
}
24982487

24992488
std::unique_ptr<PathFilter> filter;

src/libflake/flake/flake.cc

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ static Flake readFlake(
337337
auto storePath = fetchToStore(*state.store, setting.value->path(), FetchMode::Copy);
338338
flake.config.settings.emplace(
339339
state.symbols[setting.name],
340-
state.store->toRealPath(storePath));
340+
state.store->printStorePath(storePath));
341341
}
342342
else if (setting.value->type() == nInt)
343343
flake.config.settings.emplace(
@@ -423,7 +423,7 @@ static Flake getFlake(
423423
auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, accessor);
424424

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

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

787-
return {state.rootPath(state.store->toRealPath(storePath)), lockedRef};
787+
return {state.rootPath(state.store->printStorePath(storePath)), lockedRef};
788788
}
789789
}();
790790

@@ -921,21 +921,6 @@ LockedFlake lockFlake(
921921
}
922922
}
923923

924-
std::pair<StorePath, Path> sourcePathToStorePath(
925-
ref<Store> store,
926-
const SourcePath & _path)
927-
{
928-
auto path = _path.path.abs();
929-
930-
if (auto store2 = store.dynamic_pointer_cast<LocalFSStore>()) {
931-
auto realStoreDir = store2->getRealStoreDir();
932-
if (isInDir(path, realStoreDir))
933-
path = store2->storeDir + path.substr(realStoreDir.size());
934-
}
935-
936-
return store->toStorePath(path);
937-
}
938-
939924
void callFlake(EvalState & state,
940925
const LockedFlake & lockedFlake,
941926
Value & vRes)
@@ -953,7 +938,7 @@ void callFlake(EvalState & state,
953938

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

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

958943
emitTreeAttrs(
959944
state,

src/libflake/flake/flake.hh

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,6 @@ void callFlake(
234234
const LockedFlake & lockedFlake,
235235
Value & v);
236236

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

249239
void emitTreeAttrs(

src/libutil/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ sources = files(
167167
'tarfile.cc',
168168
'terminal.cc',
169169
'thread-pool.cc',
170+
'union-source-accessor.cc',
170171
'unix-domain-socket.cc',
171172
'url.cc',
172173
'users.cc',

src/libutil/mounted-source-accessor.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ struct MountedSourceAccessor : SourceAccessor
2323
return accessor->readFile(subpath);
2424
}
2525

26-
bool pathExists(const CanonPath & path) override
27-
{
28-
auto [accessor, subpath] = resolve(path);
29-
return accessor->pathExists(subpath);
30-
}
31-
3226
std::optional<Stat> maybeLstat(const CanonPath & path) override
3327
{
3428
auto [accessor, subpath] = resolve(path);
@@ -69,6 +63,12 @@ struct MountedSourceAccessor : SourceAccessor
6963
path.pop();
7064
}
7165
}
66+
67+
std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
68+
{
69+
auto [accessor, subpath] = resolve(path);
70+
return accessor->getPhysicalPath(subpath);
71+
}
7272
};
7373

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

src/libutil/source-accessor.hh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,10 @@ ref<SourceAccessor> makeFSSourceAccessor(std::filesystem::path root);
216216

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

219+
/**
220+
* Construct an accessor that presents a "union" view of a vector of
221+
* underlying accessors. Earlier accessors take precedence over later.
222+
*/
223+
ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> && accessors);
224+
219225
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#include "source-accessor.hh"
2+
3+
namespace nix {
4+
5+
struct UnionSourceAccessor : SourceAccessor
6+
{
7+
std::vector<ref<SourceAccessor>> accessors;
8+
9+
UnionSourceAccessor(std::vector<ref<SourceAccessor>> _accessors)
10+
: accessors(std::move(_accessors))
11+
{
12+
displayPrefix.clear();
13+
}
14+
15+
std::string readFile(const CanonPath & path) override
16+
{
17+
for (auto & accessor : accessors) {
18+
auto st = accessor->maybeLstat(path);
19+
if (st)
20+
return accessor->readFile(path);
21+
}
22+
throw FileNotFound("path '%s' does not exist", showPath(path));
23+
}
24+
25+
std::optional<Stat> maybeLstat(const CanonPath & path) override
26+
{
27+
for (auto & accessor : accessors) {
28+
auto st = accessor->maybeLstat(path);
29+
if (st)
30+
return st;
31+
}
32+
return std::nullopt;
33+
}
34+
35+
DirEntries readDirectory(const CanonPath & path) override
36+
{
37+
DirEntries result;
38+
for (auto & accessor : accessors) {
39+
auto st = accessor->maybeLstat(path);
40+
if (!st)
41+
continue;
42+
for (auto & entry : accessor->readDirectory(path))
43+
// Don't override entries from previous accessors.
44+
result.insert(entry);
45+
}
46+
return result;
47+
}
48+
49+
std::string readLink(const CanonPath & path) override
50+
{
51+
for (auto & accessor : accessors) {
52+
auto st = accessor->maybeLstat(path);
53+
if (st)
54+
return accessor->readLink(path);
55+
}
56+
throw FileNotFound("path '%s' does not exist", showPath(path));
57+
}
58+
59+
std::string showPath(const CanonPath & path) override
60+
{
61+
for (auto & accessor : accessors)
62+
return accessor->showPath(path);
63+
return SourceAccessor::showPath(path);
64+
}
65+
66+
std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
67+
{
68+
for (auto & accessor : accessors) {
69+
auto p = accessor->getPhysicalPath(path);
70+
if (p)
71+
return p;
72+
}
73+
return std::nullopt;
74+
}
75+
};
76+
77+
ref<SourceAccessor> makeUnionSourceAccessor(std::vector<ref<SourceAccessor>> && accessors)
78+
{
79+
return make_ref<UnionSourceAccessor>(std::move(accessors));
80+
}
81+
82+
}

0 commit comments

Comments
 (0)