Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 1 | // 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 Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 8 | // |
| 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 Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 14 | |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 15 | package com.google.devtools.build.lib.sandbox; |
| 16 | |
cushon | 9b8b790 | 2018-10-04 09:38:43 -0700 | [diff] [blame] | 17 | import com.google.auto.value.AutoValue; |
ulfjack | 3e28868 | 2018-01-08 09:31:17 -0800 | [diff] [blame] | 18 | import com.google.common.base.Preconditions; |
Philipp Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 19 | import com.google.common.collect.ImmutableSet; |
Philipp Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 20 | import com.google.devtools.build.lib.actions.ActionInput; |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 21 | import com.google.devtools.build.lib.actions.Artifact; |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 22 | import com.google.devtools.build.lib.actions.Artifact.ArtifactExpander; |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 23 | import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput; |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 24 | import com.google.devtools.build.lib.actions.Spawn; |
ulfjack | 4d7f8f7 | 2017-11-29 03:37:04 -0800 | [diff] [blame] | 25 | import com.google.devtools.build.lib.actions.Spawns; |
ulfjack | 3e28868 | 2018-01-08 09:31:17 -0800 | [diff] [blame] | 26 | import com.google.devtools.build.lib.actions.cache.VirtualActionInput; |
| 27 | import com.google.devtools.build.lib.actions.cache.VirtualActionInput.EmptyActionInput; |
ulfjack | ab21d18 | 2017-08-10 15:36:14 +0200 | [diff] [blame] | 28 | import com.google.devtools.build.lib.analysis.test.TestConfiguration; |
tomlu | 29e306d | 2018-04-19 05:41:44 -0700 | [diff] [blame] | 29 | import com.google.devtools.build.lib.exec.SpawnRunner.SpawnExecutionContext; |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 30 | import com.google.devtools.build.lib.vfs.Path; |
Philipp Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 31 | import com.google.devtools.build.lib.vfs.PathFragment; |
juliexxia | e91a450 | 2018-08-15 14:42:29 -0700 | [diff] [blame] | 32 | import com.google.devtools.common.options.OptionsParsingResult; |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 33 | import java.io.IOException; |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 34 | import java.io.OutputStream; |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 35 | import java.util.ArrayList; |
| 36 | import java.util.List; |
| 37 | import java.util.Map; |
| 38 | import java.util.TreeMap; |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 39 | |
| 40 | /** Helper methods that are shared by the different sandboxing strategies in this package. */ |
Philipp Wollermann | a1cf359 | 2016-09-28 12:27:23 +0000 | [diff] [blame] | 41 | public final class SandboxHelpers { |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 42 | |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 43 | /** |
| 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. |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 46 | * |
| 47 | * <p>Also writes any supported {@link VirtualActionInput}s found. |
| 48 | * |
| 49 | * @throws IOException If any files could not be written. |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 50 | */ |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 51 | public static Map<PathFragment, Path> processInputFiles( |
philwo | 9ed9d8a | 2018-09-03 07:30:26 -0700 | [diff] [blame] | 52 | Spawn spawn, |
| 53 | SpawnExecutionContext context, |
| 54 | Path execRoot, |
| 55 | boolean expandTreeArtifactsInRunfiles) |
| 56 | throws IOException { |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 57 | return processInputFiles( |
philwo | 9ed9d8a | 2018-09-03 07:30:26 -0700 | [diff] [blame] | 58 | context.getInputMapping(expandTreeArtifactsInRunfiles), |
| 59 | spawn, |
| 60 | context.getArtifactExpander(), |
| 61 | execRoot); |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 62 | } |
| 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. |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 67 | * |
| 68 | * <p>Also writes any supported {@link VirtualActionInput}s found. |
| 69 | * |
| 70 | * @throws IOException If any files could not be written. |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 71 | */ |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 72 | private static Map<PathFragment, Path> processInputFiles( |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 73 | Map<PathFragment, ActionInput> inputMap, |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 74 | Spawn spawn, |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 75 | ArtifactExpander artifactExpander, |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 76 | Path execRoot) |
| 77 | throws IOException { |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 78 | // 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<>(); |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 86 | artifactExpander.expand((Artifact) input, containedArtifacts); |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 87 | // 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()) { |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 97 | 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 | } |
ulfjack | 3e28868 | 2018-01-08 09:31:17 -0800 | [diff] [blame] | 111 | } |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 112 | Path inputPath = |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 113 | actionInput instanceof EmptyActionInput |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 114 | ? null |
tomlu | 2e4f703 | 2018-05-01 09:57:02 -0700 | [diff] [blame] | 115 | : execRoot.getRelative(actionInput.getExecPath()); |
| 116 | inputFiles.put(pathFragment, inputPath); |
Philipp Wollermann | 3727645 | 2017-05-09 09:27:21 -0400 | [diff] [blame] | 117 | } |
| 118 | return inputFiles; |
| 119 | } |
| 120 | |
cushon | 9b8b790 | 2018-10-04 09:38:43 -0700 | [diff] [blame] | 121 | /** 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 Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 131 | } |
cushon | 9b8b790 | 2018-10-04 09:38:43 -0700 | [diff] [blame] | 132 | } |
| 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 Wollermann | 5a50b4f | 2016-08-31 12:07:40 +0000 | [diff] [blame] | 146 | } |
| 147 | |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 148 | /** |
| 149 | * Returns true if the build options are set in a way that requires network access for all |
ulfjack | 4d7f8f7 | 2017-11-29 03:37:04 -0800 | [diff] [blame] | 150 | * actions. This is separate from {@link Spawns#requiresNetwork} to avoid having to keep a |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 151 | * reference to the full set of build options (and also for performance, since this only needs to |
| 152 | * be checked once-per-build). |
| 153 | */ |
juliexxia | e91a450 | 2018-08-15 14:42:29 -0700 | [diff] [blame] | 154 | static boolean shouldAllowNetwork(OptionsParsingResult buildOptions) { |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 155 | // Allow network access, when --java_debug is specified, otherwise we can't connect to the |
Philipp Wollermann | c5af2f3 | 2016-10-07 13:36:04 +0000 | [diff] [blame] | 156 | // remote debug server of the test. This intentionally overrides the "block-network" execution |
| 157 | // tag. |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 158 | return buildOptions |
tomlu | 6520469 | 2017-08-04 17:41:54 +0200 | [diff] [blame] | 159 | .getOptions(TestConfiguration.TestOptions.class) |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 160 | .testArguments |
ulfjack | 264f40f | 2017-07-12 12:10:11 +0200 | [diff] [blame] | 161 | .contains("--wrapper_script_flag=--debug"); |
| 162 | } |
Philipp Wollermann | 6488b3b | 2016-08-26 13:01:20 +0000 | [diff] [blame] | 163 | } |