From 11335b96c0e6728f19964f3cdab9e37ce0a078aa Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Sun, 15 Sep 2024 22:52:27 +0200 Subject: [PATCH 1/4] Add an 'add-root' comamnd to manually add indirect roots --- src/nix/add-root.cc | 64 +++++++++++++++++++++++++++++++++++++++++++++ src/nix/add-root.md | 18 +++++++++++++ src/nix/meson.build | 1 + 3 files changed, 83 insertions(+) create mode 100644 src/nix/add-root.cc create mode 100644 src/nix/add-root.md diff --git a/src/nix/add-root.cc b/src/nix/add-root.cc new file mode 100644 index 000000000..541cf2559 --- /dev/null +++ b/src/nix/add-root.cc @@ -0,0 +1,64 @@ +#include "command.hh" +#include "args.hh" +#include "shared.hh" +#include "store-cast.hh" +#include "indirect-root-store.hh" +#include "common-args.hh" +#include "strings.hh" +#include "installable-derived-path.hh" + +using namespace nix; + +struct CmdAddRoot : StoreCommand +{ + std::vector links; + bool checkResults = true; + + CmdAddRoot() + { + expectArgs({ + .label = "indirect-roots", + .handler = {&links}, + .completer = completePath, + }); + } + + std::string description() override + { + return "Add indirect gc-roots through the symlink arguments"; + } + + std::string doc() override + { + return +#include "add-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); + // TODO: ensure the path is safe from concurrent GC of fail. + } + + indirectRootStore.addIndirectRoot(indirectPath); + } + } +}; + +static auto rCmdAddRoot = registerCommand("add-root"); diff --git a/src/nix/add-root.md b/src/nix/add-root.md new file mode 100644 index 000000000..5a932a950 --- /dev/null +++ b/src/nix/add-root.md @@ -0,0 +1,18 @@ +R""( + +# Examples + + ```console + $ readlink foo + /nix/store/xxx + $ nix add-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/src/nix/meson.build b/src/nix/meson.build index 6edb768e3..68c55495a 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -68,6 +68,7 @@ subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/generate-header') nix_sources = [config_h] + files( + 'add-root.cc', 'add-to-store.cc', 'app.cc', 'self-exe.cc', From 59c6f2ba9e6241f8ca8dfb43d1cce4b2847632bb Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Tue, 17 Sep 2024 11:50:10 +0200 Subject: [PATCH 2/4] Rename to 'nix store add-gc-root' --- src/nix/meson.build | 2 +- src/nix/{add-root.cc => store-add-gc-root.cc} | 14 ++++++-------- src/nix/{add-root.md => store-add-gc-root.md} | 5 ++--- 3 files changed, 9 insertions(+), 12 deletions(-) rename src/nix/{add-root.cc => store-add-gc-root.cc} (78%) rename src/nix/{add-root.md => store-add-gc-root.md} (85%) diff --git a/src/nix/meson.build b/src/nix/meson.build index 68c55495a..9968a9342 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -68,7 +68,6 @@ subdir('build-utils-meson/diagnostics') subdir('build-utils-meson/generate-header') nix_sources = [config_h] + files( - 'add-root.cc', 'add-to-store.cc', 'app.cc', 'self-exe.cc', @@ -106,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/add-root.cc b/src/nix/store-add-gc-root.cc similarity index 78% rename from src/nix/add-root.cc rename to src/nix/store-add-gc-root.cc index 541cf2559..cecab2564 100644 --- a/src/nix/add-root.cc +++ b/src/nix/store-add-gc-root.cc @@ -4,17 +4,15 @@ #include "store-cast.hh" #include "indirect-root-store.hh" #include "common-args.hh" -#include "strings.hh" -#include "installable-derived-path.hh" using namespace nix; -struct CmdAddRoot : StoreCommand +struct CmdAddGCRoot : StoreCommand { std::vector links; bool checkResults = true; - CmdAddRoot() + CmdAddGCRoot() { expectArgs({ .label = "indirect-roots", @@ -25,13 +23,13 @@ struct CmdAddRoot : StoreCommand std::string description() override { - return "Add indirect gc-roots through the symlink arguments"; + return "Add indirect gc roots through the symlink arguments"; } std::string doc() override { return -#include "add-root.md" +#include "store-add-gc-root.md" ; } @@ -53,7 +51,7 @@ struct CmdAddRoot : StoreCommand if (checkResults) { auto path = indirectRootStore.followLinksToStorePath(indirectPath); indirectRootStore.addTempRoot(path); - // TODO: ensure the path is safe from concurrent GC of fail. + // TODO: ensure `path` is safe from concurrent GC or fail. } indirectRootStore.addIndirectRoot(indirectPath); @@ -61,4 +59,4 @@ struct CmdAddRoot : StoreCommand } }; -static auto rCmdAddRoot = registerCommand("add-root"); +static auto rCmdAddGCRoot = registerCommand2({"store", "add-gc-root"}); diff --git a/src/nix/add-root.md b/src/nix/store-add-gc-root.md similarity index 85% rename from src/nix/add-root.md rename to src/nix/store-add-gc-root.md index 5a932a950..cd4841fb8 100644 --- a/src/nix/add-root.md +++ b/src/nix/store-add-gc-root.md @@ -3,9 +3,8 @@ R""( # Examples ```console - $ readlink foo - /nix/store/xxx - $ nix add-root foo + $ ln -s /nix/store/xxx foo + $ nix store add-gc-root foo $ nix-store -q --roots /nix/store/xxx .../foo -> /nix/store/xxx ``` From 7a0da9ed31bb9ec452fd25904ff40388aad618ee Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Tue, 17 Sep 2024 11:50:32 +0200 Subject: [PATCH 3/4] Add tests for add-gc-root --- src/nix/store-add-gc-root.cc | 10 ++++- tests/functional/local.mk | 1 + tests/functional/meson.build | 1 + tests/functional/store-add-gc-root.sh | 53 +++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100755 tests/functional/store-add-gc-root.sh diff --git a/src/nix/store-add-gc-root.cc b/src/nix/store-add-gc-root.cc index cecab2564..a29cd2c0c 100644 --- a/src/nix/store-add-gc-root.cc +++ b/src/nix/store-add-gc-root.cc @@ -19,6 +19,12 @@ struct CmdAddGCRoot : StoreCommand .handler = {&links}, .completer = completePath, }); + + addFlag({ + .longName = "no-check", + .description = "Do not test the validity of created roots.", + .handler = {&checkResults, false}, + }); } std::string description() override @@ -51,7 +57,9 @@ struct CmdAddGCRoot : StoreCommand if (checkResults) { auto path = indirectRootStore.followLinksToStorePath(indirectPath); indirectRootStore.addTempRoot(path); - // TODO: ensure `path` is safe from concurrent GC or fail. + if (!indirectRootStore.isValidPath(path)) { + throw Error("Indirect root '%1%' is no a symbolic link to a valid store path", link); + } } indirectRootStore.addIndirectRoot(indirectPath); diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 3f796291a..344c10edd 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 69b6d3194..50562f3af 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 000000000..076300f43 --- /dev/null +++ b/tests/functional/store-add-gc-root.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +source common.sh + +clearStoreIfPossible + +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 + From d01ee1dcc84680f6335107db76d3f6f8954e6000 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Tue, 1 Oct 2024 06:24:35 +0200 Subject: [PATCH 4/4] Clear nix store before add-gc-root tests --- tests/functional/store-add-gc-root.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/store-add-gc-root.sh b/tests/functional/store-add-gc-root.sh index 076300f43..7b9185f5f 100755 --- a/tests/functional/store-add-gc-root.sh +++ b/tests/functional/store-add-gc-root.sh @@ -2,7 +2,9 @@ source common.sh -clearStoreIfPossible +TODO_NixOS + +clearStore function delete() { nix-store --delete "$storePath"; } function build() { nix build --no-link --print-out-paths -f simple.nix; }