blob: f05ed244a8e66512d9bfe3830ded903903c7387f [file] [log] [blame]
// Copyright 2017 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.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.runtime.BlazeWorkspace;
import com.google.devtools.build.lib.server.FailureDetails.Sandbox.Code;
import com.google.devtools.build.lib.util.OsUtils;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import java.io.IOException;
import java.util.Map;
/** Utility functions for the {@code linux-sandbox} embedded tool. */
public final class LinuxSandboxUtil {
private static final String LINUX_SANDBOX = "linux-sandbox" + OsUtils.executableExtension();
/** Returns whether using the {@code linux-sandbox} is supported in the command environment. */
public static boolean isSupported(BlazeWorkspace blazeWorkspace) {
// We can only use the linux-sandbox if the linux-sandbox exists in the embedded tools.
// This might not always be the case, e.g. while bootstrapping.
return getLinuxSandbox(blazeWorkspace) != null;
}
/** Returns the path of the {@code linux-sandbox} binary, or null if it doesn't exist. */
public static Path getLinuxSandbox(BlazeWorkspace blazeWorkspace) {
return blazeWorkspace.getBinTools().getEmbeddedPath(LINUX_SANDBOX);
}
/**
* This method does the following things:
*
* <ul>
* <li>If mount source does not exist on the host system, throw an error message
* <li>If mount target exists, check whether the source and target are of the same type
* <li>If mount target does not exist on the host system, throw an error message
* </ul>
*
* @param bindMounts the bind mounts map with target as key and source as value
* @throws UserExecException if any of the mount points are not valid
*/
public static void validateBindMounts(Map<Path, Path> bindMounts) throws UserExecException {
for (Map.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(
SandboxHelpers.createFailureDetail(
String.format("Mount source '%s' does not exist.", source),
Code.MOUNT_SOURCE_DOES_NOT_EXIST));
}
// 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(
SandboxHelpers.createFailureDetail(
String.format(
"Mount target '%s' is a %s but mount source '%s' is a %s, they must be the"
+ " same type.",
target,
(isTargetFile ? "file" : "directory"),
source,
(isSourceFile ? "file" : "directory")),
Code.MOUNT_SOURCE_TARGET_TYPE_MISMATCH));
}
} else {
// Mount target should exist in the file system
throw new UserExecException(
SandboxHelpers.createFailureDetail(
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),
Code.MOUNT_TARGET_DOES_NOT_EXIST));
}
}
}
/** Returns a newly created inaccessible file underneath the given directory. */
public static Path getInaccessibleHelperFile(Path sandboxBase) throws IOException {
// The order of the permissions settings calls matters, see
// https://github.com/bazelbuild/bazel/issues/16364
Path inaccessibleHelperFile = sandboxBase.getRelative(SandboxHelpers.INACCESSIBLE_HELPER_FILE);
FileSystemUtils.touchFile(inaccessibleHelperFile);
inaccessibleHelperFile.setExecutable(false);
inaccessibleHelperFile.setWritable(false);
inaccessibleHelperFile.setReadable(false);
return inaccessibleHelperFile;
}
/** Returns a newly created inaccessible directory underneath the given directory. */
public static Path getInaccessibleHelperDir(Path sandboxBase) throws IOException {
// The order of the permissions settings calls matters, see
// https://github.com/bazelbuild/bazel/issues/16364
Path inaccessibleHelperDir = sandboxBase.getRelative(SandboxHelpers.INACCESSIBLE_HELPER_DIR);
inaccessibleHelperDir.createDirectory();
inaccessibleHelperDir.setExecutable(false);
inaccessibleHelperDir.setWritable(false);
inaccessibleHelperDir.setReadable(false);
return inaccessibleHelperDir;
}
}