about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--pkgs/build-support/build-sandbox/src/setup.c63
-rw-r--r--tests/sandbox.nix16
2 files changed, 79 insertions, 0 deletions
diff --git a/pkgs/build-support/build-sandbox/src/setup.c b/pkgs/build-support/build-sandbox/src/setup.c
index a23983c8..da6c65c7 100644
--- a/pkgs/build-support/build-sandbox/src/setup.c
+++ b/pkgs/build-support/build-sandbox/src/setup.c
@@ -189,6 +189,62 @@ static bool bind_file(const char *path)
     return true;
 }
 
+static bool makelinks(const char *from, const char *to)
+{
+    char linktarget[PATH_MAX];
+    char *target, *tmp;
+    ssize_t linksize;
+    bool result;
+
+    if (strcmp(from, to) == 0)
+        return true;
+
+    if ((linksize = readlink(from, linktarget, PATH_MAX)) == -1) {
+        if (errno == EINVAL)
+            // Not a symbolic link
+            return true;
+
+        fprintf(stderr, "reading link %s: %s\n", from, strerror(errno));
+        return false;
+    }
+
+    linktarget[linksize] = '\0';
+
+    if ((target = get_mount_target(from)) == NULL)
+        return false;
+
+    if ((tmp = strdup(target)) == NULL) {
+        fprintf(stderr, "strdup of %s: %s\n", target, strerror(errno));
+        free(target);
+        return false;
+    }
+
+    if (!makedirs(dirname(tmp), true)) {
+        free(target);
+        free(tmp);
+        return false;
+    }
+
+    free(tmp);
+
+    if (cache_path(cached_paths, target)) {
+        if (symlink(linktarget, target) == -1) {
+            if (errno == EEXIST)
+                goto recurse;
+
+            fprintf(stderr, "creating symlink from %s to %s: %s\n",
+                    target, linktarget, strerror(errno));
+            free(target);
+            return false;
+        }
+    }
+
+recurse:
+    result = makelinks(linktarget, to);
+    free(target);
+    return result;
+}
+
 bool bind_mount(const char *path, bool restricted, bool resolve)
 {
     int mflags = MS_BIND | MS_REC;
@@ -207,6 +263,13 @@ bool bind_mount(const char *path, bool restricted, bool resolve)
     if ((target = get_mount_target(resolve ? src : path)) == NULL)
         return false;
 
+    if (resolve) {
+        if (!makelinks(path, src)) {
+            free(target);
+            return false;
+        }
+    }
+
     if (!makedirs(target, false)) {
         free(target);
         return false;
diff --git a/tests/sandbox.nix b/tests/sandbox.nix
index 20ae88d6..f3013b2e 100644
--- a/tests/sandbox.nix
+++ b/tests/sandbox.nix
@@ -18,6 +18,12 @@
     systemd.services.display-manager.enable = false;
 
     environment.systemPackages = let
+      mkNestedLinksTo = drv: let
+        mkLink = name: to: pkgs.runCommand name { inherit to; } ''
+          ln -s "$to" "$out"
+        '';
+      in mkLink "nested-1" (mkLink "nested-2" (mkLink "nested-3" drv));
+
       testPackage = pkgs.runCommand "test-sandbox" {
         program = ''
           #!${pkgs.stdenv.shell} -ex
@@ -31,6 +37,16 @@
           # Should fail because we can't access the host's PATH
           ! echo foo | grep -qF foo
 
+          # Check whether we can access files behind nested storepaths that are
+          # symlinks.
+          lfile="$(< ${mkNestedLinksTo (pkgs.writeText "target" "file")})"
+          test "$lfile" = file
+          ldir="$(< ${mkNestedLinksTo (pkgs.runCommand "target" {} ''
+            mkdir -p "$out"
+            echo dir > "$out/canary"
+          '')}/canary)"
+          test "$ldir" = dir
+
           export PATH=/run/baz-test-sandbox/bin
           echo foo > /home/foo/existing/bar
           test ! -d /home/foo/nonexisting