Skip to content

Commit b78d73f

Browse files
tjgqcopybara-github
authored andcommitted
Implement RemoteActionFileSystem#statIfFound correctly when the path cannot be canonicalized (because one of the components is a non-directory or a dangling symlink).
This improves the error message for a tree artifact containing a dangling symlink, which regressed in 4247c20 (see #15454 (comment)). PiperOrigin-RevId: 617870632 Change-Id: I6847084a52b1e4bb7d8a9384ad6cd5d015dddf1b
1 parent efeb260 commit b78d73f

7 files changed

Lines changed: 69 additions & 34 deletions

File tree

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -623,13 +623,17 @@ protected FileStatus statNullable(PathFragment path, boolean followSymlinks) {
623623
private FileStatus statInternal(PathFragment path, FollowMode followMode, StatSources statSources)
624624
throws IOException {
625625
// Canonicalize the path.
626-
if (followMode == FollowMode.FOLLOW_ALL) {
627-
path = resolveSymbolicLinks(path).asFragment();
628-
} else if (followMode == FollowMode.FOLLOW_PARENT) {
629-
PathFragment parent = path.getParentDirectory();
630-
if (parent != null) {
631-
path = resolveSymbolicLinks(parent).asFragment().getChild(path.getBaseName());
626+
try {
627+
if (followMode == FollowMode.FOLLOW_ALL) {
628+
path = resolveSymbolicLinks(path).asFragment();
629+
} else if (followMode == FollowMode.FOLLOW_PARENT) {
630+
PathFragment parent = path.getParentDirectory();
631+
if (parent != null) {
632+
path = resolveSymbolicLinks(parent).asFragment().getChild(path.getBaseName());
633+
}
632634
}
635+
} catch (FileNotFoundException e) {
636+
return null;
633637
}
634638

635639
// Since the path has been canonicalized, the operations below never need to follow symlinks.

src/main/java/com/google/devtools/build/lib/skyframe/SkyframeActionExecutor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,11 +1484,12 @@ private static void reportOutputTreeArtifactErrors(
14841484
Action action, Artifact output, Reporter reporter, IOException e) {
14851485
String errorMessage;
14861486
if (e instanceof FileNotFoundException) {
1487-
errorMessage = String.format("TreeArtifact %s was not created", output.prettyPrint());
1487+
errorMessage = String.format("output tree artifact %s was not created", output.prettyPrint());
14881488
} else {
14891489
errorMessage =
14901490
String.format(
1491-
"Error while validating output TreeArtifact %s : %s", output, e.getMessage());
1491+
"error while validating output tree artifact %s: %s",
1492+
output.prettyPrint(), e.getMessage());
14921493
}
14931494

14941495
reporter.handle(Event.error(action.getOwner().getLocation(), errorMessage));

src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -559,9 +559,7 @@ private void visit(
559559

560560
if (statFollow == null) {
561561
throw new IOException(
562-
String.format(
563-
"Child %s of tree artifact %s is a dangling symbolic link",
564-
parentRelativePath, parentDir));
562+
String.format("child %s is a dangling symbolic link", parentRelativePath));
565563
}
566564

567565
if (statFollow.isFile() && !statFollow.isSpecialFile()) {
@@ -575,9 +573,7 @@ private void visit(
575573

576574
if (type == Dirent.Type.UNKNOWN) {
577575
throw new IOException(
578-
String.format(
579-
"Child %s of tree artifact %s has an unsupported type",
580-
parentRelativePath, parentDir));
576+
String.format("child %s has an unsupported type", parentRelativePath));
581577
}
582578

583579
visitor.visit(parentRelativePath, type, traversedSymlink);

src/test/java/com/google/devtools/build/lib/remote/RemoteActionFileSystemTest.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,43 @@ public void statAndExists_followSymlinks(
451451
@Test
452452
public void statAndExists_notFound() throws Exception {
453453
RemoteActionFileSystem actionFs = (RemoteActionFileSystem) createActionFileSystem();
454-
Artifact artifact = ActionsTestUtil.createArtifact(outputRoot, "out");
455-
PathFragment path = artifact.getPath().asFragment();
454+
PathFragment path = getOutputPath("does_not_exist");
455+
456+
assertThat(actionFs.exists(path)).isFalse();
457+
458+
assertThat(actionFs.statIfFound(path, /* followSymlinks= */ true)).isNull();
459+
460+
assertThrows(
461+
FileNotFoundException.class, () -> actionFs.stat(path, /* followSymlinks= */ true));
462+
}
463+
464+
@Test
465+
public void statAndExists_isNotDirectory() throws Exception {
466+
RemoteActionFileSystem actionFs = (RemoteActionFileSystem) createActionFileSystem();
467+
PathFragment nonDirPath = getOutputPath("non_dir");
468+
PathFragment path = nonDirPath.getChild("file");
469+
470+
writeLocalFile(actionFs, nonDirPath, "content");
456471

457472
assertThat(actionFs.exists(path)).isFalse();
458473

474+
assertThat(actionFs.statIfFound(path, /* followSymlinks= */ true)).isNull();
475+
476+
assertThrows(
477+
FileNotFoundException.class, () -> actionFs.stat(path, /* followSymlinks= */ true));
478+
}
479+
480+
@Test
481+
public void statAndExists_danglingSymlink_notFound() throws Exception {
482+
RemoteActionFileSystem actionFs = (RemoteActionFileSystem) createActionFileSystem();
483+
PathFragment path = getOutputPath("sym");
484+
485+
actionFs.getPath(path).createSymbolicLink(PathFragment.create("/does_not_exist"));
486+
487+
assertThat(actionFs.exists(path)).isFalse();
488+
489+
assertThat(actionFs.statIfFound(path, /* followSymlinks= */ true)).isNull();
490+
459491
assertThrows(
460492
FileNotFoundException.class, () -> actionFs.stat(path, /* followSymlinks= */ true));
461493
}

src/test/java/com/google/devtools/build/lib/skyframe/ActionOutputMetadataStoreTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,20 @@ public void createsTreeArtifactValueFromFilesystem() throws Exception {
259259
assertThat(chmodCalls).isEmpty();
260260
}
261261

262+
@Test
263+
public void withDanglingSymlinkInTreeArtifactFailsWithException() throws Exception {
264+
SpecialArtifact treeArtifact =
265+
ActionsTestUtil.createTreeArtifactWithGeneratingAction(outputRoot, "foo/bar");
266+
TreeFileArtifact child = TreeFileArtifact.createTreeOutput(treeArtifact, "child");
267+
treeArtifact.getPath().createDirectoryAndParents();
268+
child.getPath().createSymbolicLink(PathFragment.create("/does_not_exist"));
269+
270+
ActionOutputMetadataStore store = createStore(/* outputs= */ ImmutableSet.of(treeArtifact));
271+
272+
IOException e = assertThrows(IOException.class, () -> store.getOutputMetadata(treeArtifact));
273+
assertThat(e).hasMessageThat().contains("dangling symbolic link");
274+
}
275+
262276
@Test
263277
public void resettingOutputs() throws Exception {
264278
PathFragment path = PathFragment.create("foo/bar");

src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -505,9 +505,7 @@ void run(ActionExecutionContext actionExecutionContext) throws IOException {
505505

506506
ImmutableList<Event> errors = ImmutableList.copyOf(eventCollector);
507507
assertThat(errors).hasSize(2);
508-
assertThat(errors.get(0).getMessage())
509-
.contains(
510-
"Child links/link of tree artifact " + out.getPath() + " is a dangling symbolic link");
508+
assertThat(errors.get(0).getMessage()).contains("child links/link is a dangling symbolic link");
511509
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
512510
}
513511

@@ -555,9 +553,7 @@ void run(ActionExecutionContext actionExecutionContext) throws IOException {
555553

556554
ImmutableList<Event> errors = ImmutableList.copyOf(eventCollector);
557555
assertThat(errors).hasSize(2);
558-
assertThat(errors.get(0).getMessage())
559-
.contains(
560-
"Child links/link of tree artifact " + out.getPath() + " is a dangling symbolic link");
556+
assertThat(errors.get(0).getMessage()).contains("child links/link is a dangling symbolic link");
561557
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
562558
}
563559

@@ -607,9 +603,7 @@ void run(ActionExecutionContext actionExecutionContext) throws IOException {
607603

608604
ImmutableList<Event> errors = ImmutableList.copyOf(eventCollector);
609605
assertThat(errors).hasSize(2);
610-
assertThat(errors.get(0).getMessage())
611-
.contains(
612-
"Child links/link of tree artifact " + out.getPath() + " is a dangling symbolic link");
606+
assertThat(errors.get(0).getMessage()).contains("child links/link is a dangling symbolic link");
613607
assertThat(errors.get(1).getMessage()).contains("not all outputs were created or valid");
614608
}
615609

src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactValueTest.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,7 @@ public void visitTree_throwsOnDanglingSymlink() throws Exception {
418418
assertThrows(
419419
IOException.class,
420420
() -> TreeArtifactValue.visitTree(treeDir, (child, type, traversedSymlink) -> {}));
421-
assertThat(e)
422-
.hasMessageThat()
423-
.contains("Child symlink of tree artifact /tree is a dangling symbolic link");
421+
assertThat(e).hasMessageThat().contains("child symlink is a dangling symbolic link");
424422
}
425423

426424
@Test
@@ -455,9 +453,7 @@ public Collection<Dirent> readdir(PathFragment path, boolean followSymlinks)
455453
assertThrows(
456454
IOException.class,
457455
() -> TreeArtifactValue.visitTree(treeDir, (child, type, traversedSymlink) -> {}));
458-
assertThat(e)
459-
.hasMessageThat()
460-
.contains("Child unknown of tree artifact /tree has an unsupported type");
456+
assertThat(e).hasMessageThat().contains("child unknown has an unsupported type");
461457
}
462458

463459
@Test
@@ -522,9 +518,7 @@ public long getSize() {
522518
assertThrows(
523519
IOException.class,
524520
() -> TreeArtifactValue.visitTree(treeDir, (child, type, traversedSymlink) -> {}));
525-
assertThat(e)
526-
.hasMessageThat()
527-
.contains("Child sym of tree artifact /tree has an unsupported type");
521+
assertThat(e).hasMessageThat().contains("child sym has an unsupported type");
528522
}
529523

530524
@Test

0 commit comments

Comments
 (0)