1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 14:51:16 +02:00

nix shell: Resolve symlinks in storeFS

`storeFS` is the `MountedSourceAccessor` that wraps `store->getFSAccessor()`.
This commit is contained in:
Eelco Dolstra 2025-03-26 21:07:17 +01:00 committed by John Ericson
parent eb643d034f
commit 9d3595646d
4 changed files with 42 additions and 33 deletions

View file

@ -246,22 +246,8 @@ EvalState::EvalState(
} }
, repair(NoRepair) , repair(NoRepair)
, emptyBindings(0) , emptyBindings(0)
, rootFS( , storeFS(
({ makeMountedSourceAccessor(
/* 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::root, makeEmptySourceAccessor()},
/* In the pure eval case, we can simply require /* In the pure eval case, we can simply require
@ -283,7 +269,22 @@ EvalState::EvalState(
catch it, so we don't need to do this hack. catch it, so we don't need to do this hack.
*/ */
{CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)}, {CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)},
}); }))
, rootFS(
({
/* 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) {
accessor = settings.pureEval accessor = settings.pureEval
? storeFS ? storeFS
: makeUnionSourceAccessor({accessor, storeFS}); : makeUnionSourceAccessor({accessor, storeFS});

View file

@ -265,6 +265,11 @@ public:
/** `"unknown"` */ /** `"unknown"` */
Value vStringUnknown; Value vStringUnknown;
/**
* The accessor corresponding to `store`.
*/
const ref<SourceAccessor> storeFS;
/** /**
* The accessor for the root filesystem. * The accessor for the root filesystem.
*/ */

View file

@ -65,11 +65,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment
void run(ref<Store> store, Installables && installables) override void run(ref<Store> store, Installables && installables) override
{ {
auto state = getEvalState();
auto outPaths = auto outPaths =
Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables);
auto accessor = store->getFSAccessor();
std::unordered_set<StorePath> done; std::unordered_set<StorePath> done;
std::queue<StorePath> todo; std::queue<StorePath> todo;
for (auto & path : outPaths) for (auto & path : outPaths)
@ -85,13 +85,16 @@ struct CmdShell : InstallablesCommand, MixEnvironment
if (!done.insert(path).second) if (!done.insert(path).second)
continue; continue;
if (true) auto binDir = state->storeFS->resolveSymlinks(CanonPath(store->printStorePath(path)) / "bin");
pathAdditions.push_back(store->printStorePath(path) + "/bin"); if (!store->isInStore(binDir.abs()))
throw Error("path '%s' is not in the Nix store", binDir);
auto propPath = pathAdditions.push_back(binDir.abs());
accessor->resolveSymlinks(CanonPath(path.to_string()) / "nix-support" / "propagated-user-env-packages");
if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { auto propPath = state->storeFS->resolveSymlinks(
for (auto & p : tokenizeString<Paths>(accessor->readFile(propPath))) CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages");
if (auto st = state->storeFS->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) {
for (auto & p : tokenizeString<Paths>(state->storeFS->readFile(propPath)))
todo.push(store->parseStorePath(p)); todo.push(store->parseStorePath(p));
} }
} }
@ -108,7 +111,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment
// Release our references to eval caches to ensure they are persisted to disk, because // Release our references to eval caches to ensure they are persisted to disk, because
// we are about to exec out of this process without running C++ destructors. // we are about to exec out of this process without running C++ destructors.
getEvalState()->evalCaches.clear(); state->evalCaches.clear();
execProgramInStore(store, UseLookupPath::Use, *command.begin(), args); execProgramInStore(store, UseLookupPath::Use, *command.begin(), args);
} }

View file

@ -21,7 +21,7 @@ nix shell -f shell-hello.nix 'hello^*' -c hello2 | grep 'Hello2'
nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World'
# Test that symlinks outside of the store don't work. # Test that symlinks outside of the store don't work.
expect 1 nix shell -f shell-hello.nix forbidden-symlink -c hello 2>&1 | grepQuiet "points outside source tree" expect 1 nix shell -f shell-hello.nix forbidden-symlink -c hello 2>&1 | grepQuiet "is not in the Nix store"
# Test that we're not setting any more environment variables than necessary. # Test that we're not setting any more environment variables than necessary.
# For instance, we might set an environment variable temporarily to affect some # For instance, we might set an environment variable temporarily to affect some