diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0ec819809..f82fd93b5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -17,6 +17,7 @@ #include "nix/expr/print.hh" #include "nix/fetchers/filtering-source-accessor.hh" #include "nix/util/memory-source-accessor.hh" +#include "nix/util/mounted-source-accessor.hh" #include "nix/expr/gc-small-vector.hh" #include "nix/util/url.hh" #include "nix/fetchers/fetch-to-store.hh" @@ -225,22 +226,25 @@ EvalState::EvalState( */ {CanonPath(store->storeDir), store->getFSAccessor(settings.pureEval)}, })) - , rootFS(({ - /* In pure eval mode, we provide a filesystem that only - contains the Nix store. + , rootFS([&] { + auto accessor = [&]() -> decltype(rootFS) { + /* In pure eval mode, we provide a filesystem that only + contains the Nix store. */ + if (settings.pureEval) + return storeFS; - 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(); + /* 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 realStoreDir = dirOf(store->toRealPath(StorePath::dummy)); + if (store->storeDir != realStoreDir) + return makeUnionSourceAccessor({getFSSourceAccessor(), storeFS}); - auto realStoreDir = dirOf(store->toRealPath(StorePath::dummy)); - if (settings.pureEval || store->storeDir != realStoreDir) { - accessor = settings.pureEval ? storeFS : makeUnionSourceAccessor({accessor, storeFS}); - } + return getFSSourceAccessor(); + }(); /* Apply access control if needed. */ if (settings.restrictEval || settings.pureEval) @@ -251,8 +255,8 @@ EvalState::EvalState( throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); }); - accessor; - })) + return accessor; + }()) , corepkgsFS(make_ref()) , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( @@ -334,7 +338,7 @@ EvalState::EvalState( EvalState::~EvalState() {} -void EvalState::allowPath(const Path & path) +void EvalState::allowPathLegacy(const Path & path) { if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->allowPrefix(CanonPath(path)); @@ -3177,7 +3181,7 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pat /* Allow access to paths in the search path. */ if (initAccessControl) { - allowPath(path.path.abs()); + allowPathLegacy(path.path.abs()); if (store->isInStore(path.path.abs())) { try { allowClosure(store->toStorePath(path.path.abs()).first); diff --git a/src/libexpr/include/nix/expr/eval.hh b/src/libexpr/include/nix/expr/eval.hh index 1c2552991..e5b87cc97 100644 --- a/src/libexpr/include/nix/expr/eval.hh +++ b/src/libexpr/include/nix/expr/eval.hh @@ -49,6 +49,7 @@ class StorePath; struct SingleDerivedPath; enum RepairFlag : bool; struct MemorySourceAccessor; +struct MountedSourceAccessor; namespace eval_cache { class EvalCache; @@ -320,7 +321,7 @@ public: /** * The accessor corresponding to `store`. */ - const ref storeFS; + const ref storeFS; /** * The accessor for the root filesystem. @@ -489,8 +490,11 @@ public: /** * Allow access to a path. + * + * Only for restrict eval: pure eval just whitelist store paths, + * never arbitrary paths. */ - void allowPath(const Path & path); + void allowPathLegacy(const Path & path); /** * Allow access to a store path. Note that this gets remapped to diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index f750d907d..f6f5c30ee 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -15,6 +15,7 @@ #include "nix/fetchers/fetch-settings.hh" #include "nix/util/json-utils.hh" #include "nix/util/archive.hh" +#include "nix/util/mounted-source-accessor.hh" #include #include diff --git a/src/libutil/include/nix/util/meson.build b/src/libutil/include/nix/util/meson.build index 07a4f1d11..dcfaa8e3f 100644 --- a/src/libutil/include/nix/util/meson.build +++ b/src/libutil/include/nix/util/meson.build @@ -47,6 +47,7 @@ headers = files( 'logging.hh', 'lru-cache.hh', 'memory-source-accessor.hh', + 'mounted-source-accessor.hh', 'muxable-pipe.hh', 'os-string.hh', 'pool.hh', diff --git a/src/libutil/include/nix/util/mounted-source-accessor.hh b/src/libutil/include/nix/util/mounted-source-accessor.hh new file mode 100644 index 000000000..518ae4f09 --- /dev/null +++ b/src/libutil/include/nix/util/mounted-source-accessor.hh @@ -0,0 +1,20 @@ +#pragma once + +#include "source-accessor.hh" + +namespace nix { + +struct MountedSourceAccessor : SourceAccessor +{ + virtual void mount(CanonPath mountPoint, ref accessor) = 0; + + /** + * Return the accessor mounted on `mountPoint`, or `nullptr` if + * there is no such mount point. + */ + virtual std::shared_ptr getMount(CanonPath mountPoint) = 0; +}; + +ref makeMountedSourceAccessor(std::map> mounts); + +} // namespace nix diff --git a/src/libutil/include/nix/util/source-accessor.hh b/src/libutil/include/nix/util/source-accessor.hh index aa937da48..7419ef392 100644 --- a/src/libutil/include/nix/util/source-accessor.hh +++ b/src/libutil/include/nix/util/source-accessor.hh @@ -214,8 +214,6 @@ ref getFSSourceAccessor(); */ ref makeFSSourceAccessor(std::filesystem::path root); -ref makeMountedSourceAccessor(std::map> mounts); - /** * Construct an accessor that presents a "union" view of a vector of * underlying accessors. Earlier accessors take precedence over later. diff --git a/src/libutil/mounted-source-accessor.cc b/src/libutil/mounted-source-accessor.cc index 4c32147f9..5c0ecc1ff 100644 --- a/src/libutil/mounted-source-accessor.cc +++ b/src/libutil/mounted-source-accessor.cc @@ -1,18 +1,22 @@ -#include "nix/util/source-accessor.hh" +#include "nix/util/mounted-source-accessor.hh" + +#include namespace nix { -struct MountedSourceAccessor : SourceAccessor +struct MountedSourceAccessorImpl : MountedSourceAccessor { - std::map> mounts; + boost::concurrent_flat_map> mounts; - MountedSourceAccessor(std::map> _mounts) - : mounts(std::move(_mounts)) + MountedSourceAccessorImpl(std::map> _mounts) { displayPrefix.clear(); // Currently we require a root filesystem. This could be relaxed. - assert(mounts.contains(CanonPath::root)); + assert(_mounts.contains(CanonPath::root)); + + for (auto & [path, accessor] : _mounts) + mount(path, accessor); // FIXME: return dummy parent directories automatically? } @@ -52,10 +56,9 @@ struct MountedSourceAccessor : SourceAccessor // Find the nearest parent of `path` that is a mount point. std::vector subpath; while (true) { - auto i = mounts.find(path); - if (i != mounts.end()) { + if (auto mount = getMount(path)) { std::reverse(subpath.begin(), subpath.end()); - return {i->second, CanonPath(subpath)}; + return {ref(mount), CanonPath(subpath)}; } assert(!path.isRoot()); @@ -69,11 +72,24 @@ struct MountedSourceAccessor : SourceAccessor auto [accessor, subpath] = resolve(path); return accessor->getPhysicalPath(subpath); } + + void mount(CanonPath mountPoint, ref accessor) override + { + mounts.emplace(std::move(mountPoint), std::move(accessor)); + } + + std::shared_ptr getMount(CanonPath mountPoint) override + { + if (auto res = getConcurrent(mounts, mountPoint)) + return *res; + else + return nullptr; + } }; -ref makeMountedSourceAccessor(std::map> mounts) +ref makeMountedSourceAccessor(std::map> mounts) { - return make_ref(std::move(mounts)); + return make_ref(std::move(mounts)); } } // namespace nix diff --git a/src/nix/env.cc b/src/nix/env.cc index c8fb5bee0..0a211399a 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -7,6 +7,7 @@ #include "nix/util/strings.hh" #include "nix/util/executable-path.hh" #include "nix/util/environment-variables.hh" +#include "nix/util/mounted-source-accessor.hh" using namespace nix; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 68005171f..80177cf13 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -177,8 +177,8 @@ struct ProfileManifest else if (std::filesystem::exists(profile / "manifest.nix")) { // FIXME: needed because of pure mode; ugly. - state.allowPath(state.store->followLinksToStore(profile.string())); - state.allowPath(state.store->followLinksToStore((profile / "manifest.nix").string())); + state.allowPath(state.store->followLinksToStorePath(profile.string())); + state.allowPath(state.store->followLinksToStorePath((profile / "manifest.nix").string())); auto packageInfos = queryInstalled(state, state.store->followLinksToStore(profile.string()));