forked from lix-project/lix
* 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:
parent
29cf434a35
commit
a9c4f66cfb
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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,6 +455,7 @@ 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). */
|
||||||
|
if (!ignoreLiveness)
|
||||||
addAdditionalRoots(roots);
|
addAdditionalRoots(roots);
|
||||||
|
|
||||||
if (action == gcReturnRoots) {
|
if (action == gcReturnRoots) {
|
||||||
|
|
|
@ -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 */
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -55,6 +55,9 @@ public:
|
||||||
|
|
||||||
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;
|
||||||
FdSink to;
|
FdSink to;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -25,6 +25,7 @@ typedef enum {
|
||||||
wopAddIndirectRoot,
|
wopAddIndirectRoot,
|
||||||
wopSyncWithGC,
|
wopSyncWithGC,
|
||||||
wopFindRoots,
|
wopFindRoots,
|
||||||
|
wopCollectGarbage,
|
||||||
} WorkerOp;
|
} WorkerOp;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue