aboutsummaryrefslogtreecommitdiff
path: root/src/libstore/gc.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore/gc.cc')
-rw-r--r--src/libstore/gc.cc104
1 files changed, 65 insertions, 39 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index f65fb1b2e..996f26a95 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -39,9 +39,7 @@ static void makeSymlink(const Path & link, const Path & target)
createSymlink(target, tempLink);
/* Atomically replace the old one. */
- if (rename(tempLink.c_str(), link.c_str()) == -1)
- throw SysError("cannot rename '%1%' to '%2%'",
- tempLink , link);
+ renameFile(tempLink, link);
}
@@ -79,90 +77,106 @@ Path LocalFSStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot
}
-void LocalStore::addTempRoot(const StorePath & path)
+void LocalStore::createTempRootsFile()
{
- auto state(_state.lock());
+ auto fdTempRoots(_fdTempRoots.lock());
/* Create the temporary roots file for this process. */
- if (!state->fdTempRoots) {
-
- while (1) {
- if (pathExists(fnTempRoots))
- /* It *must* be stale, since there can be no two
- processes with the same pid. */
- unlink(fnTempRoots.c_str());
+ if (*fdTempRoots) return;
- state->fdTempRoots = openLockFile(fnTempRoots, true);
+ while (1) {
+ if (pathExists(fnTempRoots))
+ /* It *must* be stale, since there can be no two
+ processes with the same pid. */
+ unlink(fnTempRoots.c_str());
- debug("acquiring write lock on '%s'", fnTempRoots);
- lockFile(state->fdTempRoots.get(), ltWrite, true);
+ *fdTempRoots = openLockFile(fnTempRoots, true);
- /* Check whether the garbage collector didn't get in our
- way. */
- struct stat st;
- if (fstat(state->fdTempRoots.get(), &st) == -1)
- throw SysError("statting '%1%'", fnTempRoots);
- if (st.st_size == 0) break;
+ debug("acquiring write lock on '%s'", fnTempRoots);
+ lockFile(fdTempRoots->get(), ltWrite, true);
- /* The garbage collector deleted this file before we could
- get a lock. (It won't delete the file after we get a
- lock.) Try again. */
- }
+ /* Check whether the garbage collector didn't get in our
+ way. */
+ struct stat st;
+ if (fstat(fdTempRoots->get(), &st) == -1)
+ throw SysError("statting '%1%'", fnTempRoots);
+ if (st.st_size == 0) break;
+ /* The garbage collector deleted this file before we could get
+ a lock. (It won't delete the file after we get a lock.)
+ Try again. */
}
+}
- if (!state->fdGCLock)
- state->fdGCLock = openGCLock();
+
+void LocalStore::addTempRoot(const StorePath & path)
+{
+ createTempRootsFile();
+
+ /* Open/create the global GC lock file. */
+ {
+ auto fdGCLock(_fdGCLock.lock());
+ if (!*fdGCLock)
+ *fdGCLock = openGCLock();
+ }
restart:
- FdLock gcLock(state->fdGCLock.get(), ltRead, false, "");
+ /* Try to acquire a shared global GC lock (non-blocking). This
+ only succeeds if the garbage collector is not currently
+ running. */
+ FdLock gcLock(_fdGCLock.lock()->get(), ltRead, false, "");
if (!gcLock.acquired) {
/* We couldn't get a shared global GC lock, so the garbage
collector is running. So we have to connect to the garbage
collector and inform it about our root. */
- if (!state->fdRootsSocket) {
+ auto fdRootsSocket(_fdRootsSocket.lock());
+
+ if (!*fdRootsSocket) {
auto socketPath = stateDir.get() + gcSocketPath;
debug("connecting to '%s'", socketPath);
- state->fdRootsSocket = createUnixDomainSocket();
+ *fdRootsSocket = createUnixDomainSocket();
try {
- nix::connect(state->fdRootsSocket.get(), socketPath);
+ nix::connect(fdRootsSocket->get(), socketPath);
} catch (SysError & e) {
/* The garbage collector may have exited, so we need to
restart. */
if (e.errNo == ECONNREFUSED) {
debug("GC socket connection refused");
- state->fdRootsSocket.close();
+ fdRootsSocket->close();
goto restart;
}
+ throw;
}
}
try {
debug("sending GC root '%s'", printStorePath(path));
- writeFull(state->fdRootsSocket.get(), printStorePath(path) + "\n", false);
+ writeFull(fdRootsSocket->get(), printStorePath(path) + "\n", false);
char c;
- readFull(state->fdRootsSocket.get(), &c, 1);
+ readFull(fdRootsSocket->get(), &c, 1);
assert(c == '1');
debug("got ack for GC root '%s'", printStorePath(path));
} catch (SysError & e) {
/* The garbage collector may have exited, so we need to
restart. */
- if (e.errNo == EPIPE) {
+ if (e.errNo == EPIPE || e.errNo == ECONNRESET) {
debug("GC socket disconnected");
- state->fdRootsSocket.close();
+ fdRootsSocket->close();
goto restart;
}
+ throw;
} catch (EndOfFile & e) {
debug("GC socket disconnected");
- state->fdRootsSocket.close();
+ fdRootsSocket->close();
goto restart;
}
}
- /* Append the store path to the temporary roots file. */
+ /* Record the store path in the temporary roots file so it will be
+ seen by a future run of the garbage collector. */
auto s = printStorePath(path) + '\0';
- writeFull(state->fdTempRoots.get(), s);
+ writeFull(_fdTempRoots.lock()->get(), s);
}
@@ -506,6 +520,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Finally cleanup([&]() {
debug("GC roots server shutting down");
+ fdServer.close();
while (true) {
auto item = remove_begin(*connections.lock());
if (!item) break;
@@ -619,6 +634,17 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
Path path = storeDir + "/" + std::string(baseName);
Path realPath = realStoreDir + "/" + std::string(baseName);
+ /* There may be temp directories in the store that are still in use
+ by another process. We need to be sure that we can acquire an
+ exclusive lock before deleting them. */
+ if (baseName.find("tmp-", 0) == 0) {
+ AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY);
+ if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) {
+ debug("skipping locked tempdir '%s'", realPath);
+ return;
+ }
+ }
+
printInfo("deleting '%1%'", path);
results.paths.insert(path);