summary refs log tree commit diff
path: root/nix
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2015-12-12 11:42:12 +0100
committerLudovic Courtès <ludo@gnu.org>2015-12-12 11:48:46 +0100
commite82e55e58c67b0215e768c4612ca542bc670f633 (patch)
tree856c4512fa1fbde59c1d9845c5a763ef8c4a14b4 /nix
parent98bd851ee891ca4a84e061fe1e78ba78c292b096 (diff)
parente35dff973375266db253747140ddf25084ecddc2 (diff)
downloadguix-e82e55e58c67b0215e768c4612ca542bc670f633.tar.gz
Merge branch 'master' into core-updates
Diffstat (limited to 'nix')
-rw-r--r--nix/libstore/build.cc99
-rw-r--r--nix/libstore/globals.cc7
-rw-r--r--nix/libstore/globals.hh2
-rw-r--r--nix/libstore/optimise-store.cc11
-rw-r--r--nix/libstore/remote-store.cc11
-rw-r--r--nix/libstore/store-api.hh13
-rw-r--r--nix/libstore/worker-protocol.hh2
-rw-r--r--nix/libutil/archive.cc2
-rw-r--r--nix/libutil/hash.cc8
-rw-r--r--nix/libutil/hash.hh5
-rw-r--r--nix/libutil/sha512.h35
-rw-r--r--nix/libutil/util.cc2
-rw-r--r--nix/libutil/util.hh11
-rw-r--r--nix/nix-daemon/nix-daemon.cc31
14 files changed, 184 insertions, 55 deletions
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index a9eedcef16..47c7f9728e 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -51,7 +51,7 @@
 #include <linux/fs.h>
 #endif
 
-#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
+#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
 
 #if CHROOT_ENABLED
 #include <sys/socket.h>
@@ -736,6 +736,9 @@ private:
     /* The temporary directory. */
     Path tmpDir;
 
+    /* The path of the temporary directory in the sandbox. */
+    Path tmpDirInSandbox;
+
     /* File descriptor for the log file. */
     FILE * fLogFile;
     BZFILE * bzLogFile;
@@ -785,10 +788,16 @@ private:
        temporary paths. */
     PathSet redirectedBadOutputs;
 
-    /* Set of inodes seen during calls to canonicalisePathMetaData()
-       for this build's outputs.  This needs to be shared between
-       outputs to allow hard links between outputs. */
-    InodesSeen inodesSeen;
+    /* The current round, if we're building multiple times. */
+    unsigned int curRound = 1;
+
+    unsigned int nrRounds;
+
+    /* Path registration info from the previous round, if we're
+       building multiple times. Since this contains the hash, it
+       allows us to compare whether two rounds produced the same
+       result. */
+    ValidPathInfos prevInfos;
 
 public:
     DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode = bmNormal);
@@ -882,6 +891,10 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const StringSet & wantedOut
     state = &DerivationGoal::init;
     name = (format("building of `%1%'") % drvPath).str();
     trace("created");
+
+    /* Prevent the .chroot directory from being
+       garbage-collected. (See isActiveTempFile() in gc.cc.) */
+    worker.store.addTempRoot(drvPath);
 }
 
 
@@ -1190,8 +1203,12 @@ void DerivationGoal::inputsRealised()
 
     /* Is this a fixed-output derivation? */
     fixedOutput = true;
-    foreach (DerivationOutputs::iterator, i, drv.outputs)
-        if (i->second.hash == "") fixedOutput = false;
+    for (auto & i : drv.outputs)
+	if (i.second.hash == "") fixedOutput = false;
+
+    /* Don't repeat fixed-output derivations since they're already
+       verified by their output hash.*/
+    nrRounds = fixedOutput ? 1 : settings.get("build-repeat", 0) + 1;
 
     /* Okay, try to build.  Note that here we don't wait for a build
        slot to become available, since we don't need one if there is a
@@ -1367,6 +1384,9 @@ void replaceValidPath(const Path & storePath, const Path tmpPath)
 }
 
 
+MakeError(NotDeterministic, BuildError)
+
+
 void DerivationGoal::buildDone()
 {
     trace("build done");
@@ -1466,6 +1486,15 @@ void DerivationGoal::buildDone()
 
         deleteTmpDir(true);
 
+        /* Repeat the build if necessary. */
+        if (curRound++ < nrRounds) {
+            outputLocks.unlock();
+            buildUser.release();
+            state = &DerivationGoal::tryToBuild;
+            worker.wakeUp(shared_from_this());
+            return;
+        }
+
         /* It is now safe to delete the lock files, since all future
            lockers will see that the output paths are valid; they will
            not create new lock files with the same names as the old
@@ -1619,10 +1648,13 @@ int childEntry(void * arg)
 
 void DerivationGoal::startBuilder()
 {
-    startNest(nest, lvlInfo, format(
-            buildMode == bmRepair ? "repairing path(s) %1%" :
-            buildMode == bmCheck ? "checking path(s) %1%" :
-            "building path(s) %1%") % showPaths(missingPaths));
+    auto f = format(
+        buildMode == bmRepair ? "repairing path(s) %1%" :
+        buildMode == bmCheck ? "checking path(s) %1%" :
+        nrRounds > 1 ? "building path(s) %1% (round %2%/%3%)" :
+        "building path(s) %1%");
+    f.exceptions(boost::io::all_error_bits ^ boost::io::too_many_args_bit);
+    startNest(nest, lvlInfo, f % showPaths(missingPaths) % curRound % nrRounds);
 
     /* Right platform? */
     if (!canBuildLocally(drv.platform)) {
@@ -1633,7 +1665,10 @@ void DerivationGoal::startBuilder()
             % drv.platform % settings.thisSystem % drvPath);
     }
 
+    useChroot = settings.useChroot;
+
     /* Construct the environment passed to the builder. */
+    env.clear();
 
     /* Most shells initialise PATH to some default (/bin:/usr/bin:...) when
        PATH is not set.  We don't want this, so we fill it in with some dummy
@@ -1664,20 +1699,25 @@ void DerivationGoal::startBuilder()
 
     /* Create a temporary directory where the build will take
        place. */
-    tmpDir = createTempDir("", "nix-build-" + storePathToName(drvPath), false, false, 0700);
+    auto drvName = storePathToName(drvPath);
+    tmpDir = createTempDir("", "nix-build-" + drvName, false, false, 0700);
+
+    /* In a sandbox, for determinism, always use the same temporary
+       directory. */
+    tmpDirInSandbox = useChroot ? "/tmp/nix-build-" + drvName + "-0" : tmpDir;
 
     /* For convenience, set an environment pointing to the top build
        directory. */
-    env["NIX_BUILD_TOP"] = tmpDir;
+    env["NIX_BUILD_TOP"] = tmpDirInSandbox;
 
     /* Also set TMPDIR and variants to point to this directory. */
-    env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDir;
+    env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmpDirInSandbox;
 
     /* Explicitly set PWD to prevent problems with chroot builds.  In
        particular, dietlibc cannot figure out the cwd because the
        inode of the current directory doesn't appear in .. (because
        getdents returns the inode of the mount point). */
-    env["PWD"] = tmpDir;
+    env["PWD"] = tmpDirInSandbox;
 
     /* Compatibility hack with Nix <= 0.7: if this is a fixed-output
        derivation, tell the builder, so that for instance `fetchurl'
@@ -1762,8 +1802,6 @@ void DerivationGoal::startBuilder()
             throw SysError(format("cannot change ownership of '%1%'") % tmpDir);
     }
 
-    useChroot = settings.useChroot;
-
     if (useChroot) {
 #if CHROOT_ENABLED
         /* Create a temporary directory in which we set up the chroot
@@ -1825,7 +1863,7 @@ void DerivationGoal::startBuilder()
             else
                 dirsInChroot[string(i, 0, p)] = string(i, p + 1);
         }
-        dirsInChroot[tmpDir] = tmpDir;
+        dirsInChroot[tmpDirInSandbox] = tmpDir;
 
         /* Make the closure of the inputs available in the chroot,
            rather than the whole Nix store.  This prevents any access
@@ -1866,13 +1904,13 @@ void DerivationGoal::startBuilder()
             }
         }
 
-        /* If we're repairing or checking, it's possible that we're
+        /* If we're repairing, checking or rebuilding part of a
+           multiple-outputs derivation, it's possible that we're
            rebuilding a path that is in settings.dirsInChroot
            (typically the dependencies of /bin/sh).  Throw them
            out. */
-        if (buildMode != bmNormal)
-            foreach (DerivationOutputs::iterator, i, drv.outputs)
-                dirsInChroot.erase(i->second.path);
+        for (auto & i : drv.outputs)
+            dirsInChroot.erase(i.second.path);
 
 #else
         throw Error("chroot builds are not supported on this platform");
@@ -2143,7 +2181,7 @@ void DerivationGoal::runChild()
         }
 #endif
 
-        if (chdir(tmpDir.c_str()) == -1)
+        if (chdir(tmpDirInSandbox.c_str()) == -1)
             throw SysError(format("changing into `%1%'") % tmpDir);
 
         /* Close all other file descriptors. */
@@ -2263,6 +2301,11 @@ void DerivationGoal::registerOutputs()
 
     ValidPathInfos infos;
 
+    /* Set of inodes seen during calls to canonicalisePathMetaData()
+       for this build's outputs.  This needs to be shared between
+       outputs to allow hard links between outputs. */
+    InodesSeen inodesSeen;
+
     /* Check whether the output paths were created, and grep each
        output path to determine what other paths it references.  Also make all
        output paths read-only. */
@@ -2434,6 +2477,16 @@ void DerivationGoal::registerOutputs()
 
     if (buildMode == bmCheck) return;
 
+    if (curRound > 1 && prevInfos != infos)
+        throw NotDeterministic(
+            format("result of ‘%1%’ differs from previous round; rejecting as non-deterministic")
+            % drvPath);
+
+    if (curRound < nrRounds) {
+        prevInfos = infos;
+        return;
+    }
+
     /* Register each output path as valid, and register the sets of
        paths referenced by each of them.  If there are cycles in the
        outputs, this will fail. */
diff --git a/nix/libstore/globals.cc b/nix/libstore/globals.cc
index 07f23d469c..84fc885eba 100644
--- a/nix/libstore/globals.cc
+++ b/nix/libstore/globals.cc
@@ -137,6 +137,13 @@ bool Settings::get(const string & name, bool def)
     return res;
 }
 
+int Settings::get(const string & name, int def)
+{
+    int res = def;
+    _get(res, name);
+    return res;
+}
+
 
 void Settings::update()
 {
diff --git a/nix/libstore/globals.hh b/nix/libstore/globals.hh
index c17e10d7c3..8c07e360f2 100644
--- a/nix/libstore/globals.hh
+++ b/nix/libstore/globals.hh
@@ -27,6 +27,8 @@ struct Settings {
 
     bool get(const string & name, bool def);
 
+    int get(const string & name, int def);
+
     void update();
 
     string pack();
diff --git a/nix/libstore/optimise-store.cc b/nix/libstore/optimise-store.cc
index c62b8e451b..d7508b025e 100644
--- a/nix/libstore/optimise-store.cc
+++ b/nix/libstore/optimise-store.cc
@@ -120,7 +120,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
         return;
     }
 
-    /* This can still happen on top-level files */
+    /* This can still happen on top-level files. */
     if (st.st_nlink > 1 && inodeHash.count(st.st_ino)) {
         printMsg(lvlDebug, format("`%1%' is already linked, with %2% other file(s).") % path % (st.st_nlink - 2));
         return;
@@ -141,6 +141,7 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
     /* Check if this is a known hash. */
     Path linkPath = linksDir + "/" + printHash32(hash);
 
+ retry:
     if (!pathExists(linkPath)) {
         /* Nope, create a hard link in the links directory. */
         if (link(path.c_str(), linkPath.c_str()) == 0) {
@@ -164,7 +165,13 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path, InodeHa
         return;
     }
 
-    printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath);
+    if (st.st_size != stLink.st_size) {
+        printMsg(lvlError, format("removing corrupted link ‘%1%’") % linkPath);
+        unlink(linkPath.c_str());
+        goto retry;
+    }
+
+    printMsg(lvlTalkative, format("linking ‘%1%’ to ‘%2%’") % path % linkPath);
 
     /* Make the containing directory writable, but only if it's not
        the store itself (we don't want or need to mess with its
diff --git a/nix/libstore/remote-store.cc b/nix/libstore/remote-store.cc
index 0539bbe127..324ef5eb30 100644
--- a/nix/libstore/remote-store.cc
+++ b/nix/libstore/remote-store.cc
@@ -462,11 +462,18 @@ Paths RemoteStore::importPaths(bool requireSignature, Source & source)
 
 void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
 {
-    if (buildMode != bmNormal) throw Error("repairing or checking is not supported when building through the Nix daemon");
     openConnection();
     writeInt(wopBuildPaths, to);
-    if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
+    if (GET_PROTOCOL_MINOR(daemonVersion) >= 13) {
         writeStrings(drvPaths, to);
+        if (GET_PROTOCOL_MINOR(daemonVersion) >= 15) {
+            writeInt(buildMode, to);
+        }
+        /* Old daemons did not take a 'buildMode' parameter, so we need to
+           validate it here on the client side.  */
+        else if (buildMode != bmNormal) throw Error("repairing or checking \
+is not supported when building through the Nix daemon");
+    }
     else {
         /* For backwards compatibility with old daemons, strip output
            identifiers. */
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index 3764f3e542..9403cbee19 100644
--- a/nix/libstore/store-api.hh
+++ b/nix/libstore/store-api.hh
@@ -88,10 +88,17 @@ struct ValidPathInfo
     Path deriver;
     Hash hash;
     PathSet references;
-    time_t registrationTime;
-    unsigned long long narSize; // 0 = unknown
+    time_t registrationTime = 0;
+    unsigned long long narSize = 0; // 0 = unknown
     unsigned long long id; // internal use only
-    ValidPathInfo() : registrationTime(0), narSize(0) { }
+
+    bool operator == (const ValidPathInfo & i) const
+    {
+        return
+            path == i.path
+            && hash == i.hash
+            && references == i.references;
+    }
 };
 
 typedef list<ValidPathInfo> ValidPathInfos;
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index d037d7402e..7b7be4a8a0 100644
--- a/nix/libstore/worker-protocol.hh
+++ b/nix/libstore/worker-protocol.hh
@@ -6,7 +6,7 @@ namespace nix {
 #define WORKER_MAGIC_1 0x6e697863
 #define WORKER_MAGIC_2 0x6478696f
 
-#define PROTOCOL_VERSION 0x10e
+#define PROTOCOL_VERSION 0x10f
 #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
 #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
 
diff --git a/nix/libutil/archive.cc b/nix/libutil/archive.cc
index 6856ea0f28..4e3d99356a 100644
--- a/nix/libutil/archive.cc
+++ b/nix/libutil/archive.cc
@@ -256,7 +256,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
                         if (i != names.end()) {
                             printMsg(lvlDebug, format("case collision between `%1%' and `%2%'") % i->first % name);
                             name += caseHackSuffix;
-                            name += int2String(++i->second);
+                            name += std::to_string(++i->second);
                         } else
                             names[name] = 0;
                     }
diff --git a/nix/libutil/hash.cc b/nix/libutil/hash.cc
index 2da00a53de..ea69aa64f9 100644
--- a/nix/libutil/hash.cc
+++ b/nix/libutil/hash.cc
@@ -11,6 +11,7 @@ extern "C" {
 #include "md5.h"
 #include "sha1.h"
 #include "sha256.h"
+#include "sha512.h"
 }
 #endif
 
@@ -40,6 +41,7 @@ Hash::Hash(HashType type)
     if (type == htMD5) hashSize = md5HashSize;
     else if (type == htSHA1) hashSize = sha1HashSize;
     else if (type == htSHA256) hashSize = sha256HashSize;
+    else if (type == htSHA512) hashSize = sha512HashSize;
     else throw Error("unknown hash type");
     assert(hashSize <= maxHashSize);
     memset(hash, 0, maxHashSize);
@@ -199,6 +201,7 @@ struct Ctx
     MD5_CTX md5;
     SHA_CTX sha1;
     SHA256_CTX sha256;
+    SHA512_CTX sha512;
 };
 
 
@@ -207,6 +210,7 @@ static void start(HashType ht, Ctx & ctx)
     if (ht == htMD5) MD5_Init(&ctx.md5);
     else if (ht == htSHA1) SHA1_Init(&ctx.sha1);
     else if (ht == htSHA256) SHA256_Init(&ctx.sha256);
+    else if (ht == htSHA512) SHA512_Init(&ctx.sha512);
 }
 
 
@@ -216,6 +220,7 @@ static void update(HashType ht, Ctx & ctx,
     if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len);
     else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len);
     else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len);
+    else if (ht == htSHA512) SHA512_Update(&ctx.sha512, bytes, len);
 }
 
 
@@ -224,6 +229,7 @@ static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
     if (ht == htMD5) MD5_Final(hash, &ctx.md5);
     else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1);
     else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256);
+    else if (ht == htSHA512) SHA512_Final(hash, &ctx.sha512);
 }
 
 
@@ -321,6 +327,7 @@ HashType parseHashType(const string & s)
     if (s == "md5") return htMD5;
     else if (s == "sha1") return htSHA1;
     else if (s == "sha256") return htSHA256;
+    else if (s == "sha512") return htSHA512;
     else return htUnknown;
 }
 
@@ -330,6 +337,7 @@ string printHashType(HashType ht)
     if (ht == htMD5) return "md5";
     else if (ht == htSHA1) return "sha1";
     else if (ht == htSHA256) return "sha256";
+    else if (ht == htSHA512) return "sha512";
     else throw Error("cannot print unknown hash type");
 }
 
diff --git a/nix/libutil/hash.hh b/nix/libutil/hash.hh
index 8f099c4f07..6b5e47cd8a 100644
--- a/nix/libutil/hash.hh
+++ b/nix/libutil/hash.hh
@@ -7,19 +7,20 @@
 namespace nix {
 
 
-typedef enum { htUnknown, htMD5, htSHA1, htSHA256 } HashType;
+typedef enum { htUnknown, htMD5, htSHA1, htSHA256, htSHA512 } HashType;
 
 
 const int md5HashSize = 16;
 const int sha1HashSize = 20;
 const int sha256HashSize = 32;
+const int sha512HashSize = 64;
 
 extern const string base32Chars;
 
 
 struct Hash
 {
-    static const unsigned int maxHashSize = 32;
+    static const unsigned int maxHashSize = 64;
     unsigned int hashSize;
     unsigned char hash[maxHashSize];
 
diff --git a/nix/libutil/sha512.h b/nix/libutil/sha512.h
new file mode 100644
index 0000000000..d2abab4c5f
--- /dev/null
+++ b/nix/libutil/sha512.h
@@ -0,0 +1,35 @@
+/* GNU Guix --- Functional package management for GNU
+   Copyright (C) 2012, 2015 Ludovic Courtès <ludo@gnu.org>
+
+   This file is part of GNU Guix.
+
+   GNU Guix is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or (at
+   your option) any later version.
+
+   GNU Guix is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <gcrypt-hash.hh>
+
+#define SHA512_CTX guix_hash_context
+
+static inline void
+SHA512_Init (struct SHA512_CTX *ctx)
+{
+  guix_hash_init (ctx, GCRY_MD_SHA512);
+}
+
+#define SHA512_Update guix_hash_update
+
+static inline void
+SHA512_Final (void *resbuf, struct SHA512_CTX *ctx)
+{
+  guix_hash_final (resbuf, ctx, GCRY_MD_SHA512);
+}
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 14026ab829..c07754487e 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -433,7 +433,7 @@ Nest::~Nest()
 
 static string escVerbosity(Verbosity level)
 {
-    return int2String((int) level);
+    return std::to_string((int) level);
 }
 
 
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index 24e16ba36a..e84d64d10a 100644
--- a/nix/libutil/util.hh
+++ b/nix/libutil/util.hh
@@ -153,8 +153,8 @@ void printMsg_(Verbosity level, const FormatOrString & fs);
 
 #define printMsg(level, f) \
     do { \
-        if (level <= verbosity) { \
-            printMsg_(level, (f)); \
+        if (level <= nix::verbosity) { \
+            nix::printMsg_(level, (f)); \
         } \
     } while (0)
 
@@ -337,13 +337,6 @@ template<class N> bool string2Int(const string & s, N & n)
     return str && str.get() == EOF;
 }
 
-template<class N> string int2String(N n)
-{
-    std::ostringstream str;
-    str << n;
-    return str.str();
-}
-
 
 /* Return true iff `s' ends in `suffix'. */
 bool hasSuffix(const string & s, const string & suffix);
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index 10159db62e..35c284f7e1 100644
--- a/nix/nix-daemon/nix-daemon.cc
+++ b/nix/nix-daemon/nix-daemon.cc
@@ -441,8 +441,8 @@ static void performOp(bool trusted, unsigned int clientVersion,
         startWork();
         TunnelSource source(from);
 
-	/* Unlike Nix, always require a signature, even for "trusted"
-	   users.  */
+        /* Unlike Nix, always require a signature, even for "trusted"
+           users.  */
         Paths paths = store->importPaths(true, source);
         stopWork();
         writeStrings(paths, to);
@@ -451,8 +451,17 @@ static void performOp(bool trusted, unsigned int clientVersion,
 
     case wopBuildPaths: {
         PathSet drvs = readStorePaths<PathSet>(from);
+        BuildMode mode = bmNormal;
+        if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
+            mode = (BuildMode)readInt(from);
+
+	    /* Repairing is not atomic, so disallowed for "untrusted"
+	       clients.  */
+            if (mode == bmRepair && !trusted)
+                throw Error("repairing is not supported when building through the Nix daemon");
+        }
         startWork();
-        store->buildPaths(drvs);
+        store->buildPaths(drvs, mode);
         stopWork();
         writeInt(1, to);
         break;
@@ -538,8 +547,8 @@ static void performOp(bool trusted, unsigned int clientVersion,
         settings.keepGoing = readInt(from) != 0;
         settings.set("build-fallback", readInt(from) ? "true" : "false");
         verbosity = (Verbosity) readInt(from);
-        settings.set("build-max-jobs", int2String(readInt(from)));
-        settings.set("build-max-silent-time", int2String(readInt(from)));
+        settings.set("build-max-jobs", std::to_string(readInt(from)));
+        settings.set("build-max-silent-time", std::to_string(readInt(from)));
         if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
             settings.useBuildHook = readInt(from) != 0;
         if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
@@ -548,7 +557,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
             settings.printBuildTrace = readInt(from) != 0;
         }
         if (GET_PROTOCOL_MINOR(clientVersion) >= 6)
-            settings.set("build-cores", int2String(readInt(from)));
+            settings.set("build-cores", std::to_string(readInt(from)));
         if (GET_PROTOCOL_MINOR(clientVersion) >= 10)
             settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
         if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
@@ -556,7 +565,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
             for (unsigned int i = 0; i < n; i++) {
                 string name = readString(from);
                 string value = readString(from);
-                if (name == "build-timeout" || name == "use-ssh-substituter")
+                if (name == "build-timeout" || name == "build-repeat" || name == "use-ssh-substituter")
                     settings.set(name, value);
                 else
                     settings.set(trusted ? name : "untrusted-" + name, value);
@@ -819,7 +828,7 @@ static void daemonLoop()
 
     /* Handle socket-based activation by systemd. */
     if (getEnv("LISTEN_FDS") != "") {
-        if (getEnv("LISTEN_PID") != int2String(getpid()) || getEnv("LISTEN_FDS") != "1")
+        if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1")
             throw Error("unexpected systemd environment variables");
         fdSocket = SD_LISTEN_FDS_START;
     }
@@ -906,10 +915,10 @@ static void daemonLoop()
             clientPid = cred.pid;
 
             struct passwd * pw = getpwuid(cred.uid);
-            string user = pw ? pw->pw_name : int2String(cred.uid);
+            string user = pw ? pw->pw_name : std::to_string(cred.uid);
 
             struct group * gr = getgrgid(cred.gid);
-            string group = gr ? gr->gr_name : int2String(cred.gid);
+            string group = gr ? gr->gr_name : std::to_string(cred.gid);
 
             Strings trustedUsers = settings.get("trusted-users", Strings({"root"}));
             Strings allowedUsers = settings.get("allowed-users", Strings({"*"}));
@@ -937,7 +946,7 @@ static void daemonLoop()
 
                 /* For debugging, stuff the pid into argv[1]. */
                 if (clientPid != -1 && argvSaved[1]) {
-                    string processName = int2String(clientPid);
+                    string processName = std::to_string(clientPid);
                     strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
                 }