diff --git a/src/nix/meson.build b/src/nix/meson.build index 6edb768e31c..9968a93421d 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -105,6 +105,7 @@ nix_sources = [config_h] + files( 'run.cc', 'search.cc', 'sigs.cc', + 'store-add-gc-root.cc', 'store-copy-log.cc', 'store-delete.cc', 'store-gc.cc', diff --git a/src/nix/store-add-gc-root.cc b/src/nix/store-add-gc-root.cc new file mode 100644 index 00000000000..a29cd2c0ca9 --- /dev/null +++ b/src/nix/store-add-gc-root.cc @@ -0,0 +1,70 @@ +#include "command.hh" +#include "args.hh" +#include "shared.hh" +#include "store-cast.hh" +#include "indirect-root-store.hh" +#include "common-args.hh" + +using namespace nix; + +struct CmdAddGCRoot : StoreCommand +{ + std::vector links; + bool checkResults = true; + + CmdAddGCRoot() + { + expectArgs({ + .label = "indirect-roots", + .handler = {&links}, + .completer = completePath, + }); + + addFlag({ + .longName = "no-check", + .description = "Do not test the validity of created roots.", + .handler = {&checkResults, false}, + }); + } + + std::string description() override + { + return "Add indirect gc roots through the symlink arguments"; + } + + std::string doc() override + { + return +#include "store-add-gc-root.md" + ; + } + + Category category() override + { + return catSecondary; + } + + void run(ref store) override + { + auto & indirectRootStore = require(*store); + + for (auto & link : links) { + auto indirectPath = absPath(link); + if (indirectRootStore.isInStore(indirectPath)) { + throw Error("Indirect root '%1%' must not be in the Nix store", link); + } + + if (checkResults) { + auto path = indirectRootStore.followLinksToStorePath(indirectPath); + indirectRootStore.addTempRoot(path); + if (!indirectRootStore.isValidPath(path)) { + throw Error("Indirect root '%1%' is no a symbolic link to a valid store path", link); + } + } + + indirectRootStore.addIndirectRoot(indirectPath); + } + } +}; + +static auto rCmdAddGCRoot = registerCommand2({"store", "add-gc-root"}); diff --git a/src/nix/store-add-gc-root.md b/src/nix/store-add-gc-root.md new file mode 100644 index 00000000000..cd4841fb8a6 --- /dev/null +++ b/src/nix/store-add-gc-root.md @@ -0,0 +1,17 @@ +R""( + +# Examples + + ```console + $ ln -s /nix/store/xxx foo + $ nix store add-gc-root foo + $ nix-store -q --roots /nix/store/xxx + .../foo -> /nix/store/xxx + ``` + +# Description + +This command adds garbage collector root to the paths referenced by the symlinks passed as arguments. +These are called indirect roots, as the root will disappear as soon as the intermediate symlink gets deleted. + +)"" diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 3f796291a56..344c10edd82 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -103,6 +103,7 @@ nix_tests = \ nix-profile.sh \ suggestions.sh \ store-info.sh \ + store-add-gc-root.sh \ fetchClosure.sh \ completions.sh \ impure-derivations.sh \ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 69b6d31949e..50562f3af37 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -184,6 +184,7 @@ suites = [ 'debugger.sh', 'extra-sandbox-profile.sh', 'help.sh', + 'store-add-gc-root.sh', ], 'workdir': meson.current_build_dir(), }, diff --git a/tests/functional/store-add-gc-root.sh b/tests/functional/store-add-gc-root.sh new file mode 100755 index 00000000000..7b9185f5fca --- /dev/null +++ b/tests/functional/store-add-gc-root.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +source common.sh + +TODO_NixOS + +clearStore + +function delete() { nix-store --delete "$storePath"; } +function build() { nix build --no-link --print-out-paths -f simple.nix; } + +# Check that path can be deleted +storePath=$(build) +delete + +# Check that add-gc-root prevents deletion, +# and removing the root make it deletable again. +storePath=$(build) +ln -sn "$storePath" myroot +nix store add-gc-root myroot +if delete; then false; fi +rm myroot +delete + +# Create several roots at once +storePath=$(build) +ln -sn "$storePath" myroot1 +ln -sn "$storePath" myroot2 +ln -sn "$storePath" myroot3 +nix store add-gc-root myroot1 myroot2 myroot3 +if delete; then false; fi +rm myroot3 myroot2 +if delete; then false; fi +rm myroot1 +delete + +# Test detection of invalid roots +# 1. path deleted before root creation +storePath=$(build) +delete +ln -sn "$storePath" myroot +if nix store add-gc-root myroot; then false; fi +nix store add-gc-root --no-check myroot +rm myroot + +# 2. invalid path +ln -sn /invalid-target myroot +if nix store add-gc-root myroot; then false; fi +nix store add-gc-root --no-check myroot +rm myroot + +# Fail when trying to setup a direct root +storePath=$(build) +if nix store add-gc-root "$storePath"; then false; fi +