When repairing a derivation, check and repair the entire output closure

If we find a corrupted path in the output closure, we rebuild the
derivation that produced that particular path.
This commit is contained in:
Eelco Dolstra 2012-10-03 10:38:09 -04:00
parent 2001895f3d
commit a3f205b249
3 changed files with 91 additions and 6 deletions

View File

@ -853,6 +853,7 @@ private:
void init(); void init();
void haveDerivation(); void haveDerivation();
void outputsSubstituted(); void outputsSubstituted();
void closureRepaired();
void inputsRealised(); void inputsRealised();
void tryToBuild(); void tryToBuild();
void buildDone(); void buildDone();
@ -896,6 +897,8 @@ private:
void killChild(); void killChild();
Path addHashRewrite(const Path & path); Path addHashRewrite(const Path & path);
void repairClosure();
}; };
@ -1046,7 +1049,7 @@ void DerivationGoal::outputsSubstituted()
nrFailed = nrNoSubstituters = 0; nrFailed = nrNoSubstituters = 0;
if (checkPathValidity(false, repair).size() == 0) { if (checkPathValidity(false, repair).size() == 0) {
amDone(ecSuccess); if (repair) repairClosure(); else amDone(ecSuccess);
return; return;
} }
@ -1055,7 +1058,7 @@ void DerivationGoal::outputsSubstituted()
/* The inputs must be built before we can build this goal. */ /* The inputs must be built before we can build this goal. */
foreach (DerivationInputs::iterator, i, drv.inputDrvs) foreach (DerivationInputs::iterator, i, drv.inputDrvs)
addWaitee(worker.makeDerivationGoal(i->first)); addWaitee(worker.makeDerivationGoal(i->first, repair));
foreach (PathSet::iterator, i, drv.inputSrcs) foreach (PathSet::iterator, i, drv.inputSrcs)
addWaitee(worker.makeSubstitutionGoal(*i)); addWaitee(worker.makeSubstitutionGoal(*i));
@ -1067,6 +1070,63 @@ void DerivationGoal::outputsSubstituted()
} }
void DerivationGoal::repairClosure()
{
/* If we're repairing, we now know that our own outputs are valid.
Now check whether the other paths in the outputs closure are
good. If not, then start derivation goals for the derivations
that produced those outputs. */
/* Get the output closure. */
PathSet outputClosure;
foreach (DerivationOutputs::iterator, i, drv.outputs)
computeFSClosure(worker.store, i->second.path, outputClosure);
/* Filter out our own outputs (which we have already checked). */
foreach (DerivationOutputs::iterator, i, drv.outputs)
outputClosure.erase(i->second.path);
/* Get all dependencies of this derivation so that we know which
derivation is responsible for which path in the output
closure. */
PathSet inputClosure;
computeFSClosure(worker.store, drvPath, inputClosure);
std::map<Path, Path> outputsToDrv;
foreach (PathSet::iterator, i, inputClosure)
if (isDerivation(*i)) {
Derivation drv = derivationFromPath(worker.store, *i);
foreach (DerivationOutputs::iterator, j, drv.outputs)
outputsToDrv[j->second.path] = *i;
}
/* Check each path (slow!). */
PathSet broken;
foreach (PathSet::iterator, i, outputClosure) {
if (worker.store.pathContentsGood(*i)) continue;
printMsg(lvlError, format("found corrupted or missing path `%1%' in the output closure of `%2%'") % *i % drvPath);
Path drvPath2 = outputsToDrv[*i];
if (drvPath2 == "") throw Error(format("don't know how to repair corrupted or missing path `%1%'") % *i);
addWaitee(worker.makeDerivationGoal(drvPath2, true));
}
if (waitees.empty()) {
amDone(ecSuccess);
return;
}
state = &DerivationGoal::closureRepaired;
}
void DerivationGoal::closureRepaired()
{
trace("closure repaired");
if (nrFailed > 0)
throw Error(format("some paths in the output closure of derivation `%1%' could not be repaired") % drvPath);
amDone(ecSuccess);
}
void DerivationGoal::inputsRealised() void DerivationGoal::inputsRealised()
{ {
trace("all inputs realised"); trace("all inputs realised");
@ -2197,6 +2257,8 @@ void DerivationGoal::computeClosure()
} }
worker.store.optimisePath(path); // FIXME: combine with scanForReferences() worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
worker.store.markContentsGood(path);
} }
/* Register each output path as valid, and register the sets of /* Register each output path as valid, and register the sets of
@ -2729,6 +2791,8 @@ void SubstitutionGoal::finished()
outputLock->setDeletion(true); outputLock->setDeletion(true);
worker.store.markContentsGood(storePath);
printMsg(lvlChatty, printMsg(lvlChatty,
format("substitution of path `%1%' succeeded") % storePath); format("substitution of path `%1%' succeeded") % storePath);

View File

@ -1673,11 +1673,27 @@ void LocalStore::verifyPath(const Path & path, const PathSet & store,
bool LocalStore::pathContentsGood(const Path & path) 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); ValidPathInfo info = queryPathInfo(path);
if (!pathExists(path)) return false; bool res;
HashResult current = hashPath(info.hash.type, path); if (!pathExists(path))
Hash nullHash(htSHA256); res = false;
return info.hash == nullHash || info.hash == current.first; else {
HashResult current = hashPath(info.hash.type, path);
Hash nullHash(htSHA256);
res = info.hash == nullHash || info.hash == 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;
} }

View File

@ -206,6 +206,8 @@ public:
contents. */ contents. */
bool pathContentsGood(const Path & path); bool pathContentsGood(const Path & path);
void markContentsGood(const Path & path);
private: private:
Path schemaPath; Path schemaPath;
@ -233,6 +235,9 @@ private:
SQLiteStmt stmtQueryDerivationOutputs; SQLiteStmt stmtQueryDerivationOutputs;
SQLiteStmt stmtQueryPathFromHashPart; SQLiteStmt stmtQueryPathFromHashPart;
/* Cache for pathContentsGood(). */
std::map<Path, bool> pathContentsGoodCache;
int getSchema(); int getSchema();
void openDB(bool create); void openDB(bool create);