forked from lix-project/lix
Move some code around
In particular, do replacing of valid paths during repair later. This prevents us from replacing a valid path after the build fails.
This commit is contained in:
parent
1da6ae4f99
commit
69fe6c58fa
|
@ -712,9 +712,6 @@ private:
|
||||||
/* Outputs that are corrupt or not valid. */
|
/* Outputs that are corrupt or not valid. */
|
||||||
PathSet missingPaths;
|
PathSet missingPaths;
|
||||||
|
|
||||||
/* Paths that have been subject to hash rewriting. */
|
|
||||||
PathSet rewrittenPaths;
|
|
||||||
|
|
||||||
/* User selected for running the builder. */
|
/* User selected for running the builder. */
|
||||||
UserLock buildUser;
|
UserLock buildUser;
|
||||||
|
|
||||||
|
@ -817,10 +814,9 @@ private:
|
||||||
|
|
||||||
friend int childEntry(void *);
|
friend int childEntry(void *);
|
||||||
|
|
||||||
/* Must be called after the output paths have become valid (either
|
/* Check that the derivation outputs all exist and register them
|
||||||
due to a successful build or hook, or because they already
|
as valid. */
|
||||||
were). */
|
void registerOutputs();
|
||||||
void computeClosure();
|
|
||||||
|
|
||||||
/* Open a log file and a pipe to it. */
|
/* Open a log file and a pipe to it. */
|
||||||
Path openLogFile();
|
Path openLogFile();
|
||||||
|
@ -1385,89 +1381,38 @@ void DerivationGoal::buildDone()
|
||||||
root. */
|
root. */
|
||||||
if (buildUser.enabled()) buildUser.kill();
|
if (buildUser.enabled()) buildUser.kill();
|
||||||
|
|
||||||
/* If the build failed, heuristically check whether this may have
|
|
||||||
been caused by a disk full condition. We have no way of
|
|
||||||
knowing whether the build actually got an ENOSPC. So instead,
|
|
||||||
check if the disk is (nearly) full now. If so, we don't mark
|
|
||||||
this build as a permanent failure. */
|
|
||||||
bool diskFull = false;
|
bool diskFull = false;
|
||||||
#if HAVE_STATVFS
|
|
||||||
if (!statusOk(status)) {
|
|
||||||
unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
|
|
||||||
struct statvfs st;
|
|
||||||
if (statvfs(settings.nixStore.c_str(), &st) == 0 &&
|
|
||||||
(unsigned long long) st.f_bavail * st.f_bsize < required)
|
|
||||||
diskFull = true;
|
|
||||||
if (statvfs(tmpDir.c_str(), &st) == 0 &&
|
|
||||||
(unsigned long long) st.f_bavail * st.f_bsize < required)
|
|
||||||
diskFull = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
/* Some cleanup per path. We do this here and not in
|
|
||||||
computeClosure() for convenience when the build has
|
|
||||||
failed. */
|
|
||||||
foreach (PathSet::iterator, i, missingPaths) {
|
|
||||||
Path path = *i;
|
|
||||||
|
|
||||||
/* If the output was already valid, just skip (discard) it. */
|
|
||||||
if (validPaths.find(path) != validPaths.end()) continue;
|
|
||||||
|
|
||||||
if (useChroot && pathExists(chrootRootDir + path)) {
|
|
||||||
/* Move output paths from the chroot to the Nix store. */
|
|
||||||
if (repair)
|
|
||||||
replaceValidPath(path, chrootRootDir + path);
|
|
||||||
else
|
|
||||||
if (rename((chrootRootDir + path).c_str(), path.c_str()) == -1)
|
|
||||||
throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path redirected;
|
|
||||||
if (repair && (redirected = redirectedBadOutputs[path]) != "" && pathExists(redirected))
|
|
||||||
replaceValidPath(path, redirected);
|
|
||||||
|
|
||||||
if (!pathExists(path)) continue;
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if (lstat(path.c_str(), &st) == -1)
|
|
||||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
|
||||||
|
|
||||||
#ifndef __CYGWIN__
|
|
||||||
/* Check that the output is not group or world writable,
|
|
||||||
as that means that someone else can have interfered
|
|
||||||
with the build. Also, the output should be owned by
|
|
||||||
the build user. */
|
|
||||||
if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
|
|
||||||
(buildUser.enabled() && st.st_uid != buildUser.getUID()))
|
|
||||||
throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Apply hash rewriting if necessary. */
|
|
||||||
if (!rewritesFromTmp.empty()) {
|
|
||||||
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
|
|
||||||
|
|
||||||
/* Canonicalise first. This ensures that the path
|
|
||||||
we're rewriting doesn't contain a hard link to
|
|
||||||
/etc/shadow or something like that. */
|
|
||||||
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
|
|
||||||
|
|
||||||
/* FIXME: this is in-memory. */
|
|
||||||
StringSink sink;
|
|
||||||
dumpPath(path, sink);
|
|
||||||
deletePath(path);
|
|
||||||
sink.s = rewriteHashes(sink.s, rewritesFromTmp);
|
|
||||||
StringSource source(sink.s);
|
|
||||||
restorePath(path, source);
|
|
||||||
|
|
||||||
rewrittenPaths.insert(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the exit status. */
|
/* Check the exit status. */
|
||||||
if (!statusOk(status)) {
|
if (!statusOk(status)) {
|
||||||
|
|
||||||
|
/* Heuristically check whether the build failure may have
|
||||||
|
been caused by a disk full condition. We have no way
|
||||||
|
of knowing whether the build actually got an ENOSPC.
|
||||||
|
So instead, check if the disk is (nearly) full now. If
|
||||||
|
so, we don't mark this build as a permanent failure. */
|
||||||
|
#if HAVE_STATVFS
|
||||||
|
unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
|
||||||
|
struct statvfs st;
|
||||||
|
if (statvfs(settings.nixStore.c_str(), &st) == 0 &&
|
||||||
|
(unsigned long long) st.f_bavail * st.f_bsize < required)
|
||||||
|
diskFull = true;
|
||||||
|
if (statvfs(tmpDir.c_str(), &st) == 0 &&
|
||||||
|
(unsigned long long) st.f_bavail * st.f_bsize < required)
|
||||||
|
diskFull = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
deleteTmpDir(false);
|
deleteTmpDir(false);
|
||||||
|
|
||||||
|
/* Move paths out of the chroot for easier debugging of
|
||||||
|
build failures. */
|
||||||
|
if (useChroot)
|
||||||
|
foreach (PathSet::iterator, i, missingPaths)
|
||||||
|
if (pathExists(chrootRootDir + *i))
|
||||||
|
rename((chrootRootDir + *i).c_str(), i->c_str());
|
||||||
|
|
||||||
if (WIFEXITED(status) && WEXITSTATUS(status) == childSetupFailed)
|
if (WIFEXITED(status) && WEXITSTATUS(status) == childSetupFailed)
|
||||||
throw Error(format("failed to set up the build environment for `%1%'") % drvPath);
|
throw Error(format("failed to set up the build environment for `%1%'") % drvPath);
|
||||||
if (diskFull)
|
if (diskFull)
|
||||||
|
@ -1476,16 +1421,16 @@ void DerivationGoal::buildDone()
|
||||||
% drvPath % statusToString(status));
|
% drvPath % statusToString(status));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Delete the chroot (if we were using one). */
|
|
||||||
autoDelChroot.reset(); /* this runs the destructor */
|
|
||||||
|
|
||||||
/* Delete redirected outputs (when doing hash rewriting). */
|
/* Delete redirected outputs (when doing hash rewriting). */
|
||||||
foreach (PathSet::iterator, i, redirectedOutputs)
|
foreach (PathSet::iterator, i, redirectedOutputs)
|
||||||
deletePath(*i);
|
deletePath(*i);
|
||||||
|
|
||||||
/* Compute the FS closure of the outputs and register them as
|
/* Compute the FS closure of the outputs and register them as
|
||||||
being valid. */
|
being valid. */
|
||||||
computeClosure();
|
registerOutputs();
|
||||||
|
|
||||||
|
/* Delete the chroot (if we were using one). */
|
||||||
|
autoDelChroot.reset(); /* this runs the destructor */
|
||||||
|
|
||||||
deleteTmpDir(true);
|
deleteTmpDir(true);
|
||||||
|
|
||||||
|
@ -2195,7 +2140,7 @@ PathSet parseReferenceSpecifiers(const Derivation & drv, string attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void DerivationGoal::computeClosure()
|
void DerivationGoal::registerOutputs()
|
||||||
{
|
{
|
||||||
map<Path, PathSet> allReferences;
|
map<Path, PathSet> allReferences;
|
||||||
map<Path, HashResult> contentHashes;
|
map<Path, HashResult> contentHashes;
|
||||||
|
@ -2217,15 +2162,60 @@ void DerivationGoal::computeClosure()
|
||||||
Path path = i->second.path;
|
Path path = i->second.path;
|
||||||
if (missingPaths.find(path) == missingPaths.end()) continue;
|
if (missingPaths.find(path) == missingPaths.end()) continue;
|
||||||
|
|
||||||
if (!pathExists(path)) {
|
if (useChroot) {
|
||||||
throw BuildError(
|
if (pathExists(chrootRootDir + path)) {
|
||||||
format("builder for `%1%' failed to produce output path `%2%'")
|
/* Move output paths from the chroot to the Nix store. */
|
||||||
% drvPath % path);
|
if (repair)
|
||||||
|
replaceValidPath(path, chrootRootDir + path);
|
||||||
|
else
|
||||||
|
if (rename((chrootRootDir + path).c_str(), path.c_str()) == -1)
|
||||||
|
throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Path redirected;
|
||||||
|
if (repair && (redirected = redirectedBadOutputs[path]) != "" && pathExists(redirected))
|
||||||
|
replaceValidPath(path, redirected);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (lstat(path.c_str(), &st) == -1)
|
if (lstat(path.c_str(), &st) == -1) {
|
||||||
|
if (errno == ENOENT)
|
||||||
|
throw BuildError(
|
||||||
|
format("builder for `%1%' failed to produce output path `%2%'")
|
||||||
|
% drvPath % path);
|
||||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __CYGWIN__
|
||||||
|
/* Check that the output is not group or world writable, as
|
||||||
|
that means that someone else can have interfered with the
|
||||||
|
build. Also, the output should be owned by the build
|
||||||
|
user. */
|
||||||
|
if ((!S_ISLNK(st.st_mode) && (st.st_mode & (S_IWGRP | S_IWOTH))) ||
|
||||||
|
(buildUser.enabled() && st.st_uid != buildUser.getUID()))
|
||||||
|
throw BuildError(format("suspicious ownership or permission on `%1%'; rejecting this build output") % path);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Apply hash rewriting if necessary. */
|
||||||
|
bool rewritten = false;
|
||||||
|
if (!rewritesFromTmp.empty()) {
|
||||||
|
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
|
||||||
|
|
||||||
|
/* Canonicalise first. This ensures that the path we're
|
||||||
|
rewriting doesn't contain a hard link to /etc/shadow or
|
||||||
|
something like that. */
|
||||||
|
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
|
||||||
|
|
||||||
|
/* FIXME: this is in-memory. */
|
||||||
|
StringSink sink;
|
||||||
|
dumpPath(path, sink);
|
||||||
|
deletePath(path);
|
||||||
|
sink.s = rewriteHashes(sink.s, rewritesFromTmp);
|
||||||
|
StringSource source(sink.s);
|
||||||
|
restorePath(path, source);
|
||||||
|
|
||||||
|
rewritten = true;
|
||||||
|
}
|
||||||
|
|
||||||
startNest(nest, lvlTalkative,
|
startNest(nest, lvlTalkative,
|
||||||
format("scanning for references inside `%1%'") % path);
|
format("scanning for references inside `%1%'") % path);
|
||||||
|
@ -2258,7 +2248,7 @@ void DerivationGoal::computeClosure()
|
||||||
/* Get rid of all weird permissions. This also checks that
|
/* Get rid of all weird permissions. This also checks that
|
||||||
all files are owned by the build user, if applicable. */
|
all files are owned by the build user, if applicable. */
|
||||||
canonicalisePathMetaData(path,
|
canonicalisePathMetaData(path,
|
||||||
buildUser.enabled() && rewrittenPaths.find(path) == rewrittenPaths.end() ? buildUser.getUID() : -1, inodesSeen);
|
buildUser.enabled() && !rewritten ? buildUser.getUID() : -1, inodesSeen);
|
||||||
|
|
||||||
/* For this output path, find the references to other paths
|
/* For this output path, find the references to other paths
|
||||||
contained in it. Compute the SHA-256 NAR hash at the same
|
contained in it. Compute the SHA-256 NAR hash at the same
|
||||||
|
|
Loading…
Reference in a new issue