summary refs log tree commit diff
path: root/nix/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'nix/libstore')
-rw-r--r--nix/libstore/build.cc171
-rw-r--r--nix/libstore/gc.cc6
-rw-r--r--nix/libstore/local-store.cc31
-rw-r--r--nix/libstore/local-store.hh2
-rw-r--r--nix/libstore/optimise-store.cc1
-rw-r--r--nix/libstore/pathlocks.cc2
-rw-r--r--nix/libstore/remote-store.cc17
-rw-r--r--nix/libstore/remote-store.hh29
-rw-r--r--nix/libstore/store-api.hh24
-rw-r--r--nix/libstore/worker-protocol.hh3
10 files changed, 154 insertions, 132 deletions
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 009fcb2c0c..85a818ba94 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -38,6 +38,9 @@
 #if HAVE_SYS_MOUNT_H
 #include <sys/mount.h>
 #endif
+#if HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
 #if HAVE_SCHED_H
 #include <sched.h>
 #endif
@@ -48,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)
+#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
 
 #if CHROOT_ENABLED
 #include <sys/socket.h>
@@ -414,19 +417,6 @@ static void commonChildInit(Pipe & logPipe)
     close(fdDevNull);
 }
 
-
-/* Convert a string list to an array of char pointers.  Careful: the
-   string list should outlive the array. */
-const char * * strings2CharPtrs(const Strings & ss)
-{
-    const char * * arr = new const char * [ss.size() + 1];
-    const char * * p = arr;
-    foreach (Strings::const_iterator, i, ss) *p++ = i->c_str();
-    *p = 0;
-    return arr;
-}
-
-
 /* Restore default handling of SIGPIPE, otherwise some programs will
    randomly say "Broken pipe". */
 static void restoreSIGPIPE()
@@ -764,7 +754,7 @@ private:
     typedef void (DerivationGoal::*GoalState)();
     GoalState state;
 
-    /* Stuff we need to pass to initChild(). */
+    /* Stuff we need to pass to runChild(). */
     typedef map<Path, Path> DirsInChroot; // maps target path to source path
     DirsInChroot dirsInChroot;
     typedef map<string, string> Environment;
@@ -828,8 +818,8 @@ private:
     /* Start building a derivation. */
     void startBuilder();
 
-    /* Initialise the builder's process. */
-    void initChild();
+    /* Run the builder's process. */
+    void runChild();
 
     friend int childEntry(void *);
 
@@ -1612,7 +1602,7 @@ void chmod_(const Path & path, mode_t mode)
 
 int childEntry(void * arg)
 {
-    ((DerivationGoal *) arg)->initChild();
+    ((DerivationGoal *) arg)->runChild();
     return 1;
 }
 
@@ -1759,37 +1749,11 @@ void DerivationGoal::startBuilder()
 
         /* Change ownership of the temporary build directory. */
         if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
-            throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
+            throw SysError(format("cannot change ownership of '%1%'") % tmpDir);
+    }
 
-        /* Check that the Nix store has the appropriate permissions,
-           i.e., owned by root and mode 1775 (sticky bit on so that
-           the builder can create its output but not mess with the
-           outputs of other processes). */
-        struct stat st;
-        if (stat(settings.nixStore.c_str(), &st) == -1)
-            throw SysError(format("cannot stat `%1%'") % settings.nixStore);
-        if (!(st.st_mode & S_ISVTX) ||
-            ((st.st_mode & S_IRWXG) != S_IRWXG) ||
-            (st.st_gid != buildUser.getGID()))
-            throw Error(format(
-                "builder does not have write permission to `%2%'; "
-                "try `chgrp %1% %2%; chmod 1775 %2%'")
-                % buildUser.getGID() % settings.nixStore);
-    }
-
-
-    /* Are we doing a chroot build?  Note that fixed-output
-       derivations are never done in a chroot, mainly so that
-       functions like fetchurl (which needs a proper /etc/resolv.conf)
-       work properly.  Purity checking for fixed-output derivations
-       is somewhat pointless anyway. */
     useChroot = settings.useChroot;
 
-    if (fixedOutput) useChroot = false;
-
-    /* Hack to allow derivations to disable chroot builds. */
-    if (get(drv.env, "__noChroot") == "1") useChroot = false;
-
     if (useChroot) {
 #if CHROOT_ENABLED
         /* Create a temporary directory in which we set up the chroot
@@ -1804,6 +1768,12 @@ void DerivationGoal::startBuilder()
 
         printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir);
 
+        if (mkdir(chrootRootDir.c_str(), 0750) == -1)
+            throw SysError(format("cannot create ‘%1%’") % chrootRootDir);
+
+        if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
+            throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
+
         /* Create a writable /tmp in the chroot.  Many builders need
            this.  (Of course they should really respect $TMPDIR
            instead.) */
@@ -1830,7 +1800,8 @@ void DerivationGoal::startBuilder()
                 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
 
         /* Create /etc/hosts with localhost entry. */
-        writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
+        if (!fixedOutput)
+            writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
 
         /* Bind-mount a user-configurable set of directories from the
            host file system. */
@@ -1853,8 +1824,12 @@ void DerivationGoal::startBuilder()
            can be bind-mounted).  !!! As an extra security
            precaution, make the fake Nix store only writable by the
            build user. */
-        createDirs(chrootRootDir + settings.nixStore);
-        chmod_(chrootRootDir + settings.nixStore, 01777);
+        Path chrootStoreDir = chrootRootDir + settings.nixStore;
+        createDirs(chrootStoreDir);
+        chmod_(chrootStoreDir, 01775);
+
+        if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
+            throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
 
         foreach (PathSet::iterator, i, inputPaths) {
             struct stat st;
@@ -1963,14 +1938,17 @@ void DerivationGoal::startBuilder()
     */
 #if CHROOT_ENABLED
     if (useChroot) {
-        char stack[32 * 1024];
-        pid = clone(childEntry, stack + sizeof(stack) - 8,
-            CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD, this);
+	char stack[32 * 1024];
+	int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD;
+	if (!fixedOutput) flags |= CLONE_NEWNET;
+	pid = clone(childEntry, stack + sizeof(stack) - 8, flags, this);
+	if (pid == -1)
+	    throw SysError("cloning builder process");
     } else
 #endif
     {
         pid = fork();
-        if (pid == 0) initChild();
+        if (pid == 0) runChild();
     }
 
     if (pid == -1) throw SysError("unable to fork");
@@ -1993,7 +1971,7 @@ void DerivationGoal::startBuilder()
 }
 
 
-void DerivationGoal::initChild()
+void DerivationGoal::runChild()
 {
     /* Warning: in the child we should absolutely not make any SQLite
        calls! */
@@ -2022,9 +2000,11 @@ void DerivationGoal::initChild()
 
             /* Set the hostname etc. to fixed values. */
             char hostname[] = "localhost";
-            sethostname(hostname, sizeof(hostname));
+            if (sethostname(hostname, sizeof(hostname)) == -1)
+                throw SysError("cannot set host name");
             char domainname[] = "(none)"; // kernel default
-            setdomainname(domainname, sizeof(domainname));
+            if (setdomainname(domainname, sizeof(domainname)) == -1)
+                throw SysError("cannot set domain name");
 
             /* Make all filesystems private.  This is necessary
                because subtrees may have been mounted as "shared"
@@ -2042,12 +2022,17 @@ void DerivationGoal::initChild()
                     throw SysError(format("unable to make filesystem `%1%' private") % fs);
             }
 
+            /* Bind-mount chroot directory to itself, to treat it as a
+               different filesystem from /, as needed for pivot_root. */
+            if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
+                throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir);
+
             /* Set up a nearly empty /dev, unless the user asked to
                bind-mount the host /dev. */
+            Strings ss;
             if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
                 createDirs(chrootRootDir + "/dev/shm");
                 createDirs(chrootRootDir + "/dev/pts");
-                Strings ss;
                 ss.push_back("/dev/full");
 #ifdef __linux__
                 if (pathExists("/dev/kvm"))
@@ -2058,13 +2043,24 @@ void DerivationGoal::initChild()
                 ss.push_back("/dev/tty");
                 ss.push_back("/dev/urandom");
                 ss.push_back("/dev/zero");
-                foreach (Strings::iterator, i, ss) dirsInChroot[*i] = *i;
                 createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
                 createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
                 createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
                 createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
             }
 
+            /* Fixed-output derivations typically need to access the
+               network, so give them access to /etc/resolv.conf and so
+               on. */
+            if (fixedOutput) {
+                ss.push_back("/etc/resolv.conf");
+                ss.push_back("/etc/nsswitch.conf");
+                ss.push_back("/etc/services");
+                ss.push_back("/etc/hosts");
+            }
+
+            for (auto & i : ss) dirsInChroot[i] = i;
+
             /* Bind-mount all the directories from the "host"
                filesystem that we want in the chroot
                environment. */
@@ -2114,13 +2110,26 @@ void DerivationGoal::initChild()
                 chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
             }
 
-            /* Do the chroot().  Below we do a chdir() to the
-               temporary build directory to make sure the current
-               directory is in the chroot.  (Actually the order
-               doesn't matter, since due to the bind mount tmpDir and
-               tmpRootDit/tmpDir are the same directories.) */
-            if (chroot(chrootRootDir.c_str()) == -1)
-                throw SysError(format("cannot change root directory to `%1%'") % chrootRootDir);
+            /* Do the chroot(). */
+            if (chdir(chrootRootDir.c_str()) == -1)
+                throw SysError(format("cannot change directory to '%1%'") % chrootRootDir);
+
+            if (mkdir("real-root", 0) == -1)
+                throw SysError("cannot create real-root directory");
+
+#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
+            if (pivot_root(".", "real-root") == -1)
+                throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir + "/real-root"));
+#undef pivot_root
+
+            if (chroot(".") == -1)
+                throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir);
+
+            if (umount2("real-root", MNT_DETACH) == -1)
+                throw SysError("cannot unmount real root filesystem");
+
+            if (rmdir("real-root") == -1)
+                throw SysError("cannot remove real-root directory");
         }
 #endif
 
@@ -2159,11 +2168,7 @@ void DerivationGoal::initChild()
         Strings envStrs;
         foreach (Environment::const_iterator, i, env)
             envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp));
-        const char * * envArr = strings2CharPtrs(envStrs);
-
-        Path program = drv.builder.c_str();
-        std::vector<const char *> args; /* careful with c_str()! */
-        string user; /* must be here for its c_str()! */
+        auto envArr = stringsToCharPtrs(envStrs);
 
         /* If we are running in `build-users' mode, then switch to the
            user we allocated above.  Make sure that we drop all root
@@ -2189,29 +2194,25 @@ void DerivationGoal::initChild()
         }
 
         /* Fill in the arguments. */
+        Strings args;
         string builderBasename = baseNameOf(drv.builder);
-        args.push_back(builderBasename.c_str());
-        foreach (Strings::iterator, i, drv.args) {
-            auto re = rewriteHashes(*i, rewritesToTmp);
-            auto cstr = new char[re.length()+1];
-            std::strcpy(cstr, re.c_str());
-
-            args.push_back(cstr);
-        }
-        args.push_back(0);
+        args.push_back(builderBasename);
+        foreach (Strings::iterator, i, drv.args)
+            args.push_back(rewriteHashes(*i, rewritesToTmp));
+        auto argArr = stringsToCharPtrs(args);
 
         restoreSIGPIPE();
 
         /* Indicate that we managed to set up the build environment. */
-        writeToStderr("\n");
+        writeFull(STDERR_FILENO, "\n");
 
         /* Execute the program.  This should not return. */
-        execve(program.c_str(), (char * *) &args[0], (char * *) envArr);
+        execve(drv.builder.c_str(), (char * *) &argArr[0], (char * *) &envArr[0]);
 
         throw SysError(format("executing `%1%'") % drv.builder);
 
     } catch (std::exception & e) {
-        writeToStderr("while setting up the build environment: " + string(e.what()) + "\n");
+        writeFull(STDERR_FILENO, "while setting up the build environment: " + string(e.what()) + "\n");
         _exit(1);
     }
 
@@ -2526,7 +2527,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
             BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size());
             if (err != BZ_OK) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err);
         } else if (fdLogFile != -1)
-            writeFull(fdLogFile, (unsigned char *) data.data(), data.size());
+            writeFull(fdLogFile, data);
     }
 
     if (hook && fd == hook->fromHook.readSide)
@@ -2836,7 +2837,7 @@ void SubstitutionGoal::tryToRun()
     args.push_back("--substitute");
     args.push_back(storePath);
     args.push_back(destPath);
-    const char * * argArr = strings2CharPtrs(args);
+    auto argArr = stringsToCharPtrs(args);
 
     /* Fork the substitute program. */
     pid = startProcess([&]() {
@@ -2846,7 +2847,7 @@ void SubstitutionGoal::tryToRun()
         if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
             throw SysError("cannot dup output pipe into stdout");
 
-        execv(sub.c_str(), (char * *) argArr);
+        execv(sub.c_str(), (char * *) &argArr[0]);
 
         throw SysError(format("executing `%1%'") % sub);
     });
diff --git a/nix/libstore/gc.cc b/nix/libstore/gc.cc
index f98e02c1e2..34768324c2 100644
--- a/nix/libstore/gc.cc
+++ b/nix/libstore/gc.cc
@@ -96,7 +96,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
                 "(are you running nix-build inside the store?)") % gcRoot);
 
     if (indirect) {
-        /* Don't clobber the the link if it already exists and doesn't
+        /* Don't clobber the link if it already exists and doesn't
            point to the Nix store. */
         if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
             throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
@@ -191,7 +191,7 @@ void LocalStore::addTempRoot(const Path & path)
     lockFile(fdTempRoots, ltWrite, true);
 
     string s = path + '\0';
-    writeFull(fdTempRoots, (const unsigned char *) s.data(), s.size());
+    writeFull(fdTempRoots, s);
 
     /* Downgrade to a read lock. */
     debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
@@ -231,7 +231,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
         if (lockFile(*fd, ltWrite, false)) {
             printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
             unlink(path.c_str());
-            writeFull(*fd, (const unsigned char *) "d", 1);
+            writeFull(*fd, "d");
             continue;
         }
 
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index a115f65847..630cb80c41 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -254,22 +254,25 @@ LocalStore::LocalStore(bool reserveSpace)
         Path perUserDir = profilesDir + "/per-user";
         createDirs(perUserDir);
         if (chmod(perUserDir.c_str(), 01777) == -1)
-            throw SysError(format("could not set permissions on `%1%' to 1777") % perUserDir);
+            throw SysError(format("could not set permissions on '%1%' to 1777") % perUserDir);
+
+        mode_t perm = 01775;
 
         struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
         if (!gr)
             throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
                 % settings.buildUsersGroup);
-
-        struct stat st;
-        if (stat(settings.nixStore.c_str(), &st))
-            throw SysError(format("getting attributes of path `%1%'") % settings.nixStore);
-
-        if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != 01775) {
-            if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
-                throw SysError(format("changing ownership of path `%1%'") % settings.nixStore);
-            if (chmod(settings.nixStore.c_str(), 01775) == -1)
-                throw SysError(format("changing permissions on path `%1%'") % settings.nixStore);
+        else {
+            struct stat st;
+            if (stat(settings.nixStore.c_str(), &st))
+                throw SysError(format("getting attributes of path '%1%'") % settings.nixStore);
+
+            if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
+                if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
+                    throw SysError(format("changing ownership of path '%1%'") % settings.nixStore);
+                if (chmod(settings.nixStore.c_str(), perm) == -1)
+                    throw SysError(format("changing permissions on path '%1%'") % settings.nixStore);
+            }
         }
     }
 
@@ -499,7 +502,7 @@ void LocalStore::makeStoreWritable()
         if (unshare(CLONE_NEWNS) == -1)
             throw SysError("setting up a private mount namespace");
 
-        if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1)
+        if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
             throw SysError(format("remounting %1% writable") % settings.nixStore);
     }
 #endif
@@ -1404,7 +1407,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
 }
 
 
-Path LocalStore::addToStore(const Path & _srcPath,
+Path LocalStore::addToStore(const string & name, const Path & _srcPath,
     bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
 {
     Path srcPath(absPath(_srcPath));
@@ -1419,7 +1422,7 @@ Path LocalStore::addToStore(const Path & _srcPath,
     else
         sink.s = readFile(srcPath);
 
-    return addToStoreFromDump(sink.s, baseNameOf(srcPath), recursive, hashAlgo, repair);
+    return addToStoreFromDump(sink.s, name, recursive, hashAlgo, repair);
 }
 
 
diff --git a/nix/libstore/local-store.hh b/nix/libstore/local-store.hh
index e0aabdba42..819f59327a 100644
--- a/nix/libstore/local-store.hh
+++ b/nix/libstore/local-store.hh
@@ -130,7 +130,7 @@ public:
     void querySubstitutablePathInfos(const PathSet & paths,
         SubstitutablePathInfos & infos);
 
-    Path addToStore(const Path & srcPath,
+    Path addToStore(const string & name, const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
         PathFilter & filter = defaultPathFilter, bool repair = false);
 
diff --git a/nix/libstore/optimise-store.cc b/nix/libstore/optimise-store.cc
index 8ba9d1a263..c62b8e451b 100644
--- a/nix/libstore/optimise-store.cc
+++ b/nix/libstore/optimise-store.cc
@@ -4,6 +4,7 @@
 #include "local-store.hh"
 #include "globals.hh"
 
+#include <cstdlib>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
diff --git a/nix/libstore/pathlocks.cc b/nix/libstore/pathlocks.cc
index b858ed238d..830858ff8d 100644
--- a/nix/libstore/pathlocks.cc
+++ b/nix/libstore/pathlocks.cc
@@ -33,7 +33,7 @@ void deleteLockFile(const Path & path, int fd)
        other processes waiting on this lock that the lock is stale
        (deleted). */
     unlink(path.c_str());
-    writeFull(fd, (const unsigned char *) "d", 1);
+    writeFull(fd, "d");
     /* Note that the result of unlink() is ignored; removing the lock
        file is an optimisation, not a necessity. */
 }
diff --git a/nix/libstore/remote-store.cc b/nix/libstore/remote-store.cc
index 448d9b6bc1..0539bbe127 100644
--- a/nix/libstore/remote-store.cc
+++ b/nix/libstore/remote-store.cc
@@ -10,6 +10,7 @@
 #include <sys/stat.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <errno.h>
 #include <fcntl.h>
 
 #include <iostream>
@@ -109,7 +110,7 @@ void RemoteStore::connectToDaemon()
        applications... */
     AutoCloseFD fdPrevDir = open(".", O_RDONLY);
     if (fdPrevDir == -1) throw SysError("couldn't open current directory");
-    chdir(dirOf(socketPath).c_str());
+    if (chdir(dirOf(socketPath).c_str()) == -1) throw SysError(format("couldn't change to directory of ‘%1%’") % socketPath);
     Path socketPathRel = "./" + baseNameOf(socketPath);
 
     struct sockaddr_un addr;
@@ -384,7 +385,7 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart)
 }
 
 
-Path RemoteStore::addToStore(const Path & _srcPath,
+Path RemoteStore::addToStore(const string & name, const Path & _srcPath,
     bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
 {
     if (repair) throw Error("repairing is not supported when building through the Nix daemon");
@@ -394,7 +395,7 @@ Path RemoteStore::addToStore(const Path & _srcPath,
     Path srcPath(absPath(_srcPath));
 
     writeInt(wopAddToStore, to);
-    writeString(baseNameOf(srcPath), to);
+    writeString(name, to);
     /* backwards compatibility hack */
     writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
     writeInt(recursive ? 1 : 0, to);
@@ -584,6 +585,16 @@ void RemoteStore::optimiseStore()
     readInt(from);
 }
 
+bool RemoteStore::verifyStore(bool checkContents, bool repair)
+{
+    openConnection();
+    writeInt(wopVerifyStore, to);
+    writeInt(checkContents, to);
+    writeInt(repair, to);
+    processStderr();
+    return readInt(from) != 0;
+}
+
 void RemoteStore::processStderr(Sink * sink, Source * source)
 {
     to.flush();
diff --git a/nix/libstore/remote-store.hh b/nix/libstore/remote-store.hh
index 98774c10b3..030120db40 100644
--- a/nix/libstore/remote-store.hh
+++ b/nix/libstore/remote-store.hh
@@ -21,15 +21,15 @@ public:
     RemoteStore();
 
     ~RemoteStore();
-    
+
     /* Implementations of abstract store API methods. */
-    
+
     bool isValidPath(const Path & path);
 
     PathSet queryValidPaths(const PathSet & paths);
-    
+
     PathSet queryAllValidPaths();
-    
+
     ValidPathInfo queryPathInfo(const Path & path);
 
     Hash queryPathHash(const Path & path);
@@ -39,21 +39,21 @@ public:
     void queryReferrers(const Path & path, PathSet & referrers);
 
     Path queryDeriver(const Path & path);
-    
+
     PathSet queryValidDerivers(const Path & path);
 
     PathSet queryDerivationOutputs(const Path & path);
-    
+
     StringSet queryDerivationOutputNames(const Path & path);
 
     Path queryPathFromHashPart(const string & hashPart);
-    
+
     PathSet querySubstitutablePaths(const PathSet & paths);
-    
+
     void querySubstitutablePathInfos(const PathSet & paths,
         SubstitutablePathInfos & infos);
-    
-    Path addToStore(const Path & srcPath,
+
+    Path addToStore(const string & name, const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
         PathFilter & filter = defaultPathFilter, bool repair = false);
 
@@ -64,7 +64,7 @@ public:
         Sink & sink);
 
     Paths importPaths(bool requireSignature, Source & source);
-    
+
     void buildPaths(const PathSet & paths, BuildMode buildMode);
 
     void ensurePath(const Path & path);
@@ -72,19 +72,20 @@ public:
     void addTempRoot(const Path & path);
 
     void addIndirectRoot(const Path & path);
-    
+
     void syncWithGC();
-    
+
     Roots findRoots();
 
     void collectGarbage(const GCOptions & options, GCResults & results);
-    
+
     PathSet queryFailedPaths();
 
     void clearFailedPaths(const PathSet & paths);
 
     void optimiseStore();
 
+    bool verifyStore(bool checkContents, bool repair);
 private:
     AutoCloseFD fdSocket;
     FdSink to;
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index 3109f100ef..3764f3e542 100644
--- a/nix/libstore/store-api.hh
+++ b/nix/libstore/store-api.hh
@@ -54,7 +54,7 @@ struct GCOptions
 };
 
 
-struct GCResults 
+struct GCResults
 {
     /* Depending on the action, the GC roots, or the paths that would
        be or have been deleted. */
@@ -82,7 +82,7 @@ struct SubstitutablePathInfo
 typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
 
 
-struct ValidPathInfo 
+struct ValidPathInfo
 {
     Path path;
     Path deriver;
@@ -100,13 +100,13 @@ typedef list<ValidPathInfo> ValidPathInfos;
 enum BuildMode { bmNormal, bmRepair, bmCheck };
 
 
-class StoreAPI 
+class StoreAPI
 {
 public:
 
     virtual ~StoreAPI() { }
 
-    /* Check whether a path is valid. */ 
+    /* Check whether a path is valid. */
     virtual bool isValidPath(const Path & path) = 0;
 
     /* Query which of the given paths is valid. */
@@ -118,7 +118,7 @@ public:
     /* Query information about a valid path. */
     virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
 
-    /* Query the hash of a valid path. */ 
+    /* Query the hash of a valid path. */
     virtual Hash queryPathHash(const Path & path) = 0;
 
     /* Query the set of outgoing FS references for a store path.  The
@@ -150,7 +150,7 @@ public:
     /* Query the full store path given the hash part of a valid store
        path, or "" if the path doesn't exist. */
     virtual Path queryPathFromHashPart(const string & hashPart) = 0;
-    
+
     /* Query which of the given paths have substitutes. */
     virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
 
@@ -159,12 +159,12 @@ public:
        info, it's omitted from the resulting ‘infos’ map. */
     virtual void querySubstitutablePathInfos(const PathSet & paths,
         SubstitutablePathInfos & infos) = 0;
-    
+
     /* Copy the contents of a path to the store and register the
        validity the resulting path.  The resulting path is returned.
        The function object `filter' can be used to exclude files (see
        libutil/archive.hh). */
-    virtual Path addToStore(const Path & srcPath,
+    virtual Path addToStore(const string & name, const Path & srcPath,
         bool recursive = true, HashType hashAlgo = htSHA256,
         PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
 
@@ -254,6 +254,10 @@ public:
     /* Optimise the disk space usage of the Nix store by hard-linking files
        with the same contents. */
     virtual void optimiseStore() = 0;
+
+    /* Check the integrity of the Nix store.  Returns true if errors
+       remain. */
+    virtual bool verifyStore(bool checkContents, bool repair) = 0;
 };
 
 
@@ -267,7 +271,7 @@ bool isStorePath(const Path & path);
 
 /* Extract the name part of the given store path. */
 string storePathToName(const Path & path);
-    
+
 void checkStoreName(const string & name);
 
 
@@ -288,7 +292,7 @@ Path followLinksToStorePath(const Path & path);
 /* Constructs a unique store path name. */
 Path makeStorePath(const string & type,
     const Hash & hash, const string & name);
-    
+
 Path makeOutputPath(const string & id,
     const Hash & hash, const string & name);
 
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index 4b040b77ce..d037d7402e 100644
--- a/nix/libstore/worker-protocol.hh
+++ b/nix/libstore/worker-protocol.hh
@@ -42,7 +42,8 @@ typedef enum {
     wopQueryValidPaths = 31,
     wopQuerySubstitutablePaths = 32,
     wopQueryValidDerivers = 33,
-    wopOptimiseStore = 34
+    wopOptimiseStore = 34,
+    wopVerifyStore = 35
 } WorkerOp;