From 2239953894cd1a35d79ba5bbfd0b265337149f76 Mon Sep 17 00:00:00 2001 From: Luke Granger-Brown Date: Fri, 4 Dec 2020 04:09:14 +0000 Subject: [PATCH] When moving paths out of sandbox, ensure we have write permission. If we're a single-user installation of Nix, then we won't have root superpowers to just ignore the permission bits. This means that we'll need permission on the directory (if it's a directory) that we're moving in order to move it with rename, because it must update the ".." directory entry. Fixes #4295. This is already working in master, thanks to e913a2989fd7dfabfd93c89fd4295386eda4277f (and followup cleanups). --- src/libstore/build.cc | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 6dafc08b6..4ba9a5385 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -3193,6 +3193,26 @@ PathSet parseReferenceSpecifiers(Store & store, const BasicDerivation & drv, con } +/* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if + it's a directory and we're not root (to be able to update the + directory's parent link ".."). */ +static void movePath(const Path & src, const Path & dst) +{ + auto st = lstat(src); + + bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); + + if (changePerm) + chmod_(src, st.st_mode | S_IWUSR); + + if (rename(src.c_str(), dst.c_str())) + throw SysError("renaming '%1%' to '%2%'", src, dst); + + if (changePerm) + chmod_(dst, st.st_mode); +} + + void DerivationGoal::registerOutputs() { /* When using a build hook, the build hook can register the output @@ -3233,9 +3253,8 @@ void DerivationGoal::registerOutputs() /* Move output paths from the chroot to the Nix store. */ if (buildMode == bmRepair) replaceValidPath(path, actualPath); - else - 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); + else if (buildMode != bmCheck) + movePath(actualPath, worker.store.toRealPath(path)); } if (buildMode != bmCheck) actualPath = worker.store.toRealPath(path); }