3 #include "local-store.hh"
20 static string gcLockName
= "gc.lock";
21 static string tempRootsDir
= "temproots";
22 static string gcRootsDir
= "gcroots";
25 /* Acquire the global GC lock. This is used to prevent new build
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. */
30 int LocalStore::openGCLock(LockType lockType
)
32 Path fnGCLock
= (format("%1%/%2%")
33 % settings
.nixStateDir
% gcLockName
).str();
35 debug(format("acquiring global GC lock `%1%'") % fnGCLock
);
37 AutoCloseFD fdGCLock
= open(fnGCLock
.c_str(), O_RDWR
| O_CREAT
, 0600);
39 throw SysError(format("opening global GC lock `%1%'") % fnGCLock
);
40 closeOnExec(fdGCLock
);
42 if (!lockFile(fdGCLock
, lockType
, false)) {
43 printMsg(lvlError
, format("waiting for the big garbage collector lock..."));
44 lockFile(fdGCLock
, lockType
, true);
47 /* !!! Restrict read permission on the GC root. Otherwise any
48 process that can open the file for reading can DoS the
51 return fdGCLock
.borrow();
55 static void makeSymlink(const Path
& link
, const Path
& target
)
57 /* Create directories up to `gcRoot'. */
58 createDirs(dirOf(link
));
60 /* Create the new symlink. */
61 Path tempLink
= (format("%1%.tmp-%2%-%3%")
62 % link
% getpid() % rand()).str();
63 createSymlink(target
, tempLink
);
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%'")
72 void LocalStore::syncWithGC()
74 AutoCloseFD fdGCLock
= openGCLock(ltRead
);
78 void LocalStore::addIndirectRoot(const Path
& path
)
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
);
87 Path
addPermRoot(StoreAPI
& store
, const Path
& _storePath
,
88 const Path
& _gcRoot
, bool indirect
, bool allowOutsideRootsDir
)
90 Path
storePath(canonPath(_storePath
));
91 Path
gcRoot(canonPath(_gcRoot
));
92 assertStorePath(storePath
);
94 if (isInStore(gcRoot
))
96 "creating a garbage collector root (%1%) in the store is forbidden "
97 "(are you running nix-build inside the store?)") % gcRoot
);
100 /* Don't clobber the link if it already exists and doesn't
101 point to the store. */
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
);
109 if (!allowOutsideRootsDir
) {
110 Path rootsDir
= canonPath((format("%1%/%2%") % settings
.nixStateDir
% gcRootsDir
).str());
112 if (string(gcRoot
, 0, rootsDir
.size() + 1) != rootsDir
+ "/")
114 "path `%1%' is not a valid garbage collector root; "
115 "it's not in the directory `%2%'")
116 % gcRoot
% rootsDir
);
119 if (baseNameOf(gcRoot
) == baseNameOf(storePath
))
120 writeFile(gcRoot
, "");
122 makeSymlink(gcRoot
, storePath
);
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())
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
);
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. */
149 void LocalStore::addTempRoot(const Path
& path
)
151 /* Create the temporary roots file for this process. */
152 if (fdTempRoots
== -1) {
155 Path dir
= (format("%1%/%2%") % settings
.nixStateDir
% tempRootsDir
).str();
158 fnTempRoots
= (format("%1%/%2%")
159 % dir
% getpid()).str();
161 AutoCloseFD fdGCLock
= openGCLock(ltRead
);
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());
168 fdTempRoots
= openLockFile(fnTempRoots
, true);
172 debug(format("acquiring read lock on `%1%'") % fnTempRoots
);
173 lockFile(fdTempRoots
, ltRead
, true);
175 /* Check whether the garbage collector didn't get in our
178 if (fstat(fdTempRoots
, &st
) == -1)
179 throw SysError(format("statting `%1%'") % fnTempRoots
);
180 if (st
.st_size
== 0) break;
182 /* The garbage collector deleted this file before we could
183 get a lock. (It won't delete the file after we get a
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);
194 string s
= path
+ '\0';
195 writeFull(fdTempRoots
, s
);
197 /* Downgrade to a read lock. */
198 debug(format("downgrading to read lock on `%1%'") % fnTempRoots
);
199 lockFile(fdTempRoots
, ltRead
, true);
203 typedef std::shared_ptr
<AutoCloseFD
> FDPtr
;
204 typedef list
<FDPtr
> FDs
;
207 static void readTempRoots(PathSet
& tempRoots
, FDs
& fds
)
209 /* Read the `temproots' directory for per-process temporary root
211 DirEntries tempRootFiles
= readDirectory(
212 (format("%1%/%2%") % settings
.nixStateDir
% tempRootsDir
).str());
214 for (auto & i
: tempRootFiles
) {
215 Path path
= (format("%1%/%2%/%3%") % settings
.nixStateDir
% tempRootsDir
% i
.name
).str();
217 debug(format("reading temporary root file `%1%'") % path
);
218 FDPtr
fd(new AutoCloseFD(open(path
.c_str(), O_RDWR
, 0666)));
220 /* It's okay if the file has disappeared. */
221 if (errno
== ENOENT
) continue;
222 throw SysError(format("opening temporary roots file `%1%'") % path
);
225 /* This should work, but doesn't, for some reason. */
226 //FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
227 //if (*fd == -1) continue;
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)) {
233 printMsg(lvlError
, format("removing stale temporary roots file `%1%'") % path
);
234 unlink(path
.c_str());
239 /* Acquire a read lock. This will prevent the owning process
240 from upgrading to a write lock, therefore it will block in
242 debug(format("waiting for read lock on `%1%'") % path
);
243 lockFile(*fd
, ltRead
, true);
245 /* Read the entire file. */
246 string contents
= readFile(*fd
);
248 /* Extract the roots. */
249 string::size_type pos
= 0, end
;
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
);
259 fds
.push_back(fd
); /* keep open */
264 static void foundRoot(StoreAPI
& store
,
265 const Path
& path
, const Path
& target
, Roots
& roots
)
267 Path storePath
= toStorePath(target
);
268 if (store
.isValidPath(storePath
))
269 roots
[path
] = storePath
;
271 printMsg(lvlInfo
, format("skipping invalid root from `%1%' to `%2%'") % path
% storePath
);
275 static void findRoots(StoreAPI
& store
, const Path
& path
, unsigned char type
, Roots
& roots
)
279 if (type
== DT_UNKNOWN
)
280 type
= getFileType(path
);
282 if (type
== DT_DIR
) {
283 for (auto & i
: readDirectory(path
))
284 findRoots(store
, path
+ "/" + i
.name
, i
.type
, roots
);
287 else if (type
== DT_LNK
) {
288 Path target
= readLink(path
);
289 if (isInStore(target
))
290 foundRoot(store
, path
, target
, roots
);
292 /* Handle indirect roots. */
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());
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
);
309 else if (type
== DT_REG
) {
310 Path storePath
= settings
.nixStore
+ "/" + baseNameOf(path
);
311 if (store
.isValidPath(storePath
))
312 roots
[path
] = storePath
;
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
);
327 Roots
LocalStore::findRoots()
331 /* Process direct roots in {gcroots,manifests,profiles}. */
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
);
341 static void addAdditionalRoots(StoreAPI
& store
, PathSet
& roots
)
343 debug(format("executing `%1% gc --list-busy' to find additional roots")
344 % settings
.guixProgram
);
346 const Strings args
= { "gc", "--list-busy" };
347 string result
= runProgram(settings
.guixProgram
, false, args
);
349 StringSet paths
= tokenizeString
<StringSet
>(result
, "\n");
351 foreach (StringSet::iterator
, i
, paths
) {
353 Path path
= toStorePath(*i
);
354 if (roots
.find(path
) == roots
.end() && store
.isValidPath(path
)) {
355 debug(format("got additional root `%1%'") % path
);
363 struct GCLimitReached
{ };
366 struct LocalStore::GCState
375 bool gcKeepDerivations
;
376 unsigned long long bytesInvalidated
;
377 bool moveToTrash
= true;
380 GCState(GCResults
& results_
) : results(results_
), bytesInvalidated(0) { }
384 bool LocalStore::isActiveTempFile(const GCState
& state
,
385 const Path
& path
, const string
& suffix
)
387 return hasSuffix(path
, suffix
)
388 && state
.tempRoots
.find(string(path
, 0, path
.size() - suffix
.size())) != state
.tempRoots
.end();
392 void LocalStore::deleteGarbage(GCState
& state
, const Path
& path
)
394 unsigned long long bytesFreed
;
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;
402 deletePath(path
, bytesFreed
, linkThreshold
);
403 state
.results
.bytesFreed
+= bytesFreed
;
407 void LocalStore::deletePathRecursive(GCState
& state
, const Path
& path
)
411 unsigned long long size
= 0;
413 if (isValidPath(path
)) {
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
);
423 if (lstat(path
.c_str(), &st
)) {
424 if (errno
== ENOENT
) return;
425 throw SysError(format("getting status of %1%") % path
);
428 if (state
.options
.maxFreed
!= ULLONG_MAX
) {
429 auto freed
= state
.results
.bytesFreed
+ state
.bytesInvalidated
;
430 double fraction
= ((double) freed
) / (double) state
.options
.maxFreed
;
431 unsigned int percentage
= (fraction
> 1. ? 1. : fraction
) * 100.;
432 printMsg(lvlInfo
, format("[%1%%%] deleting '%2%'") % percentage
% path
);
434 auto freed
= state
.results
.bytesFreed
+ state
.bytesInvalidated
;
435 freed
/= 1024ULL * 1024ULL;
436 printMsg(lvlInfo
, format("[%1% MiB] deleting '%2%'") % freed
% path
);
439 state
.results
.paths
.insert(path
);
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. */
446 if (state
.moveToTrash
&& S_ISDIR(st
.st_mode
)) {
447 // Estimate the amount freed using the narSize field. FIXME:
448 // if the path was not valid, need to determine the actual
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
) {
458 if (e
.errNo
== ENOSPC
) {
459 printMsg(lvlInfo
, format("note: can't create move `%1%': %2%") % path
% e
.msg());
460 deleteGarbage(state
, path
);
464 deleteGarbage(state
, path
);
466 if (state
.results
.bytesFreed
+ state
.bytesInvalidated
> state
.options
.maxFreed
) {
467 printMsg(lvlInfo
, format("deleted or invalidated more than %1% bytes; stopping") % state
.options
.maxFreed
);
468 throw GCLimitReached();
473 bool LocalStore::canReachRoot(GCState
& state
, PathSet
& visited
, const Path
& path
)
475 if (visited
.find(path
) != visited
.end()) return false;
477 if (state
.alive
.find(path
) != state
.alive
.end()) {
481 if (state
.dead
.find(path
) != state
.dead
.end()) {
485 if (state
.roots
.find(path
) != state
.roots
.end()) {
486 printMsg(lvlDebug
, format("cannot delete `%1%' because it's a root") % path
);
487 state
.alive
.insert(path
);
491 visited
.insert(path
);
493 if (!isValidPath(path
)) return false;
497 /* Don't delete this path if any of its referrers are alive. */
498 queryReferrers(path
, incoming
);
500 /* If gc-keep-derivations is set and this is a derivation, then
501 don't delete the derivation if any of the outputs are alive. */
502 if (state
.gcKeepDerivations
&& isDerivation(path
)) {
503 PathSet outputs
= queryDerivationOutputs(path
);
504 foreach (PathSet::iterator
, i
, outputs
)
505 if (isValidPath(*i
) && queryDeriver(*i
) == path
)
509 /* If gc-keep-outputs is set, then don't delete this path if there
510 are derivers of this path that are not garbage. */
511 if (state
.gcKeepOutputs
) {
512 PathSet derivers
= queryValidDerivers(path
);
513 foreach (PathSet::iterator
, i
, derivers
)
517 foreach (PathSet::iterator
, i
, incoming
)
519 if (canReachRoot(state
, visited
, *i
)) {
520 state
.alive
.insert(path
);
528 void LocalStore::tryToDelete(GCState
& state
, const Path
& path
)
532 if (path
== linksDir
|| path
== state
.trashDir
) return;
534 startNest(nest
, lvlDebug
, format("considering whether to delete `%1%'") % path
);
536 if (!isValidPath(path
)) {
537 /* A lock file belonging to a path that we're building right
538 now isn't garbage. */
539 if (isActiveTempFile(state
, path
, ".lock")) return;
541 /* Don't delete .chroot directories for derivations that are
542 currently being built. */
543 if (isActiveTempFile(state
, path
, ".chroot")) return;
548 if (canReachRoot(state
, visited
, path
)) {
549 printMsg(lvlDebug
, format("cannot delete `%1%' because it's still reachable") % path
);
551 /* No path we visited was a root, so everything is garbage.
552 But we only delete ‘path’ and its referrers here so that
553 ‘nix-store --delete’ doesn't have the unexpected effect of
554 recursing into derivations and outputs. */
555 state
.dead
.insert(visited
.begin(), visited
.end());
556 if (state
.shouldDelete
)
557 deletePathRecursive(state
, path
);
562 /* Unlink all files in /nix/store/.links that have a link count of 1,
563 which indicates that there are no other links and so they can be
564 safely deleted. FIXME: race condition with optimisePath(): we
565 might see a link count of 1 just before optimisePath() increases
567 void LocalStore::removeUnusedLinks(const GCState
& state
)
569 AutoCloseDir dir
= opendir(linksDir
.c_str());
570 if (!dir
) throw SysError(format("opening directory `%1%'") % linksDir
);
572 long long actualSize
= 0, unsharedSize
= 0;
574 struct dirent
* dirent
;
575 while (errno
= 0, dirent
= readdir(dir
)) {
577 string name
= dirent
->d_name
;
578 if (name
== "." || name
== "..") continue;
579 Path path
= linksDir
+ "/" + name
;
582 # define st_size stx_size
583 # define st_nlink stx_nlink
584 static int statx_flags
= AT_SYMLINK_NOFOLLOW
| AT_STATX_DONT_SYNC
;
587 if (statx(AT_FDCWD
, path
.c_str(), statx_flags
,
588 STATX_SIZE
| STATX_NLINK
, &st
) == -1) {
589 if (errno
== EINVAL
) {
590 /* Old 3.10 kernels (CentOS 7) don't support
591 AT_STATX_DONT_SYNC, so try again without it. */
592 statx_flags
&= ~AT_STATX_DONT_SYNC
;
593 if (statx(AT_FDCWD
, path
.c_str(), statx_flags
,
594 STATX_SIZE
| STATX_NLINK
, &st
) == -1)
595 throw SysError(format("statting `%1%'") % path
);
597 throw SysError(format("statting `%1%'") % path
);
602 if (lstat(path
.c_str(), &st
) == -1)
603 throw SysError(format("statting `%1%'") % path
);
606 if (st
.st_nlink
!= 1) {
607 actualSize
+= st
.st_size
;
608 unsharedSize
+= (st
.st_nlink
- 1) * st
.st_size
;
612 printMsg(lvlTalkative
, format("deleting unused link `%1%'") % path
);
614 if (unlink(path
.c_str()) == -1)
615 throw SysError(format("deleting `%1%'") % path
);
617 state
.results
.bytesFreed
+= st
.st_size
;
623 if (stat(linksDir
.c_str(), &st
) == -1)
624 throw SysError(format("statting `%1%'") % linksDir
);
625 long long overhead
= st
.st_size
;
627 printMsg(lvlInfo
, format("note: currently hard linking saves %.2f MiB")
628 % ((unsharedSize
- actualSize
- overhead
) / (1024.0 * 1024.0)));
632 void LocalStore::collectGarbage(const GCOptions
& options
, GCResults
& results
)
634 GCState
state(results
);
635 state
.options
= options
;
636 state
.trashDir
= settings
.nixStore
+ "/trash";
637 state
.gcKeepOutputs
= settings
.gcKeepOutputs
;
638 state
.gcKeepDerivations
= settings
.gcKeepDerivations
;
640 /* Using `--ignore-liveness' with `--delete' can have unintended
641 consequences if `gc-keep-outputs' or `gc-keep-derivations' are
642 true (the garbage collector will recurse into deleting the
643 outputs or derivers, respectively). So disable them. */
644 if (options
.action
== GCOptions::gcDeleteSpecific
&& options
.ignoreLiveness
) {
645 state
.gcKeepOutputs
= false;
646 state
.gcKeepDerivations
= false;
649 state
.shouldDelete
= options
.action
== GCOptions::gcDeleteDead
|| options
.action
== GCOptions::gcDeleteSpecific
;
651 /* Acquire the global GC root. This prevents
652 a) New roots from being added.
653 b) Processes from creating new temporary root files. */
654 AutoCloseFD fdGCLock
= openGCLock(ltWrite
);
656 /* Find the roots. Since we've grabbed the GC lock, the set of
657 permanent roots cannot increase now. */
658 printMsg(lvlError
, format("finding garbage collector roots..."));
659 Roots rootMap
= options
.ignoreLiveness
? Roots() : findRoots();
661 foreach (Roots::iterator
, i
, rootMap
) state
.roots
.insert(i
->second
);
663 /* Add additional roots returned by 'guix gc --list-busy'. This is
664 typically used to add running programs to the set of roots (to prevent
665 them from being garbage collected). */
666 if (!options
.ignoreLiveness
)
667 addAdditionalRoots(*this, state
.roots
);
669 /* Read the temporary roots. This acquires read locks on all
670 per-process temporary root files. So after this point no paths
671 can be added to the set of temporary roots. */
673 readTempRoots(state
.tempRoots
, fds
);
674 state
.roots
.insert(state
.tempRoots
.begin(), state
.tempRoots
.end());
676 /* After this point the set of roots or temporary roots cannot
677 increase, since we hold locks on everything. So everything
678 that is not reachable from `roots' is garbage. */
680 if (state
.shouldDelete
) {
681 if (pathExists(state
.trashDir
)) deleteGarbage(state
, state
.trashDir
);
683 createDirs(state
.trashDir
);
684 } catch (SysError
& e
) {
685 if (e
.errNo
== ENOSPC
) {
686 printMsg(lvlInfo
, format("note: can't create trash directory: %1%") % e
.msg());
687 state
.moveToTrash
= false;
692 /* Now either delete all garbage paths, or just the specified
693 paths (for gcDeleteSpecific). */
695 if (options
.action
== GCOptions::gcDeleteSpecific
) {
697 foreach (PathSet::iterator
, i
, options
.pathsToDelete
) {
699 tryToDelete(state
, *i
);
700 if (state
.dead
.find(*i
) == state
.dead
.end())
701 throw Error(format("cannot delete path `%1%' since it is still alive") % *i
);
704 } else if (options
.maxFreed
> 0) {
706 if (state
.shouldDelete
)
707 printMsg(lvlError
, format("deleting garbage..."));
709 printMsg(lvlError
, format("determining live/dead paths..."));
713 AutoCloseDir dir
= opendir(settings
.nixStore
.c_str());
714 if (!dir
) throw SysError(format("opening directory `%1%'") % settings
.nixStore
);
716 /* Read the store and immediately delete all paths that
717 aren't valid. When using --max-freed etc., deleting
718 invalid paths is preferred over deleting unreachable
719 paths, since unreachable paths could become reachable
720 again. We don't use readDirectory() here so that GCing
723 struct dirent
* dirent
;
724 while (errno
= 0, dirent
= readdir(dir
)) {
726 string name
= dirent
->d_name
;
727 if (name
== "." || name
== "..") continue;
728 Path path
= settings
.nixStore
+ "/" + name
;
729 if (isValidPath(path
))
730 entries
.push_back(path
);
732 tryToDelete(state
, path
);
737 /* Now delete the unreachable valid paths. Randomise the
738 order in which we delete entries to make the collector
739 less biased towards deleting paths that come
740 alphabetically first (e.g. /nix/store/000...). This
741 matters when using --max-freed etc. */
742 vector
<Path
> entries_(entries
.begin(), entries
.end());
743 random_shuffle(entries_
.begin(), entries_
.end());
745 foreach (vector
<Path
>::iterator
, i
, entries_
)
746 tryToDelete(state
, *i
);
748 } catch (GCLimitReached
& e
) {
752 if (state
.options
.action
== GCOptions::gcReturnLive
) {
753 state
.results
.paths
= state
.alive
;
757 if (state
.options
.action
== GCOptions::gcReturnDead
) {
758 state
.results
.paths
= state
.dead
;
762 /* Allow other processes to add to the store from here on. */
766 /* Delete the trash directory. */
767 printMsg(lvlInfo
, format("deleting `%1%'") % state
.trashDir
);
768 deleteGarbage(state
, state
.trashDir
);
770 /* Clean up the links directory. */
771 if (options
.action
== GCOptions::gcDeleteDead
|| options
.action
== GCOptions::gcDeleteSpecific
) {
772 printMsg(lvlError
, format("deleting unused links..."));
773 removeUnusedLinks(state
);
776 /* While we're at it, vacuum the database. */
777 //if (options.action == GCOptions::gcDeleteDead) vacuumDB();