| // Copyright 2018 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 static com.google.common.base.Preconditions.checkArgument; | |
| import static com.google.common.base.Preconditions.checkState; | |
| import com.google.devtools.build.lib.vfs.FileSystem; | |
| import com.google.devtools.build.lib.vfs.Path; | |
| import com.google.devtools.build.lib.vfs.PathFragment; | |
| import java.io.IOException; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Set; | |
| /** | |
| * A fake in-process sandboxfs implementation that uses symlinks on the Bazel file system API. | |
| */ | |
| final class FakeSandboxfsProcess implements SandboxfsProcess { | |
| /** File system on which the fake sandboxfs instance operates. */ | |
| private final FileSystem fileSystem; | |
| /** Directory on which the sandboxfs is serving. */ | |
| private final PathFragment mountPoint; | |
| /** | |
| * Tracker for the sandbox names that currently exist in the sandbox. | |
| * | |
| * <p>Used to catch mistakes in creating an already-existing sandbox or deleting a non-existent | |
| * sandbox. | |
| */ | |
| private final Set<String> activeSandboxes = new HashSet<>(); | |
| /** | |
| * Whether this "process" is valid or not. Used to better represent the workflow of a real | |
| * sandboxfs subprocess. | |
| */ | |
| private boolean alive = true; | |
| /** | |
| * Initializes a new sandboxfs process instance. | |
| * | |
| * <p>To better represent reality, this ensures that the mount point is present and valid. | |
| * | |
| * @param fileSystem file system on which the fake sandboxfs instance operates | |
| * @param mountPoint directory on which the sandboxfs instance is serving | |
| * @throws IOException if the mount point is missing | |
| */ | |
| FakeSandboxfsProcess(FileSystem fileSystem, PathFragment mountPoint) throws IOException { | |
| if (!fileSystem.getPath(mountPoint).exists()) { | |
| throw new IOException("Mount point " + mountPoint + " does not exist"); | |
| } else if (!fileSystem.getPath(mountPoint).isDirectory()) { | |
| throw new IOException("Mount point " + mountPoint + " is not a directory"); | |
| } | |
| this.fileSystem = fileSystem; | |
| this.mountPoint = mountPoint; | |
| } | |
| @Override | |
| public Path getMountPoint() { | |
| return fileSystem.getPath(mountPoint); | |
| } | |
| @Override | |
| public synchronized boolean isAlive() { | |
| return alive; | |
| } | |
| @Override | |
| public synchronized void destroy() { | |
| alive = false; | |
| } | |
| @Override | |
| public synchronized void createSandbox(String name, List<Mapping> mappings) throws IOException { | |
| checkState(alive, "Cannot be called after destroy()"); | |
| checkArgument(!PathFragment.containsSeparator(name)); | |
| checkArgument(!activeSandboxes.contains(name), "Sandbox %s mapped more than once", name); | |
| activeSandboxes.add(name); | |
| for (Mapping mapping : mappings) { | |
| checkArgument( | |
| mapping.path().isAbsolute(), | |
| "Mapping specifications are expected to be" + "absolute but %s is not", | |
| mapping.path()); | |
| Path link = | |
| fileSystem.getPath(mountPoint).getRelative(name).getRelative(mapping.path().toRelative()); | |
| link.getParentDirectory().createDirectoryAndParents(); | |
| Path target = fileSystem.getPath(mapping.target()); | |
| if (!target.exists()) { | |
| // Not a requirement for the creation of a symbolic link but this reflects the behavior of | |
| // the real sandboxfs. | |
| throw new IOException("Target " + mapping.target() + " does not exist"); | |
| } | |
| if (target.isSymbolicLink()) { | |
| // sandboxfs is able to expose symlinks as they are in the underlying file system. Mimic | |
| // this behavior by respecting the symlink in that case, instead of just creating a new | |
| // symlink that points to the actual target. | |
| link.createSymbolicLink(target.readSymbolicLink()); | |
| } else { | |
| link.createSymbolicLink(fileSystem.getPath(mapping.target())); | |
| } | |
| } | |
| } | |
| @Override | |
| public synchronized void destroySandbox(String name) throws IOException { | |
| checkState(alive, "Cannot be called after destroy()"); | |
| checkArgument(!PathFragment.containsSeparator(name)); | |
| checkArgument(activeSandboxes.contains(name), "Sandbox %s not previously created", name); | |
| activeSandboxes.remove(name); | |
| fileSystem.getPath(mountPoint).getRelative(name).deleteTree(); | |
| } | |
| } |