aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/gc.cc
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2019-12-11 14:53:30 +0100
committerEelco Dolstra <edolstra@gmail.com>2019-12-11 14:53:30 +0100
commitecb3a1afa2395c46c4ba2ec9da550f45414dbe6d (patch)
tree6d1038ee909bd1ba69948a0bc326cd5ba6824e01 /src/libstore/gc.cc
parentab88f4bbd4117db458a79f0a02a4de7bf7931f4c (diff)
parentf800d450b78091835ab7ca67847d76e75d877a24 (diff)
Merge remote-tracking branch 'origin/master' into flakes
Diffstat (limited to 'src/libstore/gc.cc')
-rw-r--r--src/libstore/gc.cc184
1 files changed, 95 insertions, 89 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index 842980fb7..ed81186af 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -85,12 +85,10 @@ void LocalStore::addIndirectRoot(const Path & path)
}
-Path LocalFSStore::addPermRoot(const Path & _storePath,
+Path LocalFSStore::addPermRoot(const StorePath & storePath,
const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir)
{
- Path storePath(canonPath(_storePath));
Path gcRoot(canonPath(_gcRoot));
- assertStorePath(storePath);
if (isInStore(gcRoot))
throw Error(format(
@@ -102,7 +100,7 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
throw Error(format("cannot create symlink '%1%'; already exists") % gcRoot);
- makeSymlink(gcRoot, storePath);
+ makeSymlink(gcRoot, printStorePath(storePath));
addIndirectRoot(gcRoot);
}
@@ -117,10 +115,10 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
% gcRoot % rootsDir);
}
- if (baseNameOf(gcRoot) == baseNameOf(storePath))
+ if (baseNameOf(gcRoot) == std::string(storePath.to_string()))
writeFile(gcRoot, "");
else
- makeSymlink(gcRoot, storePath);
+ makeSymlink(gcRoot, printStorePath(storePath));
}
/* Check that the root can be found by the garbage collector.
@@ -129,13 +127,12 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
check if the root is in a directory in or linked from the
gcroots directory. */
if (settings.checkRootReachability) {
- Roots roots = findRoots(false);
- if (roots[storePath].count(gcRoot) == 0)
+ auto roots = findRoots(false);
+ if (roots[storePath.clone()].count(gcRoot) == 0)
printError(
- format(
- "warning: '%1%' is not in a directory where the garbage collector looks for roots; "
- "therefore, '%2%' might be removed by the garbage collector")
- % gcRoot % storePath);
+ "warning: '%1%' is not in a directory where the garbage collector looks for roots; "
+ "therefore, '%2%' might be removed by the garbage collector",
+ gcRoot, printStorePath(storePath));
}
/* Grab the global GC root, causing us to block while a GC is in
@@ -147,7 +144,7 @@ Path LocalFSStore::addPermRoot(const Path & _storePath,
}
-void LocalStore::addTempRoot(const Path & path)
+void LocalStore::addTempRoot(const StorePath & path)
{
auto state(_state.lock());
@@ -188,7 +185,7 @@ void LocalStore::addTempRoot(const Path & path)
debug(format("acquiring write lock on '%1%'") % fnTempRoots);
lockFile(state->fdTempRoots.get(), ltWrite, true);
- string s = path + '\0';
+ string s = printStorePath(path) + '\0';
writeFull(state->fdTempRoots.get(), s);
/* Downgrade to a read lock. */
@@ -246,8 +243,7 @@ void LocalStore::findTempRoots(FDs & fds, Roots & tempRoots, bool censor)
while ((end = contents.find((char) 0, pos)) != string::npos) {
Path root(contents, pos, end - pos);
debug("got temporary root '%s'", root);
- assertStorePath(root);
- tempRoots[root].emplace(censor ? censored : fmt("{temp:%d}", pid));
+ tempRoots[parseStorePath(root)].emplace(censor ? censored : fmt("{temp:%d}", pid));
pos = end + 1;
}
@@ -260,10 +256,11 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
{
auto foundRoot = [&](const Path & path, const Path & target) {
Path storePath = toStorePath(target);
- if (isStorePath(storePath) && isValidPath(storePath))
- roots[storePath].emplace(path);
+ // FIXME
+ if (isStorePath(storePath) && isValidPath(parseStorePath(storePath)))
+ roots[parseStorePath(storePath)].emplace(path);
else
- printInfo(format("skipping invalid root from '%1%' to '%2%'") % path % storePath);
+ printInfo("skipping invalid root from '%1%' to '%2%'", path, storePath);
};
try {
@@ -299,9 +296,10 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
}
else if (type == DT_REG) {
- Path storePath = storeDir + "/" + baseNameOf(path);
- if (isStorePath(storePath) && isValidPath(storePath))
- roots[storePath].emplace(path);
+ Path storePath = storeDir + "/" + std::string(baseNameOf(path));
+ // FIXME
+ if (isStorePath(storePath) && isValidPath(parseStorePath(storePath)))
+ roots[parseStorePath(storePath)].emplace(path);
}
}
@@ -309,7 +307,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots)
catch (SysError & e) {
/* We only ignore permanent failures. */
if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
- printInfo(format("cannot read potential root '%1%'") % path);
+ printInfo("cannot read potential root '%1%'", path);
else
throw;
}
@@ -340,7 +338,9 @@ Roots LocalStore::findRoots(bool censor)
return roots;
}
-static void readProcLink(const string & file, Roots & roots)
+typedef std::unordered_map<Path, std::unordered_set<std::string>> UncheckedRoots;
+
+static void readProcLink(const string & file, UncheckedRoots & roots)
{
/* 64 is the starting buffer size gnu readlink uses... */
auto bufsiz = ssize_t{64};
@@ -369,7 +369,7 @@ static string quoteRegexChars(const string & raw)
return std::regex_replace(raw, specialRegex, R"(\$&)");
}
-static void readFileRoots(const char * path, Roots & roots)
+static void readFileRoots(const char * path, UncheckedRoots & roots)
{
try {
roots[readFile(path)].emplace(path);
@@ -381,7 +381,7 @@ static void readFileRoots(const char * path, Roots & roots)
void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
{
- Roots unchecked;
+ UncheckedRoots unchecked;
auto procDir = AutoCloseDir{opendir("/proc")};
if (procDir) {
@@ -466,16 +466,16 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
#endif
for (auto & [target, links] : unchecked) {
- if (isInStore(target)) {
- Path path = toStorePath(target);
- if (isStorePath(path) && isValidPath(path)) {
- debug(format("got additional root '%1%'") % path);
- if (censor)
- roots[path].insert(censored);
- else
- roots[path].insert(links.begin(), links.end());
- }
- }
+ if (!isInStore(target)) continue;
+ Path pathS = toStorePath(target);
+ if (!isStorePath(pathS)) continue;
+ auto path = parseStorePath(pathS);
+ if (!isValidPath(path)) continue;
+ debug("got additional root '%1%'", pathS);
+ if (censor)
+ roots[path.clone()].insert(censored);
+ else
+ roots[path.clone()].insert(links.begin(), links.end());
}
}
@@ -485,18 +485,19 @@ struct GCLimitReached { };
struct LocalStore::GCState
{
- GCOptions options;
+ const GCOptions & options;
GCResults & results;
- PathSet roots;
- PathSet tempRoots;
- PathSet dead;
- PathSet alive;
+ StorePathSet roots;
+ StorePathSet tempRoots;
+ StorePathSet dead;
+ StorePathSet alive;
bool gcKeepOutputs;
bool gcKeepDerivations;
unsigned long long bytesInvalidated;
bool moveToTrash = true;
bool shouldDelete;
- GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
+ GCState(const GCOptions & options, GCResults & results)
+ : options(options), results(results), bytesInvalidated(0) { }
};
@@ -504,7 +505,7 @@ bool LocalStore::isActiveTempFile(const GCState & state,
const Path & path, const string & suffix)
{
return hasSuffix(path, suffix)
- && state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
+ && state.tempRoots.count(parseStorePath(string(path, 0, path.size() - suffix.size())));
}
@@ -522,16 +523,17 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
unsigned long long size = 0;
- if (isStorePath(path) && isValidPath(path)) {
- PathSet referrers;
- queryReferrers(path, referrers);
+ // FIXME
+ if (isStorePath(path) && isValidPath(parseStorePath(path))) {
+ StorePathSet referrers;
+ queryReferrers(parseStorePath(path), referrers);
for (auto & i : referrers)
- if (i != path) deletePathRecursive(state, i);
- size = queryPathInfo(path)->narSize;
- invalidatePathChecked(path);
+ if (printStorePath(i) != path) deletePathRecursive(state, printStorePath(i));
+ size = queryPathInfo(parseStorePath(path))->narSize;
+ invalidatePathChecked(parseStorePath(path));
}
- Path realPath = realStoreDir + "/" + baseNameOf(path);
+ Path realPath = realStoreDir + "/" + std::string(baseNameOf(path));
struct stat st;
if (lstat(realPath.c_str(), &st)) {
@@ -555,7 +557,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
try {
if (chmod(realPath.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("making '%1%' writable") % realPath);
- Path tmp = trashDir + "/" + baseNameOf(path);
+ Path tmp = trashDir + "/" + std::string(baseNameOf(path));
if (rename(realPath.c_str(), tmp.c_str()))
throw SysError(format("unable to rename '%1%' to '%2%'") % realPath % tmp);
state.bytesInvalidated += size;
@@ -575,7 +577,7 @@ void LocalStore::deletePathRecursive(GCState & state, const Path & path)
}
-bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
+bool LocalStore::canReachRoot(GCState & state, StorePathSet & visited, const StorePath & path)
{
if (visited.count(path)) return false;
@@ -584,41 +586,41 @@ bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & p
if (state.dead.count(path)) return false;
if (state.roots.count(path)) {
- debug(format("cannot delete '%1%' because it's a root") % path);
- state.alive.insert(path);
+ debug("cannot delete '%1%' because it's a root", printStorePath(path));
+ state.alive.insert(path.clone());
return true;
}
- visited.insert(path);
+ visited.insert(path.clone());
- if (!isStorePath(path) || !isValidPath(path)) return false;
+ //FIXME
+ if (!isStorePath(printStorePath(path)) || !isValidPath(path)) return false;
- PathSet incoming;
+ StorePathSet incoming;
/* Don't delete this path if any of its referrers are alive. */
queryReferrers(path, incoming);
/* If keep-derivations is set and this is a derivation, then
don't delete the derivation if any of the outputs are alive. */
- if (state.gcKeepDerivations && isDerivation(path)) {
- PathSet outputs = queryDerivationOutputs(path);
- for (auto & i : outputs)
+ if (state.gcKeepDerivations && path.isDerivation()) {
+ for (auto & i : queryDerivationOutputs(path))
if (isValidPath(i) && queryPathInfo(i)->deriver == path)
- incoming.insert(i);
+ incoming.insert(i.clone());
}
/* If keep-outputs is set, then don't delete this path if there
are derivers of this path that are not garbage. */
if (state.gcKeepOutputs) {
- PathSet derivers = queryValidDerivers(path);
+ auto derivers = queryValidDerivers(path);
for (auto & i : derivers)
- incoming.insert(i);
+ incoming.insert(i.clone());
}
for (auto & i : incoming)
if (i != path)
if (canReachRoot(state, visited, i)) {
- state.alive.insert(path);
+ state.alive.insert(path.clone());
return true;
}
@@ -630,12 +632,13 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
{
checkInterrupt();
- auto realPath = realStoreDir + "/" + baseNameOf(path);
+ auto realPath = realStoreDir + "/" + std::string(baseNameOf(path));
if (realPath == linksDir || realPath == trashDir) return;
//Activity act(*logger, lvlDebug, format("considering whether to delete '%1%'") % path);
- if (!isStorePath(path) || !isValidPath(path)) {
+ // FIXME
+ if (!isStorePath(path) || !isValidPath(parseStorePath(path))) {
/* A lock file belonging to a path that we're building right
now isn't garbage. */
if (isActiveTempFile(state, path, ".lock")) return;
@@ -650,16 +653,17 @@ void LocalStore::tryToDelete(GCState & state, const Path & path)
if (isActiveTempFile(state, path, ".check")) return;
}
- PathSet visited;
+ StorePathSet visited;
- if (canReachRoot(state, visited, path)) {
- debug(format("cannot delete '%1%' because it's still reachable") % path);
+ if (canReachRoot(state, visited, parseStorePath(path))) {
+ debug("cannot delete '%s' because it's still reachable", path);
} else {
/* No path we visited was a root, so everything is garbage.
But we only delete ‘path’ and its referrers here so that
‘nix-store --delete’ doesn't have the unexpected effect of
recursing into derivations and outputs. */
- state.dead.insert(visited.begin(), visited.end());
+ for (auto & i : visited)
+ state.dead.insert(i.clone());
if (state.shouldDelete)
deletePathRecursive(state, path);
}
@@ -715,8 +719,7 @@ void LocalStore::removeUnusedLinks(const GCState & state)
void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
{
- GCState state(results);
- state.options = options;
+ GCState state(options, results);
state.gcKeepOutputs = settings.gcKeepOutputs;
state.gcKeepDerivations = settings.gcKeepDerivations;
@@ -741,12 +744,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */
- printError(format("finding garbage collector roots..."));
+ printError("finding garbage collector roots...");
Roots rootMap;
if (!options.ignoreLiveness)
findRootsNoTemp(rootMap, true);
- for (auto & i : rootMap) state.roots.insert(i.first);
+ for (auto & i : rootMap) state.roots.insert(i.first.clone());
/* Read the temporary roots. This acquires read locks on all
per-process temporary root files. So after this point no paths
@@ -754,9 +757,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
FDs fds;
Roots tempRoots;
findTempRoots(fds, tempRoots, true);
- for (auto & root : tempRoots)
- state.tempRoots.insert(root.first);
- state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
+ for (auto & root : tempRoots) {
+ state.tempRoots.insert(root.first.clone());
+ state.roots.insert(root.first.clone());
+ }
/* After this point the set of roots or temporary roots cannot
increase, since we hold locks on everything. So everything
@@ -768,7 +772,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
createDirs(trashDir);
} catch (SysError & e) {
if (e.errNo == ENOSPC) {
- printInfo(format("note: can't create trash directory: %1%") % e.msg());
+ printInfo("note: can't create trash directory: %s", e.msg());
state.moveToTrash = false;
}
}
@@ -780,22 +784,21 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (options.action == GCOptions::gcDeleteSpecific) {
for (auto & i : options.pathsToDelete) {
- assertStorePath(i);
- tryToDelete(state, i);
+ tryToDelete(state, printStorePath(i));
if (state.dead.find(i) == state.dead.end())
- throw Error(format(
+ throw Error(
"cannot delete path '%1%' since it is still alive. "
"To find out why use: "
- "nix-store --query --roots"
- ) % i);
+ "nix-store --query --roots",
+ printStorePath(i));
}
} else if (options.maxFreed > 0) {
if (state.shouldDelete)
- printError(format("deleting garbage..."));
+ printError("deleting garbage...");
else
- printError(format("determining live/dead paths..."));
+ printError("determining live/dead paths...");
try {
@@ -815,7 +818,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = storeDir + "/" + name;
- if (isStorePath(path) && isValidPath(path))
+ // FIXME
+ if (isStorePath(path) && isValidPath(parseStorePath(path)))
entries.push_back(path);
else
tryToDelete(state, path);
@@ -840,12 +844,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
}
if (state.options.action == GCOptions::gcReturnLive) {
- state.results.paths = state.alive;
+ for (auto & i : state.alive)
+ state.results.paths.insert(printStorePath(i));
return;
}
if (state.options.action == GCOptions::gcReturnDead) {
- state.results.paths = state.dead;
+ for (auto & i : state.dead)
+ state.results.paths.insert(printStorePath(i));
return;
}
@@ -859,7 +865,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
/* Clean up the links directory. */
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
- printError(format("deleting unused links..."));
+ printError("deleting unused links...");
removeUnusedLinks(state);
}