diff options
Diffstat (limited to 'src/main/java/com/google/devtools')
3 files changed, 136 insertions, 15 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 da50cda15a..97151e24ce 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 @@ -45,7 +45,8 @@ final class LinuxSandboxRunner extends SandboxRunner { private final Set<Path> writableDirs; private final Set<Path> inaccessiblePaths; private final Set<Path> tmpfsPaths; - private final Set<Path> bindMounts; + // a <target, source> mapping of paths to bind mount + private final Map<Path, Path> bindMounts; private final boolean sandboxDebug; LinuxSandboxRunner( @@ -56,7 +57,7 @@ final class LinuxSandboxRunner extends SandboxRunner { Set<Path> writableDirs, Set<Path> inaccessiblePaths, Set<Path> tmpfsPaths, - Set<Path> bindMounts, + Map<Path, Path> bindMounts, boolean verboseFailures, boolean sandboxDebug) { super(sandboxExecRoot, verboseFailures); @@ -155,9 +156,15 @@ final class LinuxSandboxRunner extends SandboxRunner { fileArgs.add(tmpfsPath.getPathString()); } - for (Path bindMount : bindMounts) { - fileArgs.add("-b"); - fileArgs.add(bindMount.getPathString()); + for (ImmutableMap.Entry<Path, Path> bindMount : bindMounts.entrySet()) { + fileArgs.add("-M"); + fileArgs.add(bindMount.getValue().getPathString()); + + // The file is mounted in a custom location inside the sandbox. + if (!bindMount.getKey().equals(bindMount.getValue())) { + fileArgs.add("-m"); + fileArgs.add(bindMount.getKey().getPathString()); + } } if (!allowNetwork) { 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 b4ed542ab1..dc4578ef61 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 @@ -14,7 +14,9 @@ package com.google.devtools.build.lib.sandbox; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ExecException; import com.google.devtools.build.lib.actions.ExecutionStrategy; @@ -31,6 +33,7 @@ import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import java.io.IOException; import java.util.Set; +import java.util.SortedMap; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -149,7 +152,8 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { } private SandboxRunner getSandboxRunner( - Spawn spawn, Path sandboxPath, Path sandboxExecRoot, Path sandboxTempDir) { + Spawn spawn, Path sandboxPath, Path sandboxExecRoot, Path sandboxTempDir) + throws UserExecException { if (fullySupported) { return new LinuxSandboxRunner( execRoot, @@ -159,7 +163,7 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { getWritableDirs(sandboxExecRoot, spawn.getEnvironment()), getInaccessiblePaths(), getTmpfsPaths(), - getBindMounts(blazeDirs), + getReadOnlyBindMounts(blazeDirs, sandboxExecRoot), verboseFailures, sandboxOptions.sandboxDebug); } else { @@ -175,15 +179,71 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { return tmpfsPaths.build(); } - private ImmutableSet<Path> getBindMounts(BlazeDirectories blazeDirs) { + private SortedMap<Path, Path> getReadOnlyBindMounts( + BlazeDirectories blazeDirs, Path sandboxExecRoot) throws UserExecException { Path tmpPath = blazeDirs.getFileSystem().getPath("/tmp"); - ImmutableSet.Builder<Path> bindMounts = ImmutableSet.builder(); + final SortedMap<Path, Path> bindMounts = Maps.newTreeMap(); if (blazeDirs.getWorkspace().startsWith(tmpPath)) { - bindMounts.add(blazeDirs.getWorkspace()); + bindMounts.put(blazeDirs.getWorkspace(), blazeDirs.getWorkspace()); } if (blazeDirs.getOutputBase().startsWith(tmpPath)) { - bindMounts.add(blazeDirs.getOutputBase()); + bindMounts.put(blazeDirs.getOutputBase(), blazeDirs.getOutputBase()); + } + for (ImmutableMap.Entry<String, String> additionalMountPath : + sandboxOptions.sandboxAdditionalMounts) { + try { + final Path mountTarget = blazeDirs.getFileSystem().getPath(additionalMountPath.getValue()); + // If source path is relative, treat it as a relative path inside the execution root + final Path mountSource = sandboxExecRoot.getRelative(additionalMountPath.getKey()); + // If a target has more than one source path, the latter one will take effect. + bindMounts.put(mountTarget, mountSource); + } catch (IllegalArgumentException e) { + throw new UserExecException( + String.format("Error occurred when analyzing bind mount pairs. %s", e.getMessage())); + } + } + validateBindMounts(bindMounts); + return bindMounts; + } + + /** + * This method does the following things: - If mount source does not exist on the host system, + * throw an error message - If mount target exists, check whether the source and target are of the + * same type - If mount target does not exist on the host system, throw an error message + * + * @param bindMounts the bind mounts map with target as key and source as value + * @throws UserExecException + */ + private void validateBindMounts(SortedMap<Path, Path> bindMounts) throws UserExecException { + for (SortedMap.Entry<Path, Path> bindMount : bindMounts.entrySet()) { + final Path source = bindMount.getValue(); + final Path target = bindMount.getKey(); + // Mount source should exist in the file system + if (!source.exists()) { + throw new UserExecException(String.format("Mount source '%s' does not exist.", source)); + } + // If target exists, but is not of the same type as the source, then we cannot mount it. + if (target.exists()) { + boolean areBothDirectories = source.isDirectory() && target.isDirectory(); + boolean isSourceFile = source.isFile() || source.isSymbolicLink(); + boolean isTargetFile = target.isFile() || target.isSymbolicLink(); + boolean areBothFiles = isSourceFile && isTargetFile; + if (!(areBothDirectories || areBothFiles)) { + // Source and target are not of the same type; we cannot mount it. + throw new UserExecException( + String.format( + "Mount target '%s' is not of the same type as mount source '%s'.", + target, source)); + } + } else { + // Mount target should exist in the file system + throw new UserExecException( + String.format( + "Mount target '%s' does not exist. Bazel only supports bind mounting on top of " + + "existing files/directories. Please create an empty file or directory at " + + "the mount target path according to the type of mount source.", + target)); + } } - return bindMounts.build(); } } 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 909e07cc76..b8ceb8094c 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 @@ -14,15 +14,59 @@ package com.google.devtools.build.lib.sandbox; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsBase; +import com.google.devtools.common.options.OptionsParsingException; import java.util.List; -/** - * Options for sandboxed execution. - */ +/** Options for sandboxed execution. */ public class SandboxOptions extends OptionsBase { + /** + * A converter for customized path mounting pair from the parameter list of a bazel command + * invocation. Pairs are expected to have the form 'source:target'. + */ + public static final class MountPairConverter + implements Converter<ImmutableMap.Entry<String, String>> { + + @Override + public ImmutableMap.Entry<String, String> convert(String input) throws OptionsParsingException { + + List<String> paths = Lists.newArrayList(); + for (String path : input.split("(?<!\\\\):")) { // Split on ':' but not on '\:' + if (path != null && !path.trim().isEmpty()) { + paths.add(path.replace("\\:", ":")); + } else { + throw new OptionsParsingException( + "Input " + + input + + " contains one or more empty paths. " + + "Input must be a single path to mount inside the sandbox or " + + "a mounting pair in the form of 'source:target'"); + } + } + + if (paths.size() < 1 || paths.size() > 2) { + throw new OptionsParsingException( + "Input must be a single path to mount inside the sandbox or " + + "a mounting pair in the form of 'source:target'"); + } + + return paths.size() == 1 + ? Maps.immutableEntry(paths.get(0), paths.get(0)) + : Maps.immutableEntry(paths.get(0), paths.get(1)); + } + + @Override + public String getTypeDescription() { + return "a single path or a 'source:target' pair"; + } + } + @Option( name = "ignore_unsupported_sandboxing", defaultValue = "false", @@ -59,4 +103,14 @@ public class SandboxOptions extends OptionsBase { + " (if supported by the sandboxing implementation, ignored otherwise)." ) public List<String> sandboxTmpfsPath; + + @Option( + name = "sandbox_add_mount_pair", + allowMultiple = true, + converter = MountPairConverter.class, + defaultValue = "", + category = "config", + help = "Add additional path pair to mount in sandbox." + ) + public List<ImmutableMap.Entry<String, String>> sandboxAdditionalMounts; } |