summaryrefslogtreecommitdiff
path: root/nix
diff options
context:
space:
mode:
Diffstat (limited to 'nix')
-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
14 files changed, 255 insertions, 234 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;
diff --git a/nix/libutil/hash.cc b/nix/libutil/hash.cc
index 050446610f..2da00a53de 100644
--- a/nix/libutil/hash.cc
+++ b/nix/libutil/hash.cc
@@ -84,7 +84,7 @@ string printHash(const Hash & hash)
return string(buf, hash.hashSize * 2);
}
-
+
Hash parseHash(HashType ht, const string & s)
{
Hash hash(ht);
@@ -92,7 +92,7 @@ Hash parseHash(HashType ht, const string & s)
throw Error(format("invalid hash `%1%'") % s);
for (unsigned int i = 0; i < hash.hashSize; i++) {
string s2(s, i * 2, 2);
- if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
+ if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw Error(format("invalid hash `%1%'") % s);
std::istringstream str(s2);
int n;
@@ -103,24 +103,6 @@ Hash parseHash(HashType ht, const string & s)
}
-static unsigned char divMod(unsigned char * bytes, unsigned char y)
-{
- unsigned int borrow = 0;
-
- int pos = Hash::maxHashSize - 1;
- while (pos >= 0 && !bytes[pos]) --pos;
-
- for ( ; pos >= 0; --pos) {
- unsigned int s = bytes[pos] + (borrow << 8);
- unsigned int d = s / y;
- borrow = s % y;
- bytes[pos] = d;
- }
-
- return borrow;
-}
-
-
unsigned int hashLength32(const Hash & hash)
{
return (hash.hashSize * 8 - 1) / 5 + 1;
@@ -136,19 +118,19 @@ string printHash32(const Hash & hash)
Hash hash2(hash);
unsigned int len = hashLength32(hash);
- const char * chars = base32Chars.data();
-
- string s(len, '0');
-
- int pos = len - 1;
- while (pos >= 0) {
- unsigned char digit = divMod(hash2.hash, 32);
- s[pos--] = chars[digit];
+ string s;
+ s.reserve(len);
+
+ for (int n = len - 1; n >= 0; n--) {
+ unsigned int b = n * 5;
+ unsigned int i = b / 8;
+ unsigned int j = b % 8;
+ unsigned char c =
+ (hash.hash[i] >> j)
+ | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j));
+ s.push_back(base32Chars[c & 0x1f]);
}
- for (unsigned int i = 0; i < hash2.maxHashSize; ++i)
- assert(hash2.hash[i] == 0);
-
return s;
}
@@ -159,51 +141,24 @@ string printHash16or32(const Hash & hash)
}
-static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
-{
- unsigned char carry = 0;
-
- for (int pos = 0; pos < maxSize; ++pos) {
- unsigned int m = bytes[pos] * y + carry;
- bytes[pos] = m & 0xff;
- carry = m >> 8;
- }
-
- return carry;
-}
-
-
-static bool add(unsigned char * bytes, unsigned char y, int maxSize)
-{
- unsigned char carry = y;
-
- for (int pos = 0; pos < maxSize; ++pos) {
- unsigned int m = bytes[pos] + carry;
- bytes[pos] = m & 0xff;
- carry = m >> 8;
- if (carry == 0) break;
- }
-
- return carry;
-}
-
-
Hash parseHash32(HashType ht, const string & s)
{
Hash hash(ht);
+ unsigned int len = hashLength32(ht);
+ assert(s.size() == len);
- const char * chars = base32Chars.data();
-
- for (unsigned int i = 0; i < s.length(); ++i) {
- char c = s[i];
+ for (unsigned int n = 0; n < len; ++n) {
+ char c = s[len - n - 1];
unsigned char digit;
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
- if (chars[digit] == c) break;
+ if (base32Chars[digit] == c) break;
if (digit >= 32)
- throw Error(format("invalid base-32 hash `%1%'") % s);
- if (mul(hash.hash, 32, hash.hashSize) ||
- add(hash.hash, digit, hash.hashSize))
- throw Error(format("base-32 hash `%1%' is too large") % s);
+ throw Error(format("invalid base-32 hash '%1%'") % s);
+ unsigned int b = n * 5;
+ unsigned int i = b / 8;
+ unsigned int j = b % 8;
+ hash.hash[i] |= digit << j;
+ if (i < hash.hashSize - 1) hash.hash[i + 1] |= digit >> (8 - j);
}
return hash;
@@ -299,7 +254,7 @@ Hash hashFile(HashType ht, const Path & path)
if (n == -1) throw SysError(format("reading file `%1%'") % path);
update(ht, ctx, buf, n);
}
-
+
finish(ht, ctx, hash.hash);
return hash;
}
@@ -311,7 +266,7 @@ HashSink::HashSink(HashType ht) : ht(ht)
bytes = 0;
start(ht, *ctx);
}
-
+
HashSink::~HashSink()
{
bufPos = 0;
@@ -369,7 +324,7 @@ HashType parseHashType(const string & s)
else return htUnknown;
}
-
+
string printHashType(HashType ht)
{
if (ht == htMD5) return "md5";
@@ -378,5 +333,5 @@ string printHashType(HashType ht)
else throw Error("cannot print unknown hash type");
}
-
+
}
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index a4a1ddb12a..dab4235b04 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -19,6 +19,10 @@
#include <sys/syscall.h>
#endif
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
+
extern char * * environ;
@@ -189,8 +193,12 @@ Path readLink(const Path & path)
if (!S_ISLNK(st.st_mode))
throw Error(format("`%1%' is not a symlink") % path);
char buf[st.st_size];
- if (readlink(path.c_str(), buf, st.st_size) != st.st_size)
- throw SysError(format("reading symbolic link `%1%'") % path);
+ ssize_t rlsize = readlink(path.c_str(), buf, st.st_size);
+ if (rlsize == -1)
+ throw SysError(format("reading symbolic link '%1%'") % path);
+ else if (rlsize > st.st_size)
+ throw Error(format("symbolic link ‘%1%’ size overflow %2% > %3%")
+ % path % rlsize % st.st_size);
return string(buf, st.st_size);
}
@@ -260,8 +268,8 @@ void writeFile(const Path & path, const string & s)
{
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (fd == -1)
- throw SysError(format("opening file `%1%'") % path);
- writeFull(fd, (unsigned char *) s.data(), s.size());
+ throw SysError(format("opening file '%1%'") % path);
+ writeFull(fd, s);
}
@@ -288,7 +296,7 @@ string readLine(int fd)
void writeLine(int fd, string s)
{
s += '\n';
- writeFull(fd, (const unsigned char *) s.data(), s.size());
+ writeFull(fd, s);
}
@@ -478,18 +486,13 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs)
}
-static void defaultWriteToStderr(const unsigned char * buf, size_t count)
-{
- writeFull(STDERR_FILENO, buf, count);
-}
-
-
void writeToStderr(const string & s)
{
try {
- auto p = _writeToStderr;
- if (!p) p = defaultWriteToStderr;
- p((const unsigned char *) s.data(), s.size());
+ if (_writeToStderr)
+ _writeToStderr((const unsigned char *) s.data(), s.size());
+ else
+ writeFull(STDERR_FILENO, s);
} catch (SysError & e) {
/* Ignore failing writes to stderr if we're in an exception
handler, otherwise throw an exception. We need to ignore
@@ -501,7 +504,7 @@ void writeToStderr(const string & s)
}
-void (*_writeToStderr) (const unsigned char * buf, size_t count) = defaultWriteToStderr;
+void (*_writeToStderr) (const unsigned char * buf, size_t count) = 0;
void readFull(int fd, unsigned char * buf, size_t count)
@@ -535,6 +538,12 @@ void writeFull(int fd, const unsigned char * buf, size_t count)
}
+void writeFull(int fd, const string & s)
+{
+ writeFull(fd, (const unsigned char *) s.data(), s.size());
+}
+
+
string drainFD(int fd)
{
string result;
@@ -867,6 +876,10 @@ pid_t startProcess(std::function<void()> fun,
if (pid == 0) {
_writeToStderr = 0;
try {
+#if __linux__
+ if (dieWithParent && prctl(PR_SET_PDEATHSIG, SIGKILL) == -1)
+ throw SysError("setting death signal");
+#endif
restoreAffinity();
fun();
} catch (std::exception & e) {
@@ -884,16 +897,19 @@ pid_t startProcess(std::function<void()> fun,
}
+std::vector<const char *> stringsToCharPtrs(const Strings & ss)
+{
+ std::vector<const char *> res;
+ for (auto & s : ss) res.push_back(s.c_str());
+ res.push_back(0);
+ return res;
+}
+
+
string runProgram(Path program, bool searchPath, const Strings & args)
{
checkInterrupt();
- std::vector<const char *> cargs; /* careful with c_str()! */
- cargs.push_back(program.c_str());
- for (Strings::const_iterator i = args.begin(); i != args.end(); ++i)
- cargs.push_back(i->c_str());
- cargs.push_back(0);
-
/* Create a pipe. */
Pipe pipe;
pipe.create();
@@ -903,6 +919,10 @@ string runProgram(Path program, bool searchPath, const Strings & args)
if (dup2(pipe.writeSide, STDOUT_FILENO) == -1)
throw SysError("dupping stdout");
+ Strings args_(args);
+ args_.push_front(program);
+ auto cargs = stringsToCharPtrs(args_);
+
if (searchPath)
execvp(program.c_str(), (char * *) &cargs[0]);
else
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index 0ad0026711..6a84ed8851 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);
requested number of bytes. */
void readFull(int fd, unsigned char * buf, size_t count);
void writeFull(int fd, const unsigned char * buf, size_t count);
+void writeFull(int fd, const string & s);
MakeError(EndOfFile, Error)
@@ -280,6 +281,11 @@ string runProgram(Path program, bool searchPath = false,
MakeError(ExecError, Error)
+/* Convert a list of strings to a null-terminated vector of char
+ *'s. The result must not be accessed beyond the lifetime of the
+ list of strings. */
+std::vector<const char *> stringsToCharPtrs(const Strings & ss);
+
/* Close all file descriptors except stdin, stdout, stderr, and those
listed in the given set. Good practice in child processes. */
void closeMostFDs(const set<int> & exceptions);
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index e42d602a3a..2b89190dbe 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,
}
case wopOptimiseStore:
- startWork();
- store->optimiseStore();
- stopWork();
- writeInt(1, to);
- break;
+ startWork();
+ store->optimiseStore();
+ stopWork();
+ writeInt(1, to);
+ break;
+
+ case wopVerifyStore: {
+ bool checkContents = readInt(from) != 0;
+ bool repair = readInt(from) != 0;
+ startWork();
+ if (repair && !trusted)
+ throw Error("you are not privileged to repair paths");
+ bool errors = store->verifyStore(checkContents, repair);
+ stopWork();
+ writeInt(errors, to);
+ break;
+ }
default:
throw Error(format("invalid operation %1%") % op);
@@ -743,6 +755,8 @@ static void processConnection(bool trusted)
assert(!canSendStderr);
};
+ canSendStderr = false;
+ _isInterrupted = false;
printMsg(lvlDebug, format("%1% operations") % opCount);
}
@@ -791,6 +805,9 @@ bool matchUser(const string & user, const string & group, const Strings & users)
static void daemonLoop()
{
+ if (chdir("/") == -1)
+ throw SysError("cannot change current directory");
+
/* Get rid of children automatically; don't let them become
zombies. */
setSigChldAction(true);
@@ -819,7 +836,8 @@ static void daemonLoop()
/* Urgh, sockaddr_un allows path names of only 108 characters.
So chdir to the socket directory so that we can pass a
relative path name. */
- chdir(dirOf(socketPath).c_str());
+ if (chdir(dirOf(socketPath).c_str()) == -1)
+ throw SysError("cannot change current directory");
Path socketPathRel = "./" + baseNameOf(socketPath);
struct sockaddr_un addr;
@@ -839,7 +857,8 @@ static void daemonLoop()
if (res == -1)
throw SysError(format("cannot bind to socket `%1%'") % socketPath);
- chdir("/"); /* back to the root */
+ if (chdir("/") == -1) /* back to the root */
+ throw SysError("cannot change current directory");
if (listen(fdSocket, 5) == -1)
throw SysError(format("cannot listen on socket `%1%'") % socketPath);
@@ -943,7 +962,6 @@ void run(Strings args)
if (arg == "--daemon") /* ignored for backwards compatibility */;
}
- chdir("/");
daemonLoop();
}