Separate Fileset-symlink errors out from I/O exceptions, and propagate them everywhere.
Because remaining I/O exceptions should all be environmental, made them 36 instead of 1.
Out of respect for ulfjack@, I left "strict" mode as a legitimate exception, even though it's completely dead in production.
PiperOrigin-RevId: 374474696
diff --git a/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java b/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java
index 7fb70e8..531aca3 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/CompletionContext.java
@@ -13,16 +13,12 @@
// limitations under the License.
package com.google.devtools.build.lib.actions;
-import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.RESOLVE;
-import static com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior.RESOLVE_FULLY;
-
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
+import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehaviorWithoutError;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.IOException;
import java.util.Map;
/**
@@ -77,8 +73,7 @@
ActionInputMap inputMap,
PathResolverFactory pathResolverFactory,
Path execRoot,
- String workspaceName)
- throws IOException {
+ String workspaceName) {
ArtifactPathResolver pathResolver =
pathResolverFactory.shouldCreatePathResolverForArtifactValues()
? pathResolverFactory.createPathResolverForArtifactValues(
@@ -116,7 +111,12 @@
continue;
} else if (artifact.isFileset()) {
if (expandFilesets) {
- visitFileset(artifact, receiver, fullyResolveFilesetLinks ? RESOLVE_FULLY : RESOLVE);
+ visitFileset(
+ artifact,
+ receiver,
+ fullyResolveFilesetLinks
+ ? RelativeSymlinkBehaviorWithoutError.RESOLVE_FULLY
+ : RelativeSymlinkBehaviorWithoutError.RESOLVE);
}
} else if (artifact.isTreeArtifact()) {
ImmutableCollection<? extends Artifact> expandedArtifacts =
@@ -133,17 +133,11 @@
private void visitFileset(
Artifact filesetArtifact,
ArtifactReceiver receiver,
- RelativeSymlinkBehavior relativeSymlinkBehavior) {
+ RelativeSymlinkBehaviorWithoutError relativeSymlinkBehavior) {
ImmutableList<FilesetOutputSymlink> links = expandedFilesets.get(filesetArtifact);
- FilesetManifest filesetManifest;
- try {
- filesetManifest =
- FilesetManifest.constructFilesetManifest(
- links, PathFragment.EMPTY_FRAGMENT, relativeSymlinkBehavior);
- } catch (IOException e) {
- // Unexpected: RelativeSymlinkBehavior.RESOLVE should not throw.
- throw new IllegalStateException(e);
- }
+ FilesetManifest filesetManifest =
+ FilesetManifest.constructFilesetManifestWithoutError(
+ links, PathFragment.EMPTY_FRAGMENT, relativeSymlinkBehavior);
for (Map.Entry<PathFragment, String> mapping : filesetManifest.getEntries().entrySet()) {
String targetFile = mapping.getValue();
@@ -165,8 +159,7 @@
ActionInputMap actionInputMap,
Map<Artifact, ImmutableCollection<? extends Artifact>> expandedArtifacts,
Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets,
- String workspaceName)
- throws IOException;
+ String workspaceName);
boolean shouldCreatePathResolverForArtifactValues();
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java b/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
index 66ffc85..6fdef5e 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/FilesetManifest.java
@@ -16,7 +16,6 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.flogger.GoogleLogger;
-import com.google.devtools.build.lib.bugreport.BugReport;
import com.google.devtools.build.lib.vfs.DigestHashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
@@ -38,9 +37,7 @@
private static final int MAX_SYMLINK_TRAVERSALS = 256;
private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
- /**
- * Mode that determines how to handle relative target paths.
- */
+ /** Mode that determines how to handle relative target paths. */
public enum RelativeSymlinkBehavior {
/** Ignore any relative target paths. */
IGNORE,
@@ -52,14 +49,58 @@
RESOLVE,
/** Fully resolve all relative paths, even those pointing to internal directories. */
- RESOLVE_FULLY;
+ RESOLVE_FULLY
+ }
+
+ /**
+ * Shadow of {@link RelativeSymlinkBehavior} without the {@link RelativeSymlinkBehavior#ERROR}
+ * value for callers who know there won't be an error thrown when constructing the manifest.
+ */
+ public enum RelativeSymlinkBehaviorWithoutError {
+ /** Ignore any relative target paths. */
+ IGNORE(RelativeSymlinkBehavior.IGNORE),
+
+ /** Resolve all relative target paths. */
+ RESOLVE(RelativeSymlinkBehavior.RESOLVE),
+
+ /** Fully resolve all relative paths, even those pointing to internal directories. */
+ RESOLVE_FULLY(RelativeSymlinkBehavior.RESOLVE_FULLY);
+
+ private final RelativeSymlinkBehavior target;
+
+ RelativeSymlinkBehaviorWithoutError(RelativeSymlinkBehavior target) {
+ this.target = target;
+ }
+ }
+
+ /**
+ * Constructs a FilesetManifest from the given {@code outputSymlinks}, processing relative
+ * symlinks according to {@code relSymlinkBehavior}. Use when {@link
+ * RelativeSymlinkBehavior#ERROR} is guaranteed not to be the behavior.
+ */
+ public static FilesetManifest constructFilesetManifestWithoutError(
+ List<FilesetOutputSymlink> outputSymlinks,
+ PathFragment targetPrefix,
+ RelativeSymlinkBehaviorWithoutError relSymlinkBehavior) {
+ try {
+ return constructFilesetManifest(outputSymlinks, targetPrefix, relSymlinkBehavior.target);
+ } catch (ForbiddenRelativeSymlinkException e) {
+ throw new IllegalStateException(
+ "Can't throw forbidden symlink exception unless behavior is ERROR: "
+ + relSymlinkBehavior
+ + ", "
+ + targetPrefix
+ + ", "
+ + outputSymlinks,
+ e);
+ }
}
public static FilesetManifest constructFilesetManifest(
List<FilesetOutputSymlink> outputSymlinks,
PathFragment targetPrefix,
RelativeSymlinkBehavior relSymlinkBehavior)
- throws IOException {
+ throws ForbiddenRelativeSymlinkException {
LinkedHashMap<PathFragment, String> entries = new LinkedHashMap<>();
Map<PathFragment, String> relativeLinks = new HashMap<>();
Map<String, FileArtifactValue> artifactValues = new HashMap<>();
@@ -91,12 +132,10 @@
PathFragment fullLocation,
RelativeSymlinkBehavior relSymlinkBehavior,
Map<PathFragment, String> relativeLinks)
- throws IOException {
+ throws ForbiddenRelativeSymlinkException {
switch (relSymlinkBehavior) {
case ERROR:
- IOException ioException = new IOException("runfiles target is not absolute: " + artifact);
- BugReport.sendBugReport(ioException);
- throw ioException;
+ throw new ForbiddenRelativeSymlinkException(artifact);
case RESOLVE:
case RESOLVE_FULLY:
if (!relativeLinks.containsKey(fullLocation)) { // Keep consistent behavior: no overwriting.
@@ -110,8 +149,9 @@
/** Fully resolve relative symlinks including internal directory symlinks. */
private static void fullyResolveRelativeSymlinks(
- Map<PathFragment, String> entries, Map<PathFragment, String> relativeLinks, boolean absolute)
- throws IOException {
+ Map<PathFragment, String> entries,
+ Map<PathFragment, String> relativeLinks,
+ boolean absolute) {
try {
// Construct an in-memory Filesystem containing all the non-relative-symlink entries in the
// Fileset. Treat these as regular files in the filesystem whose contents are the "real"
@@ -141,15 +181,7 @@
addSymlinks(root, entries, absolute);
} catch (IOException e) {
- // TODO(janakr): make this crash hard if there are no bug reports.
- BugReport.sendBugReport(
- new IllegalStateException(
- "Unexpected IOException from InMemoryFileSystem operations for "
- + entries
- + " with "
- + relativeLinks,
- e));
- throw e;
+ throw new IllegalStateException("InMemoryFileSystem can't throw", e);
}
}
@@ -181,8 +213,7 @@
Map<PathFragment, String> entries,
Map<PathFragment, String> relativeLinks,
boolean absolute,
- RelativeSymlinkBehavior relSymlinkBehavior)
- throws IOException {
+ RelativeSymlinkBehavior relSymlinkBehavior) {
if (relSymlinkBehavior == RelativeSymlinkBehavior.RESOLVE_FULLY && !relativeLinks.isEmpty()) {
fullyResolveRelativeSymlinks(entries, relativeLinks, absolute);
} else if (relSymlinkBehavior == RelativeSymlinkBehavior.RESOLVE) {
@@ -258,4 +289,12 @@
public Map<String, FileArtifactValue> getArtifactValues() {
return artifactValues;
}
+
+ /** Exception indicating that a relative symlink was encountered but not permitted. */
+ public static final class ForbiddenRelativeSymlinkException
+ extends ForbiddenActionInputException {
+ private ForbiddenRelativeSymlinkException(String symlinkTarget) {
+ super("Fileset symlink " + symlinkTarget + " is not absolute");
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ForbiddenActionInputException.java b/src/main/java/com/google/devtools/build/lib/actions/ForbiddenActionInputException.java
new file mode 100644
index 0000000..1464e37
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/ForbiddenActionInputException.java
@@ -0,0 +1,26 @@
+// Copyright 2021 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.actions;
+
+/**
+ * Ancestor for exceptions thrown because of a bad input: an input that is not allowed for the given
+ * spawn/execution platform (like a relative symlink in a Fileset or a directory for a platform that
+ * does not support directory inputs). Indicates a user error, rather than an I/O error.
+ */
+public class ForbiddenActionInputException extends Exception {
+ protected ForbiddenActionInputException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLine.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLine.java
index a5ad079..33e2701 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLine.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLine.java
@@ -27,7 +27,7 @@
import com.google.devtools.build.lib.actions.CommandLineExpansionException;
import com.google.devtools.build.lib.actions.CommandLineItem;
import com.google.devtools.build.lib.actions.FilesetManifest;
-import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
+import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehaviorWithoutError;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
import com.google.devtools.build.lib.actions.SingleStringArgFormatter;
import com.google.devtools.build.lib.cmdline.Label;
@@ -39,7 +39,6 @@
import com.google.devtools.build.lib.starlarkbuildapi.FileRootApi;
import com.google.devtools.build.lib.util.Fingerprint;
import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.IllegalFormatException;
@@ -354,15 +353,11 @@
fileset),
e);
}
- try {
- FilesetManifest filesetManifest =
- FilesetManifest.constructFilesetManifest(
- expandedFileSet, fileset.getExecPath(), RelativeSymlinkBehavior.IGNORE);
- for (PathFragment relativePath : filesetManifest.getEntries().keySet()) {
- expandedValues.add(new FilesetSymlinkFile(fileset, relativePath));
- }
- } catch (IOException e) {
- throw new CommandLineExpansionException("Could not expand fileset: " + e.getMessage());
+ FilesetManifest filesetManifest =
+ FilesetManifest.constructFilesetManifestWithoutError(
+ expandedFileSet, fileset.getExecPath(), RelativeSymlinkBehaviorWithoutError.IGNORE);
+ for (PathFragment relativePath : filesetManifest.getEntries().keySet()) {
+ expandedValues.add(new FilesetSymlinkFile(fileset, relativePath));
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
index 079c54a..0cfb2b7 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/AbstractSpawnStrategy.java
@@ -25,6 +25,7 @@
import com.google.devtools.build.lib.actions.ArtifactPathResolver;
import com.google.devtools.build.lib.actions.EnvironmentalExecException;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.LostInputsActionExecutionException;
import com.google.devtools.build.lib.actions.LostInputsExecException;
import com.google.devtools.build.lib.actions.MetadataProvider;
@@ -36,6 +37,7 @@
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnResult.Status;
import com.google.devtools.build.lib.actions.Spawns;
+import com.google.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
@@ -164,6 +166,13 @@
ex = e;
spawnResult = e.getSpawnResult();
// Log the Spawn and re-throw.
+ } catch (ForbiddenActionInputException e) {
+ throw new UserExecException(
+ e,
+ FailureDetail.newBuilder()
+ .setMessage("Exec failed due to forbidden input")
+ .setSpawn(FailureDetails.Spawn.newBuilder().setCode(Code.FORBIDDEN_INPUT))
+ .build());
}
SpawnLogContext spawnLogContext = actionExecutionContext.getContext(SpawnLogContext.class);
@@ -175,7 +184,7 @@
context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
context.getTimeout(),
spawnResult);
- } catch (IOException e) {
+ } catch (IOException | ForbiddenActionInputException e) {
actionExecutionContext
.getEventHandler()
.handle(
@@ -232,7 +241,8 @@
}
@Override
- public void prefetchInputs() throws IOException, InterruptedException {
+ public void prefetchInputs()
+ throws IOException, ForbiddenActionInputException, InterruptedException {
if (Spawns.shouldPrefetchInputsForLocalExecution(spawn)) {
actionExecutionContext
.getActionInputPrefetcher()
@@ -290,7 +300,7 @@
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(PathFragment baseDirectory)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
if (lazyInputMapping == null || !inputMappingBaseDirectory.equals(baseDirectory)) {
try (SilentCloseable c =
Profiler.instance().profile("AbstractSpawnStrategy.getInputMapping")) {
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnCache.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnCache.java
index b47c867..46429eb4 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnCache.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnCache.java
@@ -15,6 +15,7 @@
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
@@ -164,7 +165,7 @@
* instance's {@link CacheHandle#store} is a no-op.
*/
CacheHandle lookup(Spawn spawn, SpawnExecutionContext context)
- throws ExecException, IOException, InterruptedException;
+ throws ExecException, IOException, InterruptedException, ForbiddenActionInputException;
/**
* Returns whether this cache implementation makes sense to use together with dynamic execution.
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
index c3c8783..545e952 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnInputExpander.java
@@ -25,8 +25,10 @@
import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact;
import com.google.devtools.build.lib.actions.FileArtifactValue;
import com.google.devtools.build.lib.actions.FilesetManifest;
+import com.google.devtools.build.lib.actions.FilesetManifest.ForbiddenRelativeSymlinkException;
import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
import com.google.devtools.build.lib.actions.Spawn;
@@ -110,7 +112,7 @@
MetadataProvider actionFileCache,
ArtifactExpander artifactExpander,
PathFragment baseDirectory)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
Map<PathFragment, Map<PathFragment, Artifact>> rootsAndMappings =
runfilesSupplier.getMappings();
@@ -161,7 +163,7 @@
MetadataProvider actionFileCache,
ArtifactExpander artifactExpander,
PathFragment baseDirectory)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
Map<PathFragment, ActionInput> inputMap = new HashMap<>();
addRunfilesToInputs(
inputMap, runfilesSupplier, actionFileCache, artifactExpander, baseDirectory);
@@ -169,10 +171,10 @@
}
private static void failIfDirectory(MetadataProvider actionFileCache, ActionInput input)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
FileArtifactValue metadata = actionFileCache.getMetadata(input);
if (metadata != null && !metadata.getType().isFile()) {
- throw new IOException("Not a file: " + input.getExecPathString());
+ throw new ForbiddenNonFileException(input);
}
}
@@ -181,7 +183,7 @@
Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesetMappings,
Map<PathFragment, ActionInput> inputMappings,
PathFragment baseDirectory)
- throws IOException {
+ throws ForbiddenRelativeSymlinkException {
for (Artifact fileset : filesetMappings.keySet()) {
addFilesetManifest(
fileset.getExecPath(),
@@ -198,7 +200,7 @@
ImmutableList<FilesetOutputSymlink> filesetLinks,
Map<PathFragment, ActionInput> inputMappings,
PathFragment baseDirectory)
- throws IOException {
+ throws ForbiddenRelativeSymlinkException {
Preconditions.checkState(filesetArtifact.isFileset(), filesetArtifact);
FilesetManifest filesetManifest =
FilesetManifest.constructFilesetManifest(filesetLinks, location, relSymlinkBehavior);
@@ -230,9 +232,7 @@
* {@link ActionInput}s. The returned map does not contain tree artifacts as they are expanded to
* file artifacts.
*
- * <p>The returned map never contains {@code null} values; it uses {@link #EMPTY_FILE} for empty
- * files, which is an instance of {@link
- * com.google.devtools.build.lib.actions.cache.VirtualActionInput}.
+ * <p>The returned map never contains {@code null} values.
*
* <p>The returned map contains all runfiles, but not the {@code MANIFEST}.
*/
@@ -241,7 +241,7 @@
ArtifactExpander artifactExpander,
PathFragment baseDirectory,
MetadataProvider actionInputFileCache)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
TreeMap<PathFragment, ActionInput> inputMap = new TreeMap<>();
addInputs(inputMap, spawn, artifactExpander, baseDirectory);
addRunfilesToInputs(
@@ -253,4 +253,14 @@
addFilesetManifests(spawn.getFilesetMappings(), inputMap, baseDirectory);
return inputMap;
}
+
+ /**
+ * Exception signaling that an input was not a regular file: most likely a directory. This
+ * exception is currently never thrown in practice since we do not enforce "strict" mode.
+ */
+ private static final class ForbiddenNonFileException extends ForbiddenActionInputException {
+ ForbiddenNonFileException(ActionInput input) {
+ super("Not a file: " + input.getExecPathString());
+ }
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
index 7b79837..185cfcd 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/SpawnRunner.java
@@ -18,6 +18,7 @@
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.ArtifactPathResolver;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.FutureSpawn;
import com.google.devtools.build.lib.actions.LostInputsExecException;
import com.google.devtools.build.lib.actions.MetadataProvider;
@@ -159,7 +160,7 @@
* again. I suppose we could require implementations to memoize getInputMapping (but not compute
* it eagerly), and that may change in the future.
*/
- void prefetchInputs() throws IOException, InterruptedException;
+ void prefetchInputs() throws IOException, InterruptedException, ForbiddenActionInputException;
/**
* The input file metadata cache for this specific spawn, which can be used to efficiently
@@ -210,7 +211,7 @@
* is not the same as the execroot.
*/
SortedMap<PathFragment, ActionInput> getInputMapping(PathFragment baseDirectory)
- throws IOException;
+ throws IOException, ForbiddenActionInputException;
/** Reports a progress update to the Spawn strategy. */
void report(ProgressStatus state, String name);
@@ -247,7 +248,7 @@
* @throws ExecException if the request is malformed
*/
default FutureSpawn execAsync(Spawn spawn, SpawnExecutionContext context)
- throws InterruptedException, IOException, ExecException {
+ throws InterruptedException, IOException, ExecException, ForbiddenActionInputException {
// TODO(ulfjack): Remove this default implementation. [exec-async]
return FutureSpawn.immediate(exec(spawn, context));
}
@@ -264,7 +265,7 @@
* @throws ExecException if the request is malformed
*/
SpawnResult exec(Spawn spawn, SpawnExecutionContext context)
- throws InterruptedException, IOException, ExecException;
+ throws InterruptedException, IOException, ExecException, ForbiddenActionInputException;
/** Returns whether this SpawnRunner supports executing the given Spawn. */
boolean canExec(Spawn spawn);
diff --git a/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java
index 2ad58e1..613c3dd 100644
--- a/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/exec/local/LocalSpawnRunner.java
@@ -28,6 +28,7 @@
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.ResourceManager;
import com.google.devtools.build.lib.actions.ResourceManager.ResourceHandle;
import com.google.devtools.build.lib.actions.Spawn;
@@ -118,7 +119,7 @@
@Override
public SpawnResult exec(Spawn spawn, SpawnExecutionContext context)
- throws IOException, InterruptedException, ExecException {
+ throws IOException, InterruptedException, ExecException, ForbiddenActionInputException {
runfilesTreeUpdater.updateRunfilesDirectory(
execRoot,
@@ -186,7 +187,8 @@
setState(State.PARSING);
}
- public SpawnResult run() throws InterruptedException, IOException {
+ public SpawnResult run()
+ throws InterruptedException, IOException, ForbiddenActionInputException {
if (localExecutionOptions.localRetriesOnCrash == 0) {
return runOnce();
} else {
@@ -212,7 +214,8 @@
}
}
- private SpawnResult runOnce() throws InterruptedException, IOException {
+ private SpawnResult runOnce()
+ throws InterruptedException, IOException, ForbiddenActionInputException {
try {
return start();
} catch (InterruptedException | InterruptedIOException e) {
@@ -224,6 +227,9 @@
} catch (Error e) {
stepLog(SEVERE, e, UNHANDLED_EXCEPTION_MSG);
throw e;
+ } catch (ForbiddenActionInputException e) {
+ stepLog(WARNING, e, "Bad input file");
+ throw e;
} catch (IOException e) {
stepLog(SEVERE, e, "Local I/O error");
throw e;
@@ -275,7 +281,8 @@
}
/** Parse the request and run it locally. */
- private SpawnResult start() throws InterruptedException, IOException {
+ private SpawnResult start()
+ throws InterruptedException, IOException, ForbiddenActionInputException {
logger.atInfo().log("starting local subprocess #%d, argv: %s", id, debugCmdString());
FileOutErr outErr = context.getFileOutErr();
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
index aecd293..060e20b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteExecutionService.java
@@ -38,6 +38,7 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.Spawns;
import com.google.devtools.build.lib.actions.UserExecException;
@@ -215,7 +216,7 @@
/** Creates a new {@link RemoteAction} instance from spawn. */
public RemoteAction buildRemoteAction(Spawn spawn, SpawnExecutionContext context)
- throws IOException, UserExecException {
+ throws IOException, UserExecException, ForbiddenActionInputException {
SortedMap<PathFragment, ActionInput> inputMap = remotePathResolver.getInputMapping(context);
final MerkleTree merkleTree =
MerkleTree.build(inputMap, context.getMetadataProvider(), execRoot, digestUtil);
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
index d6b3e1c..340210b 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnCache.java
@@ -22,6 +22,7 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.FileArtifactValue;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnMetrics;
import com.google.devtools.build.lib.actions.SpawnResult;
@@ -76,7 +77,7 @@
@Override
public CacheHandle lookup(Spawn spawn, SpawnExecutionContext context)
- throws InterruptedException, IOException, ExecException {
+ throws InterruptedException, IOException, ExecException, ForbiddenActionInputException {
if (!Spawns.mayBeCached(spawn)
|| (!Spawns.mayBeCachedRemotely(spawn) && useRemoteCache(options))) {
// returning SpawnCache.NO_RESULT_NO_STORE in case the caching is disabled or in case
diff --git a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
index 0dc4c8a..ea31f5d 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/RemoteSpawnRunner.java
@@ -32,6 +32,7 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnMetrics;
import com.google.devtools.build.lib.actions.SpawnResult;
@@ -155,7 +156,7 @@
@Override
public SpawnResult exec(Spawn spawn, SpawnExecutionContext context)
- throws ExecException, InterruptedException, IOException {
+ throws ExecException, InterruptedException, IOException, ForbiddenActionInputException {
Preconditions.checkArgument(
Spawns.mayBeExecutedRemotely(spawn), "Spawn can't be executed remotely. This is a bug.");
@@ -395,7 +396,7 @@
}
private SpawnResult execLocally(Spawn spawn, SpawnExecutionContext context)
- throws ExecException, InterruptedException, IOException {
+ throws ExecException, InterruptedException, IOException, ForbiddenActionInputException {
RemoteLocalFallbackRegistry localFallbackRegistry =
context.getContext(RemoteLocalFallbackRegistry.class);
checkNotNull(localFallbackRegistry, "Expected a RemoteLocalFallbackRegistry to be registered");
@@ -413,7 +414,7 @@
SpawnExecutionContext context,
boolean uploadLocalResults,
IOException cause)
- throws ExecException, InterruptedException, IOException {
+ throws ExecException, InterruptedException, IOException, ForbiddenActionInputException {
// Regardless of cause, if we are interrupted, we should stop without displaying a user-visible
// failure/stack trace.
if (Thread.currentThread().isInterrupted()) {
@@ -520,7 +521,7 @@
@VisibleForTesting
SpawnResult execLocallyAndUpload(
RemoteAction action, Spawn spawn, SpawnExecutionContext context, boolean uploadLocalResults)
- throws ExecException, IOException, InterruptedException {
+ throws ExecException, IOException, ForbiddenActionInputException, InterruptedException {
Map<Path, Long> ctimesBefore = getInputCtimes(action.getInputMap());
SpawnResult result = execLocally(spawn, context);
Map<Path, Long> ctimesAfter = getInputCtimes(action.getInputMap());
diff --git a/src/main/java/com/google/devtools/build/lib/remote/common/RemotePathResolver.java b/src/main/java/com/google/devtools/build/lib/remote/common/RemotePathResolver.java
index d067820..5b063c6 100644
--- a/src/main/java/com/google/devtools/build/lib/remote/common/RemotePathResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/remote/common/RemotePathResolver.java
@@ -17,6 +17,7 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.ActionInputHelper;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -40,7 +41,7 @@
* ActionInput}.
*/
SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
- throws IOException;
+ throws IOException, ForbiddenActionInputException;
/** Resolves the output path relative to input root for the given {@link Path}. */
String localPathToOutputPath(Path path);
@@ -94,7 +95,7 @@
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
return context.getInputMapping(PathFragment.EMPTY_FRAGMENT);
}
@@ -151,7 +152,7 @@
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(SpawnExecutionContext context)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
// The "root directory" of the action from the point of view of RBE is the parent directory of
// the execroot locally. This is so that paths of artifacts in external repositories don't
// start with an uplevel reference.
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
index 3d14c22..f7e03f0 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionExecutionMetadata;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.ResourceManager;
import com.google.devtools.build.lib.actions.ResourceManager.ResourceHandle;
import com.google.devtools.build.lib.actions.Spawn;
@@ -91,6 +92,11 @@
createFailureDetail(
"I/O exception during sandboxed execution", Code.EXECUTION_IO_EXCEPTION);
throw new UserExecException(e, failureDetail);
+ } catch (ForbiddenActionInputException e) {
+ FailureDetail failureDetail =
+ createFailureDetail(
+ "Forbidden input found during sandboxed execution", Code.FORBIDDEN_INPUT);
+ throw new UserExecException(e, failureDetail);
}
}
@@ -105,11 +111,11 @@
}
protected abstract SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, ExecException, InterruptedException;
+ throws IOException, ExecException, InterruptedException, ForbiddenActionInputException;
private SpawnResult runSpawn(
Spawn originalSpawn, SandboxedSpawn sandbox, SpawnExecutionContext context)
- throws IOException, InterruptedException {
+ throws IOException, ForbiddenActionInputException, InterruptedException {
try {
try (SilentCloseable c = Profiler.instance().profile("sandbox.createFileSystem")) {
sandbox.createFileSystem();
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 c32638e..f510d35 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
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.Spawns;
import com.google.devtools.build.lib.exec.TreeDeleter;
@@ -206,7 +207,7 @@
@Override
protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, InterruptedException {
+ throws IOException, ForbiddenActionInputException, InterruptedException {
// Each invocation of "exec" gets its own sandbox base.
// Note that the value returned by context.getId() is only unique inside one given SpawnRunner,
// so we have to prefix our name to turn it into a globally unique value.
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 13f09e5..2fda26a 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
@@ -23,6 +23,7 @@
import com.google.common.eventbus.Subscribe;
import com.google.common.io.ByteStreams;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.Spawns;
import com.google.devtools.build.lib.actions.UserExecException;
@@ -203,7 +204,7 @@
@Override
protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, ExecException, InterruptedException {
+ throws IOException, ExecException, InterruptedException, ForbiddenActionInputException {
// Each invocation of "exec" gets its own sandbox base, execroot and temporary directory.
Path sandboxPath =
sandboxBase.getRelative(getName()).getRelative(Integer.toString(context.getId()));
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 96158ed..79ce0e0 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
@@ -21,6 +21,7 @@
import com.google.common.io.ByteStreams;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.ExecutionRequirements;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.Spawns;
import com.google.devtools.build.lib.actions.UserExecException;
@@ -154,7 +155,7 @@
@Override
protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, ExecException, InterruptedException {
+ throws IOException, ForbiddenActionInputException, ExecException, InterruptedException {
// Each invocation of "exec" gets its own sandbox base.
// Note that the value returned by context.getId() is only unique inside one given SpawnRunner,
// so we have to prefix our name to turn it into a globally unique value.
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 dff0d99..356fc67 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
@@ -16,6 +16,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.exec.TreeDeleter;
import com.google.devtools.build.lib.exec.local.LocalEnvProvider;
@@ -76,7 +77,7 @@
@Override
protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, InterruptedException {
+ throws IOException, ForbiddenActionInputException, InterruptedException {
// Each invocation of "exec" gets its own sandbox base.
// Note that the value returned by context.getId() is only unique inside one given SpawnRunner,
// so we have to prefix our name to turn it into a globally unique value.
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
index 87542fb..d64ebcd 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java
@@ -22,6 +22,7 @@
import com.google.common.eventbus.Subscribe;
import com.google.common.flogger.GoogleLogger;
import com.google.devtools.build.lib.actions.ExecException;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnExecutedEvent;
import com.google.devtools.build.lib.actions.SpawnResult;
@@ -482,7 +483,7 @@
@Override
public SpawnResult exec(Spawn spawn, SpawnExecutionContext context)
- throws InterruptedException, IOException, ExecException {
+ throws InterruptedException, IOException, ExecException, ForbiddenActionInputException {
Instant spawnExecutionStartInstant = Instant.now();
SpawnResult spawnResult;
if (sandboxSpawnRunner.canExec(spawn)) {
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 846090f..f602fab 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
@@ -18,6 +18,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionInput;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.exec.local.LocalEnvProvider;
import com.google.devtools.build.lib.exec.local.WindowsLocalEnvProvider;
@@ -60,7 +61,7 @@
@Override
protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
- throws IOException, InterruptedException {
+ throws IOException, ForbiddenActionInputException, InterruptedException {
Path tmpDir = createActionTemp(execRoot);
Path commandTmpDir = tmpDir.getRelative("work");
commandTmpDir.createDirectory();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index 5bc72d2..39293ca 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -36,7 +36,7 @@
import com.google.devtools.build.lib.actions.FileStateType;
import com.google.devtools.build.lib.actions.FileStateValue;
import com.google.devtools.build.lib.actions.FilesetManifest;
-import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehavior;
+import com.google.devtools.build.lib.actions.FilesetManifest.RelativeSymlinkBehaviorWithoutError;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
import com.google.devtools.build.lib.actions.cache.MetadataHandler;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
@@ -203,22 +203,15 @@
Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets, PathFragment execRoot) {
Map<PathFragment, FileArtifactValue> filesetMap = new HashMap<>();
for (Map.Entry<Artifact, ImmutableList<FilesetOutputSymlink>> entry : filesets.entrySet()) {
- try {
- FilesetManifest fileset =
- FilesetManifest.constructFilesetManifest(
- entry.getValue(), execRoot, RelativeSymlinkBehavior.RESOLVE);
+ FilesetManifest fileset =
+ FilesetManifest.constructFilesetManifestWithoutError(
+ entry.getValue(), execRoot, RelativeSymlinkBehaviorWithoutError.RESOLVE);
for (Map.Entry<String, FileArtifactValue> favEntry :
fileset.getArtifactValues().entrySet()) {
if (favEntry.getValue().getDigest() != null) {
filesetMap.put(PathFragment.create(favEntry.getKey()), favEntry.getValue());
}
}
- } catch (IOException e) {
- // If we cannot get the FileArtifactValue, then we will make a FileSystem call to get the
- // digest, so it is okay to skip and continue here.
- logger.atWarning().withCause(e).log(
- "Could not properly get digest for %s", entry.getKey().getExecPath());
- }
}
return ImmutableMap.copyOf(filesetMap);
}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
index 251f442..6632349 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
@@ -54,7 +54,6 @@
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import com.google.devtools.build.skyframe.ValueOrException2;
-import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
@@ -240,20 +239,16 @@
}
final CompletionContext ctx;
- try {
- ctx =
- CompletionContext.create(
- expandedArtifacts,
- expandedFilesets,
- key.topLevelArtifactContext().expandFilesets(),
- key.topLevelArtifactContext().fullyResolveFilesetSymlinks(),
- inputMap,
- pathResolverFactory,
- skyframeActionExecutor.getExecRoot(),
- workspaceNameValue.getName());
- } catch (IOException e) {
- throw new CompletionFunctionException(e);
- }
+ ctx =
+ CompletionContext.create(
+ expandedArtifacts,
+ expandedFilesets,
+ key.topLevelArtifactContext().expandFilesets(),
+ key.topLevelArtifactContext().fullyResolveFilesetSymlinks(),
+ inputMap,
+ pathResolverFactory,
+ skyframeActionExecutor.getExecRoot(),
+ workspaceNameValue.getName());
if (!rootCauses.isEmpty()) {
ImmutableMap<String, ArtifactsInOutputGroup> builtOutputs =
@@ -336,7 +331,6 @@
}
private static final class CompletionFunctionException extends SkyFunctionException {
-
private final ActionExecutionException actionException;
CompletionFunctionException(ActionExecutionException e) {
@@ -350,11 +344,6 @@
this.actionException = null;
}
- CompletionFunctionException(IOException e) {
- super(e, Transience.TRANSIENT);
- this.actionException = null;
- }
-
@Override
public boolean isCatastrophic() {
return actionException != null && actionException.isCatastrophe();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
index 810784b..1a024fc 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java
@@ -356,21 +356,11 @@
}
private void updateActionFileSystemContext(
- Action action,
FileSystem actionFileSystem,
Environment env,
MetadataInjector metadataInjector,
- ImmutableMap<Artifact, ImmutableList<FilesetOutputSymlink>> filesets)
- throws ActionExecutionException {
- try {
- outputService.updateActionFileSystemContext(
- actionFileSystem, env, metadataInjector, filesets);
- } catch (IOException e) {
- String message = "Failed to update filesystem context: " + e.getMessage();
- DetailedExitCode code =
- createDetailedExitCode(message, Code.FILESYSTEM_CONTEXT_UPDATE_FAILURE);
- throw new ActionExecutionException(message, e, action, /*catastrophe=*/ false, code);
- }
+ ImmutableMap<Artifact, ImmutableList<FilesetOutputSymlink>> filesets) {
+ outputService.updateActionFileSystemContext(actionFileSystem, env, metadataInjector, filesets);
}
void executionOver() {
@@ -466,8 +456,7 @@
boolean hasDiscoveredInputs)
throws ActionExecutionException, InterruptedException {
if (actionFileSystem != null) {
- updateActionFileSystemContext(
- action, actionFileSystem, env, metadataHandler, expandedFilesets);
+ updateActionFileSystemContext(actionFileSystem, env, metadataHandler, expandedFilesets);
}
ActionExecutionContext actionExecutionContext =
@@ -787,7 +776,6 @@
syscalls.get());
if (actionFileSystem != null) {
updateActionFileSystemContext(
- action,
actionFileSystem,
env,
THROWING_METADATA_INJECTOR_FOR_ACTIONFS,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index ea71094..6a2f6f5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -208,7 +208,6 @@
import com.google.devtools.common.options.OptionsParsingException;
import com.google.devtools.common.options.OptionsProvider;
import com.google.errorprone.annotations.ForOverride;
-import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
@@ -388,8 +387,7 @@
ActionInputMap actionInputMap,
Map<Artifact, ImmutableCollection<? extends Artifact>> expandedArtifacts,
Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets,
- String workspaceName)
- throws IOException {
+ String workspaceName) {
Preconditions.checkState(shouldCreatePathResolverForArtifactValues());
return outputService.createPathResolverForArtifactValues(
directories.getExecRoot(workspaceName).asFragment(),
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java b/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
index 4c9f255..5155be8 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/OutputService.java
@@ -183,8 +183,7 @@
FileSystem actionFileSystem,
Environment env,
MetadataInjector injector,
- ImmutableMap<Artifact, ImmutableList<FilesetOutputSymlink>> filesets)
- throws IOException {}
+ ImmutableMap<Artifact, ImmutableList<FilesetOutputSymlink>> filesets) {}
/**
* Checks the filesystem returned by {@link #createActionFileSystem} for errors attributable to
@@ -204,8 +203,7 @@
ImmutableList<Root> pathEntries,
ActionInputMap actionInputMap,
Map<Artifact, ImmutableCollection<? extends Artifact>> expandedArtifacts,
- Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets)
- throws IOException {
+ Map<Artifact, ImmutableList<FilesetOutputSymlink>> filesets) {
throw new IllegalStateException("Path resolver not supported by this class");
}
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 ca6a3b0..7729b5d 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
@@ -28,6 +28,7 @@
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.ExecutionRequirements.WorkerProtocolFormat;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.actions.ResourceManager;
import com.google.devtools.build.lib.actions.ResourceManager.ResourceHandle;
@@ -139,7 +140,7 @@
@Override
public SpawnResult exec(Spawn spawn, SpawnExecutionContext context)
- throws ExecException, IOException, InterruptedException {
+ throws ExecException, IOException, InterruptedException, ForbiddenActionInputException {
context.report(
ProgressStatus.SCHEDULING,
WorkerKey.makeWorkerTypeName(
@@ -400,6 +401,9 @@
restoreInterrupt(e);
String message = "IOException while prefetching for worker:";
throw createUserExecException(e, message, Code.PREFETCH_FAILURE);
+ } catch (ForbiddenActionInputException e) {
+ throw createUserExecException(
+ e, "Forbidden input found while prefetching for worker:", Code.FORBIDDEN_INPUT);
}
Duration setupInputsTime = setupInputsStopwatch.elapsed();
@@ -599,6 +603,12 @@
ErrorMessage.builder().message(message).exception(e).build().toString(), detailedCode);
}
+ private static UserExecException createUserExecException(
+ ForbiddenActionInputException e, String message, Code detailedCode) {
+ return createUserExecException(
+ ErrorMessage.builder().message(message).exception(e).build().toString(), detailedCode);
+ }
+
private static UserExecException createUserExecException(String message, Code detailedCode) {
return new UserExecException(
FailureDetail.newBuilder()
diff --git a/src/main/protobuf/failure_details.proto b/src/main/protobuf/failure_details.proto
index d4deeda..ab5580d 100644
--- a/src/main/protobuf/failure_details.proto
+++ b/src/main/protobuf/failure_details.proto
@@ -221,6 +221,7 @@
// TODO(b/138456686): this code should be deprecated when SpawnResult is
// refactored to prohibit undetailed failures
UNSPECIFIED_EXECUTION_FAILURE = 12 [(metadata) = { exit_code: 1 }];
+ FORBIDDEN_INPUT = 13 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -713,7 +714,7 @@
enum Code {
SANDBOX_FAILURE_UNKNOWN = 0 [(metadata) = { exit_code: 37 }];
INITIALIZATION_FAILURE = 1 [(metadata) = { exit_code: 36 }];
- EXECUTION_IO_EXCEPTION = 2 [(metadata) = { exit_code: 1 }];
+ EXECUTION_IO_EXCEPTION = 2 [(metadata) = { exit_code: 36 }];
DOCKER_COMMAND_FAILURE = 3 [(metadata) = { exit_code: 1 }];
NO_DOCKER_IMAGE = 4 [(metadata) = { exit_code: 1 }];
DOCKER_IMAGE_PREPARATION_FAILURE = 5 [(metadata) = { exit_code: 1 }];
@@ -722,6 +723,7 @@
MOUNT_SOURCE_TARGET_TYPE_MISMATCH = 8 [(metadata) = { exit_code: 1 }];
MOUNT_TARGET_DOES_NOT_EXIST = 9 [(metadata) = { exit_code: 1 }];
SUBPROCESS_START_FAILED = 10 [(metadata) = { exit_code: 36 }];
+ FORBIDDEN_INPUT = 11 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
@@ -1123,12 +1125,13 @@
NO_FLAGFILE = 4 [(metadata) = { exit_code: 1 }];
VIRTUAL_INPUT_MATERIALIZATION_FAILURE = 5 [(metadata) = { exit_code: 1 }];
BORROW_FAILURE = 6 [(metadata) = { exit_code: 1 }];
- PREFETCH_FAILURE = 7 [(metadata) = { exit_code: 1 }];
+ PREFETCH_FAILURE = 7 [(metadata) = { exit_code: 36 }];
PREPARE_FAILURE = 8 [(metadata) = { exit_code: 1 }];
REQUEST_FAILURE = 9 [(metadata) = { exit_code: 1 }];
PARSE_RESPONSE_FAILURE = 10 [(metadata) = { exit_code: 1 }];
NO_RESPONSE = 11 [(metadata) = { exit_code: 1 }];
FINISH_FAILURE = 12 [(metadata) = { exit_code: 1 }];
+ FORBIDDEN_INPUT = 13 [(metadata) = { exit_code: 1 }];
}
Code code = 1;
diff --git a/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java b/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
index e8fbf18..782395f 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/FilesetManifestTest.java
@@ -29,7 +29,6 @@
import com.google.devtools.build.lib.exec.FilesetManifestTest.OneOffManifestTests;
import com.google.devtools.build.lib.exec.FilesetManifestTest.ResolvingManifestTests;
import com.google.devtools.build.lib.vfs.PathFragment;
-import java.io.IOException;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -170,18 +169,13 @@
List<FilesetOutputSymlink> symlinks =
ImmutableList.of(filesetSymlink("bar", "foo"), filesetSymlink("foo", "/foo/bar"));
- // Because BugReport throws in tests, we catch the wrapped exception.
- IllegalStateException e =
+ FilesetManifest.ForbiddenRelativeSymlinkException e =
assertThrows(
- IllegalStateException.class,
+ FilesetManifest.ForbiddenRelativeSymlinkException.class,
() ->
FilesetManifest.constructFilesetManifest(
symlinks, PathFragment.create("out/foo"), ERROR));
- assertThat(e).hasCauseThat().isInstanceOf(IOException.class);
- assertThat(e)
- .hasCauseThat()
- .hasMessageThat()
- .contains("runfiles target is not absolute: foo");
+ assertThat(e).hasMessageThat().contains("Fileset symlink foo is not absolute");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
index 17cb13a..0ca931f 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
@@ -33,7 +33,9 @@
import com.google.devtools.build.lib.actions.ArtifactRoot.RootType;
import com.google.devtools.build.lib.actions.EmptyRunfilesSupplier;
import com.google.devtools.build.lib.actions.FileArtifactValue;
+import com.google.devtools.build.lib.actions.FilesetManifest;
import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.RunfilesSupplier;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
@@ -154,9 +156,9 @@
FakeActionInputFileCache mockCache = new FakeActionInputFileCache();
mockCache.put(artifact, FileArtifactValue.createForDirectoryWithMtime(-1));
- IOException expected =
+ ForbiddenActionInputException expected =
assertThrows(
- IOException.class,
+ ForbiddenActionInputException.class,
() ->
expander.addRunfilesToInputs(
inputMappings,
@@ -463,15 +465,13 @@
@Test
public void testManifestWithErrorOnRelativeSymlink() {
expander = new SpawnInputExpander(execRoot, /*strict=*/ true, ERROR);
- // Because BugReport throws in tests, we catch the wrapped exception.
- IllegalStateException e =
+ FilesetManifest.ForbiddenRelativeSymlinkException e =
assertThrows(
- IllegalStateException.class,
+ FilesetManifest.ForbiddenRelativeSymlinkException.class,
() ->
expander.addFilesetManifests(
simpleFilesetManifest(), inputMappings, PathFragment.EMPTY_FRAGMENT));
- assertThat(e).hasCauseThat().isInstanceOf(IOException.class);
- assertThat(e).hasCauseThat().hasMessageThat().contains("runfiles target is not absolute: foo");
+ assertThat(e).hasMessageThat().contains("Fileset symlink foo is not absolute");
}
@Test
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
index 69b54bd..9935cfd 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnCacheTest.java
@@ -38,6 +38,7 @@
import com.google.devtools.build.lib.actions.ActionInputHelper;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
import com.google.devtools.build.lib.actions.ExecutionRequirements;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.SimpleSpawn;
@@ -158,7 +159,7 @@
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(PathFragment baseDirectory)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
return new SpawnInputExpander(execRoot, /*strict*/ false)
.getInputMapping(simpleSpawn, SIMPLE_ARTIFACT_EXPANDER, baseDirectory, fakeFileCache);
}
diff --git a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
index 28d70a4..d7cbf99 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/util/FakeSpawnExecutionContext.java
@@ -19,6 +19,7 @@
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
+import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
import com.google.devtools.build.lib.actions.MetadataProvider;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.cache.MetadataInjector;
@@ -113,7 +114,7 @@
@Override
public SortedMap<PathFragment, ActionInput> getInputMapping(PathFragment baseDirectory)
- throws IOException {
+ throws IOException, ForbiddenActionInputException {
return new SpawnInputExpander(execRoot, /*strict*/ false)
.getInputMapping(spawn, this::artifactExpander, baseDirectory, metadataProvider);
}