// Copyright 2016 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.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; import com.google.devtools.build.lib.actions.ActionExecutionContext; 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.ExecException; import com.google.devtools.build.lib.actions.Spawn; import com.google.devtools.build.lib.analysis.test.TestConfiguration; import com.google.devtools.build.lib.exec.SpawnInputExpander; import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionPolicy; import com.google.devtools.build.lib.rules.fileset.FilesetActionContext; import com.google.devtools.build.lib.standalone.StandaloneSpawnStrategy; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.common.options.OptionsProvider; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; /** Helper methods that are shared by the different sandboxing strategies in this package. */ public final class SandboxHelpers { static void fallbackToNonSandboxedExecution( Spawn spawn, ActionExecutionContext actionExecutionContext) throws ExecException, InterruptedException { StandaloneSpawnStrategy standaloneStrategy = Preconditions.checkNotNull( actionExecutionContext.getContext(StandaloneSpawnStrategy.class)); standaloneStrategy.exec(spawn, actionExecutionContext); } /** * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the * host filesystem where the input files can be found. */ public static Map getInputFiles( SpawnInputExpander spawnInputExpander, Path execRoot, Spawn spawn, ActionExecutionContext executionContext) throws IOException { Map inputMap = spawnInputExpander.getInputMapping( spawn, executionContext.getArtifactExpander(), executionContext.getActionInputFileCache(), executionContext.getContext(FilesetActionContext.class)); return postProcess(inputMap, spawn, executionContext.getArtifactExpander(), execRoot); } /** * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the * host filesystem where the input files can be found. */ public static Map getInputFiles( Spawn spawn, SpawnExecutionPolicy policy, Path execRoot) throws IOException { return postProcess(policy.getInputMapping(), spawn, policy.getArtifactExpander(), execRoot); } /** * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the * host filesystem where the input files can be found. */ private static Map postProcess( Map inputMap, Spawn spawn, ArtifactExpander artifactExpander, Path execRoot) { // SpawnInputExpander#getInputMapping uses ArtifactExpander#expandArtifacts to expand // middlemen and tree artifacts, which expands empty tree artifacts to no entry. However, // actions that accept TreeArtifacts as inputs generally expect that the empty directory is // created. So we add those explicitly here. // TODO(ulfjack): Move this code to SpawnInputExpander. for (ActionInput input : spawn.getInputFiles()) { if (input instanceof Artifact && ((Artifact) input).isTreeArtifact()) { List containedArtifacts = new ArrayList<>(); artifactExpander.expand((Artifact) input, containedArtifacts); // Attempting to mount a non-empty directory results in ERR_DIRECTORY_NOT_EMPTY, so we // only mount empty TreeArtifacts as directories. if (containedArtifacts.isEmpty()) { inputMap.put(input.getExecPath(), input); } } } Map inputFiles = new TreeMap<>(); for (Map.Entry e : inputMap.entrySet()) { Path inputPath = e.getValue() == SpawnInputExpander.EMPTY_FILE ? null : execRoot.getRelative(e.getValue().getExecPath()); inputFiles.put(e.getKey(), inputPath); } return inputFiles; } public static ImmutableSet getOutputFiles(Spawn spawn) { Builder outputFiles = ImmutableSet.builder(); for (ActionInput output : spawn.getOutputFiles()) { outputFiles.add(PathFragment.create(output.getExecPathString())); } return outputFiles.build(); } /** * Returns true if the build options are set in a way that requires network access for all * actions. This is separate from {@link #shouldAllowNetwork(Spawn)} to avoid having to keep a * reference to the full set of build options (and also for performance, since this only needs to * be checked once-per-build). */ static boolean shouldAllowNetwork(OptionsProvider buildOptions) { // Allow network access, when --java_debug is specified, otherwise we can't connect to the // remote debug server of the test. This intentionally overrides the "block-network" execution // tag. return buildOptions .getOptions(TestConfiguration.TestOptions.class) .testArguments .contains("--wrapper_script_flag=--debug"); } /** Returns true if this specific spawn requires network access. */ static boolean shouldAllowNetwork(Spawn spawn) { // If the Spawn requests to block network access, do so. if (spawn.getExecutionInfo().containsKey("block-network")) { return false; } // Network access is allowed by default. return true; } }