about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2014-02-17T21·25+0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2014-02-17T21·25+0100
commit69fe6c58faa91c3b8f844e1aafca26354ea14425 (patch)
treed2da5408f1900f004ce3fee29770047dfc8f0ad4
parent1da6ae4f9904f7e09166085a2cfed8887e0e86d4 (diff)
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.
-rw-r--r--src/libstore/build.cc174
1 files changed, 82 insertions, 92 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 82731c114027..f37f3ddf2933 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -712,9 +712,6 @@ private:
     /* Outputs that are corrupt or not valid. */
     PathSet missingPaths;
 
-    /* Paths that have been subject to hash rewriting. */
-    PathSet rewrittenPaths;
-
     /* User selected for running the builder. */
     UserLock buildUser;
 
@@ -817,10 +814,9 @@ private:
 
     friend int childEntry(void *);
 
-    /* Must be called after the output paths have become valid (either
-       due to a successful build or hook, or because they already
-       were). */
-    void computeClosure();
+    /* Check that the derivation outputs all exist and register them
+       as valid. */
+    void registerOutputs();
 
     /* Open a log file and a pipe to it. */
     Path openLogFile();
@@ -1385,89 +1381,38 @@ void DerivationGoal::buildDone()
        root. */
     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;
-#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 {
 
-        /* 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);
+        /* Check the exit status. */
+        if (!statusOk(status)) {
 
-#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);
+            /* 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
 
-            /* 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);
+            deleteTmpDir(false);
 
-                rewrittenPaths.insert(path);
-            }
-        }
+            /* 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());
 
-        /* Check the exit status. */
-        if (!statusOk(status)) {
-            deleteTmpDir(false);
             if (WIFEXITED(status) && WEXITSTATUS(status) == childSetupFailed)
                 throw Error(format("failed to set up the build environment for `%1%'") % drvPath);
             if (diskFull)
@@ -1476,16 +1421,16 @@ void DerivationGoal::buildDone()
                 % drvPath % statusToString(status));
         }
 
-        /* Delete the chroot (if we were using one). */
-        autoDelChroot.reset(); /* this runs the destructor */
-
         /* Delete redirected outputs (when doing hash rewriting). */
         foreach (PathSet::iterator, i, redirectedOutputs)
             deletePath(*i);
 
         /* Compute the FS closure of the outputs and register them as
            being valid. */
-        computeClosure();
+        registerOutputs();
+
+        /* Delete the chroot (if we were using one). */
+        autoDelChroot.reset(); /* this runs the destructor */
 
         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, HashResult> contentHashes;
@@ -2217,15 +2162,60 @@ void DerivationGoal::computeClosure()
         Path path = i->second.path;
         if (missingPaths.find(path) == missingPaths.end()) continue;
 
-        if (!pathExists(path)) {
-            throw BuildError(
-                format("builder for `%1%' failed to produce output path `%2%'")
-                % drvPath % path);
+        if (useChroot) {
+            if (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);
+            }
+        } else {
+            Path redirected;
+            if (repair && (redirected = redirectedBadOutputs[path]) != "" && pathExists(redirected))
+                replaceValidPath(path, redirected);
         }
 
         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);
+        }
+
+#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,
             format("scanning for references inside `%1%'") % path);
@@ -2258,7 +2248,7 @@ void DerivationGoal::computeClosure()
         /* Get rid of all weird permissions.  This also checks that
            all files are owned by the build user, if applicable. */
         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
            contained in it.  Compute the SHA-256 NAR hash at the same