aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThéophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com>2024-04-11 13:41:49 +0200
committerK900 <me@0upti.me>2024-04-11 15:43:58 +0300
commit07b627cc6d1224df75ea2d4979c4c83bb67392f1 (patch)
treed4c46eb2c75b55154f78d9ef1cb1ffbbe84edf72
parentaeee22e5a17404b10dd14b5289e302eaf546e1aa (diff)
Merge pull request #10456 from NixOS/fixpermdeniedbind
Fix adding symlink to the sandbox paths (cherry-picked from commit da1e977bf48cff2a635034c85e7c13878e38efc2) Change-Id: I221c85a38180800ec6552d2e86a88df48398fad8
-rw-r--r--src/libstore/build/local-derivation-goal.cc26
-rw-r--r--tests/functional/linux-sandbox.sh3
-rw-r--r--tests/functional/symlink-derivation.nix36
3 files changed, 58 insertions, 7 deletions
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index c373ed27f..588fe33ba 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -389,21 +389,33 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()
#if __linux__
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) {
- if (optional && errno == ENOENT)
+
+ 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);
+ };
+
+ auto maybeSt = maybeLstat(source);
+ if (!maybeSt) {
+ if (optional)
return;
else
throw SysError("getting attributes of path '%1%'", source);
}
- if (S_ISDIR(st.st_mode))
+ auto st = *maybeSt;
+
+ 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
diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh
index ff7d257bd..04209277b 100644
--- a/tests/functional/linux-sandbox.sh
+++ b/tests/functional/linux-sandbox.sh
@@ -73,3 +73,6 @@ testCert missing fixed-output "$nocert"
# Cert in sandbox when ssl-cert-file is set to an existing file
testCert present fixed-output "$cert"
+
+# Symlinks should be added in the sandbox directly and not followed
+nix-sandbox-build symlink-derivation.nix
diff --git a/tests/functional/symlink-derivation.nix b/tests/functional/symlink-derivation.nix
new file mode 100644
index 000000000..17ba37424
--- /dev/null
+++ b/tests/functional/symlink-derivation.nix
@@ -0,0 +1,36 @@
+with import ./config.nix;
+
+let
+ foo_in_store = builtins.toFile "foo" "foo";
+ foo_symlink = mkDerivation {
+ name = "foo-symlink";
+ buildCommand = ''
+ ln -s ${foo_in_store} $out
+ '';
+ };
+ symlink_to_not_in_store = mkDerivation {
+ name = "symlink-to-not-in-store";
+ buildCommand = ''
+ ln -s ${builtins.toString ./.} $out
+ '';
+ };
+in
+mkDerivation {
+ name = "depends-on-symlink";
+ buildCommand = ''
+ (
+ set -x
+
+ # `foo_symlink` should be a symlink pointing to `foo_in_store`
+ [[ -L ${foo_symlink} ]]
+ [[ $(readlink ${foo_symlink}) == ${foo_in_store} ]]
+
+ # `symlink_to_not_in_store` should be a symlink pointing to `./.`, which
+ # is not available in the sandbox
+ [[ -L ${symlink_to_not_in_store} ]]
+ [[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]]
+ (! ls ${symlink_to_not_in_store}/)
+ )
+ echo "Success!" > $out
+ '';
+}