// 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.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; 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 com.google.devtools.build.lib.vfs.Symlinks; import java.io.IOException; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link SandboxfsSandboxedSpawn}. */ @RunWith(JUnit4.class) public class SandboxfsSandboxedSpawnTest extends SandboxTestCase { private Path workspaceDir; private Path outerDir; private SandboxfsProcess sandboxfs; @Before public final void setupTestDirs() throws IOException { workspaceDir = testRoot.getRelative("workspace"); workspaceDir.createDirectory(); outerDir = testRoot.getRelative("scratch"); outerDir.createDirectory(); Path mountPoint = testRoot.getRelative("sandbox"); mountPoint.createDirectory(); sandboxfs = new FakeSandboxfsProcess( mountPoint.getFileSystem(), mountPoint.asFragment()); } @Test public void testCreateFileSystem() throws Exception { Path helloTxt = workspaceDir.getRelative("hello.txt"); FileSystemUtils.createEmptyFile(helloTxt); SandboxedSpawn spawn = new SandboxfsSandboxedSpawn( sandboxfs, outerDir, ImmutableList.of("/bin/true"), ImmutableMap.of(), ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of(PathFragment.create("wow/writable"))); spawn.createFileSystem(); Path execRoot = spawn.getSandboxExecRoot(); assertThat(execRoot.getRelative("such/input.txt").isSymbolicLink()).isTrue(); assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()).isEqualTo(helloTxt); assertThat(execRoot.getRelative("very").isDirectory()).isTrue(); assertThat(execRoot.getRelative("wow/writable").isDirectory()).isTrue(); } @Test public void testDelete() throws Exception { Path helloTxt = workspaceDir.getRelative("hello.txt"); FileSystemUtils.createEmptyFile(helloTxt); SandboxedSpawn spawn = new SandboxfsSandboxedSpawn( sandboxfs, outerDir, ImmutableList.of("/bin/true"), ImmutableMap.of(), ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of(PathFragment.create("wow/writable"))); spawn.createFileSystem(); Path execRoot = spawn.getSandboxExecRoot(); // Pretend to do some work inside the execRoot. execRoot.getRelative("tempdir").createDirectory(); FileSystemUtils.createEmptyFile(execRoot.getRelative("very/output.txt")); FileSystemUtils.createEmptyFile(execRoot.getRelative("wow/writable/temp.txt")); spawn.delete(); assertThat(execRoot.exists()).isFalse(); } @Test public void testCopyOutputs() throws Exception { // These tests are very simple because we just rely on SandboxedSpawnTest.testMoveOutputs to // properly verify all corner cases. PathFragment outputFile = PathFragment.create("very/output.txt"); SandboxedSpawn spawn = new SandboxfsSandboxedSpawn( sandboxfs, outerDir, ImmutableList.of("/bin/true"), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of(outputFile), ImmutableSet.of()); spawn.createFileSystem(); Path execRoot = spawn.getSandboxExecRoot(); FileSystemUtils.createEmptyFile(execRoot.getRelative(outputFile)); Path outputsDir = testRoot.getRelative("outputs"); outputsDir.getRelative(outputFile.getParentDirectory()).createDirectoryAndParents(); spawn.copyOutputs(outputsDir); assertThat(outputsDir.getRelative(outputFile).isFile(Symlinks.NOFOLLOW)).isTrue(); } @Test public void testSymlinksAreNotExposed() throws Exception { Path helloTxt = workspaceDir.getRelative("dir1/hello.txt"); helloTxt.getParentDirectory().createDirectory(); FileSystemUtils.createEmptyFile(helloTxt); Path linkToHello = workspaceDir.getRelative("dir2/link-to-hello"); linkToHello.getParentDirectory().createDirectory(); linkToHello.createSymbolicLink(PathFragment.create("../dir1/hello.txt")); // Ensure that the symlink we have created has a relative target, as otherwise we wouldn't // exercise the functionality we are trying to test. assertThat(linkToHello.readSymbolicLink().isAbsolute()).isFalse(); SandboxedSpawn spawn = new SandboxfsSandboxedSpawn( sandboxfs, outerDir, ImmutableList.of("/bin/true"), ImmutableMap.of(), ImmutableMap.of(PathFragment.create("such/input.txt"), linkToHello), ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of()); spawn.createFileSystem(); Path execRoot = spawn.getSandboxExecRoot(); assertThat(execRoot.getRelative("such/input.txt").isSymbolicLink()).isTrue(); // We expect the target of the input file to be the final target of the input in use, not the // intermediate symlink we specified. Otherwise, the exposed symlink in the sandbox would be // broken because its relative target is not transitively exposed. assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()).isEqualTo(helloTxt); } }