From e772a1279a4f6e1ffa6e849ec466efacc6578ecc Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 13:31:44 +0200 Subject: [PATCH 01/32] Revert "Make tree artifacts that are symlinks to absolute paths work correctly." This reverts commit bf6ebe9f7c428e15b7c4d7e86a762b7470f97d5b. --- .../build/lib/skyframe/ActionOutputMetadataStore.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java index 9af32fd05994b3..813dbe32b196c9 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java @@ -337,9 +337,7 @@ private TreeArtifactValue constructTreeArtifactValueFromFilesystem(SpecialArtifa } if (materializationExecPath == null) { - PathFragment realpath = treeDir.resolveSymbolicLinks().asFragment(); - materializationExecPath = - realpath.startsWith(execRoot) ? realpath.relativeTo(execRoot) : realpath; + materializationExecPath = treeDir.resolveSymbolicLinks().asFragment().relativeTo(execRoot); } tree.setMaterializationExecPath(materializationExecPath); From 501429757d5755b9ca67e255f784ab3091d9d134 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 13:38:46 +0200 Subject: [PATCH 02/32] Revert "Fix two issues with `--incompatible_sandbox_hermetic_tmp` that manifested themselves when the output base was under `/tmp`:" This reverts commit fb6658c86164b81205a056a9a54975a62f2f957a. --- .../build/lib/actions/FileArtifactValue.java | 47 ++------ .../google/devtools/build/lib/sandbox/BUILD | 1 - .../sandbox/DarwinSandboxedSpawnRunner.java | 1 - .../sandbox/DockerSandboxedSpawnRunner.java | 1 - .../sandbox/LinuxSandboxedSpawnRunner.java | 1 - .../ProcessWrapperSandboxedSpawnRunner.java | 1 - .../build/lib/sandbox/SandboxHelpers.java | 111 +++--------------- .../sandbox/WindowsSandboxedSpawnRunner.java | 1 - .../skyframe/ActionOutputMetadataStore.java | 68 ++++------- .../build/lib/worker/WorkerSpawnRunner.java | 1 - .../google/devtools/build/lib/sandbox/BUILD | 2 - .../build/lib/sandbox/SandboxHelpersTest.java | 108 +---------------- .../ActionOutputMetadataStoreTest.java | 29 +++-- 13 files changed, 63 insertions(+), 309 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java index e65ed6adef5ae0..3c4786985f00ed 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java +++ b/src/main/java/com/google/devtools/build/lib/actions/FileArtifactValue.java @@ -170,19 +170,11 @@ protected boolean couldBeModifiedByMetadata(FileArtifactValue lastKnown) { /** * Optional materialization path. * - *

If present, this artifact is a copy of another artifact whose contents live at this path. - * This can happen when it is declared as a file and not as an unresolved symlink but the action - * that creates it materializes it in the filesystem as a symlink to another output artifact. This - * information is useful in two situations: - * - *

    - *
  1. When the symlink target is a remotely stored artifact, we can avoid downloading it - * multiple times when building without the bytes (see AbstractActionInputPrefetcher). - *
  2. When the symlink target is inaccessible from the sandboxed environment an action runs - * under, we can rewrite it accordingly (see SandboxHelpers). - *
- * - * @see com.google.devtools.build.lib.skyframe.TreeArtifactValue#getMaterializationExecPath(). + *

If present, this artifact is a copy of another artifact. It is still tracked as a + * non-symlink by Bazel, but materialized in the local filesystem as a symlink to the original + * artifact, whose contents live at this location. This is used by {@link + * com.google.devtools.build.lib.remote.AbstractActionInputPrefetcher} to implement zero-cost + * copies of remotely stored artifacts. */ public Optional getMaterializationExecPath() { return Optional.empty(); @@ -223,12 +215,6 @@ public static FileArtifactValue createForSourceArtifact( xattrProvider); } - public static FileArtifactValue createForResolvedSymlink( - PathFragment realPath, FileArtifactValue metadata, @Nullable byte[] digest) { - return new ResolvedSymlinkFileArtifactValue( - realPath, digest, metadata.getContentsProxy(), metadata.getSize()); - } - public static FileArtifactValue createFromInjectedDigest( FileArtifactValue metadata, @Nullable byte[] digest) { return createForNormalFile(digest, metadata.getContentsProxy(), metadata.getSize()); @@ -457,25 +443,7 @@ public String toString() { } } - private static final class ResolvedSymlinkFileArtifactValue extends RegularFileArtifactValue { - private final PathFragment realPath; - - private ResolvedSymlinkFileArtifactValue( - PathFragment realPath, - @Nullable byte[] digest, - @Nullable FileContentsProxy proxy, - long size) { - super(digest, proxy, size); - this.realPath = realPath; - } - - @Override - public Optional getMaterializationExecPath() { - return Optional.of(realPath); - } - } - - private static class RegularFileArtifactValue extends FileArtifactValue { + private static final class RegularFileArtifactValue extends FileArtifactValue { private final byte[] digest; @Nullable private final FileContentsProxy proxy; private final long size; @@ -497,8 +465,7 @@ public boolean equals(Object o) { } return Arrays.equals(digest, that.digest) && Objects.equals(proxy, that.proxy) - && size == that.size - && Objects.equals(getMaterializationExecPath(), that.getMaterializationExecPath()); + && size == that.size; } @Override diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD index 8ce20d9a9ca476..97a8f6afacc47f 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD @@ -17,7 +17,6 @@ java_library( deps = [ "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/actions:artifacts", - "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", "//src/main/java/com/google/devtools/build/lib/analysis:test/test_configuration", "//src/main/java/com/google/devtools/build/lib/cmdline", "//src/main/java/com/google/devtools/build/lib/exec:tree_deleter", diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java index 3ad9c32531e0f0..40cd3499675d27 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java @@ -228,7 +228,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, execRoot, packageRoots, diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java index 971b05cd11b99b..3dacaeaa05ee9a 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java @@ -226,7 +226,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, execRoot, packageRoots, diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 5077a3ba57b18f..314da2002ae573 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -266,7 +266,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, withinSandboxExecRoot, packageRoots, diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java index 14d2a63545a734..a01a4e6da25131 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java @@ -100,7 +100,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, execRoot, packageRoots, diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 45c3b1dd21a067..fb448319520927 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -20,7 +20,6 @@ import static com.google.devtools.build.lib.vfs.Dirent.Type.SYMLINK; import com.google.auto.value.AutoValue; -import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -30,8 +29,6 @@ import com.google.common.flogger.GoogleLogger; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.FileArtifactValue; -import com.google.devtools.build.lib.actions.InputMetadataProvider; import com.google.devtools.build.lib.actions.Spawn; import com.google.devtools.build.lib.actions.UserExecException; import com.google.devtools.build.lib.actions.cache.VirtualActionInput; @@ -483,29 +480,6 @@ private static RootedPath processFilesetSymlink( symlink.getPathString())); } - private static RootedPath processResolvedSymlink( - Root absoluteRoot, - PathFragment execRootRelativeSymlinkTarget, - Root execRootWithinSandbox, - PathFragment execRootFragment, - ImmutableList packageRoots, - Function sourceRooWithinSandbox) { - PathFragment symlinkTarget = execRootFragment.getRelative(execRootRelativeSymlinkTarget); - for (Root packageRoot : packageRoots) { - if (packageRoot.contains(symlinkTarget)) { - return RootedPath.toRootedPath( - sourceRooWithinSandbox.apply(packageRoot), packageRoot.relativize(symlinkTarget)); - } - } - - if (symlinkTarget.startsWith(execRootFragment)) { - return RootedPath.toRootedPath( - execRootWithinSandbox, symlinkTarget.relativeTo(execRootFragment)); - } - - return RootedPath.toRootedPath(absoluteRoot, symlinkTarget); - } - /** * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the * host filesystem where the input files can be found. @@ -520,7 +494,6 @@ private static RootedPath processResolvedSymlink( */ public SandboxInputs processInputFiles( Map inputMap, - InputMetadataProvider inputMetadataProvider, Path execRootPath, Path withinSandboxExecRootPath, ImmutableList packageRoots, @@ -531,24 +504,12 @@ public SandboxInputs processInputFiles( withinSandboxExecRootPath.equals(execRootPath) ? withinSandboxExecRoot : Root.fromPath(execRootPath); - Root absoluteRoot = Root.absoluteRoot(execRootPath.getFileSystem()); Map inputFiles = new TreeMap<>(); Map inputSymlinks = new TreeMap<>(); Map virtualInputs = new HashMap<>(); Map sourceRootToSandboxSourceRoot = new TreeMap<>(); - Function sourceRootWithinSandbox = - r -> { - if (!sourceRootToSandboxSourceRoot.containsKey(r)) { - int next = sourceRootToSandboxSourceRoot.size(); - sourceRootToSandboxSourceRoot.put( - r, Root.fromPath(sandboxSourceRoots.getRelative(Integer.toString(next)))); - } - - return sourceRootToSandboxSourceRoot.get(r); - }; - for (Map.Entry e : inputMap.entrySet()) { if (Thread.interrupted()) { throw new InterruptedException(); @@ -568,62 +529,23 @@ public SandboxInputs processInputFiles( if (actionInput instanceof EmptyActionInput) { inputPath = null; - } else if (actionInput instanceof VirtualActionInput) { - inputPath = RootedPath.toRootedPath(withinSandboxExecRoot, actionInput.getExecPath()); - } else if (actionInput instanceof Artifact inputArtifact) { - if (sandboxSourceRoots == null) { - inputPath = RootedPath.toRootedPath(withinSandboxExecRoot, inputArtifact.getExecPath()); - } else { - if (inputArtifact.isSourceArtifact()) { - Root sourceRoot = inputArtifact.getRoot().getRoot(); - inputPath = - RootedPath.toRootedPath( - sourceRootWithinSandbox.apply(sourceRoot), - inputArtifact.getRootRelativePath()); - } else { - PathFragment materializationExecPath = null; - if (inputArtifact.isChildOfDeclaredDirectory()) { - FileArtifactValue parentMetadata = - inputMetadataProvider.getInputMetadata(inputArtifact.getParent()); - if (parentMetadata.getMaterializationExecPath().isPresent()) { - materializationExecPath = - parentMetadata - .getMaterializationExecPath() - .get() - .getRelative(inputArtifact.getParentRelativePath()); - } - } else if (!inputArtifact.isTreeArtifact()) { - // Normally, one would not see tree artifacts here because they have already been - // expanded by the time the code gets here. However, there is one very special case: - // when an action has an archived tree artifact on its output and is executed on the - // local branch of the dynamic execution strategy, the tree artifact is zipped up - // in a little extra spawn, which has direct tree artifact on its inputs. Sadly, - // it's not easy to fix this because there isn't an easy way to inject this new - // tree artifact into the artifact expander being used. - // - // The best would be to not rely on spawn strategies for executing that little - // command: it's entirely under the control of Bazel so we can guarantee that it - // does not cause mischief. - FileArtifactValue metadata = inputMetadataProvider.getInputMetadata(actionInput); - if (metadata.getMaterializationExecPath().isPresent()) { - materializationExecPath = metadata.getMaterializationExecPath().get(); - } - } - - if (materializationExecPath != null) { - inputPath = - processResolvedSymlink( - absoluteRoot, - materializationExecPath, - withinSandboxExecRoot, - execRootPath.asFragment(), - packageRoots, - sourceRootWithinSandbox); - } else { - inputPath = - RootedPath.toRootedPath(withinSandboxExecRoot, inputArtifact.getExecPath()); - } + } else if (actionInput instanceof Artifact) { + Artifact inputArtifact = (Artifact) actionInput; + if (inputArtifact.isSourceArtifact() && sandboxSourceRoots != null) { + Root sourceRoot = inputArtifact.getRoot().getRoot(); + if (!sourceRootToSandboxSourceRoot.containsKey(sourceRoot)) { + int next = sourceRootToSandboxSourceRoot.size(); + sourceRootToSandboxSourceRoot.put( + sourceRoot, + Root.fromPath(sandboxSourceRoots.getRelative(Integer.toString(next)))); } + + inputPath = + RootedPath.toRootedPath( + sourceRootToSandboxSourceRoot.get(sourceRoot), + inputArtifact.getRootRelativePath()); + } else { + inputPath = RootedPath.toRootedPath(withinSandboxExecRoot, inputArtifact.getExecPath()); } } else { PathFragment execPath = actionInput.getExecPath(); @@ -648,7 +570,6 @@ public SandboxInputs processInputFiles( return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks, sandboxRootToSourceRoot); } - /** The file and directory outputs of a sandboxed spawn. */ @AutoValue public abstract static class SandboxOutputs { diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java index 505e2417850161..c7996e38582fa1 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java @@ -76,7 +76,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs readablePaths = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, execRoot, packageRoots, diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java index 813dbe32b196c9..da85124b88069c 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStore.java @@ -264,15 +264,7 @@ private TreeArtifactValue constructTreeArtifactValueFromFilesystem(SpecialArtifa Path treeDir = artifactPathResolver.toPath(parent); boolean chmod = executionMode.get(); - FileStatus lstat = treeDir.statIfFound(Symlinks.NOFOLLOW); - FileStatus stat; - if (lstat == null) { - stat = null; - } else if (!lstat.isSymbolicLink()) { - stat = lstat; - } else { - stat = treeDir.statIfFound(Symlinks.FOLLOW); - } + FileStatus stat = treeDir.statIfFound(Symlinks.FOLLOW); // Make sure the tree artifact root exists and is a regular directory. Note that this is how the // action is initialized, so this should hold unless the action itself has deleted the root. @@ -329,18 +321,12 @@ private TreeArtifactValue constructTreeArtifactValueFromFilesystem(SpecialArtifa } // Same rationale as for constructFileArtifactValue. - if (lstat.isSymbolicLink()) { - PathFragment materializationExecPath = null; - if (stat instanceof FileStatusWithMetadata) { - materializationExecPath = - ((FileStatusWithMetadata) stat).getMetadata().getMaterializationExecPath().orElse(null); - } - - if (materializationExecPath == null) { - materializationExecPath = treeDir.resolveSymbolicLinks().asFragment().relativeTo(execRoot); - } - - tree.setMaterializationExecPath(materializationExecPath); + if (anyRemote.get() && treeDir.isSymbolicLink() && stat instanceof FileStatusWithMetadata) { + FileArtifactValue metadata = ((FileStatusWithMetadata) stat).getMetadata(); + tree.setMaterializationExecPath( + metadata + .getMaterializationExecPath() + .orElse(treeDir.resolveSymbolicLinks().asFragment().relativeTo(execRoot))); } return tree.build(); @@ -512,13 +498,7 @@ private FileArtifactValue constructFileArtifactValue( return value; } - boolean isResolvedSymlink = - statAndValue.statNoFollow() != null - && statAndValue.statNoFollow().isSymbolicLink() - && statAndValue.realPath() != null - && !value.isRemote(); - - if (type.isFile() && !isResolvedSymlink && fileDigest != null) { + if (type.isFile() && fileDigest != null) { // The digest is in the file value and that is all that is needed for this file's metadata. return value; } @@ -534,30 +514,22 @@ private FileArtifactValue constructFileArtifactValue( artifactPathResolver.toPath(artifact).getLastModifiedTime()); } - byte[] actualDigest = fileDigest != null ? fileDigest : injectedDigest; - - if (actualDigest == null && type.isFile()) { + if (injectedDigest == null && type.isFile()) { // We don't have an injected digest and there is no digest in the file value (which attempts a // fast digest). Manually compute the digest instead. + Path path = statAndValue.pathNoFollow(); + if (statAndValue.statNoFollow() != null + && statAndValue.statNoFollow().isSymbolicLink() + && statAndValue.realPath() != null) { + // If the file is a symlink, we compute the digest using the target path so that it's + // possible to hit the digest cache - we probably already computed the digest for the + // target during previous action execution. + path = statAndValue.realPath(); + } - // If the file is a symlink, we compute the digest using the target path so that it's - // possible to hit the digest cache - we probably already computed the digest for the - // target during previous action execution. - Path pathToDigest = isResolvedSymlink ? statAndValue.realPath() : statAndValue.pathNoFollow(); - actualDigest = DigestUtils.manuallyComputeDigest(pathToDigest); - } - - if (!isResolvedSymlink) { - return FileArtifactValue.createFromInjectedDigest(value, actualDigest); + injectedDigest = DigestUtils.manuallyComputeDigest(path); } - - PathFragment realPathAsFragment = statAndValue.realPath().asFragment(); - PathFragment execRootRelativeRealPath = - realPathAsFragment.startsWith(execRoot) - ? realPathAsFragment.relativeTo(execRoot) - : realPathAsFragment; - return FileArtifactValue.createForResolvedSymlink( - execRootRelativeRealPath, value, actualDigest); + return FileArtifactValue.createFromInjectedDigest(value, injectedDigest); } /** diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index d7e21722531c31..74faf68b9340ee 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -201,7 +201,6 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionContext context) helpers.processInputFiles( context.getInputMapping( PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - context.getInputMetadataProvider(), execRoot, execRoot, packageRoots, diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD index 37afc8502ac4af..fea187dd0d4e5d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD @@ -59,14 +59,12 @@ java_test( deps = [ "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/actions:artifacts", - "//src/main/java/com/google/devtools/build/lib/actions:file_metadata", "//src/main/java/com/google/devtools/build/lib/exec:bin_tools", "//src/main/java/com/google/devtools/build/lib/exec:tree_deleter", "//src/main/java/com/google/devtools/build/lib/sandbox:sandbox_helpers", "//src/main/java/com/google/devtools/build/lib/sandbox:sandbox_options", "//src/main/java/com/google/devtools/build/lib/sandbox:sandboxed_spawns", "//src/main/java/com/google/devtools/build/lib/sandbox:tree_deleter", - "//src/main/java/com/google/devtools/build/lib/skyframe:tree_artifact_value", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs", diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 89d12e3f05adef..37148d9ad8b794 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -24,23 +24,17 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.ActionInput; -import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; -import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; import com.google.devtools.build.lib.actions.ArtifactRoot; import com.google.devtools.build.lib.actions.CommandLines.ParamFileActionInput; -import com.google.devtools.build.lib.actions.FileArtifactValue; import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; import com.google.devtools.build.lib.actions.PathMapper; import com.google.devtools.build.lib.actions.Spawn; import com.google.devtools.build.lib.actions.cache.VirtualActionInput; import com.google.devtools.build.lib.actions.util.ActionsTestUtil; import com.google.devtools.build.lib.exec.BinTools; -import com.google.devtools.build.lib.exec.util.FakeActionInputFileCache; import com.google.devtools.build.lib.exec.util.SpawnBuilder; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs; -import com.google.devtools.build.lib.skyframe.TreeArtifactValue; import com.google.devtools.build.lib.testutil.Scratch; import com.google.devtools.build.lib.testutil.TestUtils; import com.google.devtools.build.lib.vfs.DigestHashFunction; @@ -74,7 +68,6 @@ /** Tests for {@link SandboxHelpers}. */ @RunWith(JUnit4.class) public class SandboxHelpersTest { - private static final byte[] FAKE_DIGEST = new byte[] {1}; private final Scratch scratch = new Scratch(); private Path execRootPath; @@ -101,79 +94,6 @@ private RootedPath execRootedPath(String execPath) { return RootedPath.toRootedPath(execRoot, PathFragment.create(execPath)); } - @Test - public void processInputFiles_resolvesMaterializationPath_fileArtifact() throws Exception { - ArtifactRoot outputRoot = - ArtifactRoot.asDerivedRoot(execRootPath, ArtifactRoot.RootType.Output, "outputs"); - Path sandboxSourceRoot = scratch.dir("/faketmp/sandbox-source-roots"); - - Artifact input = ActionsTestUtil.createArtifact(outputRoot, "a/a"); - FileArtifactValue symlinkTargetMetadata = - FileArtifactValue.createForNormalFile(FAKE_DIGEST, null, 0L); - FileArtifactValue inputMetadata = - FileArtifactValue.createForResolvedSymlink( - PathFragment.create("b/b"), symlinkTargetMetadata, FAKE_DIGEST); - - FakeActionInputFileCache inputMetadataProvider = new FakeActionInputFileCache(); - inputMetadataProvider.put(input, inputMetadata); - - SandboxHelpers sandboxHelpers = new SandboxHelpers(); - SandboxInputs inputs = - sandboxHelpers.processInputFiles( - inputMap(input), - inputMetadataProvider, - execRootPath, - execRootPath, - ImmutableList.of(), - sandboxSourceRoot); - - assertThat(inputs.getFiles()) - .containsEntry( - input.getExecPath(), RootedPath.toRootedPath(execRoot, PathFragment.create("b/b"))); - } - - @Test - public void processInputFiles_resolvesMaterializationPath_treeArtifact() throws Exception { - ArtifactRoot outputRoot = - ArtifactRoot.asDerivedRoot(execRootPath, ArtifactRoot.RootType.Output, "outputs"); - Path sandboxSourceRoot = scratch.dir("/faketmp/sandbox-source-roots"); - SpecialArtifact parent = - ActionsTestUtil.createTreeArtifactWithGeneratingAction( - outputRoot, "bin/config/other_dir/subdir"); - - TreeFileArtifact childA = TreeFileArtifact.createTreeOutput(parent, "a/a"); - TreeFileArtifact childB = TreeFileArtifact.createTreeOutput(parent, "b/b"); - FileArtifactValue childMetadata = FileArtifactValue.createForNormalFile(FAKE_DIGEST, null, 0L); - TreeArtifactValue parentMetadata = - TreeArtifactValue.newBuilder(parent) - .putChild(childA, childMetadata) - .putChild(childB, childMetadata) - .setMaterializationExecPath(PathFragment.create("materialized")) - .build(); - - FakeActionInputFileCache inputMetadataProvider = new FakeActionInputFileCache(); - inputMetadataProvider.put(parent, parentMetadata.getMetadata()); - - SandboxHelpers sandboxHelpers = new SandboxHelpers(); - SandboxInputs inputs = - sandboxHelpers.processInputFiles( - inputMap(childA, childB), - inputMetadataProvider, - execRootPath, - execRootPath, - ImmutableList.of(), - sandboxSourceRoot); - - assertThat(inputs.getFiles()) - .containsEntry( - childA.getExecPath(), - RootedPath.toRootedPath(execRoot, PathFragment.create("materialized/a/a"))); - assertThat(inputs.getFiles()) - .containsEntry( - childB.getExecPath(), - RootedPath.toRootedPath(execRoot, PathFragment.create("materialized/b/b"))); - } - @Test public void processInputFiles_materializesParamFile() throws Exception { SandboxHelpers sandboxHelpers = new SandboxHelpers(); @@ -186,12 +106,7 @@ public void processInputFiles_materializesParamFile() throws Exception { SandboxInputs inputs = sandboxHelpers.processInputFiles( - inputMap(paramFile), - new FakeActionInputFileCache(), - execRootPath, - execRootPath, - ImmutableList.of(), - null); + inputMap(paramFile), execRootPath, execRootPath, ImmutableList.of(), null); assertThat(inputs.getFiles()) .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile")); @@ -212,12 +127,7 @@ public void processInputFiles_materializesBinToolsFile() throws Exception { SandboxInputs inputs = sandboxHelpers.processInputFiles( - inputMap(tool), - new FakeActionInputFileCache(), - execRootPath, - execRootPath, - ImmutableList.of(), - null); + inputMap(tool), execRootPath, execRootPath, ImmutableList.of(), null); assertThat(inputs.getFiles()) .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello")); @@ -263,12 +173,7 @@ protected void setExecutable(PathFragment path, boolean executable) throws IOExc try { var unused = sandboxHelpers.processInputFiles( - inputMap(input), - new FakeActionInputFileCache(), - customExecRoot, - customExecRoot, - ImmutableList.of(), - null); + inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null); finishProcessingSemaphore.release(); } catch (IOException | InterruptedException e) { throw new IllegalArgumentException(e); @@ -276,12 +181,7 @@ protected void setExecutable(PathFragment path, boolean executable) throws IOExc }); var unused = sandboxHelpers.processInputFiles( - inputMap(input), - new FakeActionInputFileCache(), - customExecRoot, - customExecRoot, - ImmutableList.of(), - null); + inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null); finishProcessingSemaphore.release(); future.get(); diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStoreTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStoreTest.java index d594263d912043..9eddd2e1c19c9c 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStoreTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStoreTest.java @@ -82,6 +82,10 @@ private enum TreeComposition { FULLY_LOCAL, FULLY_REMOTE, MIXED; + + boolean isPartiallyRemote() { + return this == FULLY_REMOTE || this == MIXED; + } } private final Map chmodCalls = Maps.newConcurrentMap(); @@ -432,10 +436,11 @@ public void fileArtifactMaterializedAsSymlink( .getPath(outputArtifact.getPath().getPathString()) .createSymbolicLink(targetArtifact.getPath().asFragment()); - PathFragment expectedMaterializationExecPath = - location == FileLocation.REMOTE && preexistingPath != null - ? preexistingPath - : targetArtifact.getExecPath(); + PathFragment expectedMaterializationExecPath = null; + if (location == FileLocation.REMOTE) { + expectedMaterializationExecPath = + preexistingPath != null ? preexistingPath : targetArtifact.getExecPath(); + } assertThat(store.getOutputMetadata(outputArtifact)) .isEqualTo(createFileMetadataForSymlinkTest(location, expectedMaterializationExecPath)); @@ -445,12 +450,7 @@ private FileArtifactValue createFileMetadataForSymlinkTest( FileLocation location, @Nullable PathFragment materializationExecPath) { switch (location) { case LOCAL: - FileArtifactValue target = - FileArtifactValue.createForNormalFile(new byte[] {1, 2, 3}, /* proxy= */ null, 10); - return materializationExecPath == null - ? target - : FileArtifactValue.createForResolvedSymlink( - materializationExecPath, target, target.getDigest()); + return FileArtifactValue.createForNormalFile(new byte[] {1, 2, 3}, /* proxy= */ null, 10); case REMOTE: return RemoteFileArtifactValue.create( new byte[] {1, 2, 3}, 10, 1, -1, materializationExecPath); @@ -495,8 +495,11 @@ public void treeArtifactMaterializedAsSymlink( .getPath(outputArtifact.getPath().getPathString()) .createSymbolicLink(targetArtifact.getPath().asFragment()); - PathFragment expectedMaterializationExecPath = - preexistingPath != null ? preexistingPath : targetArtifact.getExecPath(); + PathFragment expectedMaterializationExecPath = null; + if (composition.isPartiallyRemote()) { + expectedMaterializationExecPath = + preexistingPath != null ? preexistingPath : targetArtifact.getExecPath(); + } assertThat(store.getTreeArtifactValue(outputArtifact)) .isEqualTo( @@ -825,7 +828,7 @@ public void fileArtifactValueForSymlink_readFromCache() throws Exception { var symlinkMetadata = store.getOutputMetadata(symlink); - assertThat(symlinkMetadata.getDigest()).isEqualTo(targetMetadata.getDigest()); + assertThat(symlinkMetadata).isEqualTo(targetMetadata); assertThat(DigestUtils.getCacheStats().hitCount()).isEqualTo(1); } } From 86e40c6e999612f07e78872f3df6746831f431a6 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 15:00:21 +0200 Subject: [PATCH 03/32] Revert "Rewrite paths of writable directories that are under the execroot." This reverts commit bc1d9d38bea907beea8fd82c362c9882ddf48cfd, but keeps the sorting of writable dirs. --- .../sandbox/LinuxSandboxedSpawnRunner.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 314da2002ae573..9b15facf8692b6 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -393,24 +393,7 @@ protected ImmutableSet getWritableDirs( FileSystem fs = sandboxExecRoot.getFileSystem(); writableDirs.add(fs.getPath("/dev/shm").resolveSymbolicLinks()); writableDirs.add(fs.getPath("/tmp")); - - if (sandboxExecRoot.equals(withinSandboxExecRoot)) { - return ImmutableSet.copyOf(writableDirs); - } - - // If a writable directory is under the sandbox exec root, transform it so that its path will - // be the one that it will be available at after processing the bind mounts (this is how the - // sandbox interprets the corresponding arguments) - // - // Notably, this is usually the case for $TEST_TMPDIR because its default value is under the - // execroot. - return writableDirs.stream() - .map( - d -> - d.startsWith(sandboxExecRoot) - ? withinSandboxExecRoot.getRelative(d.relativeTo(sandboxExecRoot)) - : d) - .collect(toImmutableSet()); + return ImmutableSet.copyOf(writableDirs); } private ImmutableList prepareAndGetBindMounts( From 19296b16720eb1c656722aa1ec788f9cc85aa678 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 15:05:26 +0200 Subject: [PATCH 04/32] Revert "Add support for bind mounts under `/tmp` with hermetic tmp" This reverts commit 5e68afdaee34d4b645f74dc42c2cb93bf8f14785. --- .../sandbox/LinuxSandboxedSpawnRunner.java | 88 +++++++------------ 1 file changed, 31 insertions(+), 57 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 9b15facf8692b6..fa912d068b72d7 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -14,14 +14,13 @@ package com.google.devtools.build.lib.sandbox; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NO_NETNS; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import com.google.common.io.ByteStreams; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.ExecException; @@ -62,7 +61,6 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; -import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; @@ -308,8 +306,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context .addExecutionInfo(spawn.getExecutionInfo()) .setWritableFilesAndDirectories(writableDirs) .setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath)) - .setBindMounts( - prepareAndGetBindMounts(blazeDirs, inputs, sandboxExecRootBase, sandboxTmp)) + .setBindMounts(getBindMounts(blazeDirs, inputs, sandboxExecRootBase, sandboxTmp)) .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname) .setEnablePseudoterminal(getSandboxOptions().sandboxExplicitPseudoterminal) .setCreateNetworkNamespace(createNetworkNamespace ? NETNS_WITH_LOOPBACK : NO_NETNS) @@ -396,73 +393,50 @@ protected ImmutableSet getWritableDirs( return ImmutableSet.copyOf(writableDirs); } - private ImmutableList prepareAndGetBindMounts( + private ImmutableList getBindMounts( BlazeDirectories blazeDirs, SandboxInputs inputs, Path sandboxExecRootBase, @Nullable Path sandboxTmp) - throws UserExecException, IOException { - Path tmpPath = fileSystem.getPath(SLASH_TMP); - final SortedMap userBindMounts = new TreeMap<>(); + throws UserExecException { + Path tmpPath = fileSystem.getPath("/tmp"); + final SortedMap bindMounts = Maps.newTreeMap(); SandboxHelpers.mountAdditionalPaths( - getSandboxOptions().sandboxAdditionalMounts, sandboxExecRootBase, userBindMounts); + getSandboxOptions().sandboxAdditionalMounts, sandboxExecRootBase, bindMounts); for (Path inaccessiblePath : getInaccessiblePaths()) { if (inaccessiblePath.isDirectory(Symlinks.NOFOLLOW)) { - userBindMounts.put(inaccessiblePath, inaccessibleHelperDir); + bindMounts.put(inaccessiblePath, inaccessibleHelperDir); } else { - userBindMounts.put(inaccessiblePath, inaccessibleHelperFile); + bindMounts.put(inaccessiblePath, inaccessibleHelperFile); } } - LinuxSandboxUtil.validateBindMounts(userBindMounts); - - if (sandboxTmp == null) { - return userBindMounts.entrySet().stream() - .map(e -> BindMount.of(e.getKey(), e.getValue())) - .collect(toImmutableList()); - } - - SortedMap bindMounts = new TreeMap<>(); - for (var entry : userBindMounts.entrySet()) { - Path mountPoint = entry.getKey(); - Path content = entry.getValue(); - if (mountPoint.startsWith(tmpPath)) { - // sandboxTmp should be null if /tmp is an explicit mount point since useHermeticTmp() - // returns false in that case. - if (mountPoint.equals(tmpPath)) { - throw new IOException( - "Cannot mount /tmp explicitly with hermetic /tmp. Please file a bug at" - + " https://github.com/bazelbuild/bazel/issues/new/choose."); - } - // We need to rewrite the mount point to be under the sandbox tmp directory, which will be - // mounted onto /tmp as the final mount. - mountPoint = sandboxTmp.getRelative(mountPoint.relativeTo(tmpPath)); - mountPoint.createDirectoryAndParents(); - } - bindMounts.put(mountPoint, content); - } - + LinuxSandboxUtil.validateBindMounts(bindMounts); ImmutableList.Builder result = ImmutableList.builder(); bindMounts.forEach((k, v) -> result.add(BindMount.of(k, v))); - // First mount the real exec root and the empty directory created as the working dir of the - // action under $SANDBOX/_tmp - result.add(BindMount.of(sandboxTmp.getRelative(BAZEL_EXECROOT), blazeDirs.getExecRootBase())); - result.add(BindMount.of(sandboxTmp.getRelative(BAZEL_WORKING_DIRECTORY), sandboxExecRootBase)); - - // Then mount the individual package roots under $SANDBOX/_tmp/bazel-source-roots - inputs - .getSourceRootBindMounts() - .forEach( - (withinSandbox, real) -> { - PathFragment sandboxTmpSourceRoot = withinSandbox.asPath().relativeTo(tmpPath); - result.add(BindMount.of(sandboxTmp.getRelative(sandboxTmpSourceRoot), real)); - }); - - // Then mount $SANDBOX/_tmp at /tmp. At this point, even if the output base (and execroot) and - // individual source roots are under /tmp, they are accessible at /tmp/bazel-* - result.add(BindMount.of(tmpPath, sandboxTmp)); + if (sandboxTmp != null) { + // First mount the real exec root and the empty directory created as the working dir of the + // action under $SANDBOX/_tmp + result.add(BindMount.of(sandboxTmp.getRelative(BAZEL_EXECROOT), blazeDirs.getExecRootBase())); + result.add( + BindMount.of(sandboxTmp.getRelative(BAZEL_WORKING_DIRECTORY), sandboxExecRootBase)); + + // Then mount the individual package roots under $SANDBOX/_tmp/bazel-source-roots + inputs + .getSourceRootBindMounts() + .forEach( + (withinSandbox, real) -> { + PathFragment sandboxTmpSourceRoot = withinSandbox.asPath().relativeTo(tmpPath); + result.add(BindMount.of(sandboxTmp.getRelative(sandboxTmpSourceRoot), real)); + }); + + // Then mount $SANDBOX/_tmp at /tmp. At this point, even if the output base (and execroot) + // and individual source roots are under /tmp, they are accessible at /tmp/bazel-* + result.add(BindMount.of(tmpPath, sandboxTmp)); + } + return result.build(); } From d9f1cdd29919648ae7145359504f79e41c677f1c Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 15:07:59 +0200 Subject: [PATCH 05/32] Revert "Add support for tmpfs mounts under `/tmp` with hermetic tmp" This reverts commit 620d617b440258799caa1be434ed66e9ca8fa8c5. --- .../sandbox/LinuxSandboxedSpawnRunner.java | 28 ++++++++++++------- .../LinuxSandboxedSpawnRunnerTest.java | 21 ++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index fa912d068b72d7..86f0606acb9b1d 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -59,6 +59,7 @@ import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.SortedMap; import java.util.TreeSet; @@ -75,6 +76,7 @@ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { // Since checking if sandbox is supported is expensive, we remember what we've checked. private static final Map isSupportedMap = new HashMap<>(); + private static final AtomicBoolean warnedAboutNonHermeticTmp = new AtomicBoolean(); private static final AtomicBoolean warnedAboutUnsupportedModificationCheck = new AtomicBoolean(); @@ -205,6 +207,22 @@ private boolean useHermeticTmp() { return false; } + Optional tmpfsPathUnderTmp = + getSandboxOptions().sandboxTmpfsPath.stream() + .filter(path -> path.startsWith(SLASH_TMP)) + .findFirst(); + if (tmpfsPathUnderTmp.isPresent()) { + if (warnedAboutNonHermeticTmp.compareAndSet(false, true)) { + reporter.handle( + Event.warn( + String.format( + "Falling back to non-hermetic '/tmp' in sandbox due to '%s' being a tmpfs path", + tmpfsPathUnderTmp.get()))); + } + + return false; + } + return true; } @@ -278,16 +296,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxExecRoot); createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxWorkingDirectory); - - for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { - Path path = fileSystem.getPath(pathFragment); - if (path.startsWith(SLASH_TMP)) { - // tmpfs mount points must exist, which is usually the user's responsibility. But if the - // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp - // directory. - createDirectoryWithinSandboxTmp(sandboxTmp, path); - } - } } SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java index 1dfbf40572d4d0..dd5363686efcc6 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java @@ -203,6 +203,27 @@ public void hermeticTmp_sandboxTmpfsOnTmp_tmpNotCreatedOrMounted() throws Except assertThat(args).doesNotContain("-m /tmp"); } + @Test + public void hermeticTmp_sandboxTmpfsUnderTmp_tmpNotCreatedOrMounted() throws Exception { + runtimeWrapper.addOptions( + "--incompatible_sandbox_hermetic_tmp", "--sandbox_tmpfs_path=/tmp/subdir"); + CommandEnvironment commandEnvironment = createCommandEnvironment(); + LinuxSandboxedSpawnRunner runner = setupSandboxAndCreateRunner(commandEnvironment); + Spawn spawn = new SpawnBuilder().build(); + SandboxedSpawn sandboxedSpawn = runner.prepareSpawn(spawn, createSpawnExecutionContext(spawn)); + + Path sandboxPath = + sandboxedSpawn.getSandboxExecRoot().getParentDirectory().getParentDirectory(); + Path hermeticTmpPath = sandboxPath.getRelative("_hermetic_tmp"); + assertThat(hermeticTmpPath.isDirectory()).isFalse(); + + assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class); + String args = String.join(" ", sandboxedSpawn.getArguments()); + assertThat(args).contains("-w /tmp"); + assertThat(args).contains("-e /tmp"); + assertThat(args).doesNotContain("-m /tmp"); + } + private static LinuxSandboxedSpawnRunner setupSandboxAndCreateRunner( CommandEnvironment commandEnvironment) throws IOException { Path execRoot = commandEnvironment.getExecRoot(); From 73521a93ee7b392d139c0624f7c17554aab1d90f Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 15:08:46 +0200 Subject: [PATCH 06/32] Revert "Mount user-specified bind mounts before Bazel's own magic." This reverts commit 3748084889d85b132e0a8371cb2ab1eafdc311da. --- .../devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 86f0606acb9b1d..e10f3be0528072 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -422,7 +422,6 @@ private ImmutableList getBindMounts( LinuxSandboxUtil.validateBindMounts(bindMounts); ImmutableList.Builder result = ImmutableList.builder(); - bindMounts.forEach((k, v) -> result.add(BindMount.of(k, v))); if (sandboxTmp != null) { // First mount the real exec root and the empty directory created as the working dir of the @@ -445,6 +444,7 @@ private ImmutableList getBindMounts( result.add(BindMount.of(tmpPath, sandboxTmp)); } + bindMounts.forEach((k, v) -> result.add(BindMount.of(k, v))); return result.build(); } From cc0a4f12e96808885e56c42707607e4c808ee36b Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Fri, 12 Apr 2024 19:50:21 +0200 Subject: [PATCH 07/32] WIP --- .../LinuxSandboxCommandLineBuilder.java | 12 ++++ .../sandbox/LinuxSandboxedSpawnRunner.java | 57 ++++--------------- src/main/tools/linux-sandbox-options.cc | 10 +++- src/main/tools/linux-sandbox-options.h | 4 ++ src/main/tools/linux-sandbox-pid1.cc | 11 ++++ src/test/shell/integration/sandboxing_test.sh | 2 +- 6 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java index 967c08b5d6ce59..cdb9fe91d3d7b8 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java @@ -70,6 +70,8 @@ public static BindMount of(Path mountPoint, Path source) { private String sandboxDebugPath = null; private boolean sigintSendsSigterm = false; private String cgroupsDir; + private String overlayfsUpperdir = null; + private String overlayfsWorkdir = null; private LinuxSandboxCommandLineBuilder(Path linuxSandboxPath) { this.linuxSandboxPath = linuxSandboxPath; @@ -234,6 +236,13 @@ public LinuxSandboxCommandLineBuilder setCgroupsDir(String cgroupsDir) { return this; } + @CanIgnoreReturnValue + public LinuxSandboxCommandLineBuilder mountOverlayfsOnTmp(String upperdir, String workdir) { + this.overlayfsUpperdir = upperdir; + this.overlayfsWorkdir = workdir; + return this; + } + /** Incorporates settings from a spawn's execution info. */ @CanIgnoreReturnValue public LinuxSandboxCommandLineBuilder addExecutionInfo(Map executionInfo) { @@ -315,6 +324,9 @@ public ImmutableList buildForCommand(List commandArguments) { if (cgroupsDir != null) { commandLineBuilder.add("-C", cgroupsDir); } + if (overlayfsUpperdir != null && overlayfsWorkdir != null) { + commandLineBuilder.add("-F", overlayfsUpperdir, "-f", overlayfsWorkdir); + } commandLineBuilder.add("--"); commandLineBuilder.addAll(commandArguments); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index e10f3be0528072..a728a9cce45db3 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -248,62 +248,22 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context // The directory that will be mounted as the hermetic /tmp, if any (otherwise null) Path sandboxTmp = null; - // These paths are paths that are visible for the processes running inside the sandbox. They - // can be different from paths from the point of view of the Bazel server because if we use - // hermetic /tmp and either the output base or a source root are under /tmp, they would be - // hidden by the newly mounted hermetic /tmp . So in that case, we make the sandboxed processes - // see the exec root, the source roots and the working directory of the action at constant - // locations under /tmp . - - // Base directory for source roots; each source root is a sequentially numbered subdirectory. - Path withinSandboxSourceRoots = null; - - // Working directory of the action; this is where the inputs (and only the inputs) of the action - // are visible. - Path withinSandboxWorkingDirectory = null; - - // The exec root. Necessary because the working directory contains symlinks to the execroot. - Path withinSandboxExecRoot = execRoot; - - boolean useHermeticTmp = useHermeticTmp(); - - if (useHermeticTmp) { - // The directory which will be mounted at /tmp in the sandbox - sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); - withinSandboxSourceRoots = fileSystem.getPath(SLASH_TMP.getRelative(BAZEL_SOURCE_ROOTS)); - withinSandboxWorkingDirectory = - fileSystem - .getPath(SLASH_TMP.getRelative(BAZEL_WORKING_DIRECTORY)) - .getRelative(workspaceName); - withinSandboxExecRoot = - fileSystem.getPath(SLASH_TMP.getRelative(BAZEL_EXECROOT)).getRelative(workspaceName); - } - SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, - withinSandboxExecRoot, + execRoot, packageRoots, - withinSandboxSourceRoots); + null); sandboxExecRoot.createDirectoryAndParents(); - if (useHermeticTmp) { - for (Root root : inputs.getSourceRootBindMounts().keySet()) { - createDirectoryWithinSandboxTmp(sandboxTmp, root.asPath()); - } - - createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxExecRoot); - createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxWorkingDirectory); - } - SandboxOutputs outputs = helpers.getOutputs(spawn); ImmutableMap environment = localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); ImmutableSet writableDirs = getWritableDirs( - sandboxExecRoot, useHermeticTmp ? withinSandboxExecRoot : sandboxExecRoot, environment); + sandboxExecRoot, sandboxExecRoot, environment); Duration timeout = context.getTimeout(); SandboxOptions sandboxOptions = getSandboxOptions(); @@ -338,8 +298,15 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context } } - if (useHermeticTmp) { - commandLineBuilder.setWorkingDirectory(withinSandboxWorkingDirectory); + if (useHermeticTmp()) { + // The directory which will be mounted at /tmp in the sandbox + sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); + Path sandboxTmpUpperdir = sandboxTmp.getRelative("upperdir"); + sandboxTmpUpperdir.createDirectoryAndParents(); + Path sandboxTmpWorkdir = sandboxTmp.getRelative("workdir"); + sandboxTmpWorkdir.createDirectoryAndParents(); + commandLineBuilder.mountOverlayfsOnTmp(sandboxTmpUpperdir.getPathString(), + sandboxTmpWorkdir.getPathString()); } if (!timeout.isZero()) { diff --git a/src/main/tools/linux-sandbox-options.cc b/src/main/tools/linux-sandbox-options.cc index c2c57b38076086..17de2a3ca16aa8 100644 --- a/src/main/tools/linux-sandbox-options.cc +++ b/src/main/tools/linux-sandbox-options.cc @@ -102,7 +102,7 @@ static void ParseCommandLine(unique_ptr> args) { int c; bool source_specified = false; while ((c = getopt(args->size(), args->data(), - ":W:T:t:il:L:w:e:M:m:S:h:pC:HnNRUPD:")) != -1) { + ":W:T:t:il:L:w:e:M:m:S:h:pC:HnNRUPD:F:f:")) != -1) { if (c != 'M' && c != 'm') source_specified = false; switch (c) { case 'W': @@ -248,6 +248,14 @@ static void ParseCommandLine(unique_ptr> args) { "Cannot write debug output to more than one file."); } break; + case 'F': + ValidateIsAbsolutePath(optarg, args->front(), static_cast(c)); + opt.overlayfs_upperdir.assign(optarg); + break; + case 'f': + ValidateIsAbsolutePath(optarg, args->front(), static_cast(c)); + opt.overlayfs_workdir.assign(optarg); + break; case '?': Usage(args->front(), "Unrecognized argument: -%c (%d)", optopt, optind); break; diff --git a/src/main/tools/linux-sandbox-options.h b/src/main/tools/linux-sandbox-options.h index a4ed85d41cc381..97cbd5b33181e7 100644 --- a/src/main/tools/linux-sandbox-options.h +++ b/src/main/tools/linux-sandbox-options.h @@ -68,6 +68,10 @@ struct Options { std::string sandbox_root; // Directory to use for cgroup control std::string cgroups_dir; + // The upperdir for the /tmp overlayfs (-F) + std::string overlayfs_upperdir; + // The workdir for the /tmp overlayfs (-f) + std::string overlayfs_workdir; // Command to run (--) std::vector args; }; diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc index 7c7a2f54014200..4ae0d35211052d 100644 --- a/src/main/tools/linux-sandbox-pid1.cc +++ b/src/main/tools/linux-sandbox-pid1.cc @@ -18,6 +18,7 @@ */ #include "src/main/tools/linux-sandbox-pid1.h" +#include "src/main/tools/linux-sandbox-options.h" #include #include @@ -255,6 +256,16 @@ static void MountFilesystems() { } } + if (!opt.overlayfs_upperdir.empty() && !opt.overlayfs_workdir.empty()) { + const std::string overlayfs_opts = + "lowerdir=/tmp,upperdir=" + opt.overlayfs_upperdir + ",workdir=" + + opt.overlayfs_workdir; + PRINT_DEBUG("overlayfs mount: %s", overlayfs_opts.c_str()); + if (mount("overlay", "/tmp", "overlay", 0, overlayfs_opts.c_str()) < 0) { + DIE("mount(overlay, /tmp, overlay, 0, %s)", overlayfs_opts.c_str()); + } + } + std::unordered_set bind_mount_sources; for (size_t i = 0; i < opt.bind_mount_sources.size(); i++) { diff --git a/src/test/shell/integration/sandboxing_test.sh b/src/test/shell/integration/sandboxing_test.sh index db46d03f793716..278798e16dfeb6 100755 --- a/src/test/shell/integration/sandboxing_test.sh +++ b/src/test/shell/integration/sandboxing_test.sh @@ -736,7 +736,7 @@ sh_test( EOF cat > pkg/tmp_test.sh < Date: Mon, 15 Apr 2024 15:55:44 +0200 Subject: [PATCH 08/32] Manually revert bind mounting magic --- .../sandbox/AbstractSandboxSpawnRunner.java | 9 +- .../sandbox/DarwinSandboxedSpawnRunner.java | 3 +- .../LinuxSandboxCommandLineBuilder.java | 29 ++--- .../sandbox/LinuxSandboxedSpawnRunner.java | 104 +++--------------- .../ProcessWrapperSandboxedSpawnRunner.java | 2 +- .../build/lib/sandbox/SandboxHelpers.java | 22 ---- .../sandbox/WindowsSandboxedSpawnRunner.java | 2 +- .../build/lib/worker/SandboxedWorker.java | 7 +- .../LinuxSandboxCommandLineBuilderTest.java | 14 ++- 9 files changed, 40 insertions(+), 152 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java index 61868714abc722..9eb3915e27cc18 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java @@ -360,13 +360,12 @@ private boolean wasTimeout(Duration timeout, Duration wallTime) { /** * Gets the list of directories that the spawn will assume to be writable. * - * @param sandboxExecRoot the exec root of the sandbox from the point of view of the Bazel process - * @param withinSandboxExecRoot the exec root from the point of view of the sandboxed processes - * @param env the environment of the sandboxed processes + * @param sandboxExecRoot the exec root of the sandbox + * @param env the environment of the sandboxed processes * @throws IOException because we might resolve symlinks, which throws {@link IOException}. */ protected ImmutableSet getWritableDirs( - Path sandboxExecRoot, Path withinSandboxExecRoot, Map env) + Path sandboxExecRoot, Map env) throws IOException { // We have to make the TEST_TMPDIR directory writable if it is specified. ImmutableSet.Builder writablePaths = ImmutableSet.builder(); @@ -374,7 +373,7 @@ protected ImmutableSet getWritableDirs( // On Windows, sandboxExecRoot is actually the main execroot. We will specify // exactly which output path is writable. if (OS.getCurrent() != OS.WINDOWS) { - writablePaths.add(withinSandboxExecRoot); + writablePaths.add(execRoot); } String testTmpdir = env.get("TEST_TMPDIR"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java index 40cd3499675d27..05cd3ab96d0864 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java @@ -221,8 +221,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); final HashSet writableDirs = new HashSet<>(alwaysWritableDirs); - ImmutableSet extraWritableDirs = - getWritableDirs(sandboxExecRoot, sandboxExecRoot, environment); + ImmutableSet extraWritableDirs = getWritableDirs(sandboxExecRoot, environment); writableDirs.addAll(extraWritableDirs); SandboxInputs inputs = diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java index cdb9fe91d3d7b8..12141217824da4 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java @@ -17,9 +17,9 @@ import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; -import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.vfs.Path; @@ -36,20 +36,6 @@ * linux-sandbox} tool. */ public class LinuxSandboxCommandLineBuilder { - /** A bind mount that needs to be present when the sandboxed command runs. */ - @AutoValue - public abstract static class BindMount { - public static BindMount of(Path mountPoint, Path source) { - return new AutoValue_LinuxSandboxCommandLineBuilder_BindMount(mountPoint, source); - } - - /** "target" in mount(2) */ - public abstract Path getMountPoint(); - - /** "source" in mount(2) */ - public abstract Path getContent(); - } - private final Path linuxSandboxPath; private Path hermeticSandboxPath; private Path workingDirectory; @@ -60,7 +46,7 @@ public static BindMount of(Path mountPoint, Path source) { private Path stderrPath; private Set writableFilesAndDirectories = ImmutableSet.of(); private ImmutableSet tmpfsDirectories = ImmutableSet.of(); - private List bindMounts = ImmutableList.of(); + private Map bindMounts = ImmutableMap.of(); private Path statisticsPath; private boolean useFakeHostname = false; private NetworkNamespace createNetworkNamespace = NetworkNamespace.NO_NETNS; @@ -166,7 +152,7 @@ public LinuxSandboxCommandLineBuilder setTmpfsDirectories( * if any. */ @CanIgnoreReturnValue - public LinuxSandboxCommandLineBuilder setBindMounts(List bindMounts) { + public LinuxSandboxCommandLineBuilder setBindMounts(Map bindMounts) { this.bindMounts = bindMounts; return this; } @@ -282,11 +268,12 @@ public ImmutableList buildForCommand(List commandArguments) { for (PathFragment tmpfsPath : tmpfsDirectories) { commandLineBuilder.add("-e", tmpfsPath.getPathString()); } - for (BindMount bindMount : bindMounts) { - commandLineBuilder.add("-M", bindMount.getContent().getPathString()); + for (Path bindMountTarget : bindMounts.keySet()) { + Path bindMountSource = bindMounts.get(bindMountTarget); + commandLineBuilder.add("-M", bindMountSource.getPathString()); // The file is mounted in a custom location inside the sandbox. - if (!bindMount.getContent().equals(bindMount.getMountPoint())) { - commandLineBuilder.add("-m", bindMount.getMountPoint().getPathString()); + if (!bindMountSource.equals(bindMountTarget)) { + commandLineBuilder.add("-m", bindMountTarget.getPathString()); } } if (statisticsPath != null) { diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index a728a9cce45db3..f5177a266920a4 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -32,7 +32,6 @@ import com.google.devtools.build.lib.actions.Spawns; import com.google.devtools.build.lib.actions.UserExecException; import com.google.devtools.build.lib.actions.cache.VirtualActionInput; -import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.exec.TreeDeleter; @@ -42,7 +41,6 @@ import com.google.devtools.build.lib.profiler.Profiler; import com.google.devtools.build.lib.profiler.SilentCloseable; import com.google.devtools.build.lib.runtime.CommandEnvironment; -import com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.BindMount; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs; import com.google.devtools.build.lib.shell.Command; @@ -59,7 +57,6 @@ import java.time.Duration; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.SortedMap; import java.util.TreeSet; @@ -69,14 +66,9 @@ /** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { private static final PathFragment SLASH_TMP = PathFragment.create("/tmp"); - private static final PathFragment BAZEL_EXECROOT = PathFragment.create("bazel-execroot"); - private static final PathFragment BAZEL_WORKING_DIRECTORY = - PathFragment.create("bazel-working-directory"); - private static final PathFragment BAZEL_SOURCE_ROOTS = PathFragment.create("bazel-source-roots"); // Since checking if sandbox is supported is expensive, we remember what we've checked. private static final Map isSupportedMap = new HashMap<>(); - private static final AtomicBoolean warnedAboutNonHermeticTmp = new AtomicBoolean(); private static final AtomicBoolean warnedAboutUnsupportedModificationCheck = new AtomicBoolean(); @@ -126,8 +118,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS } private final SandboxHelpers helpers; - private final FileSystem fileSystem; - private final BlazeDirectories blazeDirs; private final Path execRoot; private final boolean allowNetwork; private final Path linuxSandbox; @@ -161,8 +151,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS TreeDeleter treeDeleter) { super(cmdEnv); this.helpers = helpers; - this.fileSystem = cmdEnv.getRuntime().getFileSystem(); - this.blazeDirs = cmdEnv.getDirectories(); this.execRoot = cmdEnv.getExecRoot(); this.allowNetwork = helpers.shouldAllowNetwork(cmdEnv.getOptions()); this.linuxSandbox = LinuxSandboxUtil.getLinuxSandbox(cmdEnv.getBlazeWorkspace()); @@ -176,12 +164,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); } - private void createDirectoryWithinSandboxTmp(Path sandboxTmp, Path withinSandboxDirectory) - throws IOException { - PathFragment withinTmp = withinSandboxDirectory.asFragment().relativeTo(SLASH_TMP); - sandboxTmp.getRelative(withinTmp).createDirectoryAndParents(); - } - private boolean useHermeticTmp() { if (!getSandboxOptions().sandboxHermeticTmp) { // No hermetic /tmp requested, so let's not do it @@ -207,46 +189,25 @@ private boolean useHermeticTmp() { return false; } - Optional tmpfsPathUnderTmp = - getSandboxOptions().sandboxTmpfsPath.stream() - .filter(path -> path.startsWith(SLASH_TMP)) - .findFirst(); - if (tmpfsPathUnderTmp.isPresent()) { - if (warnedAboutNonHermeticTmp.compareAndSet(false, true)) { - reporter.handle( - Event.warn( - String.format( - "Falling back to non-hermetic '/tmp' in sandbox due to '%s' being a tmpfs path", - tmpfsPathUnderTmp.get()))); - } - - return false; - } - return true; } @Override protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context) throws IOException, ForbiddenActionInputException, ExecException, InterruptedException { - // b/64689608: The execroot of the sandboxed process must end with the workspace name, just like - // the normal execroot does. - String workspaceName = execRoot.getBaseName(); // Each invocation of "exec" gets its own sandbox base. // Note that the value returned by context.getId() is only unique inside one given SpawnRunner, // so we have to prefix our name to turn it into a globally unique value. Path sandboxPath = sandboxBase.getRelative(getName()).getRelative(Integer.toString(context.getId())); + sandboxPath.createDirectoryAndParents(); - // The exec root base and the exec root of the sandbox from the point of view of the Bazel - // process (can be different from where the exec root appears within the sandbox due to file - // system namespace shenanigans). - Path sandboxExecRootBase = sandboxPath.getRelative("execroot"); - Path sandboxExecRoot = sandboxExecRootBase.getRelative(workspaceName); - - // The directory that will be mounted as the hermetic /tmp, if any (otherwise null) - Path sandboxTmp = null; + // b/64689608: The execroot of the sandboxed process must end with the workspace name, just like + // the normal execroot does. + String workspaceName = execRoot.getBaseName(); + Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(workspaceName); + sandboxExecRoot.createDirectoryAndParents(); SandboxInputs inputs = helpers.processInputFiles( @@ -256,14 +217,10 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context packageRoots, null); - sandboxExecRoot.createDirectoryAndParents(); - SandboxOutputs outputs = helpers.getOutputs(spawn); ImmutableMap environment = localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); - ImmutableSet writableDirs = - getWritableDirs( - sandboxExecRoot, sandboxExecRoot, environment); + ImmutableSet writableDirs = getWritableDirs(sandboxExecRoot, environment); Duration timeout = context.getTimeout(); SandboxOptions sandboxOptions = getSandboxOptions(); @@ -274,7 +231,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context .addExecutionInfo(spawn.getExecutionInfo()) .setWritableFilesAndDirectories(writableDirs) .setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath)) - .setBindMounts(getBindMounts(blazeDirs, inputs, sandboxExecRootBase, sandboxTmp)) + .setBindMounts(getBindMounts(sandboxExecRoot)) .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname) .setEnablePseudoterminal(getSandboxOptions().sandboxExplicitPseudoterminal) .setCreateNetworkNamespace(createNetworkNamespace ? NETNS_WITH_LOOPBACK : NO_NETNS) @@ -299,8 +256,8 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context } if (useHermeticTmp()) { - // The directory which will be mounted at /tmp in the sandbox - sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); + // The base dir for the upperdir and workdir of an overlayfs on /tmp. + Path sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); Path sandboxTmpUpperdir = sandboxTmp.getRelative("upperdir"); sandboxTmpUpperdir.createDirectoryAndParents(); Path sandboxTmpWorkdir = sandboxTmp.getRelative("workdir"); @@ -357,27 +314,20 @@ public String getName() { } @Override - protected ImmutableSet getWritableDirs( - Path sandboxExecRoot, Path withinSandboxExecRoot, Map env) + protected ImmutableSet getWritableDirs(Path sandboxExecRoot, Map env) throws IOException { Set writableDirs = new TreeSet<>(); - writableDirs.addAll(super.getWritableDirs(sandboxExecRoot, withinSandboxExecRoot, env)); + writableDirs.addAll(super.getWritableDirs(sandboxExecRoot, env)); FileSystem fs = sandboxExecRoot.getFileSystem(); writableDirs.add(fs.getPath("/dev/shm").resolveSymbolicLinks()); writableDirs.add(fs.getPath("/tmp")); return ImmutableSet.copyOf(writableDirs); } - private ImmutableList getBindMounts( - BlazeDirectories blazeDirs, - SandboxInputs inputs, - Path sandboxExecRootBase, - @Nullable Path sandboxTmp) - throws UserExecException { - Path tmpPath = fileSystem.getPath("/tmp"); + private SortedMap getBindMounts(Path sandboxExecRoot) throws UserExecException { final SortedMap bindMounts = Maps.newTreeMap(); SandboxHelpers.mountAdditionalPaths( - getSandboxOptions().sandboxAdditionalMounts, sandboxExecRootBase, bindMounts); + getSandboxOptions().sandboxAdditionalMounts, sandboxExecRoot, bindMounts); for (Path inaccessiblePath : getInaccessiblePaths()) { if (inaccessiblePath.isDirectory(Symlinks.NOFOLLOW)) { @@ -388,31 +338,7 @@ private ImmutableList getBindMounts( } LinuxSandboxUtil.validateBindMounts(bindMounts); - ImmutableList.Builder result = ImmutableList.builder(); - - if (sandboxTmp != null) { - // First mount the real exec root and the empty directory created as the working dir of the - // action under $SANDBOX/_tmp - result.add(BindMount.of(sandboxTmp.getRelative(BAZEL_EXECROOT), blazeDirs.getExecRootBase())); - result.add( - BindMount.of(sandboxTmp.getRelative(BAZEL_WORKING_DIRECTORY), sandboxExecRootBase)); - - // Then mount the individual package roots under $SANDBOX/_tmp/bazel-source-roots - inputs - .getSourceRootBindMounts() - .forEach( - (withinSandbox, real) -> { - PathFragment sandboxTmpSourceRoot = withinSandbox.asPath().relativeTo(tmpPath); - result.add(BindMount.of(sandboxTmp.getRelative(sandboxTmpSourceRoot), real)); - }); - - // Then mount $SANDBOX/_tmp at /tmp. At this point, even if the output base (and execroot) - // and individual source roots are under /tmp, they are accessible at /tmp/bazel-* - result.add(BindMount.of(tmpPath, sandboxTmp)); - } - - bindMounts.forEach((k, v) -> result.add(BindMount.of(k, v))); - return result.build(); + return bindMounts; } @Override diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java index a01a4e6da25131..274d83cc43e938 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java @@ -113,7 +113,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context environment, inputs, outputs, - getWritableDirs(sandboxExecRoot, sandboxExecRoot, environment), + getWritableDirs(sandboxExecRoot, environment), treeDeleter, /* sandboxDebugPath= */ null, statisticsPath, diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index fb448319520927..2e617244f68249 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -415,10 +415,6 @@ public Map getSymlinks() { return symlinks; } - public Map getSourceRootBindMounts() { - return sourceRootBindMounts; - } - public ImmutableMap getVirtualInputDigests() { return ImmutableMap.copyOf(virtualInputs); } @@ -529,24 +525,6 @@ public SandboxInputs processInputFiles( if (actionInput instanceof EmptyActionInput) { inputPath = null; - } else if (actionInput instanceof Artifact) { - Artifact inputArtifact = (Artifact) actionInput; - if (inputArtifact.isSourceArtifact() && sandboxSourceRoots != null) { - Root sourceRoot = inputArtifact.getRoot().getRoot(); - if (!sourceRootToSandboxSourceRoot.containsKey(sourceRoot)) { - int next = sourceRootToSandboxSourceRoot.size(); - sourceRootToSandboxSourceRoot.put( - sourceRoot, - Root.fromPath(sandboxSourceRoots.getRelative(Integer.toString(next)))); - } - - inputPath = - RootedPath.toRootedPath( - sourceRootToSandboxSourceRoot.get(sourceRoot), - inputArtifact.getRootRelativePath()); - } else { - inputPath = RootedPath.toRootedPath(withinSandboxExecRoot, inputArtifact.getExecPath()); - } } else { PathFragment execPath = actionInput.getExecPath(); if (execPath.isAbsolute()) { diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java index c7996e38582fa1..81cd0fa4d9faef 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java @@ -82,7 +82,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context null); ImmutableSet.Builder writablePaths = ImmutableSet.builder(); - writablePaths.addAll(getWritableDirs(execRoot, execRoot, environment)); + writablePaths.addAll(getWritableDirs(execRoot, environment)); for (ActionInput output : spawn.getOutputFiles()) { writablePaths.add(execRoot.getRelative(output.getExecPath())); } diff --git a/src/main/java/com/google/devtools/build/lib/worker/SandboxedWorker.java b/src/main/java/com/google/devtools/build/lib/worker/SandboxedWorker.java index b4d5f9e0daaea7..873ea0542d9d24 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/SandboxedWorker.java +++ b/src/main/java/com/google/devtools/build/lib/worker/SandboxedWorker.java @@ -27,7 +27,6 @@ import com.google.devtools.build.lib.profiler.SilentCloseable; import com.google.devtools.build.lib.sandbox.CgroupsInfo; import com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder; -import com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.BindMount; import com.google.devtools.build.lib.sandbox.LinuxSandboxUtil; import com.google.devtools.build.lib.sandbox.SandboxHelpers; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; @@ -146,12 +145,11 @@ private ImmutableSet getWritableDirs(Path sandboxExecRoot) throws IOExcept return writableDirs.build(); } - private ImmutableList getBindMounts(Path sandboxExecRoot, @Nullable Path sandboxTmp) + private SortedMap getBindMounts(Path sandboxExecRoot, @Nullable Path sandboxTmp) throws UserExecException, IOException { FileSystem fs = sandboxExecRoot.getFileSystem(); Path tmpPath = fs.getPath("/tmp"); final SortedMap bindMounts = Maps.newTreeMap(); - ImmutableList.Builder result = ImmutableList.builder(); // Mount a fresh, empty temporary directory as /tmp for each sandbox rather than reusing the // host filesystem's /tmp. Since we're in a worker, we clean this dir between requests. bindMounts.put(tmpPath, sandboxTmp); @@ -168,9 +166,8 @@ private ImmutableList getBindMounts(Path sandboxExecRoot, @Nullable P } } // TODO(larsrc): Handle hermetic tmp - bindMounts.forEach((k, v) -> result.add(BindMount.of(k, v))); LinuxSandboxUtil.validateBindMounts(bindMounts); - return result.build(); + return bindMounts; } @Override diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilderTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilderTest.java index 9f8df6d9c30997..d1790e620f1c1d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilderTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilderTest.java @@ -20,8 +20,9 @@ import static org.junit.Assert.assertThrows; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.BindMount; +import com.google.common.collect.ImmutableSortedMap; import com.google.devtools.build.lib.vfs.DigestHashFunction; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; @@ -125,11 +126,12 @@ public void testLinuxSandboxCommandLineBuilder_buildsWithOptionalArguments() { ImmutableSet tmpfsDirectories = ImmutableSet.of(tmpfsDir1, tmpfsDir2); - ImmutableList bindMounts = - ImmutableList.of( - BindMount.of(bindMountSameSourceAndTarget, bindMountSameSourceAndTarget), - BindMount.of(bindMountTarget1, bindMountSource1), - BindMount.of(bindMountTarget2, bindMountSource2)); + ImmutableMap bindMounts = + ImmutableSortedMap.naturalOrder() + .put(bindMountSameSourceAndTarget, bindMountSameSourceAndTarget) + .put(bindMountTarget1, bindMountSource1) + .put(bindMountTarget2, bindMountSource2) + .buildOrThrow(); String cgroupsDir = "/sys/fs/cgroups/something"; From 4d2a4944dfe2d6e587f05034f233aa44efed3a99 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 16:02:45 +0200 Subject: [PATCH 09/32] Revert "Make all sandboxed strategies work with Filesets on their inputs." This reverts commit 182988347ef02399bc8fbc31a0ad972f0ea72210. --- .../build/lib/sandbox/DarwinSandboxedSpawnRunner.java | 5 +---- .../build/lib/sandbox/DockerSandboxedSpawnRunner.java | 5 +---- .../sandbox/ProcessWrapperSandboxedSpawnRunner.java | 5 +---- .../build/lib/sandbox/WindowsSandboxedSpawnRunner.java | 5 +---- .../google/devtools/build/lib/worker/WorkerModule.java | 1 - .../devtools/build/lib/worker/WorkerSpawnRunner.java | 10 ++++++---- .../build/lib/worker/WorkerSpawnRunnerTest.java | 2 -- 7 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java index 05cd3ab96d0864..85f1e5e1f49968 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java @@ -37,7 +37,6 @@ 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 com.google.devtools.build.lib.vfs.Root; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; @@ -103,7 +102,6 @@ private static boolean computeIsSupported() throws InterruptedException { private final SandboxHelpers helpers; private final Path execRoot; - private final ImmutableList packageRoots; private final boolean allowNetwork; private final ProcessWrapper processWrapper; private final Path sandboxBase; @@ -134,7 +132,6 @@ private static boolean computeIsSupported() throws InterruptedException { super(cmdEnv); this.helpers = helpers; this.execRoot = cmdEnv.getExecRoot(); - this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); this.allowNetwork = helpers.shouldAllowNetwork(cmdEnv.getOptions()); this.alwaysWritableDirs = getAlwaysWritableDirs(cmdEnv.getRuntime().getFileSystem()); this.processWrapper = ProcessWrapper.fromCommandEnvironment(cmdEnv); @@ -229,7 +226,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, + ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java index 3dacaeaa05ee9a..25c55ff7b15693 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java @@ -45,7 +45,6 @@ import com.google.devtools.build.lib.util.ProcessUtils; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.Root; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -143,7 +142,6 @@ public static boolean isSupported(CommandEnvironment cmdEnv, Path dockerClient) private final SandboxHelpers helpers; private final Path execRoot; - private final ImmutableList packageRoots; private final boolean allowNetwork; private final Path dockerClient; private final ProcessWrapper processWrapper; @@ -181,7 +179,6 @@ public static boolean isSupported(CommandEnvironment cmdEnv, Path dockerClient) super(cmdEnv); this.helpers = helpers; this.execRoot = cmdEnv.getExecRoot(); - this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); this.allowNetwork = helpers.shouldAllowNetwork(cmdEnv.getOptions()); this.dockerClient = dockerClient; this.processWrapper = ProcessWrapper.fromCommandEnvironment(cmdEnv); @@ -228,7 +225,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, + ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java index 274d83cc43e938..a6bc257705b4ba 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java @@ -27,7 +27,6 @@ import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.Root; import java.io.IOException; import java.time.Duration; @@ -41,7 +40,6 @@ public static boolean isSupported(CommandEnvironment cmdEnv) { private final SandboxHelpers helpers; private final ProcessWrapper processWrapper; private final Path execRoot; - private final ImmutableList packageRoots; private final Path sandboxBase; private final LocalEnvProvider localEnvProvider; private final TreeDeleter treeDeleter; @@ -62,7 +60,6 @@ public static boolean isSupported(CommandEnvironment cmdEnv) { this.helpers = helpers; this.processWrapper = ProcessWrapper.fromCommandEnvironment(cmdEnv); this.execRoot = cmdEnv.getExecRoot(); - this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); this.localEnvProvider = LocalEnvProvider.forCurrentOs(cmdEnv.getClientEnv()); this.sandboxBase = sandboxBase; this.treeDeleter = treeDeleter; @@ -102,7 +99,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, + ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java index 81cd0fa4d9faef..5272fdff22e210 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java @@ -27,7 +27,6 @@ import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.Root; import java.io.IOException; import java.time.Duration; @@ -36,7 +35,6 @@ final class WindowsSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { private final SandboxHelpers helpers; private final Path execRoot; - private final ImmutableList packageRoots; private final PathFragment windowsSandbox; private final LocalEnvProvider localEnvProvider; private final Duration timeoutKillDelay; @@ -57,7 +55,6 @@ final class WindowsSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { super(cmdEnv); this.helpers = helpers; this.execRoot = cmdEnv.getExecRoot(); - this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); this.windowsSandbox = windowsSandboxPath; this.timeoutKillDelay = timeoutKillDelay; this.localEnvProvider = new WindowsLocalEnvProvider(cmdEnv.getClientEnv()); @@ -78,7 +75,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, + ImmutableList.of(), null); ImmutableSet.Builder writablePaths = ImmutableSet.builder(); diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java index e9004ddfa2908a..16c683ce9db5ad 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerModule.java @@ -228,7 +228,6 @@ public void registerSpawnStrategies( new WorkerSpawnRunner( new SandboxHelpers(), env.getExecRoot(), - env.getPackageLocator().getPathEntries(), workerPool, env.getReporter(), localEnvProvider, diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index 74faf68b9340ee..d8832be84015ce 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -64,6 +64,9 @@ import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.RootedPath; +import com.google.devtools.build.lib.vfs.Root; +import com.google.devtools.build.lib.vfs.XattrProvider; +import com.google.devtools.build.lib.vfs.XattrProvider; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; import com.google.protobuf.ByteString; @@ -94,7 +97,7 @@ final class WorkerSpawnRunner implements SpawnRunner { private final SandboxHelpers helpers; private final Path execRoot; - private final ImmutableList packageRoots; + private final WorkerPool workers; private final ExtendedEventHandler reporter; private final ResourceManager resourceManager; private final RunfilesTreeUpdater runfilesTreeUpdater; @@ -106,7 +109,6 @@ final class WorkerSpawnRunner implements SpawnRunner { public WorkerSpawnRunner( SandboxHelpers helpers, Path execRoot, - ImmutableList packageRoots, WorkerPool workers, ExtendedEventHandler reporter, LocalEnvProvider localEnvProvider, @@ -118,7 +120,7 @@ public WorkerSpawnRunner( Clock clock) { this.helpers = helpers; this.execRoot = execRoot; - this.packageRoots = packageRoots; + this.workers = checkNotNull(workers); this.reporter = reporter; this.resourceManager = resourceManager; this.runfilesTreeUpdater = runfilesTreeUpdater; @@ -203,7 +205,7 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionContext context) PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, + ImmutableList.of(), null); } SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java index d723c0c906f601..a5e7fb11c64223 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java @@ -153,7 +153,6 @@ public void testExecInWorker_virtualInputs_doesntQueryInputFileCache() new WorkerSpawnRunner( new SandboxHelpers(), execRoot, - ImmutableList.of(), WorkerTestUtils.createTestWorkerPool(worker), reporter, localEnvProvider, @@ -556,7 +555,6 @@ private WorkerSpawnRunner createWorkerSpawnRunner(WorkerOptions workerOptions) { return new WorkerSpawnRunner( new SandboxHelpers(), fs.getPath("/execRoot"), - ImmutableList.of(), WorkerTestUtils.createTestWorkerPool(worker), reporter, localEnvProvider, From 8560be2eb5e54ea4e0aa6623b6d87fb95b0c7b5c Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 16:09:46 +0200 Subject: [PATCH 10/32] Revert "Make the Linux sandbox work with ActionInputs with absolute "exec paths"." This reverts commit 70691f2b76be0b9530e49c3df18356925843b465. --- .../sandbox/DarwinSandboxedSpawnRunner.java | 1 - .../sandbox/DockerSandboxedSpawnRunner.java | 1 - .../sandbox/LinuxSandboxedSpawnRunner.java | 4 -- .../ProcessWrapperSandboxedSpawnRunner.java | 2 - .../build/lib/sandbox/SandboxHelpers.java | 49 +------------------ .../sandbox/WindowsSandboxedSpawnRunner.java | 2 - .../build/lib/worker/WorkerSpawnRunner.java | 2 - .../build/lib/sandbox/SandboxHelpersTest.java | 11 ++--- 8 files changed, 5 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java index 85f1e5e1f49968..50adba2fc350a1 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java @@ -226,7 +226,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java index 25c55ff7b15693..802c860ba77500 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java @@ -225,7 +225,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index f5177a266920a4..84d2774dbb5ea3 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -50,7 +50,6 @@ 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 com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.Symlinks; import java.io.File; import java.io.IOException; @@ -128,7 +127,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final Duration timeoutKillDelay; private final TreeDeleter treeDeleter; private final Reporter reporter; - private final ImmutableList packageRoots; private String cgroupsDir; /** @@ -161,7 +159,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.localEnvProvider = new PosixLocalEnvProvider(cmdEnv.getClientEnv()); this.treeDeleter = treeDeleter; this.reporter = cmdEnv.getReporter(); - this.packageRoots = cmdEnv.getPackageLocator().getPathEntries(); } private boolean useHermeticTmp() { @@ -214,7 +211,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - packageRoots, null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java index a6bc257705b4ba..4c9b3a1b221bda 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.sandbox; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.actions.ForbiddenActionInputException; import com.google.devtools.build.lib.actions.Spawn; @@ -99,7 +98,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - ImmutableList.of(), null); SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 2e617244f68249..cb796a2f2d124c 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -21,7 +21,6 @@ import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; @@ -442,40 +441,6 @@ public String toString() { } } - /** - * Returns the appropriate {@link RootedPath} for a Fileset symlink. - * - *

Filesets are weird because sometimes exec paths of the {@link ActionInput}s in them are not - * relative, as exec paths should be, but absolute and point to under one of the package roots or - * the execroot. In order to handle this, if we find such an absolute exec path, we iterate over - * possible base directories. - * - *

The inputs to this function should be symlinks that are contained within Filesets; in - * particular, this is different from "unresolved symlinks" in that Fileset contents are regular - * files (but implemented by symlinks in the output tree) whose contents matter and unresolved - * symlinks are symlinks for which the important content is the result of {@code readlink()} - */ - private static RootedPath processFilesetSymlink( - PathFragment symlink, - Root execRootWithinSandbox, - PathFragment execRootFragment, - ImmutableList packageRoots) { - for (Root packageRoot : packageRoots) { - if (packageRoot.contains(symlink)) { - return RootedPath.toRootedPath(packageRoot, packageRoot.relativize(symlink)); - } - } - - if (symlink.startsWith(execRootFragment)) { - return RootedPath.toRootedPath(execRootWithinSandbox, symlink.relativeTo(execRootFragment)); - } - - throw new IllegalStateException( - String.format( - "absolute action input path '%s' not found under package roots", - symlink.getPathString())); - } - /** * Returns the inputs of a Spawn as a map of PathFragments relative to an execRoot to paths in the * host filesystem where the input files can be found. @@ -484,15 +449,12 @@ private static RootedPath processFilesetSymlink( * @param execRootPath the exec root from the point of view of the Bazel server * @param withinSandboxExecRootPath the exec root from within the sandbox (different from {@code * execRootPath} because the sandbox does magic with fiile system namespaces) - * @param packageRoots the package path entries during this build - * @param sandboxSourceRoots the directory where source roots are mapped within the sandbox * @throws IOException if processing symlinks fails */ public SandboxInputs processInputFiles( Map inputMap, Path execRootPath, Path withinSandboxExecRootPath, - ImmutableList packageRoots, Path sandboxSourceRoots) throws IOException, InterruptedException { Root withinSandboxExecRoot = Root.fromPath(withinSandboxExecRootPath); @@ -526,16 +488,7 @@ public SandboxInputs processInputFiles( if (actionInput instanceof EmptyActionInput) { inputPath = null; } else { - PathFragment execPath = actionInput.getExecPath(); - if (execPath.isAbsolute()) { - // This happens for ActionInputs that are part of Filesets (see the Javadoc on - // processFilesetSymlink()) - inputPath = - processFilesetSymlink( - actionInput.getExecPath(), execRoot, execRootPath.asFragment(), packageRoots); - } else { - inputPath = RootedPath.toRootedPath(execRoot, actionInput.getExecPath()); - } + inputPath = RootedPath.toRootedPath(execRoot, actionInput.getExecPath()); } inputFiles.put(pathFragment, inputPath); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java index 5272fdff22e210..4026f847adf026 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java @@ -15,7 +15,6 @@ package com.google.devtools.build.lib.sandbox; import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.ActionInput; @@ -75,7 +74,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - ImmutableList.of(), null); ImmutableSet.Builder writablePaths = ImmutableSet.builder(); diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index d8832be84015ce..e5c9aa130b896d 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -18,7 +18,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Stopwatch; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.hash.HashCode; import com.google.devtools.build.lib.actions.ActionExecutionMetadata; @@ -205,7 +204,6 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionContext context) PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot, execRoot, - ImmutableList.of(), null); } SandboxOutputs outputs = helpers.getOutputs(spawn); diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 37148d9ad8b794..39ab8fd1aaf6b9 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -105,8 +105,7 @@ public void processInputFiles_materializesParamFile() throws Exception { UTF_8); SandboxInputs inputs = - sandboxHelpers.processInputFiles( - inputMap(paramFile), execRootPath, execRootPath, ImmutableList.of(), null); + sandboxHelpers.processInputFiles(inputMap(paramFile), execRootPath, execRootPath, null); assertThat(inputs.getFiles()) .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile")); @@ -126,8 +125,7 @@ public void processInputFiles_materializesBinToolsFile() throws Exception { PathFragment.create("_bin/say_hello")); SandboxInputs inputs = - sandboxHelpers.processInputFiles( - inputMap(tool), execRootPath, execRootPath, ImmutableList.of(), null); + sandboxHelpers.processInputFiles(inputMap(tool), execRootPath, execRootPath, null); assertThat(inputs.getFiles()) .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello")); @@ -173,15 +171,14 @@ protected void setExecutable(PathFragment path, boolean executable) throws IOExc try { var unused = sandboxHelpers.processInputFiles( - inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null); + inputMap(input), customExecRoot, customExecRoot, null); finishProcessingSemaphore.release(); } catch (IOException | InterruptedException e) { throw new IllegalArgumentException(e); } }); var unused = - sandboxHelpers.processInputFiles( - inputMap(input), customExecRoot, customExecRoot, ImmutableList.of(), null); + sandboxHelpers.processInputFiles(inputMap(input), customExecRoot, customExecRoot, null); finishProcessingSemaphore.release(); future.get(); From 896e4eb3939086b8e4347f6dc5e8130a79ab6065 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 16:28:42 +0200 Subject: [PATCH 11/32] Revert "Replace Path with RootedPath in SandboxHelpers and maintain a set of roots in SandboxHelpers." This reverts commit a556969326107423738fbf8a80bad9aaf9935578. --- .../AbstractContainerizingSandboxedSpawn.java | 5 +- .../sandbox/DarwinSandboxedSpawnRunner.java | 4 +- .../sandbox/DockerSandboxedSpawnRunner.java | 4 +- .../sandbox/LinuxSandboxedSpawnRunner.java | 4 +- .../ProcessWrapperSandboxedSpawnRunner.java | 4 +- .../build/lib/sandbox/SandboxHelpers.java | 71 +++++------------ .../build/lib/sandbox/WindowsSandboxUtil.java | 9 +-- .../sandbox/WindowsSandboxedSpawnRunner.java | 4 +- .../build/lib/worker/WorkerExecRoot.java | 5 +- .../build/lib/worker/WorkerSpawnRunner.java | 11 +-- ...tractContainerizingSandboxedSpawnTest.java | 8 +- .../build/lib/sandbox/SandboxHelpersTest.java | 76 ++++++++----------- .../sandbox/SymlinkedSandboxedSpawnTest.java | 14 +--- .../build/lib/worker/SandboxHelper.java | 28 +++---- .../lib/worker/WorkerSpawnRunnerTest.java | 47 +++++------- .../lib/worker/WorkerSpawnStrategyTest.java | 11 ++- 16 files changed, 105 insertions(+), 200 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java index 3b2930118f43ca..bfc280c42d8b73 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java @@ -27,7 +27,6 @@ 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.RootedPath; import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; @@ -163,9 +162,9 @@ void createInputs(Iterable inputsToCreate, SandboxInputs inputs) } Path key = sandboxExecRoot.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - RootedPath fileDest = inputs.getFiles().get(fragment); + Path fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - copyFile(fileDest.asPath(), key); + copyFile(fileDest, key); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java index 50adba2fc350a1..932423ff1c80bb 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java @@ -224,9 +224,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); SandboxOutputs outputs = helpers.getOutputs(spawn); final Path sandboxConfigPath = sandboxPath.getRelative("sandbox.sb"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java index 802c860ba77500..9e85cbcbd87b8c 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/DockerSandboxedSpawnRunner.java @@ -223,9 +223,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); SandboxOutputs outputs = helpers.getOutputs(spawn); Duration timeout = context.getTimeout(); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 84d2774dbb5ea3..28ee4ab5ebeb01 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -209,9 +209,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); SandboxOutputs outputs = helpers.getOutputs(spawn); ImmutableMap environment = diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java index 4c9b3a1b221bda..f19a5ea361862e 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/ProcessWrapperSandboxedSpawnRunner.java @@ -96,9 +96,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); SandboxOutputs outputs = helpers.getOutputs(spawn); return new SymlinkedSandboxedSpawn( diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index cb796a2f2d124c..7c2d84a5814ef8 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -44,8 +44,6 @@ import com.google.devtools.build.lib.vfs.FileSystemUtils.MoveResult; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.common.options.OptionsParsingResult; import java.io.IOException; @@ -244,9 +242,9 @@ private static void cleanRecursively( */ static Optional getExpectedSymlinkDestination( PathFragment fragment, SandboxInputs inputs) { - RootedPath file = inputs.getFiles().get(fragment); + Path file = inputs.getFiles().get(fragment); if (file != null) { - return Optional.of(file.asPath().asFragment()); + return Optional.of(file.asFragment()); } return Optional.ofNullable(inputs.getSymlinks().get(fragment)); } @@ -382,31 +380,27 @@ public static void mountAdditionalPaths( /** Wrapper class for the inputs of a sandbox. */ public static final class SandboxInputs { - private final Map files; + private final Map files; private final Map virtualInputs; private final Map symlinks; - private final Map sourceRootBindMounts; private static final SandboxInputs EMPTY_INPUTS = - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); public SandboxInputs( - Map files, + Map files, Map virtualInputs, - Map symlinks, - Map sourceRootBindMounts) { + Map symlinks) { this.files = files; this.virtualInputs = virtualInputs; this.symlinks = symlinks; - this.sourceRootBindMounts = sourceRootBindMounts; } public static SandboxInputs getEmptyInputs() { return EMPTY_INPUTS; } - public Map getFiles() { + public Map getFiles() { return files; } @@ -423,16 +417,10 @@ public ImmutableMap getVirtualInputDigests() { * included. */ public SandboxInputs limitedCopy(Set allowed) { - Map limitedFiles = Maps.filterKeys(files, allowed::contains); - Map limitedSymlinks = - Maps.filterKeys(symlinks, allowed::contains); - Set usedRoots = - new HashSet<>(Maps.transformValues(limitedFiles, RootedPath::getRoot).values()); - Map limitedSourceRoots = - Maps.filterKeys(sourceRootBindMounts, usedRoots::contains); - return new SandboxInputs( - limitedFiles, ImmutableMap.of(), limitedSymlinks, limitedSourceRoots); + Maps.filterKeys(files, allowed::contains), + ImmutableMap.of(), + Maps.filterKeys(symlinks, allowed::contains)); } @Override @@ -446,27 +434,14 @@ public String toString() { * host filesystem where the input files can be found. * * @param inputMap the map of action inputs and where they should be visible in the action - * @param execRootPath the exec root from the point of view of the Bazel server - * @param withinSandboxExecRootPath the exec root from within the sandbox (different from {@code - * execRootPath} because the sandbox does magic with fiile system namespaces) + * @param execRoot the exec root * @throws IOException if processing symlinks fails */ - public SandboxInputs processInputFiles( - Map inputMap, - Path execRootPath, - Path withinSandboxExecRootPath, - Path sandboxSourceRoots) + public SandboxInputs processInputFiles(Map inputMap, Path execRoot) throws IOException, InterruptedException { - Root withinSandboxExecRoot = Root.fromPath(withinSandboxExecRootPath); - Root execRoot = - withinSandboxExecRootPath.equals(execRootPath) - ? withinSandboxExecRoot - : Root.fromPath(execRootPath); - - Map inputFiles = new TreeMap<>(); + Map inputFiles = new TreeMap<>(); Map inputSymlinks = new TreeMap<>(); Map virtualInputs = new HashMap<>(); - Map sourceRootToSandboxSourceRoot = new TreeMap<>(); for (Map.Entry e : inputMap.entrySet()) { if (Thread.interrupted()) { @@ -475,7 +450,7 @@ public SandboxInputs processInputFiles( PathFragment pathFragment = e.getKey(); ActionInput actionInput = e.getValue(); if (actionInput instanceof VirtualActionInput input) { - byte[] digest = input.atomicallyWriteRelativeTo(execRootPath); + byte[] digest = input.atomicallyWriteRelativeTo(execRoot); virtualInputs.put(input, digest); } @@ -483,22 +458,14 @@ public SandboxInputs processInputFiles( Path inputPath = execRoot.getRelative(actionInput.getExecPath()); inputSymlinks.put(pathFragment, inputPath.readSymbolicLink()); } else { - RootedPath inputPath; - - if (actionInput instanceof EmptyActionInput) { - inputPath = null; - } else { - inputPath = RootedPath.toRootedPath(execRoot, actionInput.getExecPath()); - } - + Path inputPath = + actionInput instanceof EmptyActionInput + ? null + : execRoot.getRelative(actionInput.getExecPath()); inputFiles.put(pathFragment, inputPath); } } - - Map sandboxRootToSourceRoot = new TreeMap<>(); - sourceRootToSandboxSourceRoot.forEach((k, v) -> sandboxRootToSourceRoot.put(v, k.asPath())); - - return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks, sandboxRootToSourceRoot); + return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks); } /** The file and directory outputs of a sandboxed spawn. */ diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java index 707f541e98f7e8..c582ecb3df3da5 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java @@ -24,7 +24,6 @@ import com.google.devtools.build.lib.shell.SubprocessBuilder.StreamAction; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayOutputStream; import java.io.File; @@ -107,7 +106,7 @@ public static class CommandLineBuilder { private Path stdoutPath; private Path stderrPath; private Set writableFilesAndDirectories = ImmutableSet.of(); - private Map readableFilesAndDirectories = new TreeMap<>(); + private Map readableFilesAndDirectories = new TreeMap<>(); private Set inaccessiblePaths = ImmutableSet.of(); private boolean useDebugMode = false; private List commandArguments = ImmutableList.of(); @@ -166,7 +165,7 @@ public CommandLineBuilder setWritableFilesAndDirectories( /** Sets the files or directories to make readable for the sandboxed process, if any. */ @CanIgnoreReturnValue public CommandLineBuilder setReadableFilesAndDirectories( - Map readableFilesAndDirectories) { + Map readableFilesAndDirectories) { this.readableFilesAndDirectories = readableFilesAndDirectories; return this; } @@ -213,8 +212,8 @@ public ImmutableList build() { for (Path writablePath : writableFilesAndDirectories) { commandLineBuilder.add("-w", writablePath.getPathString()); } - for (RootedPath readablePath : readableFilesAndDirectories.values()) { - commandLineBuilder.add("-r", readablePath.asPath().getPathString()); + for (Path readablePath : readableFilesAndDirectories.values()) { + commandLineBuilder.add("-r", readablePath.getPathString()); } for (Path writablePath : inaccessiblePaths) { commandLineBuilder.add("-b", writablePath.getPathString()); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java index 4026f847adf026..4c866b77a3aea9 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxedSpawnRunner.java @@ -72,9 +72,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context SandboxInputs readablePaths = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); ImmutableSet.Builder writablePaths = ImmutableSet.builder(); writablePaths.addAll(getWritableDirs(execRoot, environment)); diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java index d8802d06f7b66b..f45f4f47eb26c6 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java @@ -22,7 +22,6 @@ 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.RootedPath; import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; @@ -87,9 +86,9 @@ static void createInputs(Iterable inputsToCreate, SandboxInputs in } Path key = dir.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - RootedPath fileDest = inputs.getFiles().get(fragment); + Path fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - key.createSymbolicLink(fileDest.asPath()); + key.createSymbolicLink(fileDest); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index e5c9aa130b896d..265e2092a5a569 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -202,9 +202,7 @@ public SpawnResult exec(Spawn spawn, SpawnExecutionContext context) helpers.processInputFiles( context.getInputMapping( PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot, - execRoot, - null); + execRoot); } SandboxOutputs outputs = helpers.getOutputs(spawn); @@ -314,21 +312,20 @@ static void expandArgument(SandboxInputs inputs, String arg, WorkRequest.Builder throw new InterruptedException(); } String argValue = arg.substring(1); - RootedPath path = inputs.getFiles().get(PathFragment.create(argValue)); + Path path = inputs.getFiles().get(PathFragment.create(argValue)); if (path == null) { throw new IOException( String.format( "Failed to read @-argument '%s': file is not a declared input", argValue)); } try { - for (String line : FileSystemUtils.readLines(path.asPath(), UTF_8)) { + for (String line : FileSystemUtils.readLines(path, UTF_8)) { expandArgument(inputs, line, requestBuilder); } } catch (IOException e) { throw new IOException( String.format( - "Failed to read @-argument '%s' from file '%s'.", - argValue, path.asPath().getPathString()), + "Failed to read @-argument '%s' from file '%s'.", argValue, path.getPathString()), e); } } else { diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java index f1904ef4202776..700189556cbf9d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java @@ -35,7 +35,6 @@ 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.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -374,18 +373,17 @@ private static SandboxInputs createSandboxInputs( private static SandboxInputs createSandboxInputs( ImmutableList files, ImmutableMap symlinks) { - Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); + Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); for (String file : files) { filesMap.put(PathFragment.create(file), null); } return new SandboxInputs( filesMap, - /* virtualInputs= */ ImmutableMap.of(), + /*virtualInputs=*/ ImmutableMap.of(), symlinks.entrySet().stream() .collect( toImmutableMap( - e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue()))), - ImmutableMap.of()); + e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue())))); } /** Return a list of all entries under the provided directory recursively. */ diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 39ab8fd1aaf6b9..5c8b4044bb81cb 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -43,8 +43,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -70,14 +68,12 @@ public class SandboxHelpersTest { private final Scratch scratch = new Scratch(); - private Path execRootPath; - private Root execRoot; + private Path execRoot; @Nullable private ExecutorService executorToCleanup; @Before public void createExecRoot() throws IOException { - execRootPath = scratch.dir("/execRoot"); - execRoot = Root.fromPath(execRootPath); + execRoot = scratch.dir("/execRoot"); } @After @@ -90,10 +86,6 @@ public void shutdownExecutor() throws InterruptedException { executorToCleanup.awaitTermination(TestUtils.WAIT_TIMEOUT_SECONDS, SECONDS); } - private RootedPath execRootedPath(String execPath) { - return RootedPath.toRootedPath(execRoot, PathFragment.create(execPath)); - } - @Test public void processInputFiles_materializesParamFile() throws Exception { SandboxHelpers sandboxHelpers = new SandboxHelpers(); @@ -104,16 +96,15 @@ public void processInputFiles_materializesParamFile() throws Exception { ParameterFileType.UNQUOTED, UTF_8); - SandboxInputs inputs = - sandboxHelpers.processInputFiles(inputMap(paramFile), execRootPath, execRootPath, null); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(paramFile), execRoot); assertThat(inputs.getFiles()) - .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile")); + .containsExactly(PathFragment.create("paramFile"), execRoot.getChild("paramFile")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRootPath.getChild("paramFile"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRoot.getChild("paramFile"), UTF_8)) .containsExactly("-a", "-b") .inOrder(); - assertThat(execRootPath.getChild("paramFile").isExecutable()).isTrue(); + assertThat(execRoot.getChild("paramFile").isExecutable()).isTrue(); } @Test @@ -124,16 +115,16 @@ public void processInputFiles_materializesBinToolsFile() throws Exception { scratch.file("tool", "#!/bin/bash", "echo hello"), PathFragment.create("_bin/say_hello")); - SandboxInputs inputs = - sandboxHelpers.processInputFiles(inputMap(tool), execRootPath, execRootPath, null); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(tool), execRoot); assertThat(inputs.getFiles()) - .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello")); + .containsExactly( + PathFragment.create("_bin/say_hello"), execRoot.getRelative("_bin/say_hello")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRootPath.getRelative("_bin/say_hello"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRoot.getRelative("_bin/say_hello"), UTF_8)) .containsExactly("#!/bin/bash", "echo hello") .inOrder(); - assertThat(execRootPath.getRelative("_bin/say_hello").isExecutable()).isTrue(); + assertThat(execRoot.getRelative("_bin/say_hello").isExecutable()).isTrue(); } /** @@ -169,16 +160,13 @@ protected void setExecutable(PathFragment path, boolean executable) throws IOExc executorToCleanup.submit( () -> { try { - var unused = - sandboxHelpers.processInputFiles( - inputMap(input), customExecRoot, customExecRoot, null); + sandboxHelpers.processInputFiles(inputMap(input), customExecRoot); finishProcessingSemaphore.release(); } catch (IOException | InterruptedException e) { throw new IllegalArgumentException(e); } }); - var unused = - sandboxHelpers.processInputFiles(inputMap(input), customExecRoot, customExecRoot, null); + sandboxHelpers.processInputFiles(inputMap(input), customExecRoot); finishProcessingSemaphore.release(); future.get(); @@ -242,10 +230,8 @@ public void atomicallyWriteVirtualInput_writesArbitraryVirtualInput() throws Exc @Test public void cleanExisting_updatesDirs() throws IOException, InterruptedException { - RootedPath inputTxt = - RootedPath.toRootedPath( - Root.fromPath(scratch.getFileSystem().getPath("/")), PathFragment.create("hello.txt")); - Path rootDir = execRootPath.getParentDirectory(); + Path inputTxt = scratch.getFileSystem().getPath(PathFragment.create("/hello.txt")); + Path rootDir = execRoot.getParentDirectory(); PathFragment input1 = PathFragment.create("existing/directory/with/input1.txt"); PathFragment input2 = PathFragment.create("partial/directory/input2.txt"); PathFragment input3 = PathFragment.create("new/directory/input3.txt"); @@ -253,7 +239,6 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()); Set inputsToCreate = new LinkedHashSet<>(); LinkedHashSet dirsToCreate = new LinkedHashSet<>(); @@ -274,17 +259,17 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException assertThat(inputsToCreate).containsExactly(input1, input2, input3); // inputdir1 exists fully - execRootPath.getRelative(inputDir1).createDirectoryAndParents(); + execRoot.getRelative(inputDir1).createDirectoryAndParents(); // inputdir2 exists partially, should be kept nonetheless. - execRootPath + execRoot .getRelative(inputDir2) .getParentDirectory() .getRelative("doomedSubdir") .createDirectoryAndParents(); // inputDir3 just doesn't exist // outputDir only exists partially - execRootPath.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); - execRootPath.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); + execRoot.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); + execRoot.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); // `thiswillbeafile/output` simulates a directory that was in the stashed dir but whose same // path is used later for a regular file. scratch.dir("/execRoot/thiswillbeafile/output"); @@ -295,23 +280,22 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt, input4, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()); - SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRootPath); + SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRoot); assertThat(dirsToCreate).containsExactly(inputDir2, inputDir3, outputDir); - assertThat(execRootPath.getRelative("existing/directory/with").exists()).isTrue(); - assertThat(execRootPath.getRelative("partial").exists()).isTrue(); - assertThat(execRootPath.getRelative("partial/doomedSubdir").exists()).isFalse(); - assertThat(execRootPath.getRelative("partial/directory").exists()).isFalse(); - assertThat(execRootPath.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); - assertThat(execRootPath.getRelative("out").exists()).isTrue(); - assertThat(execRootPath.getRelative("out/dir").exists()).isFalse(); + assertThat(execRoot.getRelative("existing/directory/with").exists()).isTrue(); + assertThat(execRoot.getRelative("partial").exists()).isTrue(); + assertThat(execRoot.getRelative("partial/doomedSubdir").exists()).isFalse(); + assertThat(execRoot.getRelative("partial/directory").exists()).isFalse(); + assertThat(execRoot.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); + assertThat(execRoot.getRelative("out").exists()).isTrue(); + assertThat(execRoot.getRelative("out/dir").exists()).isFalse(); } @Test public void populateInputsAndDirsToCreate_createsMappedDirectories() { ArtifactRoot outputRoot = - ArtifactRoot.asDerivedRoot(execRootPath, ArtifactRoot.RootType.Output, "outputs"); + ArtifactRoot.asDerivedRoot(execRoot, ArtifactRoot.RootType.Output, "outputs"); ActionInput outputFile = ActionsTestUtil.createArtifact(outputRoot, "bin/config/dir/file"); ActionInput outputDir = ActionsTestUtil.createTreeArtifactWithGeneratingAction( @@ -351,13 +335,13 @@ public void moveOutputs_mappedPathMovedToUnmappedPath() throws Exception { .setPathMapper(pathMapper) .build(); var sandboxHelpers = new SandboxHelpers(); - Path sandboxBase = execRootPath.getRelative("sandbox"); + Path sandboxBase = execRoot.getRelative("sandbox"); PathFragment mappedOutputPath = PathFragment.create("bin/output"); sandboxBase.getRelative(mappedOutputPath).getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeLinesAs( sandboxBase.getRelative(mappedOutputPath), UTF_8, "hello", "pathmapper"); - Path realBase = execRootPath.getRelative("real"); + Path realBase = execRoot.getRelative("real"); SandboxHelpers.moveOutputs(sandboxHelpers.getOutputs(spawn), sandboxBase, realBase); assertThat( diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java index dfc659bcb6d3ec..9c26026eeafe61 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java @@ -26,8 +26,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -62,9 +60,8 @@ public final void setupTestDirs() throws IOException { @Test public void createFileSystem() throws Exception { - RootedPath helloTxt = - RootedPath.toRootedPath(Root.fromPath(workspaceDir), PathFragment.create("hello.txt")); - FileSystemUtils.createEmptyFile(helloTxt.asPath()); + Path helloTxt = workspaceDir.getRelative("hello.txt"); + FileSystemUtils.createEmptyFile(helloTxt); SymlinkedSandboxedSpawn symlinkedExecRoot = new SymlinkedSandboxedSpawn( @@ -75,7 +72,6 @@ public void createFileSystem() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create( ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of()), @@ -89,8 +85,7 @@ public void createFileSystem() throws Exception { symlinkedExecRoot.createFileSystem(); assertThat(execRoot.getRelative("such/input.txt").isSymbolicLink()).isTrue(); - assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()) - .isEqualTo(helloTxt.asPath()); + assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()).isEqualTo(helloTxt); assertThat(execRoot.getRelative("very").isDirectory()).isTrue(); assertThat(execRoot.getRelative("wow/writable").isDirectory()).isTrue(); } @@ -107,8 +102,7 @@ public void copyOutputs() throws Exception { execRoot, ImmutableList.of("/bin/true"), ImmutableMap.of(), - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create( ImmutableSet.of(outputFile.relativeTo(execRoot)), ImmutableSet.of()), ImmutableSet.of(), diff --git a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java index 745e59b90c71c4..788614b4e47256 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java +++ b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java @@ -16,7 +16,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.cache.VirtualActionInput; import com.google.devtools.build.lib.actions.util.ActionsTestUtil; @@ -25,8 +24,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; @@ -41,7 +38,7 @@ class SandboxHelper { /** Map from workdir-relative input path to optional real file path. */ - private final Map inputs = new HashMap<>(); + private final Map inputs = new HashMap<>(); private final Map virtualInputs = new HashMap<>(); private final Map symlinks = new HashMap<>(); @@ -50,15 +47,12 @@ class SandboxHelper { private final List outputDirs = new ArrayList<>(); /** The global execRoot. */ - final Path execRootPath; - - final Root execRoot; + final Path execRoot; /** The worker process's sandbox root. */ final Path workDir; public SandboxHelper(Path execRoot, Path workDir) { - this.execRootPath = execRoot; - this.execRoot = Root.fromPath(execRoot); + this.execRoot = execRoot; this.workDir = workDir; } @@ -70,9 +64,7 @@ public SandboxHelper(Path execRoot, Path workDir) { public SandboxHelper addInputFile(String relativePath, String workspacePath) { inputs.put( PathFragment.create(relativePath), - workspacePath != null - ? RootedPath.toRootedPath(execRoot, PathFragment.create(workspacePath)) - : null); + workspacePath != null ? execRoot.getRelative(workspacePath) : null); return this; } @@ -85,7 +77,7 @@ public SandboxHelper addInputFile(String relativePath, String workspacePath) { public SandboxHelper addAndCreateInputFile( String relativePath, String workspacePath, String contents) throws IOException { addInputFile(relativePath, workspacePath); - Path absPath = execRootPath.getRelative(workspacePath); + Path absPath = execRoot.getRelative(workspacePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -96,7 +88,7 @@ public SandboxHelper addAndCreateInputFile( public SandboxHelper addAndCreateVirtualInput(String relativePath, String contents) { VirtualActionInput input = ActionsTestUtil.createVirtualActionInput(relativePath, contents); byte[] digest = - execRootPath + execRoot .getRelative(relativePath) .getFileSystem() .getDigestFunction() @@ -135,7 +127,7 @@ public SandboxHelper addOutputDir(String relativePath) { */ @CanIgnoreReturnValue public SandboxHelper addWorkerFile(String relativePath) { - Path absPath = execRootPath.getRelative(relativePath); + Path absPath = execRoot.getRelative(relativePath); workerFiles.put(PathFragment.create(relativePath), absPath); return this; } @@ -148,7 +140,7 @@ public SandboxHelper addWorkerFile(String relativePath) { public SandboxHelper addAndCreateWorkerFile(String relativePath, String contents) throws IOException { addWorkerFile(relativePath); - Path absPath = execRootPath.getRelative(relativePath); + Path absPath = execRoot.getRelative(relativePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -173,7 +165,7 @@ public SandboxHelper createExecRootFile(String relativePath, String contents) th @CanIgnoreReturnValue public SandboxHelper createWorkspaceDirFile(String workspaceDirPath, String contents) throws IOException { - Path absPath = execRootPath.getRelative(workspaceDirPath); + Path absPath = execRoot.getRelative(workspaceDirPath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -192,7 +184,7 @@ public SandboxHelper createSymlink(String relativePath, String relativeDestinati } public SandboxInputs getSandboxInputs() { - return new SandboxInputs(inputs, virtualInputs, symlinks, ImmutableMap.of()); + return new SandboxInputs(inputs, virtualInputs, symlinks); } public SandboxOutputs getSandboxOutputs() { diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java index a5e7fb11c64223..9a70b84886bf40 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java @@ -63,8 +63,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; @@ -125,8 +123,7 @@ public void testExecInWorker_happyPath() throws ExecException, InterruptedExcept spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -218,8 +215,7 @@ public void testExecInWorker_finishesAsyncOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -262,8 +258,7 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -305,8 +300,7 @@ public void testExecInWorker_unsandboxedDiesOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -339,8 +333,7 @@ public void testExecInWorker_noMultiplexWithDynamic() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -377,8 +370,7 @@ private void assertRecordedResponsethrowsException(String recordedResponse, Stri spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -423,12 +415,12 @@ public void testExpandArgument_expandsArgumentsRecursively() SandboxInputs inputs = new SandboxInputs( ImmutableMap.of( - PathFragment.create("file"), - asRootedPath("/file"), - PathFragment.create("file2"), - asRootedPath("/file2")), - ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()); + PathFragment.create("file"), + fs.getPath("/file"), + PathFragment.create("file2"), + fs.getPath("/file2")), + ImmutableMap.of(), + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "arg2", "arg3", "multi arg", ""); @@ -441,8 +433,9 @@ public void testExpandArgument_expandsOnlyProperArguments() FileSystemUtils.writeIsoLatin1(fs.getPath("/file"), "arg1\n@@nonfile\n@foo//bar\narg2"); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), asRootedPath("/file")), ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()); + ImmutableMap.of(PathFragment.create("file"), fs.getPath("/file")), + ImmutableMap.of(), + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "@@nonfile", "@foo//bar", "arg2"); @@ -453,8 +446,7 @@ public void testExpandArgument_failsOnMissingFile() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), asRootedPath("/dir/file")), - ImmutableMap.of(), + ImmutableMap.of(PathFragment.create("file"), fs.getPath("/dir/file")), ImmutableMap.of(), ImmutableMap.of()); IOException e = @@ -570,8 +562,7 @@ private WorkerSpawnRunner createWorkerSpawnRunner(WorkerOptions workerOptions) { public void testExpandArgument_failsOnUndeclaredInput() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); IOException e = assertThrows( IOException.class, @@ -580,10 +571,6 @@ public void testExpandArgument_failsOnUndeclaredInput() { assertThat(e).hasMessageThat().contains("declared input"); } - private RootedPath asRootedPath(String path) { - return RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(path)); - } - private static String logMarker(String text) { return "---8<---8<--- " + text + " ---8<---8<---\n"; } diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java index d20f8626626c84..6c8ff50f2e2729 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java @@ -20,9 +20,8 @@ import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; 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 com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.util.FileSystems; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import java.io.File; @@ -51,13 +50,13 @@ public void expandArgumentsPreservesEmptyLines() throws Exception { flags.forEach(pw::println); } - RootedPath path = - RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(flagfile.getAbsolutePath())); + Path path = fs.getPath(flagfile.getAbsolutePath()); WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("flagfile.txt"), path), ImmutableMap.of(), - ImmutableMap.of(), ImmutableMap.of()); + ImmutableMap.of(PathFragment.create("flagfile.txt"), path), + ImmutableMap.of(), + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@flagfile.txt", requestBuilder); assertThat(requestBuilder.getArgumentsList()).containsExactlyElementsIn(flags); From 161ac558d6cbe9e8ca6bae8f31f0f5214eeb6974 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 15 Apr 2024 20:56:11 +0200 Subject: [PATCH 12/32] Fix Java tests --- .../LinuxSandboxedSpawnRunnerTest.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java index dd5363686efcc6..707cd60e16466c 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java @@ -42,6 +42,7 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import java.io.IOException; import java.time.Duration; +import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -180,7 +181,10 @@ public void hermeticTmp_tmpCreatedAndMounted() throws Exception { assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class); String args = String.join(" ", sandboxedSpawn.getArguments()); assertThat(args).contains("-w /tmp"); - assertThat(args).contains("-M " + hermeticTmpPath + " -m /tmp"); + assertThat(args) + .matches( + ".* -F %1$s/[^ ]+ -f %1$s/[^ ]+ .*" + .formatted(Pattern.quote(hermeticTmpPath.getPathString()))); } @Test @@ -200,11 +204,12 @@ public void hermeticTmp_sandboxTmpfsOnTmp_tmpNotCreatedOrMounted() throws Except String args = String.join(" ", sandboxedSpawn.getArguments()); assertThat(args).contains("-w /tmp"); assertThat(args).contains("-e /tmp"); - assertThat(args).doesNotContain("-m /tmp"); + assertThat(args).doesNotContain("-F "); + assertThat(args).doesNotContain("-f "); } @Test - public void hermeticTmp_sandboxTmpfsUnderTmp_tmpNotCreatedOrMounted() throws Exception { + public void hermeticTmp_sandboxTmpfsUnderTmp_tmpCreatedAndMounted() throws Exception { runtimeWrapper.addOptions( "--incompatible_sandbox_hermetic_tmp", "--sandbox_tmpfs_path=/tmp/subdir"); CommandEnvironment commandEnvironment = createCommandEnvironment(); @@ -215,13 +220,15 @@ public void hermeticTmp_sandboxTmpfsUnderTmp_tmpNotCreatedOrMounted() throws Exc Path sandboxPath = sandboxedSpawn.getSandboxExecRoot().getParentDirectory().getParentDirectory(); Path hermeticTmpPath = sandboxPath.getRelative("_hermetic_tmp"); - assertThat(hermeticTmpPath.isDirectory()).isFalse(); + assertThat(hermeticTmpPath.isDirectory()).isTrue(); assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class); String args = String.join(" ", sandboxedSpawn.getArguments()); assertThat(args).contains("-w /tmp"); - assertThat(args).contains("-e /tmp"); - assertThat(args).doesNotContain("-m /tmp"); + assertThat(args) + .matches( + ".* -F %1$s/[^ ]+ -f %1$s/[^ ]+ .*" + .formatted(Pattern.quote(hermeticTmpPath.getPathString()))); } private static LinuxSandboxedSpawnRunner setupSandboxAndCreateRunner( From 3b84d1e2b58c497238f9aafaa44d479a2f3764cf Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 16 Apr 2024 08:46:58 +0200 Subject: [PATCH 13/32] fix mount tests --- src/test/shell/bazel/bazel_sandboxing_test.sh | 129 ++++++++++++++---- 1 file changed, 104 insertions(+), 25 deletions(-) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 80ea6dc810c144..98c84bcfd114a8 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -32,6 +32,14 @@ function set_up { sed -i.bak '/sandbox_tmpfs_path/d' "$bazelrc" } +function assert_not_exists() { + path="$1" + [ ! -f "$path" ] && return 0 + + fail "Expected file '$path' to not exist, but it did" + return 1 +} + function test_sandboxed_tooldir() { mkdir -p examples/genrule @@ -309,6 +317,57 @@ EOF bazel build //pkg:a &>$TEST_log || fail "expected build to succeed" } +function setup_tmp_hermeticity_check() { + local -r tmpdir=$1 + + mkdir -p test + cat > test/BUILD <<'EOF' +cc_binary( + name = "create_file", + srcs = ["create_file.cc"], +) + +[ + genrule( + name = "gen" + str(i), + outs = ["gen{}.txt".format(i)], + tools = [":create_file"], + cmd = """ + path=$$($(location :create_file)) + cp "$$path" $@ + """, + ) + for i in range(1, 3) +] +EOF + cat > test/create_file.cc < +#include +#include +#include +#include +#include +#include + +int main() { + int fd = open("$tmpdir/bazel_was_here", O_CREAT | O_EXCL | O_WRONLY, 0600); + if (fd < 0) { + perror("open"); + return 1; + } + if (write(fd, "HERMETIC\n", 9) != 9) { + perror("write"); + return 1; + } + close(fd); + printf("$tmpdir/bazel_was_here\n"); + return 0; +} +EOF +} + function test_add_mount_pair_tmp_source() { if [[ "$PLATFORM" == "darwin" ]]; then # Tests Linux-specific functionality @@ -321,19 +380,26 @@ function test_add_mount_pair_tmp_source() { trap "rm -fr $mounted" EXIT echo GOOD > "$mounted/data.txt" + local tmp_dir=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") + trap "rm -fr $tmp_dir" EXIT + setup_tmp_hermeticity_check "$tmp_dir" + mkdir -p pkg - cat > pkg/BUILD < pkg/BUILD <<'EOF' genrule( name = "gen", outs = ["gen.txt"], - # Verify that /tmp is still hermetic. - cmd = """[ ! -e "${mounted}/data.txt" ] && cp /etc/data.txt \$@""", + cmd = "cp /etc/data.txt $@", ) EOF # This assumes the existence of /etc on the host system - bazel build --sandbox_add_mount_pair="$mounted:/etc" //pkg:gen || fail "build failed" - assert_contains GOOD bazel-bin/pkg/gen.txt + bazel build --sandbox_add_mount_pair="$mounted:/etc" \ + //pkg:gen //test:all || fail "build failed" + assert_equals GOOD "$(cat bazel-bin/pkg/gen.txt)" + assert_equals HERMETIC "$(cat bazel-bin/test/gen1.txt)" + assert_equals HERMETIC "$(cat bazel-bin/test/gen2.txt)" + assert_not_exists "$tmp_dir/bazel_was_here" } function test_add_mount_pair_tmp_target() { @@ -348,20 +414,28 @@ function test_add_mount_pair_tmp_target() { trap "rm -fr $source_dir" EXIT echo BAD > "$source_dir/data.txt" + local tmp_dir=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") + trap "rm -fr $tmp_dir" EXIT + setup_tmp_hermeticity_check "$tmp_dir" + mkdir -p pkg cat > pkg/BUILD < \$@""", + cmd = """ls "$source_dir" > \$@""", ) EOF # This assumes the existence of /etc on the host system - bazel build --sandbox_add_mount_pair="/etc:$source_dir" //pkg:gen || fail "build failed" + bazel build --sandbox_add_mount_pair="/etc:$source_dir" \ + //pkg:gen //test:all || fail "build failed" assert_contains passwd bazel-bin/pkg/gen.txt + assert_not_contains data.txt bazel-bin/pkg/gen.txt + assert_equals HERMETIC "$(cat bazel-bin/test/gen1.txt)" + assert_equals HERMETIC "$(cat bazel-bin/test/gen2.txt)" + assert_not_exists "$tmp_dir/bazel_was_here" } function test_add_mount_pair_tmp_target_and_source() { @@ -376,22 +450,25 @@ function test_add_mount_pair_tmp_target_and_source() { trap "rm -fr $mounted" EXIT echo GOOD > "$mounted/data.txt" - local tmp_file=$(mktemp "/tmp/bazel_tmp.XXXXXXXX") - trap "rm $tmp_file" EXIT - echo BAD > "$tmp_file" + local tmp_dir=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") + trap "rm -fr $tmp_dir" EXIT + setup_tmp_hermeticity_check "$tmp_dir" mkdir -p pkg cat > pkg/BUILD < "$tmp_file" - local tmpfs=$(mktemp -d "/tmp/bazel_tmpfs.XXXXXXXX") trap "rm -fr $tmpfs" EXIT echo BAD > "$tmpfs/data.txt" + local tmp_dir=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") + trap "rm -fr $tmp_dir" EXIT + setup_tmp_hermeticity_check "$tmp_dir" + mkdir -p pkg cat > pkg/BUILD < Date: Tue, 16 Apr 2024 09:59:40 +0200 Subject: [PATCH 14/32] Add back package path test Accidentally deleted in 56dde84e0813b28331bc4e0283dc318d3c12277d. --- src/test/shell/bazel/bazel_sandboxing_test.sh | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 98c84bcfd114a8..152e840f0e1060 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -644,6 +644,96 @@ EOF assert_not_exists "$tmp_dir/bazel_was_here" } +function test_hermetic_tmp_under_tmp { + if [[ "$(uname -s)" != Linux ]]; then + echo "Skipping test: --incompatible_sandbox_hermetic_tmp is only supported in Linux" 1>&2 + return 0 + fi + + temp_dir=$(mktemp -d /tmp/test.XXXXXX) + trap 'rm -rf ${temp_dir}' EXIT + + mkdir -p "${temp_dir}/workspace/a" + mkdir -p "${temp_dir}/package-path/b" + mkdir -p "${temp_dir}/repo/c" + mkdir -p "${temp_dir}/output-base" + + cd "${temp_dir}/workspace" + cat > WORKSPACE < a/BUILD <<'EOF' +genrule( + name = "g", + outs = ["go"], + srcs = [], + cmd = "echo GO > $@", +) +sh_binary( + name = "bin", + srcs = ["bin.sh"], + data = [":s", ":go", "//b:s", "//b:go", "@repo//c:s", "@repo//c:go"], +) +genrule( + name = "t", + tools = [":bin"], + srcs = [":s", ":go", "//b:s", "//b:go", "@repo//c:s", "@repo//c:go"], + outs = ["to"], + cmd = "\n".join([ + "RUNFILES=$(location :bin).runfiles/_main", + "S=$(location :s); GO=$(location :go)", + "BS=$(location //b:s); BGO=$(location //b:go)", + "RS=$(location @repo//c:s); RGO=$(location @repo//c:go)", + "for i in $$S $$GO $$BS $$BGO $$RS $$RGO; do", + " echo reading $$i", + " cat $$i >> $@", + "done", + "for i in a/s a/go b/s b/go ../repo/c/s ../repo/c/go; do", + " echo reading $$RUNFILES/$$i", + " cat $$RUNFILES/$$i >> $@", + "done", + ]), +) +EOF + + touch a/bin.sh + chmod +x a/bin.sh + + touch ../repo/WORKSPACE + cat > ../repo/c/BUILD <<'EOF' +exports_files(["s"]) +genrule( + name = "g", + outs = ["go"], + srcs = [], + cmd = "echo GO > $@", + visibility = ["//visibility:public"], +) +EOF + + cat > ../package-path/b/BUILD <<'EOF' +exports_files(["s"]) +genrule( + name = "g", + outs = ["go"], + srcs = [], + cmd = "echo GO > $@", + visibility = ["//visibility:public"], +) +EOF + + touch "a/s" "../package-path/b/s" "../repo/c/s" + + cat WORKSPACE + bazel \ + --output_base="${temp_dir}/output-base" \ + build \ + --incompatible_sandbox_hermetic_tmp \ + --package_path="%workspace%:${temp_dir}/package-path" \ + //a:t || fail "build failed" +} + # The test shouldn't fail if the environment doesn't support running it. check_sandbox_allowed || exit 0 From 17b24043e7d025f1909e4556522d3f68aa8a3972 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Sat, 4 May 2024 19:20:20 +0200 Subject: [PATCH 15/32] Add standalone test --- .../google/devtools/build/lib/sandbox/BUILD | 26 +++++ .../sandbox/LinuxSandboxOverlayfsTest.java | 97 +++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD index fea187dd0d4e5d..82a30425865694 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD @@ -145,3 +145,29 @@ java_test( "//third_party:truth", ], ) + +java_test( + name = "LinuxSandboxOverlayfsTest", + size = "small", + srcs = ["LinuxSandboxOverlayfsTest.java"], + tags = [ + # Requires overlayfs support in the kernel + "no_macos", + "no_windows", + # Kernels have a hard limit on the number of overlayfs mounts layers + # which can be as low as 2, so running this in a sandbox with + # overlayfs may fail. + "no-sandbox", + ], + deps = [ + ":testutil", + "//src/main/java/com/google/devtools/build/lib/sandbox:linux_sandbox_command_line_builder", + "//src/main/java/com/google/devtools/build/lib/util:os", + "//src/main/java/com/google/devtools/build/lib/vfs", + "//src/test/java/com/google/devtools/build/lib/buildtool/util", + "//src/test/java/com/google/devtools/build/lib/vfs/util", + "//third_party:guava", + "//third_party:junit4", + "//third_party:truth", + ], +) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java new file mode 100644 index 00000000000000..5e02a72c1b6715 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java @@ -0,0 +1,97 @@ +// Copyright 2022 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 static com.google.common.truth.TruthJUnit.assume; +import static java.nio.charset.StandardCharsets.UTF_8; + +import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase; +import com.google.devtools.build.lib.util.OS; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.Path; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +/** Tests for linux-sandbox overlayfs functionality. */ +public final class LinuxSandboxOverlayfsTest extends BuildIntegrationTestCase { + + @Before + public void assumeRunningOnLinux() { + assume().that(OS.getCurrent()).isEqualTo(OS.LINUX); + } + + @Test + public void nestedOverlayFs() throws Exception { + var tmpMountNio = + Files.createTempDirectory(java.nio.file.Path.of("/tmp"), "bazel-sandbox-test"); + tmpMountNio.toFile().deleteOnExit(); + var tmpMount = fileSystem.getPath(tmpMountNio.toAbsolutePath().toString()); + + Path binDir = testRoot.getRelative("_bin"); + binDir.createDirectoryAndParents(); + Path linuxSandboxPath = SpawnRunnerTestUtil.copyLinuxSandboxIntoPath(binDir); + + // This path is writable with the sandboxed runner. + Path writableRoot = testRoot.getRelative("root"); + writableRoot.createDirectoryAndParents(); + + Path hermeticTmpOuter = writableRoot.getRelative("_hermetic_tmp_outer"); + Path upperDirOuter = hermeticTmpOuter.getRelative("_upper"); + upperDirOuter.createDirectoryAndParents(); + Path workDirOuter = hermeticTmpOuter.getRelative("_work"); + workDirOuter.createDirectoryAndParents(); + Path mountDirOuter = hermeticTmpOuter.getRelative("_mount"); + mountDirOuter.createDirectoryAndParents(); + FileSystemUtils.writeContent(mountDirOuter.getChild("file"), UTF_8, "outer_content"); + + Path hermeticTmpInner = writableRoot.getRelative("_hermetic_tmp_inner"); + Path upperDirInner = hermeticTmpInner.getRelative("_upper"); + upperDirInner.createDirectoryAndParents(); + Path workDirInner = hermeticTmpInner.getRelative("_work"); + workDirInner.createDirectoryAndParents(); + Path mountDirInner = hermeticTmpInner.getRelative("_mount"); + mountDirInner.createDirectoryAndParents(); + FileSystemUtils.writeContent(mountDirInner.getChild("file"), UTF_8, "inner_content"); + + var argsInner = + LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandboxPath) + .mountOverlayfsOnTmp(upperDirInner.getPathString(), workDirInner.getPathString()) + .setWritableFilesAndDirectories(Set.of(writableRoot)) + .setBindMounts(Map.of(tmpMount, mountDirInner)) + .buildForCommand(List.of("cat", tmpMount.getChild("file").getPathString())); + + var argsOuter = + LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandboxPath) + .mountOverlayfsOnTmp(upperDirOuter.getPathString(), workDirOuter.getPathString()) + .setWritableFilesAndDirectories(Set.of(writableRoot)) + .setBindMounts(Map.of(tmpMount, mountDirOuter)) + .buildForCommand(argsInner); + + Process process = new ProcessBuilder().command(argsOuter).start(); + byte[] stderr = process.getErrorStream().readAllBytes(); + assertThat(new String(stderr, StandardCharsets.UTF_8)).isEmpty(); + byte[] stdout = process.getInputStream().readAllBytes(); + assertThat(new String(stdout, StandardCharsets.UTF_8)).isEqualTo("inner_content"); + int exitCode = process.waitFor(); + assertThat(exitCode).isEqualTo(0); + } +} From 72d3623c72592d81d8e07633218ffec662eeac43 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 7 May 2024 20:15:14 +0200 Subject: [PATCH 16/32] Improve test --- .../sandbox/LinuxSandboxOverlayfsTest.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java index 5e02a72c1b6715..e3cf13230a88a9 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java @@ -17,17 +17,16 @@ import static com.google.common.truth.TruthJUnit.assume; import static java.nio.charset.StandardCharsets.UTF_8; +import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase; import com.google.devtools.build.lib.util.OS; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.Path; - import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.List; import java.util.Map; import java.util.Set; - import org.junit.Before; import org.junit.Test; @@ -53,6 +52,8 @@ public void nestedOverlayFs() throws Exception { // This path is writable with the sandboxed runner. Path writableRoot = testRoot.getRelative("root"); writableRoot.createDirectoryAndParents(); + Path debugOutputOuter = writableRoot.getRelative("debug_output_outer"); + Path debugOutputInner = writableRoot.getRelative("debug_output_inner"); Path hermeticTmpOuter = writableRoot.getRelative("_hermetic_tmp_outer"); Path upperDirOuter = hermeticTmpOuter.getRelative("_upper"); @@ -77,6 +78,7 @@ public void nestedOverlayFs() throws Exception { .mountOverlayfsOnTmp(upperDirInner.getPathString(), workDirInner.getPathString()) .setWritableFilesAndDirectories(Set.of(writableRoot)) .setBindMounts(Map.of(tmpMount, mountDirInner)) + .setSandboxDebugPath(debugOutputInner.getPathString()) .buildForCommand(List.of("cat", tmpMount.getChild("file").getPathString())); var argsOuter = @@ -84,14 +86,29 @@ public void nestedOverlayFs() throws Exception { .mountOverlayfsOnTmp(upperDirOuter.getPathString(), workDirOuter.getPathString()) .setWritableFilesAndDirectories(Set.of(writableRoot)) .setBindMounts(Map.of(tmpMount, mountDirOuter)) + .setSandboxDebugPath(debugOutputOuter.getPathString()) .buildForCommand(argsInner); Process process = new ProcessBuilder().command(argsOuter).start(); - byte[] stderr = process.getErrorStream().readAllBytes(); - assertThat(new String(stderr, StandardCharsets.UTF_8)).isEmpty(); - byte[] stdout = process.getInputStream().readAllBytes(); - assertThat(new String(stdout, StandardCharsets.UTF_8)).isEqualTo("inner_content"); - int exitCode = process.waitFor(); - assertThat(exitCode).isEqualTo(0); + try { + byte[] stderr = process.getErrorStream().readAllBytes(); + assertThat(new String(stderr, StandardCharsets.UTF_8)).isEmpty(); + byte[] stdout = process.getInputStream().readAllBytes(); + assertThat(new String(stdout, StandardCharsets.UTF_8)).isEqualTo("inner_content"); + int exitCode = process.waitFor(); + assertThat(exitCode).isEqualTo(0); + } catch (AssertionError e) { + if (debugOutputOuter.exists()) { + System.err.printf( + "\nOuter sandbox debug logs:\n%s\n", + FileSystemUtils.readContent(debugOutputOuter, UTF_8)); + } + if (debugOutputInner.exists()) { + System.err.printf( + "\nInner sandbox debug logs:\n%s\n", + FileSystemUtils.readContent(debugOutputInner, UTF_8)); + } + throw e; + } } } From 1d6fe724ee573478f78db27cd34adf3d12d12809 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 14:07:34 +0200 Subject: [PATCH 17/32] Switch to new bind mounting scheme --- .../sandbox/LinuxSandboxedSpawnRunner.java | 109 ++++++++++++++---- .../build/lib/sandbox/SandboxHelpers.java | 24 +++- ...tractContainerizingSandboxedSpawnTest.java | 2 +- .../build/lib/sandbox/SandboxHelpersTest.java | 4 +- .../sandbox/SymlinkedSandboxedSpawnTest.java | 4 +- .../build/lib/worker/SandboxHelper.java | 2 +- .../lib/worker/WorkerSpawnRunnerTest.java | 20 ++-- .../lib/worker/WorkerSpawnStrategyTest.java | 2 +- src/test/shell/bazel/bazel_sandboxing_test.sh | 45 ++++++++ 9 files changed, 167 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 28ee4ab5ebeb01..8afa8d3a181369 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -14,13 +14,14 @@ package com.google.devtools.build.lib.sandbox; +import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NO_NETNS; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; +import com.google.common.collect.Iterables; import com.google.common.io.ByteStreams; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.ExecException; @@ -50,6 +51,7 @@ 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 com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.Symlinks; import java.io.File; import java.io.IOException; @@ -58,14 +60,14 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; +import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; import javax.annotation.Nullable; /** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { - private static final PathFragment SLASH_TMP = PathFragment.create("/tmp"); - // Since checking if sandbox is supported is expensive, we remember what we've checked. private static final Map isSupportedMap = new HashMap<>(); @@ -127,6 +129,9 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final Duration timeoutKillDelay; private final TreeDeleter treeDeleter; private final Reporter reporter; + private final FileSystem fileSystem; + private final Path slashTmp; + private final boolean tmpOnPackagePath; private String cgroupsDir; /** @@ -159,6 +164,12 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.localEnvProvider = new PosixLocalEnvProvider(cmdEnv.getClientEnv()); this.treeDeleter = treeDeleter; this.reporter = cmdEnv.getReporter(); + this.fileSystem = cmdEnv.getRuntime().getFileSystem(); + this.slashTmp = cmdEnv.getRuntime().getFileSystem().getPath("/tmp"); + this.tmpOnPackagePath = + cmdEnv.getPackageLocator().getPathEntries().stream() + .map(Root::asPath) + .anyMatch(slashTmp::equals); } private boolean useHermeticTmp() { @@ -181,7 +192,13 @@ private boolean useHermeticTmp() { return false; } - if (getSandboxOptions().sandboxTmpfsPath.contains(SLASH_TMP)) { + if (tmpOnPackagePath) { + // /tmp as a package path entry seems very unlikely to work, but the bind mounting logic is + // not prepared for it and we don't want to crash. + return false; + } + + if (getSandboxOptions().sandboxTmpfsPath.contains(slashTmp.asFragment())) { // A tmpfs path under /tmp is as hermetic as "hermetic /tmp". return false; } @@ -206,6 +223,23 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(workspaceName); sandboxExecRoot.createDirectoryAndParents(); + Path sandboxTmp = null; + if (useHermeticTmp()) { + // The base dir for the upperdir and workdir of an overlayfs on /tmp. + sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); + sandboxTmp.createDirectoryAndParents(); + + for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { + Path path = fileSystem.getPath(pathFragment); + if (path.startsWith(slashTmp)) { + // tmpfs mount points must exist, which is usually the user's responsibility. But if the + // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp + // directory. + sandboxTmp.getRelative(path.relativeTo(slashTmp)).createDirectoryAndParents(); + } + } + } + SandboxInputs inputs = helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), @@ -225,7 +259,7 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context .addExecutionInfo(spawn.getExecutionInfo()) .setWritableFilesAndDirectories(writableDirs) .setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath)) - .setBindMounts(getBindMounts(sandboxExecRoot)) + .setBindMounts(prepareAndGetBindMounts(inputs, sandboxExecRoot, sandboxTmp)) .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname) .setEnablePseudoterminal(getSandboxOptions().sandboxExplicitPseudoterminal) .setCreateNetworkNamespace(createNetworkNamespace ? NETNS_WITH_LOOPBACK : NO_NETNS) @@ -249,17 +283,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context } } - if (useHermeticTmp()) { - // The base dir for the upperdir and workdir of an overlayfs on /tmp. - Path sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); - Path sandboxTmpUpperdir = sandboxTmp.getRelative("upperdir"); - sandboxTmpUpperdir.createDirectoryAndParents(); - Path sandboxTmpWorkdir = sandboxTmp.getRelative("workdir"); - sandboxTmpWorkdir.createDirectoryAndParents(); - commandLineBuilder.mountOverlayfsOnTmp(sandboxTmpUpperdir.getPathString(), - sandboxTmpWorkdir.getPathString()); - } - if (!timeout.isZero()) { commandLineBuilder.setTimeout(timeout); } @@ -318,21 +341,61 @@ protected ImmutableSet getWritableDirs(Path sandboxExecRoot, Map getBindMounts(Path sandboxExecRoot) throws UserExecException { - final SortedMap bindMounts = Maps.newTreeMap(); + private ImmutableMap prepareAndGetBindMounts( + SandboxInputs inputs, Path sandboxExecRoot, @Nullable Path sandboxTmp) + throws UserExecException, IOException { + final SortedMap userBindMounts = new TreeMap<>(); SandboxHelpers.mountAdditionalPaths( - getSandboxOptions().sandboxAdditionalMounts, sandboxExecRoot, bindMounts); + getSandboxOptions().sandboxAdditionalMounts, sandboxExecRoot, userBindMounts); for (Path inaccessiblePath : getInaccessiblePaths()) { if (inaccessiblePath.isDirectory(Symlinks.NOFOLLOW)) { - bindMounts.put(inaccessiblePath, inaccessibleHelperDir); + userBindMounts.put(inaccessiblePath, inaccessibleHelperDir); } else { - bindMounts.put(inaccessiblePath, inaccessibleHelperFile); + userBindMounts.put(inaccessiblePath, inaccessibleHelperFile); + } + } + + LinuxSandboxUtil.validateBindMounts(userBindMounts); + + if (sandboxTmp == null) { + return ImmutableMap.copyOf(userBindMounts); + } + + // Roots under /tmp are treated exactly like a user mount under /tmp to ensure that they are + // visible at the same path after mounting the hermetic tmp. + Map rootsUnderTmp = + Stream.concat(Stream.of(execRoot), inputs.getSourceRoots().stream().map(Root::asPath)) + .filter(p -> p.startsWith(slashTmp)) + .collect(toImmutableMap(p -> p, p -> p)); + + SortedMap bindMounts = new TreeMap<>(); + Iterable> allMounts = + Iterables.concat(userBindMounts.entrySet(), rootsUnderTmp.entrySet()); + for (var entry : allMounts) { + Path mountPoint = entry.getKey(); + Path content = entry.getValue(); + if (mountPoint.startsWith(slashTmp)) { + // sandboxTmp should be null if /tmp is an explicit mount point since useHermeticTmp() + // returns false in that case. + if (mountPoint.equals(slashTmp)) { + throw new IOException( + "Cannot mount /tmp explicitly with hermetic /tmp. Please file a bug at" + + " https://github.com/bazelbuild/bazel/issues/new/choose."); + } + // We need to rewrite the mount point to be under the sandbox tmp directory, which will be + // mounted onto /tmp as the final mount. + mountPoint = sandboxTmp.getRelative(mountPoint.relativeTo(slashTmp)); + mountPoint.createDirectoryAndParents(); } + bindMounts.put(mountPoint, content); } - LinuxSandboxUtil.validateBindMounts(bindMounts); - return bindMounts; + // Mount $SANDBOX/_hermetic_tmp at /tmp as the final mount. + return ImmutableMap.builder() + .putAll(bindMounts) + .put(slashTmp, sandboxTmp) + .buildOrThrow(); } @Override diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 7c2d84a5814ef8..931d2ea58546e1 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -44,6 +44,7 @@ import com.google.devtools.build.lib.vfs.FileSystemUtils.MoveResult; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; +import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.common.options.OptionsParsingResult; import java.io.IOException; @@ -383,17 +384,21 @@ public static final class SandboxInputs { private final Map files; private final Map virtualInputs; private final Map symlinks; + private final ImmutableSet sourceRoots; private static final SandboxInputs EMPTY_INPUTS = - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); public SandboxInputs( Map files, Map virtualInputs, - Map symlinks) { + Map symlinks, + ImmutableSet sourceRoots) { this.files = files; this.virtualInputs = virtualInputs; this.symlinks = symlinks; + this.sourceRoots = sourceRoots; } public static SandboxInputs getEmptyInputs() { @@ -412,6 +417,10 @@ public ImmutableMap getVirtualInputDigests() { return ImmutableMap.copyOf(virtualInputs); } + public ImmutableSet getSourceRoots() { + return sourceRoots; + } + /** * Returns a new SandboxInputs instance with only the inputs/symlinks listed in {@code allowed} * included. @@ -420,12 +429,14 @@ public SandboxInputs limitedCopy(Set allowed) { return new SandboxInputs( Maps.filterKeys(files, allowed::contains), ImmutableMap.of(), - Maps.filterKeys(symlinks, allowed::contains)); + Maps.filterKeys(symlinks, allowed::contains), + sourceRoots); } @Override public String toString() { - return "Files: " + files + "\nVirtualInputs: " + virtualInputs + "\nSymlinks: " + symlinks; + return "Files: %s\nVirtualInputs: %s\nSymlinks: %s\nSourceRoots: %s" + .formatted(files, virtualInputs, symlinks, sourceRoots); } } @@ -442,6 +453,7 @@ public SandboxInputs processInputFiles(Map inputMap, Map inputFiles = new TreeMap<>(); Map inputSymlinks = new TreeMap<>(); Map virtualInputs = new HashMap<>(); + ImmutableSet.Builder sourceRoots = ImmutableSet.builder(); for (Map.Entry e : inputMap.entrySet()) { if (Thread.interrupted()) { @@ -452,6 +464,8 @@ public SandboxInputs processInputFiles(Map inputMap, if (actionInput instanceof VirtualActionInput input) { byte[] digest = input.atomicallyWriteRelativeTo(execRoot); virtualInputs.put(input, digest); + } else if (actionInput instanceof Artifact artifact && artifact.isSourceArtifact()) { + sourceRoots.add(artifact.getRoot().getRoot()); } if (actionInput.isSymlink()) { @@ -465,7 +479,7 @@ public SandboxInputs processInputFiles(Map inputMap, inputFiles.put(pathFragment, inputPath); } } - return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks); + return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks, sourceRoots.build()); } /** The file and directory outputs of a sandboxed spawn. */ diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java index 700189556cbf9d..f33af44da8ee22 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java @@ -383,7 +383,7 @@ private static SandboxInputs createSandboxInputs( symlinks.entrySet().stream() .collect( toImmutableMap( - e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue())))); + e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue()))), ImmutableSet.of()); } /** Return a list of all entries under the provided directory recursively. */ diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 5c8b4044bb81cb..92e55263646e74 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -239,7 +239,7 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); Set inputsToCreate = new LinkedHashSet<>(); LinkedHashSet dirsToCreate = new LinkedHashSet<>(); SandboxHelpers.populateInputsAndDirsToCreate( @@ -280,7 +280,7 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt, input4, inputTxt), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRoot); assertThat(dirsToCreate).containsExactly(inputDir2, inputDir3, outputDir); assertThat(execRoot.getRelative("existing/directory/with").exists()).isTrue(); diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java index 9c26026eeafe61..fdbccefa4d5c3d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java @@ -72,7 +72,7 @@ public void createFileSystem() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableMap.of(), - ImmutableMap.of()), + ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create( ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of()), ImmutableSet.of(execRoot.getRelative("wow/writable")), @@ -102,7 +102,7 @@ public void copyOutputs() throws Exception { execRoot, ImmutableList.of("/bin/true"), ImmutableMap.of(), - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create( ImmutableSet.of(outputFile.relativeTo(execRoot)), ImmutableSet.of()), ImmutableSet.of(), diff --git a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java index 788614b4e47256..8c6c5ab4e7e366 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java +++ b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java @@ -184,7 +184,7 @@ public SandboxHelper createSymlink(String relativePath, String relativeDestinati } public SandboxInputs getSandboxInputs() { - return new SandboxInputs(inputs, virtualInputs, symlinks); + return new SandboxInputs(inputs, virtualInputs, symlinks, ImmutableSet.of()); } public SandboxOutputs getSandboxOutputs() { diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java index 9a70b84886bf40..17cc4211170bdb 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java @@ -123,7 +123,7 @@ public void testExecInWorker_happyPath() throws ExecException, InterruptedExcept spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -215,7 +215,7 @@ public void testExecInWorker_finishesAsyncOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -258,7 +258,7 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -300,7 +300,7 @@ public void testExecInWorker_unsandboxedDiesOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -333,7 +333,7 @@ public void testExecInWorker_noMultiplexWithDynamic() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -370,7 +370,7 @@ private void assertRecordedResponsethrowsException(String recordedResponse, Stri spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -420,7 +420,7 @@ public void testExpandArgument_expandsArgumentsRecursively() PathFragment.create("file2"), fs.getPath("/file2")), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "arg2", "arg3", "multi arg", ""); @@ -435,7 +435,7 @@ public void testExpandArgument_expandsOnlyProperArguments() new SandboxInputs( ImmutableMap.of(PathFragment.create("file"), fs.getPath("/file")), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "@@nonfile", "@foo//bar", "arg2"); @@ -448,7 +448,7 @@ public void testExpandArgument_failsOnMissingFile() { new SandboxInputs( ImmutableMap.of(PathFragment.create("file"), fs.getPath("/dir/file")), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); IOException e = assertThrows( IOException.class, @@ -562,7 +562,7 @@ private WorkerSpawnRunner createWorkerSpawnRunner(WorkerOptions workerOptions) { public void testExpandArgument_failsOnUndeclaredInput() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); IOException e = assertThrows( IOException.class, diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java index 6c8ff50f2e2729..d9a225289a29d6 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java @@ -56,7 +56,7 @@ public void expandArgumentsPreservesEmptyLines() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("flagfile.txt"), path), ImmutableMap.of(), - ImmutableMap.of()); + ImmutableMap.of(), ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@flagfile.txt", requestBuilder); assertThat(requestBuilder.getArgumentsList()).containsExactlyElementsIn(flags); diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 152e840f0e1060..17e864dbc0b92b 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -352,6 +352,10 @@ EOF #include int main() { + if (mkdir("$tmpdir", 0755) < 0) { + perror("mkdir"); + return 1; + } int fd = open("$tmpdir/bazel_was_here", O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd < 0) { perror("open"); @@ -734,6 +738,47 @@ EOF //a:t || fail "build failed" } +# Regression test for https://github.com/bazelbuild/bazel/issues/21215 +function test_copy_input_symlinks() { + create_workspace_with_default_repos WORKSPACE + + cat > MODULE.bazel <<'EOF' +repo = use_repo_rule("//pkg:repo.bzl", "repo") +repo(name = "some_repo") +EOF + + mkdir -p pkg + cat > pkg/BUILD <<'EOF' +genrule( + name = "copy_files", + srcs = [ + "@some_repo//:files", + ], + outs = [ + "some_file1.json", + "some_file2.json", + ], + cmd = "cp -r $(locations @some_repo//:files) $(RULEDIR)", +) +EOF + cat > pkg/repo.bzl <<'EOF' +def _impl(rctx): + rctx.file("some_file1.json", "hello") + rctx.file("some_file2.json", "world") + rctx.file("BUILD", """filegroup( + name = "files", + srcs = ["some_file1.json", "some_file2.json"], + visibility = ["//visibility:public"], +)""") + +repo = repository_rule(_impl) +EOF + + bazel build //pkg:copy_files || fail "build failed" + assert_equals hello "$(cat bazel-bin/pkg/some_file1.json)" + assert_equals world "$(cat bazel-bin/pkg/some_file2.json)" +} + # The test shouldn't fail if the environment doesn't support running it. check_sandbox_allowed || exit 0 From 47bef066ef3119bdda4e29dd0d0449e03ed974b7 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 15:41:01 +0200 Subject: [PATCH 18/32] Working mount logic for non-execroot --- MODULE.bazel | 2 +- MODULE.bazel.lock | 34 +++++------ maven_install.json | 8 +-- .../google/devtools/build/lib/sandbox/BUILD | 1 + .../sandbox/LinuxSandboxedSpawnRunner.java | 59 +++++++++++++++++-- src/test/shell/bazel/bazel_sandboxing_test.sh | 2 +- 6 files changed, 77 insertions(+), 29 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 5963710b3da581..016716aa255b7d 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -81,7 +81,7 @@ maven.install( artifacts = [ # keep sorted "com.beust:jcommander:1.82", - "com.github.ben-manes.caffeine:caffeine:3.0.5", + "com.github.ben-manes.caffeine:caffeine:3.1.8", "com.github.kevinstern:software-and-algorithms:1.0", "com.github.stephenc.jcip:jcip-annotations:1.0-1", "com.google.api-client:google-api-client:1.35.2", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index cd9180192d7875..0cc4a2ac64a7db 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,6 +1,6 @@ { "lockFileVersion": 6, - "moduleFileHash": "ec2861042ece461ae1d704f7f8643f5dbc353e5683f6138cfedf9b63b3d63343", + "moduleFileHash": "bbf22388872e2a83387d074c5c7f8fa2e6b44048a565913d2df6f9dcd3783cde", "flags": { "cmdRegistries": [ "https://bcr.bazel.build/" @@ -55,7 +55,7 @@ "attributeValues": { "artifacts": [ "com.beust:jcommander:1.82", - "com.github.ben-manes.caffeine:caffeine:3.0.5", + "com.github.ben-manes.caffeine:caffeine:3.1.8", "com.github.kevinstern:software-and-algorithms:1.0", "com.github.stephenc.jcip:jcip-annotations:1.0-1", "com.google.api-client:google-api-client:1.35.2", @@ -2927,7 +2927,7 @@ "general": { "bzlTransitiveDigest": "VBORN+IBXeLBoYFlmaQRhi4EgJwXxJ9i+65BUjwwG18=", "recordedFileInputs": { - "@@//MODULE.bazel": "ec2861042ece461ae1d704f7f8643f5dbc353e5683f6138cfedf9b63b3d63343", + "@@//MODULE.bazel": "bbf22388872e2a83387d074c5c7f8fa2e6b44048a565913d2df6f9dcd3783cde", "@@//src/test/tools/bzlmod/MODULE.bazel.lock": "c2da7815256d6bb92f953b05334419abb9fc698d13710d129a3ca9346eb04a4e" }, "recordedDirentsInputs": {}, @@ -5395,7 +5395,7 @@ "recordedFileInputs": { "@@//src/tools/android/maven_android_install.json": "09bff3e33d291336046f7c9201630fb5e014f0e60b78b6f09b84e4f5f73ed04f", "@@rules_jvm_external~//rules_jvm_external_deps_install.json": "cafb5d2d8119391eb2b322ce3840d3352ea82d496bdb8cbd4b6779ec4d044dda", - "@@//maven_install.json": "36ad2194679bab5fd1cd8d09f256650cffeedda18e488ac9f3569aa5b5561277" + "@@//maven_install.json": "7fcbc558d781872c566d6d64192c1651e46b3f9772e532bcd662c5ae830adcea" }, "recordedDirentsInputs": {}, "envVariables": {}, @@ -5904,6 +5904,17 @@ "downloaded_file_path": "v1/com/google/protobuf/protobuf-java/3.10.0/protobuf-java-3.10.0.jar" } }, + "com_github_ben_manes_caffeine_caffeine_3_1_8": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_file", + "attributes": { + "sha256": "7dd15f9df1be238ffaa367ce6f556737a88031de4294dad18eef57c474ddf1d3", + "urls": [ + "https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/3.1.8/caffeine-3.1.8.jar" + ], + "downloaded_file_path": "v1/com/github/ben-manes/caffeine/caffeine/3.1.8/caffeine-3.1.8.jar" + } + }, "io_grpc_grpc_netty_shaded_1_56_1": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_file", @@ -5993,7 +6004,7 @@ "{ \"group\": \"com.google.truth.extensions\", \"artifact\": \"truth-proto-extension\", \"version\": \"1.4.0\", \"testonly\": true }", "{ \"group\": \"org.mockito\", \"artifact\": \"mockito-core\", \"version\": \"5.4.0\", \"testonly\": true }", "{ \"group\": \"com.beust\", \"artifact\": \"jcommander\", \"version\": \"1.82\" }", - "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.0.5\" }", + "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.1.8\" }", "{ \"group\": \"com.github.kevinstern\", \"artifact\": \"software-and-algorithms\", \"version\": \"1.0\" }", "{ \"group\": \"com.github.stephenc.jcip\", \"artifact\": \"jcip-annotations\", \"version\": \"1.0-1\" }", "{ \"group\": \"com.google.api-client\", \"artifact\": \"google-api-client\", \"version\": \"1.35.2\" }", @@ -6543,17 +6554,6 @@ "downloaded_file_path": "v1/io/netty/netty-resolver/4.1.94.Final/netty-resolver-4.1.94.Final.jar" } }, - "com_github_ben_manes_caffeine_caffeine_3_0_5": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "8a9b54d3506a3b92ee46b217bcee79196b21ca6d52dc2967c686a205fb2f9c15", - "urls": [ - "https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/3.0.5/caffeine-3.0.5.jar" - ], - "downloaded_file_path": "v1/com/github/ben-manes/caffeine/caffeine/3.0.5/caffeine-3.0.5.jar" - } - }, "org_apache_httpcomponents_httpclient_4_5_6": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_file", @@ -8631,7 +8631,7 @@ "{ \"group\": \"com.google.truth.extensions\", \"artifact\": \"truth-proto-extension\", \"version\": \"1.4.0\", \"testonly\": true }", "{ \"group\": \"org.mockito\", \"artifact\": \"mockito-core\", \"version\": \"5.4.0\", \"testonly\": true }", "{ \"group\": \"com.beust\", \"artifact\": \"jcommander\", \"version\": \"1.82\" }", - "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.0.5\" }", + "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.1.8\" }", "{ \"group\": \"com.github.kevinstern\", \"artifact\": \"software-and-algorithms\", \"version\": \"1.0\" }", "{ \"group\": \"com.github.stephenc.jcip\", \"artifact\": \"jcip-annotations\", \"version\": \"1.0-1\" }", "{ \"group\": \"com.google.api-client\", \"artifact\": \"google-api-client\", \"version\": \"1.35.2\" }", diff --git a/maven_install.json b/maven_install.json index d32877bf11a99d..8ec08994d9337a 100644 --- a/maven_install.json +++ b/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": -1536314734, - "__RESOLVED_ARTIFACTS_HASH": -1123103492, + "__INPUT_ARTIFACTS_HASH": 1028355806, + "__RESOLVED_ARTIFACTS_HASH": 1772366448, "conflict_resolution": { "com.google.auto.value:auto-value-annotations:1.9": "com.google.auto.value:auto-value-annotations:1.10.4", "com.google.code.gson:gson:2.8.9": "com.google.code.gson:gson:2.9.0", @@ -32,9 +32,9 @@ }, "com.github.ben-manes.caffeine:caffeine": { "shasums": { - "jar": "8a9b54d3506a3b92ee46b217bcee79196b21ca6d52dc2967c686a205fb2f9c15" + "jar": "7dd15f9df1be238ffaa367ce6f556737a88031de4294dad18eef57c474ddf1d3" }, - "version": "3.0.5" + "version": "3.1.8" }, "com.github.kevinstern:software-and-algorithms": { "shasums": { diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD index 97a8f6afacc47f..d0689d67d52bb8 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD @@ -224,6 +224,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/util:os", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", + "//third_party:caffeine", "//third_party:guava", "//third_party:jsr305", ], diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 8afa8d3a181369..7c5e8b71947d79 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -17,7 +17,11 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NO_NETNS; +import static java.util.stream.Collectors.joining; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -57,13 +61,15 @@ import java.io.IOException; import java.time.Duration; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.Map; +import java.util.SequencedSet; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.CompletionException; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Stream; import javax.annotation.Nullable; /** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */ @@ -132,6 +138,8 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final FileSystem fileSystem; private final Path slashTmp; private final boolean tmpOnPackagePath; + private final LoadingCache> symlinkChainCache = + Caffeine.newBuilder().build(LinuxSandboxedSpawnRunner::readSymlinkChainUncached); private String cgroupsDir; /** @@ -363,15 +371,22 @@ private ImmutableMap prepareAndGetBindMounts( } // Roots under /tmp are treated exactly like a user mount under /tmp to ensure that they are - // visible at the same path after mounting the hermetic tmp. - Map rootsUnderTmp = - Stream.concat(Stream.of(execRoot), inputs.getSourceRoots().stream().map(Root::asPath)) + // visible at the same path after mounting the hermetic tmp. Since a source root can be a + // symlink to another directory under /tmp, we need to account for all intermediate symlinks. + SequencedSet resolvedRoots = new LinkedHashSet<>(); + resolvedRoots.add(execRoot); + for (Root root : inputs.getSourceRoots()) { + resolvedRoots.add(root.asPath()); + resolvedRoots.addAll(readSymlinkChain(root)); + } + ImmutableMap resolvedRootsUnderTmp = + resolvedRoots.stream() .filter(p -> p.startsWith(slashTmp)) .collect(toImmutableMap(p -> p, p -> p)); + Iterable> allMounts = + Iterables.concat(userBindMounts.entrySet(), resolvedRootsUnderTmp.entrySet()); SortedMap bindMounts = new TreeMap<>(); - Iterable> allMounts = - Iterables.concat(userBindMounts.entrySet(), rootsUnderTmp.entrySet()); for (var entry : allMounts) { Path mountPoint = entry.getKey(); Path content = entry.getValue(); @@ -398,6 +413,38 @@ private ImmutableMap prepareAndGetBindMounts( .buildOrThrow(); } + /** Returns the chain of symlink targets obtained while resolving the path of the given root. */ + private ImmutableSet readSymlinkChain(Root root) throws IOException { + // We don't expect roots to change where they point to during a single build, so we can cache + // the symlink chains. The root path itself is not stored to reduce memory usage by having most + // roots share the empty set instance. + try { + return symlinkChainCache.get(root); + } catch (CompletionException e) { + Throwables.throwIfInstanceOf(e.getCause(), IOException.class); + throw e; + } + } + + private static ImmutableSet readSymlinkChainUncached(Root root) throws IOException { + Path path = root.asPath(); + SequencedSet result = new LinkedHashSet<>(); + while (true) { + try { + path = path.getParentDirectory().getRelative(path.readSymbolicLink()); + } catch (FileSystem.NotASymlinkException e) { + break; + } + if (!result.add(path)) { + throw new IOException( + String.format( + "Cycle in symlink chain for root %s: %s", + root, result.stream().map(Path::toString).collect(joining(" -> ")))); + } + } + return ImmutableSet.copyOf(result); + } + @Override public void verifyPostCondition( Spawn originalSpawn, SandboxedSpawn sandbox, SpawnExecutionContext context) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 17e864dbc0b92b..c5a3d21f15acd1 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -730,8 +730,8 @@ EOF touch "a/s" "../package-path/b/s" "../repo/c/s" cat WORKSPACE + # --output_base="${temp_dir}/output-base" \ bazel \ - --output_base="${temp_dir}/output-base" \ build \ --incompatible_sandbox_hermetic_tmp \ --package_path="%workspace%:${temp_dir}/package-path" \ From 47808a2dd6056be8acede00e930718a451dc9558 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 16:20:58 +0200 Subject: [PATCH 19/32] Fix output base under tmp case --- .../build/lib/sandbox/LinuxSandboxedSpawnRunner.java | 8 ++++++-- src/test/shell/bazel/bazel_sandboxing_test.sh | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 7c5e8b71947d79..88437006e607e6 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -138,6 +138,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final FileSystem fileSystem; private final Path slashTmp; private final boolean tmpOnPackagePath; + private final Path outputBase; private final LoadingCache> symlinkChainCache = Caffeine.newBuilder().build(LinuxSandboxedSpawnRunner::readSymlinkChainUncached); private String cgroupsDir; @@ -178,6 +179,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS cmdEnv.getPackageLocator().getPathEntries().stream() .map(Root::asPath) .anyMatch(slashTmp::equals); + this.outputBase = cmdEnv.getOutputBase(); } private boolean useHermeticTmp() { @@ -374,9 +376,11 @@ private ImmutableMap prepareAndGetBindMounts( // visible at the same path after mounting the hermetic tmp. Since a source root can be a // symlink to another directory under /tmp, we need to account for all intermediate symlinks. SequencedSet resolvedRoots = new LinkedHashSet<>(); - resolvedRoots.add(execRoot); + resolvedRoots.add(outputBase); for (Root root : inputs.getSourceRoots()) { - resolvedRoots.add(root.asPath()); + if (!root.asPath().startsWith(outputBase)) { + resolvedRoots.add(root.asPath()); + } resolvedRoots.addAll(readSymlinkChain(root)); } ImmutableMap resolvedRootsUnderTmp = diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index c5a3d21f15acd1..17e864dbc0b92b 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -730,8 +730,8 @@ EOF touch "a/s" "../package-path/b/s" "../repo/c/s" cat WORKSPACE - # --output_base="${temp_dir}/output-base" \ bazel \ + --output_base="${temp_dir}/output-base" \ build \ --incompatible_sandbox_hermetic_tmp \ --package_path="%workspace%:${temp_dir}/package-path" \ From b1cf872217771acd0bff3884561c988ed73ba00a Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 16:25:44 +0200 Subject: [PATCH 20/32] Add comment --- src/test/shell/bazel/bazel_sandboxing_test.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 17e864dbc0b92b..73906f943adb84 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -317,6 +317,13 @@ EOF bazel build //pkg:a &>$TEST_log || fail "expected build to succeed" } +# Sets up targets under //test that, when building //test:all, verify that the +# sandbox setup ensures that /tmp contents written by one action are not visible +# to another action. +# +# Arguments: +# - The path to a unique temporary directory under /tmp that a +# file named "bazel_was_here" is written to in actions. function setup_tmp_hermeticity_check() { local -r tmpdir=$1 From 0a7c2174d96bb4e47a9be240e63ab36a7a7dc563 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 16:27:38 +0200 Subject: [PATCH 21/32] Add import --- .../devtools/build/lib/worker/WorkerSpawnStrategyTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java index d9a225289a29d6..aa77288c5f82fc 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; @@ -56,7 +57,8 @@ public void expandArgumentsPreservesEmptyLines() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("flagfile.txt"), path), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableMap.of(), + ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@flagfile.txt", requestBuilder); assertThat(requestBuilder.getArgumentsList()).containsExactlyElementsIn(flags); From 2410782b891318b8362de3a3d6bc08e6d3640cce Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 16:33:43 +0200 Subject: [PATCH 22/32] Revert caffeine update --- MODULE.bazel | 2 +- MODULE.bazel.lock | 34 +++++++++++++++++----------------- maven_install.json | 8 ++++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 016716aa255b7d..5963710b3da581 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -81,7 +81,7 @@ maven.install( artifacts = [ # keep sorted "com.beust:jcommander:1.82", - "com.github.ben-manes.caffeine:caffeine:3.1.8", + "com.github.ben-manes.caffeine:caffeine:3.0.5", "com.github.kevinstern:software-and-algorithms:1.0", "com.github.stephenc.jcip:jcip-annotations:1.0-1", "com.google.api-client:google-api-client:1.35.2", diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 0cc4a2ac64a7db..cd9180192d7875 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1,6 +1,6 @@ { "lockFileVersion": 6, - "moduleFileHash": "bbf22388872e2a83387d074c5c7f8fa2e6b44048a565913d2df6f9dcd3783cde", + "moduleFileHash": "ec2861042ece461ae1d704f7f8643f5dbc353e5683f6138cfedf9b63b3d63343", "flags": { "cmdRegistries": [ "https://bcr.bazel.build/" @@ -55,7 +55,7 @@ "attributeValues": { "artifacts": [ "com.beust:jcommander:1.82", - "com.github.ben-manes.caffeine:caffeine:3.1.8", + "com.github.ben-manes.caffeine:caffeine:3.0.5", "com.github.kevinstern:software-and-algorithms:1.0", "com.github.stephenc.jcip:jcip-annotations:1.0-1", "com.google.api-client:google-api-client:1.35.2", @@ -2927,7 +2927,7 @@ "general": { "bzlTransitiveDigest": "VBORN+IBXeLBoYFlmaQRhi4EgJwXxJ9i+65BUjwwG18=", "recordedFileInputs": { - "@@//MODULE.bazel": "bbf22388872e2a83387d074c5c7f8fa2e6b44048a565913d2df6f9dcd3783cde", + "@@//MODULE.bazel": "ec2861042ece461ae1d704f7f8643f5dbc353e5683f6138cfedf9b63b3d63343", "@@//src/test/tools/bzlmod/MODULE.bazel.lock": "c2da7815256d6bb92f953b05334419abb9fc698d13710d129a3ca9346eb04a4e" }, "recordedDirentsInputs": {}, @@ -5395,7 +5395,7 @@ "recordedFileInputs": { "@@//src/tools/android/maven_android_install.json": "09bff3e33d291336046f7c9201630fb5e014f0e60b78b6f09b84e4f5f73ed04f", "@@rules_jvm_external~//rules_jvm_external_deps_install.json": "cafb5d2d8119391eb2b322ce3840d3352ea82d496bdb8cbd4b6779ec4d044dda", - "@@//maven_install.json": "7fcbc558d781872c566d6d64192c1651e46b3f9772e532bcd662c5ae830adcea" + "@@//maven_install.json": "36ad2194679bab5fd1cd8d09f256650cffeedda18e488ac9f3569aa5b5561277" }, "recordedDirentsInputs": {}, "envVariables": {}, @@ -5904,17 +5904,6 @@ "downloaded_file_path": "v1/com/google/protobuf/protobuf-java/3.10.0/protobuf-java-3.10.0.jar" } }, - "com_github_ben_manes_caffeine_caffeine_3_1_8": { - "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", - "ruleClassName": "http_file", - "attributes": { - "sha256": "7dd15f9df1be238ffaa367ce6f556737a88031de4294dad18eef57c474ddf1d3", - "urls": [ - "https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/3.1.8/caffeine-3.1.8.jar" - ], - "downloaded_file_path": "v1/com/github/ben-manes/caffeine/caffeine/3.1.8/caffeine-3.1.8.jar" - } - }, "io_grpc_grpc_netty_shaded_1_56_1": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_file", @@ -6004,7 +5993,7 @@ "{ \"group\": \"com.google.truth.extensions\", \"artifact\": \"truth-proto-extension\", \"version\": \"1.4.0\", \"testonly\": true }", "{ \"group\": \"org.mockito\", \"artifact\": \"mockito-core\", \"version\": \"5.4.0\", \"testonly\": true }", "{ \"group\": \"com.beust\", \"artifact\": \"jcommander\", \"version\": \"1.82\" }", - "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.1.8\" }", + "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.0.5\" }", "{ \"group\": \"com.github.kevinstern\", \"artifact\": \"software-and-algorithms\", \"version\": \"1.0\" }", "{ \"group\": \"com.github.stephenc.jcip\", \"artifact\": \"jcip-annotations\", \"version\": \"1.0-1\" }", "{ \"group\": \"com.google.api-client\", \"artifact\": \"google-api-client\", \"version\": \"1.35.2\" }", @@ -6554,6 +6543,17 @@ "downloaded_file_path": "v1/io/netty/netty-resolver/4.1.94.Final/netty-resolver-4.1.94.Final.jar" } }, + "com_github_ben_manes_caffeine_caffeine_3_0_5": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_file", + "attributes": { + "sha256": "8a9b54d3506a3b92ee46b217bcee79196b21ca6d52dc2967c686a205fb2f9c15", + "urls": [ + "https://repo1.maven.org/maven2/com/github/ben-manes/caffeine/caffeine/3.0.5/caffeine-3.0.5.jar" + ], + "downloaded_file_path": "v1/com/github/ben-manes/caffeine/caffeine/3.0.5/caffeine-3.0.5.jar" + } + }, "org_apache_httpcomponents_httpclient_4_5_6": { "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", "ruleClassName": "http_file", @@ -8631,7 +8631,7 @@ "{ \"group\": \"com.google.truth.extensions\", \"artifact\": \"truth-proto-extension\", \"version\": \"1.4.0\", \"testonly\": true }", "{ \"group\": \"org.mockito\", \"artifact\": \"mockito-core\", \"version\": \"5.4.0\", \"testonly\": true }", "{ \"group\": \"com.beust\", \"artifact\": \"jcommander\", \"version\": \"1.82\" }", - "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.1.8\" }", + "{ \"group\": \"com.github.ben-manes.caffeine\", \"artifact\": \"caffeine\", \"version\": \"3.0.5\" }", "{ \"group\": \"com.github.kevinstern\", \"artifact\": \"software-and-algorithms\", \"version\": \"1.0\" }", "{ \"group\": \"com.github.stephenc.jcip\", \"artifact\": \"jcip-annotations\", \"version\": \"1.0-1\" }", "{ \"group\": \"com.google.api-client\", \"artifact\": \"google-api-client\", \"version\": \"1.35.2\" }", diff --git a/maven_install.json b/maven_install.json index 8ec08994d9337a..d32877bf11a99d 100644 --- a/maven_install.json +++ b/maven_install.json @@ -1,7 +1,7 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 1028355806, - "__RESOLVED_ARTIFACTS_HASH": 1772366448, + "__INPUT_ARTIFACTS_HASH": -1536314734, + "__RESOLVED_ARTIFACTS_HASH": -1123103492, "conflict_resolution": { "com.google.auto.value:auto-value-annotations:1.9": "com.google.auto.value:auto-value-annotations:1.10.4", "com.google.code.gson:gson:2.8.9": "com.google.code.gson:gson:2.9.0", @@ -32,9 +32,9 @@ }, "com.github.ben-manes.caffeine:caffeine": { "shasums": { - "jar": "7dd15f9df1be238ffaa367ce6f556737a88031de4294dad18eef57c474ddf1d3" + "jar": "8a9b54d3506a3b92ee46b217bcee79196b21ca6d52dc2967c686a205fb2f9c15" }, - "version": "3.1.8" + "version": "3.0.5" }, "com.github.kevinstern:software-and-algorithms": { "shasums": { From c2f876853bf0776e051c38e4aa68ab95d64cba65 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 17:43:42 +0200 Subject: [PATCH 23/32] Fix more tests and add back RootedPath --- .../AbstractContainerizingSandboxedSpawn.java | 2 +- .../LinuxSandboxCommandLineBuilder.java | 12 -- .../sandbox/LinuxSandboxedSpawnRunner.java | 1 - .../build/lib/sandbox/SandboxHelpers.java | 44 ++++--- .../build/lib/sandbox/WindowsSandboxUtil.java | 9 +- .../build/lib/worker/WorkerExecRoot.java | 6 +- .../build/lib/worker/WorkerSpawnRunner.java | 7 +- src/main/tools/linux-sandbox-options.cc | 10 +- src/main/tools/linux-sandbox-options.h | 4 - src/main/tools/linux-sandbox-pid1.cc | 11 -- ...tractContainerizingSandboxedSpawnTest.java | 8 +- .../google/devtools/build/lib/sandbox/BUILD | 26 ---- .../sandbox/LinuxSandboxOverlayfsTest.java | 114 ------------------ .../LinuxSandboxedSpawnRunnerTest.java | 32 +---- .../build/lib/sandbox/SandboxHelpersTest.java | 6 +- .../sandbox/SymlinkedSandboxedSpawnTest.java | 13 +- .../build/lib/worker/SandboxHelper.java | 25 ++-- .../lib/worker/WorkerSpawnRunnerTest.java | 44 ++++--- .../lib/worker/WorkerSpawnStrategyTest.java | 12 +- src/test/shell/integration/sandboxing_test.sh | 2 +- 20 files changed, 114 insertions(+), 274 deletions(-) delete mode 100644 src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java index bfc280c42d8b73..e66b65d2db2179 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java @@ -162,7 +162,7 @@ void createInputs(Iterable inputsToCreate, SandboxInputs inputs) } Path key = sandboxExecRoot.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - Path fileDest = inputs.getFiles().get(fragment); + Path fileDest = inputs.getFiles().get(fragment).asPath(); if (fileDest != null) { copyFile(fileDest, key); } else { diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java index 12141217824da4..ac9d0ddcd9f688 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java @@ -56,8 +56,6 @@ public class LinuxSandboxCommandLineBuilder { private String sandboxDebugPath = null; private boolean sigintSendsSigterm = false; private String cgroupsDir; - private String overlayfsUpperdir = null; - private String overlayfsWorkdir = null; private LinuxSandboxCommandLineBuilder(Path linuxSandboxPath) { this.linuxSandboxPath = linuxSandboxPath; @@ -222,13 +220,6 @@ public LinuxSandboxCommandLineBuilder setCgroupsDir(String cgroupsDir) { return this; } - @CanIgnoreReturnValue - public LinuxSandboxCommandLineBuilder mountOverlayfsOnTmp(String upperdir, String workdir) { - this.overlayfsUpperdir = upperdir; - this.overlayfsWorkdir = workdir; - return this; - } - /** Incorporates settings from a spawn's execution info. */ @CanIgnoreReturnValue public LinuxSandboxCommandLineBuilder addExecutionInfo(Map executionInfo) { @@ -311,9 +302,6 @@ public ImmutableList buildForCommand(List commandArguments) { if (cgroupsDir != null) { commandLineBuilder.add("-C", cgroupsDir); } - if (overlayfsUpperdir != null && overlayfsWorkdir != null) { - commandLineBuilder.add("-F", overlayfsUpperdir, "-f", overlayfsWorkdir); - } commandLineBuilder.add("--"); commandLineBuilder.addAll(commandArguments); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 88437006e607e6..73b21e48c7c162 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -254,7 +254,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context helpers.processInputFiles( context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), execRoot); - SandboxOutputs outputs = helpers.getOutputs(spawn); ImmutableMap environment = localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 931d2ea58546e1..69ca623aed23f2 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -21,10 +21,12 @@ import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; +import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.google.common.flogger.GoogleLogger; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.Artifact; @@ -45,6 +47,7 @@ import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.common.options.OptionsParsingResult; import java.io.IOException; @@ -243,9 +246,9 @@ private static void cleanRecursively( */ static Optional getExpectedSymlinkDestination( PathFragment fragment, SandboxInputs inputs) { - Path file = inputs.getFiles().get(fragment); + RootedPath file = inputs.getFiles().get(fragment); if (file != null) { - return Optional.of(file.asFragment()); + return Optional.of(file.asPath().asFragment()); } return Optional.ofNullable(inputs.getSymlinks().get(fragment)); } @@ -381,7 +384,7 @@ public static void mountAdditionalPaths( /** Wrapper class for the inputs of a sandbox. */ public static final class SandboxInputs { - private final Map files; + private final Map files; private final Map virtualInputs; private final Map symlinks; private final ImmutableSet sourceRoots; @@ -391,21 +394,21 @@ public static final class SandboxInputs { ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); public SandboxInputs( - Map files, + Map files, Map virtualInputs, Map symlinks, - ImmutableSet sourceRoots) { + Set sourceRoots) { this.files = files; this.virtualInputs = virtualInputs; this.symlinks = symlinks; - this.sourceRoots = sourceRoots; + this.sourceRoots = ImmutableSet.copyOf(sourceRoots); } public static SandboxInputs getEmptyInputs() { return EMPTY_INPUTS; } - public Map getFiles() { + public Map getFiles() { return files; } @@ -413,24 +416,27 @@ public Map getSymlinks() { return symlinks; } - public ImmutableMap getVirtualInputDigests() { - return ImmutableMap.copyOf(virtualInputs); - } - public ImmutableSet getSourceRoots() { return sourceRoots; } + public ImmutableMap getVirtualInputDigests() { + return ImmutableMap.copyOf(virtualInputs); + } + /** * Returns a new SandboxInputs instance with only the inputs/symlinks listed in {@code allowed} * included. */ public SandboxInputs limitedCopy(Set allowed) { + var limitedFiles = Maps.filterKeys(files, allowed::contains); + var limitedFilesRoots = + new HashSet<>(Collections2.transform(limitedFiles.values(), RootedPath::getRoot)); return new SandboxInputs( - Maps.filterKeys(files, allowed::contains), + limitedFiles, ImmutableMap.of(), Maps.filterKeys(symlinks, allowed::contains), - sourceRoots); + Sets.intersection(sourceRoots, limitedFilesRoots)); } @Override @@ -450,7 +456,7 @@ public String toString() { */ public SandboxInputs processInputFiles(Map inputMap, Path execRoot) throws IOException, InterruptedException { - Map inputFiles = new TreeMap<>(); + Map inputFiles = new TreeMap<>(); Map inputSymlinks = new TreeMap<>(); Map virtualInputs = new HashMap<>(); ImmutableSet.Builder sourceRoots = ImmutableSet.builder(); @@ -471,12 +477,12 @@ public SandboxInputs processInputFiles(Map inputMap, if (actionInput.isSymlink()) { Path inputPath = execRoot.getRelative(actionInput.getExecPath()); inputSymlinks.put(pathFragment, inputPath.readSymbolicLink()); + } else if (actionInput instanceof EmptyActionInput) { + inputFiles.put(pathFragment, null); } else { - Path inputPath = - actionInput instanceof EmptyActionInput - ? null - : execRoot.getRelative(actionInput.getExecPath()); - inputFiles.put(pathFragment, inputPath); + inputFiles.put( + pathFragment, + RootedPath.toRootedPath(Root.fromPath(execRoot), actionInput.getExecPath())); } } return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks, sourceRoots.build()); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java index c582ecb3df3da5..707f541e98f7e8 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java @@ -24,6 +24,7 @@ import com.google.devtools.build.lib.shell.SubprocessBuilder.StreamAction; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayOutputStream; import java.io.File; @@ -106,7 +107,7 @@ public static class CommandLineBuilder { private Path stdoutPath; private Path stderrPath; private Set writableFilesAndDirectories = ImmutableSet.of(); - private Map readableFilesAndDirectories = new TreeMap<>(); + private Map readableFilesAndDirectories = new TreeMap<>(); private Set inaccessiblePaths = ImmutableSet.of(); private boolean useDebugMode = false; private List commandArguments = ImmutableList.of(); @@ -165,7 +166,7 @@ public CommandLineBuilder setWritableFilesAndDirectories( /** Sets the files or directories to make readable for the sandboxed process, if any. */ @CanIgnoreReturnValue public CommandLineBuilder setReadableFilesAndDirectories( - Map readableFilesAndDirectories) { + Map readableFilesAndDirectories) { this.readableFilesAndDirectories = readableFilesAndDirectories; return this; } @@ -212,8 +213,8 @@ public ImmutableList build() { for (Path writablePath : writableFilesAndDirectories) { commandLineBuilder.add("-w", writablePath.getPathString()); } - for (Path readablePath : readableFilesAndDirectories.values()) { - commandLineBuilder.add("-r", readablePath.getPathString()); + for (RootedPath readablePath : readableFilesAndDirectories.values()) { + commandLineBuilder.add("-r", readablePath.asPath().getPathString()); } for (Path writablePath : inaccessiblePaths) { commandLineBuilder.add("-b", writablePath.getPathString()); diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java index f45f4f47eb26c6..1cf31e8063d6b5 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java @@ -22,6 +22,8 @@ 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.RootedPath; + import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; @@ -86,9 +88,9 @@ static void createInputs(Iterable inputsToCreate, SandboxInputs in } Path key = dir.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - Path fileDest = inputs.getFiles().get(fragment); + RootedPath fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - key.createSymbolicLink(fileDest); + key.createSymbolicLink(fileDest.asPath()); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index 265e2092a5a569..cdf31cea836b3e 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -312,20 +312,21 @@ static void expandArgument(SandboxInputs inputs, String arg, WorkRequest.Builder throw new InterruptedException(); } String argValue = arg.substring(1); - Path path = inputs.getFiles().get(PathFragment.create(argValue)); + RootedPath path = inputs.getFiles().get(PathFragment.create(argValue)); if (path == null) { throw new IOException( String.format( "Failed to read @-argument '%s': file is not a declared input", argValue)); } try { - for (String line : FileSystemUtils.readLines(path, UTF_8)) { + for (String line : FileSystemUtils.readLines(path.asPath(), UTF_8)) { expandArgument(inputs, line, requestBuilder); } } catch (IOException e) { throw new IOException( String.format( - "Failed to read @-argument '%s' from file '%s'.", argValue, path.getPathString()), + "Failed to read @-argument '%s' from file '%s'.", + argValue, path.asPath().getPathString()), e); } } else { diff --git a/src/main/tools/linux-sandbox-options.cc b/src/main/tools/linux-sandbox-options.cc index 17de2a3ca16aa8..c2c57b38076086 100644 --- a/src/main/tools/linux-sandbox-options.cc +++ b/src/main/tools/linux-sandbox-options.cc @@ -102,7 +102,7 @@ static void ParseCommandLine(unique_ptr> args) { int c; bool source_specified = false; while ((c = getopt(args->size(), args->data(), - ":W:T:t:il:L:w:e:M:m:S:h:pC:HnNRUPD:F:f:")) != -1) { + ":W:T:t:il:L:w:e:M:m:S:h:pC:HnNRUPD:")) != -1) { if (c != 'M' && c != 'm') source_specified = false; switch (c) { case 'W': @@ -248,14 +248,6 @@ static void ParseCommandLine(unique_ptr> args) { "Cannot write debug output to more than one file."); } break; - case 'F': - ValidateIsAbsolutePath(optarg, args->front(), static_cast(c)); - opt.overlayfs_upperdir.assign(optarg); - break; - case 'f': - ValidateIsAbsolutePath(optarg, args->front(), static_cast(c)); - opt.overlayfs_workdir.assign(optarg); - break; case '?': Usage(args->front(), "Unrecognized argument: -%c (%d)", optopt, optind); break; diff --git a/src/main/tools/linux-sandbox-options.h b/src/main/tools/linux-sandbox-options.h index 97cbd5b33181e7..a4ed85d41cc381 100644 --- a/src/main/tools/linux-sandbox-options.h +++ b/src/main/tools/linux-sandbox-options.h @@ -68,10 +68,6 @@ struct Options { std::string sandbox_root; // Directory to use for cgroup control std::string cgroups_dir; - // The upperdir for the /tmp overlayfs (-F) - std::string overlayfs_upperdir; - // The workdir for the /tmp overlayfs (-f) - std::string overlayfs_workdir; // Command to run (--) std::vector args; }; diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc index 4ae0d35211052d..7c7a2f54014200 100644 --- a/src/main/tools/linux-sandbox-pid1.cc +++ b/src/main/tools/linux-sandbox-pid1.cc @@ -18,7 +18,6 @@ */ #include "src/main/tools/linux-sandbox-pid1.h" -#include "src/main/tools/linux-sandbox-options.h" #include #include @@ -256,16 +255,6 @@ static void MountFilesystems() { } } - if (!opt.overlayfs_upperdir.empty() && !opt.overlayfs_workdir.empty()) { - const std::string overlayfs_opts = - "lowerdir=/tmp,upperdir=" + opt.overlayfs_upperdir + ",workdir=" - + opt.overlayfs_workdir; - PRINT_DEBUG("overlayfs mount: %s", overlayfs_opts.c_str()); - if (mount("overlay", "/tmp", "overlay", 0, overlayfs_opts.c_str()) < 0) { - DIE("mount(overlay, /tmp, overlay, 0, %s)", overlayfs_opts.c_str()); - } - } - std::unordered_set bind_mount_sources; for (size_t i = 0; i < opt.bind_mount_sources.size(); i++) { diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java index f33af44da8ee22..c68453d8036d89 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java @@ -35,6 +35,7 @@ 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.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -373,17 +374,18 @@ private static SandboxInputs createSandboxInputs( private static SandboxInputs createSandboxInputs( ImmutableList files, ImmutableMap symlinks) { - Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); + Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); for (String file : files) { filesMap.put(PathFragment.create(file), null); } return new SandboxInputs( filesMap, - /*virtualInputs=*/ ImmutableMap.of(), + /* virtualInputs= */ ImmutableMap.of(), symlinks.entrySet().stream() .collect( toImmutableMap( - e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue()))), ImmutableSet.of()); + e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue()))), + ImmutableSet.of()); } /** Return a list of all entries under the provided directory recursively. */ diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD index 82a30425865694..fea187dd0d4e5d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/test/java/com/google/devtools/build/lib/sandbox/BUILD @@ -145,29 +145,3 @@ java_test( "//third_party:truth", ], ) - -java_test( - name = "LinuxSandboxOverlayfsTest", - size = "small", - srcs = ["LinuxSandboxOverlayfsTest.java"], - tags = [ - # Requires overlayfs support in the kernel - "no_macos", - "no_windows", - # Kernels have a hard limit on the number of overlayfs mounts layers - # which can be as low as 2, so running this in a sandbox with - # overlayfs may fail. - "no-sandbox", - ], - deps = [ - ":testutil", - "//src/main/java/com/google/devtools/build/lib/sandbox:linux_sandbox_command_line_builder", - "//src/main/java/com/google/devtools/build/lib/util:os", - "//src/main/java/com/google/devtools/build/lib/vfs", - "//src/test/java/com/google/devtools/build/lib/buildtool/util", - "//src/test/java/com/google/devtools/build/lib/vfs/util", - "//third_party:guava", - "//third_party:junit4", - "//third_party:truth", - ], -) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java deleted file mode 100644 index e3cf13230a88a9..00000000000000 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxOverlayfsTest.java +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2022 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 static com.google.common.truth.TruthJUnit.assume; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.google.common.collect.ImmutableSet; -import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase; -import com.google.devtools.build.lib.util.OS; -import com.google.devtools.build.lib.vfs.FileSystemUtils; -import com.google.devtools.build.lib.vfs.Path; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.Before; -import org.junit.Test; - -/** Tests for linux-sandbox overlayfs functionality. */ -public final class LinuxSandboxOverlayfsTest extends BuildIntegrationTestCase { - - @Before - public void assumeRunningOnLinux() { - assume().that(OS.getCurrent()).isEqualTo(OS.LINUX); - } - - @Test - public void nestedOverlayFs() throws Exception { - var tmpMountNio = - Files.createTempDirectory(java.nio.file.Path.of("/tmp"), "bazel-sandbox-test"); - tmpMountNio.toFile().deleteOnExit(); - var tmpMount = fileSystem.getPath(tmpMountNio.toAbsolutePath().toString()); - - Path binDir = testRoot.getRelative("_bin"); - binDir.createDirectoryAndParents(); - Path linuxSandboxPath = SpawnRunnerTestUtil.copyLinuxSandboxIntoPath(binDir); - - // This path is writable with the sandboxed runner. - Path writableRoot = testRoot.getRelative("root"); - writableRoot.createDirectoryAndParents(); - Path debugOutputOuter = writableRoot.getRelative("debug_output_outer"); - Path debugOutputInner = writableRoot.getRelative("debug_output_inner"); - - Path hermeticTmpOuter = writableRoot.getRelative("_hermetic_tmp_outer"); - Path upperDirOuter = hermeticTmpOuter.getRelative("_upper"); - upperDirOuter.createDirectoryAndParents(); - Path workDirOuter = hermeticTmpOuter.getRelative("_work"); - workDirOuter.createDirectoryAndParents(); - Path mountDirOuter = hermeticTmpOuter.getRelative("_mount"); - mountDirOuter.createDirectoryAndParents(); - FileSystemUtils.writeContent(mountDirOuter.getChild("file"), UTF_8, "outer_content"); - - Path hermeticTmpInner = writableRoot.getRelative("_hermetic_tmp_inner"); - Path upperDirInner = hermeticTmpInner.getRelative("_upper"); - upperDirInner.createDirectoryAndParents(); - Path workDirInner = hermeticTmpInner.getRelative("_work"); - workDirInner.createDirectoryAndParents(); - Path mountDirInner = hermeticTmpInner.getRelative("_mount"); - mountDirInner.createDirectoryAndParents(); - FileSystemUtils.writeContent(mountDirInner.getChild("file"), UTF_8, "inner_content"); - - var argsInner = - LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandboxPath) - .mountOverlayfsOnTmp(upperDirInner.getPathString(), workDirInner.getPathString()) - .setWritableFilesAndDirectories(Set.of(writableRoot)) - .setBindMounts(Map.of(tmpMount, mountDirInner)) - .setSandboxDebugPath(debugOutputInner.getPathString()) - .buildForCommand(List.of("cat", tmpMount.getChild("file").getPathString())); - - var argsOuter = - LinuxSandboxCommandLineBuilder.commandLineBuilder(linuxSandboxPath) - .mountOverlayfsOnTmp(upperDirOuter.getPathString(), workDirOuter.getPathString()) - .setWritableFilesAndDirectories(Set.of(writableRoot)) - .setBindMounts(Map.of(tmpMount, mountDirOuter)) - .setSandboxDebugPath(debugOutputOuter.getPathString()) - .buildForCommand(argsInner); - - Process process = new ProcessBuilder().command(argsOuter).start(); - try { - byte[] stderr = process.getErrorStream().readAllBytes(); - assertThat(new String(stderr, StandardCharsets.UTF_8)).isEmpty(); - byte[] stdout = process.getInputStream().readAllBytes(); - assertThat(new String(stdout, StandardCharsets.UTF_8)).isEqualTo("inner_content"); - int exitCode = process.waitFor(); - assertThat(exitCode).isEqualTo(0); - } catch (AssertionError e) { - if (debugOutputOuter.exists()) { - System.err.printf( - "\nOuter sandbox debug logs:\n%s\n", - FileSystemUtils.readContent(debugOutputOuter, UTF_8)); - } - if (debugOutputInner.exists()) { - System.err.printf( - "\nInner sandbox debug logs:\n%s\n", - FileSystemUtils.readContent(debugOutputInner, UTF_8)); - } - throw e; - } - } -} diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java index 707cd60e16466c..1dfbf40572d4d0 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java @@ -42,7 +42,6 @@ import com.google.testing.junit.testparameterinjector.TestParameterInjector; import java.io.IOException; import java.time.Duration; -import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -181,10 +180,7 @@ public void hermeticTmp_tmpCreatedAndMounted() throws Exception { assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class); String args = String.join(" ", sandboxedSpawn.getArguments()); assertThat(args).contains("-w /tmp"); - assertThat(args) - .matches( - ".* -F %1$s/[^ ]+ -f %1$s/[^ ]+ .*" - .formatted(Pattern.quote(hermeticTmpPath.getPathString()))); + assertThat(args).contains("-M " + hermeticTmpPath + " -m /tmp"); } @Test @@ -204,31 +200,7 @@ public void hermeticTmp_sandboxTmpfsOnTmp_tmpNotCreatedOrMounted() throws Except String args = String.join(" ", sandboxedSpawn.getArguments()); assertThat(args).contains("-w /tmp"); assertThat(args).contains("-e /tmp"); - assertThat(args).doesNotContain("-F "); - assertThat(args).doesNotContain("-f "); - } - - @Test - public void hermeticTmp_sandboxTmpfsUnderTmp_tmpCreatedAndMounted() throws Exception { - runtimeWrapper.addOptions( - "--incompatible_sandbox_hermetic_tmp", "--sandbox_tmpfs_path=/tmp/subdir"); - CommandEnvironment commandEnvironment = createCommandEnvironment(); - LinuxSandboxedSpawnRunner runner = setupSandboxAndCreateRunner(commandEnvironment); - Spawn spawn = new SpawnBuilder().build(); - SandboxedSpawn sandboxedSpawn = runner.prepareSpawn(spawn, createSpawnExecutionContext(spawn)); - - Path sandboxPath = - sandboxedSpawn.getSandboxExecRoot().getParentDirectory().getParentDirectory(); - Path hermeticTmpPath = sandboxPath.getRelative("_hermetic_tmp"); - assertThat(hermeticTmpPath.isDirectory()).isTrue(); - - assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class); - String args = String.join(" ", sandboxedSpawn.getArguments()); - assertThat(args).contains("-w /tmp"); - assertThat(args) - .matches( - ".* -F %1$s/[^ ]+ -f %1$s/[^ ]+ .*" - .formatted(Pattern.quote(hermeticTmpPath.getPathString()))); + assertThat(args).doesNotContain("-m /tmp"); } private static LinuxSandboxedSpawnRunner setupSandboxAndCreateRunner( diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 92e55263646e74..58f66a3ed46c00 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -43,6 +43,8 @@ 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.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -230,7 +232,9 @@ public void atomicallyWriteVirtualInput_writesArbitraryVirtualInput() throws Exc @Test public void cleanExisting_updatesDirs() throws IOException, InterruptedException { - Path inputTxt = scratch.getFileSystem().getPath(PathFragment.create("/hello.txt")); + RootedPath inputTxt = + RootedPath.toRootedPath( + Root.fromPath(scratch.getFileSystem().getPath("/")), PathFragment.create("hello.txt")); Path rootDir = execRoot.getParentDirectory(); PathFragment input1 = PathFragment.create("existing/directory/with/input1.txt"); PathFragment input2 = PathFragment.create("partial/directory/input2.txt"); diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java index fdbccefa4d5c3d..360b3770ccbc3f 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java @@ -26,6 +26,8 @@ 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.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -60,8 +62,9 @@ public final void setupTestDirs() throws IOException { @Test public void createFileSystem() throws Exception { - Path helloTxt = workspaceDir.getRelative("hello.txt"); - FileSystemUtils.createEmptyFile(helloTxt); + RootedPath helloTxt = + RootedPath.toRootedPath(Root.fromPath(workspaceDir), PathFragment.create("hello.txt")); + FileSystemUtils.createEmptyFile(helloTxt.asPath()); SymlinkedSandboxedSpawn symlinkedExecRoot = new SymlinkedSandboxedSpawn( @@ -72,7 +75,8 @@ public void createFileSystem() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()), + ImmutableMap.of(), + ImmutableSet.of()), SandboxOutputs.create( ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of()), ImmutableSet.of(execRoot.getRelative("wow/writable")), @@ -102,7 +106,8 @@ public void copyOutputs() throws Exception { execRoot, ImmutableList.of("/bin/true"), ImmutableMap.of(), - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create( ImmutableSet.of(outputFile.relativeTo(execRoot)), ImmutableSet.of()), ImmutableSet.of(), diff --git a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java index 8c6c5ab4e7e366..3a4f18f5fc38a2 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java +++ b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java @@ -24,6 +24,8 @@ 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.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; @@ -38,7 +40,7 @@ class SandboxHelper { /** Map from workdir-relative input path to optional real file path. */ - private final Map inputs = new HashMap<>(); + private final Map inputs = new HashMap<>(); private final Map virtualInputs = new HashMap<>(); private final Map symlinks = new HashMap<>(); @@ -47,12 +49,15 @@ class SandboxHelper { private final List outputDirs = new ArrayList<>(); /** The global execRoot. */ - final Path execRoot; + final Path execRootPath; + + final Root execRoot; /** The worker process's sandbox root. */ final Path workDir; public SandboxHelper(Path execRoot, Path workDir) { - this.execRoot = execRoot; + this.execRootPath = execRoot; + this.execRoot = Root.fromPath(execRoot); this.workDir = workDir; } @@ -64,7 +69,9 @@ public SandboxHelper(Path execRoot, Path workDir) { public SandboxHelper addInputFile(String relativePath, String workspacePath) { inputs.put( PathFragment.create(relativePath), - workspacePath != null ? execRoot.getRelative(workspacePath) : null); + workspacePath != null + ? RootedPath.toRootedPath(execRoot, PathFragment.create(workspacePath)) + : null); return this; } @@ -77,7 +84,7 @@ public SandboxHelper addInputFile(String relativePath, String workspacePath) { public SandboxHelper addAndCreateInputFile( String relativePath, String workspacePath, String contents) throws IOException { addInputFile(relativePath, workspacePath); - Path absPath = execRoot.getRelative(workspacePath); + Path absPath = execRootPath.getRelative(workspacePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -88,7 +95,7 @@ public SandboxHelper addAndCreateInputFile( public SandboxHelper addAndCreateVirtualInput(String relativePath, String contents) { VirtualActionInput input = ActionsTestUtil.createVirtualActionInput(relativePath, contents); byte[] digest = - execRoot + execRootPath .getRelative(relativePath) .getFileSystem() .getDigestFunction() @@ -127,7 +134,7 @@ public SandboxHelper addOutputDir(String relativePath) { */ @CanIgnoreReturnValue public SandboxHelper addWorkerFile(String relativePath) { - Path absPath = execRoot.getRelative(relativePath); + Path absPath = execRootPath.getRelative(relativePath); workerFiles.put(PathFragment.create(relativePath), absPath); return this; } @@ -140,7 +147,7 @@ public SandboxHelper addWorkerFile(String relativePath) { public SandboxHelper addAndCreateWorkerFile(String relativePath, String contents) throws IOException { addWorkerFile(relativePath); - Path absPath = execRoot.getRelative(relativePath); + Path absPath = execRootPath.getRelative(relativePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -165,7 +172,7 @@ public SandboxHelper createExecRootFile(String relativePath, String contents) th @CanIgnoreReturnValue public SandboxHelper createWorkspaceDirFile(String workspaceDirPath, String contents) throws IOException { - Path absPath = execRoot.getRelative(workspaceDirPath); + Path absPath = execRootPath.getRelative(workspaceDirPath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java index 17cc4211170bdb..fe4032b0f84d06 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java @@ -63,6 +63,8 @@ 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.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; @@ -123,7 +125,8 @@ public void testExecInWorker_happyPath() throws ExecException, InterruptedExcept spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -215,7 +218,8 @@ public void testExecInWorker_finishesAsyncOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -258,7 +262,8 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -300,7 +305,8 @@ public void testExecInWorker_unsandboxedDiesOnInterrupt() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -333,7 +339,8 @@ public void testExecInWorker_noMultiplexWithDynamic() spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -370,7 +377,8 @@ private void assertRecordedResponsethrowsException(String recordedResponse, Stri spawn, key, context, - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -416,11 +424,12 @@ public void testExpandArgument_expandsArgumentsRecursively() new SandboxInputs( ImmutableMap.of( PathFragment.create("file"), - fs.getPath("/file"), + asRootedPath("/file"), PathFragment.create("file2"), - fs.getPath("/file2")), + asRootedPath("/file2")), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableMap.of(), + ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "arg2", "arg3", "multi arg", ""); @@ -433,9 +442,10 @@ public void testExpandArgument_expandsOnlyProperArguments() FileSystemUtils.writeIsoLatin1(fs.getPath("/file"), "arg1\n@@nonfile\n@foo//bar\narg2"); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), fs.getPath("/file")), + ImmutableMap.of(PathFragment.create("file"), asRootedPath("/file")), + ImmutableMap.of(), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "@@nonfile", "@foo//bar", "arg2"); @@ -446,9 +456,10 @@ public void testExpandArgument_failsOnMissingFile() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), fs.getPath("/dir/file")), + ImmutableMap.of(PathFragment.create("file"), asRootedPath("/dir/file")), + ImmutableMap.of(), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableSet.of()); IOException e = assertThrows( IOException.class, @@ -562,7 +573,8 @@ private WorkerSpawnRunner createWorkerSpawnRunner(WorkerOptions workerOptions) { public void testExpandArgument_failsOnUndeclaredInput() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = - new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); + new SandboxInputs( + ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); IOException e = assertThrows( IOException.class, @@ -571,6 +583,10 @@ public void testExpandArgument_failsOnUndeclaredInput() { assertThat(e).hasMessageThat().contains("declared input"); } + private RootedPath asRootedPath(String path) { + return RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(path)); + } + private static String logMarker(String text) { return "---8<---8<--- " + text + " ---8<---8<---\n"; } diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java index aa77288c5f82fc..2fb55d528be326 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java @@ -21,8 +21,9 @@ import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; 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 com.google.devtools.build.lib.vfs.Root; +import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.util.FileSystems; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import java.io.File; @@ -51,14 +52,13 @@ public void expandArgumentsPreservesEmptyLines() throws Exception { flags.forEach(pw::println); } - Path path = fs.getPath(flagfile.getAbsolutePath()); + RootedPath path = + RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(flagfile.getAbsolutePath())); WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("flagfile.txt"), path), - ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of()); + ImmutableMap.of(PathFragment.create("flagfile.txt"), path), ImmutableMap.of(), + ImmutableMap.of(), ImmutableSet.of()); WorkerSpawnRunner.expandArgument(inputs, "@flagfile.txt", requestBuilder); assertThat(requestBuilder.getArgumentsList()).containsExactlyElementsIn(flags); diff --git a/src/test/shell/integration/sandboxing_test.sh b/src/test/shell/integration/sandboxing_test.sh index 278798e16dfeb6..db46d03f793716 100755 --- a/src/test/shell/integration/sandboxing_test.sh +++ b/src/test/shell/integration/sandboxing_test.sh @@ -736,7 +736,7 @@ sh_test( EOF cat > pkg/tmp_test.sh < Date: Wed, 8 May 2024 17:56:00 +0200 Subject: [PATCH 24/32] Clean up bind mounting logic --- .../sandbox/LinuxSandboxedSpawnRunner.java | 95 +++++++++---------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 73b21e48c7c162..94be51da8d6244 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -14,7 +14,7 @@ package com.google.devtools.build.lib.sandbox; -import static com.google.common.collect.ImmutableMap.toImmutableMap; +import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NO_NETNS; import static java.util.stream.Collectors.joining; @@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; import com.google.common.io.ByteStreams; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.ExecException; @@ -137,7 +138,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final Reporter reporter; private final FileSystem fileSystem; private final Path slashTmp; - private final boolean tmpOnPackagePath; private final Path outputBase; private final LoadingCache> symlinkChainCache = Caffeine.newBuilder().build(LinuxSandboxedSpawnRunner::readSymlinkChainUncached); @@ -175,10 +175,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.reporter = cmdEnv.getReporter(); this.fileSystem = cmdEnv.getRuntime().getFileSystem(); this.slashTmp = cmdEnv.getRuntime().getFileSystem().getPath("/tmp"); - this.tmpOnPackagePath = - cmdEnv.getPackageLocator().getPathEntries().stream() - .map(Root::asPath) - .anyMatch(slashTmp::equals); this.outputBase = cmdEnv.getOutputBase(); } @@ -202,12 +198,6 @@ private boolean useHermeticTmp() { return false; } - if (tmpOnPackagePath) { - // /tmp as a package path entry seems very unlikely to work, but the bind mounting logic is - // not prepared for it and we don't want to crash. - return false; - } - if (getSandboxOptions().sandboxTmpfsPath.contains(slashTmp.asFragment())) { // A tmpfs path under /tmp is as hermetic as "hermetic /tmp". return false; @@ -233,27 +223,48 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(workspaceName); sandboxExecRoot.createDirectoryAndParents(); + SandboxInputs inputs = + helpers.processInputFiles( + context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), + execRoot); + Path sandboxTmp = null; + ImmutableSet pathsUnderTmpToMount = ImmutableSet.of(); if (useHermeticTmp()) { - // The base dir for the upperdir and workdir of an overlayfs on /tmp. - sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); - sandboxTmp.createDirectoryAndParents(); - - for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { - Path path = fileSystem.getPath(pathFragment); - if (path.startsWith(slashTmp)) { - // tmpfs mount points must exist, which is usually the user's responsibility. But if the - // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp - // directory. - sandboxTmp.getRelative(path.relativeTo(slashTmp)).createDirectoryAndParents(); + // Roots under /tmp are treated exactly like a user mount under /tmp to ensure that they are + // visible at the same path after mounting the hermetic tmp. Since a source root can be a + // symlink to a directory under /tmp, we need to account for all intermediate symlinks. + SequencedSet resolvedRoots = new LinkedHashSet<>(); + resolvedRoots.add(outputBase); + for (Root root : inputs.getSourceRoots()) { + if (!root.asPath().startsWith(outputBase)) { + resolvedRoots.add(root.asPath()); + } + resolvedRoots.addAll(readSymlinkChain(root)); + } + pathsUnderTmpToMount = + resolvedRoots.stream().filter(p -> p.startsWith(slashTmp)).collect(toImmutableSet()); + + // /tmp as a package path entry, output base or target of a local_repository seems very + // unlikely to work, but the bind mounting logic is not prepared for it and we don't want to + // crash, so just disable hermetic tmp in this case. + if (!pathsUnderTmpToMount.contains(slashTmp)) { + // The base dir for the upperdir and workdir of an overlayfs on /tmp. + sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); + sandboxTmp.createDirectoryAndParents(); + + for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { + Path path = fileSystem.getPath(pathFragment); + if (path.startsWith(slashTmp)) { + // tmpfs mount points must exist, which is usually the user's responsibility. But if the + // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp + // directory. + sandboxTmp.getRelative(path.relativeTo(slashTmp)).createDirectoryAndParents(); + } } } } - SandboxInputs inputs = - helpers.processInputFiles( - context.getInputMapping(PathFragment.EMPTY_FRAGMENT, /* willAccessRepeatedly= */ true), - execRoot); SandboxOutputs outputs = helpers.getOutputs(spawn); ImmutableMap environment = localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); @@ -268,7 +279,8 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context .addExecutionInfo(spawn.getExecutionInfo()) .setWritableFilesAndDirectories(writableDirs) .setTmpfsDirectories(ImmutableSet.copyOf(getSandboxOptions().sandboxTmpfsPath)) - .setBindMounts(prepareAndGetBindMounts(inputs, sandboxExecRoot, sandboxTmp)) + .setBindMounts( + prepareAndGetBindMounts(sandboxExecRoot, sandboxTmp, pathsUnderTmpToMount)) .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname) .setEnablePseudoterminal(getSandboxOptions().sandboxExplicitPseudoterminal) .setCreateNetworkNamespace(createNetworkNamespace ? NETNS_WITH_LOOPBACK : NO_NETNS) @@ -351,7 +363,7 @@ protected ImmutableSet getWritableDirs(Path sandboxExecRoot, Map prepareAndGetBindMounts( - SandboxInputs inputs, Path sandboxExecRoot, @Nullable Path sandboxTmp) + Path sandboxExecRoot, @Nullable Path sandboxTmp, ImmutableSet pathsUnderTmpToMount) throws UserExecException, IOException { final SortedMap userBindMounts = new TreeMap<>(); SandboxHelpers.mountAdditionalPaths( @@ -371,31 +383,14 @@ private ImmutableMap prepareAndGetBindMounts( return ImmutableMap.copyOf(userBindMounts); } - // Roots under /tmp are treated exactly like a user mount under /tmp to ensure that they are - // visible at the same path after mounting the hermetic tmp. Since a source root can be a - // symlink to another directory under /tmp, we need to account for all intermediate symlinks. - SequencedSet resolvedRoots = new LinkedHashSet<>(); - resolvedRoots.add(outputBase); - for (Root root : inputs.getSourceRoots()) { - if (!root.asPath().startsWith(outputBase)) { - resolvedRoots.add(root.asPath()); - } - resolvedRoots.addAll(readSymlinkChain(root)); - } - ImmutableMap resolvedRootsUnderTmp = - resolvedRoots.stream() - .filter(p -> p.startsWith(slashTmp)) - .collect(toImmutableMap(p -> p, p -> p)); - Iterable> allMounts = - Iterables.concat(userBindMounts.entrySet(), resolvedRootsUnderTmp.entrySet()); - SortedMap bindMounts = new TreeMap<>(); - for (var entry : allMounts) { + for (var entry : + Iterables.concat( + userBindMounts.entrySet(), Maps.asMap(pathsUnderTmpToMount, p -> p).entrySet())) { Path mountPoint = entry.getKey(); Path content = entry.getValue(); if (mountPoint.startsWith(slashTmp)) { - // sandboxTmp should be null if /tmp is an explicit mount point since useHermeticTmp() - // returns false in that case. + // sandboxTmp is null if /tmp is an explicit mount point. if (mountPoint.equals(slashTmp)) { throw new IOException( "Cannot mount /tmp explicitly with hermetic /tmp. Please file a bug at" From 561ba78f30602fe8b31bb7997a80083907ddda96 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 18:06:00 +0200 Subject: [PATCH 25/32] Reduce diff --- .../lib/sandbox/AbstractContainerizingSandboxedSpawn.java | 5 +++-- .../build/lib/sandbox/AbstractSandboxSpawnRunner.java | 2 +- .../build/lib/sandbox/LinuxSandboxedSpawnRunner.java | 5 ++--- .../com/google/devtools/build/lib/worker/WorkerExecRoot.java | 1 - .../google/devtools/build/lib/worker/WorkerSpawnRunner.java | 4 ---- 5 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java index e66b65d2db2179..3b2930118f43ca 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java @@ -27,6 +27,7 @@ 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.RootedPath; import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; @@ -162,9 +163,9 @@ void createInputs(Iterable inputsToCreate, SandboxInputs inputs) } Path key = sandboxExecRoot.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - Path fileDest = inputs.getFiles().get(fragment).asPath(); + RootedPath fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - copyFile(fileDest, key); + copyFile(fileDest.asPath(), key); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java index 9eb3915e27cc18..d72f9fbd648c13 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java @@ -373,7 +373,7 @@ protected ImmutableSet getWritableDirs( // On Windows, sandboxExecRoot is actually the main execroot. We will specify // exactly which output path is writable. if (OS.getCurrent() != OS.WINDOWS) { - writablePaths.add(execRoot); + writablePaths.add(sandboxExecRoot); } String testTmpdir = env.get("TEST_TMPDIR"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 94be51da8d6244..933b99c5f0db29 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -126,6 +126,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS } private final SandboxHelpers helpers; + private final FileSystem fileSystem; private final Path execRoot; private final boolean allowNetwork; private final Path linuxSandbox; @@ -136,7 +137,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final Duration timeoutKillDelay; private final TreeDeleter treeDeleter; private final Reporter reporter; - private final FileSystem fileSystem; private final Path slashTmp; private final Path outputBase; private final LoadingCache> symlinkChainCache = @@ -163,6 +163,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS TreeDeleter treeDeleter) { super(cmdEnv); this.helpers = helpers; + this.fileSystem = cmdEnv.getRuntime().getFileSystem(); this.execRoot = cmdEnv.getExecRoot(); this.allowNetwork = helpers.shouldAllowNetwork(cmdEnv.getOptions()); this.linuxSandbox = LinuxSandboxUtil.getLinuxSandbox(cmdEnv.getBlazeWorkspace()); @@ -173,7 +174,6 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.localEnvProvider = new PosixLocalEnvProvider(cmdEnv.getClientEnv()); this.treeDeleter = treeDeleter; this.reporter = cmdEnv.getReporter(); - this.fileSystem = cmdEnv.getRuntime().getFileSystem(); this.slashTmp = cmdEnv.getRuntime().getFileSystem().getPath("/tmp"); this.outputBase = cmdEnv.getOutputBase(); } @@ -215,7 +215,6 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context // so we have to prefix our name to turn it into a globally unique value. Path sandboxPath = sandboxBase.getRelative(getName()).getRelative(Integer.toString(context.getId())); - sandboxPath.createDirectoryAndParents(); // b/64689608: The execroot of the sandboxed process must end with the workspace name, just like // the normal execroot does. diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java index 1cf31e8063d6b5..d8802d06f7b66b 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java @@ -23,7 +23,6 @@ import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.RootedPath; - import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index cdf31cea836b3e..8ffb94168b5c86 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -61,11 +61,7 @@ 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.Root; import com.google.devtools.build.lib.vfs.RootedPath; -import com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.XattrProvider; -import com.google.devtools.build.lib.vfs.XattrProvider; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; import com.google.protobuf.ByteString; From 1a477e1b10a905eea9d0204c6fc91ecdd26cd42f Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 19:01:12 +0200 Subject: [PATCH 26/32] Fix SandboxHelpersTest --- .../build/lib/sandbox/SandboxHelpersTest.java | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 58f66a3ed46c00..6d9af8bdddc256 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -70,12 +70,14 @@ public class SandboxHelpersTest { private final Scratch scratch = new Scratch(); - private Path execRoot; + private Path execRootPath; + private Root execRoot; @Nullable private ExecutorService executorToCleanup; @Before public void createExecRoot() throws IOException { - execRoot = scratch.dir("/execRoot"); + execRootPath = scratch.dir("/execRoot"); + execRoot = Root.fromPath(execRootPath); } @After @@ -88,6 +90,10 @@ public void shutdownExecutor() throws InterruptedException { executorToCleanup.awaitTermination(TestUtils.WAIT_TIMEOUT_SECONDS, SECONDS); } + private RootedPath execRootedPath(String execPath) { + return RootedPath.toRootedPath(execRoot, PathFragment.create(execPath)); + } + @Test public void processInputFiles_materializesParamFile() throws Exception { SandboxHelpers sandboxHelpers = new SandboxHelpers(); @@ -98,15 +104,15 @@ public void processInputFiles_materializesParamFile() throws Exception { ParameterFileType.UNQUOTED, UTF_8); - SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(paramFile), execRoot); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(paramFile), execRootPath); assertThat(inputs.getFiles()) - .containsExactly(PathFragment.create("paramFile"), execRoot.getChild("paramFile")); + .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRoot.getChild("paramFile"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRootPath.getChild("paramFile"), UTF_8)) .containsExactly("-a", "-b") .inOrder(); - assertThat(execRoot.getChild("paramFile").isExecutable()).isTrue(); + assertThat(execRootPath.getChild("paramFile").isExecutable()).isTrue(); } @Test @@ -117,16 +123,15 @@ public void processInputFiles_materializesBinToolsFile() throws Exception { scratch.file("tool", "#!/bin/bash", "echo hello"), PathFragment.create("_bin/say_hello")); - SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(tool), execRoot); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(tool), execRootPath); assertThat(inputs.getFiles()) - .containsExactly( - PathFragment.create("_bin/say_hello"), execRoot.getRelative("_bin/say_hello")); + .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRoot.getRelative("_bin/say_hello"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRootPath.getRelative("_bin/say_hello"), UTF_8)) .containsExactly("#!/bin/bash", "echo hello") .inOrder(); - assertThat(execRoot.getRelative("_bin/say_hello").isExecutable()).isTrue(); + assertThat(execRootPath.getRelative("_bin/say_hello").isExecutable()).isTrue(); } /** @@ -235,7 +240,7 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException RootedPath inputTxt = RootedPath.toRootedPath( Root.fromPath(scratch.getFileSystem().getPath("/")), PathFragment.create("hello.txt")); - Path rootDir = execRoot.getParentDirectory(); + Path rootDir = execRootPath.getParentDirectory(); PathFragment input1 = PathFragment.create("existing/directory/with/input1.txt"); PathFragment input2 = PathFragment.create("partial/directory/input2.txt"); PathFragment input3 = PathFragment.create("new/directory/input3.txt"); @@ -243,7 +248,8 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableMap.of(), + ImmutableSet.of()); Set inputsToCreate = new LinkedHashSet<>(); LinkedHashSet dirsToCreate = new LinkedHashSet<>(); SandboxHelpers.populateInputsAndDirsToCreate( @@ -263,17 +269,17 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException assertThat(inputsToCreate).containsExactly(input1, input2, input3); // inputdir1 exists fully - execRoot.getRelative(inputDir1).createDirectoryAndParents(); + execRootPath.getRelative(inputDir1).createDirectoryAndParents(); // inputdir2 exists partially, should be kept nonetheless. - execRoot + execRootPath .getRelative(inputDir2) .getParentDirectory() .getRelative("doomedSubdir") .createDirectoryAndParents(); // inputDir3 just doesn't exist // outputDir only exists partially - execRoot.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); - execRoot.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); + execRootPath.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); + execRootPath.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); // `thiswillbeafile/output` simulates a directory that was in the stashed dir but whose same // path is used later for a regular file. scratch.dir("/execRoot/thiswillbeafile/output"); @@ -284,22 +290,23 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt, input4, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); - SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRoot); + ImmutableMap.of(), + ImmutableSet.of()); + SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRootPath); assertThat(dirsToCreate).containsExactly(inputDir2, inputDir3, outputDir); - assertThat(execRoot.getRelative("existing/directory/with").exists()).isTrue(); - assertThat(execRoot.getRelative("partial").exists()).isTrue(); - assertThat(execRoot.getRelative("partial/doomedSubdir").exists()).isFalse(); - assertThat(execRoot.getRelative("partial/directory").exists()).isFalse(); - assertThat(execRoot.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); - assertThat(execRoot.getRelative("out").exists()).isTrue(); - assertThat(execRoot.getRelative("out/dir").exists()).isFalse(); + assertThat(execRootPath.getRelative("existing/directory/with").exists()).isTrue(); + assertThat(execRootPath.getRelative("partial").exists()).isTrue(); + assertThat(execRootPath.getRelative("partial/doomedSubdir").exists()).isFalse(); + assertThat(execRootPath.getRelative("partial/directory").exists()).isFalse(); + assertThat(execRootPath.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); + assertThat(execRootPath.getRelative("out").exists()).isTrue(); + assertThat(execRootPath.getRelative("out/dir").exists()).isFalse(); } @Test public void populateInputsAndDirsToCreate_createsMappedDirectories() { ArtifactRoot outputRoot = - ArtifactRoot.asDerivedRoot(execRoot, ArtifactRoot.RootType.Output, "outputs"); + ArtifactRoot.asDerivedRoot(execRootPath, ArtifactRoot.RootType.Output, "outputs"); ActionInput outputFile = ActionsTestUtil.createArtifact(outputRoot, "bin/config/dir/file"); ActionInput outputDir = ActionsTestUtil.createTreeArtifactWithGeneratingAction( @@ -339,13 +346,13 @@ public void moveOutputs_mappedPathMovedToUnmappedPath() throws Exception { .setPathMapper(pathMapper) .build(); var sandboxHelpers = new SandboxHelpers(); - Path sandboxBase = execRoot.getRelative("sandbox"); + Path sandboxBase = execRootPath.getRelative("sandbox"); PathFragment mappedOutputPath = PathFragment.create("bin/output"); sandboxBase.getRelative(mappedOutputPath).getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeLinesAs( sandboxBase.getRelative(mappedOutputPath), UTF_8, "hello", "pathmapper"); - Path realBase = execRoot.getRelative("real"); + Path realBase = execRootPath.getRelative("real"); SandboxHelpers.moveOutputs(sandboxHelpers.getOutputs(spawn), sandboxBase, realBase); assertThat( From af8c8dcbadee12ad0a9955729a6826b807337301 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Wed, 8 May 2024 21:26:52 +0200 Subject: [PATCH 27/32] Fix one more test --- .../build/lib/sandbox/SymlinkedSandboxedSpawnTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java index 360b3770ccbc3f..c6674d61431116 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java @@ -89,7 +89,8 @@ public void createFileSystem() throws Exception { symlinkedExecRoot.createFileSystem(); assertThat(execRoot.getRelative("such/input.txt").isSymbolicLink()).isTrue(); - assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()).isEqualTo(helloTxt); + assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()) + .isEqualTo(helloTxt.asPath()); assertThat(execRoot.getRelative("very").isDirectory()).isTrue(); assertThat(execRoot.getRelative("wow/writable").isDirectory()).isTrue(); } From 78b78d1df25a25aa001d44c525171ad71aacb692 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Sat, 11 May 2024 21:40:38 +0200 Subject: [PATCH 28/32] Expand symlink test to cover external repos and symlinks --- src/test/shell/bazel/bazel_sandboxing_test.sh | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index 73906f943adb84..dfbff48d78c61f 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -490,13 +490,48 @@ function test_symlink_with_output_base_under_tmp() { create_workspace_with_default_repos WORKSPACE + local repo=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") + trap "rm -fr $repo" EXIT + + cat > WORKSPACE < $repo/pkg/es1 <<'EOF' +EXTERNAL_SOURCE_CONTENT +EOF + cat > $repo/pkg/BUILD <<'EOF' +exports_files(["es1"]) +genrule( + name="er1", + srcs=[], + outs=[":er1"], + cmd="echo EXTERNAL_GEN_CONTENT > $@", + visibility=["//visibility:public"], +) +EOF + mkdir -p pkg + cat > pkg/s1 <<'EOF' +SOURCE_CONTENT +EOF cat > pkg/BUILD <<'EOF' load(":r.bzl", "symlink_rule") -genrule(name="r1", srcs=[], outs=[":r1"], cmd="echo CONTENT > $@") +genrule(name="r1", srcs=[], outs=[":r1"], cmd="echo GEN_CONTENT > $@") symlink_rule(name="r2", input=":r1") genrule(name="r3", srcs=[":r2"], outs=[":r3"], cmd="cp $< $@") +symlink_rule(name="s2", input=":s1") +genrule(name="s3", srcs=[":s2"], outs=[":s3"], cmd="cp $< $@") +symlink_rule(name="er2", input="@repo//pkg:er1") +genrule(name="er3", srcs=[":er2"], outs=[":er3"], cmd="cp $< $@") +symlink_rule(name="es2", input="@repo//pkg:es1") +genrule(name="es3", srcs=[":es2"], outs=[":es3"], cmd="cp $< $@") EOF cat > pkg/r.bzl <<'EOF' @@ -513,8 +548,11 @@ EOF local tmp_output_base=$(mktemp -d "/tmp/bazel_output_base.XXXXXXXX") trap "chmod -R u+w $tmp_output_base && rm -fr $tmp_output_base" EXIT - bazel --output_base="$tmp_output_base" build //pkg:r3 - assert_contains CONTENT bazel-bin/pkg/r3 + bazel --output_base="$tmp_output_base" build //pkg:{er,es,r,s}3 + assert_contains EXTERNAL_GEN_CONTENT bazel-bin/pkg/er3 + assert_contains EXTERNAL_SOURCE_CONTENT bazel-bin/pkg/es3 + assert_contains GEN_CONTENT bazel-bin/pkg/r3 + assert_contains SOURCE_CONTENT bazel-bin/pkg/s3 bazel --output_base="$tmp_output_base" shutdown } From b6888d4cdd2e1b70f44893b6b423bc97f4ad6f01 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 14 May 2024 12:36:44 +0200 Subject: [PATCH 29/32] Modify sandboxing test to move workspace into subdirectory --- src/test/shell/bazel/bazel_sandboxing_test.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index dfbff48d78c61f..b1b25d989b8664 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -488,17 +488,10 @@ function test_symlink_with_output_base_under_tmp() { return 0 fi - create_workspace_with_default_repos WORKSPACE - local repo=$(mktemp -d "/tmp/bazel_mounted.XXXXXXXX") trap "rm -fr $repo" EXIT - cat > WORKSPACE < WORKSPACE < Date: Tue, 14 May 2024 14:25:43 +0200 Subject: [PATCH 30/32] WIp --- .../sandbox/LinuxSandboxedSpawnRunner.java | 30 ++++++++++++++----- src/test/shell/bazel/bazel_sandboxing_test.sh | 2 +- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 933b99c5f0db29..986fb85b863627 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -139,6 +139,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final Reporter reporter; private final Path slashTmp; private final Path outputBase; + private final ImmutableList packagePath; private final LoadingCache> symlinkChainCache = Caffeine.newBuilder().build(LinuxSandboxedSpawnRunner::readSymlinkChainUncached); private String cgroupsDir; @@ -176,6 +177,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.reporter = cmdEnv.getReporter(); this.slashTmp = cmdEnv.getRuntime().getFileSystem().getPath("/tmp"); this.outputBase = cmdEnv.getOutputBase(); + this.packagePath = cmdEnv.getPackageLocator().getPathEntries(); } private boolean useHermeticTmp() { @@ -235,20 +237,34 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context // symlink to a directory under /tmp, we need to account for all intermediate symlinks. SequencedSet resolvedRoots = new LinkedHashSet<>(); resolvedRoots.add(outputBase); - for (Root root : inputs.getSourceRoots()) { + for (Root root : Iterables.concat(packagePath)) { if (!root.asPath().startsWith(outputBase)) { resolvedRoots.add(root.asPath()); } - resolvedRoots.addAll(readSymlinkChain(root)); +// resolvedRoots.addAll(readSymlinkChain(root)); } - pathsUnderTmpToMount = - resolvedRoots.stream().filter(p -> p.startsWith(slashTmp)).collect(toImmutableSet()); - // /tmp as a package path entry, output base or target of a local_repository seems very // unlikely to work, but the bind mounting logic is not prepared for it and we don't want to // crash, so just disable hermetic tmp in this case. - if (!pathsUnderTmpToMount.contains(slashTmp)) { - // The base dir for the upperdir and workdir of an overlayfs on /tmp. + if (!resolvedRoots.contains(slashTmp)) { + pathsUnderTmpToMount = + resolvedRoots.stream() + .filter(p -> p.startsWith(slashTmp)) + // For any path /tmp/dir1/dir2 we encounter, we instead mount /tmp/dir1 (first two + // path segments). This is necessary to gracefully handle an edge case: + // - A workspace contains a subdirectory (e.g. examples) that is itself a workspace. + // - The child workspace brings in the parent workspace as a local_repository with + // an up-level reference. + // - The parent workspace is checked out under /tmp. + // - The parent workspace uses resolved symlink artifacts. + // In this scenario, the symlinks point to the parent workspace's external source + // root, which in turn points to the parent workspace's source directory under /tmp. + // Since the symlinks are not followed when finding the resolved roots above, only + // the child workspace's directory shows up in resolvedRoots. + .map(p -> p.getFileSystem().getPath(p.asFragment().subFragment(0, 2))) + .collect(toImmutableSet()); + + // The initially empty directory that will be mounted as /tmp in the sandbox. sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); sandboxTmp.createDirectoryAndParents(); diff --git a/src/test/shell/bazel/bazel_sandboxing_test.sh b/src/test/shell/bazel/bazel_sandboxing_test.sh index b1b25d989b8664..d6d468da566280 100755 --- a/src/test/shell/bazel/bazel_sandboxing_test.sh +++ b/src/test/shell/bazel/bazel_sandboxing_test.sh @@ -551,7 +551,7 @@ EOF local tmp_output_base=$(mktemp -d "/tmp/bazel_output_base.XXXXXXXX") trap "chmod -R u+w $tmp_output_base && rm -fr $tmp_output_base" EXIT - bazel --output_base="$tmp_output_base" build //pkg:{er,es,r,s}3 + bazel --output_base="$tmp_output_base" build //pkg:{er,es,r,s}3 --sandbox_debug assert_contains EXTERNAL_GEN_CONTENT bazel-bin/pkg/er3 assert_contains EXTERNAL_SOURCE_CONTENT bazel-bin/pkg/es3 assert_contains GEN_CONTENT bazel-bin/pkg/r3 From 9c0c738736ca98037ce12cc5577f98d0bb6f6a0a Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 14 May 2024 15:15:41 +0200 Subject: [PATCH 31/32] Simplify sandbox setup and fix integration test --- .../google/devtools/build/lib/sandbox/BUILD | 1 - .../sandbox/LinuxSandboxedSpawnRunner.java | 148 +++++++----------- 2 files changed, 59 insertions(+), 90 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD index d0689d67d52bb8..97a8f6afacc47f 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/BUILD +++ b/src/main/java/com/google/devtools/build/lib/sandbox/BUILD @@ -224,7 +224,6 @@ java_library( "//src/main/java/com/google/devtools/build/lib/util:os", "//src/main/java/com/google/devtools/build/lib/vfs", "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", - "//third_party:caffeine", "//third_party:guava", "//third_party:jsr305", ], diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 986fb85b863627..d4b9a09f917a48 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -17,11 +17,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NETNS_WITH_LOOPBACK; import static com.google.devtools.build.lib.sandbox.LinuxSandboxCommandLineBuilder.NetworkNamespace.NO_NETNS; -import static java.util.stream.Collectors.joining; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -62,15 +58,13 @@ import java.io.IOException; import java.time.Duration; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.Map; -import java.util.SequencedSet; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; -import java.util.concurrent.CompletionException; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; import javax.annotation.Nullable; /** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */ @@ -138,10 +132,7 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS private final TreeDeleter treeDeleter; private final Reporter reporter; private final Path slashTmp; - private final Path outputBase; - private final ImmutableList packagePath; - private final LoadingCache> symlinkChainCache = - Caffeine.newBuilder().build(LinuxSandboxedSpawnRunner::readSymlinkChainUncached); + private final ImmutableSet knownPathsToMountUnderHermeticTmp; private String cgroupsDir; /** @@ -176,8 +167,41 @@ private static boolean computeIsSupported(CommandEnvironment cmdEnv, Path linuxS this.treeDeleter = treeDeleter; this.reporter = cmdEnv.getReporter(); this.slashTmp = cmdEnv.getRuntime().getFileSystem().getPath("/tmp"); - this.outputBase = cmdEnv.getOutputBase(); - this.packagePath = cmdEnv.getPackageLocator().getPathEntries(); + this.knownPathsToMountUnderHermeticTmp = collectPathsToMountUnderHermeticTmp(cmdEnv); + } + + private ImmutableSet collectPathsToMountUnderHermeticTmp(CommandEnvironment cmdEnv) { + // If any path managed or tracked by Bazel is under /tmp, it needs to be explicitly mounted + // into the sandbox when using hermetic /tmp. We attempt to collect an over-approximation of + // these paths, as the main goal of hermetic /tmp is to avoid inheriting any direct + // or well-known children of /tmp from the host. + return Stream.concat( + Stream.of(cmdEnv.getOutputBase()), + cmdEnv.getPackageLocator().getPathEntries().stream().map(Root::asPath)) + .filter(p -> p.startsWith(slashTmp)) + // For any path /tmp/dir1/dir2 we encounter, we instead mount /tmp/dir1 (first two + // path segments). This is necessary to gracefully handle an edge case: + // - A workspace contains a subdirectory (e.g. examples) that is itself a workspace. + // - The child workspace brings in the parent workspace as a local_repository with + // an up-level reference. + // - The parent workspace is checked out under /tmp. + // In this scenario, the parent workspace's external source root points to the parent + // workspace's source directory under /tmp, but this directory is neither under the + // output base nor on the package path. While it would be possible to track the + // external roots of all inputs and mount their entire symlink chain, this would be + // very invasive to do in the face of resolved symlink artifacts (and impossible with + // unresolved symlinks). + // Instead, by mounting the direct children of /tmp that are parents of the source + // roots, we attempt to cover all reasonable cases in which repositories symlink + // paths relative to themselves and workspaces are checked out into subdirectories of + // /tmp. All explicit references to paths under /tmp must be handled by the user via + // --sandbox_add_mount_pair. + .map( + p -> + p.getFileSystem() + .getPath( + p.asFragment().subFragment(0, Math.min(2, p.asFragment().segmentCount())))) + .collect(toImmutableSet()); } private boolean useHermeticTmp() { @@ -200,6 +224,13 @@ private boolean useHermeticTmp() { return false; } + if (knownPathsToMountUnderHermeticTmp.contains(slashTmp)) { + // /tmp as a package path entry or output base seems very unlikely to work, but the bind + // mounting logic is not prepared for it and we don't want to crash, so just disable hermetic + // tmp in this case. + return false; + } + if (getSandboxOptions().sandboxTmpfsPath.contains(slashTmp.asFragment())) { // A tmpfs path under /tmp is as hermetic as "hermetic /tmp". return false; @@ -232,50 +263,21 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context Path sandboxTmp = null; ImmutableSet pathsUnderTmpToMount = ImmutableSet.of(); if (useHermeticTmp()) { - // Roots under /tmp are treated exactly like a user mount under /tmp to ensure that they are - // visible at the same path after mounting the hermetic tmp. Since a source root can be a - // symlink to a directory under /tmp, we need to account for all intermediate symlinks. - SequencedSet resolvedRoots = new LinkedHashSet<>(); - resolvedRoots.add(outputBase); - for (Root root : Iterables.concat(packagePath)) { - if (!root.asPath().startsWith(outputBase)) { - resolvedRoots.add(root.asPath()); - } -// resolvedRoots.addAll(readSymlinkChain(root)); - } - // /tmp as a package path entry, output base or target of a local_repository seems very - // unlikely to work, but the bind mounting logic is not prepared for it and we don't want to - // crash, so just disable hermetic tmp in this case. - if (!resolvedRoots.contains(slashTmp)) { - pathsUnderTmpToMount = - resolvedRoots.stream() - .filter(p -> p.startsWith(slashTmp)) - // For any path /tmp/dir1/dir2 we encounter, we instead mount /tmp/dir1 (first two - // path segments). This is necessary to gracefully handle an edge case: - // - A workspace contains a subdirectory (e.g. examples) that is itself a workspace. - // - The child workspace brings in the parent workspace as a local_repository with - // an up-level reference. - // - The parent workspace is checked out under /tmp. - // - The parent workspace uses resolved symlink artifacts. - // In this scenario, the symlinks point to the parent workspace's external source - // root, which in turn points to the parent workspace's source directory under /tmp. - // Since the symlinks are not followed when finding the resolved roots above, only - // the child workspace's directory shows up in resolvedRoots. - .map(p -> p.getFileSystem().getPath(p.asFragment().subFragment(0, 2))) - .collect(toImmutableSet()); - - // The initially empty directory that will be mounted as /tmp in the sandbox. - sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); - sandboxTmp.createDirectoryAndParents(); - - for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { - Path path = fileSystem.getPath(pathFragment); - if (path.startsWith(slashTmp)) { - // tmpfs mount points must exist, which is usually the user's responsibility. But if the - // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp - // directory. - sandboxTmp.getRelative(path.relativeTo(slashTmp)).createDirectoryAndParents(); - } + // Special paths under /tmp are treated exactly like a user mount under /tmp to ensure that + // they are visible at the same path after mounting the hermetic tmp. + pathsUnderTmpToMount = knownPathsToMountUnderHermeticTmp; + + // The initially empty directory that will be mounted as /tmp in the sandbox. + sandboxTmp = sandboxPath.getRelative("_hermetic_tmp"); + sandboxTmp.createDirectoryAndParents(); + + for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) { + Path path = fileSystem.getPath(pathFragment); + if (path.startsWith(slashTmp)) { + // tmpfs mount points must exist, which is usually the user's responsibility. But if the + // user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp + // directory. + sandboxTmp.getRelative(path.relativeTo(slashTmp)).createDirectoryAndParents(); } } } @@ -426,38 +428,6 @@ private ImmutableMap prepareAndGetBindMounts( .buildOrThrow(); } - /** Returns the chain of symlink targets obtained while resolving the path of the given root. */ - private ImmutableSet readSymlinkChain(Root root) throws IOException { - // We don't expect roots to change where they point to during a single build, so we can cache - // the symlink chains. The root path itself is not stored to reduce memory usage by having most - // roots share the empty set instance. - try { - return symlinkChainCache.get(root); - } catch (CompletionException e) { - Throwables.throwIfInstanceOf(e.getCause(), IOException.class); - throw e; - } - } - - private static ImmutableSet readSymlinkChainUncached(Root root) throws IOException { - Path path = root.asPath(); - SequencedSet result = new LinkedHashSet<>(); - while (true) { - try { - path = path.getParentDirectory().getRelative(path.readSymbolicLink()); - } catch (FileSystem.NotASymlinkException e) { - break; - } - if (!result.add(path)) { - throw new IOException( - String.format( - "Cycle in symlink chain for root %s: %s", - root, result.stream().map(Path::toString).collect(joining(" -> ")))); - } - } - return ImmutableSet.copyOf(result); - } - @Override public void verifyPostCondition( Spawn originalSpawn, SandboxedSpawn sandbox, SpawnExecutionContext context) From 9ebdcc24a041ef9bffddbc889cb427c2bd5cd285 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Tue, 14 May 2024 15:29:48 +0200 Subject: [PATCH 32/32] Re-revert the switch to `RootedPath` --- .../AbstractContainerizingSandboxedSpawn.java | 5 +- .../sandbox/AbstractSandboxSpawnRunner.java | 2 +- .../build/lib/sandbox/SandboxHelpers.java | 54 +++++--------- .../build/lib/sandbox/WindowsSandboxUtil.java | 9 ++- .../build/lib/worker/WorkerExecRoot.java | 5 +- .../build/lib/worker/WorkerSpawnRunner.java | 11 +-- ...tractContainerizingSandboxedSpawnTest.java | 8 +-- .../build/lib/sandbox/SandboxHelpersTest.java | 71 ++++++++----------- .../sandbox/SymlinkedSandboxedSpawnTest.java | 16 ++--- .../build/lib/worker/SandboxHelper.java | 27 +++---- .../lib/worker/WorkerSpawnRunnerTest.java | 44 ++++-------- .../lib/worker/WorkerSpawnStrategyTest.java | 12 ++-- 12 files changed, 100 insertions(+), 164 deletions(-) diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java index 3b2930118f43ca..bfc280c42d8b73 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawn.java @@ -27,7 +27,6 @@ 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.RootedPath; import java.io.IOException; import java.util.LinkedHashSet; import java.util.Set; @@ -163,9 +162,9 @@ void createInputs(Iterable inputsToCreate, SandboxInputs inputs) } Path key = sandboxExecRoot.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - RootedPath fileDest = inputs.getFiles().get(fragment); + Path fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - copyFile(fileDest.asPath(), key); + copyFile(fileDest, key); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java index d72f9fbd648c13..9eb3915e27cc18 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java @@ -373,7 +373,7 @@ protected ImmutableSet getWritableDirs( // On Windows, sandboxExecRoot is actually the main execroot. We will specify // exactly which output path is writable. if (OS.getCurrent() != OS.WINDOWS) { - writablePaths.add(sandboxExecRoot); + writablePaths.add(execRoot); } String testTmpdir = env.get("TEST_TMPDIR"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 69ca623aed23f2..7c2d84a5814ef8 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -21,12 +21,10 @@ import com.google.auto.value.AutoValue; import com.google.common.base.Preconditions; -import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; -import com.google.common.collect.Sets; import com.google.common.flogger.GoogleLogger; import com.google.devtools.build.lib.actions.ActionInput; import com.google.devtools.build.lib.actions.Artifact; @@ -46,8 +44,6 @@ import com.google.devtools.build.lib.vfs.FileSystemUtils.MoveResult; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.common.options.OptionsParsingResult; import java.io.IOException; @@ -246,9 +242,9 @@ private static void cleanRecursively( */ static Optional getExpectedSymlinkDestination( PathFragment fragment, SandboxInputs inputs) { - RootedPath file = inputs.getFiles().get(fragment); + Path file = inputs.getFiles().get(fragment); if (file != null) { - return Optional.of(file.asPath().asFragment()); + return Optional.of(file.asFragment()); } return Optional.ofNullable(inputs.getSymlinks().get(fragment)); } @@ -384,31 +380,27 @@ public static void mountAdditionalPaths( /** Wrapper class for the inputs of a sandbox. */ public static final class SandboxInputs { - private final Map files; + private final Map files; private final Map virtualInputs; private final Map symlinks; - private final ImmutableSet sourceRoots; private static final SandboxInputs EMPTY_INPUTS = - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); public SandboxInputs( - Map files, + Map files, Map virtualInputs, - Map symlinks, - Set sourceRoots) { + Map symlinks) { this.files = files; this.virtualInputs = virtualInputs; this.symlinks = symlinks; - this.sourceRoots = ImmutableSet.copyOf(sourceRoots); } public static SandboxInputs getEmptyInputs() { return EMPTY_INPUTS; } - public Map getFiles() { + public Map getFiles() { return files; } @@ -416,10 +408,6 @@ public Map getSymlinks() { return symlinks; } - public ImmutableSet getSourceRoots() { - return sourceRoots; - } - public ImmutableMap getVirtualInputDigests() { return ImmutableMap.copyOf(virtualInputs); } @@ -429,20 +417,15 @@ public ImmutableMap getVirtualInputDigests() { * included. */ public SandboxInputs limitedCopy(Set allowed) { - var limitedFiles = Maps.filterKeys(files, allowed::contains); - var limitedFilesRoots = - new HashSet<>(Collections2.transform(limitedFiles.values(), RootedPath::getRoot)); return new SandboxInputs( - limitedFiles, + Maps.filterKeys(files, allowed::contains), ImmutableMap.of(), - Maps.filterKeys(symlinks, allowed::contains), - Sets.intersection(sourceRoots, limitedFilesRoots)); + Maps.filterKeys(symlinks, allowed::contains)); } @Override public String toString() { - return "Files: %s\nVirtualInputs: %s\nSymlinks: %s\nSourceRoots: %s" - .formatted(files, virtualInputs, symlinks, sourceRoots); + return "Files: " + files + "\nVirtualInputs: " + virtualInputs + "\nSymlinks: " + symlinks; } } @@ -456,10 +439,9 @@ public String toString() { */ public SandboxInputs processInputFiles(Map inputMap, Path execRoot) throws IOException, InterruptedException { - Map inputFiles = new TreeMap<>(); + Map inputFiles = new TreeMap<>(); Map inputSymlinks = new TreeMap<>(); Map virtualInputs = new HashMap<>(); - ImmutableSet.Builder sourceRoots = ImmutableSet.builder(); for (Map.Entry e : inputMap.entrySet()) { if (Thread.interrupted()) { @@ -470,22 +452,20 @@ public SandboxInputs processInputFiles(Map inputMap, if (actionInput instanceof VirtualActionInput input) { byte[] digest = input.atomicallyWriteRelativeTo(execRoot); virtualInputs.put(input, digest); - } else if (actionInput instanceof Artifact artifact && artifact.isSourceArtifact()) { - sourceRoots.add(artifact.getRoot().getRoot()); } if (actionInput.isSymlink()) { Path inputPath = execRoot.getRelative(actionInput.getExecPath()); inputSymlinks.put(pathFragment, inputPath.readSymbolicLink()); - } else if (actionInput instanceof EmptyActionInput) { - inputFiles.put(pathFragment, null); } else { - inputFiles.put( - pathFragment, - RootedPath.toRootedPath(Root.fromPath(execRoot), actionInput.getExecPath())); + Path inputPath = + actionInput instanceof EmptyActionInput + ? null + : execRoot.getRelative(actionInput.getExecPath()); + inputFiles.put(pathFragment, inputPath); } } - return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks, sourceRoots.build()); + return new SandboxInputs(inputFiles, virtualInputs, inputSymlinks); } /** The file and directory outputs of a sandboxed spawn. */ diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java index 707f541e98f7e8..c582ecb3df3da5 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/WindowsSandboxUtil.java @@ -24,7 +24,6 @@ import com.google.devtools.build.lib.shell.SubprocessBuilder.StreamAction; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.ByteArrayOutputStream; import java.io.File; @@ -107,7 +106,7 @@ public static class CommandLineBuilder { private Path stdoutPath; private Path stderrPath; private Set writableFilesAndDirectories = ImmutableSet.of(); - private Map readableFilesAndDirectories = new TreeMap<>(); + private Map readableFilesAndDirectories = new TreeMap<>(); private Set inaccessiblePaths = ImmutableSet.of(); private boolean useDebugMode = false; private List commandArguments = ImmutableList.of(); @@ -166,7 +165,7 @@ public CommandLineBuilder setWritableFilesAndDirectories( /** Sets the files or directories to make readable for the sandboxed process, if any. */ @CanIgnoreReturnValue public CommandLineBuilder setReadableFilesAndDirectories( - Map readableFilesAndDirectories) { + Map readableFilesAndDirectories) { this.readableFilesAndDirectories = readableFilesAndDirectories; return this; } @@ -213,8 +212,8 @@ public ImmutableList build() { for (Path writablePath : writableFilesAndDirectories) { commandLineBuilder.add("-w", writablePath.getPathString()); } - for (RootedPath readablePath : readableFilesAndDirectories.values()) { - commandLineBuilder.add("-r", readablePath.asPath().getPathString()); + for (Path readablePath : readableFilesAndDirectories.values()) { + commandLineBuilder.add("-r", readablePath.getPathString()); } for (Path writablePath : inaccessiblePaths) { commandLineBuilder.add("-b", writablePath.getPathString()); diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java index d8802d06f7b66b..f45f4f47eb26c6 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerExecRoot.java @@ -22,7 +22,6 @@ 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.RootedPath; import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; @@ -87,9 +86,9 @@ static void createInputs(Iterable inputsToCreate, SandboxInputs in } Path key = dir.getRelative(fragment); if (inputs.getFiles().containsKey(fragment)) { - RootedPath fileDest = inputs.getFiles().get(fragment); + Path fileDest = inputs.getFiles().get(fragment); if (fileDest != null) { - key.createSymbolicLink(fileDest.asPath()); + key.createSymbolicLink(fileDest); } else { FileSystemUtils.createEmptyFile(key); } diff --git a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java index 8ffb94168b5c86..265e2092a5a569 100644 --- a/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/worker/WorkerSpawnRunner.java @@ -61,7 +61,11 @@ 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.Root; import com.google.devtools.build.lib.vfs.RootedPath; +import com.google.devtools.build.lib.vfs.Root; +import com.google.devtools.build.lib.vfs.XattrProvider; +import com.google.devtools.build.lib.vfs.XattrProvider; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; import com.google.protobuf.ByteString; @@ -308,21 +312,20 @@ static void expandArgument(SandboxInputs inputs, String arg, WorkRequest.Builder throw new InterruptedException(); } String argValue = arg.substring(1); - RootedPath path = inputs.getFiles().get(PathFragment.create(argValue)); + Path path = inputs.getFiles().get(PathFragment.create(argValue)); if (path == null) { throw new IOException( String.format( "Failed to read @-argument '%s': file is not a declared input", argValue)); } try { - for (String line : FileSystemUtils.readLines(path.asPath(), UTF_8)) { + for (String line : FileSystemUtils.readLines(path, UTF_8)) { expandArgument(inputs, line, requestBuilder); } } catch (IOException e) { throw new IOException( String.format( - "Failed to read @-argument '%s' from file '%s'.", - argValue, path.asPath().getPathString()), + "Failed to read @-argument '%s' from file '%s'.", argValue, path.getPathString()), e); } } else { diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java index c68453d8036d89..700189556cbf9d 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/AbstractContainerizingSandboxedSpawnTest.java @@ -35,7 +35,6 @@ 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.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -374,18 +373,17 @@ private static SandboxInputs createSandboxInputs( private static SandboxInputs createSandboxInputs( ImmutableList files, ImmutableMap symlinks) { - Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); + Map filesMap = Maps.newHashMapWithExpectedSize(files.size()); for (String file : files) { filesMap.put(PathFragment.create(file), null); } return new SandboxInputs( filesMap, - /* virtualInputs= */ ImmutableMap.of(), + /*virtualInputs=*/ ImmutableMap.of(), symlinks.entrySet().stream() .collect( toImmutableMap( - e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue()))), - ImmutableSet.of()); + e -> PathFragment.create(e.getKey()), e -> PathFragment.create(e.getValue())))); } /** Return a list of all entries under the provided directory recursively. */ diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java index 6d9af8bdddc256..5c8b4044bb81cb 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SandboxHelpersTest.java @@ -43,8 +43,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -70,14 +68,12 @@ public class SandboxHelpersTest { private final Scratch scratch = new Scratch(); - private Path execRootPath; - private Root execRoot; + private Path execRoot; @Nullable private ExecutorService executorToCleanup; @Before public void createExecRoot() throws IOException { - execRootPath = scratch.dir("/execRoot"); - execRoot = Root.fromPath(execRootPath); + execRoot = scratch.dir("/execRoot"); } @After @@ -90,10 +86,6 @@ public void shutdownExecutor() throws InterruptedException { executorToCleanup.awaitTermination(TestUtils.WAIT_TIMEOUT_SECONDS, SECONDS); } - private RootedPath execRootedPath(String execPath) { - return RootedPath.toRootedPath(execRoot, PathFragment.create(execPath)); - } - @Test public void processInputFiles_materializesParamFile() throws Exception { SandboxHelpers sandboxHelpers = new SandboxHelpers(); @@ -104,15 +96,15 @@ public void processInputFiles_materializesParamFile() throws Exception { ParameterFileType.UNQUOTED, UTF_8); - SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(paramFile), execRootPath); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(paramFile), execRoot); assertThat(inputs.getFiles()) - .containsExactly(PathFragment.create("paramFile"), execRootedPath("paramFile")); + .containsExactly(PathFragment.create("paramFile"), execRoot.getChild("paramFile")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRootPath.getChild("paramFile"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRoot.getChild("paramFile"), UTF_8)) .containsExactly("-a", "-b") .inOrder(); - assertThat(execRootPath.getChild("paramFile").isExecutable()).isTrue(); + assertThat(execRoot.getChild("paramFile").isExecutable()).isTrue(); } @Test @@ -123,15 +115,16 @@ public void processInputFiles_materializesBinToolsFile() throws Exception { scratch.file("tool", "#!/bin/bash", "echo hello"), PathFragment.create("_bin/say_hello")); - SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(tool), execRootPath); + SandboxInputs inputs = sandboxHelpers.processInputFiles(inputMap(tool), execRoot); assertThat(inputs.getFiles()) - .containsExactly(PathFragment.create("_bin/say_hello"), execRootedPath("_bin/say_hello")); + .containsExactly( + PathFragment.create("_bin/say_hello"), execRoot.getRelative("_bin/say_hello")); assertThat(inputs.getSymlinks()).isEmpty(); - assertThat(FileSystemUtils.readLines(execRootPath.getRelative("_bin/say_hello"), UTF_8)) + assertThat(FileSystemUtils.readLines(execRoot.getRelative("_bin/say_hello"), UTF_8)) .containsExactly("#!/bin/bash", "echo hello") .inOrder(); - assertThat(execRootPath.getRelative("_bin/say_hello").isExecutable()).isTrue(); + assertThat(execRoot.getRelative("_bin/say_hello").isExecutable()).isTrue(); } /** @@ -237,10 +230,8 @@ public void atomicallyWriteVirtualInput_writesArbitraryVirtualInput() throws Exc @Test public void cleanExisting_updatesDirs() throws IOException, InterruptedException { - RootedPath inputTxt = - RootedPath.toRootedPath( - Root.fromPath(scratch.getFileSystem().getPath("/")), PathFragment.create("hello.txt")); - Path rootDir = execRootPath.getParentDirectory(); + Path inputTxt = scratch.getFileSystem().getPath(PathFragment.create("/hello.txt")); + Path rootDir = execRoot.getParentDirectory(); PathFragment input1 = PathFragment.create("existing/directory/with/input1.txt"); PathFragment input2 = PathFragment.create("partial/directory/input2.txt"); PathFragment input3 = PathFragment.create("new/directory/input3.txt"); @@ -248,8 +239,7 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of()); + ImmutableMap.of()); Set inputsToCreate = new LinkedHashSet<>(); LinkedHashSet dirsToCreate = new LinkedHashSet<>(); SandboxHelpers.populateInputsAndDirsToCreate( @@ -269,17 +259,17 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException assertThat(inputsToCreate).containsExactly(input1, input2, input3); // inputdir1 exists fully - execRootPath.getRelative(inputDir1).createDirectoryAndParents(); + execRoot.getRelative(inputDir1).createDirectoryAndParents(); // inputdir2 exists partially, should be kept nonetheless. - execRootPath + execRoot .getRelative(inputDir2) .getParentDirectory() .getRelative("doomedSubdir") .createDirectoryAndParents(); // inputDir3 just doesn't exist // outputDir only exists partially - execRootPath.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); - execRootPath.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); + execRoot.getRelative(outputDir).getParentDirectory().createDirectoryAndParents(); + execRoot.getRelative("justSomeDir/thatIsDoomed").createDirectoryAndParents(); // `thiswillbeafile/output` simulates a directory that was in the stashed dir but whose same // path is used later for a regular file. scratch.dir("/execRoot/thiswillbeafile/output"); @@ -290,23 +280,22 @@ public void cleanExisting_updatesDirs() throws IOException, InterruptedException new SandboxInputs( ImmutableMap.of(input1, inputTxt, input2, inputTxt, input3, inputTxt, input4, inputTxt), ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of()); - SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRootPath); + ImmutableMap.of()); + SandboxHelpers.cleanExisting(rootDir, inputs2, inputsToCreate, dirsToCreate, execRoot); assertThat(dirsToCreate).containsExactly(inputDir2, inputDir3, outputDir); - assertThat(execRootPath.getRelative("existing/directory/with").exists()).isTrue(); - assertThat(execRootPath.getRelative("partial").exists()).isTrue(); - assertThat(execRootPath.getRelative("partial/doomedSubdir").exists()).isFalse(); - assertThat(execRootPath.getRelative("partial/directory").exists()).isFalse(); - assertThat(execRootPath.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); - assertThat(execRootPath.getRelative("out").exists()).isTrue(); - assertThat(execRootPath.getRelative("out/dir").exists()).isFalse(); + assertThat(execRoot.getRelative("existing/directory/with").exists()).isTrue(); + assertThat(execRoot.getRelative("partial").exists()).isTrue(); + assertThat(execRoot.getRelative("partial/doomedSubdir").exists()).isFalse(); + assertThat(execRoot.getRelative("partial/directory").exists()).isFalse(); + assertThat(execRoot.getRelative("justSomeDir/thatIsDoomed").exists()).isFalse(); + assertThat(execRoot.getRelative("out").exists()).isTrue(); + assertThat(execRoot.getRelative("out/dir").exists()).isFalse(); } @Test public void populateInputsAndDirsToCreate_createsMappedDirectories() { ArtifactRoot outputRoot = - ArtifactRoot.asDerivedRoot(execRootPath, ArtifactRoot.RootType.Output, "outputs"); + ArtifactRoot.asDerivedRoot(execRoot, ArtifactRoot.RootType.Output, "outputs"); ActionInput outputFile = ActionsTestUtil.createArtifact(outputRoot, "bin/config/dir/file"); ActionInput outputDir = ActionsTestUtil.createTreeArtifactWithGeneratingAction( @@ -346,13 +335,13 @@ public void moveOutputs_mappedPathMovedToUnmappedPath() throws Exception { .setPathMapper(pathMapper) .build(); var sandboxHelpers = new SandboxHelpers(); - Path sandboxBase = execRootPath.getRelative("sandbox"); + Path sandboxBase = execRoot.getRelative("sandbox"); PathFragment mappedOutputPath = PathFragment.create("bin/output"); sandboxBase.getRelative(mappedOutputPath).getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeLinesAs( sandboxBase.getRelative(mappedOutputPath), UTF_8, "hello", "pathmapper"); - Path realBase = execRootPath.getRelative("real"); + Path realBase = execRoot.getRelative("real"); SandboxHelpers.moveOutputs(sandboxHelpers.getOutputs(spawn), sandboxBase, realBase); assertThat( diff --git a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java index c6674d61431116..9c26026eeafe61 100644 --- a/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java +++ b/src/test/java/com/google/devtools/build/lib/sandbox/SymlinkedSandboxedSpawnTest.java @@ -26,8 +26,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.Symlinks; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.io.IOException; @@ -62,9 +60,8 @@ public final void setupTestDirs() throws IOException { @Test public void createFileSystem() throws Exception { - RootedPath helloTxt = - RootedPath.toRootedPath(Root.fromPath(workspaceDir), PathFragment.create("hello.txt")); - FileSystemUtils.createEmptyFile(helloTxt.asPath()); + Path helloTxt = workspaceDir.getRelative("hello.txt"); + FileSystemUtils.createEmptyFile(helloTxt); SymlinkedSandboxedSpawn symlinkedExecRoot = new SymlinkedSandboxedSpawn( @@ -75,8 +72,7 @@ public void createFileSystem() throws Exception { new SandboxInputs( ImmutableMap.of(PathFragment.create("such/input.txt"), helloTxt), ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of()), + ImmutableMap.of()), SandboxOutputs.create( ImmutableSet.of(PathFragment.create("very/output.txt")), ImmutableSet.of()), ImmutableSet.of(execRoot.getRelative("wow/writable")), @@ -89,8 +85,7 @@ public void createFileSystem() throws Exception { symlinkedExecRoot.createFileSystem(); assertThat(execRoot.getRelative("such/input.txt").isSymbolicLink()).isTrue(); - assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()) - .isEqualTo(helloTxt.asPath()); + assertThat(execRoot.getRelative("such/input.txt").resolveSymbolicLinks()).isEqualTo(helloTxt); assertThat(execRoot.getRelative("very").isDirectory()).isTrue(); assertThat(execRoot.getRelative("wow/writable").isDirectory()).isTrue(); } @@ -107,8 +102,7 @@ public void copyOutputs() throws Exception { execRoot, ImmutableList.of("/bin/true"), ImmutableMap.of(), - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create( ImmutableSet.of(outputFile.relativeTo(execRoot)), ImmutableSet.of()), ImmutableSet.of(), diff --git a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java index 3a4f18f5fc38a2..788614b4e47256 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java +++ b/src/test/java/com/google/devtools/build/lib/worker/SandboxHelper.java @@ -24,8 +24,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; @@ -40,7 +38,7 @@ class SandboxHelper { /** Map from workdir-relative input path to optional real file path. */ - private final Map inputs = new HashMap<>(); + private final Map inputs = new HashMap<>(); private final Map virtualInputs = new HashMap<>(); private final Map symlinks = new HashMap<>(); @@ -49,15 +47,12 @@ class SandboxHelper { private final List outputDirs = new ArrayList<>(); /** The global execRoot. */ - final Path execRootPath; - - final Root execRoot; + final Path execRoot; /** The worker process's sandbox root. */ final Path workDir; public SandboxHelper(Path execRoot, Path workDir) { - this.execRootPath = execRoot; - this.execRoot = Root.fromPath(execRoot); + this.execRoot = execRoot; this.workDir = workDir; } @@ -69,9 +64,7 @@ public SandboxHelper(Path execRoot, Path workDir) { public SandboxHelper addInputFile(String relativePath, String workspacePath) { inputs.put( PathFragment.create(relativePath), - workspacePath != null - ? RootedPath.toRootedPath(execRoot, PathFragment.create(workspacePath)) - : null); + workspacePath != null ? execRoot.getRelative(workspacePath) : null); return this; } @@ -84,7 +77,7 @@ public SandboxHelper addInputFile(String relativePath, String workspacePath) { public SandboxHelper addAndCreateInputFile( String relativePath, String workspacePath, String contents) throws IOException { addInputFile(relativePath, workspacePath); - Path absPath = execRootPath.getRelative(workspacePath); + Path absPath = execRoot.getRelative(workspacePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -95,7 +88,7 @@ public SandboxHelper addAndCreateInputFile( public SandboxHelper addAndCreateVirtualInput(String relativePath, String contents) { VirtualActionInput input = ActionsTestUtil.createVirtualActionInput(relativePath, contents); byte[] digest = - execRootPath + execRoot .getRelative(relativePath) .getFileSystem() .getDigestFunction() @@ -134,7 +127,7 @@ public SandboxHelper addOutputDir(String relativePath) { */ @CanIgnoreReturnValue public SandboxHelper addWorkerFile(String relativePath) { - Path absPath = execRootPath.getRelative(relativePath); + Path absPath = execRoot.getRelative(relativePath); workerFiles.put(PathFragment.create(relativePath), absPath); return this; } @@ -147,7 +140,7 @@ public SandboxHelper addWorkerFile(String relativePath) { public SandboxHelper addAndCreateWorkerFile(String relativePath, String contents) throws IOException { addWorkerFile(relativePath); - Path absPath = execRootPath.getRelative(relativePath); + Path absPath = execRoot.getRelative(relativePath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -172,7 +165,7 @@ public SandboxHelper createExecRootFile(String relativePath, String contents) th @CanIgnoreReturnValue public SandboxHelper createWorkspaceDirFile(String workspaceDirPath, String contents) throws IOException { - Path absPath = execRootPath.getRelative(workspaceDirPath); + Path absPath = execRoot.getRelative(workspaceDirPath); absPath.getParentDirectory().createDirectoryAndParents(); FileSystemUtils.writeContentAsLatin1(absPath, contents); return this; @@ -191,7 +184,7 @@ public SandboxHelper createSymlink(String relativePath, String relativeDestinati } public SandboxInputs getSandboxInputs() { - return new SandboxInputs(inputs, virtualInputs, symlinks, ImmutableSet.of()); + return new SandboxInputs(inputs, virtualInputs, symlinks); } public SandboxOutputs getSandboxOutputs() { diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java index fe4032b0f84d06..9a70b84886bf40 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnRunnerTest.java @@ -63,8 +63,6 @@ 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.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkResponse; @@ -125,8 +123,7 @@ public void testExecInWorker_happyPath() throws ExecException, InterruptedExcept spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -218,8 +215,7 @@ public void testExecInWorker_finishesAsyncOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -262,8 +258,7 @@ public void testExecInWorker_sendsCancelMessageOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -305,8 +300,7 @@ public void testExecInWorker_unsandboxedDiesOnInterrupt() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -339,8 +333,7 @@ public void testExecInWorker_noMultiplexWithDynamic() spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -377,8 +370,7 @@ private void assertRecordedResponsethrowsException(String recordedResponse, Stri spawn, key, context, - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()), + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()), SandboxOutputs.create(ImmutableSet.of(), ImmutableSet.of()), ImmutableList.of(), inputFileCache, @@ -424,12 +416,11 @@ public void testExpandArgument_expandsArgumentsRecursively() new SandboxInputs( ImmutableMap.of( PathFragment.create("file"), - asRootedPath("/file"), + fs.getPath("/file"), PathFragment.create("file2"), - asRootedPath("/file2")), + fs.getPath("/file2")), ImmutableMap.of(), - ImmutableMap.of(), - ImmutableSet.of()); + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "arg2", "arg3", "multi arg", ""); @@ -442,10 +433,9 @@ public void testExpandArgument_expandsOnlyProperArguments() FileSystemUtils.writeIsoLatin1(fs.getPath("/file"), "arg1\n@@nonfile\n@foo//bar\narg2"); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), asRootedPath("/file")), - ImmutableMap.of(), + ImmutableMap.of(PathFragment.create("file"), fs.getPath("/file")), ImmutableMap.of(), - ImmutableSet.of()); + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@file", requestBuilder); assertThat(requestBuilder.getArgumentsList()) .containsExactly("arg1", "@@nonfile", "@foo//bar", "arg2"); @@ -456,10 +446,9 @@ public void testExpandArgument_failsOnMissingFile() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("file"), asRootedPath("/dir/file")), - ImmutableMap.of(), + ImmutableMap.of(PathFragment.create("file"), fs.getPath("/dir/file")), ImmutableMap.of(), - ImmutableSet.of()); + ImmutableMap.of()); IOException e = assertThrows( IOException.class, @@ -573,8 +562,7 @@ private WorkerSpawnRunner createWorkerSpawnRunner(WorkerOptions workerOptions) { public void testExpandArgument_failsOnUndeclaredInput() { WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = - new SandboxInputs( - ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of()); + new SandboxInputs(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of()); IOException e = assertThrows( IOException.class, @@ -583,10 +571,6 @@ public void testExpandArgument_failsOnUndeclaredInput() { assertThat(e).hasMessageThat().contains("declared input"); } - private RootedPath asRootedPath(String path) { - return RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(path)); - } - private static String logMarker(String text) { return "---8<---8<--- " + text + " ---8<---8<---\n"; } diff --git a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java index 2fb55d528be326..6c8ff50f2e2729 100644 --- a/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java +++ b/src/test/java/com/google/devtools/build/lib/worker/WorkerSpawnStrategyTest.java @@ -18,12 +18,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; 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 com.google.devtools.build.lib.vfs.Root; -import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.lib.vfs.util.FileSystems; import com.google.devtools.build.lib.worker.WorkerProtocol.WorkRequest; import java.io.File; @@ -52,13 +50,13 @@ public void expandArgumentsPreservesEmptyLines() throws Exception { flags.forEach(pw::println); } - RootedPath path = - RootedPath.toRootedPath(Root.absoluteRoot(fs), fs.getPath(flagfile.getAbsolutePath())); + Path path = fs.getPath(flagfile.getAbsolutePath()); WorkRequest.Builder requestBuilder = WorkRequest.newBuilder(); SandboxInputs inputs = new SandboxInputs( - ImmutableMap.of(PathFragment.create("flagfile.txt"), path), ImmutableMap.of(), - ImmutableMap.of(), ImmutableSet.of()); + ImmutableMap.of(PathFragment.create("flagfile.txt"), path), + ImmutableMap.of(), + ImmutableMap.of()); WorkerSpawnRunner.expandArgument(inputs, "@flagfile.txt", requestBuilder); assertThat(requestBuilder.getArgumentsList()).containsExactlyElementsIn(flags);