fix(reference): preserve observed directories and unreadable links

Continue validating child directories observed before a parent enumeration failure. Reconcile unobserved link targets at the reader using canonical path identity and the nearest failed ancestor, preserving exclusions and cancellation.

Refs: #4013, #4053, #4060

Co-authored-by: XY-code1 <1611761391@qq.com>
This commit is contained in:
morlutoandXY-code1 committed 2026-10-16 04:41:40 +08:00
1 parent ee250816e1
commit d6e4fe4a4d
6 files changed
+401 -4

No files matched your search

+18 -3
View File
@@ -134,6 +134,7 @@ export const traverseDirectory = async (
readFailure,
),
);
scheduleObservedDirectories(state, directories);
return { ok: true, value: undefined };
}
const after = await validateDirectory(
@@ -152,6 +153,7 @@ export const traverseDirectory = async (
"Directory changed while it was read",
),
);
scheduleObservedDirectories(state, directories);
return { ok: true, value: undefined };
}
if (current.path !== state.root)
@@ -160,9 +162,16 @@ export const traverseDirectory = async (
kind: "directory",
path: pathFromRoot(state.root, current.path),
});
scheduleObservedDirectories(state, directories);
return { ok: true, value: undefined };
};
const scheduleObservedDirectories = (
state: TraversalState,
directories: PendingDirectory[],
): void => {
directories.reverse();
state.pending.push(...directories);
return { ok: true, value: undefined };
};
const directoryReadFailure = (cause: unknown): string => {
@@ -207,7 +216,10 @@ const processEntry = async (
ok: false,
error: failure("io", "Reference source exclusion check failed"),
};
if (excluded.value) return { ok: true, value: undefined };
if (excluded.value) {
state.exclusions.push({ path, kind: "unknown" });
return { ok: true, value: undefined };
}
state.entries.push(entryFailure(path, "unknown", "io", metadata.message));
return { ok: true, value: undefined };
}
@@ -228,7 +240,10 @@ const processEntry = async (
message: "Reference source exclusion check failed",
},
};
if (excluded.value) return { ok: true, value: undefined };
if (excluded.value) {
state.exclusions.push({ path, kind });
return { ok: true, value: undefined };
}
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
if (kind === "symlink")
state.entries.push(