Compare commits

...
Author SHA1 Message Date
morluto c00d1fa0bc fix(ntfs): preserve header facts alongside invalid fixup sequences 2026-11-10 21:28:40 -05:00
2 changed files with 41 additions and 20 deletions

No files matched your search

+34 -18
View File
@@ -127,10 +127,17 @@ describe("MFT header facts", () => {
});
});
it.each([0, 49, 64, 72])(
"reports an invalid first attribute offset %i",
(offset) => {
const record = recordOf();
it.each([
[0, 1],
[49, 1],
[64, 1],
[72, 1],
[48, 1],
[48, 0],
])(
"reports invalid first attribute offset %i with USA sequence %i",
(offset, sequenceNumber) => {
const record = recordOf({ sequenceNumber });
new DataView(record.buffer).setUint16(20, offset, true);
const parsed = parseMftImage(record);
if (!parsed.ok) throw new Error(parsed.error.message);
@@ -139,20 +146,29 @@ describe("MFT header facts", () => {
},
);
it("reports a declared record number that contradicts its stream position", () => {
const bytes = streamOf(recordOf(), recordOf());
new DataView(bytes.buffer).setUint32(1024 + 44, 0, true);
const parsed = parseMftImage(bytes);
if (!parsed.ok) throw new Error(parsed.error.message);
expect(parsed.value.records[1]).toMatchObject({
index: 1,
declaredRecordNumber: 0,
recordNumberMatchesSlot: false,
});
expect(parsed.value.limitations.join(" ")).toContain(
"disagrees with their stream slot",
);
});
it.each([1, 0, 0xffff])(
"reports a contradictory declared record number with USA sequence %i",
(sequenceNumber) => {
const bytes = streamOf(recordOf(), recordOf({ sequenceNumber }));
new DataView(bytes.buffer).setUint32(1024 + 44, 0, true);
const parsed = parseMftImage(bytes);
if (!parsed.ok) throw new Error(parsed.error.message);
expect(parsed.value.records[1]).toMatchObject({
index: 1,
declaredRecordNumber: 0,
recordNumberMatchesSlot: false,
fixups: {
malformed: sequenceNumber === 0 || sequenceNumber === 0xffff,
},
});
expect(parsed.value.metadata.fixup_unverified_record_count).toBe(
sequenceNumber === 1 ? 0 : 1,
);
expect(parsed.value.limitations.join(" ")).toContain(
"disagrees with their stream slot",
);
},
);
it("keeps the inline header window separate from the parsed record count", () => {
const parsed = parseMftImage(
+7 -2
View File
@@ -373,8 +373,13 @@ const readRecord = (
true,
);
const usaLayoutValid = updateSequenceArrayFits(
usaOffset,
fixups.entries,
recordBytes / FIXUP_SECTOR_BYTES,
);
const declaredRecordNumber =
!fixups.malformed && usaOffset >= RECORD_HEADER_WITH_NUMBER_BYTES
usaLayoutValid && usaOffset >= RECORD_HEADER_WITH_NUMBER_BYTES
? view.getUint32(RECORD_HEADER.recordNumber, true)
: null;
@@ -396,7 +401,7 @@ const readRecord = (
firstAttributeOffset % 8 === 0 &&
firstAttributeOffset >= NTFS_MFT_MIN_RECORD_BYTES &&
firstAttributeOffset + 4 <= usedBytes &&
(fixups.malformed ||
(!usaLayoutValid ||
firstAttributeOffset >= usaOffset + fixups.entries * 2),
baseRecordReference:
baseRecordReference === 0n