| // 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.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, SandboxCreator creator) 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); |
| |
| creator.create( |
| (path, underlyingPath, writable) -> { |
| checkArgument( |
| path.isAbsolute(), |
| "Mapping specifications are expected to be absolute but %s is not", |
| path); |
| Path link = |
| fileSystem.getPath(mountPoint).getRelative(name).getRelative(path.toRelative()); |
| link.getParentDirectory().createDirectoryAndParents(); |
| |
| Path target = fileSystem.getPath(underlyingPath); |
| 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 " + underlyingPath + " 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(underlyingPath)); |
| } |
| }); |
| } |
| |
| @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(); |
| } |
| } |