Skip to content

Commit

Permalink
Allow builtins.storePath in pure mode
Browse files Browse the repository at this point in the history
This is useful for
 - packaging and deploying non-OSS software with flakes.
 - bringing the shell and coreutils packages used by the test suite
   into the test VM (#6735)

The test VM actually uses a proper sandbox, so we wouldn't be able to
get away with reading those files impurely in the builder anymore.

Note that neither use would be feasible with `builtins.fetchClosure`,
because ca-derivations is too experimental and in principle not guaranteed
to work for all builds that do support input addressing.
  • Loading branch information
roberth committed Mar 24, 2023
1 parent 4dcc0a1 commit c8e761f
Showing 1 changed file with 31 additions and 16 deletions.
47 changes: 31 additions & 16 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1445,23 +1445,38 @@ static RegisterPrimOp primop_toPath({
corner cases. */
static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
if (evalSettings.pureEval)
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'%s' is not allowed in pure evaluation mode", "builtins.storePath"),
.errPos = state.positions[pos]
}));

PathSet context;
Path path = state.checkSourcePath(state.coerceToPath(pos, *args[0], context, "while evaluating the first argument passed to builtins.storePath"));
/* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink
directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path))
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = state.positions[pos]
}));
Path path = state.coerceToPath(pos, *args[0], context, "while evaluating the argument of builtins.storePath");

/* The following branch permits a dependency on a store path to be declared,
but in restrict-eval, extending the allowed paths would not be desirable. */
if (evalSettings.pureEval && !evalSettings.restrictEval) {

/* Reading from the store behaves like a pure function, but we want to
be careful about symlink resolution, which would otherwise be done
shortly after. A mutable symlink maybe be present in the chain of
symlinks, so to keep it simple, we currently disallow store paths in
symlinks altogether when in pure mode. */
if (!state.store->isStorePath(path)) {
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("'%s' is only allowed on store paths in pure evaluation mode", "builtins.storePath"),
.errPos = state.positions[pos]
}));
}
state.allowPath(path);
} else {
path = state.checkSourcePath(path);
/* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink
directly in the store. The latter condition is necessary so
e.g. nix-push does the right thing. */
if (!state.store->isStorePath(path)) path = canonPath(path, true);
if (!state.store->isInStore(path))
state.debugThrowLastTrace(EvalError({
.msg = hintfmt("path '%1%' is not in the Nix store", path),
.errPos = state.positions[pos]
}));
}

auto path2 = state.store->toStorePath(path).first;
if (!settings.readOnlyMode)
state.store->ensurePath(path2);
Expand Down

0 comments on commit c8e761f

Please sign in to comment.