Revert "nix: Guard against removing temporary roots of living processes."
[jackhill/guix/guix.git] / nix / libstore / gc.cc
CommitLineData
36457566
LC
1#include "globals.hh"
2#include "misc.hh"
3#include "local-store.hh"
4
5#include <functional>
6#include <queue>
7#include <algorithm>
8
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <errno.h>
12#include <fcntl.h>
13#include <unistd.h>
732c96f1 14#include <climits>
36457566
LC
15
16
17namespace nix {
18
19
20static string gcLockName = "gc.lock";
21static string tempRootsDir = "temproots";
22static string gcRootsDir = "gcroots";
23
24
8327e733 25/* Acquire the global GC lock. This is used to prevent new build
36457566
LC
26 processes from starting after the temporary root files have been
27 read. To be precise: when they try to create a new temporary root
28 file, they will block until the garbage collector has finished /
29 yielded the GC lock. */
30int LocalStore::openGCLock(LockType lockType)
31{
32 Path fnGCLock = (format("%1%/%2%")
33 % settings.nixStateDir % gcLockName).str();
34
35 debug(format("acquiring global GC lock `%1%'") % fnGCLock);
36
37 AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
38 if (fdGCLock == -1)
39 throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
40 closeOnExec(fdGCLock);
41
42 if (!lockFile(fdGCLock, lockType, false)) {
43 printMsg(lvlError, format("waiting for the big garbage collector lock..."));
44 lockFile(fdGCLock, lockType, true);
45 }
46
47 /* !!! Restrict read permission on the GC root. Otherwise any
48 process that can open the file for reading can DoS the
49 collector. */
50
51 return fdGCLock.borrow();
52}
53
54
55static void makeSymlink(const Path & link, const Path & target)
56{
57 /* Create directories up to `gcRoot'. */
58 createDirs(dirOf(link));
59
60 /* Create the new symlink. */
61 Path tempLink = (format("%1%.tmp-%2%-%3%")
62 % link % getpid() % rand()).str();
63 createSymlink(target, tempLink);
64
65 /* Atomically replace the old one. */
66 if (rename(tempLink.c_str(), link.c_str()) == -1)
67 throw SysError(format("cannot rename `%1%' to `%2%'")
68 % tempLink % link);
69}
70
71
72void LocalStore::syncWithGC()
73{
74 AutoCloseFD fdGCLock = openGCLock(ltRead);
75}
76
77
78void LocalStore::addIndirectRoot(const Path & path)
79{
80 string hash = printHash32(hashString(htSHA1, path));
81 Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
82 % settings.nixStateDir % gcRootsDir % hash).str());
83 makeSymlink(realRoot, path);
84}
85
86
87Path addPermRoot(StoreAPI & store, const Path & _storePath,
88 const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir)
89{
90 Path storePath(canonPath(_storePath));
91 Path gcRoot(canonPath(_gcRoot));
92 assertStorePath(storePath);
93
94 if (isInStore(gcRoot))
95 throw Error(format(
8327e733 96 "creating a garbage collector root (%1%) in the store is forbidden "
36457566
LC
97 "(are you running nix-build inside the store?)") % gcRoot);
98
99 if (indirect) {
54c260e6 100 /* Don't clobber the link if it already exists and doesn't
8327e733 101 point to the store. */
36457566
LC
102 if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
103 throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
104 makeSymlink(gcRoot, storePath);
105 store.addIndirectRoot(gcRoot);
106 }
107
108 else {
109 if (!allowOutsideRootsDir) {
110 Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
111
112 if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
113 throw Error(format(
114 "path `%1%' is not a valid garbage collector root; "
115 "it's not in the directory `%2%'")
116 % gcRoot % rootsDir);
117 }
118
2bb04905
LC
119 if (baseNameOf(gcRoot) == baseNameOf(storePath))
120 writeFile(gcRoot, "");
121 else
122 makeSymlink(gcRoot, storePath);
36457566
LC
123 }
124
125 /* Check that the root can be found by the garbage collector.
126 !!! This can be very slow on machines that have many roots.
127 Instead of reading all the roots, it would be more efficient to
128 check if the root is in a directory in or linked from the
129 gcroots directory. */
130 if (settings.checkRootReachability) {
131 Roots roots = store.findRoots();
132 if (roots.find(gcRoot) == roots.end())
133 printMsg(lvlError,
134 format(
135 "warning: `%1%' is not in a directory where the garbage collector looks for roots; "
136 "therefore, `%2%' might be removed by the garbage collector")
137 % gcRoot % storePath);
138 }
139
140 /* Grab the global GC root, causing us to block while a GC is in
141 progress. This prevents the set of permanent roots from
142 increasing while a GC is in progress. */
143 store.syncWithGC();
144
145 return gcRoot;
146}
147
148
36457566
LC
149void LocalStore::addTempRoot(const Path & path)
150{
151 /* Create the temporary roots file for this process. */
152 if (fdTempRoots == -1) {
153
154 while (1) {
155 Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
156 createDirs(dir);
157
158 fnTempRoots = (format("%1%/%2%")
159 % dir % getpid()).str();
160
161 AutoCloseFD fdGCLock = openGCLock(ltRead);
162
163 if (pathExists(fnTempRoots))
164 /* It *must* be stale, since there can be no two
165 processes with the same pid. */
166 unlink(fnTempRoots.c_str());
167
168 fdTempRoots = openLockFile(fnTempRoots, true);
169
170 fdGCLock.close();
171
172 debug(format("acquiring read lock on `%1%'") % fnTempRoots);
173 lockFile(fdTempRoots, ltRead, true);
174
175 /* Check whether the garbage collector didn't get in our
176 way. */
177 struct stat st;
178 if (fstat(fdTempRoots, &st) == -1)
179 throw SysError(format("statting `%1%'") % fnTempRoots);
180 if (st.st_size == 0) break;
181
182 /* The garbage collector deleted this file before we could
183 get a lock. (It won't delete the file after we get a
184 lock.) Try again. */
185 }
186
187 }
188
189 /* Upgrade the lock to a write lock. This will cause us to block
190 if the garbage collector is holding our lock. */
191 debug(format("acquiring write lock on `%1%'") % fnTempRoots);
192 lockFile(fdTempRoots, ltWrite, true);
193
194 string s = path + '\0';
54c260e6 195 writeFull(fdTempRoots, s);
36457566
LC
196
197 /* Downgrade to a read lock. */
198 debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
199 lockFile(fdTempRoots, ltRead, true);
200}
201
202
36457566
LC
203typedef std::shared_ptr<AutoCloseFD> FDPtr;
204typedef list<FDPtr> FDs;
205
206
207static void readTempRoots(PathSet & tempRoots, FDs & fds)
208{
209 /* Read the `temproots' directory for per-process temporary root
210 files. */
2bb04905 211 DirEntries tempRootFiles = readDirectory(
36457566
LC
212 (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
213
2bb04905
LC
214 for (auto & i : tempRootFiles) {
215 Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % i.name).str();
36457566
LC
216
217 debug(format("reading temporary root file `%1%'") % path);
218 FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
219 if (*fd == -1) {
220 /* It's okay if the file has disappeared. */
221 if (errno == ENOENT) continue;
222 throw SysError(format("opening temporary roots file `%1%'") % path);
223 }
224
225 /* This should work, but doesn't, for some reason. */
226 //FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
227 //if (*fd == -1) continue;
228
eec920ba
LC
229 /* Try to acquire a write lock without blocking. This can
230 only succeed if the owning process has died. In that case
231 we don't care about its temporary roots. */
232 if (lockFile(*fd, ltWrite, false)) {
36457566
LC
233 printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
234 unlink(path.c_str());
54c260e6 235 writeFull(*fd, "d");
36457566
LC
236 continue;
237 }
238
239 /* Acquire a read lock. This will prevent the owning process
240 from upgrading to a write lock, therefore it will block in
241 addTempRoot(). */
242 debug(format("waiting for read lock on `%1%'") % path);
243 lockFile(*fd, ltRead, true);
244
245 /* Read the entire file. */
246 string contents = readFile(*fd);
247
248 /* Extract the roots. */
249 string::size_type pos = 0, end;
250
251 while ((end = contents.find((char) 0, pos)) != string::npos) {
252 Path root(contents, pos, end - pos);
253 debug(format("got temporary root `%1%'") % root);
254 assertStorePath(root);
255 tempRoots.insert(root);
256 pos = end + 1;
257 }
258
259 fds.push_back(fd); /* keep open */
260 }
261}
262
263
264static void foundRoot(StoreAPI & store,
265 const Path & path, const Path & target, Roots & roots)
266{
267 Path storePath = toStorePath(target);
268 if (store.isValidPath(storePath))
269 roots[path] = storePath;
270 else
271 printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
272}
273
274
2bb04905 275static void findRoots(StoreAPI & store, const Path & path, unsigned char type, Roots & roots)
36457566
LC
276{
277 try {
278
2bb04905
LC
279 if (type == DT_UNKNOWN)
280 type = getFileType(path);
36457566 281
2bb04905
LC
282 if (type == DT_DIR) {
283 for (auto & i : readDirectory(path))
284 findRoots(store, path + "/" + i.name, i.type, roots);
36457566
LC
285 }
286
2bb04905 287 else if (type == DT_LNK) {
36457566
LC
288 Path target = readLink(path);
289 if (isInStore(target))
290 foundRoot(store, path, target, roots);
291
292 /* Handle indirect roots. */
293 else {
294 target = absPath(target, dirOf(path));
295 if (!pathExists(target)) {
296 if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
297 printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
298 unlink(path.c_str());
299 }
300 } else {
301 struct stat st2 = lstat(target);
302 if (!S_ISLNK(st2.st_mode)) return;
303 Path target2 = readLink(target);
304 if (isInStore(target2)) foundRoot(store, target, target2, roots);
305 }
306 }
307 }
308
2bb04905
LC
309 else if (type == DT_REG) {
310 Path storePath = settings.nixStore + "/" + baseNameOf(path);
311 if (store.isValidPath(storePath))
312 roots[path] = storePath;
313 }
314
36457566
LC
315 }
316
317 catch (SysError & e) {
318 /* We only ignore permanent failures. */
319 if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
320 printMsg(lvlInfo, format("cannot read potential root `%1%'") % path);
321 else
322 throw;
323 }
324}
325
326
327Roots LocalStore::findRoots()
328{
329 Roots roots;
330
331 /* Process direct roots in {gcroots,manifests,profiles}. */
2bb04905
LC
332 nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, DT_UNKNOWN, roots);
333 if (pathExists(settings.nixStateDir + "/manifests"))
334 nix::findRoots(*this, settings.nixStateDir + "/manifests", DT_UNKNOWN, roots);
335 nix::findRoots(*this, settings.nixStateDir + "/profiles", DT_UNKNOWN, roots);
36457566
LC
336
337 return roots;
338}
339
340
341static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
342{
2e3e5d21
LC
343 debug(format("executing `%1% gc --list-busy' to find additional roots")
344 % settings.guixProgram);
36457566 345
2e3e5d21
LC
346 const Strings args = { "gc", "--list-busy" };
347 string result = runProgram(settings.guixProgram, false, args);
36457566
LC
348
349 StringSet paths = tokenizeString<StringSet>(result, "\n");
350
351 foreach (StringSet::iterator, i, paths) {
352 if (isInStore(*i)) {
353 Path path = toStorePath(*i);
354 if (roots.find(path) == roots.end() && store.isValidPath(path)) {
355 debug(format("got additional root `%1%'") % path);
356 roots.insert(path);
357 }
358 }
359 }
360}
361
362
363struct GCLimitReached { };
364
365
366struct LocalStore::GCState
367{
368 GCOptions options;
369 GCResults & results;
370 PathSet roots;
371 PathSet tempRoots;
372 PathSet dead;
373 PathSet alive;
374 bool gcKeepOutputs;
375 bool gcKeepDerivations;
376 unsigned long long bytesInvalidated;
322eeb87 377 bool moveToTrash = true;
36457566
LC
378 Path trashDir;
379 bool shouldDelete;
380 GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
381};
382
383
384bool LocalStore::isActiveTempFile(const GCState & state,
385 const Path & path, const string & suffix)
386{
387 return hasSuffix(path, suffix)
388 && state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
389}
390
391
392void LocalStore::deleteGarbage(GCState & state, const Path & path)
393{
394 unsigned long long bytesFreed;
7033c769
LC
395
396 /* When deduplication is on, store items always have at least two links:
397 the one at PATH, and one in /gnu/store/.links. In that case, increase
398 bytesFreed when PATH has two or fewer links. */
399 size_t linkThreshold =
400 (settings.autoOptimiseStore && isStorePath(path)) ? 2 : 1;
401
402 deletePath(path, bytesFreed, linkThreshold);
36457566
LC
403 state.results.bytesFreed += bytesFreed;
404}
405
406
407void LocalStore::deletePathRecursive(GCState & state, const Path & path)
408{
409 checkInterrupt();
410
411 unsigned long long size = 0;
412
413 if (isValidPath(path)) {
414 PathSet referrers;
415 queryReferrers(path, referrers);
416 foreach (PathSet::iterator, i, referrers)
417 if (*i != path) deletePathRecursive(state, *i);
418 size = queryPathInfo(path).narSize;
419 invalidatePathChecked(path);
420 }
421
422 struct stat st;
423 if (lstat(path.c_str(), &st)) {
424 if (errno == ENOENT) return;
425 throw SysError(format("getting status of %1%") % path);
426 }
427
732c96f1 428 if (state.options.maxFreed != ULLONG_MAX) {
be0fb348
LC
429 auto freed = state.results.bytesFreed + state.bytesInvalidated;
430 double fraction = ((double) freed) / (double) state.options.maxFreed;
732c96f1
LC
431 unsigned int percentage = (fraction > 1. ? 1. : fraction) * 100.;
432 printMsg(lvlInfo, format("[%1%%%] deleting '%2%'") % percentage % path);
433 } else {
be0fb348
LC
434 auto freed = state.results.bytesFreed + state.bytesInvalidated;
435 freed /= 1024ULL * 1024ULL;
436 printMsg(lvlInfo, format("[%1% MiB] deleting '%2%'") % freed % path);
732c96f1 437 }
36457566
LC
438
439 state.results.paths.insert(path);
440
441 /* If the path is not a regular file or symlink, move it to the
442 trash directory. The move is to ensure that later (when we're
443 not holding the global GC lock) we can delete the path without
444 being afraid that the path has become alive again. Otherwise
445 delete it right away. */
322eeb87 446 if (state.moveToTrash && S_ISDIR(st.st_mode)) {
36457566
LC
447 // Estimate the amount freed using the narSize field. FIXME:
448 // if the path was not valid, need to determine the actual
449 // size.
322eeb87
LC
450 try {
451 if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
452 throw SysError(format("making `%1%' writable") % path);
453 Path tmp = state.trashDir + "/" + baseNameOf(path);
454 if (rename(path.c_str(), tmp.c_str()))
455 throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
456 state.bytesInvalidated += size;
457 } catch (SysError & e) {
d445c30e
CM
458 /* In a Docker container, rename(2) returns EXDEV when the source
459 and destination are not both on the "top layer". See:
460 https://bugs.gnu.org/41607 */
461 if (e.errNo == ENOSPC || e.errNo == EXDEV) {
322eeb87
LC
462 printMsg(lvlInfo, format("note: can't create move `%1%': %2%") % path % e.msg());
463 deleteGarbage(state, path);
464 }
465 }
36457566
LC
466 } else
467 deleteGarbage(state, path);
468
469 if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
470 printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
471 throw GCLimitReached();
472 }
473}
474
475
476bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
477{
478 if (visited.find(path) != visited.end()) return false;
479
480 if (state.alive.find(path) != state.alive.end()) {
481 return true;
482 }
483
484 if (state.dead.find(path) != state.dead.end()) {
485 return false;
486 }
487
488 if (state.roots.find(path) != state.roots.end()) {
489 printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
490 state.alive.insert(path);
491 return true;
492 }
493
494 visited.insert(path);
495
496 if (!isValidPath(path)) return false;
497
498 PathSet incoming;
499
500 /* Don't delete this path if any of its referrers are alive. */
501 queryReferrers(path, incoming);
502
503 /* If gc-keep-derivations is set and this is a derivation, then
504 don't delete the derivation if any of the outputs are alive. */
505 if (state.gcKeepDerivations && isDerivation(path)) {
506 PathSet outputs = queryDerivationOutputs(path);
507 foreach (PathSet::iterator, i, outputs)
508 if (isValidPath(*i) && queryDeriver(*i) == path)
509 incoming.insert(*i);
510 }
511
512 /* If gc-keep-outputs is set, then don't delete this path if there
513 are derivers of this path that are not garbage. */
514 if (state.gcKeepOutputs) {
515 PathSet derivers = queryValidDerivers(path);
516 foreach (PathSet::iterator, i, derivers)
517 incoming.insert(*i);
518 }
519
520 foreach (PathSet::iterator, i, incoming)
521 if (*i != path)
522 if (canReachRoot(state, visited, *i)) {
523 state.alive.insert(path);
524 return true;
525 }
526
527 return false;
528}
529
530
531void LocalStore::tryToDelete(GCState & state, const Path & path)
532{
533 checkInterrupt();
534
535 if (path == linksDir || path == state.trashDir) return;
536
537 startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
538
539 if (!isValidPath(path)) {
540 /* A lock file belonging to a path that we're building right
541 now isn't garbage. */
542 if (isActiveTempFile(state, path, ".lock")) return;
543
544 /* Don't delete .chroot directories for derivations that are
545 currently being built. */
546 if (isActiveTempFile(state, path, ".chroot")) return;
547 }
548
549 PathSet visited;
550
551 if (canReachRoot(state, visited, path)) {
552 printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
553 } else {
554 /* No path we visited was a root, so everything is garbage.
555 But we only delete ‘path’ and its referrers here so that
556 ‘nix-store --delete’ doesn't have the unexpected effect of
557 recursing into derivations and outputs. */
558 state.dead.insert(visited.begin(), visited.end());
559 if (state.shouldDelete)
560 deletePathRecursive(state, path);
561 }
562}
563
564
565/* Unlink all files in /nix/store/.links that have a link count of 1,
566 which indicates that there are no other links and so they can be
567 safely deleted. FIXME: race condition with optimisePath(): we
568 might see a link count of 1 just before optimisePath() increases
569 the link count. */
570void LocalStore::removeUnusedLinks(const GCState & state)
571{
572 AutoCloseDir dir = opendir(linksDir.c_str());
573 if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
574
575 long long actualSize = 0, unsharedSize = 0;
576
577 struct dirent * dirent;
578 while (errno = 0, dirent = readdir(dir)) {
579 checkInterrupt();
580 string name = dirent->d_name;
581 if (name == "." || name == "..") continue;
582 Path path = linksDir + "/" + name;
583
7738a721
LC
584#ifdef HAVE_STATX
585# define st_size stx_size
586# define st_nlink stx_nlink
513c0a0f 587 static int statx_flags = AT_SYMLINK_NOFOLLOW | AT_STATX_DONT_SYNC;
7738a721 588 struct statx st;
513c0a0f
LC
589
590 if (statx(AT_FDCWD, path.c_str(), statx_flags,
591 STATX_SIZE | STATX_NLINK, &st) == -1) {
592 if (errno == EINVAL) {
593 /* Old 3.10 kernels (CentOS 7) don't support
594 AT_STATX_DONT_SYNC, so try again without it. */
595 statx_flags &= ~AT_STATX_DONT_SYNC;
596 if (statx(AT_FDCWD, path.c_str(), statx_flags,
597 STATX_SIZE | STATX_NLINK, &st) == -1)
598 throw SysError(format("statting `%1%'") % path);
599 } else {
600 throw SysError(format("statting `%1%'") % path);
601 }
602 }
7738a721 603#else
36457566
LC
604 struct stat st;
605 if (lstat(path.c_str(), &st) == -1)
606 throw SysError(format("statting `%1%'") % path);
513c0a0f 607#endif
36457566 608
472a0e82
LC
609 /* Drop links for files smaller than 'deduplicationMinSize', even if
610 they have more than one hard link. */
611 if (st.st_nlink != 1 && st.st_size >= deduplicationMinSize) {
546a709f
ED
612 actualSize += st.st_size;
613 unsharedSize += (st.st_nlink - 1) * st.st_size;
36457566
LC
614 continue;
615 }
616
617 printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
618
619 if (unlink(path.c_str()) == -1)
620 throw SysError(format("deleting `%1%'") % path);
621
546a709f 622 state.results.bytesFreed += st.st_size;
7738a721
LC
623#undef st_size
624#undef st_nlink
36457566
LC
625 }
626
627 struct stat st;
628 if (stat(linksDir.c_str(), &st) == -1)
629 throw SysError(format("statting `%1%'") % linksDir);
546a709f 630 long long overhead = st.st_size;
36457566
LC
631
632 printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB")
633 % ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
634}
635
636
637void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
638{
639 GCState state(results);
640 state.options = options;
641 state.trashDir = settings.nixStore + "/trash";
642 state.gcKeepOutputs = settings.gcKeepOutputs;
643 state.gcKeepDerivations = settings.gcKeepDerivations;
644
645 /* Using `--ignore-liveness' with `--delete' can have unintended
646 consequences if `gc-keep-outputs' or `gc-keep-derivations' are
647 true (the garbage collector will recurse into deleting the
648 outputs or derivers, respectively). So disable them. */
649 if (options.action == GCOptions::gcDeleteSpecific && options.ignoreLiveness) {
650 state.gcKeepOutputs = false;
651 state.gcKeepDerivations = false;
652 }
653
654 state.shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific;
655
656 /* Acquire the global GC root. This prevents
657 a) New roots from being added.
658 b) Processes from creating new temporary root files. */
659 AutoCloseFD fdGCLock = openGCLock(ltWrite);
660
661 /* Find the roots. Since we've grabbed the GC lock, the set of
662 permanent roots cannot increase now. */
663 printMsg(lvlError, format("finding garbage collector roots..."));
664 Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
665
666 foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
667
47e99e62
LC
668 /* Add additional roots returned by 'guix gc --list-busy'. This is
669 typically used to add running programs to the set of roots (to prevent
670 them from being garbage collected). */
36457566
LC
671 if (!options.ignoreLiveness)
672 addAdditionalRoots(*this, state.roots);
673
674 /* Read the temporary roots. This acquires read locks on all
675 per-process temporary root files. So after this point no paths
676 can be added to the set of temporary roots. */
677 FDs fds;
678 readTempRoots(state.tempRoots, fds);
679 state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
680
681 /* After this point the set of roots or temporary roots cannot
682 increase, since we hold locks on everything. So everything
2bb04905 683 that is not reachable from `roots' is garbage. */
36457566
LC
684
685 if (state.shouldDelete) {
686 if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
322eeb87
LC
687 try {
688 createDirs(state.trashDir);
689 } catch (SysError & e) {
690 if (e.errNo == ENOSPC) {
691 printMsg(lvlInfo, format("note: can't create trash directory: %1%") % e.msg());
692 state.moveToTrash = false;
693 }
694 }
36457566
LC
695 }
696
697 /* Now either delete all garbage paths, or just the specified
698 paths (for gcDeleteSpecific). */
699
700 if (options.action == GCOptions::gcDeleteSpecific) {
701
702 foreach (PathSet::iterator, i, options.pathsToDelete) {
703 assertStorePath(*i);
704 tryToDelete(state, *i);
705 if (state.dead.find(*i) == state.dead.end())
706 throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
707 }
708
709 } else if (options.maxFreed > 0) {
710
711 if (state.shouldDelete)
712 printMsg(lvlError, format("deleting garbage..."));
713 else
714 printMsg(lvlError, format("determining live/dead paths..."));
715
716 try {
717
718 AutoCloseDir dir = opendir(settings.nixStore.c_str());
719 if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
720
721 /* Read the store and immediately delete all paths that
722 aren't valid. When using --max-freed etc., deleting
723 invalid paths is preferred over deleting unreachable
724 paths, since unreachable paths could become reachable
725 again. We don't use readDirectory() here so that GCing
726 can start faster. */
727 Paths entries;
728 struct dirent * dirent;
729 while (errno = 0, dirent = readdir(dir)) {
730 checkInterrupt();
731 string name = dirent->d_name;
732 if (name == "." || name == "..") continue;
733 Path path = settings.nixStore + "/" + name;
734 if (isValidPath(path))
735 entries.push_back(path);
736 else
737 tryToDelete(state, path);
738 }
739
740 dir.close();
741
742 /* Now delete the unreachable valid paths. Randomise the
743 order in which we delete entries to make the collector
744 less biased towards deleting paths that come
745 alphabetically first (e.g. /nix/store/000...). This
746 matters when using --max-freed etc. */
747 vector<Path> entries_(entries.begin(), entries.end());
748 random_shuffle(entries_.begin(), entries_.end());
749
750 foreach (vector<Path>::iterator, i, entries_)
751 tryToDelete(state, *i);
752
753 } catch (GCLimitReached & e) {
754 }
755 }
756
757 if (state.options.action == GCOptions::gcReturnLive) {
758 state.results.paths = state.alive;
759 return;
760 }
761
762 if (state.options.action == GCOptions::gcReturnDead) {
763 state.results.paths = state.dead;
764 return;
765 }
766
767 /* Allow other processes to add to the store from here on. */
768 fdGCLock.close();
769 fds.clear();
770
771 /* Delete the trash directory. */
772 printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
773 deleteGarbage(state, state.trashDir);
774
775 /* Clean up the links directory. */
776 if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
777 printMsg(lvlError, format("deleting unused links..."));
778 removeUnusedLinks(state);
779 }
780
781 /* While we're at it, vacuum the database. */
2bb04905 782 //if (options.action == GCOptions::gcDeleteDead) vacuumDB();
36457566
LC
783}
784
785
786}