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:
parent
2001895f3d
commit
a3f205b249
3 changed files with 91 additions and 6 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
if (!pathExists(path))
|
||||||
|
res = false;
|
||||||
|
else {
|
||||||
HashResult current = hashPath(info.hash.type, path);
|
HashResult current = hashPath(info.hash.type, path);
|
||||||
Hash nullHash(htSHA256);
|
Hash nullHash(htSHA256);
|
||||||
return info.hash == nullHash || info.hash == current.first;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue