* 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.
This commit is contained in:
Eelco Dolstra 2005-12-23 21:08:42 +00:00
parent 3c5619c7e4
commit 4b9e7f59ca
4 changed files with 45 additions and 11 deletions

View file

@ -303,8 +303,8 @@ static Paths topoSort(const PathSet & paths)
} }
void collectGarbage(GCAction action, PathSet & result, void collectGarbage(GCAction action, const PathSet & pathsToDelete,
unsigned long long & bytesFreed) PathSet & result, unsigned long long & bytesFreed)
{ {
result.clear(); result.clear();
bytesFreed = 0; bytesFreed = 0;
@ -398,17 +398,26 @@ void collectGarbage(GCAction action, PathSet & result,
/* Read the Nix store directory to find all currently existing /* Read the Nix store directory to find all currently existing
paths. */ paths. */
Paths storePaths = readDirectory(nixStore); PathSet storePathSet;
PathSet storePaths2; if (action != gcDeleteSpecific) {
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) Paths entries = readDirectory(nixStore);
storePaths2.insert(canonPath(nixStore + "/" + *i)); 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 /* Topologically sort them under the `referrers' relation. That
is, a < b iff a is in referrers(b). This gives us the order in is, a < b iff a is in referrers(b). This gives us the order in
which things can be deleted safely. */ which things can be deleted safely. */
/* !!! when we have multiple output paths per derivation, this /* !!! when we have multiple output paths per derivation, this
will not work anymore because we get cycles. */ 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. */ /* Try to delete store paths in the topologically sorted order. */
for (Paths::iterator i = storePaths.begin(); i != storePaths.end(); ++i) { 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); debug(format("considering deletion of `%1%'") % *i);
if (livePaths.find(*i) != livePaths.end()) { 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); debug(format("live path `%1%'") % *i);
continue; continue;
} }
@ -430,7 +441,7 @@ void collectGarbage(GCAction action, PathSet & result,
AutoCloseFD fdLock; AutoCloseFD fdLock;
if (action == gcDeleteDead) { if (action == gcDeleteDead || action == gcDeleteSpecific) {
/* Only delete a lock file if we can acquire a write lock /* Only delete a lock file if we can acquire a write lock
on it. That means that it's either stale, or the on it. That means that it's either stale, or the

View file

@ -10,6 +10,7 @@ typedef enum {
gcReturnLive, gcReturnLive,
gcReturnDead, gcReturnDead,
gcDeleteDead, gcDeleteDead,
gcDeleteSpecific,
} GCAction; } GCAction;
/* If `action' is set to `gcReturnRoots', find and return the set of /* 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 closure of) the roots. If `action' is `gcReturnDead', return the
set of paths not reachable from the roots. If `action' is set of paths not reachable from the roots. If `action' is
`gcDeleteDead', actually delete the latter set. */ `gcDeleteDead', actually delete the latter set. */
void collectGarbage(GCAction action, PathSet & result, void collectGarbage(GCAction action, const PathSet & pathsToDelete,
unsigned long long & bytesFreed); PathSet & result, unsigned long long & bytesFreed);
/* Register a temporary GC root. This root will automatically /* Register a temporary GC root. This root will automatically
disappear when this process exits. WARNING: this function should disappear when this process exits. WARNING: this function should

View file

@ -7,6 +7,7 @@ Operations:
--realise / -r: ensure path validity; if a derivation, ensure that --realise / -r: ensure path validity; if a derivation, ensure that
validity of the outputs validity of the outputs
--add / -A: copy a path to the Nix store --add / -A: copy a path to the Nix store
--delete: safely delete paths from the Nix store
--query / -q: query information --query / -q: query information
--register-substitutes: register a substitute expression (dangerous!) --register-substitutes: register a substitute expression (dangerous!)

View file

@ -518,7 +518,7 @@ static void opGC(Strings opFlags, Strings opArgs)
PathSet result; PathSet result;
PrintFreed freed(action == gcDeleteDead); PrintFreed freed(action == gcDeleteDead);
collectGarbage(action, result, freed.bytesFreed); collectGarbage(action, PathSet(), result, freed.bytesFreed);
if (action != gcDeleteDead) { if (action != gcDeleteDead) {
for (PathSet::iterator i = result.begin(); i != result.end(); ++i) 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. */ /* A sink that writes dump output to stdout. */
struct StdoutSink : DumpSink struct StdoutSink : DumpSink
{ {
@ -621,6 +640,8 @@ void run(Strings args)
op = opAddFixed; op = opAddFixed;
else if (arg == "--print-fixed-path") else if (arg == "--print-fixed-path")
op = opPrintFixedPath; op = opPrintFixedPath;
else if (arg == "--delete")
op = opDelete;
else if (arg == "--query" || arg == "-q") else if (arg == "--query" || arg == "-q")
op = opQuery; op = opQuery;
else if (arg == "--register-substitutes") else if (arg == "--register-substitutes")