fix(ntfs): expose unrecognized FILE record flag bits

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 10:03:35 +08:00
1 parent 9505c73824
commit 14030e2f11
3 files changed
+27 -6

No files matched your search

+5 -1
View File
@@ -309,7 +309,11 @@ const ntfsRecordHeaderSchema = z.strictObject({
fileOffset: offset,
sequenceNumber: offset,
hardLinkCount: offset,
flags: z.strictObject({ raw: offset, names: z.array(z.string()) }),
flags: z.strictObject({
raw: offset,
names: z.array(z.string()),
unknownMask: offset,
}),
inUse: z.boolean(),
directory: z.boolean(),
usedBytes: offset,
+10
View File
@@ -111,6 +111,16 @@ describe("MFT header facts", () => {
expect(parsed.value.limitations).toEqual([]);
});
it("identifies unrecognized flags without losing their source bits", () => {
const parsed = parseMftImage(recordOf({ flags: 0x8001 }));
if (!parsed.ok) throw new Error(parsed.error.message);
expect(parsed.value.metadata.record_headers[0]?.flags).toEqual({
raw: 0x8001,
names: ["in-use"],
unknownMask: 0x8000,
});
});
it("keeps the inline header window separate from the parsed record count", () => {
const parsed = parseMftImage(
streamOf(...Array.from({ length: 33 }, () => recordOf())),
+12 -5
View File
@@ -90,7 +90,11 @@ export interface MftRecord {
readonly sequenceNumber: number;
/** Hard link count as recorded; a zero count marks a record with no names. */
readonly hardLinkCount: number;
readonly flags: { readonly raw: number; readonly names: readonly string[] };
readonly flags: {
readonly raw: number;
readonly names: readonly string[];
readonly unknownMask: number;
};
readonly inUse: boolean;
readonly directory: boolean;
/** Used and allocated byte counts as declared by the FILE record. */
@@ -156,11 +160,14 @@ const limit = (
...(boundary === undefined ? {} : { boundary }),
});
const namesOf = (raw: number): readonly string[] => {
const parseRecordFlags = (raw: number): MftRecord["flags"] => {
const names: string[] = [];
for (const [bit, name] of FILE_RECORD_NAMES)
let unknownMask = raw;
for (const [bit, name] of FILE_RECORD_NAMES) {
if ((raw & bit) !== 0) names.push(name);
return names;
unknownMask &= ~bit;
}
return { raw, names, unknownMask };
};
const isRecordSize = (bytes: number): boolean =>
@@ -355,7 +362,7 @@ const readRecord = (
fileOffset,
sequenceNumber: view.getUint16(RECORD_HEADER.sequenceNumber, true),
hardLinkCount: view.getUint16(RECORD_HEADER.hardLinkCount, true),
flags: { raw: flags, names: namesOf(flags) },
flags: parseRecordFlags(flags),
inUse: (flags & FILE_RECORD_IN_USE) !== 0,
directory: (flags & FILE_RECORD_IS_DIRECTORY) !== 0,
usedBytes,