Support sandbox builds by non-root users
This allows an unprivileged user to perform builds on a diverted store
(i.e. where the physical store location differs from the logical
location).
Example:
  $ NIX_LOG_DIR=/tmp/log NIX_REMOTE="local?real=/tmp/store&state=/tmp/var" nix-build -E \
    'with import <nixpkgs> {}; runCommand "foo" { buildInputs = [procps nettools]; } "id; ps; ifconfig; echo $out > $out"'
will do a build in the Nix store physically in /tmp/store but
logically in /nix/store (and thus using substituters for the latter).
			
			
This commit is contained in:
		
							parent
							
								
									2f8b0e557b
								
							
						
					
					
						commit
						5e51ffb1c2
					
				
					 3 changed files with 69 additions and 53 deletions
				
			
		|  | @ -436,12 +436,11 @@ private: | ||||||
|     AutoCloseFD fdUserLock; |     AutoCloseFD fdUserLock; | ||||||
| 
 | 
 | ||||||
|     string user; |     string user; | ||||||
|     uid_t uid; |     uid_t uid = 0; | ||||||
|     gid_t gid; |     gid_t gid = 0; | ||||||
|     std::vector<gid_t> supplementaryGIDs; |     std::vector<gid_t> supplementaryGIDs; | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     UserLock(); |  | ||||||
|     ~UserLock(); |     ~UserLock(); | ||||||
| 
 | 
 | ||||||
|     void acquire(); |     void acquire(); | ||||||
|  | @ -450,8 +449,8 @@ public: | ||||||
|     void kill(); |     void kill(); | ||||||
| 
 | 
 | ||||||
|     string getUser() { return user; } |     string getUser() { return user; } | ||||||
|     uid_t getUID() { return uid; } |     uid_t getUID() { assert(uid); return uid; } | ||||||
|     uid_t getGID() { return gid; } |     uid_t getGID() { assert(gid); return gid; } | ||||||
|     std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; } |     std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; } | ||||||
| 
 | 
 | ||||||
|     bool enabled() { return uid != 0; } |     bool enabled() { return uid != 0; } | ||||||
|  | @ -462,12 +461,6 @@ public: | ||||||
| PathSet UserLock::lockedPaths; | PathSet UserLock::lockedPaths; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| UserLock::UserLock() |  | ||||||
| { |  | ||||||
|     uid = gid = 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| UserLock::~UserLock() | UserLock::~UserLock() | ||||||
| { | { | ||||||
|     release(); |     release(); | ||||||
|  | @ -768,6 +761,9 @@ private: | ||||||
|     /* Whether this is a fixed-output derivation. */ |     /* Whether this is a fixed-output derivation. */ | ||||||
|     bool fixedOutput; |     bool fixedOutput; | ||||||
| 
 | 
 | ||||||
|  |     /* Whether to run the build in a private network namespace. */ | ||||||
|  |     bool privateNetwork = false; | ||||||
|  | 
 | ||||||
|     typedef void (DerivationGoal::*GoalState)(); |     typedef void (DerivationGoal::*GoalState)(); | ||||||
|     GoalState state; |     GoalState state; | ||||||
| 
 | 
 | ||||||
|  | @ -1269,16 +1265,13 @@ void DerivationGoal::tryToBuild() | ||||||
| { | { | ||||||
|     trace("trying to build"); |     trace("trying to build"); | ||||||
| 
 | 
 | ||||||
|     if (worker.store.storeDir != worker.store.realStoreDir) |  | ||||||
|         throw Error("building with a diverted Nix store is not supported"); |  | ||||||
| 
 |  | ||||||
|     /* Check for the possibility that some other goal in this process
 |     /* Check for the possibility that some other goal in this process
 | ||||||
|        has locked the output since we checked in haveDerivation(). |        has locked the output since we checked in haveDerivation(). | ||||||
|        (It can't happen between here and the lockPaths() call below |        (It can't happen between here and the lockPaths() call below | ||||||
|        because we're not allowing multi-threading.)  If so, put this |        because we're not allowing multi-threading.)  If so, put this | ||||||
|        goal to sleep until another goal finishes, then try again. */ |        goal to sleep until another goal finishes, then try again. */ | ||||||
|     for (auto & i : drv->outputs) |     for (auto & i : drv->outputs) | ||||||
|         if (pathIsLockedByMe(i.second.path)) { |         if (pathIsLockedByMe(worker.store.toRealPath(i.second.path))) { | ||||||
|             debug(format("putting derivation ‘%1%’ to sleep because ‘%2%’ is locked by another goal") |             debug(format("putting derivation ‘%1%’ to sleep because ‘%2%’ is locked by another goal") | ||||||
|                 % drvPath % i.second.path); |                 % drvPath % i.second.path); | ||||||
|             worker.waitForAnyGoal(shared_from_this()); |             worker.waitForAnyGoal(shared_from_this()); | ||||||
|  | @ -1290,7 +1283,11 @@ void DerivationGoal::tryToBuild() | ||||||
|        can't acquire the lock, then continue; hopefully some other |        can't acquire the lock, then continue; hopefully some other | ||||||
|        goal can start a build, and if not, the main loop will sleep a |        goal can start a build, and if not, the main loop will sleep a | ||||||
|        few seconds and then retry this goal. */ |        few seconds and then retry this goal. */ | ||||||
|     if (!outputLocks.lockPaths(drv->outputPaths(), "", false)) { |     PathSet lockFiles; | ||||||
|  |     for (auto & outPath : drv->outputPaths()) | ||||||
|  |         lockFiles.insert(worker.store.toRealPath(outPath)); | ||||||
|  | 
 | ||||||
|  |     if (!outputLocks.lockPaths(lockFiles, "", false)) { | ||||||
|         worker.waitForAWhile(shared_from_this()); |         worker.waitForAWhile(shared_from_this()); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -1320,7 +1317,7 @@ void DerivationGoal::tryToBuild() | ||||||
|         Path path = i.second.path; |         Path path = i.second.path; | ||||||
|         if (worker.store.isValidPath(path)) continue; |         if (worker.store.isValidPath(path)) continue; | ||||||
|         debug(format("removing invalid path ‘%1%’") % path); |         debug(format("removing invalid path ‘%1%’") % path); | ||||||
|         deletePath(path); |         deletePath(worker.store.toRealPath(path)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Don't do a remote build if the derivation has the attribute
 |     /* Don't do a remote build if the derivation has the attribute
 | ||||||
|  | @ -1445,7 +1442,7 @@ void DerivationGoal::buildDone() | ||||||
| #if HAVE_STATVFS | #if HAVE_STATVFS | ||||||
|             unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
 |             unsigned long long required = 8ULL * 1024 * 1024; // FIXME: make configurable
 | ||||||
|             struct statvfs st; |             struct statvfs st; | ||||||
|             if (statvfs(worker.store.storeDir.c_str(), &st) == 0 && |             if (statvfs(worker.store.realStoreDir.c_str(), &st) == 0 && | ||||||
|                 (unsigned long long) st.f_bavail * st.f_bsize < required) |                 (unsigned long long) st.f_bavail * st.f_bsize < required) | ||||||
|                 diskFull = true; |                 diskFull = true; | ||||||
|             if (statvfs(tmpDir.c_str(), &st) == 0 && |             if (statvfs(tmpDir.c_str(), &st) == 0 && | ||||||
|  | @ -1683,6 +1680,9 @@ void DerivationGoal::startBuilder() | ||||||
|             useChroot = !fixedOutput && get(drv->env, "__noChroot") != "1"; |             useChroot = !fixedOutput && get(drv->env, "__noChroot") != "1"; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (worker.store.storeDir != worker.store.realStoreDir) | ||||||
|  |         useChroot = true; | ||||||
|  | 
 | ||||||
|     /* Construct the environment passed to the builder. */ |     /* Construct the environment passed to the builder. */ | ||||||
|     env.clear(); |     env.clear(); | ||||||
| 
 | 
 | ||||||
|  | @ -1819,10 +1819,8 @@ void DerivationGoal::startBuilder() | ||||||
| 
 | 
 | ||||||
|     /* If `build-users-group' is not empty, then we have to build as
 |     /* If `build-users-group' is not empty, then we have to build as
 | ||||||
|        one of the members of that group. */ |        one of the members of that group. */ | ||||||
|     if (settings.buildUsersGroup != "") { |     if (settings.buildUsersGroup != "" && getuid() == 0) { | ||||||
|         buildUser.acquire(); |         buildUser.acquire(); | ||||||
|         assert(buildUser.getUID() != 0); |  | ||||||
|         assert(buildUser.getGID() != 0); |  | ||||||
| 
 | 
 | ||||||
|         /* Make sure that no other processes are executing under this
 |         /* Make sure that no other processes are executing under this
 | ||||||
|            uid. */ |            uid. */ | ||||||
|  | @ -1906,7 +1904,7 @@ void DerivationGoal::startBuilder() | ||||||
|            environment using bind-mounts.  We put it in the Nix store |            environment using bind-mounts.  We put it in the Nix store | ||||||
|            to ensure that we can create hard-links to non-directory |            to ensure that we can create hard-links to non-directory | ||||||
|            inputs in the fake Nix store in the chroot (see below). */ |            inputs in the fake Nix store in the chroot (see below). */ | ||||||
|         chrootRootDir = drvPath + ".chroot"; |         chrootRootDir = worker.store.toRealPath(drvPath) + ".chroot"; | ||||||
|         deletePath(chrootRootDir); |         deletePath(chrootRootDir); | ||||||
| 
 | 
 | ||||||
|         /* Clean up the chroot directory automatically. */ |         /* Clean up the chroot directory automatically. */ | ||||||
|  | @ -1917,7 +1915,7 @@ void DerivationGoal::startBuilder() | ||||||
|         if (mkdir(chrootRootDir.c_str(), 0750) == -1) |         if (mkdir(chrootRootDir.c_str(), 0750) == -1) | ||||||
|             throw SysError(format("cannot create ‘%1%’") % chrootRootDir); |             throw SysError(format("cannot create ‘%1%’") % chrootRootDir); | ||||||
| 
 | 
 | ||||||
|         if (chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1) |         if (buildUser.enabled() && chown(chrootRootDir.c_str(), 0, buildUser.getGID()) == -1) | ||||||
|             throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir); |             throw SysError(format("cannot change ownership of ‘%1%’") % chrootRootDir); | ||||||
| 
 | 
 | ||||||
|         /* Create a writable /tmp in the chroot.  Many builders need
 |         /* Create a writable /tmp in the chroot.  Many builders need
 | ||||||
|  | @ -1960,18 +1958,19 @@ void DerivationGoal::startBuilder() | ||||||
|         createDirs(chrootStoreDir); |         createDirs(chrootStoreDir); | ||||||
|         chmod_(chrootStoreDir, 01775); |         chmod_(chrootStoreDir, 01775); | ||||||
| 
 | 
 | ||||||
|         if (chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1) |         if (buildUser.enabled() && chown(chrootStoreDir.c_str(), 0, buildUser.getGID()) == -1) | ||||||
|             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir); |             throw SysError(format("cannot change ownership of ‘%1%’") % chrootStoreDir); | ||||||
| 
 | 
 | ||||||
|         for (auto & i : inputPaths) { |         for (auto & i : inputPaths) { | ||||||
|  |             Path r = worker.store.toRealPath(i); | ||||||
|             struct stat st; |             struct stat st; | ||||||
|             if (lstat(i.c_str(), &st)) |             if (lstat(r.c_str(), &st)) | ||||||
|                 throw SysError(format("getting attributes of path ‘%1%’") % i); |                 throw SysError(format("getting attributes of path ‘%1%’") % i); | ||||||
|             if (S_ISDIR(st.st_mode)) |             if (S_ISDIR(st.st_mode)) | ||||||
|                 dirsInChroot[i] = i; |                 dirsInChroot[i] = r; | ||||||
|             else { |             else { | ||||||
|                 Path p = chrootRootDir + i; |                 Path p = chrootRootDir + i; | ||||||
|                 if (link(i.c_str(), p.c_str()) == -1) { |                 if (link(r.c_str(), p.c_str()) == -1) { | ||||||
|                     /* Hard-linking fails if we exceed the maximum
 |                     /* Hard-linking fails if we exceed the maximum
 | ||||||
|                        link count on a file (e.g. 32000 of ext3), |                        link count on a file (e.g. 32000 of ext3), | ||||||
|                        which is quite possible after a `nix-store |                        which is quite possible after a `nix-store | ||||||
|  | @ -1979,7 +1978,7 @@ void DerivationGoal::startBuilder() | ||||||
|                     if (errno != EMLINK) |                     if (errno != EMLINK) | ||||||
|                         throw SysError(format("linking ‘%1%’ to ‘%2%’") % p % i); |                         throw SysError(format("linking ‘%1%’ to ‘%2%’") % p % i); | ||||||
|                     StringSink sink; |                     StringSink sink; | ||||||
|                     dumpPath(i, sink); |                     dumpPath(r, sink); | ||||||
|                     StringSource source(*sink.s); |                     StringSource source(*sink.s); | ||||||
|                     restorePath(p, source); |                     restorePath(p, source); | ||||||
|                 } |                 } | ||||||
|  | @ -2112,6 +2111,10 @@ void DerivationGoal::startBuilder() | ||||||
|            CLONE_PARENT to ensure that the real builder is parented to |            CLONE_PARENT to ensure that the real builder is parented to | ||||||
|            us. |            us. | ||||||
|         */ |         */ | ||||||
|  | 
 | ||||||
|  |         if (!fixedOutput) | ||||||
|  |             privateNetwork = true; | ||||||
|  | 
 | ||||||
|         ProcessOptions options; |         ProcessOptions options; | ||||||
|         options.allowVfork = false; |         options.allowVfork = false; | ||||||
|         Pid helper = startProcess([&]() { |         Pid helper = startProcess([&]() { | ||||||
|  | @ -2120,7 +2123,10 @@ void DerivationGoal::startBuilder() | ||||||
|                 PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); |                 PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); | ||||||
|             if (stack == MAP_FAILED) throw SysError("allocating stack"); |             if (stack == MAP_FAILED) throw SysError("allocating stack"); | ||||||
|             int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; |             int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD; | ||||||
|             if (!fixedOutput) flags |= CLONE_NEWNET; |             if (getuid() != 0) | ||||||
|  |                 flags |= CLONE_NEWUSER; | ||||||
|  |             if (privateNetwork) | ||||||
|  |                 flags |= CLONE_NEWNET; | ||||||
|             pid_t child = clone(childEntry, stack + stackSize, flags, this); |             pid_t child = clone(childEntry, stack + stackSize, flags, this); | ||||||
|             if (child == -1 && errno == EINVAL) |             if (child == -1 && errno == EINVAL) | ||||||
|                 /* Fallback for Linux < 2.13 where CLONE_NEWPID and
 |                 /* Fallback for Linux < 2.13 where CLONE_NEWPID and
 | ||||||
|  | @ -2174,17 +2180,20 @@ void DerivationGoal::runChild() | ||||||
| #if __linux__ | #if __linux__ | ||||||
|         if (useChroot) { |         if (useChroot) { | ||||||
| 
 | 
 | ||||||
|             /* Initialise the loopback interface. */ |             if (privateNetwork) { | ||||||
|             AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)); |  | ||||||
|             if (fd == -1) throw SysError("cannot open IP socket"); |  | ||||||
| 
 | 
 | ||||||
|             struct ifreq ifr; |                 /* Initialise the loopback interface. */ | ||||||
|             strcpy(ifr.ifr_name, "lo"); |                 AutoCloseFD fd(socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)); | ||||||
|             ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING; |                 if (fd == -1) throw SysError("cannot open IP socket"); | ||||||
|             if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) |  | ||||||
|                 throw SysError("cannot set loopback interface flags"); |  | ||||||
| 
 | 
 | ||||||
|             fd.close(); |                 struct ifreq ifr; | ||||||
|  |                 strcpy(ifr.ifr_name, "lo"); | ||||||
|  |                 ifr.ifr_flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING; | ||||||
|  |                 if (ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) | ||||||
|  |                     throw SysError("cannot set loopback interface flags"); | ||||||
|  | 
 | ||||||
|  |                 fd.close(); | ||||||
|  |             } | ||||||
| 
 | 
 | ||||||
|             /* Set the hostname etc. to fixed values. */ |             /* Set the hostname etc. to fixed values. */ | ||||||
|             char hostname[] = "localhost"; |             char hostname[] = "localhost"; | ||||||
|  | @ -2266,7 +2275,7 @@ void DerivationGoal::runChild() | ||||||
|                     createDirs(dirOf(target)); |                     createDirs(dirOf(target)); | ||||||
|                     writeFile(target, ""); |                     writeFile(target, ""); | ||||||
|                 } |                 } | ||||||
|                 if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1) |                 if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) | ||||||
|                     throw SysError(format("bind mount from ‘%1%’ to ‘%2%’ failed") % source % target); |                     throw SysError(format("bind mount from ‘%1%’ to ‘%2%’ failed") % source % target); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -2284,7 +2293,8 @@ void DerivationGoal::runChild() | ||||||
|                requires the kernel to be compiled with |                requires the kernel to be compiled with | ||||||
|                CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case |                CONFIG_DEVPTS_MULTIPLE_INSTANCES=y (which is the case | ||||||
|                if /dev/ptx/ptmx exists). */ |                if /dev/ptx/ptmx exists). */ | ||||||
|             if (pathExists("/dev/pts/ptmx") && |             if (getuid() == 0 && | ||||||
|  |                 pathExists("/dev/pts/ptmx") && | ||||||
|                 !pathExists(chrootRootDir + "/dev/ptmx") |                 !pathExists(chrootRootDir + "/dev/ptmx") | ||||||
|                 && dirsInChroot.find("/dev/pts") == dirsInChroot.end()) |                 && dirsInChroot.find("/dev/pts") == dirsInChroot.end()) | ||||||
|             { |             { | ||||||
|  | @ -2587,10 +2597,10 @@ void DerivationGoal::registerOutputs() | ||||||
|                 if (buildMode == bmRepair) |                 if (buildMode == bmRepair) | ||||||
|                     replaceValidPath(path, actualPath); |                     replaceValidPath(path, actualPath); | ||||||
|                 else |                 else | ||||||
|                     if (buildMode != bmCheck && rename(actualPath.c_str(), path.c_str()) == -1) |                     if (buildMode != bmCheck && rename(actualPath.c_str(), worker.store.toRealPath(path).c_str()) == -1) | ||||||
|                         throw SysError(format("moving build output ‘%1%’ from the sandbox to the Nix store") % path); |                         throw SysError(format("moving build output ‘%1%’ from the sandbox to the Nix store") % path); | ||||||
|             } |             } | ||||||
|             if (buildMode != bmCheck) actualPath = path; |             if (buildMode != bmCheck) actualPath = worker.store.toRealPath(path); | ||||||
|         } else { |         } else { | ||||||
|             Path redirected = redirectedOutputs[path]; |             Path redirected = redirectedOutputs[path]; | ||||||
|             if (buildMode == bmRepair |             if (buildMode == bmRepair | ||||||
|  | @ -2641,8 +2651,6 @@ void DerivationGoal::registerOutputs() | ||||||
|             rewritten = true; |             rewritten = true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Activity act(*logger, lvlTalkative, format("scanning for references inside ‘%1%’") % path); |  | ||||||
| 
 |  | ||||||
|         /* Check that fixed-output derivations produced the right
 |         /* Check that fixed-output derivations produced the right
 | ||||||
|            outputs (i.e., the content hash should match the specified |            outputs (i.e., the content hash should match the specified | ||||||
|            hash). */ |            hash). */ | ||||||
|  | @ -2668,13 +2676,15 @@ void DerivationGoal::registerOutputs() | ||||||
|                     % dest % printHashType(ht) % printHash16or32(h2)); |                     % dest % printHashType(ht) % printHash16or32(h2)); | ||||||
|                 if (worker.store.isValidPath(dest)) |                 if (worker.store.isValidPath(dest)) | ||||||
|                     return; |                     return; | ||||||
|                 if (actualPath != dest) { |                 Path actualDest = worker.store.toRealPath(dest); | ||||||
|                     PathLocks outputLocks({dest}); |                 if (actualPath != actualDest) { | ||||||
|                     deletePath(dest); |                     PathLocks outputLocks({actualDest}); | ||||||
|                     if (rename(actualPath.c_str(), dest.c_str()) == -1) |                     deletePath(actualDest); | ||||||
|  |                     if (rename(actualPath.c_str(), actualDest.c_str()) == -1) | ||||||
|                         throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest); |                         throw SysError(format("moving ‘%1%’ to ‘%2%’") % actualPath % dest); | ||||||
|                 } |                 } | ||||||
|                 path = actualPath = dest; |                 path = dest; | ||||||
|  |                 actualPath = actualDest; | ||||||
|             } else { |             } else { | ||||||
|                 if (h != h2) |                 if (h != h2) | ||||||
|                     throw BuildError( |                     throw BuildError( | ||||||
|  | @ -2692,6 +2702,7 @@ void DerivationGoal::registerOutputs() | ||||||
|            contained in it.  Compute the SHA-256 NAR hash at the same |            contained in it.  Compute the SHA-256 NAR hash at the same | ||||||
|            time.  The hash is stored in the database so that we can |            time.  The hash is stored in the database so that we can | ||||||
|            verify later on whether nobody has messed with the store. */ |            verify later on whether nobody has messed with the store. */ | ||||||
|  |         Activity act(*logger, lvlTalkative, format("scanning for references inside ‘%1%’") % path); | ||||||
|         HashResult hash; |         HashResult hash; | ||||||
|         PathSet references = scanForReferences(actualPath, allPaths, hash); |         PathSet references = scanForReferences(actualPath, allPaths, hash); | ||||||
| 
 | 
 | ||||||
|  | @ -2700,7 +2711,7 @@ void DerivationGoal::registerOutputs() | ||||||
|             auto info = *worker.store.queryPathInfo(path); |             auto info = *worker.store.queryPathInfo(path); | ||||||
|             if (hash.first != info.narHash) { |             if (hash.first != info.narHash) { | ||||||
|                 if (settings.keepFailed) { |                 if (settings.keepFailed) { | ||||||
|                     Path dst = path + checkSuffix; |                     Path dst = worker.store.toRealPath(path + checkSuffix); | ||||||
|                     deletePath(dst); |                     deletePath(dst); | ||||||
|                     if (rename(actualPath.c_str(), dst.c_str())) |                     if (rename(actualPath.c_str(), dst.c_str())) | ||||||
|                         throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst); |                         throw SysError(format("renaming ‘%1%’ to ‘%2%’") % actualPath % dst); | ||||||
|  | @ -2743,7 +2754,7 @@ void DerivationGoal::registerOutputs() | ||||||
|                 /* Our requisites are the union of the closures of our references. */ |                 /* Our requisites are the union of the closures of our references. */ | ||||||
|                 for (auto & i : references) |                 for (auto & i : references) | ||||||
|                     /* Don't call computeFSClosure on ourselves. */ |                     /* Don't call computeFSClosure on ourselves. */ | ||||||
|                     if (actualPath != i) |                     if (path != i) | ||||||
|                         worker.store.computeFSClosure(i, used); |                         worker.store.computeFSClosure(i, used); | ||||||
|             } else |             } else | ||||||
|                 used = references; |                 used = references; | ||||||
|  | @ -2775,8 +2786,7 @@ void DerivationGoal::registerOutputs() | ||||||
|         checkRefs("disallowedRequisites", false, true); |         checkRefs("disallowedRequisites", false, true); | ||||||
| 
 | 
 | ||||||
|         if (curRound == nrRounds) { |         if (curRound == nrRounds) { | ||||||
|             worker.store.optimisePath(path); // FIXME: combine with scanForReferences()
 |             worker.store.optimisePath(actualPath); // FIXME: combine with scanForReferences()
 | ||||||
| 
 |  | ||||||
|             worker.markContentsGood(path); |             worker.markContentsGood(path); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -39,6 +39,7 @@ public: | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | // FIXME: not thread-safe!
 | ||||||
| bool pathIsLockedByMe(const Path & path); | bool pathIsLockedByMe(const Path & path); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -503,6 +503,11 @@ public: | ||||||
|         const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); |         const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false); | ||||||
| 
 | 
 | ||||||
|     virtual Path getRealStoreDir() { return storeDir; } |     virtual Path getRealStoreDir() { return storeDir; } | ||||||
|  | 
 | ||||||
|  |     Path toRealPath(const Path & storePath) | ||||||
|  |     { | ||||||
|  |         return getRealStoreDir() + "/" + baseNameOf(storePath); | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue