From 4b9e7f59ca14c3de7b0cfdaebca98fa2639bbbf9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 23 Dec 2005 21:08:42 +0000 Subject: [PATCH] * Revived the old "nix-store --delete" operation that deletes the specified paths from the Nix store. However, this operation is safe: it refuses to delete anything that the garbage collector wouldn't delete. --- src/libstore/gc.cc | 27 +++++++++++++++++++-------- src/libstore/gc.hh | 5 +++-- src/nix-store/help.txt | 1 + src/nix-store/main.cc | 23 ++++++++++++++++++++++- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index cb808b6d1..c02f59f2c 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -303,8 +303,8 @@ static Paths topoSort(const PathSet & paths) } -void collectGarbage(GCAction action, PathSet & result, - unsigned long long & bytesFreed) +void collectGarbage(GCAction action, const PathSet & pathsToDelete, + PathSet & result, unsigned long long & bytesFreed) { result.clear(); bytesFreed = 0; @@ -398,17 +398,26 @@ void collectGarbage(GCAction action, PathSet & result, /* Read the Nix store directory to find all currently existing paths. */ - Paths storePaths = readDirectory(nixStore); - PathSet storePaths2; - for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) - storePaths2.insert(canonPath(nixStore + "/" + *i)); + PathSet storePathSet; + if (action != gcDeleteSpecific) { + Paths entries = readDirectory(nixStore); + for (Paths::iterator i = entries.begin(); i != entries.end(); ++i) + storePathSet.insert(canonPath(nixStore + "/" + *i)); + } else { + for (PathSet::iterator i = pathsToDelete.begin(); + i != pathsToDelete.end(); ++i) + { + assertStorePath(*i); + storePathSet.insert(*i); + } + } /* Topologically sort them under the `referrers' relation. That is, a < b iff a is in referrers(b). This gives us the order in which things can be deleted safely. */ /* !!! when we have multiple output paths per derivation, this will not work anymore because we get cycles. */ - storePaths = topoSort(storePaths2); + Paths storePaths = topoSort(storePathSet); /* Try to delete store paths in the topologically sorted order. */ for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) { @@ -416,6 +425,8 @@ void collectGarbage(GCAction action, PathSet & result, debug(format("considering deletion of `%1%'") % *i); if (livePaths.find(*i) != livePaths.end()) { + if (action == gcDeleteSpecific) + throw Error(format("cannot delete path `%1%' since it is still alive") % *i); debug(format("live path `%1%'") % *i); continue; } @@ -430,7 +441,7 @@ void collectGarbage(GCAction action, PathSet & result, AutoCloseFD fdLock; - if (action == gcDeleteDead) { + if (action == gcDeleteDead || action == gcDeleteSpecific) { /* Only delete a lock file if we can acquire a write lock on it. That means that it's either stale, or the diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh index eb1858729..b05d88f93 100644 --- a/src/libstore/gc.hh +++ b/src/libstore/gc.hh @@ -10,6 +10,7 @@ typedef enum { gcReturnLive, gcReturnDead, gcDeleteDead, + gcDeleteSpecific, } GCAction; /* If `action' is set to `gcReturnRoots', find and return the set of @@ -19,8 +20,8 @@ typedef enum { closure of) the roots. If `action' is `gcReturnDead', return the set of paths not reachable from the roots. If `action' is `gcDeleteDead', actually delete the latter set. */ -void collectGarbage(GCAction action, PathSet & result, - unsigned long long & bytesFreed); +void collectGarbage(GCAction action, const PathSet & pathsToDelete, + PathSet & result, unsigned long long & bytesFreed); /* Register a temporary GC root. This root will automatically disappear when this process exits. WARNING: this function should diff --git a/src/nix-store/help.txt b/src/nix-store/help.txt index 628805835..074745813 100644 --- a/src/nix-store/help.txt +++ b/src/nix-store/help.txt @@ -7,6 +7,7 @@ Operations: --realise / -r: ensure path validity; if a derivation, ensure that validity of the outputs --add / -A: copy a path to the Nix store + --delete: safely delete paths from the Nix store --query / -q: query information --register-substitutes: register a substitute expression (dangerous!) diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index 8bb1b1254..6f1fab13d 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -518,7 +518,7 @@ static void opGC(Strings opFlags, Strings opArgs) PathSet result; PrintFreed freed(action == gcDeleteDead); - collectGarbage(action, result, freed.bytesFreed); + collectGarbage(action, PathSet(), result, freed.bytesFreed); if (action != gcDeleteDead) { for (PathSet::iterator i = result.begin(); i != result.end(); ++i) @@ -527,6 +527,25 @@ static void opGC(Strings opFlags, Strings opArgs) } +/* Remove paths from the Nix store if possible (i.e., if they do not + have any remaining referrers and are not reachable from any GC + roots). */ +static void opDelete(Strings opFlags, Strings opArgs) +{ + if (!opFlags.empty()) throw UsageError("unknown flag"); + + PathSet pathsToDelete; + for (Strings::iterator i = opArgs.begin(); + i != opArgs.end(); ++i) + pathsToDelete.insert(fixPath(*i)); + + PathSet dummy; + PrintFreed freed(true); + collectGarbage(gcDeleteSpecific, pathsToDelete, + dummy, freed.bytesFreed); +} + + /* A sink that writes dump output to stdout. */ struct StdoutSink : DumpSink { @@ -621,6 +640,8 @@ void run(Strings args) op = opAddFixed; else if (arg == "--print-fixed-path") op = opPrintFixedPath; + else if (arg == "--delete") + op = opDelete; else if (arg == "--query" || arg == "-q") op = opQuery; else if (arg == "--register-substitutes")