blob: 90e3ec7a819d1f0f672e4cfba4cd2701fe4a373d [file] [log] [blame]
ulfjacka63da8e2017-07-12 16:51:20 +02001// Copyright 2014 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//
7// http://www.apache.org/licenses/LICENSE-2.0
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.
14
15package com.google.devtools.build.lib.sandbox;
16
ruperts5274d8b2017-12-20 10:45:58 -080017import com.google.common.collect.ImmutableList;
ulfjacka63da8e2017-07-12 16:51:20 +020018import com.google.common.collect.ImmutableMap;
19import com.google.common.collect.ImmutableSet;
20import com.google.common.collect.Maps;
21import com.google.common.io.ByteStreams;
frazze-jobb11f7d802021-09-06 08:45:27 -070022import com.google.devtools.build.lib.actions.ActionInput;
ulfjacka63da8e2017-07-12 16:51:20 +020023import com.google.devtools.build.lib.actions.ExecException;
ulfjack4d7f8f72017-11-29 03:37:04 -080024import com.google.devtools.build.lib.actions.ExecutionRequirements;
frazze-jobb11f7d802021-09-06 08:45:27 -070025import com.google.devtools.build.lib.actions.FileArtifactValue;
26import com.google.devtools.build.lib.actions.FileContentsProxy;
janakr300c11e2021-05-18 12:10:43 -070027import com.google.devtools.build.lib.actions.ForbiddenActionInputException;
ulfjacka63da8e2017-07-12 16:51:20 +020028import com.google.devtools.build.lib.actions.Spawn;
ulfjack4d7f8f72017-11-29 03:37:04 -080029import com.google.devtools.build.lib.actions.Spawns;
ulfjacka63da8e2017-07-12 16:51:20 +020030import com.google.devtools.build.lib.actions.UserExecException;
frazze-jobb11f7d802021-09-06 08:45:27 -070031import com.google.devtools.build.lib.actions.cache.VirtualActionInput;
ulfjacka63da8e2017-07-12 16:51:20 +020032import com.google.devtools.build.lib.analysis.BlazeDirectories;
jmmv16af94c2019-04-16 07:44:34 -070033import com.google.devtools.build.lib.exec.TreeDeleter;
László Csomorcfccdf12017-10-16 17:11:55 +020034import com.google.devtools.build.lib.exec.local.LocalEnvProvider;
Laszlo Csomora495bae2018-01-04 08:13:57 -080035import com.google.devtools.build.lib.exec.local.PosixLocalEnvProvider;
ulfjackef5b63d2018-06-15 07:19:11 -070036import com.google.devtools.build.lib.profiler.Profiler;
37import com.google.devtools.build.lib.profiler.SilentCloseable;
ulfjacka63da8e2017-07-12 16:51:20 +020038import com.google.devtools.build.lib.runtime.CommandEnvironment;
jmmv03671522020-03-12 08:21:38 -070039import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs;
cushon9b8b7902018-10-04 09:38:43 -070040import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs;
mschaller07933882020-06-24 14:38:23 -070041import com.google.devtools.build.lib.server.FailureDetails.Sandbox.Code;
ulfjacka63da8e2017-07-12 16:51:20 +020042import com.google.devtools.build.lib.shell.Command;
43import com.google.devtools.build.lib.shell.CommandException;
44import com.google.devtools.build.lib.util.OS;
frazze-jobb11f7d802021-09-06 08:45:27 -070045import com.google.devtools.build.lib.vfs.FileStatus;
ulfjacka63da8e2017-07-12 16:51:20 +020046import com.google.devtools.build.lib.vfs.FileSystem;
47import com.google.devtools.build.lib.vfs.Path;
lberki7149f572021-02-08 04:15:53 -080048import com.google.devtools.build.lib.vfs.PathFragment;
ulfjacka63da8e2017-07-12 16:51:20 +020049import com.google.devtools.build.lib.vfs.Symlinks;
50import java.io.File;
51import java.io.IOException;
ulfjackdeab0cf2017-08-08 13:08:24 +020052import java.time.Duration;
twerthbc898ca2018-07-06 03:23:26 -070053import java.util.HashMap;
ulfjacka63da8e2017-07-12 16:51:20 +020054import java.util.Map;
ulfjacka63da8e2017-07-12 16:51:20 +020055import java.util.SortedMap;
jmmvbd923e72018-04-10 14:01:34 -070056import javax.annotation.Nullable;
ulfjacka63da8e2017-07-12 16:51:20 +020057
58/** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */
59final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner {
ulfjacka63da8e2017-07-12 16:51:20 +020060
twerthbc898ca2018-07-06 03:23:26 -070061 // Since checking if sandbox is supported is expensive, we remember what we've checked.
62 private static final Map<Path, Boolean> isSupportedMap = new HashMap<>();
63
ulfjack65173662018-06-11 07:01:48 -070064 /**
65 * Returns whether the linux sandbox is supported on the local machine by running a small command
twerthbc898ca2018-07-06 03:23:26 -070066 * in it.
ulfjack65173662018-06-11 07:01:48 -070067 */
janakrca6d7ac2020-08-18 07:42:05 -070068 public static boolean isSupported(final CommandEnvironment cmdEnv) throws InterruptedException {
ulfjacka63da8e2017-07-12 16:51:20 +020069 if (OS.getCurrent() != OS.LINUX) {
70 return false;
71 }
ruperts5274d8b2017-12-20 10:45:58 -080072 if (!LinuxSandboxUtil.isSupported(cmdEnv)) {
ulfjacka63da8e2017-07-12 16:51:20 +020073 return false;
74 }
twerthbc898ca2018-07-06 03:23:26 -070075 Path linuxSandbox = LinuxSandboxUtil.getLinuxSandbox(cmdEnv);
janakrca6d7ac2020-08-18 07:42:05 -070076 Boolean isSupported;
77 synchronized (isSupportedMap) {
78 isSupported = isSupportedMap.get(linuxSandbox);
79 if (isSupported != null) {
80 return isSupported;
81 }
82 isSupported = computeIsSupported(cmdEnv, linuxSandbox);
83 isSupportedMap.put(linuxSandbox, isSupported);
84 }
85 return isSupported;
twerthbc898ca2018-07-06 03:23:26 -070086 }
ulfjacka63da8e2017-07-12 16:51:20 +020087
janakrca6d7ac2020-08-18 07:42:05 -070088 private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxSandbox)
89 throws InterruptedException {
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -070090 ImmutableList<String> linuxSandboxArgv =
Tobias Werth83ca7d12022-05-10 04:50:26 -070091 LinuxSandboxUtil.commandLineBuilder(linuxSandbox, ImmutableList.of("/bin/true"))
92 .setTimeout(Duration.ofSeconds(1))
93 .build();
aehligfb059882017-12-20 06:02:07 -080094 ImmutableMap<String, String> env = ImmutableMap.of();
ruperts5274d8b2017-12-20 10:45:58 -080095 Path execRoot = cmdEnv.getExecRoot();
ulfjacka63da8e2017-07-12 16:51:20 +020096 File cwd = execRoot.getPathFile();
97
ruperts5274d8b2017-12-20 10:45:58 -080098 Command cmd = new Command(linuxSandboxArgv.toArray(new String[0]), env, cwd);
ulfjackef5b63d2018-06-15 07:19:11 -070099 try (SilentCloseable c = Profiler.instance().profile("LinuxSandboxedSpawnRunner.isSupported")) {
ulfjackf2d45952017-08-09 15:27:49 +0200100 cmd.execute(ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream());
ulfjacka63da8e2017-07-12 16:51:20 +0200101 } catch (CommandException e) {
102 return false;
103 }
104
105 return true;
106 }
107
jmmv4e5b5b02020-03-19 10:02:10 -0700108 private final SandboxHelpers helpers;
tomluf903eb52017-10-27 12:12:11 -0400109 private final FileSystem fileSystem;
ulfjacka63da8e2017-07-12 16:51:20 +0200110 private final BlazeDirectories blazeDirs;
111 private final Path execRoot;
112 private final boolean allowNetwork;
113 private final Path linuxSandbox;
philwod3de5cc2018-04-16 06:40:19 -0700114 private final Path sandboxBase;
ulfjacka63da8e2017-07-12 16:51:20 +0200115 private final Path inaccessibleHelperFile;
116 private final Path inaccessibleHelperDir;
László Csomorcfccdf12017-10-16 17:11:55 +0200117 private final LocalEnvProvider localEnvProvider;
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700118 private final Duration timeoutKillDelay;
jmmvd3006152020-03-09 07:54:37 -0700119 @Nullable private final SandboxfsProcess sandboxfsProcess;
jmmvc2ba4a02019-03-18 14:51:45 -0700120 private final boolean sandboxfsMapSymlinkTargets;
jmmv16af94c2019-04-16 07:44:34 -0700121 private final TreeDeleter treeDeleter;
rupertsda009412017-12-22 16:59:38 -0800122
123 /**
124 * Creates a sandboxed spawn runner that uses the {@code linux-sandbox} tool.
125 *
jmmv4e5b5b02020-03-19 10:02:10 -0700126 * @param helpers common tools and state across all spawns during sandboxed execution
rupertsda009412017-12-22 16:59:38 -0800127 * @param cmdEnv the command environment to use
128 * @param sandboxBase path to the sandbox base directory
rupertsda009412017-12-22 16:59:38 -0800129 * @param inaccessibleHelperFile path to a file that is (already) inaccessible
130 * @param inaccessibleHelperDir path to a directory that is (already) inaccessible
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700131 * @param timeoutKillDelay an additional grace period before killing timing out commands
jmmvbd923e72018-04-10 14:01:34 -0700132 * @param sandboxfsProcess instance of the sandboxfs process to use; may be null for none, in
133 * which case the runner uses a symlinked sandbox
jmmvc2ba4a02019-03-18 14:51:45 -0700134 * @param sandboxfsMapSymlinkTargets map the targets of symlinks within the sandbox if true
rupertsda009412017-12-22 16:59:38 -0800135 */
136 LinuxSandboxedSpawnRunner(
jmmv4e5b5b02020-03-19 10:02:10 -0700137 SandboxHelpers helpers,
rupertsda009412017-12-22 16:59:38 -0800138 CommandEnvironment cmdEnv,
139 Path sandboxBase,
rupertsda009412017-12-22 16:59:38 -0800140 Path inaccessibleHelperFile,
141 Path inaccessibleHelperDir,
jmmvbd923e72018-04-10 14:01:34 -0700142 Duration timeoutKillDelay,
jmmvc2ba4a02019-03-18 14:51:45 -0700143 @Nullable SandboxfsProcess sandboxfsProcess,
jmmv16af94c2019-04-16 07:44:34 -0700144 boolean sandboxfsMapSymlinkTargets,
145 TreeDeleter treeDeleter) {
philwod3de5cc2018-04-16 06:40:19 -0700146 super(cmdEnv);
jmmv4e5b5b02020-03-19 10:02:10 -0700147 this.helpers = helpers;
tomluf903eb52017-10-27 12:12:11 -0400148 this.fileSystem = cmdEnv.getRuntime().getFileSystem();
ulfjacka63da8e2017-07-12 16:51:20 +0200149 this.blazeDirs = cmdEnv.getDirectories();
150 this.execRoot = cmdEnv.getExecRoot();
jmmv4e5b5b02020-03-19 10:02:10 -0700151 this.allowNetwork = helpers.shouldAllowNetwork(cmdEnv.getOptions());
ruperts5274d8b2017-12-20 10:45:58 -0800152 this.linuxSandbox = LinuxSandboxUtil.getLinuxSandbox(cmdEnv);
philwod3de5cc2018-04-16 06:40:19 -0700153 this.sandboxBase = sandboxBase;
ulfjacka63da8e2017-07-12 16:51:20 +0200154 this.inaccessibleHelperFile = inaccessibleHelperFile;
155 this.inaccessibleHelperDir = inaccessibleHelperDir;
rupertsda009412017-12-22 16:59:38 -0800156 this.timeoutKillDelay = timeoutKillDelay;
jmmvbd923e72018-04-10 14:01:34 -0700157 this.sandboxfsProcess = sandboxfsProcess;
jmmvc2ba4a02019-03-18 14:51:45 -0700158 this.sandboxfsMapSymlinkTargets = sandboxfsMapSymlinkTargets;
Laszlo Csomor5bfa5842018-01-15 06:01:25 -0800159 this.localEnvProvider = new PosixLocalEnvProvider(cmdEnv.getClientEnv());
jmmv16af94c2019-04-16 07:44:34 -0700160 this.treeDeleter = treeDeleter;
ulfjacka63da8e2017-07-12 16:51:20 +0200161 }
162
163 @Override
ulfjack40349652019-09-02 03:06:42 -0700164 protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context)
janakr300c11e2021-05-18 12:10:43 -0700165 throws IOException, ForbiddenActionInputException, ExecException, InterruptedException {
philwof032268f2018-04-25 06:16:05 -0700166 // Each invocation of "exec" gets its own sandbox base.
167 // Note that the value returned by context.getId() is only unique inside one given SpawnRunner,
168 // so we have to prefix our name to turn it into a globally unique value.
169 Path sandboxPath =
170 sandboxBase.getRelative(getName()).getRelative(Integer.toString(context.getId()));
171 sandboxPath.getParentDirectory().createDirectory();
philwod3de5cc2018-04-16 06:40:19 -0700172 sandboxPath.createDirectory();
dannark2a5512f2018-04-04 14:02:13 -0700173
philwod3de5cc2018-04-16 06:40:19 -0700174 // b/64689608: The execroot of the sandboxed process must end with the workspace name, just like
175 // the normal execroot does.
jmmv45d652a2020-03-09 08:48:16 -0700176 String workspaceName = execRoot.getBaseName();
177 Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(workspaceName);
philwod3de5cc2018-04-16 06:40:19 -0700178 sandboxExecRoot.getParentDirectory().createDirectory();
179 sandboxExecRoot.createDirectory();
László Csomorcfccdf12017-10-16 17:11:55 +0200180
laszlocsomord2cb96a2020-01-14 02:35:04 -0800181 ImmutableMap<String, String> environment =
felly472320c2018-10-29 14:27:00 -0700182 localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp");
Laszlo Csomor6ab93a62018-01-08 00:45:57 -0800183
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700184 ImmutableSet<Path> writableDirs = getWritableDirs(sandboxExecRoot, environment);
jmmv03671522020-03-12 08:21:38 -0700185
186 SandboxInputs inputs =
jmmv4e5b5b02020-03-19 10:02:10 -0700187 helpers.processInputFiles(
lberki7149f572021-02-08 04:15:53 -0800188 context.getInputMapping(PathFragment.EMPTY_FRAGMENT),
lberki7149f572021-02-08 04:15:53 -0800189 execRoot);
jmmv4e5b5b02020-03-19 10:02:10 -0700190 SandboxOutputs outputs = helpers.getOutputs(spawn);
jmmv03671522020-03-12 08:21:38 -0700191
tomlu29e306d2018-04-19 05:41:44 -0700192 Duration timeout = context.getTimeout();
rupertsda009412017-12-22 16:59:38 -0800193
194 LinuxSandboxUtil.CommandLineBuilder commandLineBuilder =
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700195 LinuxSandboxUtil.commandLineBuilder(linuxSandbox, spawn.getArguments())
jmmv2bb3b732020-09-03 10:04:27 -0700196 .addExecutionInfo(spawn.getExecutionInfo())
rupertsda009412017-12-22 16:59:38 -0800197 .setWritableFilesAndDirectories(writableDirs)
ajurkowski36380522021-06-11 17:16:59 -0700198 .setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath))
rupertsda009412017-12-22 16:59:38 -0800199 .setBindMounts(getReadOnlyBindMounts(blazeDirs, sandboxExecRoot))
200 .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname)
Austin Schuh520e9072018-08-16 06:50:54 -0700201 .setCreateNetworkNamespace(
202 !(allowNetwork
203 || Spawns.requiresNetwork(
204 spawn, getSandboxOptions().defaultSandboxAllowNetwork)))
philwod3de5cc2018-04-16 06:40:19 -0700205 .setUseDebugMode(getSandboxOptions().sandboxDebug)
206 .setKillDelay(timeoutKillDelay);
rupertsda009412017-12-22 16:59:38 -0800207
208 if (!timeout.isZero()) {
209 commandLineBuilder.setTimeout(timeout);
210 }
philwod3de5cc2018-04-16 06:40:19 -0700211
rupertsda009412017-12-22 16:59:38 -0800212 if (spawn.getExecutionInfo().containsKey(ExecutionRequirements.REQUIRES_FAKEROOT)) {
213 commandLineBuilder.setUseFakeRoot(true);
214 } else if (getSandboxOptions().sandboxFakeUsername) {
215 commandLineBuilder.setUseFakeUsername(true);
216 }
217
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700218 Path statisticsPath = null;
rupertsda009412017-12-22 16:59:38 -0800219 if (getSandboxOptions().collectLocalSandboxExecutionStatistics) {
Philipp Wollermann23e1c5d2018-03-23 07:39:27 -0700220 statisticsPath = sandboxPath.getRelative("stats.out");
221 commandLineBuilder.setStatisticsPath(statisticsPath);
rupertsda009412017-12-22 16:59:38 -0800222 }
223
jmmvbd923e72018-04-10 14:01:34 -0700224 if (sandboxfsProcess != null) {
ulfjack40349652019-09-02 03:06:42 -0700225 return new SandboxfsSandboxedSpawn(
226 sandboxfsProcess,
227 sandboxPath,
jmmv45d652a2020-03-09 08:48:16 -0700228 workspaceName,
ulfjack40349652019-09-02 03:06:42 -0700229 commandLineBuilder.build(),
230 environment,
jmmv03671522020-03-12 08:21:38 -0700231 inputs,
ulfjack40349652019-09-02 03:06:42 -0700232 outputs,
233 ImmutableSet.of(),
234 sandboxfsMapSymlinkTargets,
235 treeDeleter,
236 statisticsPath);
frazze-jobb11f7d802021-09-06 08:45:27 -0700237 } else if (getSandboxOptions().useHermetic) {
238 commandLineBuilder.setHermeticSandboxPath(sandboxPath);
239 return new HardlinkedSandboxedSpawn(
240 sandboxPath,
241 sandboxExecRoot,
242 commandLineBuilder.build(),
243 environment,
244 inputs,
245 outputs,
246 writableDirs,
247 treeDeleter,
248 statisticsPath,
249 getSandboxOptions().sandboxDebug);
jmmvbd923e72018-04-10 14:01:34 -0700250 } else {
ulfjack40349652019-09-02 03:06:42 -0700251 return new SymlinkedSandboxedSpawn(
252 sandboxPath,
253 sandboxExecRoot,
254 commandLineBuilder.build(),
255 environment,
jmmv03671522020-03-12 08:21:38 -0700256 inputs,
ulfjack40349652019-09-02 03:06:42 -0700257 outputs,
258 writableDirs,
259 treeDeleter,
larsrc1adb5122021-06-16 03:39:10 -0700260 statisticsPath,
261 getSandboxOptions().reuseSandboxDirectories,
262 sandboxBase,
263 spawn.getMnemonic());
jmmvbd923e72018-04-10 14:01:34 -0700264 }
ulfjacka63da8e2017-07-12 16:51:20 +0200265 }
266
267 @Override
olaolaec085532018-02-22 10:33:28 -0800268 public String getName() {
ulfjacka63da8e2017-07-12 16:51:20 +0200269 return "linux-sandbox";
270 }
271
272 @Override
Laszlo Csomor6cc2ad82018-01-11 05:30:13 -0800273 protected ImmutableSet<Path> getWritableDirs(Path sandboxExecRoot, Map<String, String> env)
274 throws IOException {
ulfjacka63da8e2017-07-12 16:51:20 +0200275 ImmutableSet.Builder<Path> writableDirs = ImmutableSet.builder();
Laszlo Csomor6cc2ad82018-01-11 05:30:13 -0800276 writableDirs.addAll(super.getWritableDirs(sandboxExecRoot, env));
ulfjacka63da8e2017-07-12 16:51:20 +0200277
278 FileSystem fs = sandboxExecRoot.getFileSystem();
279 writableDirs.add(fs.getPath("/dev/shm").resolveSymbolicLinks());
280 writableDirs.add(fs.getPath("/tmp"));
281
282 return writableDirs.build();
283 }
284
ulfjacka63da8e2017-07-12 16:51:20 +0200285 private SortedMap<Path, Path> getReadOnlyBindMounts(
286 BlazeDirectories blazeDirs, Path sandboxExecRoot) throws UserExecException {
tomluf903eb52017-10-27 12:12:11 -0400287 Path tmpPath = fileSystem.getPath("/tmp");
ulfjacka63da8e2017-07-12 16:51:20 +0200288 final SortedMap<Path, Path> bindMounts = Maps.newTreeMap();
289 if (blazeDirs.getWorkspace().startsWith(tmpPath)) {
290 bindMounts.put(blazeDirs.getWorkspace(), blazeDirs.getWorkspace());
291 }
292 if (blazeDirs.getOutputBase().startsWith(tmpPath)) {
293 bindMounts.put(blazeDirs.getOutputBase(), blazeDirs.getOutputBase());
294 }
295 for (ImmutableMap.Entry<String, String> additionalMountPath :
Benjamin Peterson54c6f322017-08-04 15:56:55 +0200296 getSandboxOptions().sandboxAdditionalMounts) {
ulfjacka63da8e2017-07-12 16:51:20 +0200297 try {
tomluf903eb52017-10-27 12:12:11 -0400298 final Path mountTarget = fileSystem.getPath(additionalMountPath.getValue());
ulfjacka63da8e2017-07-12 16:51:20 +0200299 // If source path is relative, treat it as a relative path inside the execution root
300 final Path mountSource = sandboxExecRoot.getRelative(additionalMountPath.getKey());
301 // If a target has more than one source path, the latter one will take effect.
302 bindMounts.put(mountTarget, mountSource);
303 } catch (IllegalArgumentException e) {
304 throw new UserExecException(
mschaller07933882020-06-24 14:38:23 -0700305 createFailureDetail(
306 String.format("Error occurred when analyzing bind mount pairs. %s", e.getMessage()),
307 Code.BIND_MOUNT_ANALYSIS_FAILURE));
ulfjacka63da8e2017-07-12 16:51:20 +0200308 }
309 }
310 for (Path inaccessiblePath : getInaccessiblePaths()) {
311 if (inaccessiblePath.isDirectory(Symlinks.NOFOLLOW)) {
312 bindMounts.put(inaccessiblePath, inaccessibleHelperDir);
313 } else {
314 bindMounts.put(inaccessiblePath, inaccessibleHelperFile);
315 }
316 }
317 validateBindMounts(bindMounts);
318 return bindMounts;
319 }
320
321 /**
larsrcef9ea7a2021-02-12 06:19:07 -0800322 * This method does the following things:
323 *
324 * <ul>
325 * <li>If mount source does not exist on the host system, throw an error message
326 * <li>If mount target exists, check whether the source and target are of the same type
327 * <li>If mount target does not exist on the host system, throw an error message
328 * </ul>
ulfjacka63da8e2017-07-12 16:51:20 +0200329 *
330 * @param bindMounts the bind mounts map with target as key and source as value
larsrcef9ea7a2021-02-12 06:19:07 -0800331 * @throws UserExecException if any of the mount points are not valid
ulfjacka63da8e2017-07-12 16:51:20 +0200332 */
333 private void validateBindMounts(SortedMap<Path, Path> bindMounts) throws UserExecException {
larsrcef9ea7a2021-02-12 06:19:07 -0800334 for (Map.Entry<Path, Path> bindMount : bindMounts.entrySet()) {
ulfjacka63da8e2017-07-12 16:51:20 +0200335 final Path source = bindMount.getValue();
336 final Path target = bindMount.getKey();
337 // Mount source should exist in the file system
338 if (!source.exists()) {
mschaller07933882020-06-24 14:38:23 -0700339 throw new UserExecException(
340 createFailureDetail(
341 String.format("Mount source '%s' does not exist.", source),
342 Code.MOUNT_SOURCE_DOES_NOT_EXIST));
ulfjacka63da8e2017-07-12 16:51:20 +0200343 }
344 // If target exists, but is not of the same type as the source, then we cannot mount it.
345 if (target.exists()) {
346 boolean areBothDirectories = source.isDirectory() && target.isDirectory();
347 boolean isSourceFile = source.isFile() || source.isSymbolicLink();
348 boolean isTargetFile = target.isFile() || target.isSymbolicLink();
349 boolean areBothFiles = isSourceFile && isTargetFile;
350 if (!(areBothDirectories || areBothFiles)) {
351 // Source and target are not of the same type; we cannot mount it.
352 throw new UserExecException(
mschaller07933882020-06-24 14:38:23 -0700353 createFailureDetail(
354 String.format(
larsrcef9ea7a2021-02-12 06:19:07 -0800355 "Mount target '%s' is a %s but mount source '%s' is a %s, they must be the"
356 + " same type.",
357 target,
358 (isTargetFile ? "file" : "directory"),
359 source,
360 (isSourceFile ? "file" : "directory")),
mschaller07933882020-06-24 14:38:23 -0700361 Code.MOUNT_SOURCE_TARGET_TYPE_MISMATCH));
ulfjacka63da8e2017-07-12 16:51:20 +0200362 }
363 } else {
364 // Mount target should exist in the file system
365 throw new UserExecException(
mschaller07933882020-06-24 14:38:23 -0700366 createFailureDetail(
367 String.format(
368 "Mount target '%s' does not exist. Bazel only supports bind mounting on top of "
369 + "existing files/directories. Please create an empty file or directory at "
370 + "the mount target path according to the type of mount source.",
371 target),
372 Code.MOUNT_TARGET_DOES_NOT_EXIST));
ulfjacka63da8e2017-07-12 16:51:20 +0200373 }
374 }
375 }
jmmvb93e61b2019-04-15 10:00:10 -0700376
377 @Override
frazze-jobb11f7d802021-09-06 08:45:27 -0700378 public void verifyPostCondition(
379 Spawn originalSpawn, SandboxedSpawn sandbox, SpawnExecutionContext context)
380 throws IOException, ForbiddenActionInputException {
381 if (getSandboxOptions().useHermetic) {
382 checkForConcurrentModifications(context);
383 }
384 }
385
386 private void checkForConcurrentModifications(SpawnExecutionContext context)
387 throws IOException, ForbiddenActionInputException {
Googler9eb783e2022-06-01 03:20:56 -0700388 for (ActionInput input : context.getInputMapping(PathFragment.EMPTY_FRAGMENT).values()) {
frazze-jobb11f7d802021-09-06 08:45:27 -0700389 if (input instanceof VirtualActionInput) {
390 continue;
391 }
392
393 FileArtifactValue metadata = context.getMetadataProvider().getMetadata(input);
394 Path path = execRoot.getRelative(input.getExecPath());
395
396 try {
397 if (wasModifiedSinceDigest(metadata.getContentsProxy(), path)) {
398 throw new IOException("input dependency " + path + " was modified during execution.");
399 }
400 } catch (UnsupportedOperationException e) {
401 throw new IOException(
402 "input dependency "
403 + path
404 + " could not be checked for modifications during execution.",
405 e);
406 }
407 }
408 }
409
410 private boolean wasModifiedSinceDigest(FileContentsProxy proxy, Path path) throws IOException {
411 if (proxy == null) {
412 return false;
413 }
414 FileStatus stat = path.statIfFound(Symlinks.FOLLOW);
415 return stat == null || !stat.isFile() || proxy.isModified(FileContentsProxy.create(stat));
416 }
417
418 @Override
jmmv16af94c2019-04-16 07:44:34 -0700419 public void cleanupSandboxBase(Path sandboxBase, TreeDeleter treeDeleter) throws IOException {
420 // Delete the inaccessible files synchronously, bypassing the treeDeleter. They are only a
421 // couple of files that can be deleted fast, and ensuring they are gone at the end of every
422 // build avoids annoying permission denied errors if the user happens to run "rm -rf" on the
423 // output base. (We have some tests that do that.)
jmmvb93e61b2019-04-15 10:00:10 -0700424 if (inaccessibleHelperDir.exists()) {
425 inaccessibleHelperDir.chmod(0700);
426 inaccessibleHelperDir.deleteTree();
427 }
428 if (inaccessibleHelperFile.exists()) {
429 inaccessibleHelperFile.chmod(0600);
430 inaccessibleHelperFile.delete();
431 }
432
jmmv16af94c2019-04-16 07:44:34 -0700433 super.cleanupSandboxBase(sandboxBase, treeDeleter);
jmmvb93e61b2019-04-15 10:00:10 -0700434 }
ulfjacka63da8e2017-07-12 16:51:20 +0200435}