diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2005-02-01 15:05:32 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2005-02-01 15:05:32 +0000 |
commit | 65b6c8ab4c7832abdad46a29ce2ef18d289b2471 (patch) | |
tree | 3038d7ed1f60efdf2e2dad43cfec93023d7c699f /src | |
parent | 630ae0c9d7f65a2d6bef85a5194b4d704e54eded (diff) |
* Move root finding from `nix-collect-garbage' to `nix-store --gc'.
This was necessary becase root finding must be done after
acquisition of the global GC lock.
This makes `nix-collect-garbage' obsolete; it is now just a wrapper
around `nix-store --gc'.
* Automatically remove stale GC roots (i.e., indirect GC roots that
point to non-existent paths).
Diffstat (limited to 'src')
-rw-r--r-- | src/libstore/gc.cc | 59 | ||||
-rw-r--r-- | src/libstore/gc.hh | 24 | ||||
-rw-r--r-- | src/nix-store/main.cc | 20 |
3 files changed, 74 insertions, 29 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 8385e31b1..323acf265 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -26,7 +26,9 @@ static int openGCLock(LockType lockType) { Path fnGCLock = (format("%1%/%2%") % nixStateDir % gcLockName).str(); - + + debug(format("acquiring global GC lock `%1%'") % fnGCLock); + AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600); if (fdGCLock == -1) throw SysError(format("opening global GC lock `%1%'") % fnGCLock); @@ -234,6 +236,46 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds) } +static void findRoots(const Path & path, bool recurseSymlinks, + PathSet & roots) +{ + struct stat st; + if (lstat(path.c_str(), &st) == -1) + throw SysError(format("statting `%1%'") % path); + + printMsg(lvlVomit, format("looking at `%1%'") % path); + + if (S_ISDIR(st.st_mode)) { + Strings names = readDirectory(path); + for (Strings::iterator i = names.begin(); i != names.end(); ++i) + findRoots(path + "/" + *i, recurseSymlinks, roots); + } + + else if (S_ISLNK(st.st_mode)) { + string target = readLink(path); + Path target2 = absPath(target, dirOf(path)); + + if (isStorePath(target2)) { + debug(format("found root `%1%' in `%2%'") + % target2 % path); + roots.insert(target2); + } + + else if (recurseSymlinks) { + if (pathExists(target2)) + findRoots(target2, false, roots); + else { + printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target2); + /* Note that we only delete when recursing, i.e., when + we are still in the `gcroots' tree. We never + delete stuff outside that tree. */ + unlink(path.c_str()); + } + } + } +} + + static void dfsVisit(const PathSet & paths, const Path & path, PathSet & visited, Paths & sorted) { @@ -265,8 +307,7 @@ static Paths topoSort(const PathSet & paths) } -void collectGarbage(const PathSet & roots, GCAction action, - PathSet & result) +void collectGarbage(GCAction action, PathSet & result) { result.clear(); @@ -275,8 +316,16 @@ void collectGarbage(const PathSet & roots, GCAction action, b) Processes from creating new temporary root files. */ AutoCloseFD fdGCLock = openGCLock(ltWrite); - /* !!! Find the roots here, after we've grabbed the GC lock, since - the set of permanent roots cannot increase now. */ + /* Find the roots. Since we've grabbed the GC lock, the set of + permanent roots cannot increase now. */ + Path rootsDir = canonPath((format("%1%/%2%") % nixStateDir % gcRootsDir).str()); + PathSet roots; + findRoots(rootsDir, true, roots); + + if (action == gcReturnRoots) { + result = roots; + return; + } /* Determine the live paths which is just the closure of the roots under the `references' relation. */ diff --git a/src/libstore/gc.hh b/src/libstore/gc.hh index e3da4505d..b6a367c4b 100644 --- a/src/libstore/gc.hh +++ b/src/libstore/gc.hh @@ -5,15 +5,21 @@ /* Garbage collector operation. */ -typedef enum { gcReturnLive, gcReturnDead, gcDeleteDead } GCAction; - -/* If `action' is set to `soReturnLive', return the set of paths - reachable from (i.e. in the closure of) the specified roots. If - `action' is `soReturnDead', return the set of paths not reachable - from the roots. If `action' is `soDeleteDead', actually delete the - latter set. */ -void collectGarbage(const PathSet & roots, GCAction action, - PathSet & result); +typedef enum { + gcReturnRoots, + gcReturnLive, + gcReturnDead, + gcDeleteDead, +} 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, PathSet & result); /* Register a temporary GC root. This root will automatically disappear when this process exits. WARNING: this function should diff --git a/src/nix-store/main.cc b/src/nix-store/main.cc index 3edcff7ee..d473475b8 100644 --- a/src/nix-store/main.cc +++ b/src/nix-store/main.cc @@ -38,9 +38,7 @@ static Path followSymlinks(Path & path) while (!isStorePath(path)) { if (!isLink(path)) return path; string target = readLink(path); - path = canonPath(string(target, 0, 1) == "/" - ? target - : path + "/" + target); + path = absPath(target, dirOf(path)); } return path; } @@ -308,27 +306,19 @@ static void opIsValid(Strings opFlags, Strings opArgs) static void opGC(Strings opFlags, Strings opArgs) { - GCAction action; + GCAction action = gcDeleteDead; /* Do what? */ for (Strings::iterator i = opFlags.begin(); i != opFlags.end(); ++i) - if (*i == "--print-live") action = gcReturnLive; + if (*i == "--print-roots") action = gcReturnRoots; + else if (*i == "--print-live") action = gcReturnLive; else if (*i == "--print-dead") action = gcReturnDead; else if (*i == "--delete") action = gcDeleteDead; else throw UsageError(format("bad sub-operation `%1%' in GC") % *i); - /* Read the roots. */ - PathSet roots; - while (1) { - Path root; - getline(cin, root); - if (cin.eof()) break; - roots.insert(root); - } - PathSet result; - collectGarbage(roots, action, result); + collectGarbage(action, result); if (action != gcDeleteDead) { for (PathSet::iterator i = result.begin(); i != result.end(); ++i) |