refactor(ntfs): share the producer-owned record identity verdict

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
This commit is contained in:
N0zoM1z0andgensokyo-dev authored and gensokyo-dev committed 2026-11-11 14:10:25 +08:00
1 parent 04e9654e11
commit 4e96d64189
3 files changed
+26 -12

No files matched your search

+2 -5
View File
@@ -1,4 +1,4 @@
import { FIXUP_SECTOR_BYTES, type MftRecord } from "./ntfsMft.js";
import { hasVerifiedMftRecordIdentity, type MftRecord } from "./ntfsMft.js";
import type { UsnRecord } from "./ntfsUsnJournal.js";
type RecordIdentity = Pick<
@@ -40,12 +40,9 @@ export const createUsnMftCorrelator = (
// logical MFT stream. Extension records do not identify separate files.
if (
record.baseRecordReference !== null ||
record.sequenceNumber === 0 ||
!record.extentValid ||
record.fixups.verifiedSectors * FIXUP_SECTOR_BYTES !== record.totalBytes
!hasVerifiedMftRecordIdentity(record)
)
continue;
if (record.recordNumberMatchesSlot === false) continue;
const reference =
(BigInt(record.sequenceNumber) << 48n) | BigInt(record.index);
+19 -7
View File
@@ -118,6 +118,22 @@ export interface MftRecord extends MftAttributeScan {
readonly fixups: MftRecordFixups;
}
/** Producer-owned identity validity; base/extension ownership is a separate relation. */
export const hasVerifiedMftRecordIdentity = (
record: Pick<
MftRecord,
| "sequenceNumber"
| "extentValid"
| "recordNumberMatchesSlot"
| "fixups"
| "totalBytes"
>,
): boolean =>
record.sequenceNumber !== 0 &&
record.extentValid &&
record.recordNumberMatchesSlot !== false &&
record.fixups.verifiedSectors * FIXUP_SECTOR_BYTES === record.totalBytes;
export interface MftInventory {
readonly metadata: MftMetadata;
/** The record size the stream declared, or the fixed default when it did not. */
@@ -463,11 +479,6 @@ const resolveMftAttributeLists = (
decoded.set(index, record);
return ok(record);
};
const verifiedIdentity = (record: MftRecord): boolean =>
record.sequenceNumber !== 0 &&
record.extentValid &&
record.recordNumberMatchesSlot !== false &&
record.fixups.verifiedSectors * FIXUP_SECTOR_BYTES === record.totalBytes;
return records.map((base): MftRecord => {
if (
@@ -484,7 +495,7 @@ const resolveMftAttributeLists = (
seenEntries = new Set<number>(),
seenAttributes = new Set<number>();
const queue: MftAttributeListEntry[] = [];
const baseVerified = verifiedIdentity(base);
const baseVerified = hasVerifiedMftRecordIdentity(base);
let complete = baseVerified && base.attributeCoverage === "complete";
if (!complete) incompleteRecords.add(base.index);
const enqueueList = (attribute: MftAttribute): void => {
@@ -512,7 +523,8 @@ const resolveMftAttributeLists = (
const selected = selectRecord(entry.reference.recordNumber);
if (!selected.ok) return selected.error;
const target = selected.value;
if (!verifiedIdentity(target)) return unresolved("unverified-record");
if (!hasVerifiedMftRecordIdentity(target))
return unresolved("unverified-record");
if (target.sequenceNumber !== entry.reference.sequenceNumber)
return unresolved("sequence-mismatch");
if (