Fixes #400: Linux sandboxing and relative symbolic links.
Symlink resolution did not work in all cases and broke ./compile.sh on certain Linux distros.

--
MOS_MIGRATED_REVID=101775459
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java
index 26a5650..6cd142c 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java
@@ -51,11 +51,9 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -205,15 +203,9 @@
       Path target = mount.getValue();
       validateAndAddMount(sandboxPath, fixedMounts, source, target);
 
-      // Iteratively resolve symlinks and mount the whole chain. Take care not to run into a cyclic
-      // symlink - when we already processed the source once, we can exit the loop. Skyframe will
-      // catch cyclic symlinks for declared inputs, but this won't help if there is one in the parts
-      // of the host system that we mount.
-      Set<Path> seenSources = new HashSet<>();
-      while (source.isSymbolicLink() && seenSources.add(source)) {
-        source = source.getParentDirectory().getRelative(source.readSymbolicLink());
+      if (source.isSymbolicLink()) {
+        source = source.resolveSymbolicLinks();
         target = sandboxPath.getRelative(source.asFragment().relativeTo("/"));
-
         validateAndAddMount(sandboxPath, fixedMounts, source, target);
       }
     }
diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh
index 24f6baa..14f110a 100755
--- a/src/test/shell/bazel/bazel_sandboxing_test.sh
+++ b/src/test/shell/bazel/bazel_sandboxing_test.sh
@@ -73,6 +73,12 @@
   ln -sf cyclic2 examples/genrule/cyclic1
   ln -sf cyclic1 examples/genrule/cyclic2
 
+  # Create relative symlinks.
+  mkdir -p examples/genrule/symlinks/{a,ok/sub}
+  echo OK > examples/genrule/symlinks/ok/x.txt
+  ln -s $PWD/examples/genrule/symlinks/ok/sub examples/genrule/symlinks/a/b
+  ln -s ../x.txt examples/genrule/symlinks/a/b/x.txt
+
   cat << 'EOF' > examples/genrule/BUILD
 genrule(
   name = "works",
@@ -103,6 +109,13 @@
 )
 
 genrule(
+  name = "relative_symlinks",
+  srcs = [ "symlinks/a/b/x.txt" ],
+  outs = [ "relative_symlinks.txt" ],
+  cmd = "cat $(location :symlinks/a/b/x.txt) > $@",
+)
+
+genrule(
   name = "breaks1",
   srcs = [ "a.txt" ],
   outs = [ "breaks1.txt" ],
@@ -169,6 +182,23 @@
     || fail "Genrule didn't produce output: examples/genrule:tools_work"
 }
 
+# Test for #400: Linux sandboxing and relative symbolic links.
+#
+# let A = examples/genrule/symlinks/a/b/x.txt -> ../x.txt
+# where   examples/genrule/symlinks/a/b -> examples/genrule/symlinks/ok/sub
+# thus the realpath of A is example/genrule/symlinks/ok/x.txt
+# but if the code doesn't correctly resolve intermediate symlinks and instead
+# uses string operations to handle ".." parts, it will arrive at:
+# examples/genrule/symlinks/a/x.txt, which is wrong.
+#
+function test_sandbox_relative_symlink_in_inputs() {
+  bazel build --genrule_strategy=sandboxed \
+    examples/genrule:relative_symlinks \
+    || fail "Hermetic genrule failed: examples/genrule:relative_symlinks"
+  [ -f "${BAZEL_GENFILES_DIR}/examples/genrule/relative_symlinks.txt" ] \
+    || fail "Genrule didn't produce output: examples/genrule:relative_symlinks"
+}
+
 function test_sandbox_undeclared_deps() {
   bazel build --genrule_strategy=sandboxed \
     examples/genrule:breaks1 \