aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java144
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java439
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/MountMap.java56
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java25
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextProvider.java70
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SandboxModule.java54
-rw-r--r--src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java7
-rw-r--r--src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java47
8 files changed, 307 insertions, 535 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
index bae4ec9d45..06c84de165 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java
@@ -15,7 +15,6 @@
package com.google.devtools.build.lib.sandbox;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import com.google.devtools.build.lib.actions.ExecException;
@@ -27,6 +26,7 @@ import com.google.devtools.build.lib.shell.CommandException;
import com.google.devtools.build.lib.shell.TerminationStatus;
import com.google.devtools.build.lib.util.CommandFailureUtils;
import com.google.devtools.build.lib.util.OsUtils;
+import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
@@ -36,7 +36,11 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
/**
* Helper class for running the Linux sandbox. This runner prepares environment inside the sandbox,
@@ -44,34 +48,27 @@ import java.util.List;
*/
public class LinuxSandboxRunner {
private static final String LINUX_SANDBOX = "linux-sandbox" + OsUtils.executableExtension();
- private static final String SANDBOX_TIP =
- "\n\nSandboxed execution failed, which may be legitimate (e.g. a compiler error), "
- + "or due to missing dependencies. To enter the sandbox environment for easier debugging,"
- + " run the following command in parentheses. On command failure, "
- + "a bash shell running inside the sandbox will then automatically be spawned:\n\n";
private final Path execRoot;
- private final Path sandboxPath;
private final Path sandboxExecRoot;
private final Path argumentsFilePath;
- private final ImmutableMap<Path, Path> mounts;
- private final ImmutableSet<Path> createDirs;
+ private final Set<Path> writablePaths;
+ private final List<Path> inaccessiblePaths;
private final boolean verboseFailures;
private final boolean sandboxDebug;
- public LinuxSandboxRunner(
+ LinuxSandboxRunner(
Path execRoot,
- Path sandboxPath,
- ImmutableMap<Path, Path> mounts,
- ImmutableSet<Path> createDirs,
+ Path sandboxExecRoot,
+ Set<Path> writablePaths,
+ List<Path> inaccessiblePaths,
boolean verboseFailures,
boolean sandboxDebug) {
this.execRoot = execRoot;
- this.sandboxPath = sandboxPath;
- this.sandboxExecRoot = sandboxPath.getRelative(execRoot.asFragment().relativeTo("/"));
+ this.sandboxExecRoot = sandboxExecRoot;
this.argumentsFilePath =
- sandboxPath.getParentDirectory().getRelative(sandboxPath.getBaseName() + ".params");
- this.mounts = mounts;
- this.createDirs = createDirs;
+ sandboxExecRoot.getParentDirectory().getRelative(sandboxExecRoot.getBaseName() + ".params");
+ this.writablePaths = writablePaths;
+ this.inaccessiblePaths = inaccessiblePaths;
this.verboseFailures = verboseFailures;
this.sandboxDebug = sandboxDebug;
}
@@ -114,21 +111,20 @@ public class LinuxSandboxRunner {
*
* @param spawnArguments - arguments of spawn to run inside the sandbox
* @param env - environment to run sandbox in
- * @param cwd - current working directory
* @param outErr - error output to capture sandbox's and command's stderr
- * @param outputs - files to extract from the sandbox, paths are relative to the exec root
- * @throws ExecException
+ * @param outputs - files to extract from the sandbox, paths are relative to the exec root @throws
+ * ExecException
*/
public void run(
List<String> spawnArguments,
- ImmutableMap<String, String> env,
- File cwd,
+ Map<String, String> env,
FileOutErr outErr,
+ Map<PathFragment, Path> inputs,
Collection<PathFragment> outputs,
int timeout,
boolean blockNetwork)
throws IOException, ExecException {
- createFileSystem(outputs);
+ createFileSystem(inputs, outputs);
List<String> fileArgs = new ArrayList<>();
List<String> commandLineArgs = new ArrayList<>();
@@ -139,13 +135,9 @@ public class LinuxSandboxRunner {
fileArgs.add("-D");
}
- // Sandbox directory.
- fileArgs.add("-S");
- fileArgs.add(sandboxPath.getPathString());
-
// Working directory of the spawn.
fileArgs.add("-W");
- fileArgs.add(cwd.toString());
+ fileArgs.add(sandboxExecRoot.toString());
// Kill the process after a timeout.
if (timeout != -1) {
@@ -154,26 +146,22 @@ public class LinuxSandboxRunner {
}
// Create all needed directories.
- for (Path createDir : createDirs) {
- fileArgs.add("-d");
- fileArgs.add(createDir.getPathString());
+ for (Path writablePath : writablePaths) {
+ fileArgs.add("-w");
+ fileArgs.add(writablePath.getPathString());
+ if (writablePath.startsWith(sandboxExecRoot)) {
+ FileSystemUtils.createDirectoryAndParents(writablePath);
+ }
}
- if (blockNetwork) {
- // Block network access out of the namespace.
- fileArgs.add("-n");
+ for (Path inaccessiblePath : inaccessiblePaths) {
+ fileArgs.add("-i");
+ fileArgs.add(inaccessiblePath.getPathString());
}
- // Mount all the inputs.
- for (ImmutableMap.Entry<Path, Path> mount : mounts.entrySet()) {
- fileArgs.add("-M");
- fileArgs.add(mount.getValue().getPathString());
-
- // The file is mounted in a custom location inside the sandbox.
- if (!mount.getValue().equals(mount.getKey())) {
- fileArgs.add("-m");
- fileArgs.add(mount.getKey().getPathString());
- }
+ if (blockNetwork) {
+ // Block network access out of the namespace.
+ fileArgs.add("-N");
}
FileSystemUtils.writeLinesAs(argumentsFilePath, StandardCharsets.ISO_8859_1, fileArgs);
@@ -182,7 +170,8 @@ public class LinuxSandboxRunner {
commandLineArgs.add("--");
commandLineArgs.addAll(spawnArguments);
- Command cmd = new Command(commandLineArgs.toArray(new String[0]), env, cwd);
+ Command cmd =
+ new Command(commandLineArgs.toArray(new String[0]), env, sandboxExecRoot.getPathFile());
try {
cmd.execute(
@@ -200,40 +189,77 @@ public class LinuxSandboxRunner {
}
String message =
CommandFailureUtils.describeCommandFailure(
- verboseFailures, commandLineArgs, env, cwd.getPath());
- String finalMsg = (sandboxDebug && verboseFailures) ? SANDBOX_TIP + message : message;
- throw new UserExecException(finalMsg, e, timedOut);
+ verboseFailures, commandLineArgs, env, sandboxExecRoot.getPathString());
+ throw new UserExecException(message, e, timedOut);
} finally {
copyOutputs(outputs);
}
}
- private void createFileSystem(Collection<PathFragment> outputs) throws IOException {
- FileSystemUtils.createDirectoryAndParents(sandboxPath);
+ private void createFileSystem(Map<PathFragment, Path> inputs, Collection<PathFragment> outputs)
+ throws IOException {
+ Set<Path> createdDirs = new HashSet<>();
+ FileSystemUtils.createDirectoryAndParentsWithCache(createdDirs, sandboxExecRoot);
+ createParentDirectoriesForInputs(createdDirs, inputs.keySet());
+ createSymlinksForInputs(inputs);
+ createDirectoriesForOutputs(createdDirs, outputs);
+ }
+
+ /**
+ * No input can be a child of another input, because otherwise we might try to create a symlink
+ * below another symlink we created earlier - which means we'd actually end up writing somewhere
+ * in the workspace.
+ *
+ * <p>If all inputs were regular files, this situation could naturally not happen - but
+ * unfortunately, we might get the occasional action that has directories in its inputs.
+ *
+ * <p>Creating all parent directories first ensures that we can safely create symlinks to
+ * directories, too, because we'll get an IOException with EEXIST if inputs happen to be nested
+ * once we start creating the symlinks for all inputs.
+ */
+ private void createParentDirectoriesForInputs(Set<Path> createdDirs, Set<PathFragment> inputs)
+ throws IOException {
+ for (PathFragment inputPath : inputs) {
+ Path dir = sandboxExecRoot.getRelative(inputPath).getParentDirectory();
+ Preconditions.checkArgument(dir.startsWith(sandboxExecRoot));
+ FileSystemUtils.createDirectoryAndParentsWithCache(createdDirs, dir);
+ }
+ }
+
+ private void createSymlinksForInputs(Map<PathFragment, Path> inputs) throws IOException {
+ // All input files are relative to the execroot.
+ for (Entry<PathFragment, Path> entry : inputs.entrySet()) {
+ Path key = sandboxExecRoot.getRelative(entry.getKey());
+ key.createSymbolicLink(entry.getValue());
+ }
+ }
- // Prepare the output directories in the sandbox.
+ /** Prepare the output directories in the sandbox. */
+ private void createDirectoriesForOutputs(Set<Path> createdDirs, Collection<PathFragment> outputs)
+ throws IOException {
for (PathFragment output : outputs) {
- FileSystemUtils.createDirectoryAndParents(
- sandboxExecRoot.getRelative(output.getParentDirectory()));
+ FileSystemUtils.createDirectoryAndParentsWithCache(
+ createdDirs, sandboxExecRoot.getRelative(output.getParentDirectory()));
+ FileSystemUtils.createDirectoryAndParentsWithCache(
+ createdDirs, execRoot.getRelative(output.getParentDirectory()));
}
}
private void copyOutputs(Collection<PathFragment> outputs) throws IOException {
for (PathFragment output : outputs) {
Path source = sandboxExecRoot.getRelative(output);
- Path target = execRoot.getRelative(output);
- FileSystemUtils.createDirectoryAndParents(target.getParentDirectory());
if (source.isFile() || source.isSymbolicLink()) {
+ Path target = execRoot.getRelative(output);
Files.move(source.getPathFile(), target.getPathFile());
}
}
}
public void cleanup() throws IOException {
- if (sandboxPath.exists()) {
- FileSystemUtils.deleteTree(sandboxPath);
+ if (sandboxExecRoot.exists()) {
+ FileSystemUtils.deleteTree(sandboxExecRoot);
}
- if (!sandboxDebug && argumentsFilePath.exists()) {
+ if (argumentsFilePath.exists()) {
argumentsFilePath.delete();
}
}
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 9a04ed870c..4ee34e891e 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
@@ -13,10 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.sandbox;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.Files;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
@@ -33,28 +30,22 @@ import com.google.devtools.build.lib.actions.SpawnActionContext;
import com.google.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.analysis.AnalysisUtils;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
-import com.google.devtools.build.lib.analysis.config.RunUnder;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.rules.cpp.CppCompileAction;
import com.google.devtools.build.lib.rules.fileset.FilesetActionContext;
-import com.google.devtools.build.lib.rules.test.TestRunnerAction;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.standalone.StandaloneSpawnStrategy;
-import com.google.devtools.build.lib.unix.NativePosixFiles;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.io.FileOutErr;
-import com.google.devtools.build.lib.vfs.FileStatus;
import com.google.devtools.build.lib.vfs.FileSystem;
-import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
-import com.google.devtools.build.lib.vfs.SearchPath;
-import com.google.devtools.build.lib.vfs.Symlinks;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
@@ -67,10 +58,18 @@ import java.util.concurrent.atomic.AtomicInteger;
contextType = SpawnActionContext.class
)
public class LinuxSandboxedStrategy implements SpawnActionContext {
+ private static Boolean sandboxingSupported = null;
+
+ public static boolean isSupported(CommandEnvironment env) {
+ if (sandboxingSupported == null) {
+ sandboxingSupported = LinuxSandboxRunner.isSupported(env);
+ }
+ return sandboxingSupported.booleanValue();
+ }
+
private final ExecutorService backgroundWorkers;
private final SandboxOptions sandboxOptions;
- private final ImmutableMap<String, String> clientEnv;
private final BlazeDirectories blazeDirs;
private final Path execRoot;
private final boolean verboseFailures;
@@ -79,16 +78,14 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
private final AtomicInteger execCounter = new AtomicInteger();
private final String productName;
- public LinuxSandboxedStrategy(
+ LinuxSandboxedStrategy(
SandboxOptions options,
- Map<String, String> clientEnv,
BlazeDirectories blazeDirs,
ExecutorService backgroundWorkers,
boolean verboseFailures,
boolean unblockNetwork,
String productName) {
this.sandboxOptions = options;
- this.clientEnv = ImmutableMap.copyOf(clientEnv);
this.blazeDirs = blazeDirs;
this.execRoot = blazeDirs.getExecRoot();
this.backgroundWorkers = Preconditions.checkNotNull(backgroundWorkers);
@@ -129,18 +126,21 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
String execId = uuid + "-" + execCounter.getAndIncrement();
// Each invocation of "exec" gets its own sandbox.
- Path sandboxPath =
- execRoot.getRelative(productName + "-sandbox").getRelative(execId);
+ Path sandboxExecRoot =
+ blazeDirs.getOutputBase().getRelative(productName + "-sandbox").getRelative(execId);
// Gather all necessary mounts for the sandbox.
- ImmutableMap<Path, Path> mounts;
+ Map<PathFragment, Path> mounts;
try {
mounts = getMounts(spawn, actionExecutionContext);
} catch (IllegalArgumentException | IOException e) {
throw new EnvironmentalExecException("Could not prepare mounts for sandbox execution", e);
}
- ImmutableSet<Path> createDirs = createImportantDirs(spawn.getEnvironment());
+ Map<String, String> env = new HashMap<>(spawn.getEnvironment());
+
+ ImmutableSet<Path> writablePaths = getWritablePaths(sandboxExecRoot, env);
+ ImmutableList<Path> inaccessiblePaths = getInaccessiblePaths();
int timeout = getTimeout(spawn);
@@ -157,17 +157,17 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
final LinuxSandboxRunner runner =
new LinuxSandboxRunner(
execRoot,
- sandboxPath,
- mounts,
- createDirs,
+ sandboxExecRoot,
+ writablePaths,
+ inaccessiblePaths,
verboseFailures,
sandboxOptions.sandboxDebug);
try {
runner.run(
spawn.getArguments(),
- spawn.getEnvironment(),
- execRoot.getPathFile(),
+ env,
outErr,
+ mounts,
outputFiles.build(),
timeout,
!this.unblockNetwork && !spawn.getExecutionInfo().containsKey("requires-network"));
@@ -213,191 +213,86 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
return -1;
}
- /**
- * Most programs expect certain directories to be present, e.g. /tmp. Make sure they are.
- *
- * <p>Note that $HOME is handled by linux-sandbox.c, because it changes user to nobody and the
- * home directory of that user is not known by us.
- */
- private ImmutableSet<Path> createImportantDirs(Map<String, String> env) {
- ImmutableSet.Builder<Path> dirs = ImmutableSet.builder();
- FileSystem fs = blazeDirs.getFileSystem();
+ /** Gets the list of directories that the spawn will assume to be writable. */
+ private ImmutableSet<Path> getWritablePaths(Path sandboxExecRoot, Map<String, String> env) {
+ ImmutableSet.Builder<Path> writablePaths = ImmutableSet.builder();
+ // We have to make the TEST_TMPDIR directory writable if it is specified.
if (env.containsKey("TEST_TMPDIR")) {
- PathFragment testTmpDir = new PathFragment(env.get("TEST_TMPDIR"));
- if (testTmpDir.isAbsolute()) {
- dirs.add(fs.getPath(testTmpDir));
- } else {
- dirs.add(execRoot.getRelative(testTmpDir));
- }
- }
- dirs.add(fs.getPath("/tmp"));
- return dirs.build();
- }
-
- private ImmutableMap<Path, Path> getMounts(Spawn spawn, ActionExecutionContext executionContext)
- throws IOException, ExecException {
- ImmutableMap.Builder<Path, Path> result = new ImmutableMap.Builder<>();
- result.putAll(mountUsualUnixDirs());
- result.putAll(mountUserDefinedPath());
-
- MountMap mounts = new MountMap();
- mounts.putAll(setupBlazeUtils());
- mounts.putAll(mountRunfilesFromManifests(spawn));
- mounts.putAll(mountRunfilesFromSuppliers(spawn));
- mounts.putAll(mountFilesFromFilesetManifests(spawn, executionContext));
- mounts.putAll(mountInputs(spawn, executionContext));
- mounts.putAll(mountRunUnderCommand(spawn));
- result.putAll(finalizeMounts(mounts));
- return result.build();
- }
-
- /**
- * Helper method of {@link #finalizeMounts}. This method handles adding a single path
- * to the output map, including making sure it exists and adding the target of a
- * symbolic link if necessary.
- *
- * @param finalizedMounts the map to add the mapping(s) to
- * @param target the key to add to the map
- * @param source the value to add to the map
- * @param stat information about source (passed in to avoid fetching it twice)
- */
- private static void finalizeMountPath(
- MountMap finalizedMounts, Path target, Path source, FileStatus stat) throws IOException {
- // The source must exist.
- Preconditions.checkArgument(stat != null, "%s does not exist", source.toString());
- finalizedMounts.put(target, source);
-
- if (stat.isSymbolicLink()) {
- Path symlinkTarget = source.resolveSymbolicLinks();
- Preconditions.checkArgument(
- symlinkTarget.exists(), "%s does not exist", symlinkTarget.toString());
- finalizedMounts.put(symlinkTarget, symlinkTarget);
+ Path testTmpDir = sandboxExecRoot.getRelative(env.get("TEST_TMPDIR"));
+ writablePaths.add(testTmpDir);
+ env.put("TEST_TMPDIR", testTmpDir.getPathString());
}
+ return writablePaths.build();
}
- /**
- * Performs various checks on each mounted file which require stating each one.
- * Contained in one function to allow minimizing the number of syscalls involved.
- *
- * Checks for each mount if the source refers to a symbolic link and if yes, adds another mount
- * for the target of that symlink to ensure that it keeps working inside the sandbox.
- *
- * Checks for each mount if the source refers to a directory and if yes, replaces that mount with
- * mounts of all files inside that directory.
- *
- * Validates all mounts against a set of criteria and throws an exception on error.
- *
- * @return a new mounts multimap with all mounts and the added mounts.
- */
- @VisibleForTesting
- static MountMap finalizeMounts(Map<Path, Path> mounts) throws IOException {
- MountMap finalizedMounts = new MountMap();
- for (Entry<Path, Path> mount : mounts.entrySet()) {
- Path target = mount.getKey();
- Path source = mount.getValue();
-
- FileStatus stat = source.statNullable(Symlinks.NOFOLLOW);
-
- if (stat != null && stat.isDirectory()) {
- for (Path subSource : FileSystemUtils.traverseTree(source, Predicates.alwaysTrue())) {
- Path subTarget = target.getRelative(subSource.relativeTo(source));
- finalizeMountPath(
- finalizedMounts, subTarget, subSource, subSource.statNullable(Symlinks.NOFOLLOW));
- }
- } else {
- finalizeMountPath(finalizedMounts, target, source, stat);
- }
- }
- return finalizedMounts;
- }
-
- /**
- * Mount a certain set of unix directories to make the usual tools and libraries available to the
- * spawn that runs.
- *
- * Throws an exception if any of them do not exist.
- */
- private MountMap mountUsualUnixDirs() throws IOException {
- MountMap mounts = new MountMap();
- FileSystem fs = blazeDirs.getFileSystem();
- mounts.put(fs.getPath("/bin"), fs.getPath("/bin"));
- mounts.put(fs.getPath("/sbin"), fs.getPath("/sbin"));
- mounts.put(fs.getPath("/etc"), fs.getPath("/etc"));
-
- // Check if /etc/resolv.conf is a symlink and mount its target
- // Fix #738
- Path resolv = fs.getPath("/etc/resolv.conf");
- if (resolv.exists() && resolv.isSymbolicLink()) {
- mounts.put(resolv.resolveSymbolicLinks(), resolv.resolveSymbolicLinks());
- }
-
- for (String entry : NativePosixFiles.readdir("/")) {
- if (entry.startsWith("lib")) {
- Path libDir = fs.getRootDirectory().getRelative(entry);
- mounts.put(libDir, libDir);
- }
- }
- for (String entry : NativePosixFiles.readdir("/usr")) {
- if (!entry.equals("local")) {
- Path usrDir = fs.getPath("/usr").getRelative(entry);
- mounts.put(usrDir, usrDir);
- }
- }
- for (Path path : mounts.values()) {
- Preconditions.checkArgument(path.exists(), "%s does not exist", path.toString());
+ private ImmutableList<Path> getInaccessiblePaths() {
+ ImmutableList.Builder<Path> inaccessiblePaths = ImmutableList.builder();
+ for (String path : sandboxOptions.sandboxBlockPath) {
+ inaccessiblePaths.add(blazeDirs.getFileSystem().getPath(path));
}
- return mounts;
+ return inaccessiblePaths.build();
}
- /**
- * Mount the embedded tools.
- */
- private MountMap setupBlazeUtils() {
- MountMap mounts = new MountMap();
- Path mount = blazeDirs.getEmbeddedBinariesRoot().getRelative("build-runfiles");
- mounts.put(mount, mount);
+ private Map<PathFragment, Path> getMounts(Spawn spawn, ActionExecutionContext executionContext)
+ throws IOException, ExecException {
+ Map<PathFragment, Path> mounts = new HashMap<>();
+ mountRunfilesFromManifests(mounts, spawn);
+ mountRunfilesFromSuppliers(mounts, spawn);
+ mountFilesFromFilesetManifests(mounts, spawn, executionContext);
+ mountInputs(mounts, spawn, executionContext);
return mounts;
}
- /**
- * Mount all runfiles that the spawn needs as specified in its runfiles manifests.
- */
- private MountMap mountRunfilesFromManifests(Spawn spawn) throws IOException, ExecException {
- MountMap mounts = new MountMap();
- for (Entry<PathFragment, Artifact> manifest : spawn.getRunfilesManifests().entrySet()) {
+ /** Mount all runfiles that the spawn needs as specified in its runfiles manifests. */
+ private void mountRunfilesFromManifests(Map<PathFragment, Path> mounts, Spawn spawn)
+ throws IOException, ExecException {
+ for (Map.Entry<PathFragment, Artifact> manifest : spawn.getRunfilesManifests().entrySet()) {
String manifestFilePath = manifest.getValue().getPath().getPathString();
Preconditions.checkState(!manifest.getKey().isAbsolute());
- Path targetDirectory = execRoot.getRelative(manifest.getKey());
-
- mounts.putAll(parseManifestFile(targetDirectory, new File(manifestFilePath), false, ""));
+ PathFragment targetDirectory = manifest.getKey();
+
+ parseManifestFile(
+ blazeDirs.getFileSystem(),
+ mounts,
+ targetDirectory,
+ new File(manifestFilePath),
+ false,
+ "");
}
- return mounts;
}
- /**
- * Mount all files that the spawn needs as specified in its fileset manifests.
- */
- private MountMap mountFilesFromFilesetManifests(
- Spawn spawn, ActionExecutionContext executionContext) throws IOException, ExecException {
+ /** Mount all files that the spawn needs as specified in its fileset manifests. */
+ private void mountFilesFromFilesetManifests(
+ Map<PathFragment, Path> mounts, Spawn spawn, ActionExecutionContext executionContext)
+ throws IOException, ExecException {
final FilesetActionContext filesetContext =
executionContext.getExecutor().getContext(FilesetActionContext.class);
- MountMap mounts = new MountMap();
for (Artifact fileset : spawn.getFilesetManifests()) {
- Path manifest =
- execRoot.getRelative(AnalysisUtils.getManifestPathFromFilesetPath(fileset.getExecPath()));
- Path targetDirectory = execRoot.getRelative(fileset.getExecPathString());
-
- mounts.putAll(
- parseManifestFile(
- targetDirectory, manifest.getPathFile(), true, filesetContext.getWorkspaceName()));
+ File manifestFile =
+ new File(
+ execRoot.getPathString(),
+ AnalysisUtils.getManifestPathFromFilesetPath(fileset.getExecPath()).getPathString());
+ PathFragment targetDirectory = fileset.getExecPath();
+
+ parseManifestFile(
+ blazeDirs.getFileSystem(),
+ mounts,
+ targetDirectory,
+ manifestFile,
+ true,
+ filesetContext.getWorkspaceName());
}
- return mounts;
}
- static MountMap parseManifestFile(
- Path targetDirectory, File manifestFile, boolean isFilesetManifest, String workspaceName)
+ /** A parser for the MANIFEST files used by Filesets and runfiles. */
+ static void parseManifestFile(
+ FileSystem fs,
+ Map<PathFragment, Path> mounts,
+ PathFragment targetDirectory,
+ File manifestFile,
+ boolean isFilesetManifest,
+ String workspaceName)
throws IOException, ExecException {
- MountMap mounts = new MountMap();
int lineNum = 0;
for (String line : Files.readLines(manifestFile, StandardCharsets.UTF_8)) {
if (isFilesetManifest && (++lineNum % 2 == 0)) {
@@ -409,7 +304,14 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
String[] fields = line.trim().split(" ");
- Path targetPath;
+ // The "target" field is always a relative path that is to be interpreted in this way:
+ // (1) If this is a fileset manifest and our workspace name is not empty, the first segment
+ // of each "target" path must be the workspace name, which is then stripped before further
+ // processing.
+ // (2) The "target" path is then appended to the "targetDirectory", which is a path relative
+ // to the execRoot. Together, this results in the full path in the execRoot in which place a
+ // symlink referring to "source" has to be created (see below).
+ PathFragment targetPath;
if (isFilesetManifest) {
PathFragment targetPathFragment = new PathFragment(fields[0]);
if (!workspaceName.isEmpty()) {
@@ -424,13 +326,15 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
targetPath = targetDirectory.getRelative(fields[0]);
}
+ // The "source" field, if it exists, is always an absolute path and may point to any file in
+ // the filesystem (it is not limited to files in the workspace or execroot).
Path source;
switch (fields.length) {
case 1:
- source = targetDirectory.getFileSystem().getPath("/dev/null");
+ source = fs.getPath("/dev/null");
break;
case 2:
- source = targetDirectory.getFileSystem().getPath(fields[1]);
+ source = fs.getPath(fields[1]);
break;
default:
throw new IllegalStateException("'" + line + "' splits into more than 2 parts");
@@ -438,38 +342,34 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
mounts.put(targetPath, source);
}
- return mounts;
}
- /**
- * Mount all runfiles that the spawn needs as specified via its runfiles suppliers.
- */
- private MountMap mountRunfilesFromSuppliers(Spawn spawn) throws IOException {
- MountMap mounts = new MountMap();
- FileSystem fs = blazeDirs.getFileSystem();
+ /** Mount all runfiles that the spawn needs as specified via its runfiles suppliers. */
+ private void mountRunfilesFromSuppliers(Map<PathFragment, Path> mounts, Spawn spawn)
+ throws IOException {
Map<PathFragment, Map<PathFragment, Artifact>> rootsAndMappings =
spawn.getRunfilesSupplier().getMappings();
- for (Entry<PathFragment, Map<PathFragment, Artifact>> rootAndMappings :
+ for (Map.Entry<PathFragment, Map<PathFragment, Artifact>> rootAndMappings :
rootsAndMappings.entrySet()) {
- Path root = fs.getRootDirectory().getRelative(rootAndMappings.getKey());
- for (Entry<PathFragment, Artifact> mapping : rootAndMappings.getValue().entrySet()) {
+ PathFragment root = rootAndMappings.getKey();
+ if (root.isAbsolute()) {
+ root = root.relativeTo(execRoot.asFragment());
+ }
+ for (Map.Entry<PathFragment, Artifact> mapping : rootAndMappings.getValue().entrySet()) {
Artifact sourceArtifact = mapping.getValue();
- Path source = (sourceArtifact != null) ? sourceArtifact.getPath() : fs.getPath("/dev/null");
+ PathFragment source =
+ (sourceArtifact != null) ? sourceArtifact.getExecPath() : new PathFragment("/dev/null");
Preconditions.checkArgument(!mapping.getKey().isAbsolute());
- Path target = root.getRelative(mapping.getKey());
- mounts.put(target, source);
+ PathFragment target = root.getRelative(mapping.getKey());
+ mounts.put(target, execRoot.getRelative(source));
}
}
- return mounts;
}
- /**
- * Mount all inputs of the spawn.
- */
- private MountMap mountInputs(Spawn spawn, ActionExecutionContext actionExecutionContext) {
- MountMap mounts = new MountMap();
-
+ /** Mount all inputs of the spawn. */
+ private void mountInputs(
+ Map<PathFragment, Path> mounts, Spawn spawn, ActionExecutionContext actionExecutionContext) {
List<ActionInput> inputs =
ActionInputHelper.expandArtifacts(
spawn.getInputFiles(), actionExecutionContext.getArtifactExpander());
@@ -485,124 +385,9 @@ public class LinuxSandboxedStrategy implements SpawnActionContext {
if (input.getExecPathString().contains("internal/_middlemen/")) {
continue;
}
- Path mount = execRoot.getRelative(input.getExecPathString());
- mounts.put(mount, mount);
- }
- return mounts;
- }
-
- /**
- * If a --run_under= option is set and refers to a command via its path (as opposed to via its
- * label), we have to mount this. Note that this is best effort and works fine for shell scripts
- * and small binaries, but we can't track any further dependencies of this command.
- *
- * <p>If --run_under= refers to a label, it is automatically provided in the spawn's input files,
- * so mountInputs() will catch that case.
- */
- private MountMap mountRunUnderCommand(Spawn spawn) {
- MountMap mounts = new MountMap();
-
- if (spawn.getResourceOwner() instanceof TestRunnerAction) {
- TestRunnerAction testRunnerAction = ((TestRunnerAction) spawn.getResourceOwner());
- RunUnder runUnder = testRunnerAction.getExecutionSettings().getRunUnder();
- if (runUnder != null && runUnder.getCommand() != null) {
- PathFragment sourceFragment = new PathFragment(runUnder.getCommand());
- Path mount;
- if (sourceFragment.isAbsolute()) {
- mount = blazeDirs.getFileSystem().getPath(sourceFragment);
- } else if (blazeDirs.getExecRoot().getRelative(sourceFragment).exists()) {
- mount = blazeDirs.getExecRoot().getRelative(sourceFragment);
- } else {
- List<Path> searchPath =
- SearchPath.parse(blazeDirs.getFileSystem(), clientEnv.get("PATH"));
- mount = SearchPath.which(searchPath, runUnder.getCommand());
- }
- if (mount != null) {
- mounts.put(mount, mount);
- }
- }
- }
- return mounts;
- }
-
- /**
- * Mount all user defined path in --sandbox_add_path.
- */
- private MountMap mountUserDefinedPath() throws IOException {
- MountMap mounts = new MountMap();
- FileSystem fs = blazeDirs.getFileSystem();
-
- ImmutableList<Path> exclude =
- ImmutableList.of(blazeDirs.getWorkspace(), blazeDirs.getOutputBase());
-
- for (String pathStr : sandboxOptions.sandboxAddPath) {
- Path path = fs.getPath(pathStr);
-
- // Check if path is in {workspace, outputBase}
- for (Path exc : exclude) {
- if (path.startsWith(exc)) {
- throw new IllegalArgumentException(
- "Mounting subdirectory of WORKSPACE or OUTPUTBASE to sandbox is not allowed.");
- }
- }
-
- // Check if path is ancestor of {workspace, outputBase}
- // Mount subdirectory of path except {workspace, outputBase}
- mounts.putAll(mountChildDirExclude(path, exclude));
+ PathFragment mount = new PathFragment(input.getExecPathString());
+ mounts.put(mount, execRoot.getRelative(mount));
}
-
- return mounts;
- }
-
- /**
- * Mount all subdirectories recursively except some paths
- */
- private MountMap mountDirExclude(Path path, List<Path> exclude) throws IOException {
- MountMap mounts = new MountMap();
-
- if (!path.isDirectory(Symlinks.NOFOLLOW)) {
- if (!exclude.contains(path)) {
- mounts.put(path, path);
- }
- return mounts;
- }
-
- try {
- for (Path child : path.getDirectoryEntries()) {
- // Ignore broken symlink
- if (!child.exists()) {
- continue;
- }
-
- mounts.putAll(mountChildDirExclude(child, exclude));
- }
- } catch (IOException e) {
- throw new IOException("Illegal additional path for mount", e);
- }
-
- return mounts;
- }
-
- /**
- * Helper function of mountDirExclude and mountUserDefinedPath
- */
- private MountMap mountChildDirExclude(Path child, List<Path> exclude) throws IOException {
- MountMap mounts = new MountMap();
-
- boolean startsWithFlag = false;
- for (Path exc : exclude) {
- if (exc.startsWith(child)) {
- startsWithFlag = true;
- break;
- }
- }
- if (!startsWithFlag) {
- mounts.put(child, child);
- } else if (!exclude.contains(child)) {
- mounts.putAll(mountDirExclude(child, exclude));
- }
-
- return mounts;
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/MountMap.java b/src/main/java/com/google/devtools/build/lib/sandbox/MountMap.java
deleted file mode 100644
index 5051d27df3..0000000000
--- a/src/main/java/com/google/devtools/build/lib/sandbox/MountMap.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2015 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.sandbox;
-
-import com.google.common.collect.ForwardingSortedMap;
-import com.google.devtools.build.lib.vfs.Path;
-
-import java.util.Map;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-/**
- * A map that throws an exception when trying to replace a key (i.e. once a key gets a value,
- * any additional attempt of putting a value on the same key will throw an exception).
- *
- * <p>Returns entries sorted by path depth (shorter paths first) and in lexicographical order.
- */
-final class MountMap extends ForwardingSortedMap<Path, Path> {
- final TreeMap<Path, Path> delegate = new TreeMap<>();
-
- @Override
- protected SortedMap<Path, Path> delegate() {
- return delegate;
- }
-
- @Override
- public Path put(Path key, Path value) {
- Path previousValue = get(key);
- if (previousValue == null) {
- return super.put(key, value);
- } else if (previousValue.equals(value)) {
- return value;
- } else {
- throw new IllegalArgumentException(
- String.format("Cannot mount both '%s' and '%s' onto '%s'", previousValue, value, key));
- }
- }
-
- @Override
- public void putAll(Map<? extends Path, ? extends Path> map) {
- for (Entry<? extends Path, ? extends Path> entry : map.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
index 2d6e28e562..7b4e30218a 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextConsumer.java
@@ -15,12 +15,11 @@ package com.google.devtools.build.lib.sandbox;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableMultimap.Builder;
import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.actions.ActionContextConsumer;
import com.google.devtools.build.lib.actions.Executor.ActionContext;
import com.google.devtools.build.lib.actions.SpawnActionContext;
-import com.google.devtools.build.lib.util.OS;
+import com.google.devtools.build.lib.runtime.CommandEnvironment;
/**
* {@link ActionContextConsumer} that requests the action contexts necessary for sandboxed
@@ -28,6 +27,19 @@ import com.google.devtools.build.lib.util.OS;
*/
public class SandboxActionContextConsumer implements ActionContextConsumer {
+ private final ImmutableMultimap<Class<? extends ActionContext>, String> contexts;
+
+ public SandboxActionContextConsumer(CommandEnvironment env) {
+ ImmutableMultimap.Builder<Class<? extends ActionContext>, String> contexts =
+ ImmutableMultimap.builder();
+
+ if (LinuxSandboxedStrategy.isSupported(env)) {
+ contexts.put(SpawnActionContext.class, "sandboxed");
+ }
+
+ this.contexts = contexts.build();
+ }
+
@Override
public ImmutableMap<String, String> getSpawnActionContexts() {
return ImmutableMap.of();
@@ -35,13 +47,6 @@ public class SandboxActionContextConsumer implements ActionContextConsumer {
@Override
public Multimap<Class<? extends ActionContext>, String> getActionContexts() {
- Builder<Class<? extends ActionContext>, String> contexts = ImmutableMultimap.builder();
-
- if (OS.getCurrent() == OS.LINUX) {
- contexts.put(SpawnActionContext.class, "sandboxed");
- }
-
- return contexts.build();
+ return contexts;
}
-
}
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextProvider.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextProvider.java
index 6bf355610e..7bf38c901b 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxActionContextProvider.java
@@ -14,11 +14,11 @@
package com.google.devtools.build.lib.sandbox;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.devtools.build.lib.actions.ActionContextProvider;
import com.google.devtools.build.lib.actions.Executor.ActionContext;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.buildtool.BuildRequest;
+import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.exec.ExecutionOptions;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
import com.google.devtools.build.lib.util.OS;
@@ -30,11 +30,16 @@ import java.util.concurrent.ExecutorService;
*/
public class SandboxActionContextProvider extends ActionContextProvider {
+ public static final String SANDBOX_NOT_SUPPORTED_MESSAGE =
+ "Sandboxed execution is not supported on your system and thus hermeticity of actions cannot "
+ + "be guaranteed. See http://bazel.io/docs/bazel-user-manual.html#sandboxing for more "
+ + "information. You can turn off this warning via --ignore_unsupported_sandboxing";
+
@SuppressWarnings("unchecked")
- private final ImmutableList<ActionContext> strategies;
+ private final ImmutableList<ActionContext> contexts;
- private SandboxActionContextProvider(ImmutableList<ActionContext> strategies) {
- this.strategies = strategies;
+ private SandboxActionContextProvider(ImmutableList<ActionContext> contexts) {
+ this.contexts = contexts;
}
public static SandboxActionContextProvider create(
@@ -46,36 +51,45 @@ public class SandboxActionContextProvider extends ActionContextProvider {
.getOptions(BuildConfiguration.Options.class)
.testArguments
.contains("--wrapper_script_flag=--debug");
- Builder<ActionContext> strategies = ImmutableList.builder();
+ ImmutableList.Builder<ActionContext> contexts = ImmutableList.builder();
- if (OS.getCurrent() == OS.LINUX) {
- strategies.add(
- new LinuxSandboxedStrategy(
- buildRequest.getOptions(SandboxOptions.class),
- env.getClientEnv(),
- env.getDirectories(),
- backgroundWorkers,
- verboseFailures,
- unblockNetwork,
- env.getRuntime().getProductName()));
- } else if (OS.getCurrent() == OS.DARWIN) {
- strategies.add(
- DarwinSandboxedStrategy.create(
- buildRequest.getOptions(SandboxOptions.class),
- env.getClientEnv(),
- env.getDirectories(),
- backgroundWorkers,
- verboseFailures,
- unblockNetwork,
- env.getRuntime().getProductName()));
+ switch (OS.getCurrent()) {
+ case LINUX:
+ if (LinuxSandboxedStrategy.isSupported(env)) {
+ contexts.add(
+ new LinuxSandboxedStrategy(
+ buildRequest.getOptions(SandboxOptions.class),
+ env.getDirectories(),
+ backgroundWorkers,
+ verboseFailures,
+ unblockNetwork,
+ env.getRuntime().getProductName()));
+ } else if (!buildRequest.getOptions(SandboxOptions.class).ignoreUnsupportedSandboxing) {
+ env.getReporter().handle(Event.warn(SANDBOX_NOT_SUPPORTED_MESSAGE));
+ }
+ break;
+ case DARWIN:
+ if (DarwinSandboxRunner.isSupported()) {
+ contexts.add(
+ DarwinSandboxedStrategy.create(
+ buildRequest.getOptions(SandboxOptions.class),
+ env.getClientEnv(),
+ env.getDirectories(),
+ backgroundWorkers,
+ verboseFailures,
+ unblockNetwork,
+ env.getRuntime().getProductName()));
+ }
+ break;
+ default:
+ // No sandboxing available.
}
- return new SandboxActionContextProvider(strategies.build());
+ return new SandboxActionContextProvider(contexts.build());
}
@Override
public Iterable<ActionContext> getActionContexts() {
- return strategies;
+ return contexts;
}
-
}
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 5b9ca58931..8a20cb4f16 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
@@ -20,11 +20,9 @@ import com.google.devtools.build.lib.actions.ActionContextProvider;
import com.google.devtools.build.lib.buildtool.BuildRequest;
import com.google.devtools.build.lib.buildtool.buildevent.BuildStartingEvent;
import com.google.devtools.build.lib.concurrent.ExecutorUtil;
-import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.runtime.BlazeModule;
import com.google.devtools.build.lib.runtime.Command;
import com.google.devtools.build.lib.runtime.CommandEnvironment;
-import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.common.options.OptionsBase;
import java.io.IOException;
@@ -35,66 +33,30 @@ import java.util.concurrent.Executors;
* This module provides the Sandbox spawn strategy.
*/
public class SandboxModule extends BlazeModule {
- public static final String SANDBOX_NOT_SUPPORTED_MESSAGE =
- "Sandboxed execution is not supported on your system and thus hermeticity of actions cannot "
- + "be guaranteed. See http://bazel.io/docs/bazel-user-manual.html#sandboxing for more "
- + "information. You can turn off this warning via --ignore_unsupported_sandboxing";
-
// Per-server state
private ExecutorService backgroundWorkers;
- private Boolean sandboxingSupported = null;
// Per-command state
private CommandEnvironment env;
private BuildRequest buildRequest;
- private synchronized boolean isSandboxingSupported(CommandEnvironment env) {
- switch (OS.getCurrent()) {
- case LINUX:
- sandboxingSupported = LinuxSandboxRunner.isSupported(env);
- break;
- case DARWIN:
- sandboxingSupported = DarwinSandboxRunner.isSupported();
- break;
- default:
- sandboxingSupported = false;
- }
- return sandboxingSupported.booleanValue();
- }
-
@Override
public Iterable<ActionContextProvider> getActionContextProviders() {
- Preconditions.checkNotNull(buildRequest);
Preconditions.checkNotNull(env);
- if (isSandboxingSupported(env)) {
- Iterable<ActionContextProvider> ret;
- try {
- ret =
- ImmutableList.<ActionContextProvider>of(
- SandboxActionContextProvider.create(env, buildRequest, backgroundWorkers));
- } catch (IOException e) {
- throw new IllegalArgumentException(e);
- }
- return ret;
- }
-
- // For now, sandboxing is only supported on Linux and there's not much point in showing a scary
- // warning to the user if they can't do anything about it.
- if (!buildRequest.getOptions(SandboxOptions.class).ignoreUnsupportedSandboxing
- && OS.getCurrent() == OS.LINUX) {
- env.getReporter().handle(Event.warn(SANDBOX_NOT_SUPPORTED_MESSAGE));
+ Preconditions.checkNotNull(buildRequest);
+ Preconditions.checkNotNull(backgroundWorkers);
+ try {
+ return ImmutableList.<ActionContextProvider>of(
+ SandboxActionContextProvider.create(env, buildRequest, backgroundWorkers));
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
}
-
- return ImmutableList.of();
}
@Override
public Iterable<ActionContextConsumer> getActionContextConsumers() {
Preconditions.checkNotNull(env);
- if (isSandboxingSupported(env)) {
- return ImmutableList.<ActionContextConsumer>of(new SandboxActionContextConsumer());
- }
- return ImmutableList.of();
+ return ImmutableList.<ActionContextConsumer>of(new SandboxActionContextConsumer(env));
}
@Override
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
index f1291d2cbb..f18792f606 100644
--- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxOptions.java
@@ -15,7 +15,6 @@ package com.google.devtools.build.lib.sandbox;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
-
import java.util.List;
/**
@@ -42,11 +41,11 @@ public class SandboxOptions extends OptionsBase {
public boolean sandboxDebug;
@Option(
- name = "sandbox_add_path",
+ name = "sandbox_block_path",
allowMultiple = true,
defaultValue = "",
category = "config",
- help = "Add additional path to mount to sandbox. Path including workspace is not allowed."
+ help = "For sandboxed actions, disallow access to this path."
)
- public List<String> sandboxAddPath;
+ public List<String> sandboxBlockPath;
}
diff --git a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
index 8c864547ff..cf12b50f21 100644
--- a/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/vfs/FileSystemUtils.java
@@ -22,7 +22,6 @@ import com.google.common.io.ByteStreams;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ConditionallyThreadSafe;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.util.Preconditions;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -32,6 +31,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
/**
* Helper functions that implement often-used complex operations on file
@@ -582,10 +582,36 @@ public class FileSystemUtils {
*/
@ThreadSafe
public static boolean createDirectoryAndParents(Path dir) throws IOException {
+ return createDirectoryAndParentsWithCache(null, dir);
+ }
+
+ /**
+ * Attempts to create a directory with the name of the given path, creating ancestors as
+ * necessary. Only creates directories or their parents if they are not contained in the set
+ * {@code createdDirs} and instead assumes that they already exist. This saves a round-trip to the
+ * kernel, but is only safe when no one deletes directories that have been created by this method.
+ *
+ * <p>Postcondition: completes normally iff {@code dir} denotes an existing directory (not
+ * necessarily canonical); completes abruptly otherwise.
+ *
+ * @return true if the directory was successfully created anew, false if it already existed
+ * (including the case where {@code dir} denotes a symlink to an existing directory)
+ * @throws IOException if the directory could not be created
+ */
+ @ThreadSafe
+ public static boolean createDirectoryAndParentsWithCache(Set<Path> createdDirs, Path dir)
+ throws IOException {
// Optimised for minimal number of I/O calls.
// Don't attempt to create the root directory.
- if (dir.getParentDirectory() == null) { return false; }
+ if (dir.getParentDirectory() == null) {
+ return false;
+ }
+
+ // We already created that directory.
+ if (createdDirs != null && createdDirs.contains(dir)) {
+ return false;
+ }
FileSystem filesystem = dir.getFileSystem();
if (filesystem instanceof UnionFileSystem) {
@@ -596,12 +622,23 @@ public class FileSystemUtils {
}
try {
- return dir.createDirectory();
+ boolean result = dir.createDirectory();
+ if (createdDirs != null) {
+ createdDirs.add(dir);
+ }
+ return result;
} catch (IOException e) {
if (e.getMessage().endsWith(" (No such file or directory)")) { // ENOENT
- createDirectoryAndParents(dir.getParentDirectory());
- return dir.createDirectory();
+ createDirectoryAndParentsWithCache(createdDirs, dir.getParentDirectory());
+ boolean result = dir.createDirectory();
+ if (createdDirs != null) {
+ createdDirs.add(dir);
+ }
+ return result;
} else if (e.getMessage().endsWith(" (File exists)") && dir.isDirectory()) { // EEXIST
+ if (createdDirs != null) {
+ createdDirs.add(dir);
+ }
return false;
} else {
throw e; // some other error (e.g. ENOTDIR, EACCES, etc.)