summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config-daemon.ac2
-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
-rw-r--r--nix/libutil/hash.cc101
-rw-r--r--nix/libutil/util.cc62
-rw-r--r--nix/libutil/util.hh6
-rw-r--r--nix/nix-daemon/nix-daemon.cc34
15 files changed, 256 insertions, 235 deletions
diff --git a/config-daemon.ac b/config-daemon.ac
index a6cf29ca426..f96cc8f7ac1 100644
--- a/config-daemon.ac
+++ b/config-daemon.ac
@@ -76,7 +76,7 @@ if test "x$guix_build_daemon" = "xyes"; then
76 76
77 dnl Chroot support. 77 dnl Chroot support.
78 AC_CHECK_FUNCS([chroot unshare]) 78 AC_CHECK_FUNCS([chroot unshare])
79 AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h tr1/unordered_set]) 79 AC_CHECK_HEADERS([sched.h sys/param.h sys/mount.h sys/syscall.h])
80 80
81 if test "x$ac_cv_func_chroot" != "xyes"; then 81 if test "x$ac_cv_func_chroot" != "xyes"; then
82 AC_MSG_ERROR(['chroot' function missing, bailing out]) 82 AC_MSG_ERROR(['chroot' function missing, bailing out])
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 009fcb2c0c1..85a818ba94f 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -38,6 +38,9 @@
38#if HAVE_SYS_MOUNT_H 38#if HAVE_SYS_MOUNT_H
39#include <sys/mount.h> 39#include <sys/mount.h>
40#endif 40#endif
41#if HAVE_SYS_SYSCALL_H
42#include <sys/syscall.h>
43#endif
41#if HAVE_SCHED_H 44#if HAVE_SCHED_H
42#include <sched.h> 45#include <sched.h>
43#endif 46#endif
@@ -48,7 +51,7 @@
48#include <linux/fs.h> 51#include <linux/fs.h>
49#endif 52#endif
50 53
51#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) 54#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
52 55
53#if CHROOT_ENABLED 56#if CHROOT_ENABLED
54#include <sys/socket.h> 57#include <sys/socket.h>
@@ -414,19 +417,6 @@ static void commonChildInit(Pipe & logPipe)
414 close(fdDevNull); 417 close(fdDevNull);
415} 418}
416 419
417
418/* Convert a string list to an array of char pointers. Careful: the
419 string list should outlive the array. */
420const char * * strings2CharPtrs(const Strings & ss)
421{
422 const char * * arr = new const char * [ss.size() + 1];
423 const char * * p = arr;
424 foreach (Strings::const_iterator, i, ss) *p++ = i->c_str();
425 *p = 0;
426 return arr;
427}
428
429
430/* Restore default handling of SIGPIPE, otherwise some programs will 420/* Restore default handling of SIGPIPE, otherwise some programs will
431 randomly say "Broken pipe". */ 421 randomly say "Broken pipe". */
432static void restoreSIGPIPE() 422static void restoreSIGPIPE()
@@ -764,7 +754,7 @@ private:
764 typedef void (DerivationGoal::*GoalState)(); 754 typedef void (DerivationGoal::*GoalState)();
765 GoalState state; 755 GoalState state;
766 756
767 /* Stuff we need to pass to initChild(). */ 757 /* Stuff we need to pass to runChild(). */
768 typedef map<Path, Path> DirsInChroot; // maps target path to source path 758 typedef map<Path, Path> DirsInChroot; // maps target path to source path
769 DirsInChroot dirsInChroot; 759 DirsInChroot dirsInChroot;
770 typedef map<string, string> Environment; 760 typedef map<string, string> Environment;
@@ -828,8 +818,8 @@ private:
828 /* Start building a derivation. */ 818 /* Start building a derivation. */
829 void startBuilder(); 819 void startBuilder();
830 820
831 /* Initialise the builder's process. */ 821 /* Run the builder's process. */
832 void initChild(); 822 void runChild();
833 823
834 friend int childEntry(void *); 824 friend int childEntry(void *);
835 825
@@ -1612,7 +1602,7 @@ void chmod_(const Path & path, mode_t mode)
1612 1602
1613int childEntry(void * arg) 1603int childEntry(void * arg)
1614{ 1604{
1615 ((DerivationGoal *) arg)->initChild(); 1605 ((DerivationGoal *) arg)->runChild();
1616 return 1; 1606 return 1;
1617} 1607}
1618 1608
@@ -1759,37 +1749,11 @@ void DerivationGoal::startBuilder()
1759 1749
1760 /* Change ownership of the temporary build directory. */ 1750 /* Change ownership of the temporary build directory. */
1761 if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1) 1751 if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
1762 throw SysError(format("cannot change ownership of `%1%'") % tmpDir); 1752 throw SysError(format("cannot change ownership of '%1%'") % tmpDir);
1753 }
1763 1754
1764 /* Check that the Nix store has the appropriate permissions,
1765 i.e., owned by root and mode 1775 (sticky bit on so that
1766 the builder can create its output but not mess with the
1767 outputs of other processes). */
1768 struct stat st;
1769 if (stat(settings.nixStore.c_str(), &st) == -1)
1770 throw SysError(format("cannot stat `%1%'") % settings.nixStore);
1771 if (!(st.st_mode & S_ISVTX) ||
1772 ((st.st_mode & S_IRWXG) != S_IRWXG) ||
1773 (st.st_gid != buildUser.getGID()))
1774 throw Error(format(
1775 "builder does not have write permission to `%2%'; "
1776 "try `chgrp %1% %2%; chmod 1775 %2%'")
1777 % buildUser.getGID() % settings.nixStore);
1778 }
1779
1780
1781 /* Are we doing a chroot build? Note that fixed-output
1782 derivations are never done in a chroot, mainly so that
1783 functions like fetchurl (which needs a proper /etc/resolv.conf)
1784 work properly. Purity checking for fixed-output derivations
1785 is somewhat pointless anyway. */
1786 useChroot = settings.useChroot; 1755 useChroot = settings.useChroot;
1787 1756
1788 if (fixedOutput) useChroot = false;
1789
1790 /* Hack to allow derivations to disable chroot builds. */
1791 if (get(drv.env, "__noChroot") == "1") useChroot = false;
1792
1793 if (useChroot) { 1757 if (useChroot) {
1794#if CHROOT_ENABLED 1758#if CHROOT_ENABLED
1795 /* Create a temporary directory in which we set up the chroot 1759 /* Create a temporary directory in which we set up the chroot
@@ -1804,6 +1768,12 @@ void DerivationGoal::startBuilder()
1804 1768
1805 printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir); 1769 printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % chrootRootDir);
1806 1770
1771 if (mkdir(chrootRootDir.c_str(), 0750) == -1)
1772 throw SysError(format("cannot create ‘%1%’") % chrootRootDir);
1773
1774 if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1)
1775 throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir);
1776
1807 /* Create a writable /tmp in the chroot. Many builders need 1777 /* Create a writable /tmp in the chroot. Many builders need
1808 this. (Of course they should really respect $TMPDIR 1778 this. (Of course they should really respect $TMPDIR
1809 instead.) */ 1779 instead.) */
@@ -1830,7 +1800,8 @@ void DerivationGoal::startBuilder()
1830 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str()); 1800 % (buildUser.enabled() ? buildUser.getGID() : getgid())).str());
1831 1801
1832 /* Create /etc/hosts with localhost entry. */ 1802 /* Create /etc/hosts with localhost entry. */
1833 writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n"); 1803 if (!fixedOutput)
1804 writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n");
1834 1805
1835 /* Bind-mount a user-configurable set of directories from the 1806 /* Bind-mount a user-configurable set of directories from the
1836 host file system. */ 1807 host file system. */
@@ -1853,8 +1824,12 @@ void DerivationGoal::startBuilder()
1853 can be bind-mounted). !!! As an extra security 1824 can be bind-mounted). !!! As an extra security
1854 precaution, make the fake Nix store only writable by the 1825 precaution, make the fake Nix store only writable by the
1855 build user. */ 1826 build user. */
1856 createDirs(chrootRootDir + settings.nixStore); 1827 Path chrootStoreDir = chrootRootDir + settings.nixStore;
1857 chmod_(chrootRootDir + settings.nixStore, 01777); 1828 createDirs(chrootStoreDir);
1829 chmod_(chrootStoreDir, 01775);
1830
1831 if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1)
1832 throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir);
1858 1833
1859 foreach (PathSet::iterator, i, inputPaths) { 1834 foreach (PathSet::iterator, i, inputPaths) {
1860 struct stat st; 1835 struct stat st;
@@ -1963,14 +1938,17 @@ void DerivationGoal::startBuilder()
1963 */ 1938 */
1964#if CHROOT_ENABLED 1939#if CHROOT_ENABLED
1965 if (useChroot) { 1940 if (useChroot) {
1966 char stack[32 * 1024]; 1941 char stack[32 * 1024];
1967 pid = clone(childEntry, stack + sizeof(stack) - 8, 1942 int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD;
1968 CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWUTS | SIGCHLD, this); 1943 if (!fixedOutput) flags |= CLONE_NEWNET;
1944 pid = clone(childEntry, stack + sizeof(stack) - 8, flags, this);
1945 if (pid == -1)
1946 throw SysError("cloning builder process");
1969 } else 1947 } else
1970#endif 1948#endif
1971 { 1949 {
1972 pid = fork(); 1950 pid = fork();
1973 if (pid == 0) initChild(); 1951 if (pid == 0) runChild();
1974 } 1952 }
1975 1953
1976 if (pid == -1) throw SysError("unable to fork"); 1954 if (pid == -1) throw SysError("unable to fork");
@@ -1993,7 +1971,7 @@ void DerivationGoal::startBuilder()
1993} 1971}
1994 1972
1995 1973
1996void DerivationGoal::initChild() 1974void DerivationGoal::runChild()
1997{ 1975{
1998 /* Warning: in the child we should absolutely not make any SQLite 1976 /* Warning: in the child we should absolutely not make any SQLite
1999 calls! */ 1977 calls! */
@@ -2022,9 +2000,11 @@ void DerivationGoal::initChild()
2022 2000
2023 /* Set the hostname etc. to fixed values. */ 2001 /* Set the hostname etc. to fixed values. */
2024 char hostname[] = "localhost"; 2002 char hostname[] = "localhost";
2025 sethostname(hostname, sizeof(hostname)); 2003 if (sethostname(hostname, sizeof(hostname)) == -1)
2004 throw SysError("cannot set host name");
2026 char domainname[] = "(none)"; // kernel default 2005 char domainname[] = "(none)"; // kernel default
2027 setdomainname(domainname, sizeof(domainname)); 2006 if (setdomainname(domainname, sizeof(domainname)) == -1)
2007 throw SysError("cannot set domain name");
2028 2008
2029 /* Make all filesystems private. This is necessary 2009 /* Make all filesystems private. This is necessary
2030 because subtrees may have been mounted as "shared" 2010 because subtrees may have been mounted as "shared"
@@ -2042,12 +2022,17 @@ void DerivationGoal::initChild()
2042 throw SysError(format("unable to make filesystem `%1%' private") % fs); 2022 throw SysError(format("unable to make filesystem `%1%' private") % fs);
2043 } 2023 }
2044 2024
2025 /* Bind-mount chroot directory to itself, to treat it as a
2026 different filesystem from /, as needed for pivot_root. */
2027 if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
2028 throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir);
2029
2045 /* Set up a nearly empty /dev, unless the user asked to 2030 /* Set up a nearly empty /dev, unless the user asked to
2046 bind-mount the host /dev. */ 2031 bind-mount the host /dev. */
2032 Strings ss;
2047 if (dirsInChroot.find("/dev") == dirsInChroot.end()) { 2033 if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
2048 createDirs(chrootRootDir + "/dev/shm"); 2034 createDirs(chrootRootDir + "/dev/shm");
2049 createDirs(chrootRootDir + "/dev/pts"); 2035 createDirs(chrootRootDir + "/dev/pts");
2050 Strings ss;
2051 ss.push_back("/dev/full"); 2036 ss.push_back("/dev/full");
2052#ifdef __linux__ 2037#ifdef __linux__
2053 if (pathExists("/dev/kvm")) 2038 if (pathExists("/dev/kvm"))
@@ -2058,13 +2043,24 @@ void DerivationGoal::initChild()
2058 ss.push_back("/dev/tty"); 2043 ss.push_back("/dev/tty");
2059 ss.push_back("/dev/urandom"); 2044 ss.push_back("/dev/urandom");
2060 ss.push_back("/dev/zero"); 2045 ss.push_back("/dev/zero");
2061 foreach (Strings::iterator, i, ss) dirsInChroot[*i] = *i;
2062 createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd"); 2046 createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
2063 createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin"); 2047 createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
2064 createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout"); 2048 createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
2065 createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr"); 2049 createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
2066 } 2050 }
2067 2051
2052 /* Fixed-output derivations typically need to access the
2053 network, so give them access to /etc/resolv.conf and so
2054 on. */
2055 if (fixedOutput) {
2056 ss.push_back("/etc/resolv.conf");
2057 ss.push_back("/etc/nsswitch.conf");
2058 ss.push_back("/etc/services");
2059 ss.push_back("/etc/hosts");
2060 }
2061
2062 for (auto & i : ss) dirsInChroot[i] = i;
2063
2068 /* Bind-mount all the directories from the "host" 2064 /* Bind-mount all the directories from the "host"
2069 filesystem that we want in the chroot 2065 filesystem that we want in the chroot
2070 environment. */ 2066 environment. */
@@ -2114,13 +2110,26 @@ void DerivationGoal::initChild()
2114 chmod_(chrootRootDir + "/dev/pts/ptmx", 0666); 2110 chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
2115 } 2111 }
2116 2112
2117 /* Do the chroot(). Below we do a chdir() to the 2113 /* Do the chroot(). */
2118 temporary build directory to make sure the current 2114 if (chdir(chrootRootDir.c_str()) == -1)
2119 directory is in the chroot. (Actually the order 2115 throw SysError(format("cannot change directory to '%1%'") % chrootRootDir);
2120 doesn't matter, since due to the bind mount tmpDir and 2116
2121 tmpRootDit/tmpDir are the same directories.) */ 2117 if (mkdir("real-root", 0) == -1)
2122 if (chroot(chrootRootDir.c_str()) == -1) 2118 throw SysError("cannot create real-root directory");
2123 throw SysError(format("cannot change root directory to `%1%'") % chrootRootDir); 2119
2120#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
2121 if (pivot_root(".", "real-root") == -1)
2122 throw SysError(format("cannot pivot old root directory onto '%1%'") % (chrootRootDir + "/real-root"));
2123#undef pivot_root
2124
2125 if (chroot(".") == -1)
2126 throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir);
2127
2128 if (umount2("real-root", MNT_DETACH) == -1)
2129 throw SysError("cannot unmount real root filesystem");
2130
2131 if (rmdir("real-root") == -1)
2132 throw SysError("cannot remove real-root directory");
2124 } 2133 }
2125#endif 2134#endif
2126 2135
@@ -2159,11 +2168,7 @@ void DerivationGoal::initChild()
2159 Strings envStrs; 2168 Strings envStrs;
2160 foreach (Environment::const_iterator, i, env) 2169 foreach (Environment::const_iterator, i, env)
2161 envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp)); 2170 envStrs.push_back(rewriteHashes(i->first + "=" + i->second, rewritesToTmp));
2162 const char * * envArr = strings2CharPtrs(envStrs); 2171 auto envArr = stringsToCharPtrs(envStrs);
2163
2164 Path program = drv.builder.c_str();
2165 std::vector<const char *> args; /* careful with c_str()! */
2166 string user; /* must be here for its c_str()! */
2167 2172
2168 /* If we are running in `build-users' mode, then switch to the 2173 /* If we are running in `build-users' mode, then switch to the
2169 user we allocated above. Make sure that we drop all root 2174 user we allocated above. Make sure that we drop all root
@@ -2189,29 +2194,25 @@ void DerivationGoal::initChild()
2189 } 2194 }
2190 2195
2191 /* Fill in the arguments. */ 2196 /* Fill in the arguments. */
2197 Strings args;
2192 string builderBasename = baseNameOf(drv.builder); 2198 string builderBasename = baseNameOf(drv.builder);
2193 args.push_back(builderBasename.c_str()); 2199 args.push_back(builderBasename);
2194 foreach (Strings::iterator, i, drv.args) { 2200 foreach (Strings::iterator, i, drv.args)
2195 auto re = rewriteHashes(*i, rewritesToTmp); 2201 args.push_back(rewriteHashes(*i, rewritesToTmp));
2196 auto cstr = new char[re.length()+1]; 2202 auto argArr = stringsToCharPtrs(args);
2197 std::strcpy(cstr, re.c_str());
2198
2199 args.push_back(cstr);
2200 }
2201 args.push_back(0);
2202 2203
2203 restoreSIGPIPE(); 2204 restoreSIGPIPE();
2204 2205
2205 /* Indicate that we managed to set up the build environment. */ 2206 /* Indicate that we managed to set up the build environment. */
2206 writeToStderr("\n"); 2207 writeFull(STDERR_FILENO, "\n");
2207 2208
2208 /* Execute the program. This should not return. */ 2209 /* Execute the program. This should not return. */
2209 execve(program.c_str(), (char * *) &args[0], (char * *) envArr); 2210 execve(drv.builder.c_str(), (char * *) &argArr[0], (char * *) &envArr[0]);
2210 2211
2211 throw SysError(format("executing `%1%'") % drv.builder); 2212 throw SysError(format("executing `%1%'") % drv.builder);
2212 2213
2213 } catch (std::exception & e) { 2214 } catch (std::exception & e) {
2214 writeToStderr("while setting up the build environment: " + string(e.what()) + "\n"); 2215 writeFull(STDERR_FILENO, "while setting up the build environment: " + string(e.what()) + "\n");
2215 _exit(1); 2216 _exit(1);
2216 } 2217 }
2217 2218
@@ -2526,7 +2527,7 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
2526 BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size()); 2527 BZ2_bzWrite(&err, bzLogFile, (unsigned char *) data.data(), data.size());
2527 if (err != BZ_OK) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err); 2528 if (err != BZ_OK) throw Error(format("cannot write to compressed log file (BZip2 error = %1%)") % err);
2528 } else if (fdLogFile != -1) 2529 } else if (fdLogFile != -1)
2529 writeFull(fdLogFile, (unsigned char *) data.data(), data.size()); 2530 writeFull(fdLogFile, data);
2530 } 2531 }
2531 2532
2532 if (hook && fd == hook->fromHook.readSide) 2533 if (hook && fd == hook->fromHook.readSide)
@@ -2836,7 +2837,7 @@ void SubstitutionGoal::tryToRun()
2836 args.push_back("--substitute"); 2837 args.push_back("--substitute");
2837 args.push_back(storePath); 2838 args.push_back(storePath);
2838 args.push_back(destPath); 2839 args.push_back(destPath);
2839 const char * * argArr = strings2CharPtrs(args); 2840 auto argArr = stringsToCharPtrs(args);
2840 2841
2841 /* Fork the substitute program. */ 2842 /* Fork the substitute program. */
2842 pid = startProcess([&]() { 2843 pid = startProcess([&]() {
@@ -2846,7 +2847,7 @@ void SubstitutionGoal::tryToRun()
2846 if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1) 2847 if (dup2(outPipe.writeSide, STDOUT_FILENO) == -1)
2847 throw SysError("cannot dup output pipe into stdout"); 2848 throw SysError("cannot dup output pipe into stdout");
2848 2849
2849 execv(sub.c_str(), (char * *) argArr); 2850 execv(sub.c_str(), (char * *) &argArr[0]);
2850 2851
2851 throw SysError(format("executing `%1%'") % sub); 2852 throw SysError(format("executing `%1%'") % sub);
2852 }); 2853 });
diff --git a/nix/libstore/gc.cc b/nix/libstore/gc.cc
index f98e02c1e21..34768324c26 100644
--- a/nix/libstore/gc.cc
+++ b/nix/libstore/gc.cc
@@ -96,7 +96,7 @@ Path addPermRoot(StoreAPI & store, const Path & _storePath,
96 "(are you running nix-build inside the store?)") % gcRoot); 96 "(are you running nix-build inside the store?)") % gcRoot);
97 97
98 if (indirect) { 98 if (indirect) {
99 /* Don't clobber the the link if it already exists and doesn't 99 /* Don't clobber the link if it already exists and doesn't
100 point to the Nix store. */ 100 point to the Nix store. */
101 if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) 101 if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
102 throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot); 102 throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
@@ -191,7 +191,7 @@ void LocalStore::addTempRoot(const Path & path)
191 lockFile(fdTempRoots, ltWrite, true); 191 lockFile(fdTempRoots, ltWrite, true);
192 192
193 string s = path + '\0'; 193 string s = path + '\0';
194 writeFull(fdTempRoots, (const unsigned char *) s.data(), s.size()); 194 writeFull(fdTempRoots, s);
195 195
196 /* Downgrade to a read lock. */ 196 /* Downgrade to a read lock. */
197 debug(format("downgrading to read lock on `%1%'") % fnTempRoots); 197 debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
@@ -231,7 +231,7 @@ static void readTempRoots(PathSet & tempRoots, FDs & fds)
231 if (lockFile(*fd, ltWrite, false)) { 231 if (lockFile(*fd, ltWrite, false)) {
232 printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path); 232 printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
233 unlink(path.c_str()); 233 unlink(path.c_str());
234 writeFull(*fd, (const unsigned char *) "d", 1); 234 writeFull(*fd, "d");
235 continue; 235 continue;
236 } 236 }
237 237
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index a115f658475..630cb80c413 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -254,22 +254,25 @@ LocalStore::LocalStore(bool reserveSpace)
254 Path perUserDir = profilesDir + "/per-user"; 254 Path perUserDir = profilesDir + "/per-user";
255 createDirs(perUserDir); 255 createDirs(perUserDir);
256 if (chmod(perUserDir.c_str(), 01777) == -1) 256 if (chmod(perUserDir.c_str(), 01777) == -1)
257 throw SysError(format("could not set permissions on `%1%' to 1777") % perUserDir); 257 throw SysError(format("could not set permissions on '%1%' to 1777") % perUserDir);
258
259 mode_t perm = 01775;
258 260
259 struct group * gr = getgrnam(settings.buildUsersGroup.c_str()); 261 struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
260 if (!gr) 262 if (!gr)
261 throw Error(format("the group `%1%' specified in `build-users-group' does not exist") 263 throw Error(format("the group `%1%' specified in `build-users-group' does not exist")
262 % settings.buildUsersGroup); 264 % settings.buildUsersGroup);
263 265 else {
264 struct stat st; 266 struct stat st;
265 if (stat(settings.nixStore.c_str(), &st)) 267 if (stat(settings.nixStore.c_str(), &st))
266 throw SysError(format("getting attributes of path `%1%'") % settings.nixStore); 268 throw SysError(format("getting attributes of path '%1%'") % settings.nixStore);
267 269
268 if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != 01775) { 270 if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
269 if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1) 271 if (chown(settings.nixStore.c_str(), 0, gr->gr_gid) == -1)
270 throw SysError(format("changing ownership of path `%1%'") % settings.nixStore); 272 throw SysError(format("changing ownership of path '%1%'") % settings.nixStore);
271 if (chmod(settings.nixStore.c_str(), 01775) == -1) 273 if (chmod(settings.nixStore.c_str(), perm) == -1)
272 throw SysError(format("changing permissions on path `%1%'") % settings.nixStore); 274 throw SysError(format("changing permissions on path '%1%'") % settings.nixStore);
275 }
273 } 276 }
274 } 277 }
275 278
@@ -499,7 +502,7 @@ void LocalStore::makeStoreWritable()
499 if (unshare(CLONE_NEWNS) == -1) 502 if (unshare(CLONE_NEWNS) == -1)
500 throw SysError("setting up a private mount namespace"); 503 throw SysError("setting up a private mount namespace");
501 504
502 if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1) 505 if (mount(0, settings.nixStore.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
503 throw SysError(format("remounting %1% writable") % settings.nixStore); 506 throw SysError(format("remounting %1% writable") % settings.nixStore);
504 } 507 }
505#endif 508#endif
@@ -1404,7 +1407,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
1404} 1407}
1405 1408
1406 1409
1407Path LocalStore::addToStore(const Path & _srcPath, 1410Path LocalStore::addToStore(const string & name, const Path & _srcPath,
1408 bool recursive, HashType hashAlgo, PathFilter & filter, bool repair) 1411 bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
1409{ 1412{
1410 Path srcPath(absPath(_srcPath)); 1413 Path srcPath(absPath(_srcPath));
@@ -1419,7 +1422,7 @@ Path LocalStore::addToStore(const Path & _srcPath,
1419 else 1422 else
1420 sink.s = readFile(srcPath); 1423 sink.s = readFile(srcPath);
1421 1424
1422 return addToStoreFromDump(sink.s, baseNameOf(srcPath), recursive, hashAlgo, repair); 1425 return addToStoreFromDump(sink.s, name, recursive, hashAlgo, repair);
1423} 1426}
1424 1427
1425 1428
diff --git a/nix/libstore/local-store.hh b/nix/libstore/local-store.hh
index e0aabdba420..819f59327a2 100644
--- a/nix/libstore/local-store.hh
+++ b/nix/libstore/local-store.hh
@@ -130,7 +130,7 @@ public:
130 void querySubstitutablePathInfos(const PathSet & paths, 130 void querySubstitutablePathInfos(const PathSet & paths,
131 SubstitutablePathInfos & infos); 131 SubstitutablePathInfos & infos);
132 132
133 Path addToStore(const Path & srcPath, 133 Path addToStore(const string & name, const Path & srcPath,
134 bool recursive = true, HashType hashAlgo = htSHA256, 134 bool recursive = true, HashType hashAlgo = htSHA256,
135 PathFilter & filter = defaultPathFilter, bool repair = false); 135 PathFilter & filter = defaultPathFilter, bool repair = false);
136 136
diff --git a/nix/libstore/optimise-store.cc b/nix/libstore/optimise-store.cc
index 8ba9d1a2637..c62b8e451b4 100644
--- a/nix/libstore/optimise-store.cc
+++ b/nix/libstore/optimise-store.cc
@@ -4,6 +4,7 @@
4#include "local-store.hh" 4#include "local-store.hh"
5#include "globals.hh" 5#include "globals.hh"
6 6
7#include <cstdlib>
7#include <sys/types.h> 8#include <sys/types.h>
8#include <sys/stat.h> 9#include <sys/stat.h>
9#include <unistd.h> 10#include <unistd.h>
diff --git a/nix/libstore/pathlocks.cc b/nix/libstore/pathlocks.cc
index b858ed238de..830858ff8d9 100644
--- a/nix/libstore/pathlocks.cc
+++ b/nix/libstore/pathlocks.cc
@@ -33,7 +33,7 @@ void deleteLockFile(const Path & path, int fd)
33 other processes waiting on this lock that the lock is stale 33 other processes waiting on this lock that the lock is stale
34 (deleted). */ 34 (deleted). */
35 unlink(path.c_str()); 35 unlink(path.c_str());
36 writeFull(fd, (const unsigned char *) "d", 1); 36 writeFull(fd, "d");
37 /* Note that the result of unlink() is ignored; removing the lock 37 /* Note that the result of unlink() is ignored; removing the lock
38 file is an optimisation, not a necessity. */ 38 file is an optimisation, not a necessity. */
39} 39}
diff --git a/nix/libstore/remote-store.cc b/nix/libstore/remote-store.cc
index 448d9b6bc1d..0539bbe1270 100644
--- a/nix/libstore/remote-store.cc
+++ b/nix/libstore/remote-store.cc
@@ -10,6 +10,7 @@
10#include <sys/stat.h> 10#include <sys/stat.h>
11#include <sys/socket.h> 11#include <sys/socket.h>
12#include <sys/un.h> 12#include <sys/un.h>
13#include <errno.h>
13#include <fcntl.h> 14#include <fcntl.h>
14 15
15#include <iostream> 16#include <iostream>
@@ -109,7 +110,7 @@ void RemoteStore::connectToDaemon()
109 applications... */ 110 applications... */
110 AutoCloseFD fdPrevDir = open(".", O_RDONLY); 111 AutoCloseFD fdPrevDir = open(".", O_RDONLY);
111 if (fdPrevDir == -1) throw SysError("couldn't open current directory"); 112 if (fdPrevDir == -1) throw SysError("couldn't open current directory");
112 chdir(dirOf(socketPath).c_str()); 113 if (chdir(dirOf(socketPath).c_str()) == -1) throw SysError(format("couldn't change to directory of ‘%1%’") % socketPath);
113 Path socketPathRel = "./" + baseNameOf(socketPath); 114 Path socketPathRel = "./" + baseNameOf(socketPath);
114 115
115 struct sockaddr_un addr; 116 struct sockaddr_un addr;
@@ -384,7 +385,7 @@ Path RemoteStore::queryPathFromHashPart(const string & hashPart)
384} 385}
385 386
386 387
387Path RemoteStore::addToStore(const Path & _srcPath, 388Path RemoteStore::addToStore(const string & name, const Path & _srcPath,
388 bool recursive, HashType hashAlgo, PathFilter & filter, bool repair) 389 bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
389{ 390{
390 if (repair) throw Error("repairing is not supported when building through the Nix daemon"); 391 if (repair) throw Error("repairing is not supported when building through the Nix daemon");
@@ -394,7 +395,7 @@ Path RemoteStore::addToStore(const Path & _srcPath,
394 Path srcPath(absPath(_srcPath)); 395 Path srcPath(absPath(_srcPath));
395 396
396 writeInt(wopAddToStore, to); 397 writeInt(wopAddToStore, to);
397 writeString(baseNameOf(srcPath), to); 398 writeString(name, to);
398 /* backwards compatibility hack */ 399 /* backwards compatibility hack */
399 writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to); 400 writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
400 writeInt(recursive ? 1 : 0, to); 401 writeInt(recursive ? 1 : 0, to);
@@ -584,6 +585,16 @@ void RemoteStore::optimiseStore()
584 readInt(from); 585 readInt(from);
585} 586}
586 587
588bool RemoteStore::verifyStore(bool checkContents, bool repair)
589{
590 openConnection();
591 writeInt(wopVerifyStore, to);
592 writeInt(checkContents, to);
593 writeInt(repair, to);
594 processStderr();
595 return readInt(from) != 0;
596}
597
587void RemoteStore::processStderr(Sink * sink, Source * source) 598void RemoteStore::processStderr(Sink * sink, Source * source)
588{ 599{
589 to.flush(); 600 to.flush();
diff --git a/nix/libstore/remote-store.hh b/nix/libstore/remote-store.hh
index 98774c10b3d..030120db406 100644
--- a/nix/libstore/remote-store.hh
+++ b/nix/libstore/remote-store.hh
@@ -21,15 +21,15 @@ public:
21 RemoteStore(); 21 RemoteStore();
22 22
23 ~RemoteStore(); 23 ~RemoteStore();
24 24
25 /* Implementations of abstract store API methods. */ 25 /* Implementations of abstract store API methods. */
26 26
27 bool isValidPath(const Path & path); 27 bool isValidPath(const Path & path);
28 28
29 PathSet queryValidPaths(const PathSet & paths); 29 PathSet queryValidPaths(const PathSet & paths);
30 30
31 PathSet queryAllValidPaths(); 31 PathSet queryAllValidPaths();
32 32
33 ValidPathInfo queryPathInfo(const Path & path); 33 ValidPathInfo queryPathInfo(const Path & path);
34 34
35 Hash queryPathHash(const Path & path); 35 Hash queryPathHash(const Path & path);
@@ -39,21 +39,21 @@ public:
39 void queryReferrers(const Path & path, PathSet & referrers); 39 void queryReferrers(const Path & path, PathSet & referrers);
40 40
41 Path queryDeriver(const Path & path); 41 Path queryDeriver(const Path & path);
42 42
43 PathSet queryValidDerivers(const Path & path); 43 PathSet queryValidDerivers(const Path & path);
44 44
45 PathSet queryDerivationOutputs(const Path & path); 45 PathSet queryDerivationOutputs(const Path & path);
46 46
47 StringSet queryDerivationOutputNames(const Path & path); 47 StringSet queryDerivationOutputNames(const Path & path);
48 48
49 Path queryPathFromHashPart(const string & hashPart); 49 Path queryPathFromHashPart(const string & hashPart);
50 50
51 PathSet querySubstitutablePaths(const PathSet & paths); 51 PathSet querySubstitutablePaths(const PathSet & paths);
52 52
53 void querySubstitutablePathInfos(const PathSet & paths, 53 void querySubstitutablePathInfos(const PathSet & paths,
54 SubstitutablePathInfos & infos); 54 SubstitutablePathInfos & infos);
55 55
56 Path addToStore(const Path & srcPath, 56 Path addToStore(const string & name, const Path & srcPath,
57 bool recursive = true, HashType hashAlgo = htSHA256, 57 bool recursive = true, HashType hashAlgo = htSHA256,
58 PathFilter & filter = defaultPathFilter, bool repair = false); 58 PathFilter & filter = defaultPathFilter, bool repair = false);
59 59
@@ -64,7 +64,7 @@ public:
64 Sink & sink); 64 Sink & sink);
65 65
66 Paths importPaths(bool requireSignature, Source & source); 66 Paths importPaths(bool requireSignature, Source & source);
67 67
68 void buildPaths(const PathSet & paths, BuildMode buildMode); 68 void buildPaths(const PathSet & paths, BuildMode buildMode);
69 69
70 void ensurePath(const Path & path); 70 void ensurePath(const Path & path);
@@ -72,19 +72,20 @@ public:
72 void addTempRoot(const Path & path); 72 void addTempRoot(const Path & path);
73 73
74 void addIndirectRoot(const Path & path); 74 void addIndirectRoot(const Path & path);
75 75
76 void syncWithGC(); 76 void syncWithGC();
77 77
78 Roots findRoots(); 78 Roots findRoots();
79 79
80 void collectGarbage(const GCOptions & options, GCResults & results); 80 void collectGarbage(const GCOptions & options, GCResults & results);
81 81
82 PathSet queryFailedPaths(); 82 PathSet queryFailedPaths();
83 83
84 void clearFailedPaths(const PathSet & paths); 84 void clearFailedPaths(const PathSet & paths);
85 85
86 void optimiseStore(); 86 void optimiseStore();
87 87
88 bool verifyStore(bool checkContents, bool repair);
88private: 89private:
89 AutoCloseFD fdSocket; 90 AutoCloseFD fdSocket;
90 FdSink to; 91 FdSink to;
diff --git a/nix/libstore/store-api.hh b/nix/libstore/store-api.hh
index 3109f100ef9..3764f3e5424 100644
--- a/nix/libstore/store-api.hh
+++ b/nix/libstore/store-api.hh
@@ -54,7 +54,7 @@ struct GCOptions
54}; 54};
55 55
56 56
57struct GCResults 57struct GCResults
58{ 58{
59 /* Depending on the action, the GC roots, or the paths that would 59 /* Depending on the action, the GC roots, or the paths that would
60 be or have been deleted. */ 60 be or have been deleted. */
@@ -82,7 +82,7 @@ struct SubstitutablePathInfo
82typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos; 82typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
83 83
84 84
85struct ValidPathInfo 85struct ValidPathInfo
86{ 86{
87 Path path; 87 Path path;
88 Path deriver; 88 Path deriver;
@@ -100,13 +100,13 @@ typedef list<ValidPathInfo> ValidPathInfos;
100enum BuildMode { bmNormal, bmRepair, bmCheck }; 100enum BuildMode { bmNormal, bmRepair, bmCheck };
101 101
102 102
103class StoreAPI 103class StoreAPI
104{ 104{
105public: 105public:
106 106
107 virtual ~StoreAPI() { } 107 virtual ~StoreAPI() { }
108 108
109 /* Check whether a path is valid. */ 109 /* Check whether a path is valid. */
110 virtual bool isValidPath(const Path & path) = 0; 110 virtual bool isValidPath(const Path & path) = 0;
111 111
112 /* Query which of the given paths is valid. */ 112 /* Query which of the given paths is valid. */
@@ -118,7 +118,7 @@ public:
118 /* Query information about a valid path. */ 118 /* Query information about a valid path. */
119 virtual ValidPathInfo queryPathInfo(const Path & path) = 0; 119 virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
120 120
121 /* Query the hash of a valid path. */ 121 /* Query the hash of a valid path. */
122 virtual Hash queryPathHash(const Path & path) = 0; 122 virtual Hash queryPathHash(const Path & path) = 0;
123 123
124 /* Query the set of outgoing FS references for a store path. The 124 /* Query the set of outgoing FS references for a store path. The
@@ -150,7 +150,7 @@ public:
150 /* Query the full store path given the hash part of a valid store 150 /* Query the full store path given the hash part of a valid store
151 path, or "" if the path doesn't exist. */ 151 path, or "" if the path doesn't exist. */
152 virtual Path queryPathFromHashPart(const string & hashPart) = 0; 152 virtual Path queryPathFromHashPart(const string & hashPart) = 0;
153 153
154 /* Query which of the given paths have substitutes. */ 154 /* Query which of the given paths have substitutes. */
155 virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0; 155 virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
156 156
@@ -159,12 +159,12 @@ public:
159 info, it's omitted from the resulting ‘infos’ map. */ 159 info, it's omitted from the resulting ‘infos’ map. */
160 virtual void querySubstitutablePathInfos(const PathSet & paths, 160 virtual void querySubstitutablePathInfos(const PathSet & paths,
161 SubstitutablePathInfos & infos) = 0; 161 SubstitutablePathInfos & infos) = 0;
162 162
163 /* Copy the contents of a path to the store and register the 163 /* Copy the contents of a path to the store and register the
164 validity the resulting path. The resulting path is returned. 164 validity the resulting path. The resulting path is returned.
165 The function object `filter' can be used to exclude files (see 165 The function object `filter' can be used to exclude files (see
166 libutil/archive.hh). */ 166 libutil/archive.hh). */
167 virtual Path addToStore(const Path & srcPath, 167 virtual Path addToStore(const string & name, const Path & srcPath,
168 bool recursive = true, HashType hashAlgo = htSHA256, 168 bool recursive = true, HashType hashAlgo = htSHA256,
169 PathFilter & filter = defaultPathFilter, bool repair = false) = 0; 169 PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
170 170
@@ -254,6 +254,10 @@ public:
254 /* Optimise the disk space usage of the Nix store by hard-linking files 254 /* Optimise the disk space usage of the Nix store by hard-linking files
255 with the same contents. */ 255 with the same contents. */
256 virtual void optimiseStore() = 0; 256 virtual void optimiseStore() = 0;
257
258 /* Check the integrity of the Nix store. Returns true if errors
259 remain. */
260 virtual bool verifyStore(bool checkContents, bool repair) = 0;
257}; 261};
258 262
259 263
@@ -267,7 +271,7 @@ bool isStorePath(const Path & path);
267 271
268/* Extract the name part of the given store path. */ 272/* Extract the name part of the given store path. */
269string storePathToName(const Path & path); 273string storePathToName(const Path & path);
270 274
271void checkStoreName(const string & name); 275void checkStoreName(const string & name);
272 276
273 277
@@ -288,7 +292,7 @@ Path followLinksToStorePath(const Path & path);
288/* Constructs a unique store path name. */ 292/* Constructs a unique store path name. */
289Path makeStorePath(const string & type, 293Path makeStorePath(const string & type,
290 const Hash & hash, const string & name); 294 const Hash & hash, const string & name);
291 295
292Path makeOutputPath(const string & id, 296Path makeOutputPath(const string & id,
293 const Hash & hash, const string & name); 297 const Hash & hash, const string & name);
294 298
diff --git a/nix/libstore/worker-protocol.hh b/nix/libstore/worker-protocol.hh
index 4b040b77ce6..d037d7402ed 100644
--- a/nix/libstore/worker-protocol.hh
+++ b/nix/libstore/worker-protocol.hh
@@ -42,7 +42,8 @@ typedef enum {
42 wopQueryValidPaths = 31, 42 wopQueryValidPaths = 31,
43 wopQuerySubstitutablePaths = 32, 43 wopQuerySubstitutablePaths = 32,
44 wopQueryValidDerivers = 33, 44 wopQueryValidDerivers = 33,
45 wopOptimiseStore = 34 45 wopOptimiseStore = 34,
46 wopVerifyStore = 35
46} WorkerOp; 47} WorkerOp;
47 48
48 49
diff --git a/nix/libutil/hash.cc b/nix/libutil/hash.cc
index 050446610f0..2da00a53de0 100644
--- a/nix/libutil/hash.cc
+++ b/nix/libutil/hash.cc
@@ -84,7 +84,7 @@ string printHash(const Hash & hash)
84 return string(buf, hash.hashSize * 2); 84 return string(buf, hash.hashSize * 2);
85} 85}
86 86
87 87
88Hash parseHash(HashType ht, const string & s) 88Hash parseHash(HashType ht, const string & s)
89{ 89{
90 Hash hash(ht); 90 Hash hash(ht);
@@ -92,7 +92,7 @@ Hash parseHash(HashType ht, const string & s)
92 throw Error(format("invalid hash `%1%'") % s); 92 throw Error(format("invalid hash `%1%'") % s);
93 for (unsigned int i = 0; i < hash.hashSize; i++) { 93 for (unsigned int i = 0; i < hash.hashSize; i++) {
94 string s2(s, i * 2, 2); 94 string s2(s, i * 2, 2);
95 if (!isxdigit(s2[0]) || !isxdigit(s2[1])) 95 if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
96 throw Error(format("invalid hash `%1%'") % s); 96 throw Error(format("invalid hash `%1%'") % s);
97 std::istringstream str(s2); 97 std::istringstream str(s2);
98 int n; 98 int n;
@@ -103,24 +103,6 @@ Hash parseHash(HashType ht, const string & s)
103} 103}
104 104
105 105
106static unsigned char divMod(unsigned char * bytes, unsigned char y)
107{
108 unsigned int borrow = 0;
109
110 int pos = Hash::maxHashSize - 1;
111 while (pos >= 0 && !bytes[pos]) --pos;
112
113 for ( ; pos >= 0; --pos) {
114 unsigned int s = bytes[pos] + (borrow << 8);
115 unsigned int d = s / y;
116 borrow = s % y;
117 bytes[pos] = d;
118 }
119
120 return borrow;
121}
122
123
124unsigned int hashLength32(const Hash & hash) 106unsigned int hashLength32(const Hash & hash)
125{ 107{
126 return (hash.hashSize * 8 - 1) / 5 + 1; 108 return (hash.hashSize * 8 - 1) / 5 + 1;
@@ -136,19 +118,19 @@ string printHash32(const Hash & hash)
136 Hash hash2(hash); 118 Hash hash2(hash);
137 unsigned int len = hashLength32(hash); 119 unsigned int len = hashLength32(hash);
138 120
139 const char * chars = base32Chars.data(); 121 string s;
140 122 s.reserve(len);
141 string s(len, '0'); 123
142 124 for (int n = len - 1; n >= 0; n--) {
143 int pos = len - 1; 125 unsigned int b = n * 5;
144 while (pos >= 0) { 126 unsigned int i = b / 8;
145 unsigned char digit = divMod(hash2.hash, 32); 127 unsigned int j = b % 8;
146 s[pos--] = chars[digit]; 128 unsigned char c =
129 (hash.hash[i] >> j)
130 | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
131 s.push_back(base32Chars[c & 0x1f]);
147 } 132 }
148 133
149 for (unsigned int i = 0; i < hash2.maxHashSize; ++i)
150 assert(hash2.hash[i] == 0);
151
152 return s; 134 return s;
153} 135}
154 136
@@ -159,51 +141,24 @@ string printHash16or32(const Hash & hash)
159} 141}
160 142
161 143
162static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
163{
164 unsigned char carry = 0;
165
166 for (int pos = 0; pos < maxSize; ++pos) {
167 unsigned int m = bytes[pos] * y + carry;
168 bytes[pos] = m & 0xff;
169 carry = m >> 8;
170 }
171
172 return carry;
173}
174
175
176static bool add(unsigned char * bytes, unsigned char y, int maxSize)
177{
178 unsigned char carry = y;
179
180 for (int pos = 0; pos < maxSize; ++pos) {
181 unsigned int m = bytes[pos] + carry;
182 bytes[pos] = m & 0xff;
183 carry = m >> 8;
184 if (carry == 0) break;
185 }
186
187 return carry;
188}
189
190
191Hash parseHash32(HashType ht, const string & s) 144Hash parseHash32(HashType ht, const string & s)
192{ 145{
193 Hash hash(ht); 146 Hash hash(ht);
147 unsigned int len = hashLength32(ht);
148 assert(s.size() == len);
194 149
195 const char * chars = base32Chars.data(); 150 for (unsigned int n = 0; n < len; ++n) {
196 151 char c = s[len - n - 1];
197 for (unsigned int i = 0; i < s.length(); ++i) {
198 char c = s[i];
199 unsigned char digit; 152 unsigned char digit;
200 for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ 153 for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
201 if (chars[digit] == c) break; 154 if (base32Chars[digit] == c) break;
202 if (digit >= 32) 155 if (digit >= 32)
203 throw Error(format("invalid base-32 hash `%1%'") % s); 156 throw Error(format("invalid base-32 hash '%1%'") % s);
204 if (mul(hash.hash, 32, hash.hashSize) || 157 unsigned int b = n * 5;
205 add(hash.hash, digit, hash.hashSize)) 158 unsigned int i = b / 8;
206 throw Error(format("base-32 hash `%1%' is too large") % s); 159 unsigned int j = b % 8;
160 hash.hash[i] |= digit << j;
161 if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
207 } 162 }
208 163
209 return hash; 164 return hash;
@@ -299,7 +254,7 @@ Hash hashFile(HashType ht, const Path & path)
299 if (n == -1) throw SysError(format("reading file `%1%'") % path); 254 if (n == -1) throw SysError(format("reading file `%1%'") % path);
300 update(ht, ctx, buf, n); 255 update(ht, ctx, buf, n);
301 } 256 }
302 257
303 finish(ht, ctx, hash.hash); 258 finish(ht, ctx, hash.hash);
304 return hash; 259 return hash;
305} 260}
@@ -311,7 +266,7 @@ HashSink::HashSink(HashType ht) : ht(ht)
311 bytes = 0; 266 bytes = 0;
312 start(ht, *ctx); 267 start(ht, *ctx);
313} 268}
314 269
315HashSink::~HashSink() 270HashSink::~HashSink()
316{ 271{
317 bufPos = 0; 272 bufPos = 0;
@@ -369,7 +324,7 @@ HashType parseHashType(const string & s)
369 else return htUnknown; 324 else return htUnknown;
370} 325}
371 326
372 327
373string printHashType(HashType ht) 328string printHashType(HashType ht)
374{ 329{
375 if (ht == htMD5) return "md5"; 330 if (ht == htMD5) return "md5";
@@ -378,5 +333,5 @@ string printHashType(HashType ht)
378 else throw Error("cannot print unknown hash type"); 333 else throw Error("cannot print unknown hash type");
379} 334}
380 335
381 336
382} 337}
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index a4a1ddb12a0..dab4235b04f 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -19,6 +19,10 @@
19#include <sys/syscall.h> 19#include <sys/syscall.h>
20#endif 20#endif
21 21
22#ifdef __linux__
23#include <sys/prctl.h>
24#endif
25
22 26
23extern char * * environ; 27extern char * * environ;
24 28
@@ -189,8 +193,12 @@ Path readLink(const Path & path)
189 if (!S_ISLNK(st.st_mode)) 193 if (!S_ISLNK(st.st_mode))
190 throw Error(format("`%1%' is not a symlink") % path); 194 throw Error(format("`%1%' is not a symlink") % path);
191 char buf[st.st_size]; 195 char buf[st.st_size];
192 if (readlink(path.c_str(), buf, st.st_size) != st.st_size) 196 ssize_t rlsize = readlink(path.c_str(), buf, st.st_size);
193 throw SysError(format("reading symbolic link `%1%'") % path); 197 if (rlsize == -1)
198 throw SysError(format("reading symbolic link '%1%'") % path);
199 else if (rlsize > st.st_size)
200 throw Error(format("symbolic link ‘%1%’ size overflow %2% > %3%")
201 % path % rlsize % st.st_size);
194 return string(buf, st.st_size); 202 return string(buf, st.st_size);
195} 203}
196 204
@@ -260,8 +268,8 @@ void writeFile(const Path & path, const string & s)
260{ 268{
261 AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666); 269 AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
262 if (fd == -1) 270 if (fd == -1)
263 throw SysError(format("opening file `%1%'") % path); 271 throw SysError(format("opening file '%1%'") % path);
264 writeFull(fd, (unsigned char *) s.data(), s.size()); 272 writeFull(fd, s);
265} 273}
266 274
267 275
@@ -288,7 +296,7 @@ string readLine(int fd)
288void writeLine(int fd, string s) 296void writeLine(int fd, string s)
289{ 297{
290 s += '\n'; 298 s += '\n';
291 writeFull(fd, (const unsigned char *) s.data(), s.size()); 299 writeFull(fd, s);
292} 300}
293 301
294 302
@@ -478,18 +486,13 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs)
478} 486}
479 487
480 488
481static void defaultWriteToStderr(const unsigned char * buf, size_t count)
482{
483 writeFull(STDERR_FILENO, buf, count);
484}
485
486
487void writeToStderr(const string & s) 489void writeToStderr(const string & s)
488{ 490{
489 try { 491 try {
490 auto p = _writeToStderr; 492 if (_writeToStderr)
491 if (!p) p = defaultWriteToStderr; 493 _writeToStderr((const unsigned char *) s.data(), s.size());
492 p((const unsigned char *) s.data(), s.size()); 494 else
495 writeFull(STDERR_FILENO, s);
493 } catch (SysError & e) { 496 } catch (SysError & e) {
494 /* Ignore failing writes to stderr if we're in an exception 497 /* Ignore failing writes to stderr if we're in an exception
495 handler, otherwise throw an exception. We need to ignore 498 handler, otherwise throw an exception. We need to ignore
@@ -501,7 +504,7 @@ void writeToStderr(const string & s)
501} 504}
502 505
503 506
504void (*_writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr; 507void (*_writeToStderr) (const unsigned char * buf, size_t count) = 0;
505 508
506 509
507void readFull(int fd, unsigned char * buf, size_t count) 510void readFull(int fd, unsigned char * buf, size_t count)
@@ -535,6 +538,12 @@ void writeFull(int fd, const unsigned char * buf, size_t count)
535} 538}
536 539
537 540
541void writeFull(int fd, const string & s)
542{
543 writeFull(fd, (const unsigned char *) s.data(), s.size());
544}
545
546
538string drainFD(int fd) 547string drainFD(int fd)
539{ 548{
540 string result; 549 string result;
@@ -867,6 +876,10 @@ pid_t startProcess(std::function<void()> fun,
867 if (pid == 0) { 876 if (pid == 0) {
868 _writeToStderr = 0; 877 _writeToStderr = 0;
869 try { 878 try {
879#if __linux__
880 if (dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
881 throw SysError("setting death signal");
882#endif
870 restoreAffinity(); 883 restoreAffinity();
871 fun(); 884 fun();
872 } catch (std::exception & e) { 885 } catch (std::exception & e) {
@@ -884,16 +897,19 @@ pid_t startProcess(std::function<void()> fun,
884} 897}
885 898
886 899
900std::vector<const char *> stringsToCharPtrs(const Strings & ss)
901{
902 std::vector<const char *> res;
903 for (auto & s : ss) res.push_back(s.c_str());
904 res.push_back(0);
905 return res;
906}
907
908
887string runProgram(Path program, bool searchPath, const Strings & args) 909string runProgram(Path program, bool searchPath, const Strings & args)
888{ 910{
889 checkInterrupt(); 911 checkInterrupt();
890 912
891 std::vector<const char *> cargs; /* careful with c_str()! */
892 cargs.push_back(program.c_str());
893 for (Strings::const_iterator i = args.begin(); i != args.end(); ++i)
894 cargs.push_back(i->c_str());
895 cargs.push_back(0);
896
897 /* Create a pipe. */ 913 /* Create a pipe. */
898 Pipe pipe; 914 Pipe pipe;
899 pipe.create(); 915 pipe.create();
@@ -903,6 +919,10 @@ string runProgram(Path program, bool searchPath, const Strings & args)
903 if (dup2(pipe.writeSide, STDOUT_FILENO) == -1) 919 if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
904 throw SysError("dupping stdout"); 920 throw SysError("dupping stdout");
905 921
922 Strings args_(args);
923 args_.push_front(program);
924 auto cargs = stringsToCharPtrs(args_);
925
906 if (searchPath) 926 if (searchPath)
907 execvp(program.c_str(), (char * *) &cargs[0]); 927 execvp(program.c_str(), (char * *) &cargs[0]);
908 else 928 else
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index 0ad0026711e..6a84ed88518 100644
--- a/nix/libutil/util.hh
+++ b/nix/libutil/util.hh
@@ -171,6 +171,7 @@ extern void (*_writeToStderr) (const unsigned char * buf, size_t count);
171 requested number of bytes. */ 171 requested number of bytes. */
172void readFull(int fd, unsigned char * buf, size_t count); 172void readFull(int fd, unsigned char * buf, size_t count);
173void writeFull(int fd, const unsigned char * buf, size_t count); 173void writeFull(int fd, const unsigned char * buf, size_t count);
174void writeFull(int fd, const string & s);
174 175
175MakeError(EndOfFile, Error) 176MakeError(EndOfFile, Error)
176 177
@@ -280,6 +281,11 @@ string runProgram(Path program, bool searchPath = false,
280 281
281MakeError(ExecError, Error) 282MakeError(ExecError, Error)
282 283
284/* Convert a list of strings to a null-terminated vector of char
285 *'s. The result must not be accessed beyond the lifetime of the
286 list of strings. */
287std::vector<const char *> stringsToCharPtrs(const Strings & ss);
288
283/* Close all file descriptors except stdin, stdout, stderr, and those 289/* Close all file descriptors except stdin, stdout, stderr, and those
284 listed in the given set. Good practice in child processes. */ 290 listed in the given set. Good practice in child processes. */
285void closeMostFDs(const set<int> & exceptions); 291void closeMostFDs(const set<int> & exceptions);
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index e42d602a3af..2b89190dbe0 100644
--- a/nix/nix-daemon/nix-daemon.cc
+++ b/nix/nix-daemon/nix-daemon.cc
@@ -641,11 +641,23 @@ static void performOp(bool trusted, unsigned int clientVersion,
641 } 641 }
642 642
643 case wopOptimiseStore: 643 case wopOptimiseStore:
644 startWork(); 644 startWork();
645 store->optimiseStore(); 645 store->optimiseStore();
646 stopWork(); 646 stopWork();
647 writeInt(1, to); 647 writeInt(1, to);
648 break; 648 break;
649
650 case wopVerifyStore: {
651 bool checkContents = readInt(from) != 0;
652 bool repair = readInt(from) != 0;
653 startWork();
654 if (repair && !trusted)
655 throw Error("you are not privileged to repair paths");
656 bool errors = store->verifyStore(checkContents, repair);
657 stopWork();
658 writeInt(errors, to);
659 break;
660 }
649 661
650 default: 662 default:
651 throw Error(format("invalid operation %1%") % op); 663 throw Error(format("invalid operation %1%") % op);
@@ -743,6 +755,8 @@ static void processConnection(bool trusted)
743 assert(!canSendStderr); 755 assert(!canSendStderr);
744 }; 756 };
745 757
758 canSendStderr = false;
759 _isInterrupted = false;
746 printMsg(lvlDebug, format("%1% operations") % opCount); 760 printMsg(lvlDebug, format("%1% operations") % opCount);
747} 761}
748 762
@@ -791,6 +805,9 @@ bool matchUser(const string & user, const string & group, const Strings & users)
791 805
792static void daemonLoop() 806static void daemonLoop()
793{ 807{
808 if (chdir("/") == -1)
809 throw SysError("cannot change current directory");
810
794 /* Get rid of children automatically; don't let them become 811 /* Get rid of children automatically; don't let them become
795 zombies. */ 812 zombies. */
796 setSigChldAction(true); 813 setSigChldAction(true);
@@ -819,7 +836,8 @@ static void daemonLoop()
819 /* Urgh, sockaddr_un allows path names of only 108 characters. 836 /* Urgh, sockaddr_un allows path names of only 108 characters.
820 So chdir to the socket directory so that we can pass a 837 So chdir to the socket directory so that we can pass a
821 relative path name. */ 838 relative path name. */
822 chdir(dirOf(socketPath).c_str()); 839 if (chdir(dirOf(socketPath).c_str()) == -1)
840 throw SysError("cannot change current directory");
823 Path socketPathRel = "./" + baseNameOf(socketPath); 841 Path socketPathRel = "./" + baseNameOf(socketPath);
824 842
825 struct sockaddr_un addr; 843 struct sockaddr_un addr;
@@ -839,7 +857,8 @@ static void daemonLoop()
839 if (res == -1) 857 if (res == -1)
840 throw SysError(format("cannot bind to socket `%1%'") % socketPath); 858 throw SysError(format("cannot bind to socket `%1%'") % socketPath);
841 859
842 chdir("/"); /* back to the root */ 860 if (chdir("/") == -1) /* back to the root */
861 throw SysError("cannot change current directory");
843 862
844 if (listen(fdSocket, 5) == -1) 863 if (listen(fdSocket, 5) == -1)
845 throw SysError(format("cannot listen on socket `%1%'") % socketPath); 864 throw SysError(format("cannot listen on socket `%1%'") % socketPath);
@@ -943,7 +962,6 @@ void run(Strings args)
943 if (arg == "--daemon") /* ignored for backwards compatibility */; 962 if (arg == "--daemon") /* ignored for backwards compatibility */;
944 } 963 }
945 964
946 chdir("/");
947 daemonLoop(); 965 daemonLoop();
948} 966}
949 967