Make the Linux sandbox work with ActionInputs with absolute "exec paths".

Progress towards #3236.

RELNOTES: None.
PiperOrigin-RevId: 493542957
Change-Id: Iff396e77d7624bdb033b198068aa137397495db0
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java
index 60cae3a..3da85cc 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java
@@ -232,7 +232,11 @@
 
     SandboxInputs inputs =
         helpers.processInputFiles(
-            context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+            context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+            execRoot,
+            execRoot,
+            ImmutableList.of(),
+            null);
     SandboxOutputs outputs = helpers.getOutputs(spawn);
 
     final Path sandboxConfigPath = sandboxPath.getRelative("sandbox.sb");
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
index dbd5679..de253b0 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java
@@ -222,7 +222,11 @@
 
     SandboxInputs inputs =
         helpers.processInputFiles(
-            context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+            context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+            execRoot,
+            execRoot,
+            ImmutableList.of(),
+            null);
     SandboxOutputs outputs = helpers.getOutputs(spawn);
 
     Duration timeout = context.getTimeout();
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
index 7f95327..9c41e4f 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
@@ -48,6 +48,7 @@
 import com.google.devtools.build.lib.vfs.FileSystem;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.Symlinks;
 import java.io.File;
 import java.io.IOException;
@@ -128,6 +129,7 @@
   private final boolean sandboxfsMapSymlinkTargets;
   private final TreeDeleter treeDeleter;
   private final Reporter reporter;
+  private final ImmutableList<Root> packageRoots;
 
   /**
    * Creates a sandboxed spawn runner that uses the {@code linux-sandbox} tool.
@@ -168,6 +170,7 @@
     this.localEnvProvider = new PosixLocalEnvProvider(cmdEnv.getClientEnv());
     this.treeDeleter = treeDeleter;
     this.reporter = cmdEnv.getReporter();
+    this.packageRoots = cmdEnv.getPackageLocator().getPathEntries();
   }
 
   @Override
@@ -219,7 +222,11 @@
 
     SandboxInputs inputs =
         helpers.processInputFiles(
-            context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+            context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+            execRoot,
+            execRoot,
+            packageRoots,
+            null);
     SandboxOutputs outputs = helpers.getOutputs(spawn);
 
     Duration timeout = context.getTimeout();
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java
index 42c46dc..21f8225 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java
@@ -14,6 +14,7 @@
 
 package com.google.devtools.build.lib.sandbox;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
@@ -111,7 +112,11 @@
 
     SandboxInputs inputs =
         helpers.processInputFiles(
-            context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+            context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+            execRoot,
+            execRoot,
+            ImmutableList.of(),
+            null);
     SandboxOutputs outputs = helpers.getOutputs(spawn);
 
     if (sandboxfsProcess != null) {
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
index 67aa543..b8ae7e3 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java
@@ -20,6 +20,7 @@
 
 import com.google.auto.value.AutoValue;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
@@ -462,15 +463,49 @@
   }
 
   /**
+   * Returns the appropriate {@link RootedPath} for a Fileset symlink.
+   *
+   * <p>Filesets are weird because sometimes exec paths of the {@link ActionInput}s in them are not
+   * relative, as exec paths should be, but absolute and point to under one of the package roots. In
+   * order to handle this, if we find such an absolute exec path, we iterate over the package path
+   * entries to turn it into a {@link RootedPath}.
+   *
+   * <p>The inputs to this function should be symlinks that are contained within Filesets; in
+   * particular, this is different from "unresolved symlinks" in that Fileset contents are regular
+   * files (but implemented by symlinks in the output tree) whose contents matter and unresolved
+   * symlinks are symlinks for which the important content is the result of {@code readlink()}
+   */
+  private static RootedPath processFilesetSymlink(
+      PathFragment symlink, ImmutableList<Root> packageRoots) {
+    for (Root packageRoot : packageRoots) {
+      if (packageRoot.contains(symlink)) {
+        return RootedPath.toRootedPath(packageRoot, packageRoot.relativize(symlink));
+      }
+    }
+
+    throw new IllegalStateException(
+        String.format(
+            "absolute action input path '%s' not found under package roots",
+            symlink.getPathString()));
+  }
+
+  /**
    * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
    * host filesystem where the input files can be found.
    *
+   * @param inputMap the map of action inputs and where they should be visible in the action
+   * @param execRootPath the exec root from the point of view of the Bazel server
+   * @param withinSandboxExecRootPath the exec root from within the sandbox (different from {@code
+   *     execRootPath} because the sandbox does magic with fiile system namespaces)
+   * @param packageRoots the package path entries during this build
+   * @param sandboxSourceRoots the directory where source roots are mapped within the sandbox
    * @throws IOException if processing symlinks fails
    */
   public SandboxInputs processInputFiles(
       Map<PathFragment, ActionInput> inputMap,
       Path execRootPath,
       Path withinSandboxExecRootPath,
+      ImmutableList<Root> packageRoots,
       Path sandboxSourceRoots)
       throws IOException {
     Root withinSandboxExecRoot = Root.fromPath(withinSandboxExecRootPath);
@@ -503,7 +538,14 @@
         if (actionInput instanceof EmptyActionInput) {
           inputPath = null;
         } else {
-          inputPath = RootedPath.toRootedPath(execRoot, actionInput.getExecPath());
+          PathFragment execPath = actionInput.getExecPath();
+          if (execPath.isAbsolute()) {
+            // This happens for ActionInputs that are part of Filesets (see the Javadoc on
+            // processFilesetSymlink())
+            inputPath = processFilesetSymlink(actionInput.getExecPath(), packageRoots);
+          } else {
+            inputPath = RootedPath.toRootedPath(execRoot, actionInput.getExecPath());
+          }
         }
 
         inputFiles.put(pathFragment, inputPath);
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java
index 2ccb7de..b3b2fac 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java
@@ -15,6 +15,7 @@
 package com.google.devtools.build.lib.sandbox;
 
 import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.ActionInput;
@@ -71,7 +72,11 @@
 
     SandboxInputs readablePaths =
         helpers.processInputFiles(
-            context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+            context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+            execRoot,
+            execRoot,
+            ImmutableList.of(),
+            null);
 
     ImmutableSet.Builder<Path> writablePaths = ImmutableSet.builder();
     writablePaths.addAll(getWritableDirs(execRoot, environment));
diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
index fd65a37..2599bf0 100644
--- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java
@@ -19,6 +19,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Stopwatch;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.hash.HashCode;
 import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
@@ -187,7 +188,11 @@
           Profiler.instance().profile(ProfilerTask.WORKER_SETUP, "Setting up inputs")) {
         inputFiles =
             helpers.processInputFiles(
-                context.getInputMapping(PathFragment.EMPTY_FRAGMENT), execRoot, execRoot, null);
+                context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
+                execRoot,
+                execRoot,
+                ImmutableList.of(),
+                null);
       }
       SandboxOutputs outputs = helpers.getOutputs(spawn);
 
diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java
index eb5e370..dc81f1d 100644
--- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java
+++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java
@@ -101,7 +101,8 @@
             UTF_8);
 
     SandboxInputs inputs =
-        sandboxHelpers.processInputFiles(inputMap(paramFile), execRootPath, execRootPath, null);
+        sandboxHelpers.processInputFiles(
+            inputMap(paramFile), execRootPath, execRootPath, ImmutableList.of(), null);
 
     assertThat(inputs.getFiles())
         .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile"));
@@ -121,7 +122,8 @@
             PathFragment.create("_bin/say_hello"));
 
     SandboxInputs inputs =
-        sandboxHelpers.processInputFiles(inputMap(tool), execRootPath, execRootPath, null);
+        sandboxHelpers.processInputFiles(
+            inputMap(tool), execRootPath, execRootPath, ImmutableList.of(), null);
 
     assertThat(inputs.getFiles())
         .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello"));
@@ -167,14 +169,15 @@
               try {
                 var unused =
                     sandboxHelpers.processInputFiles(
-                        inputMap(input), customExecRoot, customExecRoot, null);
+                        inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null);
                 finishProcessingSemaphore.release();
               } catch (IOException e) {
                 throw new IllegalArgumentException(e);
               }
             });
     var unused =
-        sandboxHelpers.processInputFiles(inputMap(input), customExecRoot, customExecRoot, null);
+        sandboxHelpers.processInputFiles(
+            inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null);
     finishProcessingSemaphore.release();
     future.get();