From 630ae0c9d7f65a2d6bef85a5194b4d704e54eded Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 1 Feb 2005 13:48:46 +0000 Subject: [PATCH] * nix-build: use an indirection scheme to make it easier for users to get rid of GC roots. Nix-build places a symlink `result' in the current directory. Previously, removing that symlink would not remove the store path being linked to as a GC root. Now, the GC root created by nix-build is actually a symlink in `/nix/var/nix/gcroots/auto' to `result'. So if that symlink is removed the GC root automatically becomes invalid (since it can no longer be resolved). The root itself is not automatically removed - the garbage collector should delete dangling roots. --- scripts/nix-build.in | 47 +++++++++++++++++----------- src/libstore/gc.cc | 61 +++++++++++++++++++++++++------------ src/libstore/gc.hh | 3 +- src/libutil/util.cc | 9 ++++++ src/libutil/util.hh | 2 ++ src/nix-instantiate/main.cc | 8 +++-- src/nix-store/main.cc | 21 +++++++------ 7 files changed, 101 insertions(+), 50 deletions(-) diff --git a/scripts/nix-build.in b/scripts/nix-build.in index 33fbc61a3..e11a88dcd 100644 --- a/scripts/nix-build.in +++ b/scripts/nix-build.in @@ -8,41 +8,52 @@ if test -z "$nixExpr"; then fi extraArgs= -noLink= +addDrvLink=0 +addOutLink=1 -userName=$USER -if test -z "$username"; then userName="unknown"; fi +trap 'rm -f ./.nix-build-tmp-*' EXIT + + +# Process the arguments. for i in "$@"; do case "$i" in - --no-link) - noLink=1 + + --add-drv-link) + addDrvLink=1 ;; + + --no-link) + addOutLink=0 + ;; + -*) extraArgs="$extraArgs $i" ;; + *) + # Instantiate the Nix expression. + prefix= + if test "$addDrvLink" = 0; then prefix=.nix-build-tmp-; fi storeExprs=$(@bindir@/nix-instantiate \ - --add-root "@localstatedir@/nix/gcroots/nix-build/$userName-drv" \ + --add-root ./${prefix}derivation --indirect \ "$i") + for j in $storeExprs; do - echo "store expression is $j" >&2 + echo "store expression is $j $(readlink "$j")" >&2 done + + # Build the resulting store derivation. + prefix= + if test "$addOutLink" = 0; then prefix=.nix-build-tmp-; fi outPaths=$(@bindir@/nix-store \ - --add-root "@localstatedir@/nix/gcroots/nix-build/$userName-out" \ + --add-root ./${prefix}result --indirect \ -rv $extraArgs $storeExprs) + for j in $outPaths; do - echo "$j" - if test -z "$noLink"; then - if test -L result; then - rm result - elif test -e result; then - echo "cannot remove \`result' (not a symlink)" - exit 1 - fi - ln -s "$j" result - fi + echo "$j $(readlink "$j")" done + ;; esac done diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ee9a369dc..8385e31b1 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -13,6 +13,8 @@ static string gcLockName = "gc.lock"; +static string tempRootsDir = "temproots"; +static string gcRootsDir = "gcroots"; /* Acquire the global GC lock. This is used to prevent new Nix @@ -49,40 +51,61 @@ static void createDirs(const Path & path) } -Path addPermRoot(const Path & _storePath, const Path & _gcRoot) +void createSymlink(const Path & link, const Path & target, bool careful) +{ + /* Create directories up to `gcRoot'. */ + createDirs(dirOf(link)); + + /* Remove the old symlink. */ + if (pathExists(link)) { + if (careful && (!isLink(link) || !isStorePath(readLink(link)))) + throw Error(format("cannot create symlink `%1%'; already exists") % link); + unlink(link.c_str()); + } + + /* And create the new own. */ + if (symlink(target.c_str(), link.c_str()) == -1) + throw SysError(format("symlinking `%1%' to `%2%'") + % link % target); +} + + +Path addPermRoot(const Path & _storePath, const Path & _gcRoot, + bool indirect) { Path storePath(canonPath(_storePath)); Path gcRoot(canonPath(_gcRoot)); - - Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % "gcroots").str()); - - if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/") - throw Error(format( - "path `%1%' is not a valid garbage collector root; " - "it's not in the `%1%' directory") - % gcRoot % rootsDir); + assertStorePath(storePath); /* Grab the global GC root. This prevents the set of permanent roots from increasing while a GC is in progress. */ AutoCloseFD fdGCLock = openGCLock(ltRead); - /* Create directories up to `gcRoot'. */ - createDirs(dirOf(gcRoot)); + if (indirect) { + string hash = printHash32(hashString(htSHA1, gcRoot)); + Path realRoot = canonPath((format("%1%/%2%/auto/%3%") + % nixStateDir % gcRootsDir % hash).str()); - /* Remove the old symlink. */ - unlink(gcRoot.c_str()); + createSymlink(gcRoot, storePath, true); + createSymlink(realRoot, gcRoot, false); + } - /* And create the new own. */ - if (symlink(storePath.c_str(), gcRoot.c_str()) == -1) - throw SysError(format("symlinking `%1%' to `%2%'") - % gcRoot % storePath); + else { + Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); + + if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/") + throw Error(format( + "path `%1%' is not a valid garbage collector root; " + "it's not in the directory `%2%'") + % gcRoot % rootsDir); + + createSymlink(gcRoot, storePath, false); + } return gcRoot; } -static string tempRootsDir = "temproots"; - /* The file to which we write our temporary roots. */ static Path fnTempRoots; static AutoCloseFD fdTempRoots; diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh index 03e1d7691..e3da4505d 100644 --- a/src/libstore/gc.hh +++ b/src/libstore/gc.hh @@ -27,7 +27,8 @@ void addTempRoot(const Path & path); void removeTempRoots(); /* Register a permanent GC root. */ -Path addPermRoot(const Path & storePath, const Path & gcRoot); +Path addPermRoot(const Path & storePath, const Path & gcRoot, + bool indirect); #endif /* !__GC_H */ diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 5b6fb6202..108c054b7 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -135,6 +135,15 @@ Path readLink(const Path & path) } +bool isLink(const Path & path) +{ + struct stat st; + if (lstat(path.c_str(), &st)) + throw SysError(format("getting status of `%1%'") % path); + return S_ISLNK(st.st_mode); +} + + Strings readDirectory(const Path & path) { Strings names; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 104e3f265..d9d5a7cdf 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -84,6 +84,8 @@ bool pathExists(const Path & path); in any way canonicalised. */ Path readLink(const Path & path); +bool isLink(const Path & path); + /* Read the contents of a directory. The entries `.' and `..' are removed. */ Strings readDirectory(const Path & path); diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc index 7d12c201f..3e9fad4ae 100644 --- a/src/nix-instantiate/main.cc +++ b/src/nix-instantiate/main.cc @@ -29,6 +29,7 @@ static Expr evalStdin(EvalState & state, bool parseOnly) static Path gcRoot; static int rootNr = 0; +static bool indirectRoot = false; /* Print out the paths of the resulting derivation(s). If the user @@ -51,7 +52,8 @@ static void printDrvPaths(EvalState & state, Expr e) printGCWarning(); else drvPath = addPermRoot(drvPath, - makeRootName(gcRoot, rootNr)); + makeRootName(gcRoot, rootNr), + indirectRoot); cout << format("%1%\n") % drvPath; return; } @@ -110,8 +112,10 @@ void run(Strings args) else if (arg == "--add-root") { if (i == args.end()) throw UsageError("`--add-root requires an argument"); - gcRoot = *i++; + gcRoot = absPath(*i++); } + else if (arg == "--indirect") + indirectRoot = true; else if (arg[0] == '-') throw UsageError(format("unknown flag `%1%`") % arg); else diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index c1fedaf48..3edcff7ee 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -1,9 +1,5 @@ #include -#include -#include -#include - #include "globals.hh" #include "build.hh" #include "gc.hh" @@ -24,6 +20,7 @@ void printHelp() static Path gcRoot; static int rootNr = 0; +static bool indirectRoot = false; static Path findOutput(const Derivation & drv, string id) @@ -37,11 +34,9 @@ static Path findOutput(const Derivation & drv, string id) static Path followSymlinks(Path & path) { + path = absPath(path); while (!isStorePath(path)) { - struct stat st; - if (lstat(path.c_str(), &st)) - throw SysError(format("getting status of `%1%'") % path); - if (!S_ISLNK(st.st_mode)) return path; + if (!isLink(path)) return path; string target = readLink(path); path = canonPath(string(target, 0, 1) == "/" ? target @@ -64,7 +59,9 @@ static Path realisePath(const Path & path) if (gcRoot == "") printGCWarning(); else - outPath = addPermRoot(outPath, makeRootName(gcRoot, rootNr)); + outPath = addPermRoot(outPath, + makeRootName(gcRoot, rootNr), + indirectRoot); return outPath; } else { @@ -191,6 +188,7 @@ static void opQuery(Strings opFlags, Strings opArgs) for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); i++) { + *i = followSymlinks(*i); if (forceRealise) realisePath(*i); Derivation drv = derivationFromPath(*i); cout << format("%1%\n") % findOutput(drv, "out"); @@ -206,6 +204,7 @@ static void opQuery(Strings opFlags, Strings opArgs) for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); i++) { + *i = followSymlinks(*i); Path path = maybeUseOutput(*i, useOutput, forceRealise); if (query == qRequisites) storePathRequisites(path, includeOutputs, paths); @@ -441,8 +440,10 @@ void run(Strings args) else if (arg == "--add-root") { if (i == args.end()) throw UsageError("`--add-root requires an argument"); - gcRoot = *i++; + gcRoot = absPath(*i++); } + else if (arg == "--indirect") + indirectRoot = true; else if (arg[0] == '-') opFlags.push_back(arg); else