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 <Artturin@artturin.com>
This commit is contained in:
Théophane Hufschmitt 2024-04-10 15:19:18 +02:00
parent 872d93eb13
commit 913db9f738

View file

@ -24,6 +24,7 @@
#include <regex> #include <regex>
#include <queue> #include <queue>
#include <sys/stat.h>
#include <sys/un.h> #include <sys/un.h>
#include <fcntl.h> #include <fcntl.h>
#include <termios.h> #include <termios.h>
@ -396,20 +397,30 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
static void doBind(const Path & source, const Path & target, bool optional = false) { static void doBind(const Path & source, const Path & target, bool optional = false) {
debug("bind mounting '%1%' to '%2%'", source, target); debug("bind mounting '%1%' to '%2%'", source, target);
struct stat st; 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) if (optional && errno == ENOENT)
return; return;
else else
throw SysError("getting attributes of path '%1%'", source); throw SysError("getting attributes of path '%1%'", source);
} }
if (S_ISDIR(st.st_mode)) if (S_ISDIR(st.st_mode)) {
createDirs(target); 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)); createDirs(dirOf(target));
writeFile(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 #endif