From a9c4f66cfb1618833cc70ceaf13733730b634193 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 5 Dec 2006 02:18:46 +0000 Subject: [PATCH] * Allow unprivileged users to run the garbage collector and to do `nix-store --delete'. But unprivileged users are not allowed to ignore liveness. * `nix-store --delete --ignore-liveness': ignore the runtime roots as well. --- src/libmain/shared.cc | 1 - src/libstore/Makefile.am | 2 +- src/libstore/build.cc | 1 - src/libstore/gc.cc | 8 +++--- src/libstore/gc.hh | 42 ----------------------------- src/libstore/local-store.cc | 1 - src/libstore/local-store.hh | 3 +++ src/libstore/remote-store.cc | 21 +++++++++++++++ src/libstore/remote-store.hh | 3 +++ src/libstore/store-api.hh | 48 +++++++++++++++++++++++++++++++++ src/libstore/worker-protocol.hh | 1 + src/nix-env/main.cc | 1 - src/nix-env/profiles.cc | 2 +- src/nix-instantiate/main.cc | 1 - src/nix-store/main.cc | 5 ++-- src/nix-worker/main.cc | 22 +++++++++++++++ 16 files changed, 106 insertions(+), 56 deletions(-) delete mode 100644 src/libstore/gc.hh diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index c1bd1b73a7..393f31fcda 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -1,6 +1,5 @@ #include "shared.hh" #include "globals.hh" -#include "gc.hh" #include "store-api.hh" #include "util.hh" diff --git a/src/libstore/Makefile.am b/src/libstore/Makefile.am index bb217464e0..21b1545f6c 100644 --- a/src/libstore/Makefile.am +++ b/src/libstore/Makefile.am @@ -6,7 +6,7 @@ libstore_la_SOURCES = \ pkginclude_HEADERS = \ store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \ - globals.hh db.hh references.hh pathlocks.hh gc.hh \ + globals.hh db.hh references.hh pathlocks.hh \ worker-protocol.hh libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 8a109bcefd..202fb0ac44 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -2,7 +2,6 @@ #include "pathlocks.hh" #include "misc.hh" #include "globals.hh" -#include "gc.hh" #include "local-store.hh" #include "db.hh" #include "util.hh" diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index e5217c9b86..4d8f25e655 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -1,4 +1,3 @@ -#include "gc.hh" #include "globals.hh" #include "misc.hh" #include "pathlocks.hh" @@ -428,7 +427,7 @@ static Paths topoSort(const PathSet & paths) } -void collectGarbage(GCAction action, const PathSet & pathsToDelete, +void LocalStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) { result.clear(); @@ -446,7 +445,7 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, /* Find the roots. Since we've grabbed the GC lock, the set of permanent roots cannot increase now. */ - Roots rootMap = ignoreLiveness ? Roots() : findRoots(true); + Roots rootMap = ignoreLiveness ? Roots() : nix::findRoots(true); PathSet roots; for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) @@ -456,7 +455,8 @@ void collectGarbage(GCAction action, const PathSet & pathsToDelete, NIX_ROOT_FINDER environment variable. This is typically used to add running programs to the set of roots (to prevent them from being garbage collected). */ - addAdditionalRoots(roots); + if (!ignoreLiveness) + addAdditionalRoots(roots); if (action == gcReturnRoots) { result = roots; diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh deleted file mode 100644 index d4f40afa2a..0000000000 --- a/src/libstore/gc.hh +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __GC_H -#define __GC_H - -#include "types.hh" - - -namespace nix { - - -/* Garbage collector operation. */ -typedef enum { - gcReturnRoots, - gcReturnLive, - gcReturnDead, - gcDeleteDead, - gcDeleteSpecific, -} GCAction; - -/* If `action' is set to `gcReturnRoots', find and return the set of - roots for the garbage collector. These are the store paths - symlinked to in the `gcroots' directory. If `action' is - `gcReturnLive', return the set of paths reachable from (i.e. in the - 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, const PathSet & pathsToDelete, - bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); - -/* Remove the temporary roots file for this process. Any temporary - root becomes garbage after this point unless it has been registered - as a (permanent) root. */ -void removeTempRoots(); - -/* Register a permanent GC root. */ -Path addPermRoot(const Path & storePath, const Path & gcRoot, - bool indirect, bool allowOutsideRootsDir = false); - - -} - - -#endif /* !__GC_H */ diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2f3f3a7da8..c1fcb035bc 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -4,7 +4,6 @@ #include "db.hh" #include "archive.hh" #include "pathlocks.hh" -#include "gc.hh" #include "aterm.hh" #include "derivations-ast.hh" diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index a7b3926cc7..58bb70d0e4 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -66,6 +66,9 @@ public: void syncWithGC(); Roots findRoots(); + + void collectGarbage(GCAction action, const PathSet & pathsToDelete, + bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); }; diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 6ddbb3e2a2..504eb52329 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -302,6 +302,27 @@ Roots RemoteStore::findRoots() } +void RemoteStore::collectGarbage(GCAction action, const PathSet & pathsToDelete, + bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) +{ + result.clear(); + bytesFreed = 0; + writeInt(wopCollectGarbage, to); + writeInt(action, to); + writeStringSet(pathsToDelete, to); + writeInt(ignoreLiveness, to); + + processStderr(); + + result = readStringSet(from); + + /* Ugh - NAR integers are 64 bits, but read/writeInt() aren't. */ + unsigned int lo = readInt(from); + unsigned int hi = readInt(from); + bytesFreed = (((unsigned long long) hi) << 32) | lo; +} + + void RemoteStore::processStderr() { unsigned int msg; diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index 11e7e328be..82cfb08e95 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -54,6 +54,9 @@ public: void syncWithGC(); Roots findRoots(); + + void collectGarbage(GCAction action, const PathSet & pathsToDelete, + bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed); private: AutoCloseFD fdSocket; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index bda232d3d2..19c5d81cf2 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -37,6 +37,16 @@ typedef list Substitutes; typedef std::map Roots; +/* Garbage collector operation. */ +typedef enum { + gcReturnRoots, + gcReturnLive, + gcReturnDead, + gcDeleteDead, + gcDeleteSpecific, +} GCAction; + + class StoreAPI { public: @@ -127,6 +137,33 @@ public: outside of the Nix store that point to `storePath'. */ virtual Roots findRoots() = 0; + /* Depending on `action', this function does the following: + + - `gcReturnRoots': find and return the set of roots for the + garbage collector. These are the store paths symlinked to in + the `gcroots' directory. + + - `gcReturnLive': return the set of paths reachable from + (i.e. in the closure of) the roots. + + - `gcReturnDead': return the set of paths not reachable from + the roots. + + - `gcDeleteDead': actually delete the latter set. + + - `gcDeleteSpecific': delete the paths listed in + `pathsToDelete', insofar as they are not reachable. + + If `ignoreLiveness' is set, then reachability from the roots is + ignored (dangerous!). However, the paths must still be + unreferenced *within* the store (i.e., there can be no other + store paths that depend on them). + + For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the + number of bytes that would be or was freed is returned in + `bytesFreed'. */ + virtual void collectGarbage(GCAction action, const PathSet & pathsToDelete, + bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed) = 0; }; @@ -177,6 +214,17 @@ std::pair computeStorePathForPath(const Path & srcPath, Path computeStorePathForText(const string & suffix, const string & s); +/* Remove the temporary roots file for this process. Any temporary + root becomes garbage after this point unless it has been registered + as a (permanent) root. */ +void removeTempRoots(); + + +/* Register a permanent GC root. */ +Path addPermRoot(const Path & storePath, const Path & gcRoot, + bool indirect, bool allowOutsideRootsDir = false); + + /* For now, there is a single global store API object, but we'll purify that in the future. */ extern boost::shared_ptr store; diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index bb5d9d7b34..b333910110 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -25,6 +25,7 @@ typedef enum { wopAddIndirectRoot, wopSyncWithGC, wopFindRoots, + wopCollectGarbage, } WorkerOp; diff --git a/src/nix-env/main.cc b/src/nix-env/main.cc index f2988485cd..90d799224e 100644 --- a/src/nix-env/main.cc +++ b/src/nix-env/main.cc @@ -2,7 +2,6 @@ #include "names.hh" #include "globals.hh" #include "misc.hh" -#include "gc.hh" #include "shared.hh" #include "parser.hh" #include "eval.hh" diff --git a/src/nix-env/profiles.cc b/src/nix-env/profiles.cc index 0014161842..75585b1b29 100644 --- a/src/nix-env/profiles.cc +++ b/src/nix-env/profiles.cc @@ -1,6 +1,6 @@ #include "profiles.hh" +#include "store-api.hh" #include "util.hh" -#include "gc.hh" #include #include diff --git a/src/nix-instantiate/main.cc b/src/nix-instantiate/main.cc index 71f0a44e27..ffd8a2e71f 100644 --- a/src/nix-instantiate/main.cc +++ b/src/nix-instantiate/main.cc @@ -2,7 +2,6 @@ #include #include "globals.hh" -#include "gc.hh" #include "shared.hh" #include "eval.hh" #include "parser.hh" diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index eb037bda19..701e839742 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -3,7 +3,6 @@ #include "globals.hh" #include "misc.hh" -#include "gc.hh" #include "archive.hh" #include "shared.hh" #include "dotgraph.hh" @@ -573,7 +572,7 @@ static void opGC(Strings opFlags, Strings opArgs) PathSet result; PrintFreed freed(action == gcDeleteDead || action == gcReturnDead, action == gcReturnDead); - collectGarbage(action, PathSet(), false, result, freed.bytesFreed); + store->collectGarbage(action, PathSet(), false, result, freed.bytesFreed); if (action != gcDeleteDead) { for (PathSet::iterator i = result.begin(); i != result.end(); ++i) @@ -601,7 +600,7 @@ static void opDelete(Strings opFlags, Strings opArgs) PathSet dummy; PrintFreed freed(true, false); - collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness, + store->collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness, dummy, freed.bytesFreed); } diff --git a/src/nix-worker/main.cc b/src/nix-worker/main.cc index e772421f39..4fa43f643c 100644 --- a/src/nix-worker/main.cc +++ b/src/nix-worker/main.cc @@ -281,6 +281,28 @@ static void performOp(Source & from, Sink & to, unsigned int op) break; } + case wopCollectGarbage: { + GCAction action = (GCAction) readInt(from); + PathSet pathsToDelete = readStorePaths(from); + bool ignoreLiveness = readInt(from); + + PathSet result; + unsigned long long bytesFreed; + + startWork(); + if (ignoreLiveness) + throw Error("you are not allowed to ignore liveness"); + store->collectGarbage(action, pathsToDelete, ignoreLiveness, + result, bytesFreed); + stopWork(); + + writeStringSet(result, to); + writeInt(bytesFreed & 0xffffffff, to); + writeInt(bytesFreed >> 32, to); + + break; + } + default: throw Error(format("invalid operation %1%") % op); }