blob: 48caa7166cb1008d8dbffc2fc6f4af88c32a6d11 [file] [log] [blame]
Philipp Wollermann6488b3b2016-08-26 13:01:20 +00001// Copyright 2016 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +00007// http://www.apache.org/licenses/LICENSE-2.0
Philipp Wollermann6488b3b2016-08-26 13:01:20 +00008//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000014
Philipp Wollermann6488b3b2016-08-26 13:01:20 +000015package com.google.devtools.build.lib.sandbox;
16
cushon9b8b7902018-10-04 09:38:43 -070017import com.google.auto.value.AutoValue;
ulfjack3e288682018-01-08 09:31:17 -080018import com.google.common.base.Preconditions;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000019import com.google.common.collect.ImmutableSet;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000020import com.google.devtools.build.lib.actions.ActionInput;
Philipp Wollermann37276452017-05-09 09:27:21 -040021import com.google.devtools.build.lib.actions.Artifact;
ulfjack264f40f2017-07-12 12:10:11 +020022import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander;
tomlu2e4f7032018-05-01 09:57:02 -070023import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput;
Philipp Wollermann6488b3b2016-08-26 13:01:20 +000024import com.google.devtools.build.lib.actions.Spawn;
ulfjack4d7f8f72017-11-29 03:37:04 -080025import com.google.devtools.build.lib.actions.Spawns;
ulfjack3e288682018-01-08 09:31:17 -080026import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
27import com.google.devtools.build.lib.actions.cache.VirtualActionInput.EmptyActionInput;
ulfjackab21d182017-08-10 15:36:14 +020028import com.google.devtools.build.lib.analysis.test.TestConfiguration;
tomlu29e306d2018-04-19 05:41:44 -070029import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext;
Philipp Wollermann37276452017-05-09 09:27:21 -040030import com.google.devtools.build.lib.vfs.Path;
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +000031import com.google.devtools.build.lib.vfs.PathFragment;
juliexxiae91a4502018-08-15 14:42:29 -070032import com.google.devtools.common.options.OptionsParsingResult;
Philipp Wollermann37276452017-05-09 09:27:21 -040033import java.io.IOException;
tomlu2e4f7032018-05-01 09:57:02 -070034import java.io.OutputStream;
Philipp Wollermann37276452017-05-09 09:27:21 -040035import java.util.ArrayList;
36import java.util.List;
37import java.util.Map;
38import java.util.TreeMap;
Philipp Wollermann6488b3b2016-08-26 13:01:20 +000039
40/** Helper methods that are shared by the different sandboxing strategies in this package. */
Philipp Wollermanna1cf3592016-09-28 12:27:23 +000041public final class SandboxHelpers {
Philipp Wollermann6488b3b2016-08-26 13:01:20 +000042
Philipp Wollermann37276452017-05-09 09:27:21 -040043 /**
44 * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
45 * host filesystem where the input files can be found.
tomlu2e4f7032018-05-01 09:57:02 -070046 *
47 * <p>Also writes any supported {@link VirtualActionInput}s found.
48 *
49 * @throws IOException If any files could not be written.
Philipp Wollermann37276452017-05-09 09:27:21 -040050 */
tomlu2e4f7032018-05-01 09:57:02 -070051 public static Map<PathFragment, Path> processInputFiles(
philwo9ed9d8a2018-09-03 07:30:26 -070052 Spawn spawn,
53 SpawnExecutionContext context,
54 Path execRoot,
55 boolean expandTreeArtifactsInRunfiles)
56 throws IOException {
tomlu2e4f7032018-05-01 09:57:02 -070057 return processInputFiles(
philwo9ed9d8a2018-09-03 07:30:26 -070058 context.getInputMapping(expandTreeArtifactsInRunfiles),
59 spawn,
60 context.getArtifactExpander(),
61 execRoot);
ulfjack264f40f2017-07-12 12:10:11 +020062 }
63
64 /**
65 * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the
66 * host filesystem where the input files can be found.
tomlu2e4f7032018-05-01 09:57:02 -070067 *
68 * <p>Also writes any supported {@link VirtualActionInput}s found.
69 *
70 * @throws IOException If any files could not be written.
ulfjack264f40f2017-07-12 12:10:11 +020071 */
tomlu2e4f7032018-05-01 09:57:02 -070072 private static Map<PathFragment, Path> processInputFiles(
ulfjack264f40f2017-07-12 12:10:11 +020073 Map<PathFragment, ActionInput> inputMap,
tomlu2e4f7032018-05-01 09:57:02 -070074 Spawn spawn,
ulfjack264f40f2017-07-12 12:10:11 +020075 ArtifactExpander artifactExpander,
tomlu2e4f7032018-05-01 09:57:02 -070076 Path execRoot)
77 throws IOException {
Philipp Wollermann37276452017-05-09 09:27:21 -040078 // SpawnInputExpander#getInputMapping uses ArtifactExpander#expandArtifacts to expand
79 // middlemen and tree artifacts, which expands empty tree artifacts to no entry. However,
80 // actions that accept TreeArtifacts as inputs generally expect that the empty directory is
81 // created. So we add those explicitly here.
82 // TODO(ulfjack): Move this code to SpawnInputExpander.
83 for (ActionInput input : spawn.getInputFiles()) {
84 if (input instanceof Artifact && ((Artifact) input).isTreeArtifact()) {
85 List<Artifact> containedArtifacts = new ArrayList<>();
ulfjack264f40f2017-07-12 12:10:11 +020086 artifactExpander.expand((Artifact) input, containedArtifacts);
Philipp Wollermann37276452017-05-09 09:27:21 -040087 // Attempting to mount a non-empty directory results in ERR_DIRECTORY_NOT_EMPTY, so we
88 // only mount empty TreeArtifacts as directories.
89 if (containedArtifacts.isEmpty()) {
90 inputMap.put(input.getExecPath(), input);
91 }
92 }
93 }
94
95 Map<PathFragment, Path> inputFiles = new TreeMap<>();
96 for (Map.Entry<PathFragment, ActionInput> e : inputMap.entrySet()) {
tomlu2e4f7032018-05-01 09:57:02 -070097 PathFragment pathFragment = e.getKey();
98 ActionInput actionInput = e.getValue();
99 if (actionInput instanceof VirtualActionInput) {
100 if (actionInput instanceof ParamFileActionInput) {
101 ParamFileActionInput paramFileInput = (ParamFileActionInput) actionInput;
102 Path outputPath = execRoot.getRelative(paramFileInput.getExecPath());
103 outputPath.getParentDirectory().createDirectoryAndParents();
104 try (OutputStream outputStream = outputPath.getOutputStream()) {
105 paramFileInput.writeTo(outputStream);
106 }
107 } else {
108 // TODO(ulfjack): Handle all virtual inputs, e.g., by writing them to a file.
109 Preconditions.checkState(actionInput instanceof EmptyActionInput);
110 }
ulfjack3e288682018-01-08 09:31:17 -0800111 }
Philipp Wollermann37276452017-05-09 09:27:21 -0400112 Path inputPath =
tomlu2e4f7032018-05-01 09:57:02 -0700113 actionInput instanceof EmptyActionInput
Philipp Wollermann37276452017-05-09 09:27:21 -0400114 ? null
tomlu2e4f7032018-05-01 09:57:02 -0700115 : execRoot.getRelative(actionInput.getExecPath());
116 inputFiles.put(pathFragment, inputPath);
Philipp Wollermann37276452017-05-09 09:27:21 -0400117 }
118 return inputFiles;
119 }
120
cushon9b8b7902018-10-04 09:38:43 -0700121 /** The file and directory outputs of a sandboxed spawn. */
122 @AutoValue
123 public abstract static class SandboxOutputs {
124 public abstract ImmutableSet<PathFragment> files();
125
126 public abstract ImmutableSet<PathFragment> dirs();
127
128 public static SandboxOutputs create(
129 ImmutableSet<PathFragment> files, ImmutableSet<PathFragment> dirs) {
130 return new AutoValue_SandboxHelpers_SandboxOutputs(files, dirs);
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000131 }
cushon9b8b7902018-10-04 09:38:43 -0700132 }
133
134 public static SandboxOutputs getOutputs(Spawn spawn) {
135 ImmutableSet.Builder<PathFragment> files = ImmutableSet.builder();
136 ImmutableSet.Builder<PathFragment> dirs = ImmutableSet.builder();
137 for (ActionInput output : spawn.getOutputFiles()) {
138 PathFragment path = PathFragment.create(output.getExecPathString());
139 if (output instanceof Artifact && ((Artifact) output).isTreeArtifact()) {
140 dirs.add(path);
141 } else {
142 files.add(path);
143 }
144 }
145 return SandboxOutputs.create(files.build(), dirs.build());
Philipp Wollermann5a50b4f2016-08-31 12:07:40 +0000146 }
147
ulfjack264f40f2017-07-12 12:10:11 +0200148 /**
149 * Returns true if the build options are set in a way that requires network access for all
ulfjack4d7f8f72017-11-29 03:37:04 -0800150 * actions. This is separate from {@link Spawns#requiresNetwork} to avoid having to keep a
ulfjack264f40f2017-07-12 12:10:11 +0200151 * reference to the full set of build options (and also for performance, since this only needs to
152 * be checked once-per-build).
153 */
juliexxiae91a4502018-08-15 14:42:29 -0700154 static boolean shouldAllowNetwork(OptionsParsingResult buildOptions) {
Philipp Wollermann6488b3b2016-08-26 13:01:20 +0000155 // Allow network access, when --java_debug is specified, otherwise we can't connect to the
Philipp Wollermannc5af2f32016-10-07 13:36:04 +0000156 // remote debug server of the test. This intentionally overrides the "block-network" execution
157 // tag.
ulfjack264f40f2017-07-12 12:10:11 +0200158 return buildOptions
tomlu65204692017-08-04 17:41:54 +0200159 .getOptions(TestConfiguration.TestOptions.class)
Philipp Wollermann6488b3b2016-08-26 13:01:20 +0000160 .testArguments
ulfjack264f40f2017-07-12 12:10:11 +0200161 .contains("--wrapper_script_flag=--debug");
162 }
Philipp Wollermann6488b3b2016-08-26 13:01:20 +0000163}