From e2e168f7c27f5239badf6e8705264bd907d6b82c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 31 Aug 2010 11:47:31 +0000 Subject: [PATCH] `nix-store --verify' improvements: * If a path has disappeared, check its referrers first, and don't try to invalidate paths that have valid referrers. Otherwise we get a foreign key constraint violation. * Read the whole Nix store directory instead of statting each valid path, which is slower. * Acquire the global GC lock. --- src/libstore/gc.cc | 3 +- src/libstore/local-store.cc | 69 ++++++++++++++++++++++++++++--------- src/libstore/local-store.hh | 10 ++++-- 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ea784bcd9..7d58cd50a 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -1,6 +1,5 @@ #include "globals.hh" #include "misc.hh" -#include "pathlocks.hh" #include "local-store.hh" #include @@ -31,7 +30,7 @@ static const int defaultGcLevel = 1000; read. To be precise: when they try to create a new temporary root file, they will block until the garbage collector has finished / yielded the GC lock. */ -static int openGCLock(LockType lockType) +int LocalStore::openGCLock(LockType lockType) { Path fnGCLock = (format("%1%/%2%") % nixStateDir % gcLockName).str(); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 9254ceee4..fd6ad6464 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1226,27 +1226,25 @@ void LocalStore::deleteFromStore(const Path & path, unsigned long long & bytesFr void LocalStore::verifyStore(bool checkContents) { - /* Check whether all valid paths actually exist. */ - printMsg(lvlInfo, "checking path existence"); + printMsg(lvlError, format("reading the Nix store...")); - PathSet validPaths2 = queryValidPaths(), validPaths; + /* Acquire the global GC lock to prevent a garbage collection. */ + AutoCloseFD fdGCLock = openGCLock(ltWrite); - foreach (PathSet::iterator, i, validPaths2) { - checkInterrupt(); - /* !!! invalidatePath() will probably fail due to the foreign - key constraints on the Ref table. */ - if (!isStorePath(*i)) { - printMsg(lvlError, format("path `%1%' is not in the Nix store") % *i); - invalidatePath(*i); - } else if (!pathExists(*i)) { - printMsg(lvlError, format("path `%1%' disappeared") % *i); - invalidatePath(*i); - } else validPaths.insert(*i); - } + Paths entries = readDirectory(nixStore); + PathSet store(entries.begin(), entries.end()); + + /* Check whether all valid paths actually exist. */ + printMsg(lvlInfo, "checking path existence..."); + + PathSet validPaths2 = queryValidPaths(), validPaths, done; + + foreach (PathSet::iterator, i, validPaths2) + verifyPath(*i, store, done, validPaths); /* Optionally, check the content hashes (slow). */ if (checkContents) { - printMsg(lvlInfo, "checking hashes"); + printMsg(lvlInfo, "checking hashes..."); foreach (PathSet::iterator, i, validPaths) { ValidPathInfo info = queryPathInfo(*i); @@ -1264,6 +1262,45 @@ void LocalStore::verifyStore(bool checkContents) } +void LocalStore::verifyPath(const Path & path, const PathSet & store, + PathSet & done, PathSet & validPaths) +{ + checkInterrupt(); + + if (done.find(path) != done.end()) return; + done.insert(path); + + if (!isStorePath(path)) { + printMsg(lvlError, format("path `%1%' is not in the Nix store") % path); + invalidatePath(path); + return; + } + + if (store.find(baseNameOf(path)) == store.end()) { + /* Check any referrers first. If we can invalidate them + first, then we can invalidate this path as well. */ + bool canInvalidate = true; + PathSet referrers; queryReferrers(path, referrers); + foreach (PathSet::iterator, i, referrers) + if (*i != path) { + verifyPath(*i, store, done, validPaths); + if (validPaths.find(*i) != validPaths.end()) + canInvalidate = false; + } + + if (canInvalidate) { + printMsg(lvlError, format("path `%1%' disappeared, removing from database...") % path); + invalidatePath(path); + } else + printMsg(lvlError, format("path `%1%' disappeared, but it still has valid referrers!") % path); + + return; + } + + validPaths.insert(path); +} + + /* Functions for upgrading from the pre-SQLite database. */ PathSet LocalStore::queryValidPathsOld() diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 3ae964f05..0d7ec1f49 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -5,6 +5,7 @@ #include "store-api.hh" #include "util.hh" +#include "pathlocks.hh" class sqlite3; @@ -16,8 +17,8 @@ namespace nix { /* Nix store and database schema version. Version 1 (or 0) was Nix <= 0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10. - Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.14. Version 6 is - Nix 0.15. */ + Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is + Nix 1.0. */ const int nixSchemaVersion = 6; @@ -233,6 +234,9 @@ private: void invalidatePath(const Path & path); + void verifyPath(const Path & path, const PathSet & store, + PathSet & done, PathSet & validPaths); + void upgradeStore6(); PathSet queryValidPathsOld(); ValidPathInfo queryPathInfoOld(const Path & path); @@ -244,6 +248,8 @@ private: bool isActiveTempFile(const GCState & state, const Path & path, const string & suffix); + int openGCLock(LockType lockType); + void startSubstituter(const Path & substituter, RunningSubstituter & runningSubstituter);