fix(apple): recheck cache identity around digest reads

This commit is contained in:
morluto committed 2026-10-08 16:36:44 +08:00
1 parent d4ba8f661b
commit dbc7ebc2b6
1 file changed
+38 -2
+38 -2
View File
@@ -70,18 +70,42 @@ class CacheFile {
readonly suffix: string,
readonly handle: FileHandle,
readonly size: number,
/** Device, inode, size, and mtime of the file as opened. */
readonly identity: string,
) {}
static async open(path: string, suffix: string): Promise<CacheFile> {
const handle = await open(path, "r");
try {
return new CacheFile(suffix, handle, (await handle.stat()).size);
const metadata = await handle.stat();
return new CacheFile(
suffix,
handle,
metadata.size,
CacheFile.identify(metadata),
);
} catch (cause: unknown) {
await handle.close();
throw cause;
}
}
static identify(metadata: {
readonly dev: number;
readonly ino: number;
readonly size: number;
readonly mtimeMs: number;
}): string {
return [metadata.dev, metadata.ino, metadata.size, metadata.mtimeMs].join(
":",
);
}
/** Whether the open file still matches the identity recorded at open. */
async unchanged(): Promise<boolean> {
return CacheFile.identify(await this.handle.stat()) === this.identity;
}
async read(offset: number, length: number): Promise<Buffer> {
if (offset < 0 || offset + length > this.size)
throw new ArtifactReaderFailure(
@@ -547,9 +571,16 @@ export class DyldSharedCache {
}
private async hashFile(
file: { readonly handle: FileHandle; readonly size: number },
file: CacheFile,
signal?: AbortSignal,
): Promise<string> {
// In-place rewrites through the same inode keep this handle valid, so a
// digest is bound to its bytes only when the identity is unchanged.
if (!(await file.unchanged()))
throw new ArtifactReaderFailure(
"integrity",
"dyld cache file changed before its digest was read",
);
const hash = createHash("sha256");
const buffer = Buffer.alloc(HASH_CHUNK_BYTES);
let position = 0;
@@ -577,6 +608,11 @@ export class DyldSharedCache {
"integrity",
"dyld cache file changed while its digest was read",
);
if (!(await file.unchanged()))
throw new ArtifactReaderFailure(
"integrity",
"dyld cache file changed while its digest was read",
);
return hash.digest("hex");
}