From 5d91526f1b886e21afd8b10de0d09cced1954a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Na=C3=AFm=20Favier?= Date: Fri, 23 Sep 2022 23:30:29 +0200 Subject: [PATCH] Allow restricting possible runtime references Adds a new derivation attribute `__visibleReferences` which restricts the list of paths that are searched in build outputs in order to establish runtime references. Setting this attribute requires the experimental feature `invisible-references` to be enabled. Useful when creating filesystem images containing their own embedded Nix store: setting `__visibleReferences = [];` makes them self-contained blobs of data with no runtime dependencies. --- .../src/language/advanced-attributes.md | 25 ++++++++++++++++--- src/libstore/build/local-derivation-goal.cc | 21 +++++++++++++++- src/libutil/experimental-features.cc | 1 + src/libutil/experimental-features.hh | 1 + src/nix/develop.cc | 1 + tests/check-refs.nix | 11 ++++++++ tests/check-refs.sh | 11 ++++++++ 7 files changed, 67 insertions(+), 4 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 2e7e80ed0ec7..75d8c5aa755a 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -55,6 +55,25 @@ Derivations can declare some infrequently used optional attributes. dependency on `foobar` or any other derivation depending recursively on `foobar`. + - [`__visibleReferences`]{#adv-attr-__visibleReferences}\ + The **experimental** attribute `__visibleReferences` restricts the set of + store paths that Nix will consider when scanning for runtime + dependencies. By default, this set includes build inputs, output + paths, and paths built by recursive Nix invocations. Setting this + attribute will restrict the set to the given list. + For example, + + ```nix + __visibleReferences = []; + ``` + + effectively produces a blob of data without any runtime dependencies. + This is useful when generating self-contained filesystem images with + their own embedded Nix store: hashes found inside such an image refer + to the embedded store and not to the host's Nix store. + + This only has an effect if the `invisible-references` experimental feature is enabled. + - [`exportReferencesGraph`]{#adv-attr-exportReferencesGraph}\ This attribute allows builders access to the references graph of their inputs. The attribute is a list of inputs in the Nix store @@ -207,13 +226,13 @@ Derivations can declare some infrequently used optional attributes. the hash in either hexadecimal or base-32 notation. (See the [`nix-hash` command](../command-ref/nix-hash.md) for information about converting to and from base-32 notation.) - + - [`__contentAddressed`]{#adv-attr-__contentAddressed} If this **experimental** attribute is set to true, then the derivation outputs will be stored in a content-addressed location rather than the traditional input-addressed one. - This only has an effect if the `ca-derivation` experimental feature is enabled. - + This only has an effect if the `ca-derivations` experimental feature is enabled. + Setting this attribute also requires setting `outputHashMode` and `outputHashAlgo` like for *fixed-output derivations* (see above). - [`passAsFile`]{#adv-attr-passAsFile}\ diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 18b682e1351a..4bc1e80f21b9 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2138,12 +2138,31 @@ DrvOutputs LocalDerivationGoal::registerOutputs() /* The paths that can be referenced are the input closures, the output paths, and any paths that have been built via recursive - Nix calls. */ + Nix calls; this list is restricted to the paths in the + __visibleReferences derivation attribute, if present. */ StorePathSet referenceablePaths; for (auto & p : inputPaths) referenceablePaths.insert(p); for (auto & i : scratchOutputs) referenceablePaths.insert(i.second); for (auto & p : addedPaths) referenceablePaths.insert(p); + if (auto visibleReferences = parsedDrv->getStringsAttr("__visibleReferences")) { + settings.requireExperimentalFeature(Xp::InvisibleReferences); + + StorePathSet spec; + for (auto & i : *visibleReferences) { + if (worker.store.isStorePath(i)) + spec.insert(worker.store.parseStorePath(i)); + else + throw BuildError("derivation attribute __visibleReferences contains an illegal reference '%s'", i); + } + + StorePathSet tmp; + std::set_intersection(referenceablePaths.begin(), referenceablePaths.end(), + spec.begin(), spec.end(), + std::inserter(tmp, tmp.begin())); + referenceablePaths = std::move(tmp); + } + /* FIXME `needsHashRewrite` should probably be removed and we get to the real reason why we aren't using the chroot dir */ auto toRealPathChroot = [&](const Path & p) -> Path { diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index fa79cca6b2f8..2313f02bda39 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -14,6 +14,7 @@ std::map stringifiedXpFeatures = { { Xp::NoUrlLiterals, "no-url-literals" }, { Xp::FetchClosure, "fetch-closure" }, { Xp::ReplFlake, "repl-flake" }, + { Xp::InvisibleReferences, "invisible-references" }, }; const std::optional parseExperimentalFeature(const std::string_view & name) diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index d09ab025c67f..f3fc7b0c2606 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -23,6 +23,7 @@ enum struct ExperimentalFeature NoUrlLiterals, FetchClosure, ReplFlake, + InvisibleReferences, }; /** diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 4de1097548c3..3a0edb04c71a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -192,6 +192,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore drv.env.erase("allowedRequisites"); drv.env.erase("disallowedReferences"); drv.env.erase("disallowedRequisites"); + drv.env.erase("__visibleReferences"); /* Rehash and write the derivation. FIXME: would be nice to use 'buildDerivation', but that's privileged. */ diff --git a/tests/check-refs.nix b/tests/check-refs.nix index 9d90b0920542..9f5cd77c864b 100644 --- a/tests/check-refs.nix +++ b/tests/check-refs.nix @@ -67,4 +67,15 @@ rec { disallowedReferences = [test5]; }; + test11 = makeTest 11 { + buildCommand = "echo ${dep} > $out"; + __visibleReferences = []; + disallowedReferences = [dep]; + }; + + test12 = makeTest 12 { + buildCommand = "echo ${dep} ${src} > $out"; + __visibleReferences = [dep]; + }; + } diff --git a/tests/check-refs.sh b/tests/check-refs.sh index 16bbabc40985..868ec7dfcebb 100644 --- a/tests/check-refs.sh +++ b/tests/check-refs.sh @@ -40,3 +40,14 @@ nix-build -o $RESULT check-refs.nix -A test7 # test10 should succeed (no disallowed references). nix-build -o $RESULT check-refs.nix -A test10 + +if isDaemonNewer 2.12pre20220922; then + # test11 should succeed (no referenceable paths). + test11=$(nix-build --extra-experimental-features invisible-references -o $RESULT check-refs.nix -A test11) + [[ -z $(nix-store -q --references "$test11") ]] + + # test12 should succeed. + test12=$(nix-build --extra-experimental-features invisible-references -o $RESULT check-refs.nix -A test12) + nix-store -q --references "$test12" | grep -q "$dep" + ! nix-store -q --references "$test12" | grep -q aux-ref +fi