From 913db9f7385b8717d9eaf6269e9f319e78e4c564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 10 Apr 2024 15:19:18 +0200 Subject: [PATCH] Fix permission denied when building symlink derivation which points to a symlink out of the store Bind-mounting symlinks is apparently not possible, which is why the thing was failing. Fortunately, symlinks are small, so we can fallback to copy them at no cost. Fix https://github.com/NixOS/nix/issues/9579 Co-authored-by: Artturin --- src/libstore/build/local-derivation-goal.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index ab66195b8..68c387a9d 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -396,20 +397,30 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() static void doBind(const Path & source, const Path & target, bool optional = false) { debug("bind mounting '%1%' to '%2%'", source, target); struct stat st; - if (stat(source.c_str(), &st) == -1) { + + auto bindMount = [&]() { + if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) + throw SysError("bind mount from '%1%' to '%2%' failed", source, target); + }; + + if (lstat(source.c_str(), &st) == -1) { if (optional && errno == ENOENT) return; else throw SysError("getting attributes of path '%1%'", source); } - if (S_ISDIR(st.st_mode)) + if (S_ISDIR(st.st_mode)) { createDirs(target); - else { + bindMount(); + } else if (S_ISLNK(st.st_mode)) { + // Symlinks can (apparently) not be bind-mounted, so just copy it + createDirs(dirOf(target)); + copyFile(source, target, /* andDelete */ false); + } else { createDirs(dirOf(target)); writeFile(target, ""); + bindMount(); } - if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) - throw SysError("bind mount from '%1%' to '%2%' failed", source, target); }; #endif