Skip to content

Commit dc91c5b

Browse files
fmeumbazel-io
authored andcommitted
Disallow output bases under GC-able directories
We have had user's report spurious build failures due to the disk cache GC collecting files under the output base when output base and disk cache were set up at the same path. Closes bazelbuild#26138. PiperOrigin-RevId: 785412502 Change-Id: I44865ce44782a21a4cc15b61f0e0c9d4f13ea194
1 parent 34b3b43 commit dc91c5b

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
import com.google.devtools.build.lib.vfs.RootedPath;
123123
import com.google.devtools.common.options.OptionsBase;
124124
import com.google.devtools.common.options.OptionsParsingResult;
125+
import java.io.FileNotFoundException;
125126
import java.io.IOException;
126127
import java.lang.reflect.InvocationTargetException;
127128
import java.time.Instant;
@@ -377,6 +378,50 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
377378
repositoryCache.getRepoContentsCache().setPath(toPath(repoOptions.repoContentsCache, env));
378379
}
379380
Path repoContentsCachePath = repositoryCache.getRepoContentsCache().getPath();
381+
if (repoContentsCachePath != null) {
382+
// Check that the repo contents cache directory, which is managed by a garbage collecting
383+
// idle task, does not contain the output base. Since the specified output base path may be
384+
// a symlink, we resolve it fully. Intermediate symlinks do not have to be checked as the
385+
// garbage collector ignores symlinks. We also resolve the repo contents cache directory,
386+
// where intermediate symlinks also don't matter since deletion only occurs under the fully
387+
// resolved path.
388+
Path resolvedOutputBase = env.getOutputBase();
389+
try {
390+
resolvedOutputBase = resolvedOutputBase.resolveSymbolicLinks();
391+
} catch (FileNotFoundException ignored) {
392+
// Will be created later.
393+
} catch (IOException e) {
394+
throw new AbruptExitException(
395+
detailedExitCode(
396+
"could not resolve output base: %s".formatted(e.getMessage()),
397+
Code.BAD_REPO_CONTENTS_CACHE),
398+
e);
399+
}
400+
Path resolvedRepoContentsCache = repoContentsCachePath;
401+
try {
402+
resolvedRepoContentsCache = resolvedRepoContentsCache.resolveSymbolicLinks();
403+
} catch (FileNotFoundException ignored) {
404+
// Will be created later.
405+
} catch (IOException e) {
406+
throw new AbruptExitException(
407+
detailedExitCode(
408+
"could not resolve repo contents cache path: %s".formatted(e.getMessage()),
409+
Code.BAD_REPO_CONTENTS_CACHE),
410+
e);
411+
}
412+
if (resolvedOutputBase.startsWith(resolvedRepoContentsCache)) {
413+
// This is dangerous as the repo contents cache GC may delete files in the output base.
414+
throw new AbruptExitException(
415+
detailedExitCode(
416+
"""
417+
The output base [%s] is inside the repo contents cache [%s]. This can cause \
418+
spurious failures. Disable the repo contents cache with `--repo_contents_cache=`, \
419+
or specify `--repo_contents_cache=<path that doesn't contain the output base>`.
420+
"""
421+
.formatted(resolvedOutputBase, resolvedRepoContentsCache),
422+
Code.BAD_REPO_CONTENTS_CACHE));
423+
}
424+
}
380425
if (repoContentsCachePath != null
381426
&& env.getWorkspace() != null
382427
&& repoContentsCachePath.startsWith(env.getWorkspace())) {

src/main/java/com/google/devtools/build/lib/remote/RemoteModule.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
import io.netty.handler.codec.DecoderException;
110110
import io.netty.handler.codec.http.HttpResponseStatus;
111111
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
112+
import java.io.FileNotFoundException;
112113
import java.io.IOException;
113114
import java.net.URI;
114115
import java.net.URISyntaxException;
@@ -342,6 +343,38 @@ public void beforeCommand(CommandEnvironment env) throws AbruptExitException {
342343
boolean enableRemoteDownloader = shouldEnableRemoteDownloader(remoteOptions);
343344

344345
if (enableDiskCache) {
346+
// Check that the disk cache directory, which is managed by a garbage collecting idle task,
347+
// does not contain the output base. Since the specified output base path may be a symlink,
348+
// we resolve it fully. Intermediate symlinks do not have to be checked as the garbage
349+
// collector ignores symlinks. We also resolve the disk cache directory, where intermediate
350+
// symlinks also don't matter since deletion only occurs under the fully resolved path.
351+
Path resolvedOutputBase = env.getOutputBase();
352+
try {
353+
resolvedOutputBase = resolvedOutputBase.resolveSymbolicLinks();
354+
} catch (FileNotFoundException ignored) {
355+
// Will be created later.
356+
} catch (IOException e) {
357+
throw createOptionsExitException(
358+
"Failed to resolve output base: %s".formatted(e.getMessage()),
359+
FailureDetails.RemoteOptions.Code.EXECUTION_WITH_INVALID_CACHE);
360+
}
361+
Path resolvedDiskCache = env.getWorkingDirectory().getRelative(remoteOptions.diskCache);
362+
try {
363+
resolvedDiskCache = resolvedDiskCache.resolveSymbolicLinks();
364+
} catch (FileNotFoundException ignored) {
365+
// Will be created later.
366+
} catch (IOException e) {
367+
throw createOptionsExitException(
368+
"Failed to resolve disk cache directory: %s".formatted(e.getMessage()),
369+
FailureDetails.RemoteOptions.Code.EXECUTION_WITH_INVALID_CACHE);
370+
}
371+
if (resolvedOutputBase.startsWith(resolvedDiskCache)) {
372+
// This is dangerous as the disk cache GC may delete files in the output base.
373+
throw createOptionsExitException(
374+
"The output base [%s] cannot be a subdirectory of the --disk_cache directory [%s]"
375+
.formatted(resolvedOutputBase, resolvedDiskCache),
376+
FailureDetails.RemoteOptions.Code.EXECUTION_WITH_INVALID_CACHE);
377+
}
345378
var gcIdleTask =
346379
DiskCacheGarbageCollectorIdleTask.create(remoteOptions, env.getWorkingDirectory());
347380
if (gcIdleTask != null) {

0 commit comments

Comments
 (0)