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