Make LocalStore thread-safe
Necessary for multi-threaded commands like "nix verify-paths".
This commit is contained in:
parent
05fbc606fc
commit
f398949b40
|
@ -239,6 +239,9 @@ private:
|
||||||
/* Last time the goals in `waitingForAWhile' where woken up. */
|
/* Last time the goals in `waitingForAWhile' where woken up. */
|
||||||
time_t lastWokenUp;
|
time_t lastWokenUp;
|
||||||
|
|
||||||
|
/* Cache for pathContentsGood(). */
|
||||||
|
std::map<Path, bool> pathContentsGoodCache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/* Set if at least one derivation had a BuildError (i.e. permanent
|
/* Set if at least one derivation had a BuildError (i.e. permanent
|
||||||
|
@ -304,6 +307,12 @@ public:
|
||||||
void waitForInput();
|
void waitForInput();
|
||||||
|
|
||||||
unsigned int exitStatus();
|
unsigned int exitStatus();
|
||||||
|
|
||||||
|
/* Check whether the given valid path exists and has the right
|
||||||
|
contents. */
|
||||||
|
bool pathContentsGood(const Path & path);
|
||||||
|
|
||||||
|
void markContentsGood(const Path & path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1159,7 +1168,7 @@ void DerivationGoal::repairClosure()
|
||||||
/* Check each path (slow!). */
|
/* Check each path (slow!). */
|
||||||
PathSet broken;
|
PathSet broken;
|
||||||
for (auto & i : outputClosure) {
|
for (auto & i : outputClosure) {
|
||||||
if (worker.store.pathContentsGood(i)) continue;
|
if (worker.pathContentsGood(i)) continue;
|
||||||
printMsg(lvlError, format("found corrupted or missing path ‘%1%’ in the output closure of ‘%2%’") % i % drvPath);
|
printMsg(lvlError, format("found corrupted or missing path ‘%1%’ in the output closure of ‘%2%’") % i % drvPath);
|
||||||
Path drvPath2 = outputsToDrv[i];
|
Path drvPath2 = outputsToDrv[i];
|
||||||
if (drvPath2 == "")
|
if (drvPath2 == "")
|
||||||
|
@ -2799,7 +2808,7 @@ void DerivationGoal::registerOutputs()
|
||||||
if (curRound == nrRounds) {
|
if (curRound == nrRounds) {
|
||||||
worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
|
worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
|
||||||
|
|
||||||
worker.store.markContentsGood(path);
|
worker.markContentsGood(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidPathInfo info;
|
ValidPathInfo info;
|
||||||
|
@ -2977,7 +2986,7 @@ PathSet DerivationGoal::checkPathValidity(bool returnValid, bool checkHash)
|
||||||
if (!wantOutput(i.first, wantedOutputs)) continue;
|
if (!wantOutput(i.first, wantedOutputs)) continue;
|
||||||
bool good =
|
bool good =
|
||||||
worker.store.isValidPath(i.second.path) &&
|
worker.store.isValidPath(i.second.path) &&
|
||||||
(!checkHash || worker.store.pathContentsGood(i.second.path));
|
(!checkHash || worker.pathContentsGood(i.second.path));
|
||||||
if (good == returnValid) result.insert(i.second.path);
|
if (good == returnValid) result.insert(i.second.path);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -3385,7 +3394,7 @@ void SubstitutionGoal::finished()
|
||||||
outputLock->setDeletion(true);
|
outputLock->setDeletion(true);
|
||||||
outputLock.reset();
|
outputLock.reset();
|
||||||
|
|
||||||
worker.store.markContentsGood(storePath);
|
worker.markContentsGood(storePath);
|
||||||
|
|
||||||
printMsg(lvlChatty,
|
printMsg(lvlChatty,
|
||||||
format("substitution of path ‘%1%’ succeeded") % storePath);
|
format("substitution of path ‘%1%’ succeeded") % storePath);
|
||||||
|
@ -3785,6 +3794,32 @@ unsigned int Worker::exitStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Worker::pathContentsGood(const Path & path)
|
||||||
|
{
|
||||||
|
std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path);
|
||||||
|
if (i != pathContentsGoodCache.end()) return i->second;
|
||||||
|
printMsg(lvlInfo, format("checking path ‘%1%’...") % path);
|
||||||
|
ValidPathInfo info = store.queryPathInfo(path);
|
||||||
|
bool res;
|
||||||
|
if (!pathExists(path))
|
||||||
|
res = false;
|
||||||
|
else {
|
||||||
|
HashResult current = hashPath(info.narHash.type, path);
|
||||||
|
Hash nullHash(htSHA256);
|
||||||
|
res = info.narHash == nullHash || info.narHash == current.first;
|
||||||
|
}
|
||||||
|
pathContentsGoodCache[path] = res;
|
||||||
|
if (!res) printMsg(lvlError, format("path ‘%1%’ is corrupted or missing!") % path);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Worker::markContentsGood(const Path & path)
|
||||||
|
{
|
||||||
|
pathContentsGoodCache[path] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -147,35 +147,36 @@ Path Store::addPermRoot(const Path & _storePath,
|
||||||
|
|
||||||
void LocalStore::addTempRoot(const Path & path)
|
void LocalStore::addTempRoot(const Path & path)
|
||||||
{
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
/* Create the temporary roots file for this process. */
|
/* Create the temporary roots file for this process. */
|
||||||
if (fdTempRoots == -1) {
|
if (state->fdTempRoots == -1) {
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
|
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
|
||||||
createDirs(dir);
|
createDirs(dir);
|
||||||
|
|
||||||
fnTempRoots = (format("%1%/%2%")
|
state->fnTempRoots = (format("%1%/%2%") % dir % getpid()).str();
|
||||||
% dir % getpid()).str();
|
|
||||||
|
|
||||||
AutoCloseFD fdGCLock = openGCLock(ltRead);
|
AutoCloseFD fdGCLock = openGCLock(ltRead);
|
||||||
|
|
||||||
if (pathExists(fnTempRoots))
|
if (pathExists(state->fnTempRoots))
|
||||||
/* It *must* be stale, since there can be no two
|
/* It *must* be stale, since there can be no two
|
||||||
processes with the same pid. */
|
processes with the same pid. */
|
||||||
unlink(fnTempRoots.c_str());
|
unlink(state->fnTempRoots.c_str());
|
||||||
|
|
||||||
fdTempRoots = openLockFile(fnTempRoots, true);
|
state->fdTempRoots = openLockFile(state->fnTempRoots, true);
|
||||||
|
|
||||||
fdGCLock.close();
|
fdGCLock.close();
|
||||||
|
|
||||||
debug(format("acquiring read lock on ‘%1%’") % fnTempRoots);
|
debug(format("acquiring read lock on ‘%1%’") % state->fnTempRoots);
|
||||||
lockFile(fdTempRoots, ltRead, true);
|
lockFile(state->fdTempRoots, ltRead, true);
|
||||||
|
|
||||||
/* Check whether the garbage collector didn't get in our
|
/* Check whether the garbage collector didn't get in our
|
||||||
way. */
|
way. */
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fdTempRoots, &st) == -1)
|
if (fstat(state->fdTempRoots, &st) == -1)
|
||||||
throw SysError(format("statting ‘%1%’") % fnTempRoots);
|
throw SysError(format("statting ‘%1%’") % state->fnTempRoots);
|
||||||
if (st.st_size == 0) break;
|
if (st.st_size == 0) break;
|
||||||
|
|
||||||
/* The garbage collector deleted this file before we could
|
/* The garbage collector deleted this file before we could
|
||||||
|
@ -187,15 +188,15 @@ void LocalStore::addTempRoot(const Path & path)
|
||||||
|
|
||||||
/* Upgrade the lock to a write lock. This will cause us to block
|
/* Upgrade the lock to a write lock. This will cause us to block
|
||||||
if the garbage collector is holding our lock. */
|
if the garbage collector is holding our lock. */
|
||||||
debug(format("acquiring write lock on ‘%1%’") % fnTempRoots);
|
debug(format("acquiring write lock on ‘%1%’") % state->fnTempRoots);
|
||||||
lockFile(fdTempRoots, ltWrite, true);
|
lockFile(state->fdTempRoots, ltWrite, true);
|
||||||
|
|
||||||
string s = path + '\0';
|
string s = path + '\0';
|
||||||
writeFull(fdTempRoots, s);
|
writeFull(state->fdTempRoots, s);
|
||||||
|
|
||||||
/* Downgrade to a read lock. */
|
/* Downgrade to a read lock. */
|
||||||
debug(format("downgrading to read lock on ‘%1%’") % fnTempRoots);
|
debug(format("downgrading to read lock on ‘%1%’") % state->fnTempRoots);
|
||||||
lockFile(fdTempRoots, ltRead, true);
|
lockFile(state->fdTempRoots, ltRead, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -55,20 +55,21 @@ void checkStoreNotSymlink()
|
||||||
|
|
||||||
|
|
||||||
LocalStore::LocalStore()
|
LocalStore::LocalStore()
|
||||||
: reservedPath(settings.nixDBPath + "/reserved")
|
: linksDir(settings.nixStore + "/.links")
|
||||||
, didSetSubstituterEnv(false)
|
, reservedPath(settings.nixDBPath + "/reserved")
|
||||||
|
, schemaPath(settings.nixDBPath + "/schema")
|
||||||
{
|
{
|
||||||
schemaPath = settings.nixDBPath + "/schema";
|
auto state(_state.lock());
|
||||||
|
|
||||||
if (settings.readOnlyMode) {
|
if (settings.readOnlyMode) {
|
||||||
openDB(false);
|
openDB(*state, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create missing state directories if they don't already exist. */
|
/* Create missing state directories if they don't already exist. */
|
||||||
createDirs(settings.nixStore);
|
createDirs(settings.nixStore);
|
||||||
makeStoreWritable();
|
makeStoreWritable();
|
||||||
createDirs(linksDir = settings.nixStore + "/.links");
|
createDirs(linksDir);
|
||||||
Path profilesDir = settings.nixStateDir + "/profiles";
|
Path profilesDir = settings.nixStateDir + "/profiles";
|
||||||
createDirs(profilesDir);
|
createDirs(profilesDir);
|
||||||
createDirs(settings.nixStateDir + "/temproots");
|
createDirs(settings.nixStateDir + "/temproots");
|
||||||
|
@ -140,7 +141,7 @@ LocalStore::LocalStore()
|
||||||
} catch (SysError & e) {
|
} catch (SysError & e) {
|
||||||
if (e.errNo != EACCES) throw;
|
if (e.errNo != EACCES) throw;
|
||||||
settings.readOnlyMode = true;
|
settings.readOnlyMode = true;
|
||||||
openDB(false);
|
openDB(*state, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ LocalStore::LocalStore()
|
||||||
|
|
||||||
else if (curSchema == 0) { /* new store */
|
else if (curSchema == 0) { /* new store */
|
||||||
curSchema = nixSchemaVersion;
|
curSchema = nixSchemaVersion;
|
||||||
openDB(true);
|
openDB(*state, true);
|
||||||
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
|
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,14 +187,14 @@ LocalStore::LocalStore()
|
||||||
|
|
||||||
if (curSchema < 7) { upgradeStore7(); }
|
if (curSchema < 7) { upgradeStore7(); }
|
||||||
|
|
||||||
openDB(false);
|
openDB(*state, false);
|
||||||
|
|
||||||
if (curSchema < 8) {
|
if (curSchema < 8) {
|
||||||
SQLiteTxn txn(db);
|
SQLiteTxn txn(state->db);
|
||||||
if (sqlite3_exec(db, "alter table ValidPaths add column ultimate integer", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(state->db, "alter table ValidPaths add column ultimate integer", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "upgrading database schema");
|
throwSQLiteError(state->db, "upgrading database schema");
|
||||||
if (sqlite3_exec(db, "alter table ValidPaths add column sigs text", 0, 0, 0) != SQLITE_OK)
|
if (sqlite3_exec(state->db, "alter table ValidPaths add column sigs text", 0, 0, 0) != SQLITE_OK)
|
||||||
throwSQLiteError(db, "upgrading database schema");
|
throwSQLiteError(state->db, "upgrading database schema");
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,14 +203,16 @@ LocalStore::LocalStore()
|
||||||
lockFile(globalLock, ltRead, true);
|
lockFile(globalLock, ltRead, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
else openDB(false);
|
else openDB(*state, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalStore::~LocalStore()
|
LocalStore::~LocalStore()
|
||||||
{
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (auto & i : runningSubstituters) {
|
for (auto & i : state->runningSubstituters) {
|
||||||
if (i.second.disabled) continue;
|
if (i.second.disabled) continue;
|
||||||
i.second.to.close();
|
i.second.to.close();
|
||||||
i.second.from.close();
|
i.second.from.close();
|
||||||
|
@ -222,9 +225,9 @@ LocalStore::~LocalStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (fdTempRoots != -1) {
|
if (state->fdTempRoots != -1) {
|
||||||
fdTempRoots.close();
|
state->fdTempRoots.close();
|
||||||
unlink(fnTempRoots.c_str());
|
unlink(state->fnTempRoots.c_str());
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
ignoreException();
|
ignoreException();
|
||||||
|
@ -250,13 +253,14 @@ bool LocalStore::haveWriteAccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::openDB(bool create)
|
void LocalStore::openDB(State & state, bool create)
|
||||||
{
|
{
|
||||||
if (!haveWriteAccess())
|
if (!haveWriteAccess())
|
||||||
throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath);
|
throw SysError(format("Nix database directory ‘%1%’ is not writable") % settings.nixDBPath);
|
||||||
|
|
||||||
/* Open the Nix database. */
|
/* Open the Nix database. */
|
||||||
string dbPath = settings.nixDBPath + "/db.sqlite";
|
string dbPath = settings.nixDBPath + "/db.sqlite";
|
||||||
|
auto & db(state.db);
|
||||||
if (sqlite3_open_v2(dbPath.c_str(), &db.db,
|
if (sqlite3_open_v2(dbPath.c_str(), &db.db,
|
||||||
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
|
SQLITE_OPEN_READWRITE | (create ? SQLITE_OPEN_CREATE : 0), 0) != SQLITE_OK)
|
||||||
throw Error(format("cannot open Nix database ‘%1%’") % dbPath);
|
throw Error(format("cannot open Nix database ‘%1%’") % dbPath);
|
||||||
|
@ -309,41 +313,41 @@ void LocalStore::openDB(bool create)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prepare SQL statements. */
|
/* Prepare SQL statements. */
|
||||||
stmtRegisterValidPath.create(db,
|
state.stmtRegisterValidPath.create(db,
|
||||||
"insert into ValidPaths (path, hash, registrationTime, deriver, narSize, ultimate, sigs) values (?, ?, ?, ?, ?, ?, ?);");
|
"insert into ValidPaths (path, hash, registrationTime, deriver, narSize, ultimate, sigs) values (?, ?, ?, ?, ?, ?, ?);");
|
||||||
stmtUpdatePathInfo.create(db,
|
state.stmtUpdatePathInfo.create(db,
|
||||||
"update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ? where path = ?;");
|
"update ValidPaths set narSize = ?, hash = ?, ultimate = ?, sigs = ? where path = ?;");
|
||||||
stmtAddReference.create(db,
|
state.stmtAddReference.create(db,
|
||||||
"insert or replace into Refs (referrer, reference) values (?, ?);");
|
"insert or replace into Refs (referrer, reference) values (?, ?);");
|
||||||
stmtQueryPathInfo.create(db,
|
state.stmtQueryPathInfo.create(db,
|
||||||
"select id, hash, registrationTime, deriver, narSize, ultimate, sigs from ValidPaths where path = ?;");
|
"select id, hash, registrationTime, deriver, narSize, ultimate, sigs from ValidPaths where path = ?;");
|
||||||
stmtQueryReferences.create(db,
|
state.stmtQueryReferences.create(db,
|
||||||
"select path from Refs join ValidPaths on reference = id where referrer = ?;");
|
"select path from Refs join ValidPaths on reference = id where referrer = ?;");
|
||||||
stmtQueryReferrers.create(db,
|
state.stmtQueryReferrers.create(db,
|
||||||
"select path from Refs join ValidPaths on referrer = id where reference = (select id from ValidPaths where path = ?);");
|
"select path from Refs join ValidPaths on referrer = id where reference = (select id from ValidPaths where path = ?);");
|
||||||
stmtInvalidatePath.create(db,
|
state.stmtInvalidatePath.create(db,
|
||||||
"delete from ValidPaths where path = ?;");
|
"delete from ValidPaths where path = ?;");
|
||||||
stmtRegisterFailedPath.create(db,
|
state.stmtRegisterFailedPath.create(db,
|
||||||
"insert or ignore into FailedPaths (path, time) values (?, ?);");
|
"insert or ignore into FailedPaths (path, time) values (?, ?);");
|
||||||
stmtHasPathFailed.create(db,
|
state.stmtHasPathFailed.create(db,
|
||||||
"select time from FailedPaths where path = ?;");
|
"select time from FailedPaths where path = ?;");
|
||||||
stmtQueryFailedPaths.create(db,
|
state.stmtQueryFailedPaths.create(db,
|
||||||
"select path from FailedPaths;");
|
"select path from FailedPaths;");
|
||||||
// If the path is a derivation, then clear its outputs.
|
// If the path is a derivation, then clear its outputs.
|
||||||
stmtClearFailedPath.create(db,
|
state.stmtClearFailedPath.create(db,
|
||||||
"delete from FailedPaths where ?1 = '*' or path = ?1 "
|
"delete from FailedPaths where ?1 = '*' or path = ?1 "
|
||||||
"or path in (select d.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where v.path = ?1);");
|
"or path in (select d.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where v.path = ?1);");
|
||||||
stmtAddDerivationOutput.create(db,
|
state.stmtAddDerivationOutput.create(db,
|
||||||
"insert or replace into DerivationOutputs (drv, id, path) values (?, ?, ?);");
|
"insert or replace into DerivationOutputs (drv, id, path) values (?, ?, ?);");
|
||||||
stmtQueryValidDerivers.create(db,
|
state.stmtQueryValidDerivers.create(db,
|
||||||
"select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where d.path = ?;");
|
"select v.id, v.path from DerivationOutputs d join ValidPaths v on d.drv = v.id where d.path = ?;");
|
||||||
stmtQueryDerivationOutputs.create(db,
|
state.stmtQueryDerivationOutputs.create(db,
|
||||||
"select id, path from DerivationOutputs where drv = ?;");
|
"select id, path from DerivationOutputs where drv = ?;");
|
||||||
// Use "path >= ?" with limit 1 rather than "path like '?%'" to
|
// Use "path >= ?" with limit 1 rather than "path like '?%'" to
|
||||||
// ensure efficient lookup.
|
// ensure efficient lookup.
|
||||||
stmtQueryPathFromHashPart.create(db,
|
state.stmtQueryPathFromHashPart.create(db,
|
||||||
"select path from ValidPaths where path >= ? limit 1;");
|
"select path from ValidPaths where path >= ? limit 1;");
|
||||||
stmtQueryValidPaths.create(db, "select path from ValidPaths");
|
state.stmtQueryValidPaths.create(db, "select path from ValidPaths");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -538,9 +542,10 @@ void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation &
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
uint64_t LocalStore::addValidPath(State & state,
|
||||||
|
const ValidPathInfo & info, bool checkOutputs)
|
||||||
{
|
{
|
||||||
stmtRegisterValidPath.use()
|
state.stmtRegisterValidPath.use()
|
||||||
(info.path)
|
(info.path)
|
||||||
("sha256:" + printHash(info.narHash))
|
("sha256:" + printHash(info.narHash))
|
||||||
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
(info.registrationTime == 0 ? time(0) : info.registrationTime)
|
||||||
|
@ -549,7 +554,7 @@ uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
||||||
(info.ultimate ? 1 : 0, info.ultimate)
|
(info.ultimate ? 1 : 0, info.ultimate)
|
||||||
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
(concatStringsSep(" ", info.sigs), !info.sigs.empty())
|
||||||
.exec();
|
.exec();
|
||||||
uint64_t id = sqlite3_last_insert_rowid(db);
|
uint64_t id = sqlite3_last_insert_rowid(state.db);
|
||||||
|
|
||||||
/* If this is a derivation, then store the derivation outputs in
|
/* If this is a derivation, then store the derivation outputs in
|
||||||
the database. This is useful for the garbage collector: it can
|
the database. This is useful for the garbage collector: it can
|
||||||
|
@ -566,7 +571,7 @@ uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
||||||
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
if (checkOutputs) checkDerivationOutputs(info.path, drv);
|
||||||
|
|
||||||
for (auto & i : drv.outputs) {
|
for (auto & i : drv.outputs) {
|
||||||
stmtAddDerivationOutput.use()
|
state.stmtAddDerivationOutput.use()
|
||||||
(id)
|
(id)
|
||||||
(i.first)
|
(i.first)
|
||||||
(i.second.path)
|
(i.second.path)
|
||||||
|
@ -578,16 +583,11 @@ uint64_t LocalStore::addValidPath(const ValidPathInfo & info, bool checkOutputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::addReference(uint64_t referrer, uint64_t reference)
|
|
||||||
{
|
|
||||||
stmtAddReference.use()(referrer)(reference).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::registerFailedPath(const Path & path)
|
void LocalStore::registerFailedPath(const Path & path)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
stmtRegisterFailedPath.use()(path)(time(0)).step();
|
auto state(_state.lock());
|
||||||
|
state->stmtRegisterFailedPath.use()(path)(time(0)).step();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -595,7 +595,8 @@ void LocalStore::registerFailedPath(const Path & path)
|
||||||
bool LocalStore::hasPathFailed(const Path & path)
|
bool LocalStore::hasPathFailed(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<bool>([&]() {
|
return retrySQLite<bool>([&]() {
|
||||||
return stmtHasPathFailed.use()(path).next();
|
auto state(_state.lock());
|
||||||
|
return state->stmtHasPathFailed.use()(path).next();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +604,9 @@ bool LocalStore::hasPathFailed(const Path & path)
|
||||||
PathSet LocalStore::queryFailedPaths()
|
PathSet LocalStore::queryFailedPaths()
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
auto useQueryFailedPaths(stmtQueryFailedPaths.use());
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto useQueryFailedPaths(state->stmtQueryFailedPaths.use());
|
||||||
|
|
||||||
PathSet res;
|
PathSet res;
|
||||||
while (useQueryFailedPaths.next())
|
while (useQueryFailedPaths.next())
|
||||||
|
@ -617,10 +620,12 @@ PathSet LocalStore::queryFailedPaths()
|
||||||
void LocalStore::clearFailedPaths(const PathSet & paths)
|
void LocalStore::clearFailedPaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
SQLiteTxn txn(db);
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
SQLiteTxn txn(state->db);
|
||||||
|
|
||||||
for (auto & path : paths)
|
for (auto & path : paths)
|
||||||
stmtClearFailedPath.use()(path).exec();
|
state->stmtClearFailedPath.use()(path).exec();
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
});
|
});
|
||||||
|
@ -649,9 +654,10 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
return retrySQLite<ValidPathInfo>([&]() {
|
return retrySQLite<ValidPathInfo>([&]() {
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
/* Get the path info. */
|
/* Get the path info. */
|
||||||
auto useQueryPathInfo(stmtQueryPathInfo.use()(path));
|
auto useQueryPathInfo(state->stmtQueryPathInfo.use()(path));
|
||||||
|
|
||||||
if (!useQueryPathInfo.next())
|
if (!useQueryPathInfo.next())
|
||||||
throw Error(format("path ‘%1%’ is not valid") % path);
|
throw Error(format("path ‘%1%’ is not valid") % path);
|
||||||
|
@ -662,19 +668,19 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
|
|
||||||
info.registrationTime = useQueryPathInfo.getInt(2);
|
info.registrationTime = useQueryPathInfo.getInt(2);
|
||||||
|
|
||||||
auto s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 3);
|
auto s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 3);
|
||||||
if (s) info.deriver = s;
|
if (s) info.deriver = s;
|
||||||
|
|
||||||
/* Note that narSize = NULL yields 0. */
|
/* Note that narSize = NULL yields 0. */
|
||||||
info.narSize = useQueryPathInfo.getInt(4);
|
info.narSize = useQueryPathInfo.getInt(4);
|
||||||
|
|
||||||
info.ultimate = sqlite3_column_int(stmtQueryPathInfo, 5) == 1;
|
info.ultimate = useQueryPathInfo.getInt(5) == 1;
|
||||||
|
|
||||||
s = (const char *) sqlite3_column_text(stmtQueryPathInfo, 6);
|
s = (const char *) sqlite3_column_text(state->stmtQueryPathInfo, 6);
|
||||||
if (s) info.sigs = tokenizeString<StringSet>(s, " ");
|
if (s) info.sigs = tokenizeString<StringSet>(s, " ");
|
||||||
|
|
||||||
/* Get the references. */
|
/* Get the references. */
|
||||||
auto useQueryReferences(stmtQueryReferences.use()(info.id));
|
auto useQueryReferences(state->stmtQueryReferences.use()(info.id));
|
||||||
|
|
||||||
while (useQueryReferences.next())
|
while (useQueryReferences.next())
|
||||||
info.references.insert(useQueryReferences.getStr(0));
|
info.references.insert(useQueryReferences.getStr(0));
|
||||||
|
@ -685,9 +691,9 @@ ValidPathInfo LocalStore::queryPathInfo(const Path & path)
|
||||||
|
|
||||||
|
|
||||||
/* Update path info in the database. */
|
/* Update path info in the database. */
|
||||||
void LocalStore::updatePathInfo(const ValidPathInfo & info)
|
void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info)
|
||||||
{
|
{
|
||||||
stmtUpdatePathInfo.use()
|
state.stmtUpdatePathInfo.use()
|
||||||
(info.narSize, info.narSize != 0)
|
(info.narSize, info.narSize != 0)
|
||||||
("sha256:" + printHash(info.narHash))
|
("sha256:" + printHash(info.narHash))
|
||||||
(info.ultimate ? 1 : 0, info.ultimate)
|
(info.ultimate ? 1 : 0, info.ultimate)
|
||||||
|
@ -697,44 +703,44 @@ void LocalStore::updatePathInfo(const ValidPathInfo & info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint64_t LocalStore::queryValidPathId(const Path & path)
|
uint64_t LocalStore::queryValidPathId(State & state, const Path & path)
|
||||||
{
|
{
|
||||||
auto use(stmtQueryPathInfo.use()(path));
|
auto use(state.stmtQueryPathInfo.use()(path));
|
||||||
if (!use.next())
|
if (!use.next())
|
||||||
throw Error(format("path ‘%1%’ is not valid") % path);
|
throw Error(format("path ‘%1%’ is not valid") % path);
|
||||||
return use.getInt(0);
|
return use.getInt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::isValidPath_(const Path & path)
|
bool LocalStore::isValidPath(State & state, const Path & path)
|
||||||
{
|
{
|
||||||
return stmtQueryPathInfo.use()(path).next();
|
return state.stmtQueryPathInfo.use()(path).next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::isValidPath(const Path & path)
|
bool LocalStore::isValidPath(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<bool>([&]() {
|
return retrySQLite<bool>([&]() {
|
||||||
return isValidPath_(path);
|
auto state(_state.lock());
|
||||||
|
return isValidPath(*state, path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
PathSet LocalStore::queryValidPaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
|
||||||
PathSet res;
|
PathSet res;
|
||||||
for (auto & i : paths)
|
for (auto & i : paths)
|
||||||
if (isValidPath_(i)) res.insert(i);
|
if (isValidPath(i)) res.insert(i);
|
||||||
return res;
|
return res;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PathSet LocalStore::queryAllValidPaths()
|
PathSet LocalStore::queryAllValidPaths()
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
auto use(stmtQueryValidPaths.use());
|
auto state(_state.lock());
|
||||||
|
auto use(state->stmtQueryValidPaths.use());
|
||||||
PathSet res;
|
PathSet res;
|
||||||
while (use.next()) res.insert(use.getStr(0));
|
while (use.next()) res.insert(use.getStr(0));
|
||||||
return res;
|
return res;
|
||||||
|
@ -742,9 +748,9 @@ PathSet LocalStore::queryAllValidPaths()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::queryReferrers_(const Path & path, PathSet & referrers)
|
void LocalStore::queryReferrers(State & state, const Path & path, PathSet & referrers)
|
||||||
{
|
{
|
||||||
auto useQueryReferrers(stmtQueryReferrers.use()(path));
|
auto useQueryReferrers(state.stmtQueryReferrers.use()(path));
|
||||||
|
|
||||||
while (useQueryReferrers.next())
|
while (useQueryReferrers.next())
|
||||||
referrers.insert(useQueryReferrers.getStr(0));
|
referrers.insert(useQueryReferrers.getStr(0));
|
||||||
|
@ -755,7 +761,8 @@ void LocalStore::queryReferrers(const Path & path, PathSet & referrers)
|
||||||
{
|
{
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
return retrySQLite<void>([&]() {
|
return retrySQLite<void>([&]() {
|
||||||
queryReferrers_(path, referrers);
|
auto state(_state.lock());
|
||||||
|
queryReferrers(*state, path, referrers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,7 +778,9 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
auto useQueryValidDerivers(stmtQueryValidDerivers.use()(path));
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto useQueryValidDerivers(state->stmtQueryValidDerivers.use()(path));
|
||||||
|
|
||||||
PathSet derivers;
|
PathSet derivers;
|
||||||
while (useQueryValidDerivers.next())
|
while (useQueryValidDerivers.next())
|
||||||
|
@ -785,7 +794,10 @@ PathSet LocalStore::queryValidDerivers(const Path & path)
|
||||||
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<PathSet>([&]() {
|
return retrySQLite<PathSet>([&]() {
|
||||||
auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()
|
||||||
|
(queryValidPathId(*state, path)));
|
||||||
|
|
||||||
PathSet outputs;
|
PathSet outputs;
|
||||||
while (useQueryDerivationOutputs.next())
|
while (useQueryDerivationOutputs.next())
|
||||||
|
@ -799,7 +811,10 @@ PathSet LocalStore::queryDerivationOutputs(const Path & path)
|
||||||
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
StringSet LocalStore::queryDerivationOutputNames(const Path & path)
|
||||||
{
|
{
|
||||||
return retrySQLite<StringSet>([&]() {
|
return retrySQLite<StringSet>([&]() {
|
||||||
auto useQueryDerivationOutputs(stmtQueryDerivationOutputs.use()(queryValidPathId(path)));
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()
|
||||||
|
(queryValidPathId(*state, path)));
|
||||||
|
|
||||||
StringSet outputNames;
|
StringSet outputNames;
|
||||||
while (useQueryDerivationOutputs.next())
|
while (useQueryDerivationOutputs.next())
|
||||||
|
@ -817,11 +832,13 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
|
||||||
Path prefix = settings.nixStore + "/" + hashPart;
|
Path prefix = settings.nixStore + "/" + hashPart;
|
||||||
|
|
||||||
return retrySQLite<Path>([&]() {
|
return retrySQLite<Path>([&]() {
|
||||||
auto useQueryPathFromHashPart(stmtQueryPathFromHashPart.use()(prefix));
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
auto useQueryPathFromHashPart(state->stmtQueryPathFromHashPart.use()(prefix));
|
||||||
|
|
||||||
if (!useQueryPathFromHashPart.next()) return "";
|
if (!useQueryPathFromHashPart.next()) return "";
|
||||||
|
|
||||||
const char * s = (const char *) sqlite3_column_text(stmtQueryPathFromHashPart, 0);
|
const char * s = (const char *) sqlite3_column_text(state->stmtQueryPathFromHashPart, 0);
|
||||||
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
return s && prefix.compare(0, prefix.size(), s, prefix.size()) == 0 ? s : "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -829,13 +846,13 @@ Path LocalStore::queryPathFromHashPart(const string & hashPart)
|
||||||
|
|
||||||
void LocalStore::setSubstituterEnv()
|
void LocalStore::setSubstituterEnv()
|
||||||
{
|
{
|
||||||
if (didSetSubstituterEnv) return;
|
static std::atomic_flag done;
|
||||||
|
|
||||||
|
if (done.test_and_set()) return;
|
||||||
|
|
||||||
/* Pass configuration options (including those overridden with
|
/* Pass configuration options (including those overridden with
|
||||||
--option) to substituters. */
|
--option) to substituters. */
|
||||||
setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
|
setenv("_NIX_OPTIONS", settings.pack().c_str(), 1);
|
||||||
|
|
||||||
didSetSubstituterEnv = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -957,10 +974,12 @@ template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & r
|
||||||
|
|
||||||
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||||
{
|
{
|
||||||
|
auto state(_state.lock());
|
||||||
|
|
||||||
PathSet res;
|
PathSet res;
|
||||||
for (auto & i : settings.substituters) {
|
for (auto & i : settings.substituters) {
|
||||||
if (res.size() == paths.size()) break;
|
if (res.size() == paths.size()) break;
|
||||||
RunningSubstituter & run(runningSubstituters[i]);
|
RunningSubstituter & run(state->runningSubstituters[i]);
|
||||||
startSubstituter(i, run);
|
startSubstituter(i, run);
|
||||||
if (run.disabled) continue;
|
if (run.disabled) continue;
|
||||||
string s = "have ";
|
string s = "have ";
|
||||||
|
@ -977,6 +996,7 @@ PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||||
res.insert(path);
|
res.insert(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -984,7 +1004,9 @@ PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||||
void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
||||||
PathSet & paths, SubstitutablePathInfos & infos)
|
PathSet & paths, SubstitutablePathInfos & infos)
|
||||||
{
|
{
|
||||||
RunningSubstituter & run(runningSubstituters[substituter]);
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
RunningSubstituter & run(state->runningSubstituters[substituter]);
|
||||||
startSubstituter(substituter, run);
|
startSubstituter(substituter, run);
|
||||||
if (run.disabled) return;
|
if (run.disabled) return;
|
||||||
|
|
||||||
|
@ -1048,22 +1070,24 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
if (settings.syncBeforeRegistering) sync();
|
if (settings.syncBeforeRegistering) sync();
|
||||||
|
|
||||||
return retrySQLite<void>([&]() {
|
return retrySQLite<void>([&]() {
|
||||||
SQLiteTxn txn(db);
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
SQLiteTxn txn(state->db);
|
||||||
PathSet paths;
|
PathSet paths;
|
||||||
|
|
||||||
for (auto & i : infos) {
|
for (auto & i : infos) {
|
||||||
assert(i.narHash.type == htSHA256);
|
assert(i.narHash.type == htSHA256);
|
||||||
if (isValidPath_(i.path))
|
if (isValidPath(*state, i.path))
|
||||||
updatePathInfo(i);
|
updatePathInfo(*state, i);
|
||||||
else
|
else
|
||||||
addValidPath(i, false);
|
addValidPath(*state, i, false);
|
||||||
paths.insert(i.path);
|
paths.insert(i.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto & i : infos) {
|
for (auto & i : infos) {
|
||||||
auto referrer = queryValidPathId(i.path);
|
auto referrer = queryValidPathId(*state, i.path);
|
||||||
for (auto & j : i.references)
|
for (auto & j : i.references)
|
||||||
addReference(referrer, queryValidPathId(j));
|
state->stmtAddReference.use()(referrer)(queryValidPathId(*state, j)).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the derivation outputs are correct. We can't do
|
/* Check that the derivation outputs are correct. We can't do
|
||||||
|
@ -1090,13 +1114,11 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||||
|
|
||||||
/* Invalidate a path. The caller is responsible for checking that
|
/* Invalidate a path. The caller is responsible for checking that
|
||||||
there are no referrers. */
|
there are no referrers. */
|
||||||
void LocalStore::invalidatePath(const Path & path)
|
void LocalStore::invalidatePath(State & state, const Path & path)
|
||||||
{
|
{
|
||||||
debug(format("invalidating path ‘%1%’") % path);
|
debug(format("invalidating path ‘%1%’") % path);
|
||||||
|
|
||||||
drvHashes.erase(path);
|
state.stmtInvalidatePath.use()(path).exec();
|
||||||
|
|
||||||
stmtInvalidatePath.use()(path).exec();
|
|
||||||
|
|
||||||
/* Note that the foreign key constraints on the Refs table take
|
/* Note that the foreign key constraints on the Refs table take
|
||||||
care of deleting the references entries for `path'. */
|
care of deleting the references entries for `path'. */
|
||||||
|
@ -1465,15 +1487,17 @@ void LocalStore::invalidatePathChecked(const Path & path)
|
||||||
assertStorePath(path);
|
assertStorePath(path);
|
||||||
|
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
SQLiteTxn txn(db);
|
auto state(_state.lock());
|
||||||
|
|
||||||
if (isValidPath_(path)) {
|
SQLiteTxn txn(state->db);
|
||||||
PathSet referrers; queryReferrers_(path, referrers);
|
|
||||||
|
if (isValidPath(*state, path)) {
|
||||||
|
PathSet referrers; queryReferrers(*state, path, referrers);
|
||||||
referrers.erase(path); /* ignore self-references */
|
referrers.erase(path); /* ignore self-references */
|
||||||
if (!referrers.empty())
|
if (!referrers.empty())
|
||||||
throw PathInUse(format("cannot delete path ‘%1%’ because it is in use by %2%")
|
throw PathInUse(format("cannot delete path ‘%1%’ because it is in use by %2%")
|
||||||
% path % showPaths(referrers));
|
% path % showPaths(referrers));
|
||||||
invalidatePath(path);
|
invalidatePath(*state, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
|
@ -1542,7 +1566,10 @@ bool LocalStore::verifyStore(bool checkContents, bool repair)
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update) updatePathInfo(info);
|
if (update) {
|
||||||
|
auto state(_state.lock());
|
||||||
|
updatePathInfo(*state, info);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1572,7 +1599,8 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||||
|
|
||||||
if (!isStorePath(path)) {
|
if (!isStorePath(path)) {
|
||||||
printMsg(lvlError, format("path ‘%1%’ is not in the Nix store") % path);
|
printMsg(lvlError, format("path ‘%1%’ is not in the Nix store") % path);
|
||||||
invalidatePath(path);
|
auto state(_state.lock());
|
||||||
|
invalidatePath(*state, path);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1590,7 +1618,8 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||||
|
|
||||||
if (canInvalidate) {
|
if (canInvalidate) {
|
||||||
printMsg(lvlError, format("path ‘%1%’ disappeared, removing from database...") % path);
|
printMsg(lvlError, format("path ‘%1%’ disappeared, removing from database...") % path);
|
||||||
invalidatePath(path);
|
auto state(_state.lock());
|
||||||
|
invalidatePath(*state, path);
|
||||||
} else {
|
} else {
|
||||||
printMsg(lvlError, format("path ‘%1%’ disappeared, but it still has valid referrers!") % path);
|
printMsg(lvlError, format("path ‘%1%’ disappeared, but it still has valid referrers!") % path);
|
||||||
if (repair)
|
if (repair)
|
||||||
|
@ -1610,32 +1639,6 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool LocalStore::pathContentsGood(const Path & path)
|
|
||||||
{
|
|
||||||
std::map<Path, bool>::iterator i = pathContentsGoodCache.find(path);
|
|
||||||
if (i != pathContentsGoodCache.end()) return i->second;
|
|
||||||
printMsg(lvlInfo, format("checking path ‘%1%’...") % path);
|
|
||||||
ValidPathInfo info = queryPathInfo(path);
|
|
||||||
bool res;
|
|
||||||
if (!pathExists(path))
|
|
||||||
res = false;
|
|
||||||
else {
|
|
||||||
HashResult current = hashPath(info.narHash.type, path);
|
|
||||||
Hash nullHash(htSHA256);
|
|
||||||
res = info.narHash == nullHash || info.narHash == current.first;
|
|
||||||
}
|
|
||||||
pathContentsGoodCache[path] = res;
|
|
||||||
if (!res) printMsg(lvlError, format("path ‘%1%’ is corrupted or missing!") % path);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::markContentsGood(const Path & path)
|
|
||||||
{
|
|
||||||
pathContentsGoodCache[path] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
|
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
|
||||||
|
|
||||||
static void makeMutable(const Path & path)
|
static void makeMutable(const Path & path)
|
||||||
|
@ -1690,21 +1693,25 @@ void LocalStore::upgradeStore7()
|
||||||
|
|
||||||
void LocalStore::vacuumDB()
|
void LocalStore::vacuumDB()
|
||||||
{
|
{
|
||||||
if (sqlite3_exec(db, "vacuum;", 0, 0, 0) != SQLITE_OK)
|
auto state(_state.lock());
|
||||||
throwSQLiteError(db, "vacuuming SQLite database");
|
|
||||||
|
if (sqlite3_exec(state->db, "vacuum;", 0, 0, 0) != SQLITE_OK)
|
||||||
|
throwSQLiteError(state->db, "vacuuming SQLite database");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void LocalStore::addSignatures(const Path & storePath, const StringSet & sigs)
|
void LocalStore::addSignatures(const Path & storePath, const StringSet & sigs)
|
||||||
{
|
{
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
SQLiteTxn txn(db);
|
auto state(_state.lock());
|
||||||
|
|
||||||
|
SQLiteTxn txn(state->db);
|
||||||
|
|
||||||
auto info = queryPathInfo(storePath);
|
auto info = queryPathInfo(storePath);
|
||||||
|
|
||||||
info.sigs.insert(sigs.begin(), sigs.end());
|
info.sigs.insert(sigs.begin(), sigs.end());
|
||||||
|
|
||||||
updatePathInfo(info);
|
updatePathInfo(*state, info);
|
||||||
|
|
||||||
txn.commit();
|
txn.commit();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "sqlite.hh"
|
#include "sqlite.hh"
|
||||||
#include <string>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "pathlocks.hh"
|
#include "pathlocks.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "sync.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -52,12 +54,47 @@ struct RunningSubstituter
|
||||||
class LocalStore : public LocalFSStore
|
class LocalStore : public LocalFSStore
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/* Lock file used for upgrading. */
|
||||||
|
AutoCloseFD globalLock;
|
||||||
|
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
/* The SQLite database object. */
|
||||||
|
SQLite db;
|
||||||
|
|
||||||
|
/* Some precompiled SQLite statements. */
|
||||||
|
SQLiteStmt stmtRegisterValidPath;
|
||||||
|
SQLiteStmt stmtUpdatePathInfo;
|
||||||
|
SQLiteStmt stmtAddReference;
|
||||||
|
SQLiteStmt stmtQueryPathInfo;
|
||||||
|
SQLiteStmt stmtQueryReferences;
|
||||||
|
SQLiteStmt stmtQueryReferrers;
|
||||||
|
SQLiteStmt stmtInvalidatePath;
|
||||||
|
SQLiteStmt stmtRegisterFailedPath;
|
||||||
|
SQLiteStmt stmtHasPathFailed;
|
||||||
|
SQLiteStmt stmtQueryFailedPaths;
|
||||||
|
SQLiteStmt stmtClearFailedPath;
|
||||||
|
SQLiteStmt stmtAddDerivationOutput;
|
||||||
|
SQLiteStmt stmtQueryValidDerivers;
|
||||||
|
SQLiteStmt stmtQueryDerivationOutputs;
|
||||||
|
SQLiteStmt stmtQueryPathFromHashPart;
|
||||||
|
SQLiteStmt stmtQueryValidPaths;
|
||||||
|
|
||||||
|
/* The file to which we write our temporary roots. */
|
||||||
|
Path fnTempRoots;
|
||||||
|
AutoCloseFD fdTempRoots;
|
||||||
|
|
||||||
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
|
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
|
||||||
RunningSubstituters runningSubstituters;
|
RunningSubstituters runningSubstituters;
|
||||||
|
|
||||||
Path linksDir;
|
};
|
||||||
|
|
||||||
Path reservedPath;
|
Sync<State, std::recursive_mutex> _state;
|
||||||
|
|
||||||
|
const Path linksDir;
|
||||||
|
const Path reservedPath;
|
||||||
|
const Path schemaPath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -174,76 +211,25 @@ public:
|
||||||
a substituter (if available). */
|
a substituter (if available). */
|
||||||
void repairPath(const Path & path);
|
void repairPath(const Path & path);
|
||||||
|
|
||||||
/* Check whether the given valid path exists and has the right
|
|
||||||
contents. */
|
|
||||||
bool pathContentsGood(const Path & path);
|
|
||||||
|
|
||||||
void markContentsGood(const Path & path);
|
|
||||||
|
|
||||||
void setSubstituterEnv();
|
void setSubstituterEnv();
|
||||||
|
|
||||||
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
void addSignatures(const Path & storePath, const StringSet & sigs) override;
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Path schemaPath;
|
|
||||||
|
|
||||||
/* Lock file used for upgrading. */
|
|
||||||
AutoCloseFD globalLock;
|
|
||||||
|
|
||||||
/* The SQLite database object. */
|
|
||||||
SQLite db;
|
|
||||||
|
|
||||||
/* Some precompiled SQLite statements. */
|
|
||||||
SQLiteStmt stmtRegisterValidPath;
|
|
||||||
SQLiteStmt stmtUpdatePathInfo;
|
|
||||||
SQLiteStmt stmtAddReference;
|
|
||||||
SQLiteStmt stmtQueryPathInfo;
|
|
||||||
SQLiteStmt stmtQueryReferences;
|
|
||||||
SQLiteStmt stmtQueryReferrers;
|
|
||||||
SQLiteStmt stmtInvalidatePath;
|
|
||||||
SQLiteStmt stmtRegisterFailedPath;
|
|
||||||
SQLiteStmt stmtHasPathFailed;
|
|
||||||
SQLiteStmt stmtQueryFailedPaths;
|
|
||||||
SQLiteStmt stmtClearFailedPath;
|
|
||||||
SQLiteStmt stmtAddDerivationOutput;
|
|
||||||
SQLiteStmt stmtQueryValidDerivers;
|
|
||||||
SQLiteStmt stmtQueryDerivationOutputs;
|
|
||||||
SQLiteStmt stmtQueryPathFromHashPart;
|
|
||||||
SQLiteStmt stmtQueryValidPaths;
|
|
||||||
|
|
||||||
/* Cache for pathContentsGood(). */
|
|
||||||
std::map<Path, bool> pathContentsGoodCache;
|
|
||||||
|
|
||||||
bool didSetSubstituterEnv;
|
|
||||||
|
|
||||||
/* The file to which we write our temporary roots. */
|
|
||||||
Path fnTempRoots;
|
|
||||||
AutoCloseFD fdTempRoots;
|
|
||||||
|
|
||||||
int getSchema();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
static bool haveWriteAccess();
|
static bool haveWriteAccess();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void openDB(bool create);
|
int getSchema();
|
||||||
|
|
||||||
|
void openDB(State & state, bool create);
|
||||||
|
|
||||||
void makeStoreWritable();
|
void makeStoreWritable();
|
||||||
|
|
||||||
uint64_t queryValidPathId(const Path & path);
|
uint64_t queryValidPathId(State & state, const Path & path);
|
||||||
|
|
||||||
uint64_t addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
|
uint64_t addValidPath(State & state, const ValidPathInfo & info, bool checkOutputs = true);
|
||||||
|
|
||||||
void addReference(uint64_t referrer, uint64_t reference);
|
void invalidatePath(State & state, const Path & path);
|
||||||
|
|
||||||
void appendReferrer(const Path & from, const Path & to, bool lock);
|
|
||||||
|
|
||||||
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
|
|
||||||
|
|
||||||
void invalidatePath(const Path & path);
|
|
||||||
|
|
||||||
/* Delete a path from the Nix store. */
|
/* Delete a path from the Nix store. */
|
||||||
void invalidatePathChecked(const Path & path);
|
void invalidatePathChecked(const Path & path);
|
||||||
|
@ -251,7 +237,7 @@ private:
|
||||||
void verifyPath(const Path & path, const PathSet & store,
|
void verifyPath(const Path & path, const PathSet & store,
|
||||||
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
|
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
|
||||||
|
|
||||||
void updatePathInfo(const ValidPathInfo & info);
|
void updatePathInfo(State & state, const ValidPathInfo & info);
|
||||||
|
|
||||||
void upgradeStore6();
|
void upgradeStore6();
|
||||||
void upgradeStore7();
|
void upgradeStore7();
|
||||||
|
@ -299,8 +285,8 @@ private:
|
||||||
void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
|
void optimisePath_(OptimiseStats & stats, const Path & path, InodeHash & inodeHash);
|
||||||
|
|
||||||
// Internal versions that are not wrapped in retry_sqlite.
|
// Internal versions that are not wrapped in retry_sqlite.
|
||||||
bool isValidPath_(const Path & path);
|
bool isValidPath(State & state, const Path & path);
|
||||||
void queryReferrers_(const Path & path, PathSet & referrers);
|
void queryReferrers(State & state, const Path & path, PathSet & referrers);
|
||||||
|
|
||||||
/* Add signatures to a ValidPathInfo using the secret keys
|
/* Add signatures to a ValidPathInfo using the secret keys
|
||||||
specified by the ‘secret-key-files’ option. */
|
specified by the ‘secret-key-files’ option. */
|
||||||
|
|
|
@ -22,11 +22,11 @@ namespace nix {
|
||||||
scope.
|
scope.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<class T>
|
template<class T, class M = std::mutex>
|
||||||
class Sync
|
class Sync
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::mutex mutex;
|
M mutex;
|
||||||
T data;
|
T data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -38,7 +38,7 @@ public:
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
Sync * s;
|
Sync * s;
|
||||||
std::unique_lock<std::mutex> lk;
|
std::unique_lock<M> lk;
|
||||||
friend Sync;
|
friend Sync;
|
||||||
Lock(Sync * s) : s(s), lk(s->mutex) { }
|
Lock(Sync * s) : s(s), lk(s->mutex) { }
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in a new issue