* 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.
This commit is contained in:
Eelco Dolstra 2006-12-05 02:18:46 +00:00
parent 29cf434a35
commit a9c4f66cfb
16 changed files with 106 additions and 56 deletions

View file

@ -1,6 +1,5 @@
#include "shared.hh" #include "shared.hh"
#include "globals.hh" #include "globals.hh"
#include "gc.hh"
#include "store-api.hh" #include "store-api.hh"
#include "util.hh" #include "util.hh"

View file

@ -6,7 +6,7 @@ libstore_la_SOURCES = \
pkginclude_HEADERS = \ pkginclude_HEADERS = \
store-api.hh local-store.hh remote-store.hh derivations.hh misc.hh \ 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 worker-protocol.hh
libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la libstore_la_LIBADD = ../libutil/libutil.la ../boost/format/libformat.la

View file

@ -2,7 +2,6 @@
#include "pathlocks.hh" #include "pathlocks.hh"
#include "misc.hh" #include "misc.hh"
#include "globals.hh" #include "globals.hh"
#include "gc.hh"
#include "local-store.hh" #include "local-store.hh"
#include "db.hh" #include "db.hh"
#include "util.hh" #include "util.hh"

View file

@ -1,4 +1,3 @@
#include "gc.hh"
#include "globals.hh" #include "globals.hh"
#include "misc.hh" #include "misc.hh"
#include "pathlocks.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) bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed)
{ {
result.clear(); 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 /* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */ permanent roots cannot increase now. */
Roots rootMap = ignoreLiveness ? Roots() : findRoots(true); Roots rootMap = ignoreLiveness ? Roots() : nix::findRoots(true);
PathSet roots; PathSet roots;
for (Roots::iterator i = rootMap.begin(); i != rootMap.end(); ++i) 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 NIX_ROOT_FINDER environment variable. This is typically used
to add running programs to the set of roots (to prevent them to add running programs to the set of roots (to prevent them
from being garbage collected). */ from being garbage collected). */
addAdditionalRoots(roots); if (!ignoreLiveness)
addAdditionalRoots(roots);
if (action == gcReturnRoots) { if (action == gcReturnRoots) {
result = roots; result = roots;

View file

@ -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 */

View file

@ -4,7 +4,6 @@
#include "db.hh" #include "db.hh"
#include "archive.hh" #include "archive.hh"
#include "pathlocks.hh" #include "pathlocks.hh"
#include "gc.hh"
#include "aterm.hh" #include "aterm.hh"
#include "derivations-ast.hh" #include "derivations-ast.hh"

View file

@ -66,6 +66,9 @@ public:
void syncWithGC(); void syncWithGC();
Roots findRoots(); Roots findRoots();
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
}; };

View file

@ -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() void RemoteStore::processStderr()
{ {
unsigned int msg; unsigned int msg;

View file

@ -54,6 +54,9 @@ public:
void syncWithGC(); void syncWithGC();
Roots findRoots(); Roots findRoots();
void collectGarbage(GCAction action, const PathSet & pathsToDelete,
bool ignoreLiveness, PathSet & result, unsigned long long & bytesFreed);
private: private:
AutoCloseFD fdSocket; AutoCloseFD fdSocket;

View file

@ -37,6 +37,16 @@ typedef list<Substitute> Substitutes;
typedef std::map<Path, Path> Roots; typedef std::map<Path, Path> Roots;
/* Garbage collector operation. */
typedef enum {
gcReturnRoots,
gcReturnLive,
gcReturnDead,
gcDeleteDead,
gcDeleteSpecific,
} GCAction;
class StoreAPI class StoreAPI
{ {
public: public:
@ -127,6 +137,33 @@ public:
outside of the Nix store that point to `storePath'. */ outside of the Nix store that point to `storePath'. */
virtual Roots findRoots() = 0; 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<Path, Hash> computeStorePathForPath(const Path & srcPath,
Path computeStorePathForText(const string & suffix, const string & s); 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 /* For now, there is a single global store API object, but we'll
purify that in the future. */ purify that in the future. */
extern boost::shared_ptr<StoreAPI> store; extern boost::shared_ptr<StoreAPI> store;

View file

@ -25,6 +25,7 @@ typedef enum {
wopAddIndirectRoot, wopAddIndirectRoot,
wopSyncWithGC, wopSyncWithGC,
wopFindRoots, wopFindRoots,
wopCollectGarbage,
} WorkerOp; } WorkerOp;

View file

@ -2,7 +2,6 @@
#include "names.hh" #include "names.hh"
#include "globals.hh" #include "globals.hh"
#include "misc.hh" #include "misc.hh"
#include "gc.hh"
#include "shared.hh" #include "shared.hh"
#include "parser.hh" #include "parser.hh"
#include "eval.hh" #include "eval.hh"

View file

@ -1,6 +1,6 @@
#include "profiles.hh" #include "profiles.hh"
#include "store-api.hh"
#include "util.hh" #include "util.hh"
#include "gc.hh"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>

View file

@ -2,7 +2,6 @@
#include <iostream> #include <iostream>
#include "globals.hh" #include "globals.hh"
#include "gc.hh"
#include "shared.hh" #include "shared.hh"
#include "eval.hh" #include "eval.hh"
#include "parser.hh" #include "parser.hh"

View file

@ -3,7 +3,6 @@
#include "globals.hh" #include "globals.hh"
#include "misc.hh" #include "misc.hh"
#include "gc.hh"
#include "archive.hh" #include "archive.hh"
#include "shared.hh" #include "shared.hh"
#include "dotgraph.hh" #include "dotgraph.hh"
@ -573,7 +572,7 @@ static void opGC(Strings opFlags, Strings opArgs)
PathSet result; PathSet result;
PrintFreed freed(action == gcDeleteDead || action == gcReturnDead, PrintFreed freed(action == gcDeleteDead || action == gcReturnDead,
action == gcReturnDead); action == gcReturnDead);
collectGarbage(action, PathSet(), false, result, freed.bytesFreed); store->collectGarbage(action, PathSet(), false, 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)
@ -601,7 +600,7 @@ static void opDelete(Strings opFlags, Strings opArgs)
PathSet dummy; PathSet dummy;
PrintFreed freed(true, false); PrintFreed freed(true, false);
collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness, store->collectGarbage(gcDeleteSpecific, pathsToDelete, ignoreLiveness,
dummy, freed.bytesFreed); dummy, freed.bytesFreed);
} }

View file

@ -281,6 +281,28 @@ static void performOp(Source & from, Sink & to, unsigned int op)
break; 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: default:
throw Error(format("invalid operation %1%") % op); throw Error(format("invalid operation %1%") % op);
} }