diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index a99bb1a81..20f194e6e 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -617,27 +617,51 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } else { - printMsg(lvlError, format("reading the Nix store...")); - Paths entries = readDirectory(nixStore); - - /* Randomise the order in which we delete entries to make the - collector less biased towards deleting paths that come - alphabetically first (e.g. /nix/store/000...). This - matters when using --max-freed etc. */ - vector entries_(entries.begin(), entries.end()); - random_shuffle(entries_.begin(), entries_.end()); - if (shouldDelete(state.options.action)) printMsg(lvlError, format("deleting garbage...")); else printMsg(lvlError, format("determining live/dead paths...")); try { + + AutoCloseDir dir = opendir(nixStore.c_str()); + if (!dir) throw SysError(format("opening directory `%1%'") % nixStore); + + /* Read the store and immediately delete all paths that + aren't valid. When using --max-freed etc., deleting + invalid paths is preferred over deleting unreachable + paths, since unreachable paths could become reachable + again. We don't use readDirectory() here so that GCing + can start faster. */ + Paths entries; + struct dirent * dirent; + while (errno = 0, dirent = readdir(dir)) { + checkInterrupt(); + string name = dirent->d_name; + if (name == "." || name == "..") continue; + Path path = nixStore + "/" + name; + if (isValidPath(path)) + entries.push_back(path); + else + tryToDelete(state, path); + } + + dir.close(); + + /* Now delete the unreachable valid paths. Randomise the + order in which we delete entries to make the collector + less biased towards deleting paths that come + alphabetically first (e.g. /nix/store/000...). This + matters when using --max-freed etc. */ + vector entries_(entries.begin(), entries.end()); + random_shuffle(entries_.begin(), entries_.end()); + foreach (vector::iterator, i, entries_) tryToDelete(state, nixStore + "/" + *i); + } catch (GCLimitReached & e) { } - } + } } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 9adaac40d..0352754f5 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -701,7 +701,7 @@ AutoCloseDir::AutoCloseDir(DIR * dir) AutoCloseDir::~AutoCloseDir() { - if (dir) closedir(dir); + close(); } @@ -717,6 +717,14 @@ AutoCloseDir::operator DIR *() } +void AutoCloseDir::close() +{ + if (dir) { + closedir(dir); + dir = 0; + } +} + ////////////////////////////////////////////////////////////////////// diff --git a/src/libutil/util.hh b/src/libutil/util.hh index f86290f31..a1cf68e69 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -223,6 +223,7 @@ public: ~AutoCloseDir(); void operator =(DIR * dir); operator DIR *(); + void close(); };