diff options
author | 2016-08-31 12:07:40 +0000 | |
---|---|---|
committer | 2016-08-31 14:51:58 +0000 | |
commit | 5a50b4f1cd3eaaf52893a54debeae90ed1c65c0f (patch) | |
tree | e76582267ad3ac110159df743e331d5ab6fbc43a /src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java | |
parent | 5b261d5ae723165b0d716b865612afcff1d48cd1 (diff) |
Refactor our sandboxing code.
--
MOS_MIGRATED_REVID=131817068
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java | 115 |
1 files changed, 115 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java b/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java new file mode 100644 index 0000000000..de0f31f46e --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SymlinkedExecRoot.java @@ -0,0 +1,115 @@ +// Copyright 2016 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.common.io.Files; +import com.google.devtools.build.lib.util.Preconditions; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.io.IOException; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Creates an execRoot for a Spawn that contains input files as symlinks to their original + * destination. + */ +final class SymlinkedExecRoot implements SandboxExecRoot { + + private final Path sandboxExecRoot; + + public SymlinkedExecRoot(Path sandboxExecRoot) { + this.sandboxExecRoot = sandboxExecRoot; + } + + @Override + public void createFileSystem( + Map<PathFragment, Path> inputs, Collection<PathFragment> outputs, Set<Path> writableDirs) + throws IOException { + Set<Path> createdDirs = new HashSet<>(); + FileSystemUtils.createDirectoryAndParentsWithCache(createdDirs, sandboxExecRoot); + createParentDirectoriesForInputs(createdDirs, inputs.keySet()); + createSymlinksForInputs(inputs); + createWritableDirectories(createdDirs, writableDirs); + createDirectoriesForOutputs(createdDirs, outputs); + } + + /** + * No input can be a child of another input, because otherwise we might try to create a symlink + * below another symlink we created earlier - which means we'd actually end up writing somewhere + * in the workspace. + * + * <p>If all inputs were regular files, this situation could naturally not happen - but + * unfortunately, we might get the occasional action that has directories in its inputs. + * + * <p>Creating all parent directories first ensures that we can safely create symlinks to + * directories, too, because we'll get an IOException with EEXIST if inputs happen to be nested + * once we start creating the symlinks for all inputs. + */ + private void createParentDirectoriesForInputs(Set<Path> createdDirs, Set<PathFragment> inputs) + throws IOException { + for (PathFragment inputPath : inputs) { + Path dir = sandboxExecRoot.getRelative(inputPath).getParentDirectory(); + Preconditions.checkArgument(dir.startsWith(sandboxExecRoot)); + FileSystemUtils.createDirectoryAndParentsWithCache(createdDirs, dir); + } + } + + private void createSymlinksForInputs(Map<PathFragment, Path> inputs) throws IOException { + // All input files are relative to the execroot. + for (Entry<PathFragment, Path> entry : inputs.entrySet()) { + Path key = sandboxExecRoot.getRelative(entry.getKey()); + key.createSymbolicLink(entry.getValue()); + } + } + + private void createWritableDirectories(Set<Path> createdDirs, Set<Path> writableDirs) + throws IOException { + for (Path writablePath : writableDirs) { + if (writablePath.startsWith(sandboxExecRoot)) { + FileSystemUtils.createDirectoryAndParentsWithCache(createdDirs, writablePath); + } + } + } + + /** Prepare the output directories in the sandbox. */ + private void createDirectoriesForOutputs(Set<Path> createdDirs, Collection<PathFragment> outputs) + throws IOException { + for (PathFragment output : outputs) { + FileSystemUtils.createDirectoryAndParentsWithCache( + createdDirs, sandboxExecRoot.getRelative(output.getParentDirectory())); + } + } + + /** Moves all {@code outputs} to {@code execRoot}. */ + @Override + public void copyOutputs(Path execRoot, Collection<PathFragment> outputs) throws IOException { + Set<Path> createdDirs = new HashSet<>(); + for (PathFragment output : outputs) { + Path source = sandboxExecRoot.getRelative(output); + if (source.isFile() || source.isSymbolicLink()) { + FileSystemUtils.createDirectoryAndParentsWithCache( + createdDirs, execRoot.getRelative(output.getParentDirectory())); + + Path target = execRoot.getRelative(output); + Files.move(source.getPathFile(), target.getPathFile()); + } + } + } +} |