I am writing some code to parse through the MFT on disk in NTFS volumes. This is straightforward, but one particular corner case caught my eye, and I can't find a clear answer anywhere on the internet.
For normal files in NTFS it is possible to have multiple MFT records for a single file, if the file has more attributes than can fit in a single record (for example, many $FILE_NAME attributes if the file has many hard-links, or many $DATA attributes if it has many Alternate Data Streams).
The $MFT file at reference-number 0 holds the data runs for the MFT itself. Normally it is a single record with no children. Is it possible for the $MFT file to have child records? If it were possible, how would you know where to find them? Would those child records have to be stored with very low reference numbers so that you could reliably get to them without having to have parsed the $MFT already to know where they were on disk?
There is a special type of attribute called $ATTRIBUTE_LIST
. A file or directory can have up to 65536 attributes and they can't possibly fit into a single MFT entry. It basically contains a list of all the file's attributes except himself. Each entry in the list contains the attribute type and the MFT reference of where to find the attribute. That's what the base file reference field in the file record header is for.
If the list gets too big for a MFT entry, the attribute can become non-resident and the list will be found by interpreting the data run of the attribute.
Because the type of the $ATTRIBUTE_LIST
is 32, it's placed usually right after the $STANDARD_INFORMATION
attribute and will contain attributes with greater types (like $FILE_NAME
or $DATA
).
When a file becomes very fragmented, the $DATA
attribute run list will not fit in a single MFT entry. This is also a case where $ATTRIBUTE_LIST
will be used to store the $DATA
attribute in multiple entries.
The $MFT
entry rarely has this problem since the allocation alogrithm is designed to prevent that. But if a $MFT
for a volume becomes very fragmented it can have more than one entry to store it's $DATA
.
tl;dr: Yes; I believe this is what ERROR_DISK_TOO_FRAGMENTED
/STATUS_MFT_TOO_FRAGMENTED
are for.
To elaborate:
The MFT file can most certainly have child records. If you need to construct one like this, just open \$MFT
(do it on a RAM disk unless you want to mess up a physical volume...) and then FSCTL_MOVE_FILE
each entry, alternating between the beginning and the end of the volume. You'll severely fragment the MFT and cause it to generate an $ATTRIBUTE_LIST
, such that it won't even fit into the last 4 of the 16 initial records anymore. It'll overflow into later slots.
Logic dictates, however, that the MFT needs to be bootstrappable. Thus I can only conclude each child described by an $ATTRIBUTE_LIST
entry must be in a slot from a previous extent. As such, it is possible to run into a situation where the volume has enough free space to grow the MFT, but no free slots to describe the MFT's next extent. I think this is one situation where the driver would return STATUS_MFT_TOO_FRAGMENTED
.
Good luck writing an efficient parser for this, it's rather tedious.
(n.b. It's possible but harder to fragment the $ATTRIBUTE_LIST
itself too. But I read that its run-list must fit within a single record, so this imposes a hard limit on the number of fragments.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With