Introduction
Keramics provides read-only access to a collection of data formats.
This document is intended as a working document of specifications of data formats used by the Keramics project. These specifications are based on available documentation and analysis of data samples.
Note that these might differ from authorative format specifications and are works in progress.
Storage media image formats
A storage media image format is used to store data from storage media devices such as a hard disk, a floppy or optical disk like CD-ROM or DVD.
Formats
- Expert Witness Compression Format (EWF)
- Expert Witness Compression Format version 2 (EWF2)
- Mac OS sparse bundle
- Mac OS sparse image
- Parallels Disk Image (PDI)
- QEMU Copy-On-Write (QCOW)
- Universal Disk Image Format (UDIF)
- Virtual Hard Disk (VHD)
- Virtual Hard Disk version 2 (VHDX)
- VMWare Virtual Disk Format (VMDK)
Expert Witness Compression Format (EWF)
EWF is short for Expert Witness Compression Format. It is a file type used to store storage media images for digital forensic purposes. It is currently widely used in the field of computer forensics in proprietary tooling like EnCase en FTK.
The “ASR Data - E01 Compression Format” is the earliest known specification of the format was provided by ASR Data for the SMART application.
The EWF format was superseded by the Expert Witness Compression Format version 2 in EnCase 7 (EWF2-Ex01 and EWF2-Lx01). EnCase 7 also uses a different version of EWF-L01 then its predecessors.
Overview
The Expert Witness Compression Format (EWF) is used to store:
- storage media images, such as hard disks, USB sticks, optical disks
- individual volumes or partitions
- “physical” RAM and process memory
EWF can store data compressed or uncompressed, in a single image in one or more segment files. Each segment file consist of a standard header, followed by multiple sections. A single section cannot span multiple files. Sections are arranged back-to-back.
Terminology
In this document when referred to the EWF format it refers to the original specification by ASR Data. The newer formats like that of EnCase are deducted from the original specification and will be referred to as the EWF-E01, because of the default file extension. Whereas the Logical File Evidence (LVF) format introduced in EnCase 5, which is also stored in the EWF format will be referred to as EWF-L01. The SMART format is viewed separately to allow for discussion if the implementation differs from the specification by ASR Data and will be referred to as the EWF-S01, because of the default file extension.
All offsets are relative to the beginning of an individual section, unless otherwise noted. EnCase allows a maximum size of a segment file to be 2000 MiB. This has to do with the size of the offset of the chunk of media data. This is a 32 bit value where the most significant bit (MSB) is used as a compression flag. Therefore the maximum offset size (31 bit) can address about 2048 MiB. In EnCase 6.7 an addition was made to the table value to provide for a base offset to allow for segment files greater than 2048 MiB.
A chunk is defined as the sector size (per default 512 bytes) multiplied by the block size, the number of sectors per chunk (block) (per default 64 sectors). The data within the EWF format is stored in little-endian. The terms block and chunk are used intermittently.
Segment file
EWF stores data in one or more segment files (or segments). Each segment file consists of:
- A file header.
- One or more sections.
File header
Each segment file starts with a file header.
EWF defines that the file header consists of 2 parts, namely:
- a signature part
- fields part
EWF, EWF-E01 and SMART (EWF-S01)
The file header, used by both the EWF-E01 and SMART (EWF-S01) formats, is 13 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "EVF\x09\x0d\x0a\xff\x00" | Signature |
| 8 | 1 | 0x01 | Start of fields |
| 9 | 2 | Segment number, which must be 1 or higher | |
| 11 | 2 | 0x0000 | End of fields |
The segment number contains a number which refers to the number of the segment file, starting with 1 for the first file.
Note this means there could only be a maximum of 65535 (0xffff) files, if it is an unsigned value.
EWF-L01
The file header, used by the EWF-L01 format, is 13 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "LVF\x09\x0d\x0a\xff\x00" | Signature |
| 8 | 1 | 0x01 | Start of fields |
| 9 | 2 | Segment number, which must be 1 or higher | |
| 11 | 2 | 0x0000 | End of fields |
The segment number contains a number which refers to the number of the segment file, starting with 1 for the first file.
Note this means there could only be a maximum of 65535 (0xffff) files, if it is an unsigned value.
Segment file extensions
The SMART (EWF-S01) and the EWF-E01 formats use a different naming convention for the segment files.
SMART (EWF-S01)
The SMART (EWF-S01) extension naming has two distinct parts.
- The first segment file has the extension ‘.s01’.
- The next segment file has the extension ’.s02.
- This will continue up to ‘.s99’.
- After which the next segment file has the extension ‘.saa’.
- The next segment file has the extension ‘.sab’.
- This will continue up to ‘.saz’.
- The next segment file has the extension ‘.sba’.
- This will continue up to ‘.szz’.
- The next segment file has the extension ‘.faa’.
- This will continue up to ‘.zzz’.
- Not confirmed but other sources report it will even continue to the use the extensions ‘.{aa’.
Keramics supports extensions up to .zzz
EWF-E01
The EWF-E01 extension naming has two distinct parts.
- The first segment file has the extension ‘.E01’.
- The next segment file has the extension ’.E02.
- This will continue up to ‘.E99’.
- After which the next segment file has the extension ‘.EAA’.
- The next segment file has the extension ‘.EAB’.
- This will continue up to ‘.EAZ’.
- The next segment file has the extension ‘.EBA’.
- This will continue up to ‘.EZZ’.
- The next segment file has the extension ‘.FAA’.
- This will continue up to ‘.ZZZ’.
- Not confirmed but other sources report it will even continue to the use the extensions ‘.[AA’.
Keramics supports extensions up to .ZZZ
EWF-L01
The EWF-L01 extension naming has two distinct parts.
- The first segment file has the extension ‘.L01’.
- The next segment file has the extension ’.L02.
- This will continue up to ‘.L99’.
- After which the next segment file has the extension ‘.LAA’.
- The next segment file has the extension ‘.LAB’.
- This will continue up to ‘.LAZ’.
- The next segment file has the extension ‘.LBA’.
- This will continue up to ‘.LZZ’.
- The next segment file has the extension ‘.MAA’.
- This will continue up to ‘.ZZZ’.
- Not confirmed but other sources report it will even continue to the use the extensions ‘.[AA’.
Keramics supports extensions up to .ZZZ
Segment file set identifier GUID
Segment file sets do not have a strict unique identifier. However the volume section contains a GUID that can be used for this purpose. Where:
- linen 5 to 6 use a time and MAC address based version (1) of the GUID
- EnCase 5 to 7 and linen 6 to 7 use a random based version (4) of the GUID
Note that in linen 6 the switch from a version 1 to 4 GUID was somewhere made between version 6.01 and 6.19.
See RFC4122 for more information about the different GUID versions.
The sections
The remainder of the segment file consists of sections. Every section starts with the same data this will be referred to as the section header.
Section header
The section header consist of 76 bytes, it contains information about a specific section.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Section type, a string containing the section type definition, such as "header" or "volume" | |
| 16 | 8 | Next section offset, where the offset is relative from the start of the segment file | |
| 24 | 8 | Section size | |
| 32 | 40 | 0x00 | Unknown (Padding) |
| 72 | 4 | Checksum, which contains an Adler-32 of all the previous data within the section header |
Some sections contain additional data, refer to paragraph section types for more information.
Note Expert Witness 1.35 (for Windows) does not set the section size.
Note that in EnCase 2 DOS version the padding itself does not contains 0-byte values but data, probably the memory is not filled with 0-byte values.
Section types
There are multiple section types. “ASR Data - E01 Compression Format” defines the following:
- Header section
- Volume section
- Table section
- Next and Done section
The following sections type were found analyzing more recent EnCase files (EWF-E01):
- Header2 section
- Disk section
- Sectors section
- Table2 section
- Data section
- Error2 section
- Session section
- Hash section
- Digest section
The following sections type were found analyzing more recent EnCase files (EWF-L01):
- Ltree section
- Ltypes section
Header2 section
The header2 section is identified in the section data type field as “header2”. Some aspects of this section are:
- Found in EWF-E01 in EnCase 4 to 7, and EWF-L01 in EnCase 5 to 7
- Found at the start of the first segment file. Not found in subsequent segment files.
- The same header2 section is found twice directly after one and other.
The additional data this section contains is the following:
| Offset | Size | Value | Description |
|---|---|---|---|
| 76 (0x4c) | ... | Information about the acquired media |
The information about the acquired media consists of zlib compressed data. It contains text in UTF16 format specifying information about the acquired media. The text multiple lines separated by an end of line character(s).
The first 2 bytes of the UTF16 string are the byte order mark (BOM):
- 0xff 0xfe for UTF-16 litte-endian
- 0xfe 0xff for UTF-16 big-endian
In the next paragraphs the various variants of the header2 section are described.
EnCase 4 (EWF-E01)
In EnCase 4 (EWF-E01) the header2 information consist of 5 lines, and contains the equivalent information as the header section.
| Line number | Value | Description |
|---|---|---|
| 1 | 1 | The number of categories provided |
| 2 | main | The name/type of the category provided |
| 3 | Identifiers for the values in the 4th line | |
| 4 | The data for the different identifiers in the 3rd line | |
| 5 | (an empty line) |
The end of line character(s) is a newline (0x0a).
Note this end of line character differs from the one used in the header section.
The 3rd and the 4th line consist of the following tab (0x09) separated values.
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | a | Unique description |
| 2 | c | Case number |
| 3 | n | Evidence number |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | av | Version, which contains the EnCase version used to acquire the media |
| 7 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 8 | m | Acquisition date and time |
| 9 | u | System date and time |
| 10 | p | Password hash |
Also see header2 values
Note the hashing algorithm is the same as for the header section.
EnCase 5 to 7 (EWF-E01)
In EnCase 5 to 7 (EWF-E01) the header2 information consist of 17 lines, and contains:
| Line number | Value | Description |
|---|---|---|
| 1 | 3 | The number of categories provided |
| 2 | main | The name/type of the category provided |
| 3 | Identifier for the values in the category | |
| 4 | The data for the different identifiers in the category | |
| 5 | (an empty line) | |
| 6 | srce | The name/type of the category provided, also see sources category |
| 7 | ||
| 8 | Identifier for the values in the category | |
| 9 | The data for the different identifiers in the category | |
| 10 | ||
| 11 | (an empty line) | |
| 12 | sub | The name/type of the category provided, also see subjects category |
| 13 | ||
| 14 | Identifier for the values in the category | |
| 15 | The data for the different identifiers in the category | |
| 16 | ||
| 17 | (an empty line) |
The end of line character(s) is a newline (0x0a).
Main category
The 3rd and the 4th line consist of the following tab (0x09) separated values.
Note the actual values in this category are dependent on the version of EnCase.
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | a | Unique description |
| 2 | c | Case number |
| 3 | n | Evidence number |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | md | The model of the media, such as hard disk model (introduced in EnCase 6) |
| 7 | sn | The serial number of media (introduced in EnCase 6) |
| 8 | l | The device label (introduced in EnCase 6.19) |
| 9 | av | Version, which contains the EnCase version used to acquire the media. EnCase limits this value to 12 characters |
| 10 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 11 | m | Acquisition date and time |
| 12 | u | System date and time |
| 13 | p | Password hash |
| 14 | pid | Process identifier, which contains the identifier of the process memory acquired (introduced in EnCase 6.12/Winen 6.11) |
| 15 | dc | Unknown |
| 16 | ext | Extents, which contains the extents of the process memory acquired (introduced in EnCase 6.12/Winen 6.11) |
Also see header2 values
Note that both the acquiry and system date and time are empty in a file created by winen.
Note that the date values in the header section (not the header2 section) are set to: “Thu Jan 1 00:00:00 1970”. Where the time is dependent on the time zone and daylight savings.
Note that in a Logicube Dossier generated header2 section an additional emtpy value in the 4th line was observed. The number of values in the 3rd and 4th can differ.
Sources category
Line 6 the srce category contains information about acquisition sources.
TODO: describe what a source is in the context of EnCase.
Line 7 consists of 2 values, namely the values are “0 1”.
The 8th line consist of the following tab (0x09) separated values.
Note that the actual values in this category are dependent on the version of EnCase.
| Identifier number | Character in 8rd line | Meaning |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the source |
| 4 | ev | Evidence number, which contains a string |
| 5 | tb | Total bytes, which contains an integer |
| 6 | lo | Logical offset, which contains an integer which is -1 when value is not set |
| 7 | po | Physical offset, which contains an integer which is -1 when value is not set |
| 8 | ah | MD5 hash, which contains a string with the MD5 hash of the source |
| 9 | sh | SHA1 hash, contains a string with the SHA1 hash of the source (introduced in EnCase 6.19) |
| 10 | gu | Device GUID, which contains a string with a GUID or "0" if not set |
| 11 | pgu | Primary device GUID, which contains a string with a GUID or "0" if not set (introduced in EnCase 7) |
| 12 | aq | Acquisition date and time, which contains an integer with a POSIX timestamp |
Line 9 consists of 2 values, namely the values are “0 0”.
Line 10 contains the values defined by line 8.
Note the default values of some of these values has changed around EnCase 6.12.
If the “ha” value contains “00000000000000000000000000000000” this means the MD5 hash is not set. The same applies for the “sha” value when it contains “0000000000000000000000000000000000000000” the SHA1 has is not set.
Subjects category
Line 12 the sub category contains information about subjects.
TODO: describe what a subject is in the context of EnCase.
Line 13 consists of 2 values, namely the values are “0 1”.
The 14th line consist of the following tab (0x09) separated values.
| Identifier number | Character in 14rd line | Meaning |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the subject |
| 4 | nu | Unknown (Number) |
| 5 | co | Unknown (Comment) |
| 6 | gu | Unknown (GUID) |
Line 15 consists of 2 values, namely the values are “0 0”.
Line 16 contains the values defined by line 14.
Note that the default values of some of these values has changed around EnCase 6.12.
EnCase 5 to 7 (EWF-L01)
The EnCase 5 to 7 (EWF-E01) header2 section specification also applies to the EnCase 5 to 7 (EWF-L01) format. However:
- both the acquired and system date and time are not set
Header2 values
| Identifier | Description | Notes |
|---|---|---|
| a | Unique description | Free form string. Note that EnCase might not respond when this value is large e.g. >= 1 MiB |
| av | Version | Free form string. EnCase limits this string to 12 - 1 characters |
| c | Case number | Free form string. EnCase limits this string to 3000 - 1 characters |
| dc | Unknown | |
| e | Examiner name | Free form string. EnCase limits this string to 3000 - 1 characters |
| ext | Extents | Extents header value |
| l | Device label | Free form string |
| m | Acquisition date and time | String containing POSIX 32-bit epoch timestamp, e.g. "1142163845" which represents the date: March 12 2006, 11:44:05 |
| md | Model | Free form string. EnCase limits this string to 3000 - 1 characters |
| n | Evidence number | Free form string. EnCase limits this string to 3000 - 1 characters |
| ov | Platform | Free form string. EnCase limits this string to 24 - 1 characters |
| pid | Process identifier | String containing the process identifier (pid) number |
| p | Password hash | String containing the password hash. If no password is set it should be simply the character '0' |
| sn | Serial Number | Free form string. EnCase limits this string to 3000 - 1 characters |
| t | Notes | Free form string. EnCase limits this string to 3000 - 1 characters |
| u | System date and time | String containing POSIX 32-bit epoch timestamp, e.g. "1142163845" which represents the date: March 12 2006, 11:44:05 |
Note the restrictions were tested with EnCase 7.02.01, older versions could have a restriction of 40 characters instead of 3000 characters.
Extents header value
An extents header value consist of:
number of entries
entries that consist of: S <1> <2> <3>
Header section
The header section is identified in the section data type field as “header”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”
- Found in EWF-E01 in EnCase 1 to 7 or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7, and SMART (EWF-S01)
- Found at the start of the first segment file or in EnCase 4 to 7 after the header2 section in the first segment file. Typically not found in subsequent segment files with the exception of Logicube Dossier generated EWF-E01 files.
The additional data this section contains is the following:
| Offset | Size | Value | Description |
|---|---|---|---|
| 76 (0x4c) | ... | Information about the acquired media |
The information about the acquired media consists of zlib compressed data. It contains text in ASCII format specifying information about the acquired media. The text multiple lines separated by an end of line character(s).
In the next paragraphs the various variants of the header section are described. In all cases the information consists of at least 4 lines:
| Line number | Value | Description |
|---|---|---|
| 1 | 1 | The number of categories provided |
| 2 | main | The name/type of the category provided |
| 3 | Identifiers for the values in the 4th line | |
| 4 | The data for the different identifiers in the 3rd line |
An additional 5th line is found in FTK Imager, EnCase 1 to 7 (EWF-E01).
| Line number | Value | Description |
|---|---|---|
| 5 | (an empty line) |
EWF format
Some aspects of this section are:
- “ASR Data - E01 Compression Format” specifies the end of line character(s) is a newline (0x0a).
According to “ASR Data - E01 Compression Format” the 3rd and the 4th line consist of the following tab (0x09) separated values:
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | c | Case number |
| 2 | n | Evidence number |
| 3 | a | Unique description |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | m | Acquisition date and time |
| 7 | u | System date and time |
| 8 | p | Password hash |
| 9 | r | Compression level |
Also see header values
“ASR Data - E01 Compression Format” states that the Expert Witness Compression uses ‘f’, fastest compression.
EnCase 1 (EWF-E01)
Some aspects of this section are:
- The header section is defined only once.
- It is the first section of the first segment file. It is not found in subsequent segment files.
- The header data itself is compressed using zlib.
- The end of line character(s) is a carriage return (0x0d) followed by a newline (0x0a).
The 3rd and the 4th line consist of the following tab (0x09) separated values“
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | c | Case number |
| 2 | n | Evidence number |
| 3 | a | Unique description |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | m | Acquisition date and time |
| 7 | u | System date and time |
| 8 | p | Password hash |
| 9 | r | Compression level |
Also see header values
SMART (EWF-S01)
Some aspects of this section are:
- The header section is defined once.
- It is the first section of the first segment file. It is not found in subsequent segment files.
- The header data is always processed by zlib, however the same compression level is used as for the chunks. This could mean compression level 0 which is no compression.
The SMART format uses the FTK Imager (EWF-E01) specification for this section. Note that this could be something FTK Imager specific.
EnCase 2 and 3 (EWF-E01)
Some aspects of this section are:
- The same header section defined twice.
- It is the first and second section of the first segment file. It is not found in subsequent segment files.
- The header data itself is compressed using zlib.
- The end of line character(s) is a carriage return (0x0d) followed by a newline (0x0a).
The 3rd and the 4th line consist of the following tab (0x09) separated values:
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | c | Case number |
| 2 | n | Evidence number |
| 3 | a | Unique description |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | av | Version, which contains the EnCase version used to acquire the media |
| 7 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 8 | m | Acquisition date and time |
| 9 | u | System date and time |
| 10 | p | Password hash |
| 11 | r | Compression level |
Also see header values
EnCase 4 to 7 (EWF-E01)
Some aspects of this section are:
- The header is defined only once.
- It resides after the header2 sections of the first segment file. It is not found in subsequent segment files.
- The header data itself is compressed using zlib.
- The end of line character(s) is a carriage return (0x0d) followed by a newline (0x0a).
The 3rd and the 4th line consist of the following tab (0x09) separated values:
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | c | Case number |
| 2 | n | Evidence number |
| 3 | a | Unique description |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | av | Version, which contains the EnCase version used to acquire the media |
| 7 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 8 | m | Acquisition date and time |
| 9 | u | System date and time |
| 10 | p | Password hash |
Also see header values
linen 5 to 7 (EWF-E01)
Some aspects of this section are:
- The same header section defined twice.
- It is the first and second section of the first segment file. It is not found in subsequent segment files.
- The header data itself is compressed using zlib.
- The end of line character(s) is a newline (0x0a).
The header information consist of 18 lines
The remainder of the string contains the following information:
| Line number | Value | Description |
|---|---|---|
| 1 | 3 | The number of categories provided |
| 2 | main | The name/type of the category provided |
| 3 | Identifier for the values in the 4th line | |
| 4 | The data for the different identifiers in the 3rd line | |
| 5 | (an empty line) | |
| 6 | srce | The name/type of the section provided, also see Sources category |
| 7 | ||
| 8 | Identifier for the values in the section | |
| 9 | ||
| 10 | ||
| 11 | (an empty line) | |
| 12 | sub | The name/type of the section provided, also see Subjects category |
| 13 | ||
| 14 | Identifier for the values in the section | |
| 15 | ||
| 16 | ||
| 17 | (an empty line) |
The end of line character(s) is a newline (0x0a).
Main category - linen 5
The 3rd and the 4th line consist of the following tab (0x09) separated values.
Note the actual values in this category are dependent on the version of linen.
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | a | Unique description |
| 2 | c | Case number |
| 3 | n | Evidence number |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | av | Version, which contains the linen version used to acquire the media |
| 7 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 8 | m | Acquisition date and time |
| 9 | u | System date and time |
| 10 | p | Password hash |
Also see header values
Main category - linen 6 to 7
The 3rd and the 4th line consist of the following tab (0x09) separated values.
Note the actual values in this category are dependent on the version of linen.
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | a | Unique description |
| 2 | c | Case number |
| 3 | n | Evidence number |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | md | The model of the media, such as hard disk model (Introduced in linen 6) |
| 7 | sn | The serial number of media (Introduced in linen 6) |
| 8 | l | The device label (Introduced in linen 6.19) |
| 9 | av | Version, which contains the linen version used to acquire the media |
| 10 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 11 | m | Acquisition date and time |
| 12 | u | System date and time |
| 13 | p | Password hash |
| 14 | pid | Process identifier, which contains the identifier of the process memory acquired (Introduced in linen 6.19 or earlier) |
| 15 | dc | Unknown (Introduced in linen 6) |
| 16 | ext | Extents, which contains the extents of the process memory acquired (Introduced in linen 6.19 or earlier) |
Note as of linen 6.19 the acquire date and time is in UTC and the system date and time is in local time. Where as before both values were in local time.
Also see header values
Sources category
Line 6 the srce category contains information about acquisition sources
TODO: describe what a source is in the context of EnCase.
Line 7 consists of 2 values, namely the values are “0 1”.
The 8th line consist of the following tab (0x09) separated values.
| Identifier number | Character in 8rd line | Meaning |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the source |
| 4 | ev | Evidence number, which contains a string |
| 5 | tb | Total bytes, which contains an integer |
| 6 | lo | Logical offset, which contains an integer which is -1 when value is not set |
| 7 | po | Physical offset, which contains an integer which is -1 when value is not set |
| 8 | ah | Unknown (MD5?), which contains a string |
| 9 | sh | Unknown (SHA1?), which contains a string (Introduced in linen 6.19 or earlier) |
| 10 | gu | Device GUID, which contains a string with a GUID or "0" if not set |
| 11 | aq | Acquisition date and time, which contains an integer with a POSIX timestamp |
Line 9 consists of 2 values, namely the values are “0 0”.
Line 10 contains the values defined by line 8.
Note the default values of some of these values has changed around linen 6.19 or earlier.
Subjects category
Line 12 the sub category contains information about subjects.
TODO: describe what a subject is in the context of EnCase.
Line 13 consists of 2 values, namely the values are “0 1”.
The 14th line consist of the following tab (0x09) separated values.
| Identifier number | Character in 14rd line | Meaning |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the subject |
| 4 | nu | Unknown (Number) |
| 5 | co | Unknown (Comment) |
| 6 | gu | Unknown (GUID) |
Line 15 consists of 2 values, namely the values are “0 0”.
Line 16 contains the values defined by line 14.
Note the default values of some of these values has changed around linen 6.19 or earlier.
FTK Imager (EWF-E01)
Some aspects of this section are:
- In FTK Imager (EWF-E01) the same header section defined twice.
- It is the first and second section of the first segment file. It is not found in subsequent segment files.
- The header data itself is compressed using zlib. Note that the compression level can be none and therefore the header looks uncompressed.
- In FTK Imager the end of line character(s) is a newline (0x0a).
The 3rd and the 4th line consist of the following tab (0x09) separated values:
| Identifier number | Character in 3rd line | Value in 4th line |
|---|---|---|
| 1 | c | Case number |
| 2 | n | Evidence number |
| 3 | a | Unique description |
| 4 | e | Examiner name |
| 5 | t | Notes |
| 6 | av | Version, which contains the FTK Imager version used to acquire the media |
| 7 | ov | Platform, which contains the platform/operating system used to acquire the media |
| 8 | m | Acquisition date and time |
| 9 | u | System date and time |
| 10 | p | Password hash |
| 11 | r | Compression level |
Also see header values
EnCase 5 to 7 (EWF-L01)
The EnCase 4 to 7 (EWF-E01) header section specification is also used for the EnCase 5 to 7 (EWF-L01) format, with the following aspects:
- In EnCase 5 both the acquired and system date and time are set to 0.
- In EnCase 6 and 7 both the acquired and system date and time are set to Jan 1, 1970 00:00:00 (the time is dependent on the local timezone and daylight savings)
Header values
| Identifier | Description | Notes |
|---|---|---|
| a | Unique description | Free form string. Note that EnCase might not respond when this value is large e.g. >= 1 MiB |
| av | Version | Free form string. EnCase limits this string to 12 - 1 characters |
| c | Case number | Free form string. EnCase limits this string to 3000 - 1 characters |
| dc | Unknown | |
| e | Examiner name | Free form string. EnCase limits this string to 3000 - 1 characters |
| ext | Extents | Extents header value |
| l | Device label | Free form string |
| m | Acquisition date and time | Contains a date and time header value |
| md | Model | Free form string. EnCase limits this string to 3000 - 1 characters |
| n | Evidence number | Free form string. EnCase limits this string to 3000 - 1 characters |
| ov | Platform | Free form string. EnCase limits this string to 24 -1 characters |
| pid | Process identifier | String containing the process identifier (pid) number |
| p | Password hash | String containing the password hash. If no password is set it should be simply the character '0' |
| r | Compression level | Compression header value |
| sn | Serial Number | Free form string. EnCase limits this string to 3000 - 1 characters |
| t | Notes | Free form string. EnCase limits this string to 3000 - 1 characters |
| u | Systemdate and time | Contains a date and time header value |
Note the restrictions were tested with EnCase 7.02.01, older versions could have a restriction of 40 characters instead of 3000 characters.
Date and time header value
In EnCase a date and time contains a string of individual values separated by a space, e.g. “2002 3 4 10 19 59”, which represents March 4, 2002 10:19:59.
In linen a date and time contains a string with a POSIX 32-bit epoch timestamp, e.g. “1142163845” which represents the date: March 12 2006, 11:44:05
Extents header value
An extents header value consist of:
number of entries
entries that consist of: S <1> <2> <3>
Compression header value
A compression header value consist of a single character that represent the compression level.
| Character value | Meaning |
|---|---|
| b | Best compression is used |
| f | Fastest compression is used |
| n | No compression is used |
Notes
There should not be a tab, carriage return and newline characters within the text in the 4th line. Or is there a method to escape these characters?
“ASR Data - E01 Compression Format” states that these characters should not be used in the free form text. Need to confirm this, the specification only speaks of a newline character.
Currently the password has no a additional value than allow an application check it. The data itself is not protected using the password. The password hashing algorithm is unknown. Need to find out. And does the algorithm differ per EnCase version? probably not. The algorithm does not differ in EnCase 1 to 7. FTK Imager does not bother with a password.
Volume section
The volume section is identified in the section data type field as “volume”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”
- Found in EWF-E01 in EnCase 1 to 7 or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7, and SMART (EWF-S01)
- Found after the header section of the first segment file. Not found in subsequent segment files.
In the next paragraphs the various versions of the volume section are described.
EWF specification
The specification according to “ASR Data - E01 Compression Format”.
The volume section data is 94 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0x01 | Unknown (Reserved) |
| 4 | 4 | The number of chunks within the all segment files | |
| 8 | 4 | The number of sectors per chunk, which contains 64 per default | |
| 12 | 4 | The number of bytes per sectors, which contains 512 per default | |
| 16 | 4 | The sectors count, the number of sectors within all segment files | |
| 20 | 20 | 0x00 | Unknown (Reserved) |
| 40 | 45 | 0x00 | Unknown (Padding) |
| 85 | 5 | Signature, which contains the EWF file header signature | |
| 90 | 4 | Checksum, which contains an Adler-32 of all the previous data within the volume section data |
The number of chunks is a 32-bit value this means it maximum of addressable chunks would be:
4294967295 (= 2^32 - 1). For a chunk size of 32768 x 4294967295, which is about 127 TiB. The
maximum segment file amount is 2^16 - 1 = 65535. This allows for an equal number of storage if a
segment file is filled to its maximum number of chunks.
However Keramics is restricted at 14295 segment files, due to the extension naming schema of the segment files.
SMART (EWF-S01)
The SMART format uses the EWF specification for this section.
In SMART the signature (reverse) value is the string “SMART” (0x53 0x4d 0x41 0x52 0x54) instead of the file header signature.
FTK Imager, EnCase 1 to 7 and linen 5 to 7 (EWF-E01)
The specification for FTK Imager, EnCase 1 to 7 and linen 5 to 7.
The volume section data is 1052 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Media type | |
| 1 | 3 | 0x00 | Unknown (empty values) |
| 4 | 4 | The number of chunks within the all segment files | |
| 8 | 4 | The number of sectors per chunk (or block size), which contains 64 per default. EnCase 5 is the first version which allows this value to be different than 64 | |
| 12 | 4 | The number of bytes per sector | |
| 16 | 8 | The sectors count, which contains the number of sectors within all segment files. This value probably has been changed in EnCase 6 from a 32-bit value to a 64-bit value to support media >2TiB | |
| 24 | 4 | The number of cylinders of the C:H:S value, which most of the time this value is empty (0x00) | |
| 28 | 4 | The number of heads of the C:H:S value, which most of the time this value is empty (0x00) | |
| 32 | 4 | The number of sectors of the C:H:S value, which most of the time this value is empty (0x00) | |
| 36 | 1 | Media flags | |
| 37 | 3 | 0x00 | Unknown (empty values) |
| 40 | 4 | PALM volume start sector | |
| 44 | 4 | 0x00 | Unknown (empty values) |
| 48 | 4 | SMART logs start sector, which contains an offset relative from the end of media, e.g. a value of 10 would refer to sector = number of sectors - 10 | |
| 52 | 1 | Compression level (Introduced in EnCase 5) | |
| 53 | 3 | 0x00 | Unknown (empty values, these values seem to be part of the compression level) |
| 56 | 4 | The sector error granularity, which contains the error block size (Introduced in EnCase 5) | |
| 60 | 4 | 0x00 | Unknown (empty values) |
| 64 | 16 | Segment file set identifier, which contains a GUID/UUID generated on the acquiry system probably used to uniquely identify a set of segment files (Introduced in EnCase 5) | |
| 80 | 963 | 0x00 | Unknown (empty values) |
| 1043 | 5 | 0x00 | Unknown (Signature) |
| 1048 | 4 | Checksum, which contains an Adler-32 of all the previous data within the volume section data |
TODO: a value that could be in the volume is the RAID stripe size
Note that EnCase requires for media that contains no partition table that the is physical media flag is not set and vice versa. Other tools like FTK check the actual storage media data.
EnCase 5 to 7 (EWF-L01)
The EWF-L01 format uses the EnCase 5 (EWF-E01) volume section specification. However:
- the volume type contains 0x0e
- the number of chunks is 0
- the number of bytes per sectors is some kind of block size value (4096), perhaps the source file system block size
- the sectors count, represents some other value because (sector_size x sector_amount != total_size). The total size is in the ltree section.
Media type
| Value | Identifier | Description |
|---|---|---|
| 0x00 | A removable storage media device | |
| 0x01 | A fixed storage media device | |
| 0x03 | An optical disc (CD/DVD/BD) | |
| 0x0e | Logical Evidence (LEF or L01) | |
| 0x10 | Physical Memory (RAM) or process memory |
Note that FTK imager versions, before version 2.9, set the storage media to fixed (0x01). The exact version of FTK imager where this behavior changed is unknown.
Media flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | Is an image file. In FTK Imager, EnCase 1 to 7 this bit is always set, when not set EnCase seems to see the image file as a device | |
| 0x02 | Is physical device or device type, where 0 represents a non physical device (logical) and 1 represents a physical device | |
| 0x04 | Fastbloc write blocker used | |
| 0x08 | Tableau write blocker used. This was added in EnCase 6.13 |
Note that if both the the Fastbloc and Tableau write blocker media flags are set EnCase only shows the Fastbloc.
Compression level
| Value | Identifier | Description |
|---|---|---|
| 0x00 | no compression | |
| 0x01 | good compression | |
| 0x02 | best compression |
Note that EnCase 7 no longer provides the fast and best compression options.
Disk section
The disk section is identified in the section data type field as “disk”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- Not found in SMART (EWF-S01).
With a disk section in an FTK Imager 2.3 (EWF-E01) image it was confirmed that the disk section is the same as the volume section.
Note that the disk section was found only in FTK Imager 2.3 when acquiring a physical disk not a floppy. This requires additional research, it is currently assumed that the disk section some old method to differentiate between a partition (volume) image or a physical disk image.
Data section
The data section is identified in the section data type field as “data”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- Found in EWF-E01 in EnCase 1 to 7 or linen 5 to 7 or FTK Imager, and EWF-L01 in EnCase 5 to 7. Not found in SMART (EWF-S01).
- For multiple segment files it does not reside in the first segment file. For a single segment file it does.
- Found after the last table2 section in a single segment file or for multiple segment files at the start of the segment files, except for the first.
- The data section has data it should should contain the same information as the volume section.
The data section is a copy of the volume section.
FTK Imager, EnCase 1 to 7 and linen 5 to 7 (EWF-E01)
Note that in Logicube products (Talon (firmware predating April 2013) and Forensic dossier (before version 3.3.3RC16)) the checksum is not calculated and set to 0.
Sectors section
The sectors section is identified in the section data type field as “sectors”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- Found in EWF-E01 in EnCase 2 to 7, or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7. Not found in EnCase 1 (EWF-E01) or SMART (EWF-S01).
- The first sectors section can be found after the volume section in the first segment file or at the after the data section in subsequent segment files. Successive sector data sections are found after the sector table2 section.
The sectors section contains the actual chunks of media data.
- The sectors section can contain multiple chunks.
- The default size of a chunk is 32768 bytes of data (64 standard sectors, with a size of 512 bytes per sector). It is possible in EnCase 5 and 6 and linen 5 and 6 to change the number of sectors per block to 64, 128, 256, 1024, 2048, 4096, 8192, 16384 or 32768. In EnCase 7 and linen 7 this has been reduced to 64, 128, 256, 1024.
Data chunk
The first chunk is often located directly after the section header, although the format does not require this.
When the data is compressed and the compressed data (with checksum) is larger than the uncompressed data (without the checksum) the data chunk is stored uncompressed. The default size of a chunk is 32768 bytes of data (64 standard sectors).
An uncompressed data chunk is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Uncompressed chunk data | |
| ... | 4 | Checksum, which contains an Adler-32 of the chunk data |
The compressed data chunk consist of zlib compressed data. The checksum of the compressed data chunk is part the zlib compressed data format.
Optical disc images
For a MODE-1 CD-ROM optical disc image EnCase only seems to support 2048 bytes per sector (the data).
The raw sector size of a MODE-1 CD-ROM is 2352 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Synchronization bytes | |
| 16 | 2048 | Data | |
| 2054 | 4 | Error detection | |
| 2058 | 8 | 0x00 | Unknown (Empty values) |
| 2066 | 276 | Error correction |
TODO: add information about Mode-2 and Mode-XA
Table section
The table section is identified in the section data type field as “table”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”.
- Found in EWF-E01 in EnCase 1 to 7 or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7, and SMART (EWF-S01)
Note that the offsets within the section header are 8 bytes (64 bits) of size while the offsets in the table entry array are 4 bytes (32 bits) in size.
In the next paragraphs the various versions of the table section are described.
EWF specification
Some aspects of the table section according to the EWF specification are:
- The first table section resides after the volume section in the first segment file or after the file header in subsequent segment files.
- It can be found in every segment file.
The table section consists of:
- the table header
- an array of table entries
- the data chunks
Table header
The table header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | The number of entries | |
| 4 | 16 | 0x00 | Unknown (Padding) |
| 20 | 4 | Checksum, which contains an Adler-32 of all the previous data within the table header data |
According to “ASR Data - E01 Compression Format”:
- the number of entries, contains 0x01
- the table can hold 16375 entries if more entries are required an additional table section should be created.
Table entry
The table entry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Chunk data offset |
The most significant bit (MSB) in the chunk data offset indicates if the chunk is compressed (1) or uncompressed (0).
A chunk data offset points to the start of the chunk of media data, which resides in the same table section within the segment file. The offset contains a value relative to the start of the file.
Data chunk
The first chunk is often located directly after the last table entry, although the format does not require this.
A data chunk is always compressed even when no compression is required. This approach provides a checksum for each chunk. The default size of a chunk is 32768 bytes of data (64 standard sectors). The resulting size of the “compressed” chunk can therefore be larger than the default chunk size.
Note that this was deducted from the behavior of FTK Imager for SMART (EWF-S01).
The compressed data chunk consist of zlib compressed data. The checksum of the compressed data chunk is part the zlib compressed data format.
SMART (EWF-S01)
The table section in the SMART (EWF-S01) format is equivalent to that of the EWF specification.
EnCase 1 (EWF-E01)
Some aspects of this section are:
- The table section resides after the volume section in the first segment file or after the file header in subsequent segment files.
- It can be found in every segment file.
The table section consists of:
- the table header
- an array of table entries
- the table footer
- the data chunks
Table header
The table header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | The number of entries | |
| 4 | 16 | 0x00 | Unknown (Padding) |
| 20 | 4 | Checksum, which contains an Adler-32 of all the previous data within the table header data |
The table can hold 16375 entries if more entries are required an additional table section should be created.
Table entry
The table entry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Chunk data offset |
The most significant bit (MSB) in the chunk data offset indicates if the chunk is compressed (1) or uncompressed (0).
A chunk data offset points to the start of the chunk of media data, which resides in the same table section within the segment file. The offset contains a value relative to the start of the file.
Table footer
The table footer is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains an Adler-32 of the offset array |
Data chunk
The first chunk is often located directly after the table footer, although the format does not require this.
When the data is compressed and the compressed data (with checksum) is larger than the uncompressed data (without the checksum) the data chunk is stored uncompressed. The default size of a chunk is 32768 bytes of data (64 standard sectors).
An uncompressed data chunk is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Uncompressed chunk data | |
| ... | 4 | Checksum, which contains an Adler-32 of the chunk data |
The compressed data chunk consist of zlib compressed data. The checksum of the compressed data chunk is part the zlib compressed data format.
FTK Imager and EnCase 2 to 5 and linen 5 (EWF-E01)
Some aspects of this section are:
- The table section resides after the sectors section.
- It can be found in every segment file.
- The data chunks are no longer stored in this section but in the sectors section instead.
- The table2 section contains a mirror copy of the table section. In EWF-E01 it is always present.
The table section consists of:
- the table header
- an array of table entries
- the table footer
Table header
The sector table header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | The number of entries | |
| 4 | 16 | 0x00 | Unknown (Padding) |
| 20 | 4 | Checksum, which contains an Adler-32 of all the previous data within the table header data |
The table section can hold 16375 entries. A new table section should be created to hold more entries. Both FTK Imager and EnCase 5 can handle more than 16375, FTK 1 cannot. To contain more than 16375 chunks new sectors, table and table2 sections need to be created after the table2 section.
Table entry
The table entry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Chunk data offset |
The most significant bit (MSB) in the chunk data offset indicates if the chunk is compressed (1) or uncompressed (0).
A chunk data offset points to the start of the chunk of media data, which resides in the preceding sectors section within the segment file. The offset contains a value relative to the start of the file.
Table footer
The table footer is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains an Adler-32 of the offset array |
EnCase 6 to 7 and linen 6 to 7 (EWF-E01)
Some aspects of this section are:
- Every segment file contains its own table section.
- It resides after the sectors section.
- The data chunks are no longer stored in this section but in the sectors section instead.
- The table2 section contains a mirror copy of the table section. In EWF-E01 it is always present.
The table section consists of:
- the table header
- an array of table entries
- the table footer
Table header
The sector table header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | The number of entries | |
| 4 | 4 | 0x00 | Unknown (Padding) |
| 8 | 8 | The table base offset | |
| 16 | 4 | 0x00 | Unknown (Padding) |
| 20 | 4 | Checksum, which contains an Adler-32 of all the previous data within the table header data |
As of EnCase 6 the number of entries is no longer restricted to 16375 entries. The new limit seems to be 65534.
Table entry
The table entry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Chunk data offset |
The most significant bit (MSB) in the chunk data offset indicates if the chunk is compressed (1) or uncompressed (0).
A chunk data offset points to the start of the chunk of media data, which resides in the preceding sectors section within the segment file. The offset contains a value relative to the table base offset.
In EnCase 6.7.1 the sectors section can be larger than 2048Mb. The table entries offsets are 31 bit values in EnCase6 the offset in a table entry value will actually use the full 32 bit if the 2048Mb has been exceeded. This behavior is no longer present in EnCase 6.8 so it is assumed to be a bug. Keramics currently assumes that the if the 31 bit value overflows the following chunks are uncompressed. This allows EnCase 6.7.1 faulty EWF files to be converted.
Table footer
The table footer is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains an Adler-32 of the offset array |
EnCase 6 to 7 (EWF-L01)
The EWF-L01 format uses the EnCase 6 to 7 (EWF-E01) table section specification.
Table2 section
The table2 section is identified in the section data type field as “table2”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- Found in EWF-E01 in EnCase 2 to 7, or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7. Not found in EnCase 1 (EWF-E01) or SMART (EWF-S01).
- Uses the same format as the table section.
- Resides directly after the table section.
FTK Imager and EnCase 2 to 7 and linen 5 to 7 (EWF-E01)
The table2 section contains a mirror copy of the table section. Probably intended for recovery purposes.
EnCase 5 to 7 (EWF-L01)
The EWF-L01 format uses the EWF-E01 table2 section specification.
Next section
The next section is identified in the section data type field as “next”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”.
- Found in EWF-E01 in EnCase 1 to 7 or linen 5 to 7 or FTK Imager, EWF-L01 in EnCase 5 to 7, and SMART (EWF-S01)
- The last section within a segment other than the last segment file.
- The offset to the next section in the section header of the next section point to itself (the start of the next section).
- It should be the last section in a segment file, other than the last segment file.
SMART (EWF-S01)
It resides after the table or table2 section.
FTK Imager, EnCase and linen (EWF-E01)
It resides after the data section in a single segment file or for multiple segment files after the table2 section.
In the EnCase (EWF-E01) format the size in the section header is 0 instead of 76 (the size of the section header).
Note that FTK imager versions before 2.9 sets the section size to 76. At the moment it is unknown in which version this behavior was changed.
Ltypes section
The ltypes section is identifier in the section data type field as “ltypes”. Some aspects of this section are:
- Found in EWF-L01 in of EnCase 7
- Found in the last segment file after table2 section before tree section.
The additional ltypes section data is 6 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Unknown | |
| 2 | 2 | Unknown | |
| 4 | 2 | Unknown |
Ltree section
The ltree section is identifier in the section data type field as “ltree”. Some aspects of this section are:
- Found in EWF-L01 in of EnCase 5 to 7
- Found in the last segment file after ltypes section and before data section.
The ltree section consists of:
- ltree header
- ltree data
Ltree header
The ltree header is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Integrity hash, which contains the MD5 of the ltree data | |
| 16 | 8 | Data size | |
| 24 | 4 | Checksum, which contains an Adler-32 of all the data within the ltree header where the checksum value itself is zeroed out | |
| 28 | 20 | Unknown (empty values) |
Ltree data
The ltree data string consists of an UTF-16 little-endian encoded string without byte order mark. The ltree data is not strict UTF-16 since it allows for unpaired surrogates, such as “U+d800” and “U+dc00”.
Other observed characteristics where the names in the ltree deviate from the original source:
- [U+0001-U+0008] were converted to U+00ba
- [U+0009, U+000a] were stripped
- [U+000b, U+000c] were converted to U+0020
- U+000d was converted to U+0002
- U+00ba remained the same
Note that this behavior could be related to EnCase as well and might not be specific for EWF-L01.
The ltree data string contains the following information:
| Line number | Value | Description |
|---|---|---|
| 1 | 5 | The number of categories provided |
| 2 | rec | Information about unknown, also see Records category |
| ... | (an empty line) | |
| ... | perm | Information about file permissions, also see Permissions category |
| ... | (an empty line) | |
| ... | srce | Information about acquisition sources, also see sources category |
| ... | (an empty line) | |
| ... | sub | Information about unknown, also see subjects category |
| ... | (an empty line) | |
| ... | entry | Information about file entries, also see File entries category |
| ... | (an empty line) |
The end of line character(s) is a newline (0x0a).
Records category
The rec category contains information about records.
The 1st line of the category contains the string “rec”.
The 2nd line of the category contains tab (0x09) separated type indicators.
| Identifier number | Type indicator | Description |
|---|---|---|
| 1 | tb | Total bytes, which contains an integer with size of the logical file data (media data) |
| 2 | cl | Unknown (Clusters?) |
| 3 | n | Unknown (introduced in EnCase 6.19) |
| 4 | fp | Unknown (introduced in EnCase 7) |
| 5 | pg | Unknown (introduced in EnCase 7) |
| 6 | lg | Unknown (introduced in EnCase 7) |
| 7 | ig | Unknown (introduced in EnCase 7) |
The 3rd line of the category consist of the tab (0x09) separated values.
Permissions category
The perm category contains information about file permissions.
The 1st line of the category contains the string “perm”.
The 2nd line consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | The number of permission groups in the category | |
| 2 | 1 | Unknown |
The 3rd line of the category contains tab (0x09) separated type indicators. For more information see the sections below.
The remaining lines in the category consist of:
- category root entry
- zero or more permissions group entries
- zero or more permission entries
- zero or more permissions group entries
Each entry consist of 2 lines:
| Line number | Value | Description |
|---|---|---|
| 1 | Number of entries | |
| 2 | Tab (0x09) separated values that correspond to the type indicators |
The 1st line of the category root entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | The number of permission groups in the category |
The 1st line of the permission group entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | The number of permissions in the group |
The 1st line of the permission entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | 0 | Unknown |
Permission type indicators
| Identifier number | Type indicator | Description |
|---|---|---|
| 1 | p | Is parent, where 1 represents if the entry is a category root or permissions group and 0 represents if the entry is a permission |
| 2 | n | Name, which contains a string |
| 3 | s | Security identifier, which contains a string with either a Windows NT security identifier (SID) or a POSIX user (uid) or group identifier (gid) in the format " number:" such as " 99:" |
| 4 | pr | Property type, also see permission types |
| 5 | nta | Access mask |
| 6 | nti | Unknown (Windows NT access control entry (ACE) flags?, which contains an integer with a Windows NT access control entry (ACE) flags) |
| 7 | nts | Unknown (Permission?) (Removed in EnCase 6) |
Permission types
| Value | Identifier | Description |
|---|---|---|
| (empty) | Owner or category root | |
| 1 | Group | |
| 2 | Allow | |
| 6 | Other | |
| 10 | Unknown (permissions group?) | |
Access mask
Access mask seen in combination with property types 0, 1 and 6
| Value | Identifier | Description |
|---|---|---|
| (empty) | Owner or category root | |
| 0x00000001 | [Lst Fldr/Rd Data] | List folder / Read data |
| 0x00000002 | [Crt Fl/W Data] | Create file / Write data |
| 0x00000020 | [Trav Fldr/X Fl] | Traverse folder / Execute file |
Access mask seen in combination with property type 2
[0x001200a9] [R&X] [R] [Sync]
[0x001301bf] [M] [R&X] [R] [W] [Sync]
[0x001f01ff] [FC] [M] [R&X] [R] [W] [Sync]
| Value | Identifier | Description |
|---|---|---|
| (empty) | Owner or category root | |
| 0x00000001 | ||
| 0x00000002 | ||
| 0x00000004 | ||
| 0x00000008 | ||
| 0x00000010 | ||
| 0x00000020 | ||
| 0x00000040 | ||
| 0x00000080 | ||
| 0x00000100 | ||
| 0x00010000 | ||
| 0x00020000 | ||
| 0x00040000 | ||
| 0x00080000 | ||
| 0x00100000 | ||
Sources category
The srce category contains information about acquisition sources of the file entries.
TODO: describe what an acquisition source is in the context of EnCase.
The 1st line of the category contains the string “srce”.
The 2nd line consists of 2 values.
| Value index | Value | Description |
|---|---|---|
| 1 | The number of sources in the category | |
| 2 | 1 | Unknown |
The 3rd line of the category contains tab (0x09) separated type indicators. For more information see the sections below.
The remaining lines in the category consist of:
- category root
- zero or more source entries
Each entry consist of 2 lines:
| Line number | Value | Description |
|---|---|---|
| 1 | Number of entries | |
| 2 | Tab (0x09) separated values that correspond to the type indicators |
The 1st line of the category root entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | The number of sources in the category |
The 1st line of the source entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | 0 | Unknown |
Source type indicators
| Identifier number | Type indicator | Description |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the source |
| 4 | ev | Evidence number, which contains a string |
| 5 | do | Domain, which contains a string (introduced in EnCase 7.9) |
| 6 | loc | Location, which contains a string (introduced in EnCase 7.9) |
| 7 | se | Serial number, which contains a string (introduced in EnCase 7.9) |
| 8 | mfr | Manufacturer, which contains a string (introduced in EnCase 7.9) |
| 9 | mo | Model, which contains a string (introduced in EnCase 7.9) |
| 10 | tb | Total bytes, which contains an integer |
| 11 | lo | Logical offset, which contains an integer which is -1 when value is not set |
| 12 | po | Physical offset, which contains an integer which is -1 when value is not set |
| 13 | ah | MD5 hash, which contains a string with the MD5 hash of the source |
| 14 | sh | SHA1 hash, which contains a string with the SHA1 hash of the source (introduced in EnCase 6.19) |
| 15 | gu | Device GUID, which contains a string with a GUID or "0" if not set |
| 16 | pgu | Primary device GUID, which contains a string with a GUID or "0" if not set (introduced in EnCase 7) |
| 17 | aq | Acquisition date and time, which contains an integer with a POSIX timestamp |
| 18 | ip | IP address, which contains a string (introduced in EnCase 7.9) |
| 19 | si | Unknown (Static IP address?), Contains 1 if static, empty otherwise (introduced in EnCase 7.9) |
| 20 | ma | MAC address, which contains a string without separator characters (introduced in EnCase 7.9) |
| 21 | dt | Drive type, which contains a single character (introduced in EnCase 7.9) |
The acquisition date and time is in the form of: “1142163845”, which is a POSIX epoch timestamp and represents the date: March 12 2006, 11:44:05.
If the “ha” value contains “00000000000000000000000000000000” this means the MD5 hash is not set. The same applies for the “sha” value when it contains “0000000000000000000000000000000000000000” the SHA1 has is not set.
If the “ma” value contains “000000000000” this means the MAC address is not set.
Drive type
| Character value | Meaning |
|---|---|
| f | Fixed drive |
Subjects category
The sub category contains information about TODO
TODO: describe what a subject is in the context of EnCase.
The 1st line of the category contains the string “sub”.
The 2nd line consists of 2 values.
| Value index | Value | Description |
|---|---|---|
| 1 | The number of subjects in the category | |
| 2 | 1 | Unknown |
The 3rd line of the category contains tab (0x09) separated type indicators. For more information see the sections below.
The remaining lines in the category consist of:
- category root
- zero or more subject entries
Each entry consist of 2 lines:
| Line number | Value | Description |
|---|---|---|
| 1 | Number of entries | |
| 2 | Tab (0x09) separated values that correspond to the type indicators |
The 1st line of the category root entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | The number of subject in the category |
The 1st line of the subject entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 | Unknown |
| 2 | 0 | Unknown |
Subject type indicators
| Identifier number | Type indicator | Description |
|---|---|---|
| 1 | p | |
| 2 | n | |
| 3 | id | Identifier, which contains an integer identifying the subject |
| 4 | nu | Unknown (Number) |
| 5 | co | Unknown (Comment) |
| 6 | gu | Unknown (GUID) |
File entries category
The entry category contains information about the file entries.
The 1st line of the category contains the string “entry”.
The 2nd line consists of 2 values.
| Value index | Value | Description |
|---|---|---|
| 1 | The number of file entries in the category or 1 if unknown | |
| 2 | 1 | Unknown |
The 3rd line of the category contains tab (0x09) separated type indicators. For more information see the sections below.
The remaining lines in the category consist of:
- category root
- zero or more file entries
- zero or more sub file entries
- …
- zero or more sub file entries
- zero or more file entries
Each entry consist of 2 lines:
| Line number | Value | Description |
|---|---|---|
| 1 | Number of entries | |
| 2 | Tab (0x09) separated values that correspond to the type indicators |
The 1st line of the category root entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | 0 if not set or 26 if Unknown | |
| 2 | The number of file entries in the category |
The 1st line of the file entry consists of the following 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | Number of file entries in the parent file entry or 0 if not set | |
| 2 | The number of sub file entries in the file entry |
EnCase 5 and 6 (EWF-L01) file entry type indicators
| Identifier number | Character in 29th line | Meaning |
|---|---|---|
| 1 | p | Is parent, where 1 => if the entry is a directory and (empty) => if the entry is a file |
| 2 | n | Name |
| 3 | id | Identifier, contains an integer identifying the file entry |
| 4 | opr | File entry flags |
| 5 | src | Source identifier, which contains an integer that corresponds to an identifier in the Sources category |
| 6 | sub | Subject identifier, which contains an integer that corresponds to an identifier in the Subjects category |
| 7 | cid | Unknown (record type) |
| 8 | jq | Unknown |
| 9 | cr | Creation date and time |
| 10 | ac | Access date and time, for which currently is assumed the precision is date only |
| 11 | wr | (File) modification (last written) date and time |
| 12 | mo | (File system) entry modification date and time |
| 13 | dl | Deletion date and time |
| 14 | aq | Acquisition date and time, which contains an integer with a POSIX timestamp |
| 15 | ha | MD5 hash, which contains a string with the MD5 hash of the file data |
| 16 | ls | File size in bytes. If the file size is 0 the data size should be 1 |
| 17 | du | Duplicate data offset, relative from the start of the media data |
| 18 | lo | Logical offset, which contains an integer which is -1 when value is not set |
| 19 | po | Physical offset, which contains an integer which is -1 when value is not set (or does this value contain the segment file in which the start of the data is stored, -1 for a single segment file?) |
| 20 | mid | GUID, which contains a string with a GUID (introduced in EnCase 6.19) |
| 21 | cfi | Unknown (introduced in EnCase 6.14) |
| 22 | be | Binary extents |
| 23 | pm | Permissions group index, which contains an integer that corresponds to an identifier in the Permissions category or -1 if not set. The value is 0 by default |
| 24 | lpt | Unknown (introduced in EnCase 6.19) |
The creation, access and last written date and time are in the form of: “1142163845”, which is a POSIX epoch timestamp and represents the date: March 12 2006, 11:44:05.
The “ha” value (Hash) consist of a MD5 hash string when file entries are hashed. If the “ha” value contains “00000000000000000000000000000000” this means the MD5 hash is not set.
Ltree file entries
The ltree entries of files and directories consist of entries starting with: 0 followed by the number of sub file entries.
The entries of files and directories:
| Line number | Value | Description |
|---|---|---|
| 1 | (empty) | The root directory |
| 2 | The target drive/mount point | |
| 3 | The actual single file entries |
EnCase 7 (EWF-L01) file entry type indicators
| Identifier number | Character in 29th line | Meaning |
|---|---|---|
| 1 | mid | GUID, which contains a string with a GUID |
| 2 | ls | File size, in bytes. If the file size is 0 the data size should be 1 |
| 3 | be | Binary extents |
| 4 | id | Identifier, which contains an integer identifying the file entry |
| 5 | cr | Creation date and time |
| 6 | ac | Access date and time |
| 7 | wr | (File) modification (last written) date and time |
| 8 | mo | (File system) entry modification date and time |
| 9 | dl | Deletion date and time |
| 10 | sig | Unknown (Introduced in EnCase 7) |
| 11 | ha | MD5 hash, which contains a string with the MD5 hash of the file data |
| 12 | sha | SHA1 hash, which contains a string with the SHA1 hash of the file data. (Introduced in EnCase 7) |
| 13 | ent | Unknown, seen "B" (Introduced in EnCase 7.9) |
| 14 | snh | Short name (or DOS 8.3 name) (Introduced in EnCase 7.9) |
| 15 | p | Is parent, where "1" represents that the entry is a directory and "" (an empty string) that the entry is a file |
| 16 | n | Name |
| 17 | du | Duplicate data offset, relative from the start of the media data |
| 18 | lo | Logical offset, which contains an integer which is -1 when value is not set |
| 19 | po | Physical offset, which contains an integer which is -1 when value is not set (or does this value contain the segment file in which the start of the data is stored, -1 for a single segment file?) |
| 20 | pm | Permissions group index, which contains an integer that corresponds to an identifier in the Permissions category or -1 if not set. The value is 0 by default |
| 21 | oes | Unknown (Original extents?) (Introduced in EnCase 7) |
| 22 | opr | File entry flags |
| 23 | src | Source identifier, which contains an integer that corresponds to an identifier in the Sources category |
| 24 | sub | Subject identifier, which contains an integer that corresponds to an identifier in the Subjects category |
| 25 | cid | Unknown (record type?) |
| 26 | jq | Unknown |
| 27 | alt | Unknown (Introduced in EnCase 7) |
| 28 | ep | Unknown (Introduced in EnCase 7) |
| 29 | aq | Acquisition date and time, which contains an integer with a POSIX timestamp |
| 30 | cfi | Unknown |
| 31 | sg | Unknown (Introduced in EnCase 7) |
| 32 | ea | Extended attributes (Introduced in EnCase 7.9) |
| 33 | lpt | Unknown |
If the “ha” value contains “00000000000000000000000000000000” this means the MD5 hash is not set. The same applies for the “sha” value when it contains “0000000000000000000000000000000000000000” the SHA1 has is not set.
File entry name
A file entry name (“n” value):
- can contain path segment separator characters like “\” and “/”
- uses the “MIDDLE DOT” Unicode character (U+00b7) as a (NTFS) alternative data stream (ADS) name seperator
Note that a regular “MIDDLE DOT” Unicode character will be encoded in the same way so no real way to reliably tell the difference.
An empty name has been observed to be represented as “NoName”.
Short name
The short name (“snh”) value contains 2 values:
| Value number | Value | Description |
|---|---|---|
| 1 | The number of characters in the short name including the end-of-string character | |
| 2 | The short name string, without an end-of-string character |
For example: “13 FILE10~1.TXT”
Original extents
TODO: add some text
1 30a555b 30a6000 12011ae00 9008d7 3f 43 1 12011ae00 30a6000 120113 30a6 9008d7 18530
Ltree file entries
The ltree entries of files and directories consist of entries starting with: 26 followed by the number of sub file entries.
The entries of files and directories:
| Line number | Value | Description |
|---|---|---|
| 1 | LogicalEntries | The root directory |
| 2 | The target drive/mount point | |
| 3 | The actual single file entries |
File entry flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Unknown (Is read-only?) | |
| 0x00000002 | Hidden | Is hidden |
| 0x00000004 | System | Is system |
| 0x00000008 | Archive | Is archive |
| 0x00000010 | Sym Link | Is symbolic link, junction or reparse point |
| 0x00000080 | Deleted | Is deleted |
| 0x00001000 | Hard Linked | Is hard link |
| 0x00002000 | Stream | Is stream |
| 0x00100000 | Internal | Is internal (used in combination with 0x00000006?) |
| 0x00200000 | Unallocated Clusters | Unknown |
| 0x00400000 | Unknown | |
| 0x01000000 | Unknown | |
| 0x02000000 | Folder | Is folder |
| 0x04000000 | Data is sparse |
If 0x00002000 or 0x02000000 are not set the file entry is of type “File”.
If the sparse data flag is set:
- the data size should be 1 and data should consist of a single byte value.
- the data size should be equal to the file size and data should be the same.
If the duplicate data offset value is not set the single byte value in the data should be used to reconstruct the file data. E.g. if the file size is 4096 and the data contains the byte value 0x00 the resulting file should consists of 4096 0-byte values.
If the duplicate data offset value is set the single byte in the data is ignored and the duplicate data offset refers to the location where the data stored.
Binary extents value
The binary extents value contains 3 values separated by a space:
Unknown Offset Size
Where:
- unknown always is 1, could this be the number of extents?
- extent data offset, relative from the start of the media data
- extent data size
The offset and size are specified in hexadecimal values.
Note that the binary extents value contains only 1 value for the first single file entry.
Extended attributes value
The extended attributes value contains base-16 encoded data, which consists of:
- Extended attributes header (stored as an extended attribute)
- One or more extended attributes
Extended attributes header
The extended attributes header is 37 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0 | Unknown (0 => root, 1 => otherwise) |
| 4 | 1 | 1 | Unknown (0 => is leaf node, 1 => is branch node?) |
| 5 | 4 | 11 | Number of characters in name string including the end-of-string character |
| 9 | 4 | 1 | Number of characters in value string including the end-of-string character |
| 13 | 22 | "Attributes\0" | Name string, which contains an UTF-16 little-endian encoded string including end-of-string character |
| 35 | 2 | "\0" | Value string, which contains an UTF-16 little-endian encoded string including end-of-string character |
Extended attribute
An extended attributes is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (0 => root, 1 => otherwise) | |
| 4 | 1 | Unknown (0 => is leaf node, 1 => is branch node?) | |
| 5 | 4 | Number of characters in name string including the end-of-string character | |
| 9 | 4 | Number of characters in value string including the end-of-string character | |
| 13 | ... | Name string, which contains an UTF-16 little-endian encoded string including end-of-string character | |
| ... | ... | Value string, which contains an UTF-16 little-endian encoded string including end-of-string character |
TODO: complete section
Note that branch nodes are presuably used to group attributes, however these are not used consistently and are not shown by EnCase 7.
Map section
Some aspects of this section are:
- Found in EWF-L01 in of EnCase 7 (First seen in EnCase 7.4.1.10)
- Found in the last segment file after data section before done section.
The map consists of:
- map string
- map entries array
Map string
The map string consists of an UTF-16 little-endian encoded string without the UTF-16 endian byte order mark.
The map string contains the following information:
| Line number | Value | Description |
|---|---|---|
| 1 | 1 | The number of categories provided |
| 2 | r | Probably the type of information provided |
| 3 | c | Identifier for the values in the 4th line |
| 4 | The data for the different identifiers in the 3rd line | |
| 5 | (an empty line) |
Map string values
| Identifier number | Character in 29th line | Meaning |
|---|---|---|
| 1 | C | Number of map entries (count) |
The number of map entries should match the number of file entries in the ltree.
Map entry
A map entry is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown | |
| 4 | 4 | Unknown (empty values or part of previous value) | |
| 8 | 16 | Unknown |
Session section
The session section is identifier in the section data type field as “session”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- It is not found in SMART (EWF-S01) and FTK Imager (EWF-E01).
- It is found in EnCase 5 and 6 (EWF-E01) files.
- It is only added to the last segment file for images of optical disc (CD/DVD/BD) media.
- It is found after the data section and before the error2 section.
The session section data consists of:
- The session header
- The session entries array
- The session footer
Session header
The session header is 36 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of sessions | |
| 4 | 28 | Unknown (empty values) | |
| 32 | 4 | Checksum, which contains an Adler-32 of all the previous data within the additional session section data |
Session entry
A session entry is 32 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Flags | |
| 4 | 4 | Start sector | |
| 8 | 24 | Unknown (empty values) |
EnCase stores audio tracks as 0 byte data with a sector size of 2048.
Note that for a CD the first session sector is stored as 16, although the actual session starts at sector 0. Could this value be overloaded to indicate the size of the reserved space between the start of the session and the ISO 9660 volume descriptor.
Session flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | If set the track is an audio track otherwise the track is a data track |
Session footer
The session footer is 4 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains an Adler-32 of all the data within the session entries array |
Error2 section
The error2 section is identifier in the section data type field as “error2”. Some aspects of this section are:
- Not defined in “ASR Data - E01 Compression Format”.
- It is not found in SMART (EWF-S01).
- It is found in, EnCase 3 to 7 and linen 5 to 7 (EWF-E01) files.
- It is only added to the last segment file when errors were encountered while reading the input.
TODO: check FTK Imager, EnCase 1 and 2 for presence of the error2 section.
It contains the sectors that have read errors. The sector where a read error occurred are filled with zero’s during acquiry by EnCase.
The error2 section data consists of:
- The error2 header
- The error2 entries array
- The error2 footer
Error2 header
The error2 header is 520 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of entries | |
| 4 | 512 | Unknown (empty values) | |
| 516 | 4 | Checksum, which contains an Adler-32 of all the previous data within the error2 header data |
Error2 entry
An error2 entry is 8 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Start sector | |
| 4 | 4 | The number of sectors |
Error2 footer
The error2 footer is 4 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains an Adler-32 of all the data within the error2 entries array |
Digest section
The digest section is identified in the section data type field as “digest”. Some aspects of this section are:
- It is found in EnCase 6 to 7 files, as of EnCase 6.12 and linen 6.12 (EWF-E01).
The digest section contains a MD5 and/or SHA1 hash of the data within the chunks.
The digest section data is 80 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | MD5 hash of the media data | |
| 16 | 20 | SHA1 hash of the media data | |
| 36 | 40 | 0x00 | Unknown (Padding) |
| 76 | 4 | Checksum, which contains an Adler-32 of all the previous data within the digest section data |
Hash section
The hash section is identified in the section data type field as “hash”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”.
- It is found in SMART (EWF-S01) and FTK Imager, EnCase 1 to 7 and linen 5 to 7 (EWF-E01) files.
- It is not found in EnCase 5 (EWF-L01).
- The hash section is optional, it does not need to be present. If it does it resides in the last segment file before the done section.
The hash section contains a MD5 hash of the data within the chunks.
The hash section data is 36 byte in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | MD5 hash of the media data | |
| 16 | 16 | Unknown | |
| 32 | 4 | Checksum, which contains an Adler-32 of all the previous data within the additional hash section data |
Notes
Observations regarding the unknown value:
- is zero in SMART
- is zero in EnCase 3 and below
- in EnCase 4 the first 4 bytes are 0, the next 8 bytes seem random, the last 4 bytes seem fixed
- in EnCase 5 and 6 the first 8 bytes seem random, the last 8 bytes equal the file header signature
- in linen 5 the first and last set of 4 bytes seem the same, the second set of 4 bytes seem to be random, the third set of 4 bytes seem to contain a piece of the file header signature
- in linen 6 the first and third set of 4 bytes seem random, the second and last set of 4 bytes seem to be the same
- EnCase5 seems to contain a GUID of the acquired device?
Test with EnCase 4 show that:
- The value does not equal the checksum of the media data
- Does not differentiate for the same media acquired within the same program session, using different formats, but differ for different media and different program sessions
Done section
The done section is identified in the section data type field as “done”. Some aspects of this section are:
- Defined in “ASR Data - E01 Compression Format”.
- It is found in SMART (EWF-S01), FTK Imager, EnCase 1 to 7 and linen 5 to 7 (EWF-E01) and EnCase 5 (EWF-L01) files.
- The done section is the last section within the last segment file.
- The offset to the next section in the section header of the done section point to itself (the start of the done section).
- It should be the last section in the last segment file.
SMART (EWF-S01)
It resides after the table or table2 section.
FTK Imager, EnCase and linen (EWF-E01)
It resides after the data section in a single segment file or for multiple segment files after the table2 section.
In the EnCase (EWF-E01) format the size in the section header is 0 instead of 76 (the size of the section header).
Note that FTK imager versions before 2.9 sets the section size to 76. At the moment it is unknown in which version this behavior was changed.
Incomplete section
The incomplete section is identified in the section data type field as “incomplete”.
This section is seen rarely. It was seen in an EnCase 6.13 (EWF-E01) file as the last last section within the last segment file. The incomplete section was preceded by a hash and digest section, although later in the set of EWF files another hash and digest section were defined.
It is currently assumed that the incomplete section indicates an incomplete image created using remote imaging. The incomplete section contains data but currently there is no indication what purpose the data has.
EWF-X
EWF-X (extended) is an experimental format to enhance the EWF format. EWF-X is based on the EWF-E01 format. EWF-X does not limit the table entries to 16375. EWF-X is not the same as version 2 of EWF.
TODO: add note about the table entry limit.
Sections
Additional sections provided in the EWF-X format are:
- xheader
- xhash
Xheader
The xheader section contains zlib compressed data containing XML data containing the header values.
<?xml version="1.0" encoding="UTF-8"?>
<xheader>
<case_number>1</case_number>
<description>Description</description>
<examiner_name>John D.</examiner_name>
<evidence_number>1.1</evidence_number>
<notes>Just a floppy in my system</notes>
<acquiry_operating_system>Linux</acquiry_operating_system>
<acquiry_date>Sat Jan 20 18:32:08 2007 CET</acquiry_date>
<acquiry_software>ewfacquire</acquiry_software>
<acquiry_software_version>20070120</acquiry_software_version>
</xheader>
Xhash
The xhash section contains zlib compressed data containing XML data containing the hash values.
<?xml version="1.0" encoding="UTF-8"?>
<xhash>
<md5>ae1ce8f5ac079d3ee93f97fe3792bda3</md5>
<sha1>31a58f090460b92220d724b28eeb2838a1df6184</sha1>
</xhash>
GUID
EWF-X uses a random based version of the GUID
Format edge cases and corruption scenarios
This chapter contains several corruption scenarios that have been encountered “in the wild”.
Corrupt uncompressed chunk
TODO: add description
Corrupt compressed chunk
TODO: add description
DEFLATE uncompressed block data with copy of uncompressed data size of 0
Seen in combination with some firmware versions of Tableau TD3 forensic imager.
In this corruption scenarion the copy of uncompressed data size value of the DEFLATE uncompressed block data is set to 0 instead of the 1s complement of the uncompressed data size.
Keramics currently does not handle this corruption scenario.
Corrupt section header
TODO: add description
reading section header from file IO pool entry: 1 at offset: 415912423
type : table2
next offset : 415978027
size : 65604
checksum : 0xf35f03e0
number of offsets : 16375
base offset : 0x00000000
checksum : 0x180d0137
reading section header from file IO pool entry: 1 at offset: 415978027
type : sectors
next offset : 415978027
size : 0
checksum : 0x1ad00464
Corrupt table section
TODO: add description
Scenarios:
- with and with out table 2
- corruption in number of entries
- corruption in entry data
Corrupted segment file header
TODO: add description
Partial segment file
TODO: add description
Missing segment file(s)
TODO: add description
Dual image: section size versus offset
The section headers define both the next section offset and the size of the section. If an implementation reads only one of the two to determine the next section, a dual EWF image can be crafted that consists of two separate images including hashes.
Keramics will mark such an image as corrupted.
Table entries offset overflow
In EnCase 6.7.1 the sectors section can be larger than 2048 MiB. The table entries offsets are 31 bit values in EnCase6 the offset in a table entry value will actually use the full 32 bit if the 2048 MiB has been exceeded. This behavior is no longer present in EnCase 6.8 so it is assumed to be a bug.
Keramics currently assumes that the if the 31 bit value overflows the following chunks are uncompressed. This allows EnCase 6.7.1 faulty EWF files to be converted by Keramics.
Multiple incomplete segment file set identifiers
Although rare it can occur that a set of EWF image files changes its segment file set identifier. This was seen in an image created by EnCase 6.13, presumably using remote imaging. The image contained 3 different segment file set identifiers. The first changes after an incomplete section. The second one changed without any clear indication. The corresponding data section also changed in some extent e.g. compression method and media flags, the is physical flag being dropped. The change was consistent across multiple segment files. It is unlikely that deliberate manipulation is involved. EnCase considers the image as invalid.
Although with some tweaking of the individual segment file sets could be read. In this case the data read from the segment file sets was heavily corrupted. For now Keramics does not support reading multiple segment files sets from a single image, but this might change in the future.
AD encryption
As of version 2.8 FTK Imager supports “AD encryption”. Although the output file uses the EWF extensions the file actually is a AES-256 encrypted container. The EWF can be encrypted using a passphrase or a certificate.
TODO: link to format definition
References
Expert Witness Compression Format version 2 (EWF2)
TODO: add description
Mac OS Encrypted Encoding
Mac OS uses encrypted encoding (CEncryptedEncoding) to encrypt various formats, such as:
Overview
There are 2 known versions of Encrypted Encoding.
Encrypted Encoding version 1
A version 1 encrypted container consist of:
- Encrypted data
- Encrypted container footer at the end of the file
Format version 1 supports the following key protectors:
- Passphrase
Encrypted Encoding version 2
A version 2 encrypted container consist of:
- Encrypted container header at the start of the file
- Key protectors
- Unknown (empty values), probably reserved for the key protectors
- Encrypted data, typically at offset 122368
Version 2 supports the following key protectors:
- Passphrase
- Public key
- Unknown (keybag)
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | N/A |
Encrypted container
Encrypted container footer
The encrypted container footer is 1276 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Container identifier (UUID), used in Mac OS keychain as account identifier | |
| 16 | 4 | Block size, in number of bytes | |
| 20 | 4 | Key protector encryption method | |
| 24 | 4 | Key protector padding type | |
| 28 | 4 | Key protector encryption mode | |
| 32 | 4 | Key protector key size, in number of bits | |
| 36 | 4 | Key protector initialization vector size | |
| 40 | 4 | Key derivation method | |
| 44 | 4 | Unknown | |
| 48 | 4 | Key derivation number of iterations | |
| 52 | 4 | Key derivation salt size, in number of bytes | |
| 56 | 32 | Key derivation salt | |
| 88 | 4 | Block initialization vector size | |
| 92 | 4 | Block encryption mode | |
| 96 | 4 | Block encryption method | |
| 100 | 4 | Block key size, in number of bits | |
| 104 | 32 | Unknown (Wrapped block (or master) data encryption key (DEK) initialization vector?) | |
| 136 | 4 | Wrapped block (or master) data encryption key (DEK) size | |
| 140 | 256 | Wrapped block (or master) data encryption key (DEK) | |
| 396 | 4 | HMAC method | |
| 400 | 4 | HMAC key size, in number of bits | |
| 404 | 32 | Unknown (Wrapped block HMAC initialization vector?) | |
| 436 | 4 | Wrapped block HMAC key size | |
| 440 | 256 | Wrapped block HMAC key | |
| 696 | 4 | Integrity encryption method | |
| 700 | 4 | Integrity key size, in number of bits | |
| 704 | 32 | Unknown (Wrapped integrity key initialization vector?) | |
| 736 | 4 | Wrapped integrity key size | |
| 740 | 256 | Wrapped integrity key | |
| 996 | 4 | Unknown (data size) | |
| 1000 | 256 | Unknown (data) | |
| 1256 | 4 | Data fork offset, where the offset is relative from the start of the container | |
| 1260 | 4 | Data fork size, in number of bytes | |
| 1264 | 4 | 1 | Encrypted Encoding format version |
| 1268 | 8 | "cdsaencr" | Signature |
Note that “cdsaencr” presumably is short for Common Data Security Architecture (CDSA) encryption. Common Security Services Manager (CSSM) is part of CDSA.
Key data can be obtained from the wrapped key data using the following approach (presumably based on RFC 3537):
- Use the specified key derivation method, e.g. PDBKDF2, with salt and number of iterations to determine the key encryption key (KEK) based on a passphrase.
- Pad the initialization vector [0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05] with 0-byte values if necessesary, e.g. if initialization vector is 8 bytes but the encryption method (AES) requires an initialization vector of 16 bytes.
- Decrypt the wrapped key data using the encryption method and mode, e.g. DES3-CBC, with the number of bits of the KEK (defined by encryption key size) and the initialization vector if applicable.
- Remove the padding, specified by the padding type.
- Reverse the resulting intermediate key data.
The intermediate key data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Initialization vector | |
| 8 | ... | Wrapped key data |
- Pad the initialization vector of the intermediate key data with 0-byte values if necessesary.
- Decrypt the wrapped key data (of the intermediate key data) using the encryption method and mode, e.g. DES3-CBC, with the number of bits of the KEK (defined by encryption key size) and the initialization vector (of the intermediate key data) if applicable.
- Remove the padding, specified by the padding type.
The decypted key data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0 | Signature |
| 4 | ... | Key data |
Encrypted container header
The encrypted container header is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "encrcdsa" | Signature |
| 8 | 4 | 2 | Encrypted Encrypted Encoding format version |
| 12 | 4 | Block initialization vector size, in number of bytes | |
| 16 | 4 | Block encryption mode | |
| 20 | 4 | Block encryption method | |
| 24 | 4 | Block key size, in number of bits | |
| 28 | 4 | HMAC method | |
| 32 | 4 | HMAC key size, in number of bits | |
| 36 | 16 | Container identifier (UUID), used in Mac OS keychain as account identifier | |
| 52 | 4 | Block size, in number of bytes | |
| 56 | 8 | Data fork size, in number of bytes | |
| 64 | 8 | Data fork offset, where the offset is relative from the start of the container | |
| 72 | 4 | Number of key protector descriptors | |
| 76 | ... | Array of key protector descriptors |
Key protector descriptor
The key protector descriptor is 20 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unlock type | |
| 4 | 8 | Data offset, where the offset is relative from the start of the container | |
| 12 | 8 | Data size |
Unlock types
| Value | Identifier | Description |
|---|---|---|
| 1 | CSSM_APPLE_UNLOCK_TYPE_KEY_DIRECT | Master key wrapped by passphrase, stored as passphrase wrapped key |
| 2 | CSSM_APPLE_UNLOCK_TYPE_WRAPPED_PRIVATE | Master key wrapped by a public key, stored as public key wrapped key |
| 3 | CSSM_APPLE_UNLOCK_TYPE_KEYBAG | Master key wrapped by keybag |
Passphrase wrapped key
The passphrase wrapped key is 616 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Key derivation method | |
| 4 | 8 | Key derivation number of iterations | |
| 12 | 4 | Key derivation salt size, in number of bytes | |
| 16 | 32 | Key derivation salt | |
| 48 | 4 | Encryption initialization vector size, in number of bytes | |
| 52 | 32 | Encryption initialization vector | |
| 84 | 4 | Encryption key size, in number of bits | |
| 88 | 4 | Encryption method | |
| 92 | 4 | Padding type | |
| 96 | 4 | Encryption mode | |
| 100 | 4 | Wrapped key data size | |
| 104 | 64 | Wrapped key data | |
| 168 | 448 | Unknown (empty values) |
Key data can be obtained from the wrapped key data using the following approach:
- Use the specified key derivation method, e.g. PDBKDF2, with salt and number of iterations to determine the key encryption key (KEK) based on a passphrase.
- Pad the initialization vector with 0-byte values if necessesary, e.g. if initialization vector is 8 bytes but the encryption method (AES) requires an initialization vector of 16 bytes.
- Decrypt the wrapped key data using the encryption method and mode, e.g. DES3-CBC, with the number of bits of the KEK (defined by encryption key size) and the initialization vector if applicable.
- Remove the padding, specified by the padding type.
The decypted key data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Block (or master) data encryption key (DEK) | |
| ... | ... | Block HMAC key | |
| ... | 5 | "CKIE\x00" | Signature |
Public key wrapped key
TODO: complete section
The public key wrapped key is 564 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Public key hash size | |
| 4 | 20 | Public key hash | |
| 24 | 4 | Unknown | |
| 28 | 4 | Unknown | |
| 32 | 4 | Unknown | |
| 36 | 4 | Unknown (encryption method?) | |
| 40 | 4 | Unknown | |
| 44 | 4 | Unknown | |
| 48 | 4 | Unknown (size) | |
| 52 | 256 | Unknown | |
| 308 | 256 | Unknown (empty values) |
Keybag wrapped key
TODO: complete section
Algorithm identifiers
| Value | Identifier | Description |
|---|---|---|
| 0 | CSSM_ALGID_NONE | No algorithm (none) |
| 1 | CSSM_ALGID_CUSTOM | Custom algorithm |
| 2 | CSSM_ALGID_DH | Diffie Hellman key exchange |
| 3 | CSSM_ALGID_PH | Pohlig Hellman key exchange |
| 4 | CSSM_ALGID_KEA | Key Exchange Algorithm |
| 5 | CSSM_ALGID_MD2 | MD2 |
| 6 | CSSM_ALGID_MD4 | MD4 |
| 7 | CSSM_ALGID_MD5 | MD5 |
| 8 | CSSM_ALGID_SHA1 | SHA-1 |
| 9 | CSSM_ALGID_NHASH | N-Hash |
| 10 | CSSM_ALGID_HAVAL | HAVAL |
| 11 | CSSM_ALGID_RIPEMD | RIPE-MD |
| 12 | CSSM_ALGID_IBCHASH | IBC-Hash |
| 13 | CSSM_ALGID_RIPEMAC | RIPE-MAC |
| 14 | CSSM_ALGID_DES | DES |
| 15 | CSSM_ALGID_DESX | DESX |
| 16 | CSSM_ALGID_RDES | RDES |
| 17 | CSSM_ALGID_3DES_3KEY_EDE (or CSSM_ALGID_3DES_3KEY) | Triple-DES with 3 keys applied encrypt, decrypt, encrypt (EDE) |
| 18 | CSSM_ALGID_3DES_2KEY_EDE (or CSSM_ALGID_3DES_2KEY) | Triple-DES with 2 keys applied encrypt, decrypt, encrypt (EDE), with the first key used for the first and last operation |
| 19 | CSSM_ALGID_3DES_1KEY_EEE | Triple-DES with 1 keys applied encrypt, encrypt, encrypt (EEE), with the first key used for all operation |
| 20 | CSSM_ALGID_3DES_3KEY_EEE | Triple-DES with 3 keys applied encrypt, encrypt, encrypt (EEE) |
| 21 | CSSM_ALGID_3DES_2KEY_EEE | Triple-DES with 2 keys applied encrypt, encrypt, encrypt (EEE), with the first key used for the first and last operation |
| 22 | CSSM_ALGID_IDEA | IDEA |
| 23 | CSSM_ALGID_RC2 | RC2 |
| 24 | CSSM_ALGID_RC5 | RC5 |
| 25 | CSSM_ALGID_RC4 | RC4 |
| 26 | CSSM_ALGID_SEAL | SEAL |
| 27 | CSSM_ALGID_CAST | CAST |
| 28 | CSSM_ALGID_BLOWFISH | Blowfish |
| 29 | CSSM_ALGID_SKIPJACK | Skipjac |
| 30 | CSSM_ALGID_LUCIFER | Lucifer |
| 31 | CSSM_ALGID_MADRYGA | Madryga |
| 32 | CSSM_ALGID_FEAL | FEAL |
| 33 | CSSM_ALGID_REDOC | REDOC 2 |
| 34 | CSSM_ALGID_REDOC3 | REDOC 3 |
| 35 | CSSM_ALGID_LOKI | LOKI |
| 36 | CSSM_ALGID_KHUFU | KHUFU |
| 37 | CSSM_ALGID_KHAFRE | KHAFRE |
| 38 | CSSM_ALGID_MMB | MMB |
| 39 | CSSM_ALGID_GOST | GOST |
| 40 | CSSM_ALGID_SAFER | SAFER (K-40, K-64, K-128) |
| 41 | CSSM_ALGID_CRAB | CRAB |
| 42 | CSSM_ALGID_RSA | RSA |
| 43 | CSSM_ALGID_DSA | DSA |
| 44 | CSSM_ALGID_MD5WithRSA | MD5/RSA |
| 45 | CSSM_ALGID_MD2WithRSA | MD2/RSA |
| 46 | CSSM_ALGID_ElGamal | ElGamal |
| 47 | CSSM_ALGID_MD2Random | MD2-based random numbers |
| 48 | CSSM_ALGID_MD5Random | MD5-based random numbers |
| 49 | CSSM_ALGID_SHARandom | SHA-based random numbers |
| 50 | CSSM_ALGID_DESRandom | DES-based random numbers |
| 51 | CSSM_ALGID_SHA1WithRSA | SHA-1/RSA |
| 52 | CSSM_ALGID_CDMF | CDMF |
| 53 | CSSM_ALGID_CAST3 | CAST3 |
| 54 | CSSM_ALGID_CAST5 | CAST5 |
| 55 | CSSM_ALGID_GenericSecret | Generic secret |
| 56 | CSSM_ALGID_ConcatBaseAndKey | Concatenate base key with key |
| 57 | CSSM_ALGID_ConcatKeyAndBase | Concatenate key with base key |
| 58 | CSSM_ALGID_ConcatBaseAndData | Concatenate base key with data |
| 59 | CSSM_ALGID_ConcatDataAndBase | Concatenate data with base key |
| 60 | CSSM_ALGID_XORBaseAndData | XOR base key with data |
| 61 | CSSM_ALGID_ExtractFromKey | Extract key from base key |
| 62 | CSSM_ALGID_SSL3PreMasterGen | SSL 3 with 48 byte pre-master key |
| 63 | CSSM_ALGID_SSL3MasterDerive | Derive an SSL 3 key from a pre-master key |
| 64 | CSSM_ALGID_SSL3KeyAndMacDerive | Derive SSL3 key and MAC |
| 65 | CSSM_ALGID_SSL3MD5_MAC | SSL 3 with MD5 MAC |
| 66 | CSSM_ALGID_SSL3SHA1_MAC | SSL 3 with SHA-1 MAC |
| 67 | CSSM_ALGID_PKCS5_PBKDF1_MD5 | PKCS5 key derivation using PBKDF1 with MD5 |
| 68 | CSSM_ALGID_PKCS5_PBKDF1_MD2 | PKCS5 key derivation using PBKDF1 with MD2 |
| 69 | CSSM_ALGID_PKCS5_PBKDF1_SHA1 | PKCS5 key derivation using PBKDF1 with SHA-1 |
| 70 | CSSM_ALGID_WrapLynks | Spyrus LYNKS DES based wrapping scheme with checksum |
| 71 | CSSM_ALGID_WrapSET_OAEP | SET key wrapping |
| 72 | CSSM_ALGID_BATON | Fortezza BATON |
| 73 | CSSM_ALGID_ECDSA | Elliptic Curve DSA |
| 74 | CSSM_ALGID_MAYFLY | Fortezza MAYFLY |
| 75 | CSSM_ALGID_JUNIPER | Fortezza JUNIPER |
| 76 | CSSM_ALGID_FASTHASH | Fortezza FASTHASH |
| 77 | CSSM_ALGID_3DES | Generix 3DES |
| 78 | CSSM_ALGID_SSL3MD5 | SSL 3 with MD5 |
| 79 | CSSM_ALGID_SSL3SHA1 | SSL 3 with SHA-1 |
| 80 | CSSM_ALGID_FortezzaTimestamp | Fortezza with timestamp |
| 81 | CSSM_ALGID_SHA1WithDSA | SHA-1 with DSA |
| 82 | CSSM_ALGID_SHA1WithECDSA | SHA-1 with Elliptic Curve DSA |
| 83 | CSSM_ALGID_DSA_BSAFE | DSA with BSAFE Key |
| 84 | CSSM_ALGID_ECDH | Elliptic Curve DiffieHellman Key Exchange |
| 85 | CSSM_ALGID_ECMQV | Elliptic Curve MQV key exchange |
| 86 | CSSM_ALGID_PKCS12_SHA1_PBE | PKCS12 SHA-1 PBE key derivation |
| 87 | CSSM_ALGID_ECNRA | Elliptic Curve Nyberg-Rueppel |
| 88 | CSSM_ALGID_SHA1WithECNRA | SHA-1 with Elliptic Curve Nyberg-Rueppel |
| 89 | CSSM_ALGID_ECES | Elliptic Curve Encryption Scheme |
| 90 | CSSM_ALGID_ECAES | Elliptic Curve Authenticate Encryption Scheme |
| 91 | CSSM_ALGID_SHA1HMAC | SHA1-MAC |
| 92 | CSSM_ALGID_FIPS186Random | FIPS186 Random |
| 93 | CSSM_ALGID_ECC | Elliptic Curve Encryption (ECC) |
| 94 | CSSM_ALGID_MQV | Discrete-Log MQV key exchange |
| 95 | CSSM_ALGID_NRA | Discrete-Log Nyberg-Rueppel Signature scheme |
| 96 | CSSM_ALGID_IntelPlatformRandom | Intel Platform Random Number Generator |
| 97 | CSSM_ALGID_UTC | Date and time value in the form: "YYYYMMDDhhmmss" |
| 98 | CSSM_ALGID_HAVAL3 | HAVAL3 Digest |
| 99 | CSSM_ALGID_HAVAL4 | HAVAL4 Digest |
| 100 | CSSM_ALGID_HAVAL5 | HAVAL5 Digest |
| 101 | CSSM_ALGID_TIGER | TIGER Digest |
| 102 | CSSM_ALGID_MD5HMAC | HMAC-MD5 |
| 103 | CSSM_ALGID_PKCS5_PBKDF2 | PKCS5 key derivation using PBKDF2 with SHA-1 (PBKDF2-HMAC-SHA1) |
| 104 | CSSM_ALGID_RUNNING_COUNTER | Running hardware counter |
| 0x80000000 | CSSM_ALGID_VENDOR_DEFINED | Vendor defined algorithm |
| 0x80000001 | CSSM_ALGID_AES | Advanced Encryption Standard (AES) |
Padding types
| Value | Identifier | Description |
|---|---|---|
| 0 | CSSM_PADDING_NONE | No padding |
| 1 | CSSM_PADDING_CUSTOM | Unknown |
| 2 | CSSM_PADDING_ZERO | Pad with 0 |
| 3 | CSSM_PADDING_ONE | Pad with 1 |
| 4 | CSSM_PADDING_ALTERNATE | Unknown |
| 5 | CSSM_PADDING_FF | Unknown (Pad with 0xff?) |
| 6 | CSSM_PADDING_PKCS5 | Pad using Public-Key Cryptography Standard (PKCS) 5 (RFC 2898) |
| 7 | CSSM_PADDING_PKCS7 | Pad using Public-Key Cryptography Standard (PKCS) 7 (RFC 2315) |
| 8 | CSSM_PADDING_CIPHERSTEALING | Unknown |
| 9 | CSSM_PADDING_RANDOM | Unknown |
| 10 | CSSM_PADDING_PKCS1 | Pad using Public-Key Cryptography Standard (PKCS) 1 (RFC 2437) |
Encryption modes
| Value | Identifier | Description |
|---|---|---|
| 0 | CSSM_ALGMODE_NONE | Unknown (Null algorithm mode) |
| 1 | CSSM_ALGMODE_CUSTOM | Unknown (Custom mode) |
| 2 | CSSM_ALGMODE_ECB | Electronic CodeBook (ECB) mode, without padding |
| 3 | CSSM_ALGMODE_ECBPad | Electronic CodeBook (ECB) mode with padding |
| 4 | CSSM_ALGMODE_CBC | Cipher Block Chaining (CBC) mode, without padding |
| 5 | CSSM_ALGMODE_CBC_IV8 | Cipher Block Chaining (CBC) mode with 8 byte initialization vector, without padding |
| 6 | CSSM_ALGMODE_CBCPadIV8 | Cipher Block Chaining (CBC) mode with 8 byte initialization vector, with padding |
| 7 | CSSM_ALGMODE_CFB | Cipher feedback (CFB) mode |
| 8 | CSSM_ALGMODE_CFB_IV8 | Cipher feedback (CFB) mode with 8 byte initialization vector |
| 9 | CSSM_ALGMODE_CFBPadIV8 | Cipher feedback (CFB) mode with 8 byte initialization vector, with padding |
| 10 | CSSM_ALGMODE_OFB | Output FeedBack (OFB) mode |
| 11 | CSSM_ALGMODE_OFB_IV8 | Output FeedBack (OFB) mode mode with 8 byte initialization vector |
| 12 | CSSM_ALGMODE_OFBPadIV8 | Output FeedBack (OFB) mode with 8 byte initialization vector, with padding |
| 13 | CSSM_ALGMODE_COUNTER | Counter mode |
| 14 | CSSM_ALGMODE_BC | Block Chaining mode |
| 15 | CSSM_ALGMODE_PCBC | Propagating Cipher Block Chaining (CBC) mode |
| 16 | CSSM_ALGMODE_CBCC | Cipher Block Chaining (CBC) with checksum mode |
| 17 | CSSM_ALGMODE_OFBNLF | Output FeedBack (OFB) with non-linear function mode |
| 18 | CSSM_ALGMODE_PBC | Plaintext Block Chaining (PBC) mode |
| 19 | CSSM_ALGMODE_PFB | Plaintext FeedBack (PFB) mode |
| 20 | CSSM_ALGMODE_CBCPD | Cipher Block Chaining (CBC) if Plaintext Difference mode |
| 21 | CSSM_ALGMODE_PUBLIC_KEY | Public key mode |
| 22 | CSSM_ALGMODE_PRIVATE_KEY | Private key mode |
| 23 | CSSM_ALGMODE_SHUFFLE | Fortezza shuffle mode |
| 24 | CSSM_ALGMODE_ECB64 | 64 byte Electronic CodeBook (ECB) mode |
| 25 | CSSM_ALGMODE_CBC64 | 64 byte Cipher Block Chaining (CBC) mode |
| 26 | CSSM_ALGMODE_OFB64 | 64 byte Output FeedBack (OFB) mode |
| 28 | CSSM_ALGMODE_CFB32 | 32 byte Cipher feedback (CFB) mode |
| 29 | CSSM_ALGMODE_CFB16 | 16 byte Cipher feedback (CFB) mode |
| 30 | CSSM_ALGMODE_CFB8 | 8 byte Cipher feedback (CFB) mode |
| 31 | CSSM_ALGMODE_WRAP | Unknown |
| 32 | CSSM_ALGMODE_PRIVATE_WRAP | Unknown |
| 33 | CSSM_ALGMODE_RELAYX | Unknown |
| 34 | CSSM_ALGMODE_ECB128 | 128 byte Electronic CodeBook (ECB) mode |
| 35 | CSSM_ALGMODE_ECB96 | 96 byte Electronic CodeBook (ECB) mode |
| 36 | CSSM_ALGMODE_CBC128 | 128 byte Cipher Block Chaining (CBC) mode |
| 37 | CSSM_ALGMODE_OAEP_HASH | Unknown (Algorithm mode for SET key wrapping?) |
| 38 | CSSM_ALGMODE_PKCS1_EME_V15 | Public-Key Cryptography Standard (PKCS) 1 version 1.5 |
| 39 | CSSM_ALGMODE_PKCS1_EME_OAEP | Public-Key Cryptography Standard (PKCS) 1 version 2.0 |
| 40 | CSSM_ALGMODE_PKCS1_EMSA_V15 | Unknown |
| 41 | CSSM_ALGMODE_ISO_9796 | Unknown |
| 42 | CSSM_ALGMODE_X9_31 | Unknown |
Encrypted block data
The encrypted block data can be decrypted using the following approach:
- Calculate the specified block HMAC, e.g. HMAC-SHA-1, with the block HMAC key and the block number stored as a 32-bit big-endian value, where 0 represents the first block. This HMAC is used as the initialization vector for decryption.
- Decrypt the encrypted data using the block encryption method and mode, e.g. AES-CBC, with the number of bits of the block DEK (defined by the block encryption key size) and the initialization vector if applicable.
Mac OS sparse bundle (.sparsebundle) format
The Mac OS sparse bundle (.sparsebundle) format is one of the disk image formats supported natively by Mac OS.
Mac OS referers to the sparse image format as CSparseBundleDiskImage and was introduced in Mac OS X Leopard (10.5).
Overview
A sparse bundle consists of a directory (bundle) with the .sparsbundle suffix containing:
- “Info.bckup” file
- “Info.plist” file
- “token” file
- “bands” directory containing the band files
Sparse bundles can be encrypted. For an encrypted sparse bundle:
- the “token” file contains a Encrypted Encoding container
- the band files contain encrypted data
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | N/A |
| Date and time values | N/A |
| Character strings | N/A |
Info.plist and Info.bckup files
The Info.plist and its backup (Info.bckup) contain a XML plist.
This plist is also referred to as “Information Property List” and contains a single dictionary with the following key-value pairs.
| Identifier | Value | Description |
|---|---|---|
| CFBundleInfoDictionaryVersion | "6.0" | The information property list format version |
| band-size | The maximum size of a band file in bytes | |
| bundle-backingstore-version | 1 | Unknown |
| diskimage-bundle-type | "com.apple.diskimage.sparsebundle" | The bundle type |
| size | The media size in bytes |
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>band-size</key>
<integer>8388608</integer>
<key>bundle-backingstore-version</key>
<integer>1</integer>
<key>diskimage-bundle-type</key>
<string>com.apple.diskimage.sparsebundle</string>
<key>size</key>
<integer>4194304</integer>
</dict>
</plist>
Token file
The token file of an unencrypted sparse bundle is empty.
The token file of an encryped spase bundle contains either an Encrypted Encoding container footer or an Encrypted Encoding container header.
Bands directory
The bands directory contains files containing the actual data of the bands. The files are named using a hexadecimal naming scheme where “0” is the 1st band, “a” the 10th, “f” the 15th, “10” the 16th, etc.
Mac OS sparse image (.sparseimage) format
The Mac OS sparse image (.sparseimage) format is one of the disk image formats supported natively by Mac OS.
Mac OS referers to the sparse image format as CSparseDiskImage.
Overview
Sparse images can be encrypted. An encrypted sparse image uses a Encrypted Encoding container.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | N/A |
The number of bytes per sector is 512.
Image formats
Known types of sparse image files are:
- Sparse image file
- Encrypted sparse image file
Sparse image file
A sparse image file consists of:
- header data
- bands data
Encrypted Encoding version 1 encrypted sparse image file
An Encrypted Encoding version 1 encryped sparse image file consists of:
- Data fork, containing encrypted sparse image data
- Encrypted Encoding container footer at the end of the file
Encrypted image format version 2 encrypted sparse image file
An Encrypted Encoding version 2 encryped sparse image file consists of:
- Encrypted Encoding container header at the start of the file
- Key protectors
- Unknown (empty values), probably reserved for the key protectors
- Data fork, containing encrypted sparse image data
Header data
The header data is 4096 bytes in size and consist of:
- file header
- band numbers array
- trailing data, which should be filled with 0-byte values
File header
The file header is 64 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "sprs" | Signature |
| 4 | 4 | Unknown (format version?), seen 3 | |
| 8 | 4 | Number of sectors per band | |
| 12 | 4 | Unknown, seen 1 | |
| 16 | 4 | The media data size in sectors | |
| 20 | 12 | 0 | Unknown (0-byte values) |
| 32 | 4 | Unknown | |
| 36 | 28 | 0 | Unknown (0-byte values) |
Band numbers array
The band numbers array consists of:
- one or more band numbers
Band number
A band number is 4 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Band number, where 0 indicates a sparse range and any other value refers to a location in the media data |
Where the corresponding media offset can be calculated as following:
media_offset = (band_number - 1) * sectors_per_band * 512
The offset of band data can be calculated as following:
band_data_offset = 4096 + (array_index * sectors_per_band * 512)
For example if the first array entry contains a band number of 4, then the band data is located at
offset 4096 and the corresponding media offset is: 3 * sectors_per_band * 512.
Parallels Disk Image (PDI) format
The Parallels Disk Image format used in Parallels virtualization products as one of its image formats. It is both used the store hard disk images and snapshots.
Overview
A Parallels Disk Image consists of a directory, typically named “{NAME}.hdd” containing:
- Descriptor file (DiskDescriptor.xml) and backup (DiskDescriptor.xml.Backup)
- {NAME}.hdd file
- Storage data file ({NAME}.hdd.0.{GUID}.hds)
- {NAME}.hdd.drh
Where {NAME} is an arbitrary name and {GUID} is a unique identifier.
Disk types
The Parallels Disk Image format support multiple disk types:
| Identifier | Description |
|---|---|
| Expanding | Disk that consists of a single (dynamic size) sparse storage data file |
| Plain | Disk that consists of a single single (fixed size) raw storage data file |
| Split | Disk that consists of a one or more split storage data files, either expanding or plain, holding upto 2G of data |
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Character strings | UTF-8 by default, the encoding is defined in the disk descriptor XML file |
The number of bytes per sector is 512.
Descriptor file
The DiskDescriptor.xml and its backup (DiskDescriptor.xml.Backup) contain the “Parallels_disk_image” XML element tha consists of the following values:
| Identifier | Description |
|---|---|
| Disk_Parameters | The disk parameters |
| StorageData | Information about the storage data files |
| Snapshots | Information about snapshots |
<?xml version='1.0' encoding='UTF-8'?>
<Parallels_disk_image Version="1.0">
<Disk_Parameters>
<Disk_size>134217728</Disk_size>
<Cylinders>262144</Cylinders>
<PhysicalSectorSize>4096</PhysicalSectorSize>
<LogicSectorSize>512</LogicSectorSize>
<Heads>16</Heads>
<Sectors>32</Sectors>
<Padding>0</Padding>
<Encryption>
<Engine>{00000000-0000-0000-0000-000000000000}</Engine>
<Data></Data>
</Encryption>
<UID>{GUID}</UID>
<Name>{NAME}</Name>
<Miscellaneous>
<CompatLevel>level2</CompatLevel>
<Bootable>1</Bootable>
<ChangeState>0</ChangeState>
<SuspendState>0</SuspendState>
</Miscellaneous>
</Disk_Parameters>
<StorageData>
<Storage>
<Start>0</Start>
<End>134217728</End>
<Blocksize>2048</Blocksize>
<Image>
<GUID>{GUID}</GUID>
<Type>Compressed</Type>
<File>{NAME}.hdd.0.{GUID}.hds</File>
</Image>
...
</Storage>
...
</StorageData>
<Snapshots>
<Shot>
<GUID>{GUID}</GUID>
<ParentGUID>{GUID}</ParentGUID>
</Shot>
...
</Snapshots>
</Parallels_disk_image>
Disk parameters
The disk parameters are stored in the “Disk_Parameters” XML element and contains the following values.
| Identifier | Description |
|---|---|
| Cylinders | Number of cylinders |
| Disk_size | Disk size, in number of sectors |
| Encryption | "Encryption" sub XML element |
| Heads | Number of heads |
| Miscellaneous | "Miscellaneous" sub XML element |
| Name | Name of the disk |
| LogicSectorSize | Optional logical sector size, which is 512 bytes by default |
| Padding | Unknown (padding) |
| PhysicalSectorSize | Optional physical sector size, which is 4096 bytes by default |
| Sectors | Number of sectors per cylinder |
| UID | Unknown (identifier) |
Encryption
<Encryption>
<Engine>{00000000-0000-0000-0000-000000000000}</Engine>
<Data></Data>
<Salt></Salt>
</Encryption>
Miscellaneous
<Miscellaneous>
<CompatLevel>level2</CompatLevel>
<Bootable>1</Bootable>
<ChangeState>0</ChangeState>
<SuspendState>0</SuspendState>
<DupBlocksCnt>0</DupBlocksCnt>
<CorruptBlocksCnt>0</CorruptBlocksCnt>
<UnrefBlocksCnt>0</UnrefBlocksCnt>
<OutOfDiskBlocksCnt>0</OutOfDiskBlocksCnt>
<BatOverlapBlocksCnt>0</BatOverlapBlocksCnt>
<BlocksCnt>0</BlocksCnt>
<TruncatedBlocksCnt>0</TruncatedBlocksCnt>
<ReferencedBlocksCnt>0</ReferencedBlocksCnt>
<ShutdownState>0</ShutdownState>
<GuestToolsVersion>17.1.1-51537</GuestToolsVersion>
</Miscellaneous>
CompatLevel
Seen: level0 and level2
Storage data
The “StorageData” XML element contains the following values.
| Identifier | Description |
|---|---|
| Storage | One or more "Storage" XML sub elements |
Note that a split disk contains multiple “Storage” XML sub elements.
Storage
The “Storage” XML element contains the following values.
| Identifier | Description |
|---|---|
| Start | Start sector number of the segment stored in the storage data file |
| End | End sector number of the segment stored in the storage data file |
| Blocksize | Block size, in number of sectors |
| Image | One or more "Image" sub XML elements |
Image
The “Image” XML element contains the following values.
| Identifier | Description |
|---|---|
| GUID | Identifier of snapshot (or layer) |
| Type | Storage data file type |
| File | Name (or path) of the storage data file |
Snapshots data
The “Snapshots” XML element contains the following values.
| Identifier | Description |
|---|---|
| Shot | One or more "Shot" sub XML elements |
Shot
The “Shot” XML element contains the following values.
| Identifier | Description |
|---|---|
| GUID | Identifier of snapshot (or layer) |
| ParentGUID | Identifier of parent snapshot (or layer), which contains "{00000000-0000-0000-0000-000000000000}" if not set |
Storage data file
Storage data file types
| Value | Description |
|---|---|
| "Compressed" | Sparse storage data file |
| "Plain" | Raw storage data file |
Raw storage data file
The raw (or plain) storage data file contains the disk image data including free space.
Sparse storage data file
The sparse storage data file contains the actual disk image data without free space.
A sparse storage data file consists of:
- file header
- block allocation table (BAT)
- data blocks
Sparse storage data file header
The sparse storage data file header is 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | "WithoutFreeSpace" or "WithouFreSpacExt" | Signature |
| 16 | 4 | 2 | Format version |
| 20 | 4 | Number of heads | |
| 24 | 4 | Number of cylinders | |
| 28 | 4 | Block size (or number of tracks) in number of sectors | |
| 32 | 4 | Number of blocks, which is equivalent to the number of block allocation table entries | |
| 36 | 8 | Number of sectors | |
| 44 | 4 | Unknown (Creator?), seen: "\x00\x00\x00\x00", "pd17", "pd22" | |
| 48 | 4 | Data start sector number, which is relative to the start of the sparse storage data file | |
| 52 | 4 | Unknown (Flags?) | |
| 56 | 8 | Unknown (Features start sector?) |
Block allocation table (BAT)
The block allocation table consists of 32-bit entries. An entry contains the sector number where the data block starts is set to 0 if the block is sparse or stored in the parent disk image.
For example block allocation table entry 0 corresponds to disk image offset 0. If contains a value of 0x800 the corresponding data block is stored at file offset 0x100000 (0x800 x 512).
QEMU Copy-On-Write (QCOW) image file format
The QEMU Copy-On-Write (QCOW) image file format is used by the QEMU Open Source Process Emulator to store disk images (storage media)
Overview
A QCOW image file consists of:
- the file header
- optional file header extensions
- the level 1 table (cluster block aligned)
- the reference count table (cluster block aligned)
- reference count blocks
- snapshot headers (8-byte aligned on cluster block boundary)
- cluster blocks containing:
- level 2 tables
- storage media data
The storage media data is stored in cluster blocks. Each cluster block is a multitude of 512 bytes. The level 1 (L1) table contains level 1 reference of level 2 (L2) tables. The level 2 tables contain level 2 references of the storage media.
There are multiple versions of the QCOW image file format. QCOW (version 1) and QCOW2 (version 2 and later) are sometimes considered even as separate image formats. Version 3 is considered as an extended version of QCOW2.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian in most cases, note that some values are in little-endian |
| Date and time values | Number of seconds since Jan 1, 1970 00:00:00 UTC (POSIX epoch) |
| Character strings | UTF-8 |
Note that this document assumes that character strings are stored in UTF-8.
The number of bytes per sector is 512.
Encryption
The QCOW image format can encrypted the media data stored in the image format. Currently supported encryption methods are:
- AES-CBC 128-bit
- Linux Unified Key Setup (LUKS)
If no encryption is used the encryption method in the file header is set to none (0).
Note it is currently unknown if the format supports compression and encryption at the same time. It does not appear to be supported by qemu-img.
AES-CBC 128-bit
Both encryption and decryption use:
- AES-CBC with a 128-bits key decryption of sector data
The key is direct copy of the first 16 characters of a user provided (narrow character) password. If the password is smaller than 16 characters. The remaining key data is set to 0-byte values.
Note that it is currently unclear which character sets are allowed and how characters outside the 7-bit ASCII set should be handled.
The initialization vector of the AES-CBC is using media data sector number (relative to the start of the disk) in little-endian format as the first 64 bits of the 128 bit initialization vector. The remaining initialization vector data is set to 0-byte values. The first sector number is 0 and the bytes per sector are 512.
Linux Unified Key Setup (LUKS)
TODO: complete section
File header
File header – version 1
The file header - version 1 is 48 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "QFI\xfb" or "\x51\x46\x49\xfb" | The signature |
| 4 | 4 | 1 | Format version |
| 8 | 8 | Backing file name offset | |
| 16 | 4 | Backing file name size | |
| 20 | 4 | Modification date and time, which contains a POSIX timestamp | |
| 24 | 8 | Storage media size | |
| 32 | 1 | Number of cluster block bits | |
| 33 | 1 | Number of level 2 table bits | |
| 34 | 2 | Unknown (empty values) | |
| 36 | 4 | Encryption method | |
| 40 | 8 | Level 1 table offset |
The cluster block size is calculated as:
cluster_block_size = 1 << number_of_cluster_block_bits
The level 2 table size is calculated as:
level2_table_size = (1 << number_of_level2_table_bits) * 8
The level 1 table size is calculated as:
level1_table_entry_size = cluster_block_size * (1 << number_of_level2_table_bits)
level1_table_size = media_size / level1_table_entry_size
if media_size % level1_table_entry_size != 0:
level1_table_size += 1
level1_table_size *= 8
The backing file name is set in snapshot image files and is normally stored after the file header.
File header – version 2
The file header - version 2 is 72 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "QFI\xfb" or "\x51\x46\x49\xfb" | The signature |
| 4 | 4 | 2 | Format version |
| 8 | 8 | Backing file name offset | |
| 16 | 4 | Backing file name size | |
| 20 | 4 | Number of cluster block bits | |
| 24 | 8 | Storage media size | |
| 32 | 4 | Encryption method | |
| 36 | 4 | Number of level 1 table references | |
| 40 | 8 | Level 1 table offset | |
| 48 | 8 | Reference count table offset | |
| 56 | 4 | Reference count table clusters | |
| 60 | 4 | Number of snapshots | |
| 64 | 8 | Snapshots offset |
The cluster block size is calculated as:
cluster_block_size = 1 << number_of_cluster_block_bits
The number of level 2 table bits is calculated as:
number_of_level2_table_bits = number_of_cluster_block_bits - 3
The level 2 table size is calculated as:
level_table2_size = (1 << number_of_level2_table_bits) * 8
The level 1 table size is calculated as:
level1_table_size = number_of_level1_table_references * 8
The backing file name is set in snapshot image files and is normally stored after the file header.
File header – version 3
The file header - version 3 is 104 or 112 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "QFI\xfb" or "\x51\x46\x49\xfb" | The signature |
| 4 | 4 | 3 | Format version |
| 8 | 8 | Backing file name offset | |
| 16 | 4 | Backing file name size | |
| 20 | 4 | Number of cluster block bits | |
| 24 | 8 | Storage media size | |
| 32 | 4 | Encryption method | |
| 36 | 4 | Number of level 1 table references | |
| 40 | 8 | Level 1 table offset | |
| 48 | 8 | Reference count table offset | |
| 56 | 4 | Reference count table clusters | |
| 60 | 4 | Number of snapshots | |
| 64 | 8 | Snapshots offset | |
| 72 | 8 | Incompatible feature flags | |
| 80 | 8 | Compatible feature flags | |
| 88 | 8 | Auto-clear feature flags | |
| 96 | 4 | Reference count order | |
| 100 | 4 | 104 or 112 | File header size, which contains the size of the file header, this value does not include the size of the file header extensions |
| If file header size equals 112 | |||
| 104 | 1 | Compression method | |
| 105 | 7 | Unknown (padding) | |
The cluster block size is calculated as:
cluster_block_size = 1 << number_of_cluster_block_bits
The number of level 2 table bits is calculated as:
number_of_level2_table_bits = number_of_cluster_block_bits - 3
The level 2 table size is calculated as:
level_table2_size = (1 << number_of_level2_table_bits) * 8
The level 1 table size is calculated as:
level1_table_size = number_of_level1_table_references * 8
The backing file name is set in snapshot image files and is normally stored after the file header.
Encryption methods
| Value | Identifier | Description |
|---|---|---|
| 0 | QCOW_CRYPT_NONE | No encryption |
| 1 | QCOW_CRYPT_AES | AES-CBC 128-bits encryption |
| 2 | QCOW_CRYPT_LUKS | Linux Unified Key Setup (LUKS) encryption |
Incompatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | QCOW2_INCOMPAT_DIRTY | Is dirty (or in use) |
| 0x0000000000000002 | QCOW2_INCOMPAT_CORRUPT | Is corrupt |
| 0x0000000000000004 | QCOW2_INCOMPAT_DATA_FILE | Uses data file |
| 0x0000000000000008 | QCOW2_INCOMPAT_COMPRESSION | Uses non-standard compression, where "standard compression" refers to zlib |
| 0x0000000000000010 | QCOW2_INCOMPAT_EXTL2 | Has extended L2 (table) entries |
Compatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | QCOW2_COMPAT_LAZY_REFCOUNTS | Uses lazy reference counting |
Auto-clear feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | QCOW2_AUTOCLEAR_BITMAPS | |
| 0x0000000000000002 | QCOW2_AUTOCLEAR_DATA_FILE_RAW |
Compression methods
| Value | Identifier | Description |
|---|---|---|
| 0 | zlib compression | |
| 1 | zstd compression (RFC 8878) |
File header extensions
A file header extension consist of:
- file header extension header
- file header extension data
File header extension header
The file header extension header is 8 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | The extension type (signature) | |
| 4 | 4 | The extension data size |
File header extension types
| Value | Identifier | Description |
|---|---|---|
| 0x0537be77 | QCOW2_EXT_MAGIC_CRYPTO_HEADER | Crypto header |
| 0x23852875 | QCOW2_EXT_MAGIC_BITMAPS | Bitmaps |
| 0x44415441 or "DATA" | QCOW2_EXT_MAGIC_DATA_FILE | Data-file |
| 0x6803f857 | QCOW2_EXT_MAGIC_FEATURE_TABLE | Feature table |
| 0xe2792aca | QCOW2_EXT_MAGIC_BACKING_FORMAT | Backing format |
Backing format file header extension
The backing format file header extension header is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Backing format identifier, which contains an UTF-8 string without end-of-string character |
Bitmaps file header extension
TODO: complete section
Crypto header file header extension
The crypto header file header extension header is 16 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | The crypto data offset | |
| 8 | 8 | The crypto data size |
Data-file file header extension
The data-file file header extension header is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Data-file file name, which contains an UTF-8 string without end-of-string character |
Feature table file header extension
TODO: complete section
Level 1 table
The level 1 table contains level 2 table references.
A reference value of 0 represents unused or unallocated and is considered as sparse or stored in a corresponding backing file.
Level 2 table reference – version 1
The level 2 table reference is 8-bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 63 bits | Level 2 table offset, which contains an offset relative from the start of the file | |
| 7.7 | 1 bit | QCOW_OFLAG_COMPRESSED | Is compressed flag |
Level 2 table reference – version 2 or 3
The level 2 table reference is 8-bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 62 bits | Level 2 table offset, which contains an offset relative from the start of the file | |
| 7.6 | 1 bit | QCOW_OFLAG_COMPRESSED | Is compressed flag |
| 7.7 | 1 bit | QCOW_OFLAG_COPIED | Is copied flag |
The is copied flag indicates that the reference count of the corresponding level 2 table is exactly one.
Level 2 table
The level 2 table contains cluster block references.
The level 2 table size is calculated as:
level2_table_size = (1 << number_of_level2_table_bits) * 8
A reference value of 0 represents unused or unallocated and is considered as sparse or stored in a corresponding backing file.
Cluster block reference – version 1
The cluster block reference - version 1 is 8-bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 63 bits | Cluster block offset, which contains an offset relative to the start of the cluster block | |
| 7.7 | 1 bit | QCOW_OFLAG_COMPRESSED | Is compressed flag |
Cluster block reference – version 2 or 3
The cluster block reference - version 2 or 3 is 8-bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 62 bits | Cluster block offset, which contains an offset relative to the start of the cluster block | |
| 7.6 | 1 bit | QCOW_OFLAG_COMPRESSED | Is compressed flag |
| 7.7 | 1 bit | QCOW_OFLAG_COPIED | Is copied flag |
The is copied flag indicates that the reference count of the corresponding cluster block is exactly one.
Reference count table
The cluster data blocks are referenced counted. For every cluster data block a 16-bit reference count is stored in the reference count table.
The reference count table is stored in cluster block sizes. The file header contains the number of blocks (or reference count table clusters).
TODO: complete section
Cluster data block
To retrieve a cluster data block corresponding a certain storage media offset:
Determine the level 1 table index from the offset:
level1_table_index_bit_shift = number_of_cluster_block_bits + number_of_level2_table_bits
For version 1:
level1_table_index = (offset & 0x7fffffffffffffff) >> level1_table_index_bit_shift
For version 2 and 3:
level1_table_index = (offset & 0x3fffffffffffffff) >> level1_table_index_bit_shift
Retrieve the level 2 table offset from the level 1 table. If the level 2 table offset is 0 and the image has a backing file the cluster data block is stored in the backing file otherwise the cluster block is considered sparse.
Read the corresponding level 2 table.
Determine the level 2 table index from the offset:
level2_table_index_bit_mask = ~(0xffffffffffffffff << number_of_level2_table_bits)
level2_table_index = (offset >> number_of_cluster_block_bits) >> level2_table_index_bit_mask
Retrieve the cluster block offset from the level 2 table. If the cluster block offset is 0 and the image has a backing file the cluster data block is stored in the backing file otherwise the cluster block is considered sparse.
Uncompressed cluster data block
If the is compressed flag (QCOW_OFLAG_COMPRESSED) is not set:
cluster_block_bit_mask = ~(0xffffffffffffffff << number_of_cluster_block_bits)
cluster_block_data_offset = (offset & cluster_block_bit_mask) + cluster_block_offset
Note that in version 2 or 3 the last cluster block in the file can be smaller than the cluster block size defined by the number of cluster block bits in the file header. This does not seem to be the case for version 1.
Compressed cluster data block
If the is compressed flag (QCOW_OFLAG_COMPRESSED) is set the cluster block data is stored using the compression method defined by the file header or DEFLATE by default.
Multiple compressed cluster data blocks are stored together in cluster block sizes. The compressed cluster data blocks are sector (512 bytes) aligned.
The compressed data uses a DEFLATE (inflate) window bits value of -12
Compressed chunk data block – version 1
compressed_size_bit_shift = 63 - number_of_cluster_block_bits
compressed_block_size = (
(cluster_block_offset & 0x7fffffffffffffff) >> compressed_size_bit_shift)
compressed_block_offset &= ~(0xffffffffffffffff << compressed_size_bit_shift)
Compressed chunk data block – version 2 or 3
compressed_size_bit_shift = 62 - (number_of_cluster_block_bits – 8)
According to “the QCOW2 Image Format” the compressed block size is calculated as following:
compressed_block_size = (
(((cluster_block_offset & 0x3fffffffffffffff) >> compressed_size_bit_shift) + 1) * 512)
Since the compressed block size is stored in 512 byte sectors this value does not contain the exact byte size of the compressed cluster block data. It sometimes lacks the size of the last partially filled sector and one sector should be added if possible within the bounds of the cluster blocks size and the file size.
cluster_block_offset &= ~(0xffffffffffffffff << compressed_size_bit_shift)
Snapshots
As of version 1 QCOW can use the backing file name in the file header to point to a backing file (or parent image) that contains the snapshot image where the current image only contains the modifications. Version 2 adds support to store snapshot inside the image.
Snapshot header - version 2 or 3
An in-image snapshot is created by adding a snapshot header, copying the L1 table and incrementing the reference counts of all L2 tables and data clusters referenced by the L1 table.
The snapshot header is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Level 1 table offset | |
| 8 | 4 | Level 1 size | |
| 12 | 2 | Identifier string size | |
| 14 | 2 | Name size | |
| 16 | 4 | Date in seconds | |
| 20 | 4 | Date in nano seconds | |
| 24 | 8 | VM clock in nano seconds | |
| 32 | 4 | VM state size | |
| 36 | 4 | Extra data size | |
| 40 | ... | Extra data | |
| ... | ... | Identifier string size | |
| ... | ... | Name |
TODO: complete section
References
- The QCOW Image Format, by Mark McLoughlin
- The QCOW2 Image Format, by Mark McLoughlin
Universal Disk Image Format (UDIF)
The Universal Disk Image Format (UDIF) (.dmg) is one of the disk image formats supported natively by Mac OS. UDIF supersedes the New Disk Image Format (NDIF) format and was introduced in Max OS X 10.0 (Cheetah).
Mac OS referers to the UDIF image format as CUDIFEncoding.
Overview
An UDIF image can consists of one or more segment files, where:
- the first segment file is named: “image.dmg”
- successive segment files are named: “image.###.dmgpart”, where “###” represents a numeric value starting with 2 with 0 padding, e.g. “image.002.dmgpart”. Segment files after 999 are assumed to be named without the 0 padding, e.g. “image.1234.dmgpart”.
The data forks of the segment files are used as a contiguous data stream. A compressed block can be stored across multiple segment files.
Only the first segment file contains a resource fork or XML plist.
Known UDIF image types are:
| Identifier | Description |
|---|---|
| UDBZ | bzip2 compressed UDIF |
| UDCO | Apple Data Compression (ADC) compressed UDIF |
| UDIF | Read-write uncompressed UDIF |
| UDRO | Read-only uncompressed UDIF |
| UDxx | Uncompressed UDIF |
| UDZO | zlib/DEFLATE compressed UDIF |
| ULFO | LZFSE compressed UDIF |
| ULMO | LZMA compressed UDIF |
UDIF images can be encrypted. An encrypted UDIF image consists of one of more UDIF segment files, where each segment file uses a Encrypted Encoding container with its own key protectors.
Terminology
| Term | Description |
|---|---|
| Flattened image | The disk image is a self-contained, a resource fork is stored within the image |
| Unflattened image | The disk image uses the file system to store a resource fork |
Image formats
Known types of UDIF segment files are:
- Uncompressed segment file
- Compressed segment file
- Encrypted segment file
Uncompressed segment file format
An uncompressed UDIF segment file consist of:
- Image data
- File footer at the end of the file
Note that an uncompressed UDIF image without file footer is equivalent to a RAW storage media image (CRawDiskImage).
Compressed segment file format
A compressed UDIF segment file consist of:
- Data fork, containing the image data
- Optional XML plist
- Optional resource fork
- File footer at the end of the file
Encrypted Encoding version 1 encrypted UDIF segment file
An Encrypted Encoding version 1 encryped UDIF segment file consists of:
- Data fork, containing encrypted UDIF data
- Encrypted Encoding container footer at the end of the file
Note that the encrypted UDIF data can contain an uncompressed UDIF image without file footer.
Encrypted Encoding version 2 encrypted UDIF segment file
An Encrypted Encoding version 2 encryped UDIF segment file consists of:
- Encrypted Encoding container header at the start of the file
- Key protectors
- Unknown (empty values), probably reserved for the key protectors
- Data fork, containing encrypted UDIF data
Note that the encrypted UDIF data can contain an uncompressed UDIF image without file footer.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | N/A |
The number of bytes per sector is 512.
File footer
The file footer (also known as resource file or metadata) (UDIFResourceFile) is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "koly" | Signature |
| 4 | 4 | 4 | Format version |
| 8 | 4 | 512 | File footer size, in number of bytes |
| 12 | 4 | Image flags | |
| 16 | 8 | Segment logical offset | |
| 24 | 8 | Data fork offset, where the offset is relative from the start of the image file | |
| 32 | 8 | Data fork size, in number of bytes | |
| 40 | 8 | Resource fork offset, where the offset is relative from the start of the image file | |
| 48 | 8 | Resource fork size, in number of bytes | |
| 56 | 4 | Segment number, where 1 represents the first segment and contains 0 if not set | |
| 60 | 4 | Number of segments, which contains 0 if not set | |
| 64 | 16 | Segment set identifier, which contains an UUID | |
| 80 | 4 | Data checksum type | |
| 84 | 4 | Data checksum size, in number of bits | |
| 88 | 128 | Data checksum | |
| Introduced in Mac OS 10.2 | |||
| 216 | 8 | XML plist offset, where the offset is relative from the start of the image file | |
| 224 | 8 | XML plist size | |
| 232 | 120 | Unknown (Reserved) | |
| 352 | 4 | Master checksum type | |
| 356 | 4 | Master checksum size, in number of bits | |
| 360 | 128 | Master checksum | |
| 488 | 4 | Image type (or variant) | |
| 492 | 8 | Media size, in number of sectors, which contains the total number of sectors in the (uncompressed) image | |
| 500 | 4 | Unknown (reserved) | |
| 504 | 4 | Unknown (reserved) | |
| 508 | 4 | Unknown (reserved) | |
In an encrypted image file the offsets are relative from the start of the unencrypted image file.
Note that both the XML plist and resource fork size can be 0, such as in an UDIF stub (UDxx) image.
Image flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | kUDIFFlagsFlattened | Flattened image (set by hdiutil flatten/unflatten) |
| 0x00000002 | kUDIFFlagsInPlace | |
| 0x00000004 | kUDIFFlagsInternetEnabled | Internet enabled (set by hdiutil internet-enable) |
| 0x00000008 | kUDIFFlagsIsEncrypted |
Checksum types
| Value | Identifier | Description |
|---|---|---|
| 2 | CRC-32 | |
| 4 | MD5 |
Image types
| Value | Identifier | Description |
|---|---|---|
| 1 | kUDIFDeviceImageType | Device image |
| 2 | kUDIFPartitionImageType | Paritition image |
Resource fork
In older UDIF images the resource fork contains the image metadata, such as the block table. The resource fork consists of:
- Resource fork header
- Resource data
- Resource map
Resource fork header
The resource fork header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Resource data offset, which contains the byte offset relative to the start of the resource fork | |
| 4 | 4 | Resource map offset, which contains the byte offset relative to the start of the resource fork | |
| 8 | 4 | Resource data size, in number of bytes | |
| 12 | 4 | Resource map size, in number of bytes |
Resource data
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Size, in number of bytes | |
| 4 | ... | Data |
Resource map
The resource map consists of:
- Resource map header
- Entries list
- Names
Resource map header
The resource map header is 28 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Unknown (reserved) | |
| 16 | 4 | Unknown (next resource map) | |
| 20 | 2 | Unknown (file reference number) | |
| 22 | 2 | Unknown (resource file attribute flags) | |
| 24 | 2 | Entries list offset, which contains the byte offset relative to the start of the resource map | |
| 26 | 2 | Names list offset, which contains the byte offset relative to the start of the resource map |
Resource map entries list
The entries (or type) list is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Number of entries, stored as value - 1 | |
| 2 | ... | Array of entries |
Resource map entry
The resource map entry is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Type indicator (or signature) | |
| 4 | 2 | Number of resource descriptors, stored as value - 1 | |
| 6 | 2 | Resource descriptors offset, which contains the byte offset relative to the start of the entries list |
A resource map entry is comparable to an item in the XML plist resource-fork dictionary such as the “blkx” item.
Resource descriptor
The resource descriptor (or reference list) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Resource identifier. Corresponds to the "ID" value in the XML plist. | |
| 2 | 2 | Resource name offset, which contains the byte offset relative to the start of the names list where 0xffff indicates the resource has no name. Corresponds to the "Name" value in the XML plist. | |
| 4 | 1 | Resource flags (0x20: Purgeable, 0x40: Protected). Corresponds to the "Attributes" value in the XML plist. | |
| 5 | 3 | Resource data offset, which contains the byte offset relative to the start of the resource data | |
| 8 | 4 | Unknown (reserved) |
Resource name
The resource name is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Name size | |
| 2 | ... | Name string, without an end-of-string character |
XML plist
The XML plist contains image metadata such as the block table.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>resource-fork</key>
<dict>
<key>blkx</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0050</string>
<key>CFName</key>
<string>Protective Master Boot Record (MBR : 0)</string>
<key>Data</key>
<data>
bWlzaAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAA
AAgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAIAAAAgQfL6MwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACgAAABQAAAAMAAAAAAAAAAAAAAAAAAAABAAAA
AAAAIA0AAAAAAAAAH/////8AAAAAAAAAAAAAAAEAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAA=
</data>
<key>ID</key>
<string>-1</string>
<key>Name</key>
<string>Protective Master Boot Record (MBR : 0)</string>
</dict>
...
</array>
<key>plst</key>
<array>
<dict>
<key>Attributes</key>
<string>0x0050</string>
<key>Data</key>
<data>
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAQAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAA
</data>
<key>ID</key>
<string>0</string>
<key>Name</key>
<string></string>
</dict>
</array>
</dict>
</dict>
</plist>
The XML plist contains the following key-value pairs:
| Identifier | Description |
|---|---|
| resource-fork | dictionary |
XML plist resource-fork dictionary
The resource-fork dictionary contains the following key-value pairs:
| Identifier | Description |
|---|---|
| blkx | array of dictionaries, which contains Block table (or block extents) values |
| LPic | optional array of dictionaries, which contains values related to license information |
| plst | array of dictionaries, which contains values related to image properties |
| STR# | optional array of dictionaries, which contains values related to license information |
| TEXT | optional array of dictionaries, which contains values related to license information |
XML plist array entry
An array entry contains the following key-value pairs:
| Identifier | Description |
|---|---|
| Attributes | string that contains a hexadecimal formatted integer value |
| CFName | string |
| Data | string that contains base-64 encoded data |
| ID | string that contains a decimal formatted integer value |
| Name | string |
Note the the blkx array appears the only one that uses CFName.
Block table
The block table (BLKXTable) is of variable size and consists of:
- block table header
- block table entries
The block table header
The block table header is 204 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "mish" | Signature |
| 4 | 4 | 1 | Format version |
| 8 | 8 | Start sector, which contains the sector number relative to the start of the media data | |
| 16 | 8 | Number of sectors | |
| 24 | 8 | Base data offset, which contains the byte offset relative to the start of the segment data stream | |
| 32 | 4 | Unknown (BuffersNeeded) | |
| 36 | 4 | Unknown (BlockDescriptors) | |
| 40 | 6 x 4 = 24 | 0 | Unknown (reserved) |
| 64 | 4 | Checksum type | |
| 68 | 4 | Checksum size | |
| 72 | 128 | Checksum | |
| 200 | 4 | Number of entries |
Block table entry
The block table entry (BLKXChunkEntry) is 40 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Entry type | |
| 4 | 4 | Unknown (comment related?) | |
| 8 | 8 | Start sector, which contains the sector number relative to the start of the start sector of the block table | |
| 16 | 8 | Number of sectors | |
| 24 | 8 | Data offset, which contains the byte offset relative to the base data offset in the block table header | |
| 32 | 8 | Data size, which contain the number of bytes of data stored, which is 0 for sparse data |
UDIF block table entry types
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | Unknown (sparse) | |
| 0x00000001 | Uncompressed (raw) data | |
| 0x00000002 | Sparse (used for Apple_Free) | |
| 0x7ffffffe | Comment | |
| 0x80000004 | ADC compressed data | |
| 0x80000005 | zlib compressed data | |
| 0x80000006 | bzip2 compressed data | |
| 0x80000007 | LZFSE compressed data | |
| 0x80000008 | LZMA compressed data | |
| 0xffffffff | Block table entries terminator |
UDIF comment
TODO: complete section
Notes
Is the maximum compressed chunk size 2048 sectors?
Comment seems to reference compressed data but has no size or number of sectors value.
Format edge cases and corruption scenarios
Non-sequential segment files
It is currently unknown if non-sequential segment files are supported.
XML plist and resource fork both in use
The XML plist and resource fork could be used simultaneously, allowing for a single UDIF to contain multiple images.
It is currently assumed that the XML plist is leading.
XML plist and/or resource fork in non-first segment files
The XML plist and/or resource fork could be used in non-first segment files.
Virtual Hard Disk (VHD) image format
The Virtual Hard Disk (VHD) format is used by Microsoft vitualization products as one of its image formats. It is both used the store hard disk images and snapshots.
Overview
There are multiple types of VHD images, namely:
- Fixed-size VHD image
- Dynamic-size (or sparse) VHD image
- Differential (or differencing) VHD image
Fixed-size hard disk image
A fixed-size VHD image consists of:
- data
- file footer
Note that a fixed-size VHD image is equivalent to a raw storage media image with an additional footer.
Dynamic-size (or sparse) hard disk image
A dynamic-size (or sparse) VHD image consists of:
- copy of file footer
- dynamic disk header
- block allocation table
- data in blocks
- file footer
Differential hard disk image
A differential (or differencing) VHD image consists of:
- copy of file footer
- dynamic disk header
- block allocation table
- data in blocks
- file footer
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | Number of seconds since January 1, 2000 00:00:00 UTC |
| Character strings | UCS-2 big-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
The number of bytes per sector is 512.
Undo disk image
Virtual PC has a feature to create “Undo Disks”. This undo disk feature stores a differential hard disk image in files named something similar like:
VirtualPCUndo_<name>_0_0_hhmmssMMDDYYYY.vud
Where the date and time seems to be stored in UTC and <name> represents the name of the parent image.
File footer
The file footer is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "conectix" | Signature (also referred to as cookie) |
| 8 | 4 | Features | |
| 12 | 4 | 0x00010000 | Format version, where the upper 16-bit are the major version and the lower 16-bit the minor version |
| 16 | 8 | Next offset, which contains the offset to the next (metadata) structure. The offset is relative from the start of the file. It should only be set in dynamic and differential disk images. In fixed disk images it should be set to 0xffffffffffffffff (-1) | |
| 24 | 4 | Modification time, which contains the number of seconds since January 1, 2000 00:00:00 UTC | |
| 28 | 4 | Creator application | |
| 32 | 4 | Creator version, where the upper 16-bit are the major version and the lower 16-bit the minor version | |
| 36 | 4 | Creator (host) operating system | |
| 40 | 8 | Disk size, which contains the size of the disk in bytes | |
| 48 | 8 | Data size, which contains the size of the data in bytes | |
| 56 | 4 | Disk geometry | |
| 60 | 4 | Disk type | |
| 64 | 4 | Checksum, which contains a one's complement of the sum of the file footer excluding the checksum itself | |
| 68 | 16 | Identifier, which contains a big-endian UUID | |
| 84 | 1 | Saved state, which contains a flag to indicate the image is in saved state | |
| 85 | 427 | 0 | Unknown (Reserved should contain 0-byte values) |
Features
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Is temporary disk, which indicates that this disk is a candidate for deletion on shutdown | |
| 0.1 | 1 bit | Unknown (Reserved, must be set to 1) | |
| 0.2 | 30 bits | Unknown (Reserved, must be set to 0) |
A value of 0 represents no features are enabled.
Creator application
| Value | Identifier | Description |
|---|---|---|
| "d2v\x00" | Disk2vhd | |
| "qemu" | Qemu | |
| "vpc\x20" | Virtual PC | |
| "vs\x20\x20" | Virtual Server | |
| "win\x20" | Windows (Disk Management) |
Creator host operating system
| Value | Identifier | Description |
|---|---|---|
| "Mac\x20" | Macintosh | |
| "Wi2k" | Windows |
Disk geometry
The disk geometry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Number of cylinders | |
| 2 | 1 | Number of heads | |
| 3 | 1 | Number of sectors per track (cylinder) |
Disk type
| Value | Identifier | Description |
|---|---|---|
| 0 | None | |
| 1 | Unknown (Deprecated) | |
| 2 | Fixed hard disk | |
| 3 | Dynamic hard disk | |
| 4 | Differential hard disk | |
| 5 | Unknown (Deprecated) | |
| 6 | Unknown (Deprecated) |
Dynamic disk header
The dynamic disk header is 1024 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "cxsparse" | Signature (Cookie) |
| 8 | 8 | Next offset, which contains the offset to the next (metadata) structure. The offset is relative from the start of the file. Currently this is unused and should be set to 0xffffffffffffffff (-1) | |
| 16 | 8 | Block allocation table offset, whic contains the offset to the block allocation table structure. The offset is relative from the start of the file | |
| 24 | 4 | 0x00010000 | Format version, where the upper 16-bit are the major version and the lower 16-bit the minor version |
| 28 | 4 | Number of blocks, which is equivalent to the number of block allocation table entries | |
| 32 | 4 | Block size. The block size must be a power-of-two multitude of the sector size and does not include the size of the sector bitmap. The default block size is 4096 x 512-byte sectors (2 MiB) | |
| 36 | 4 | Checksum, which contains a one's complement of the sum of the dynamic disk header excluding the checksum itself | |
| 40 | 16 | Parent identifier, which contains a big-endian UUID that identifies the parent image. Only used by differential hard disk images | |
| 56 | 4 | Parent last modification time, which contains the number of seconds since January 1, 2000 00:00:00 UTC. Only used by differential hard disk images | |
| 60 | 4 | 0 | Unknown (Reserved should contain 0-byte values) |
| 64 | 512 | Parent name, which contains an UCS-2 big-endian string. Only used by differential hard disk images | |
| 576 | 8 x 24 = 192 | Array of parent locator entries. Only used by differential hard disk images | |
| 768 | 256 | 0 | Unknown (Reserved should contain 0-byte values) |
The maximum number of block allocation table entries should match the maximum possible number of blocks in the disk.
Note that the parent name can also contain a full path, e.g. in .avhd files. The part segments are separated by the \ character.
Parent locator entry
The parent locator entry is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Locator platform code | |
| 4 | 4 | Platform data space, which contains the number of 512-byte sectors needed to store the parent hard disk locator | |
| 8 | 4 | Locator data size | |
| 12 | 4 | 0 | Unknown (Reserved should contain 0-byte values) |
| 16 | 8 | Locator data offset, which contains the offset to the locator data. The offset is relative from the start of the file |
Locator platform code
| Value | Identifier | Description |
|---|---|---|
| 0 | None | |
| "Mac\x20" | Mac OS alias stored as a blob | |
| "MacX" | File URL with UTF-8 encoding conforming to RFC 2396 | |
| "W2ku" | Absolute Windows path, which contains an UCS-2 big-endian string | |
| "W2ru" | Windows path relative to the differential image, which contains an UCS-2 big-endian string | |
| "Wi2k" | Unknown (Deprecated) | |
| "Wi2r" | Unknown (Deprecated) |
Block allocation table
The block allocation table is only used in dynamic and differential disk images.
The block allocation table consists of 32-bit entries. An entry contains the sector number where the data block starts or is set to 0xffffffff (-1) if the block is sparse or stored in the parent disk image.
if block_allocation_table_entry == 0xffffffff:
block is sparse or stored in parent
else:
file_offset = (block_allocation_table_entry * 512 ) + sector_bitmap_size
Unused block in a dynamic disk are sparse and should be filled with zero byte values. In a differential disk the block is stored in the parent disk image.
Data blocks
Data blocks are only used in dynamic and differential disk images.
A data block consists of:
- sector bitmap
- sector data
size_of_bitmap (in bytes) = block_size / (512 * 8)
The size of the bitmap is rounded up to the next multitude of the sector size.
Sector bitmap
In dynamic disk images the sector bitmap indicates which sectors contain data (bit set to 1) or are sparse (bit set to 0).
In differential disk images the sector bitmap indicates which sectors are stored within the image (bit set to 1) or in the parent (bit set to 0).
The bitmap is padded to a 512-byte sector boundary.
The bitmap is stored on a per-byte basis with the MSB represents the first bit in the bitmap.
References
- VHD Specifications, by Microsoft
Virtual Hard Disk version 2 (VHDX) image format
The Virtual Hard Disk version 2 (VHDX) format is used by Microsoft vitualization products as one of its image formats. It is both used the store hard disk images and snapshots.
Overview
A VHDX image file consist of:
- file header
- 2x image headers
- 2x region tables
- log or metadata journal
- block allocation table (BAT) region
- metadata region
- metadata table
- metadata items
- image (content) data
The elements are stored in 64 KiB (65536 bytes) aligned blocks
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | N/A |
| Character strings | UCS-2 little-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
The number of bytes per sector is 512 or 4096 depending on the logical sector size.
File header
The file header of (file type identifier) is 64 KiB (65536 bytes) in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "vhdxfile" | Signature |
| 8 | 512 | Creator application and version, with contains an UCS-2 little-endian string with end-of-string character | |
| 520 | 65016 | Unknown (reserved) |
Image header
The image header is 4 KiB (4096 bytes) in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "head" | Signature |
| 4 | 4 | Checksum | |
| 8 | 8 | Sequence number | |
| 16 | 16 | File write identifier, which contains a GUID | |
| 32 | 16 | Data write identifier, which contains a GUID | |
| 48 | 16 | Log identifier, which contains a GUID | |
| 64 | 2 | Log format version | |
| 66 | 2 | 1 | Format version |
| 68 | 4 | Log size, which according to MS-VHDX this value must be a multitude of 1 MiB | |
| 72 | 8 | Log offset, which according to MS-VHDX this value must be a multitude of 1 MiB and greater than or equal to 1 MiB | |
| 80 | 4016 | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
Checksum calculation
The CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated over the 4 KiB bytes of data of the image header, where the image header checkum value is considered to be 0 during calculation.
Region table
The region table is stored in a block of 64 KiB (65536 bytes) and consists of:
- region table header
- 0 or more region table entries
- Unknown (reserved)
TODO: determine if 0 entries is actually supported
Region table header
The region table header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "regi" | Signature |
| 4 | 4 | Checksum | |
| 8 | 4 | Number of table entries, which according to MS-VHDX this value must be less than or equal to 2047 | |
| 12 | 4 | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
The CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated over the 64 KiB bytes of data of the region table where the image header checkum value is considered to be 0 during calculation.
Region table entry
The region table entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Region type identifier, which contains a GUID | |
| 16 | 8 | Region data offset, which contains an offset relative to the start of the file. According to MS-VHDX this value must be a multitude of 1 MiB and greater than or equal to 1 MiB | |
| 24 | 4 | Region data size, which according to MS-VHDX this value must be a multitude of 1 MiB | |
| 28 | 4 | Is required flag, which contains 1 to indicate the region type needs to be supported |
Region type identifiers
| Value | Identifier | Description |
|---|---|---|
| 2dc27766-f623-4200-9d64-115e9bfd4a08 | Block allocation table (BAT) region | |
| 8b7ca206-4790-4b9a-b8fe-575f050f886e | Metadata region |
Metadata region
The metadata region contains:
- metadata table
- metadata items
Metadata table
The metadata table is stored in a block of 64 KiB (65536 bytes) and consists of:
- metadata table header
- 0 or more metadata table entries
- Unknown (reserved)
TODO: determine if 0 entries is actually supported
Metadata table header
The metadata table header is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "metadata" | Signature |
| 8 | 2 | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
| 10 | 2 | Number of table entries, which according to MS-VHDX this value must be less than or equal to 2047 | |
| 12 | 20 | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
Metadata table entry
The metdata table entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Metadata item identifier, which contains a GUID | |
| 16 | 4 | Metadata item offset, which contains an offset relative to the start of the metadata region. According to MS-VHDX this value must be greater than 64 KiB | |
| 20 | 4 | Metadata item size | |
| 24 | 8 | Unknown |
TODO: describe last 8 bytes
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | IsUser | |
| 0x00000002 | IsVirtualDisk | |
| 0x00000004 | IsRequired |
Metadata items
Metadata item identifiers
| Value | Identifier | Description |
|---|---|---|
| 2fa54224-cd1b-4876-b211-5dbed83bf4b8 | Virtual disk size | |
| 8141bf1d-a96f-4709-ba47-f233a8faab5f | Logical sector size | |
| a8d35f2d-b30b-454d-abf7-d3d84834ab0c | Parent locator | |
| beca12ab-b2e6-4523-93ef-c309e000c746 | Virtual disk identifier | |
| caa16737-fa36-4d43-b3b6-33f0aa44e76b | File parameters | |
| cda348c7-445d-4471-9cc9-e9885251c556 | Physical sector size |
File parameters metadata item
The file parameters metadata item is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Block size, which according to MS-VHDX this value must be a power of 2 and greater than or equal to 1 MiB and not greater than 256 MiB | |
| 4.0 | 1 bit | Blocks remain allocated flag, which is used to indicate the file is a fixed-size image | |
| 4.1 | 1 bit | Has parent flag, which indicates if the VHDX file contains a differential image that has a parent image | |
| 4.2 | 30 bits | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
Logical sector size metadata item
The logical sector size metadata item is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Logical sector size, which according to MS-VHDX this value must be either 512 or 4096 |
Parent locator metadata item
The parent locator metadata item is of variable size and consits of:
- parent locator header
- 0 or more parent locator entry
- parent locator key and value data
TODO: determine if 0 entries is actually supported
Parent locator header
The parent locator header is 20 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Parent locator type indicator, which contains the GUID: b04aefb7-d19e-4a81-b789-25b8e9445913 | |
| 16 | 2 | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
| 18 | 2 | Number of entries (or key-value pairs) |
Parent locator entry
The parent locator entry is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Key data offset, which contains the offset relative from the start of the parent locator header | |
| 4 | 4 | Value data offset, which contains the offset relative from the start of the parent locator header | |
| 8 | 2 | Key data size | |
| 10 | 2 | Value data size |
Parent locator key and value data
A parent locator key or value is stored as UCS-2 little-endian string without end-of-string character.
Known keys are:
| Value | Description |
|---|---|
| absolute_win32_path | The value contains an absolute drive Windows path "\?\c:\file.vhdx" |
| parent_linkage | The value contains a string of a GUID. This GUID should correspond to the data write identifier of the parent image |
| parent_linkage2 | The value contains a string of a GUID |
| relative_path | The value contains a relative Windows path "..\file.vhdx" |
| volume_path | The value contains an absolute volume Windows path with "\?\Volume{%GUID%}\file.vhdx" |
Physical sector size metadata item
The physical sector size metadata item is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Physical sector size, which according to MS-VHDX this value must be either 512 or 4096 |
Virtual disk identifier metadata item
The virtual disk identifier metadata item is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Virtual disk identifier, which contains a GUID |
Note that in contrast to VHD (version 1) the virtual disk identifier does not change between a differential image and its parent. The data write identifier seems to be used instead.
Virtual disk size metadata item
The virtual disk size metadata item is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Virtual disk size |
Block allocation table (BAT) region
The block allocation table (BAT) region contains the block allocation table. The entries of this table describe the location of either blocks containing image content data (or payload blocks) or blocks containing a sector bitmap.
The size of an individual sector bitmap block is 1 MiB which allows for 2^23 sectors to be
represented by the bitmap.
Block allocation table (BAT) entries are grouped in chunks. The size of a chunk can be calculated as following:
number_of_entries_per_chunk = (2^23 * logical_sector_size) / block_size
The block allocation table (BAT) consists of:
- one or more chunks containing:
- number of entries per chunk x BAT entry describing image content data
- 1 x BAT entry describing the a sector bitmap
Unused BAT entries are filled with 0-byte values.
The block allocation table (BAT) of:
- a fixed-size image does not contain sector bitmap entries;
- a dynamic-size image does contain sector bitmap entries, although according to MS-VHDX are not used;
- a differential image does contain sector bitmap entries.
Block allocation table (BAT) entry
The block allocation table (BAT) entry is 64 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 3 bits | Block state | |
| 0.3 | 17 bits | 0 | Unknown (reserved), which according to MS-VHDX this value must be set to 0 |
| 2.4 | 44 bits | Block offset, which contains the offset relative from the start of the file as a multitude of 1 MiB |
Block states
Payload block states
| Value | Identifier | Description |
|---|---|---|
| 0 | PAYLOAD_BLOCK_NOT_PRESENT | Block is new and therefore not (yet) stored in the file |
| 1 | PAYLOAD_BLOCK_UNDEFINED | Block is not stored in the file |
| 2 | PAYLOAD_BLOCK_ZERO | Block is sparse and therefore filled with 0-byte values |
| 3 | PAYLOAD_BLOCK_UNMAPPED | Block has been unmapped |
| 6 | PAYLOAD_BLOCK_FULLY_PRESENT | Block is stored in the file |
| 7 | PAYLOAD_BLOCK_PARTIALLY_PRESENT | Block is stored in the parent |
Sector bitmap block states
| Value | Identifier | Description |
|---|---|---|
| 0 | SB_BLOCK_NOT_PRESENT | Block is new and therefore not (yet) stored in the file |
| 6 | SB_BLOCK_PRESENT | Block is stored in the file |
Sector bitmap
In differential disk images the sector bitmap indicates which sectors are stored within the image (bit set to 1) or in the parent (bit set to 0).
The bitmap is stored in a 1 MiB block.
The bitmap is stored on a per-byte basis with the LSB represents the first bit in the bitmap.
Log (metadata journal)
TODO: complete section
The log serves as metadata journal is of variable size and consist of contiguous circular (ring) buffer that contains log entries.
Log entry
TODO: complete section
4 KiB (4096 bytes) in size
Log entry header
TODO: complete section
Zero descriptor
TODO: complete section
Data descriptor
TODO: complete section
Data sector
TODO: complete section
References
- MS-VHDX: Virtual Hard Disk v2 (VHDX) File Format, by Microsoft
VMware Virtual Disk (VMDK) format
The VMware Virtual Disk (VMDK) format is used by VMware virtualization products as one of its image format.
Overview
A VMDK disk image can consist of multiple files, such as:
- descriptor file
- extent data files
- raw extent data file
- VMDK sparse extent data file
- COWD sparse extent data file
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | |
| Character strings | narrow character (Single Byte Character (SBC) or Multi Byte Character (MBC)) stored using a codepage defined in the descriptor file |
The number of bytes per sector is 512.
Disk types
There are multiple types of VMKD images, namely:
The 2GbMaxExtentFlat (or twoGbMaxExtentFlat) disk image, which consists of:
- a descriptor file (<name>.vmdk)
- raw data extent files (<name>-f###.vmdk), where ### is contains a decimal value starting with 1.
The 2GbMaxExtentSparse (or twoGbMaxExtentSparse) disk image, which consists of:
- a descriptor file (<name>.vmdk)
- VMDK sparse data extent files (<name>-s###.vmdk), where ### is contains a decimal value starting with 1.
The monolithicFlat disk image, which consists of:
- a descriptor file (<name>.vmdk)
- raw data extent file (<name>-f001.vmdk)
The monolithicSparse disk image, which consists of:
- VMDK sparse data extent file (<name>.vmdk) also contains the descriptor file data.
The vmfs disk image, which consists of:
- a descriptor file (<name>.vmdk)
- raw data extent file (<name>-flat.vmdk)
The vmfsSparse differential disk image, which consists of:
- a descriptor file (<name>.vmdk)
- COWD sparse data extent files (<name>-delta.vmdk)
TODO: describe more disk types
Delta links
A delta link is similar to a differential image where the image contains the changes (or delta) in comparison of a parent image. According to the Virtual Disk Format 5.0 specification one delta image can chain to another delta image.
TODO: Name <name>-delta.vmdk
Descriptor file
The descriptor file is a case-insensitive text based file that contains the following information:
- optional comment and empty lines
- header
- extent descriptions
- optional change tracking file
- disk data base (DDB)
Note that the descriptor file can contains leading and trailing whitespace. Lines are separated by a line feed character (0x0a). And leading comment (starting with #) and empty lines.
Header
The header of a descriptor file looks similar to the data below.
# Disk DescriptorFile
version=1
CID=12345678
parentCID=ffffffff
createType="twoGbMaxExtentSparse"
The header consists of the following values:
| Value | Description |
|---|---|
| "# Disk DescriptorFile" | Section header (or file signature) |
| version | Format version |
| encoding | Encoding |
| CID | Content identifier, which contains a random 32-bit value updated the first time the content of the virtual disk is modified after the virtual disk is opened |
| parentCID | The content identifier of the parent, which contains a 32-bit value identifying the parent content, where a value of 'ffffffff' (-1) represents no parent content |
| isNativeSnapshot | TODO: add description. A value of "no" has been observed in a VMWare Player 9 descriptor file |
| createType | Disk type |
| parentFileNameHint | Contains the path to the parent image, which is only present if the image is a differential image (delta link) |
TODO: confirm if a content identifier of ‘fffffffe’ (-2) represents that the long content identifier should be used
Format versions
| Value | Description |
|---|---|
| 1 | TODO: add description |
| 2 | TODO: add description |
| 3 | TODO: add description |
Encodings
Note that it is currently unknown which encodings are supported, currently it is assumed that at least the Windows codepages are supported and that the default is UTF-8.
| Value | Description |
|---|---|
| Big5 | Big5 assumed to be equivalent to Windows codepage 950 |
| GBK | GBK assumed to be equivalent to Windows codepage 936, which was observed in VMWare Workstation for Windows, Chinese edition |
| Shift_JIS | Shift_JIS assumed to be equivalent to Windows codepage 932, which was observed in VMWare Workstation for Windows, Japanese edition |
| UTF-8 | UTF-8 |
| windows-949-2000 | Windows codepage 949, 2000 version |
| windows-1252 | Windows codepage 1252, which was observed in VMWare Player 9 descriptor file |
Disk types
| Value | Description |
|---|---|
| 2GbMaxExtentFlat, twoGbMaxExtentFlat | The disk is split into fixed-size extents of maximum 2 GB, which consists of raw extent data files |
| 2GbMaxExtentSparse, twoGbMaxExtentSparse | The disk is split into sparse (dynamic-size) extents of maximum 2 GB, which consists of VMDK sparse extent data files |
| custom | TODO: add description. Descriptor file with arbitrary extents, used to mount v2i-format |
| fullDevice | The disk uses a full physical disk device |
| monolithicFlat | The disk is a single raw extent data file |
| monolithicSparse | The disk is a single VMDK sparse extent data file |
| partitionedDevice | The disk uses a full physical disk device, using access per partition |
| streamOptimized | The disk is a single compressed VMDK sparse extent data file |
| vmfs | The disk is a single raw extent data file, which is similar to the "monolithicFlat" |
| vmfsEagerZeroedThick | The disk is a single raw extent data file |
| vmfsPreallocated | The disk is a single raw extent data file |
| vmfsRaw | The disk uses a full physical disk device |
| vmfsRDM, vmfsRawDeviceMap | The disk uses a full physical disk device, which is also referred to as Raw Device Map (RDM) |
| vmfsRDMP, vmfsPassthroughRawDeviceMap | The disk uses a full physical disk device, which is similar to the Raw Device Map (RDM), but sends SCSI commands to underlying hardware |
| vmfsSparse | The disk is split into COWD sparse (dynamic-size) extents |
| vmfsThin | The disk is split into COWD sparse (dynamic-size) extents |
Extent descriptions
The extent descriptions of a descriptor file looks similar to the data below.
# Extent description
RW 4192256 SPARSE "test-s001.vmdk"
# Extent description
RW 1048576 FLAT "test-f001.vmdk" 0
The extent descriptions consists of the following values:
| Value | Description |
|---|---|
| "# Extent description" | Section header |
| Extent descriptors |
Extent descriptor
The extent descriptor consists of the following values:
| Value | Description |
|---|---|
| 1st | Access mode |
| 2nd | The number of sectors |
| 3rd | Extent type |
| If extent type is not ZERO | |
| 4th | Path of the VMDK extent data file, relative to the location of the VMDK descriptor file |
| Optional | |
| 5th | The extent start sector |
| Seen in VMWare Player 9 in combination with a physical device extent on Windows | |
| 6th and 7th | "partitionUUID" followed by a device identifier |
The extent offset is specified only for flat extents and corresponds to the offset in the file or device where the extent data is located. For device-backed virtual disks (physical or raw disks) the extent offset can be non-zero. For raw extent data files the extent offset should be zero.
Extent access mode
The extent access mode consists of the following values:
| Value | Description |
|---|---|
| NOACCESS | No access |
| RDONLY | Read only |
| RW | Read write |
Extent types
The extent type consists of the following values:
| Value | Description |
|---|---|
| FLAT | raw extent data file |
| SPARSE | VMDK sparse extent data file |
| ZERO | Sparse extent that consists of 0-byte values |
| VMFS | raw extent data file |
| VMFSSPARSE | COWD sparse extent data file |
| VMFSRDM | Unknown (Physical disk device that uses RDM?) |
| VMFSRAW | Unknown (Physical disk device?) |
Note that VMWare Player 9 has been observed to use “FLAT” for Windows devices
Change tracking file section
The change tracking file section was introduced in version 3 and looks similar to:
# Change Tracking File
changeTrackPath="test-flat.vmdk"
The change tracking file section consists of the following values:
| Value | Description |
|---|---|
| "# Change Tracking File" | Section header |
| changeTrackPath | Unknown (The path to the change tracking file?) |
Disk database
The disk data base of a descriptor file looks similar to the data below.
# The Disk Data Base
#DDB
ddb.virtualHWVersion = "4"
ddb.geometry.cylinders = "16383"
ddb.geometry.heads = "16"
ddb.geometry.sectors = "63"
ddb.adapterType = "ide"
ddb.toolsVersion = "0"
The disk data base consists of the following values:
| Value | Description |
|---|---|
| "# The Disk Data Base" | Section header |
| "#DDB" | Currently assumed to be part of the section header |
| ddb.deletable | Unknown (seen: "true") |
| ddb.virtualHWVersion | The virtual hardware version. For VMWare Player and Workstation this seems to correspond with the application version |
| ddb.longContentID | The long content identifier, which contains a 128-bit base16 encoded value, without spaces |
| ddb.uuid | UUIDm which contains a 128-bit base16 encoded value, with spaces between bytes |
| ddb.geometry.cylinders | The number of cylinders |
| ddb.geometry.heads | The number of heads |
| ddb.geometry.sectors | The number of sectors |
| ddb.geometry.biosCylinders | The number of cylinders as reported by the BIOS |
| ddb.geometry.biosHeads | The number of heads as reported by the BIOS |
| ddb.geometry.biosSectors | The number of sectors as reported by the BIOS |
| ddb.adapterType | Disk adapter type |
| ddb.toolsVersion | String containing the version of the installed VMWare tools version |
| ddb.thinProvisioned | Unknown (seen: "1") |
VirtualBox has been observed to use a different case for “disk” in the section header:
# The disk Data Base
Virtual hardware version
| Value | Description |
|---|---|
| 4 | TODO: add description |
| 6 | TODO: add description |
| 7 | TODO: add description |
| 9 | VMWare Player/Workstation 9.0 |
Disk adapter types
| Value | Description |
|---|---|
| ide | TODO: add description |
| buslogic | TODO: add description |
| lsilogic | TODO: add description |
| legacyESX | TODO: add description |
The buslogic and lsilogic values are for SCSI disks and show which virtual SCSI adapter is configured for the virtual machine. The legacyESX value is for older ESX Server virtual machines when the adapter type used in creating the virtual machine is not known.
The raw extent data file
The raw extent data file contains the actual disk data. The raw extent data file can be a file or a device.
This type of extent data file is also known as “Simple” or “Flat Extent”.
The VMDK sparse extent data file
The VMDK sparse extent data file contains the actual disk data. A VMDK sparse extent data file consists of:
- file header
- optional embedded descriptor file
- optional secondary grain directory
- optional secondary grain tables
- (primary) grain directory
- (primary) grain tables
- grains
- optional backup file header
This type of extent data file is also known as “Hosted Sparse Extent” or “Stream-Optimized Compressed Sparse Extent” when markers are used.
Note that the actual layout can vary per file, Stream-Optimized Compressed Sparse Extent have been observed to use secondary file headers.
Changes in format version 2:
- added encrypted disk support (though this feature never seem to never have been implemented).
Changes in format version 3:
- the size of extent files is no longer limited to 2 GiB;
- added support for persistent changed block tracking (CBT).
Note that “CBT”, the changeTrackPath value in the descriptor file references a file that describes changed areas on the virtual disk.
File header
The file header is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "KDMV" | Signature |
| 4 | 4 | 1, 2 or 3 | Format version |
| 8 | 4 | Flags | |
| 12 | 8 | Maximum data number of sectors (capacity) | |
| 20 | 8 | Sectors per grain, which must be a power of 2 and > 8 | |
| 28 | 8 | Embedded descriptor file start sector, which is relative from the start of the file or 0 if not set | |
| 36 | 8 | Embedded descriptor file size in sectors | |
| 44 | 4 | 512 | The number of grains table entries |
| 48 | 8 | Secondary grain directory start sector, which is relative from the start of the file or 0 if not set | |
| 56 | 8 | Primary grain directory start sector, which is relative from the start of the file, 0 if not set or 0xffffffffffffffff (GD_AT_END) if relative from the end of the file | |
| 64 | 8 | Metadata size in sectors | |
| 72 | 1 | Value to determine if the extent data file was cleanly closed (or dirty flag) | |
| 73 | 1 | '\n' | Single end of line character |
| 74 | 1 | ' ' | Non end of line character |
| 75 | 1 | '\r' | First double end of line character |
| 76 | 1 | '\n' | Second double end of line character |
| 77 | 2 | Compression method | |
| 79 | 433 | 0 | Unknown (Padding) |
The end of line characters are used to detect corruption due to file transfers that alter line end characters.
According to Virtual Disk Format 5.0 specification the maximum data number of sectors (capacity) should be a multitude of the sectors per grain. Note that it has been observed that this is not always the case.
If the primary grain directory start sector is 0xffffffffffffffff (GD_AT_END) in a Stream-Optimized Compressed Sparse Extent there should be a secondary file header stored at offset -1024 relative from the end of the file (stream) that contains the correct grain directory start sector.
Flags
The flags consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Valid new line detection test | |
| 0x00000002 | Use secondary grain directory. The secondary (redundant) grain directory should be used instead of the primary grain directory | |
| As of format version 2 | ||
| 0x00000004 | Use zeroed-grain table entry. The zeroed-grain table entry overloads grain data sector number 1 to indicate the grain is sparse | |
| Common | ||
| 0x00010000 | Has compressed grain data | |
| 0x00020000 | Contains metadata, where the file contains markers to identify metadata or data blocks | |
Compression method
The compression method consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | COMPRESSION_NONE | No compression |
| 0x00000001 | COMPRESSION_DEFLATE | Compression using Deflate (RFC1951) |
Markers
The markers are used in Stream-Optimized Compressed Sparse Extents. The corresponding flag must be set for markers to be present. An example of the layout of a Stream-Optimized Compressed Sparse Extent that uses markers is:
- file header
- embedded descriptor
- compressed grain markers
- grain table marker
- grain table
- grain directory marker
- grain directory
- footer marker
- secondary file header
- end-of-stream marker
The marker
The marker is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Value | |
| 8 | 4 | Marker data size | |
| If marker data size equals 0 | |||
| 12 | 4 | Marker type | |
| 16 | 496 | 0 | Unknown (Padding) |
| If marker data size > 0 | |||
| 12 | ... | Compressed grain data | |
If the marker data size > 0 the marker is a compressed grain marker.
Marker types
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | MARKER_EOS | End-of-stream marker |
| 0x00000001 | MARKER_GT | Grain table (metadata) marker |
| 0x00000002 | MARKER_GD | Grain directory (metadata) marker |
| 0x00000003 | MARKER_FOOTER | Footer (metadata) marker |
Compressed grain marker
The compressed grain marker indicates that compressed data follows.
| Offset | Size | Value | Description |
|---|---|---|---|
| Compressed grain header | |||
| 0 | 8 | 0 | Logical sector number |
| 8 | 4 | Compressed data size | |
| 12 | ... | Compressed data, which contains Deflate compressed data | |
Note that the compressed grain data can be larger than the grain data size.
End of stream marker
The end-of-stream marker indicates the end of the virtual disk. Basically the end-of-stream marker is an empty sector block.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0 | Value |
| 8 | 4 | 0 | Marker data size |
| 12 | 4 | MARKER_EOS | Marker type |
| 16 | 496 | 0 | Unknown (Padding) |
Grain table marker
The grain table marker indicates that a grain table follows the marker sector block.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0 | Value |
| 8 | 4 | 0 | Marker data size |
| 12 | 4 | MARKER_GT | Marker type |
| 16 | 496 | 0 | Unknown (Padding) |
| 512 | ... | Grain table |
Grain directory marker
The grain directory marker indicates that a grain directory follows the marker sector block.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0 | Value |
| 8 | 4 | 0 | Marker data size |
| 12 | 4 | MARKER_GD | Marker type |
| 16 | 496 | 0 | Unknown (Padding) |
| 512 | ... | Grain directory |
Footer marker
The footer marker indicates that a footer follows the marker sector block.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0 | Value |
| 8 | 4 | 0 | Marker data size |
| 12 | 4 | MARKER_FOOTER | Marker type |
| 16 | 496 | 0 | Unknown (Padding) |
| 512 | ... | Footer |
Grain directory
The grain directory is also referred to as level 0 metadata.
The size of the grain directory is dependent on the number of grains in the extent data file. The number of entries in the grain directory can be determined as following:
grain_table_size = number_of_grain_table_entries * grain_size
number_of_grain_directory_entries = maximum_data_size / grain_table_size
if maximum_data_size % grain_table_size > 0:
number_of_grain_directory_entries += 1
The grain directory consists of 32-bit grain table offsets:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Grain table start sector, which is relative from the start of the file or 0 if sparse or the sector is stored in the parent image |
The grain directory is stored in a multitude of 512 byte sized blocks.
Note that as of VMDK sparse extent data file version 2 if the “use zeroed-grain table entry” flag is set, a start sector of 1 indicates the grain table is sparse.
Grain table
The grain table is also referred to as level 1 metadata.
The size of the grain table is of variable size. The number of entries in the grain table is stored in the file header. Note that the number of entries in the last grain table is dependent on the maximum data size and not necessarily the same as the value stored in the file header.
The grain directory consists of 32-bit grain table offsets:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Grain data sector number, which is relative from the start of the file or 0 if sparse or the sector is stored in the parent image |
The number of entries in a grain table and should be 512, therefore the size of the grain table is 512 x 4 = 2048 bytes.
The grain table is stored in a multitude of 512 byte sized blocks.
Note that as of VMDK sparse extent data file version 2 if the “use zeroed-grain table entry” flag is set, a sector number of 1 indicates the grain table is sparse.
Grain data
In an uncompressed sparse extent data file the data is stored at the grain data sector number.
In a compressed sparse extent data file every non-sparse grain is assumed to be stored compressed.
Compressed grain data
The compressed grain data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Compressed grain header | |||
| 0 | 8 | 0 | Logical sector number |
| 8 | 4 | Compressed data size | |
| 12 | ... | Compressed data, which contains zlib compressed data | |
| ... | ... | Unknown (Padding) | |
The uncompressed data size should be the grain size or less for the last grain.
Footer
The footer is only used in Stream-Optimized Compressed Sparse Extents. The footer is the same as the file header. The footer should be the last block of the disk and immediately followed by the end-of-stream marker so that they together make up the last two sectors of the disk.
The header and footer differ in that the grain directory offset value in the header is set to 0xffffffffffffffff (GD_AT_END) and in the footer to the correct value.
Changed block tracking (CBT)
TODO: complete section
The COWD sparse extent data file
The copy-on-write disk (COWD) sparse extent data file contains the actual disk data. The COW sparse extent data file consists of:
- file header
- grain directory
- grain tables
- grains
This type of extent data file is also known as ESX Server Sparse Extent.
File header
The file header is 2048 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "COWD" | Signature |
| 4 | 4 | 1 | Format version |
| 8 | 4 | 0x00000003 | Unknown (Flags) |
| 12 | 4 | Maximum data number of sectors (capacity) | |
| 16 | 4 | Sectors per grain | |
| 20 | 4 | 4 | Grain directory start sector, which is relative from the start of the file or 0 if not set |
| 24 | 4 | Number of grain directory entries | |
| 28 | 4 | The next free sector | |
| In root extent data file | |||
| 32 | 4 | The number of cylinders | |
| 36 | 4 | The number of heads | |
| 40 | 4 | The number of sectors | |
| 44 | 1016 | Unknown (Empty values) | |
| In child extent data files | |||
| 32 | 1024 | Parent file name | |
| 1056 | 4 | Parent generation | |
| Common | |||
| 1060 | 4 | Generation | |
| 1064 | 60 | Name | |
| 1124 | 512 | Description | |
| 1636 | 4 | Saved generation | |
| 1640 | 8 | Unknown (Reserved) | |
| 1648 | 4 | Value to determine if the extent data file was cleanly closed (or dirty flag) | |
| 1652 | 396 | Unknown (Padding) | |
Note that the parent file name seems not to be set in recent delta sparse extent files.
Grain directory
The grain directory is also referred to as level 0 metadata.
The size of the grain directory is dependent on the number of grains in the extent data file. The number of entries in the grain directory is stored in the file header.
The grain directory consists of 32-bit grain table offsets:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Grain table start sector, which is relative from the start of the file or 0 if not set |
The grain directory is stored in a multitude of 512 byte sized blocks. Unused bytes are set to 0.
Grain table
The grain table is also referred to as level 1 metadata.
The size of the grain table is of variable size. The number of entries in a grain table is the fixed value of 4096.
The grain directory consists of 32-bit grain table offsets:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Grain sector number, which is relative from the start of the file or 0 if not set |
The grain table is stored in a multitude of 512 byte sized blocks. Unused bytes are set to 0.
Change tracking file
TODO: complete section
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\xa2\x72\x19\xf6" | Unknown (signature?) |
| 4 | 4 | 1 | Unknown (version?) |
| 8 | 4 | Unknown (empty values) | |
| 12 | 4 | 0x200 | Unknown |
| 16 | 8 | Unknown | |
| 24 | 8 | Unknown | |
| 32 | 4 | Unknown | |
| 36 | 4 | Unknown | |
| 40 | 4 | Unknown | |
| 44 | 16 | Unknown (UUID?) | |
| 60 | ... | Unknown (empty values?) |
Format edge cases and corruption scenarios
The total size specified by the number of grain table entries is lager than size specified by the maximum number of sectors. Seen in VMDK images generated by qemu-img.
Notes
The markers can be used to scan for the individual parts of the VMDK sparse extent data file if the stream has been truncated, but not that this can be very expensive process IO-wise.
References
- Virtual Disk Format 5.0, by VMWare
Volume system formats
A volume (or logical drive) is a single continous accessible storage area, typically containing a file system. A volume system is used to manage the storage of one or more volumes.
A partition is a part of (typically) storage media. A partition is different conceptually, but its usage is comparable to a volume.
This documentation uses the term volume systems in a broad sense to describe:
- encrypted volumes;
- logical volume managers;
- partitioning schemes;
- and volume snapshots.
Formats
- Apple Partition Map (APM)
- BitLocker Drive Encryption (BDE)
- BSD disklabel
- Core Storage (CS)
- GUID Partition Table (GPT)
- Linux Logical Volume Manager (LVM)
- Linux Unified Key Setup (LUKS) Disk Encryption
- Master Boot Record (MBR)
- SGI disklabel (sgilabel)
Also see: hybrid volume and file system formats
Apple Partition Map (APM) format
The Apple Partition Map (APM) format is used on Motorola based Macintosh computers. On Intel based Macintosh computers the GUID Partition Table (GPT) format is used.
Overview
An Apple Partition Map (APM) consists of:
- a drive descriptor
- partition map entry of type “Apple_partition_map”
- zero or more partition map entries
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | ASCII |
The number of bytes per sector is 512 or 2048.
Terminology
| Term | Description |
|---|---|
| Physical block | A fixed location on the storage media defined by the storage media |
| Logical block | An abstract location on the storage media defined by software |
The drive descriptor
The driver descriptor identifies the device drivers installed on a storage medium. The driver descriptor can contain refer to multiple device drivers. Every device driver is stored in a separate partition.
The drive descriptor is situated in the first block of the storage medium. This block is referred to as the device driver block. The driver descriptor block is not considered part of any partition.
The drive descriptor is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "\x45\x52" or "ER" | Signature |
| 2 | 2 | The block size of the device in bytes | |
| 4 | 4 | The number of blocks on the device | |
| 8 | 2 | Device type (Reserved) | |
| 10 | 2 | Device identifier (Reserved) | |
| 12 | 4 | Device data (Reserved) | |
| 16 | 2 | The number of driver descriptors | |
| 18 | 8 | The first device driver descriptor | |
| 26 | 484 | Additional driver descriptors, where unused entries are 16-bit integer values filled with 0 |
The device driver descriptor
The device driver descriptor is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Start block of the device driver | |
| 4 | 2 | Device driver number of blocks | |
| 6 | 2 | Operating system type, where is 1 represents "Mac OS" |
The partition map
The partition map is stored after the drive descriptor. The partition map consists of multiple entries that must be stored continuously. The partition map itself is considered a partition therefore the first entry in the partition map describes the partition map itself.
The partition map entry
A partition map entry is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "\x50\x4d" or "PM" | Signature |
| 2 | 2 | 0x00 | Unknown (Reserved) |
| 4 | 4 | Total number of entries in the partition map | |
| 8 | 4 | Partition start sector | |
| 12 | 4 | Partition number of sectors | |
| 16 | 32 | Partition name, which contains an ASCII string | |
| 48 | 32 | Partition type, which contains an ASCII string | |
| 80 | 4 | Data area start sector | |
| 84 | 4 | Data area number of sectors | |
| 88 | 4 | Status flags | |
| 92 | 4 | Boot code start sector | |
| 96 | 4 | Boot code number of sectors | |
| 100 | 4 | Boot code address | |
| 104 | 4 | Unknown (Reserved) | |
| 108 | 4 | Boot code entry point | |
| 112 | 4 | Unknown (Reserved) | |
| 116 | 4 | Boot code checksum | |
| 120 | 16 | Processor type | |
| 136 | 188 x 2 = 376 | 0x00 | Unknown (Reserved) |
Note that the partition name can be empty.
Partition types
The partition types consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| "Apple_Boot" | ||
| "Apple_Boot_RAID" | ||
| "Apple_Bootstrap" | ||
| "Apple_Driver" | ||
| "Apple_Driver43" | ||
| "Apple_Driver43_CD" | ||
| "Apple_Driver_ATA" | ||
| "Apple_Driver_ATAPI" | ||
| "Apple_Driver_IOKit" | ||
| "Apple_Driver_OpenFirmware" | ||
| "Apple_Extra" | ||
| "Apple_Free" | ||
| "Apple_FWDriver" | ||
| "Apple_HFS" | ||
| "Apple_HFSX" | ||
| "Apple_Loader" | ||
| "Apple_MDFW" | ||
| "Apple_MFS" | ||
| "Apple_partition_map" | ||
| "Apple_Patches" | ||
| "Apple_PRODOS" | ||
| "Apple_RAID" | ||
| "Apple_Rhapsody_UFS" | ||
| "Apple_Scratch" | ||
| "Apple_Second" | ||
| "Apple_UFS" | ||
| "Apple_UNIX_SVR2" | ||
| "Apple_Void" | ||
| "Be_BFS" | ||
| "MFS" |
Status flags
The partition status flags consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Is valid | |
| 0x00000002 | Is allocated | |
| 0x00000004 | Is in use | |
| 0x00000008 | Contains boot information | |
| 0x00000010 | Is readable | |
| 0x00000020 | Is writable | |
| 0x00000040 | Boot code is position independent | |
| 0x00000100 | Contains a chain-compatible driver | |
| 0x00000200 | Contains a real driver | |
| 0x00000400 | Contains a chain driver | |
| 0x40000000 | Automatic mount at startup | |
| 0x80000000 | Is startup partition |
Note that the “is in use” status flags does not appear to be used consistently.
BitLocker Drive Encryption (BDE) format
The BitLocker Drive Encryption (BDE) format is used by Microsoft Windows to encrypt volumes.
Overview
There are multiple versions of BitLocker Drive Encryption (BDE):
- BitLocker Windows Vista, Windows 7 and 10; used to encrypt volumes on fixed storage media, like harddisks, which typically contain NTFS file systems.
- BitLocker To Go; introduced in Windows 7; used to encrypt removable drives, which typically contain FAT file systems.
- BitLocker Used Disk Space Only encryption; used to encrypt only the used space of volumes, which presumably was introduced in Windows 8.
- BitLocker Encrypt-on-Write (EOW), which presumably was introduced in Windows 10 (1511).
Note that Windows treats NTFS volumes on removable drives are treated as NTFS volumes on fixed storage media.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | FILETIME in UTC |
| Character strings | UCS-2 little-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
Identifiers
BitLocker is known to use the following identifiers:
- 4967d63b-2e29-4ad8-8399-f6a339e3d001, for BitLocker and BitLocker To Go
- 92a84d3b-dd80-4d0e-9e4e-b1e3284eaed8, for BitLocker Used Disk Space Only encryption
Metadata files
BitLocker exposes various files in the “\System Volume Information” directory of the unencrypted volume that correspond to the BitLocker metadata areas.
The contents of the metadata files, on an unencrypted volume, consists of 0-byte values. It is assumed that these files are used to prevent the BitLocker metadata to be overwritten.
Note that not all tools zero out the metadata areas.
BitLocker Windows Vista
In BitLocker Windows Vista the “\System Volume Information” directory contains the following BitLocker related files:
- “FVE.{e40ad34d-dae9-4bc7-95bd-b16218c10f72}.[123]” maps a metadata block; typically 16384 bytes in size.
BitLocker Windows Windows 7 and later
In BitLocker Windows 7 and later the “\System Volume Information” directory contains the following BitLocker related files:
- “FVE2.{09cf57b8-9e6c-43d4-ae1f-0408882a397d}.[1-6]”, maps an Encrypt-on-Write (EOW) block map area, used by “Used Disk Space Only encryption”.
- “FVE2.{24e6f0ae-6a00-4f73-984b-75ce9942852d}” maps the block that contains the encrypted boot record; typically 8192 bytes in size.
- “FVE2.{93de4bce-e958-48b6-9fac-602d0294e5ef}.[12]”, maps Encrypt-on-Write (EOW) descriptor, used by “Used Disk Space Only encryption”.
- “FVE2.{aff97bac-a69b-45da-aba1-2cfbce434750}.[12]”, introduced in Windows 8, typically 512 bytes in size (possibly 1 sector in size?).
- “FVE2.{c9ca54a3-6983-46b7-8684-a7e5e23499e3}.[1-6]”, maps an Encrypt-on-Write (EOW) relocation log area, used by “Used Disk Space Only encryption”.
- “FVE2.{da392a22-cae0-4f0f-9a30-b8830385d046}”, introduced in Windows 10, typically 65536 bytes in size.
- “FVE2.{e40ad34d-dae9-4bc7-95bd-b16218c10f72}.[1-3]” maps a metadata block; typically 65536 bytes in size.
BitLocker To Go
BitLocker To Go uses a hybrid volume that has a encrypted and an unencrypted part. The unencrypted part contains various files. Application files for the BitLocker To Go helper application; which can also be found in:
C:\Windows\BitLockerDiscoveryVolumeContents\
- “COV 0000. BL” maps the block that contains the BitLocker To Go GUID and the offsets to the metadata; typically 32768 bytes in size.
- “COV 0000. ER” maps the encrypted data.
- “PAD 0000. PD” maps padding.
- “PAD 0000. NG” unknown; typically 0 bytes in size.
It has been observed that the “COV 0000. ER” and “PAD 0000. NG” files can be split in multiple 4294934528 byte (4 GiB - 32768) on a FAT32 volume, such as “COV 0001. ER”, “COV 0002. ER”, … or “PAD 0001. NG”, …
The “PAD 0000. NG” are presumaly used to fill the root directory with entries so that no new files may be created on the volume.
Keys
To encrypt storage media BitLocker uses different kind of keys.
Volume Master Key (VMK)
The Volume Master Key (VMK) is 256-bit of size and is stored in multiple Volume Master Key (VMK) structures. The VMK is stored encrypted with either the recovery password, external key, or the TPM.
It is also possible that the VMK is stored unencrypted which is referred to as clear key.
Full Volume Encryption Key (FVEK)
The Full Volume Encryption Key (FVEK) is stored encrypted with the Volume Master Key (VMK). The size of the FVEK is dependent on the encryption method used:
- For AES 128-bit the key is 128-bit of size
- For AES 256-bit the key is 256-bit of size
When Elephant Diffuser is used the key data of the structure that hold the FVEK is always 512-bit of size. The First 256-bit are reserved for the FVEK and the other 256-bit for the TWEAK key. Only 128-bit of the 256-bits are used when the encryption method is AES 128-bit.
TWEAK key
The TWEAK is stored encrypted with the Volume Master Key (VMK). The size of the TWEAK key is dependent on the encryption method used:
- For AES 128-bit the key is 128-bit of size
- For AES 256-bit the key is 256-bit of size
The TWEAK key is only present when Elephant Diffuser is used. The TWEAK key is stored in the key data of the structure that hold the Full Volume Encryption Key (FVEK) is always 512-bit of size. The First 256-bit are reserved for the FVEK and the other 256-bit for the TWEAK key. Only 128-bit of the 256-bits are used when the encryption method is AES 128-bit.
Recovery password
BitLocker provides for a recovery (or numerical) password to unlock the encrypted data. The recovery password is used to determine a recovery password key. Not to be confused with the “recovery key” key protector.
Example recovery password:
471207-278498-422125-177177-561902-537405-468006-693451
A valid recovery password consists of 48 digits where every number is dividable by 11 with a remainder of 0. The result of a division by 11 of a number is a 16-bit value. The individual 16-bit values make up a 128-bit key.
The corresponding recovery password key is calculated using the following approach, written partially in pseudo C:
Initialize a structure consisting of:
uint8_t last_sha256[ 32 ];
uint8_t initial_sha256[ 32 ];
uint8_t salt[ 16 ];
uint64_t count;
Initialize both the last SHA-256 and the count to 0.
Calculate the SHA-256 of the 128-bit key and update the initial SHA-256 value.
The salt is stored on disk in the stretch key which is stored in the recovery password key protected Volume Master Key (VMK).
Loop for 1048576 (0x100000) times:
- calculate the SHA-256 of the structure and update the last SHA-256 value
- increment the count by 1
The last SHA-256 value contains the 256-bit key which is recovery pass key that can unlock the recovery password key protected Volume Master Key (VMK).
Clear key
The clear key is an unprotected 256-bit key stored on the volume to decrypt the VMK. It is used when the encrypted volume is being decrypted.
External key
The external key is used by the “startup key” and “recovery key” key protectors. The external key is stored in a file named “{%GUID%}.BEK”. The GUID in the filename equals the key identifier in the BitLocker metadata.
There can be multiple external keys for a single BitLocker encrypted volume. Each key is identified a by a different key identifier.
User key
BitLocker To Go provides for a user password (or passphrase) to unlock the encrypted data. The user password is used to determine a user key.
TODO: check if the password can be maximal 49 characters in size.
Convert the user password into a UCS-2 little-endian string.
Initialize a structure consisting of:
uint8_t last_sha256[ 32 ];
uint8_t initial_sha256[ 32 ];
uint8_t salt[ 16 ];
uint64_t count;
Initialize both the last SHA-256 and the count to 0.
Calculate the SHA-256 of the user password.
Calculate the SHA-256 of the SHA-256 of the user password, and set it as the initial SHA-256 value.
The salt is stored on disk in the stretch key which is stored in the user key (or password) protected Volume Master Key (VMK).
Loop for 1048576 (0x100000) times:
- calculate the SHA-256 of the structure and update the last SHA-256 value
- increment the count by 1
The last SHA-256 value contains the 256-bit key which is user key that can unlock the user key (or password) protected Volume Master Key (VMK).
Encryption methods
BitLocker uses different kind of encryption methods. To encrypt the sector data it either uses AES-CBC (Cipher Block Chaining) with or without Elephant Elephant Diffuser or AES-XTS (XEX-based tweaked-codebook mode with ciphertext stealing).
To encrypt the key data BitLocker uses AES-CCM (Counter with cipher block chaining message authentication code (CBC-MAC)).
AES-CBC
Both encryption and decryption use:
- AES-CBC with FVEK decryption of sector data
The initialization vector of the AES-CBC is the sector offset AES-ECB encrypted with the FVEK stored as a 16-byte little-endian value. The sector offset is the offset of the sector relative from the start of the volume.
AES-CBC with Elephant Diffuser
Encryption:
- XOR with sector key
- Elephant Elephant Diffuser A
- Elephant Elephant Diffuser B
- AES-CBC with FVEK
Decryption:
- AES-CBC with FVEK
- Elephant Elephant Diffuser B
- Elephant Elephant Diffuser A
- XOR with sector key
The initialization vector of the AES-CBC is the sector offset AES-ECB encrypted with the FVEK stored as a 16-byte little-endian value. The sector offset is the offset of the sector relative from the start of the volume.
The sector key 32-byte of size and contains:
- the lower 16-byte contain a little-endian version of the offset of the sector, relative from the start of the volume, AES-ECB encrypted with the TWEAK key
- the upper 16-byte contain a 16-byte little-endian version of the offset of the sector, relative from the start of the volume, with the most upper bit set (or upper byte set to 0x80) AES-ECB encrypted with the TWEAK key
AES-CCM
The key data is encrypted using AES-CCM with an initialization vector of 0.
AES-XTS
The FVEK contains both XTS keys.
Both encryption and decryption use:
- AES-XTS with FVEK decryption of sector data
The initialization vector of the AES-XTS is the sector number stored as a 16-byte little-endian value. The sector number is the offset of the sector relative from the start of the volume divided by the sector size.
Elephant Diffuser
The Elephant Diffuser A and B variants are described in “AES-CBC + Elephant diffuser - A Disk Encryption Algorithm for Windows Vista”.
Virtual sector(s)
In BitLocker the certain sector(s) of the encrypted storage media are handled in a specific manner. These are sectors to store:
- the unencrypted boot record
- the BitLocker metadata
BitLocker Windows Vista
In BitLocker Windows Vista the first sector of the unencrypted boot recored sector is reconstructed by replacing values in the BitLocker boot record, namely
- replacing the “File system signature” with “NTFS\x20\x20\x20\x20”
- replacing the “metadata block 1 cluster block number” with the “MTF mirror cluster block number”
The 15 sectors directly following the first sector are also unencrypted.
The sectors that contain the BDE metadata are shown as empty sectors; containing 0-byte values.
BitLocker Windows 7 and To Go
Both BitLocker Windows 7 and To Go store an encrypted version of the unencrypted boot record in a specific location. This location is defined in the metadata area descriptors.
The encrypted boot record is commonly 8192 bytes an size, entailing the first 16 sectors.
The sectors that contain the encrypted boot recored and the BDE metadata are shown as empty sectors; containing 0-byte values.
BitLocker Windows 10
In later versions of Bitlocker Windows 10 the metadata area descriptors is not always present.
The number of boot record sectors in the metadata block header can be used to determine the boot record size.
The encrypted boot record is commonly 8192 bytes an size, entailing the first 16 sectors.
Boot record
BitLocker Windows Vista
The BitLocker Windows Vista boot record is similar to a NTFS boot record. The differences have been emphasized in bold. The boot record is 512 bytes of size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x52\x90" | Boot entry point |
| 3 | 8 | "-FVE-FS-" | File system signature |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector. Note that the following values are supported by mkntfs: 256, 512, 1024, 2048 and 4096 | |
| 13 | 1 | Number of sectors per cluster block | |
| 14 | 2 | 0 | Unknown (Reserved Sectors), which is not used by NTFS and must be 0 |
| 16 | 1 | 0 | Number of cluster block allocation tables, which is not used by NTFS and must be 0 |
| 17 | 2 | 0 | Number of root directory entries, which is not not used by NTFS and must be 0 |
| 19 | 2 | 0 | Number of sectors (16-bit), which is not used by NTFS must be 0 |
| 21 | 1 | Media descriptor | |
| 22 | 2 | 0 | Cluster block allocation table size (16-bit) in number of sectors, which is not used by NTFS and must be 0 |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | 0x3f | Sectors per track, which is not used by NTFS |
| 26 | 2 | 0xff | Number of heads, which is not used by NTFS |
| 28 | 4 | 0x3f | Number of hidden sectors, which is not used by NTFS |
| 32 | 4 | 0x00 | Number of sectors (32-bit), which is not used by NTFS must be 0 |
| NTFS version 8.0 BIOS parameter block (BPB) or extended BPB, which was introduced in Windows NT 3.1 | |||
| 36 | 1 | 0x80 | Unknown (Disc unit number), which is not used by NTFS |
| 37 | 1 | 0x00 | Unknown (Flags), which is not used by NTFS |
| 38 | 1 | 0x80 | Unknown (BPB version signature byte), which is not used by NTFS |
| 39 | 1 | 0x00 | Unknown (Reserved), which is not used by NTFS |
| 40 | 8 | Number of sectors (64-bit) | |
| 48 | 8 | Master File Table (MFT) cluster block number | |
| 56 | 8 | Metadata block 1 cluster block number | |
| 64 | 4 | MFT entry size | |
| 68 | 4 | Index entry size | |
| 72 | 8 | Volume serial number | |
| 80 | 4 | 0 | Checksum, which is not used by NTFS |
| Common | |||
| 84 | 426 | Boot code | |
| 510 | 2 | "\x55\xaa" | The (boot) signature |
Note that the number of sectors can be 1 less then the value indicated in the partition table.
BitLocker Windows 7 and later
The BitLocker Windows 7 boot record for a NTFS volume is similar to a FAT32 boot record. The differences have been emphasized in bold. The boot record is 512 bytes of size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x58\x90" | Boot entry point Boot entry point (JMP +90, NOP) |
| 3 | 8 | "-FVE-FS-" | File system signature (or OEM name) |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector, which must be 512, 1024, 2048 or 4096 | |
| 13 | 1 | Sectors per cluster block, which must be 1, 2, 4, 8, 16, 32, 64 or 128 | |
| 14 | 2 | Number of reserved sectors (reserved region), which starts at the first sector of the volume (sector 0) and must be 1 or more (typically 1 or 32) | |
| 16 | 1 | Number of cluster block allocation tables, which must be 1 or more (typically 2) | |
| 17 | 2 | Number of root directory entries | |
| 19 | 2 | Total number of sectors (16-bit) | |
| 21 | 1 | Media descriptor | |
| 22 | 2 | Cluster block allocation table size (16-bit) | |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | Number of sectors per track | |
| 26 | 2 | Number of heads | |
| 28 | 4 | Number of hidden sectors, which contains the volume start sector number | |
| 32 | 4 | Total number of sectors (32-bit) | |
| 36 | 4 | Cluster block allocation table size (32-bit), in number of sectors | |
| 40 | 2 | Extended flags | |
| 42 | 1 | Format revision minor number | |
| 43 | 1 | Format revision major number | |
| 44 | 4 | Root directory start cluster | |
| 48 | 2 | File system information (FSINFO) sector number | |
| 50 | 2 | Boot record sector number | |
| 52 | 12 | Unknown (reserved) | |
| 64 | 1 | Drive number | |
| 65 | 1 | Unknown (reserved for Windows NT) | |
| 66 | 1 | Extended boot signature | |
| If extended boot signature == 0x29 | |||
| 67 | 4 | Volume serial number, which can be derived from the system current date and time | |
| 71 | 11 | Volume label, which contains a narrow character string or "NO\x20NAME\x20\x20\x20\x20" if not set | |
| 82 | 8 | "FAT32\x20\x20\x20" | File system hint, which is informational and not required |
| If extended boot signature != 0x29 | |||
| 67 | 23 | Unknown | |
| Common | |||
| 90 | 70 | Bootcode | |
| 160 | 16 | BitLocker identifier, which contains a GUID | |
| 176 | 8 | Metadata block 1 offset, which is relative to the start of the volume | |
| 184 | 8 | Metadata block 2 offset, which is relative to the start of the volume | |
| 192 | 8 | Metadata block 3 offset, which is relative to the start of the volume | |
| 200 | 310 | Unknown (part of bootcode) | |
| 510 | 2 | 0x55 0xaa | Sector signature |
BitLocker To Go
BitLocker To Go on an NTFS volume is similar to BitLocker Windows 7. The BitLocker Windows To Go boot record for a FAT volume is similar to FAT32 boot record. The differences have been emphasized in bold. The boot record is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x58\x90" | Boot entry point |
| 3 | 8 | "MSWIN4.1" | File system signature (or OEM name) |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector, which must be 512, 1024, 2048 or 4096 | |
| 13 | 1 | Sectors per cluster block, which must be 1, 2, 4, 8, 16, 32, 64 or 128 | |
| 14 | 2 | Number of reserved sectors (reserved region), which starts at the first sector of the volume (sector 0) and must be 1 or more (typically 1 or 32) | |
| 16 | 1 | Number of cluster block allocation tables, which must be 1 or more (typically 2) | |
| 17 | 2 | Number of root directory entries | |
| 19 | 2 | Total number of sectors (16-bit) | |
| 21 | 1 | Media descriptor | |
| 22 | 2 | Cluster block allocation table size (16-bit) | |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | Number of sectors per track | |
| 26 | 2 | Number of heads | |
| 28 | 4 | Number of hidden sectors | |
| 32 | 4 | Total number of sectors (32-bit) | |
| 36 | 4 | Cluster block allocation table size (32-bit), in number of sectors | |
| 40 | 2 | Extended flags | |
| 42 | 1 | Format revision minor number | |
| 43 | 1 | Format revision major number | |
| 44 | 4 | Root directory start cluster | |
| 48 | 2 | File system information (FSINFO) sector number | |
| 50 | 2 | Boot record sector number | |
| 52 | 12 | Unknown (reserved) | |
| 64 | 1 | Drive number | |
| 65 | 1 | Unknown (reserved for Windows NT) | |
| 66 | 1 | Extended boot signature | |
| If extended boot signature == 0x29 | |||
| 67 | 4 | Volume serial number, which can be derived from the system current date and time | |
| 71 | 11 | Volume label, which contains a narrow character string or "NO\x20NAME\x20\x20\x20\x20" if not set | |
| 82 | 8 | "FAT32\x20\x20\x20" | File system hint, which is informational and not required |
| If extended boot signature != 0x29 | |||
| 67 | 23 | Unknown | |
| Common | |||
| 90 | 334 | Bootcode | |
| 424 | 16 | BitLocker identifier, which contains a GUID | |
| 440 | 8 | Metadata block 1 offset, which is relative to the start of the volume | |
| 448 | 8 | Metadata block 2 offset, which is relative to the start of the volume | |
| 456 | 8 | Metadata block 3 offset, which is relative to the start of the volume | |
| 464 | 46 | Unknown | |
| 510 | 2 | 0x55 0xaa | Sector signature |
BitLocker Used Disk Space Only encryption
The BitLocker Used Disk Space Only encryption boot record for a NTFS volume is similar to a BitLocker Windows 7 and later boot record. The differences have been emphasized in bold. The boot record is 512 bytes of size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x58\x90" | Boot entry point Boot entry point (JMP +90, NOP) |
| 3 | 8 | "-FVE-FS-" | File system signature (or OEM name) |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector, which must be 512, 1024, 2048 or 4096 | |
| 13 | 1 | Sectors per cluster block, which must be 1, 2, 4, 8, 16, 32, 64 or 128 | |
| 14 | 2 | Number of reserved sectors (reserved region), which starts at the first sector of the volume (sector 0) and must be 1 or more (typically 1 or 32) | |
| 16 | 1 | Number of cluster block allocation tables, which must be 1 or more (typically 2) | |
| 17 | 2 | Number of root directory entries | |
| 19 | 2 | Total number of sectors (16-bit) | |
| 21 | 1 | Media descriptor | |
| 22 | 2 | Cluster block allocation table size (16-bit) | |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | Number of sectors per track | |
| 26 | 2 | Number of heads | |
| 28 | 4 | Number of hidden sectors, which contains the volume start sector number | |
| 32 | 4 | Total number of sectors (32-bit) | |
| 36 | 4 | Cluster block allocation table size (32-bit), in number of sectors | |
| 40 | 2 | Extended flags | |
| 42 | 1 | Format revision minor number | |
| 43 | 1 | Format revision major number | |
| 44 | 4 | Root directory start cluster | |
| 48 | 2 | File system information (FSINFO) sector number | |
| 50 | 2 | Boot record sector number | |
| 52 | 12 | Unknown (reserved) | |
| 64 | 1 | Drive number | |
| 65 | 1 | Unknown (reserved for Windows NT) | |
| 66 | 1 | Extended boot signature | |
| If extended boot signature == 0x29 | |||
| 67 | 4 | Volume serial number, which can be derived from the system current date and time | |
| 71 | 11 | Volume label, which contains a narrow character string or "NO\x20NAME\x20\x20\x20\x20" if not set | |
| 82 | 8 | "FAT32\x20\x20\x20" | File system hint, which is informational and not required |
| If extended boot signature != 0x29 | |||
| 67 | 23 | Unknown | |
| Common | |||
| 90 | 70 | Bootcode | |
| 160 | 16 | BitLocker Used Disk Space Only identifier, which contains a GUID | |
| 176 | 8 | Metadata block 1 offset, which is relative to the start of the volume | |
| 184 | 8 | Metadata block 2 offset, which is relative to the start of the volume | |
| 192 | 8 | Metadata block 3 offset, which is relative to the start of the volume | |
| 200 | 8 | Encrypt-on-Write descriptor 1 offset, which is relative to the start of the volume | |
| 208 | 8 | Encrypt-on-Write descriptor 2 offset, which is relative to the start of the volume | |
| 216 | 294 | Unknown (part of bootcode) | |
| 510 | 2 | 0x55 0xaa | Sector signature |
Note that Windows 10 version for Bitlocker without “Used Disk Space Only encryption” can also contain Encrypt-on-Write structures within the metadata blocks, these might be remnants of of encrypting the volume.
Metadata block
A BitLocker encrypted volume contains 3 metadata blocks. Which is typically:
- 12384 bytes in size for BitLocker Windows Vista;
- 65536 bytes in size for BitLocker Windows 7 and later.
A metadata block consists of:
- a (metadata) block header
- a metadata header
- an array of metadata entries
- padding (0-byte values)
Metadata block header
Metadata block header version 1 - Windows Vista
The metadata block header version 1 is 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "-FVE-FS-" | Signature |
| 8 | 2 | Unknown (Header size?) | |
| 10 | 2 | 1 | Format version |
| 12 | 2 | Unknown, which is commonly 0x04 | |
| 14 | 2 | Unknown, which is commonly 0x04 | |
| 16 | 16 | 0 | Unknown (empty values) |
| 32 | 8 | metadata block 1 offset, which is relative to the start of the volume | |
| 40 | 8 | metadata block 2 offset, which is relative to the start of the volume | |
| 48 | 8 | metadata block 3 offset, which is relative to the start of the volume | |
| 56 | 8 | MFT mirror cluster block number |
Metadata block header version 2 – Windows 7 and later
The metadata block header version 2 is 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "-FVE-FS-" | Signature |
| 8 | 2 | Unknown (Header size?) | |
| 10 | 2 | 2 | Format version |
| 12 | 2 | Unknown, which is commonly 0x04, but 0x05 has been observed in a partial decrypted volume (protection status?) | |
| 14 | 2 | Unknown, which is commonly 0x04, but 0x01 has been observed in a partial decrypted volume | |
| 16 | 8 | Encrypted volume size, in number of bytes | |
| 24 | 4 | Unknown | |
| 28 | 4 | Number of boot record sectors | |
| 32 | 8 | metadata block 1 offset, which is relative to the start of the volume | |
| 40 | 8 | metadata block 2 offset, which is relative to the start of the volume | |
| 48 | 8 | metadata block 3 offset, which is relative to the start of the volume | |
| 56 | 8 | Boot record offset, which is relative to the start of the volume and contains the offset of the boot record of the unencrypted bolume |
Note that when decrypting BitLocker will decrypt from the back to the front. The encrypted volume size contains the number of bytes of the volume that are still encrypted (or need to be decrypted).
Metadata header
The metadata header is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Metadata size, which includes the size value but not the size of the metadata block header | |
| 4 | 4 | 1 | Format version |
| 8 | 4 | 48 | Metadata header size |
| 12 | 4 | Metadata size copy | |
| 16 | 16 | Volume identifier, which contains a GUID | |
| 32 | 4 | Next nonce counter | |
| 36 | 2 | Encryption method | |
| 38 | 2 | Copy of encryption method | |
| 40 | 8 | Creation time, which contains a FILETIME |
Note that it is currently unknown what the copy of encryption method value is used for.
Encryption methods
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | Unknown (Not encrypted/External Key) | |
| 0x1000 | Unknown (Used in stretch key) | |
| 0x1001 | Unknown (Used in stretch key) | |
| 0x2000 | Unknown (Used in AES-CCM encrypted key) | |
| 0x2001 | Unknown (Used in AES-CCM encrypted key) | |
| 0x2002 | Unknown (Used in AES-CCM encrypted key) | |
| 0x2003 | Unknown (Used in AES-CCM encrypted key) | |
| 0x2004 | Unknown (Used in AES-CCM encrypted key) | |
| 0x2005 | Unknown (Used in AES-CCM encrypted key) | |
| 0x8000 | AES-128-CBC with Elephant Diffuser | |
| 0x8001 | AES-256-CBC with Elephant Diffuser | |
| 0x8002 | AES-128-CBC | |
| 0x8003 | AES-256-CBC | |
| 0x8004 | AES-128-XTS | |
| 0x8005 | AES-256-XTS |
Metadata entry
The metadata entry is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Entry size, which includes the size value | |
| 2 | 2 | Entry type | |
| 4 | 2 | Value type | |
| 6 | 2 | Unknown (Flags? Values of 0x0001, 0x0003, 0x0005 and 0x0105 have been observed) | |
| 8 | ... | Entry data |
Metadata entry types
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | None, entry is a property | |
| 0x0002 | Volume Master Key (VMK) | |
| 0x0003 | Full Volume Encryption Key (FVEK) | |
| 0x0004 | Unknown (Validation) | |
| 0x0006 | Startup key | |
| 0x0007 | Description (Drive label), which contains computer name, volume name and date | |
| 0x000b | Unknown (FVEAutoUnlock key?) | |
| 0x000f | Metadata area descriptors |
Note that older versions of BitLocker use a locale dependent date format in the description, such as “MM/DD/YYYY”. Recent versions of BitLocker use “YYYY-MM-DD”.
Metadata value types
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | Erased | |
| 0x0001 | Key | |
| 0x0002 | String, which contains an UCS-2 little-endian string with end-of-string character | |
| 0x0003 | Stretch Key | |
| 0x0004 | Use Key | |
| 0x0005 | AES-CCM encrypted key | |
| 0x0006 | TPM encoded key | |
| 0x0007 | Validation | |
| 0x0008 | Volume master key | |
| 0x0009 | External key | |
| 0x000a | Update | |
| 0x000b | Error | |
| 0x000f | Metadata area descriptors |
Key encrypted key (KEK)
The key encrypted key has value type 0x0001 and is variable in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Encryption method | |
| 2 | 2 | Unknown | |
| 4 | ... | Key data |
Stretch encrypted key
The stretch encrypted key has value type 0x0003 and is variable in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Encryption method | |
| 2 | 2 | Unknown | |
| 4 | 16 | Salt | |
| 20 | ... | Metadata entry, which contains an AES-CCM encrypted key |
AES-CCM encrypted key
The AES-CCM encrypted key has value type 0x0005 and is variable in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Nonce | |||
| 0 | 8 | Nonce date and time, which contains a FILETIME | |
| 8 | 4 | Nonce counter | |
| 12 | 16 | AES-CCM tag (CBC-MAC) | |
| 28 | ... | AES-CCM encrypted data | |
Unencrypted (AES-CCM encrypted) data
The unencrypted (AES-CCM encrypted) data is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Size, which does not include the size of the MAC | |
| 4 | 2 | 1 | Unknown (Format version) |
| 6 | 2 | Unknown | |
| 8 | 2 | Encryption method | |
| 10 | 2 | Unknown | |
| 12 | ... | Unencrypted key data |
TPM encoded key
The TPM encoded key has value type 0x0006 and is variable in size and consists of:
TODO: complete section
Validation
The validation has value type 0x0007 and is variable in size and consists of:
TODO: complete section
Volume Master Key (VMK)
The Volume Master Key has value type 0x0008 and is variable in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Key identifier, which contains a GUID | |
| 16 | 8 | Last modification date and time, which contains a FILETIME | |
| 24 | 2 | Unknown | |
| 26 | 2 | Protector type | |
| 28 | ... | Properties, which contains an array of metadata entries, where entry type is 0 |
The available properties depend on the VMK type.
The clear key protected VMK consists of:
- key (with 256-bit of key data)
- AES-CCM encrypted key
The external key protected VMK consists of:
- optional description string containing “ExternalKey\x00”
- Stretch Key
- AES-CCM encrypted key
The password protected VMK consists of:
- optional description string containing “ExternalKey\x00”
- Stretch Key
- AES-CCM encrypted key
The recovery password key protected VMK consists of:
- optional description string containing “DiskPassword\x00”
- Stretch Key
- AES-CCM encrypted key
The TPM protected VMK consists of:
- optional description string containing “TPM Protection\x00”
- TPM encoded key
Key protector types
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | VMK protected with clear key, which basically is an unprotected VMK | |
| 0x0100 | VMK protected with TPM | |
| 0x0200 | VMK protected with external key (startup key or recovery key) | |
| 0x0500 | VMK protected with TPM and PIN | |
| 0x0800 | VMK protected with recovery password | |
| 0x2000 | VMK protected with password (or passphrase) |
TODO: SID (or Active Directory account or group) protector
External Key
The External Key has value type 0x0009 and is variable in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Key identifier, which contains a GUID | |
| 16 | 8 | Last modification date and time, which contains a FILETIME | |
| 24 | ... | Properties, which contains an array of metadata entries where the entry type is set to 0 |
The available properties:
- optional description string containing “ExternalKey\x00”
- key
Metadata area descriptors
The metadata area descriptors has value type 0x000f and is 16 or more bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Boot record offset | |
| 8 | 8 | Boot record size | |
| If size > 16, related to "FVE2.{aff97bac-a69b-45da-aba1-2cfbce434750}.[12]"? | |||
| 16 | 2 | 3, 4 or 5 | Unknown (format version or number of descriptors?) |
| 18 | 2 | 36, 60 or 76 | Unknown (size of additional data?) |
| 20 | 4 | Unknown | |
| 24 | 8 | Unknown (flags?) | |
| 32 | 8 | Unknown (offset?) | |
| 40 | 4 | Unknown (size?) | |
| 44 | 4 | 512 | Logical sector size |
| 48 | 4 | 512 or 4096 | Physical sector size |
| If additional data size > 36 | |||
| 52 | 8 | 10 | Unknown |
| 60 | 4 | 0x0000295a, 0x00004a61, 0x00004a62 or 0x00004a65 | Unknown |
| 64 | 8 | 2 | Unknown |
| 72 | 4 | 0x02010110 | Unknown |
| If additional data size > 60 | |||
| 76 | 8 | Unknown area offset, related to "FVE2.{da392a22-cae0-4f0f-9a30-b8830385d046}" | |
| 84 | 8 | Unknown area size, related to "FVE2.{da392a22-cae0-4f0f-9a30-b8830385d046}" | |
The metadata area descriptors seems to have been introduced in Windows 7. They specify the location, within the encrypted volume, where certain metadata is stored, such as the unencrypted boot record.
The unencrypted boot record is commonly 8192 bytes in size for BitLocker Windows 7 (and later) and 5365760 bytes for BitLocker To Go.
Unknown flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | ||
| 0x02 | ||
| 0x04 | ||
| 0x08 | ||
| 0x10 | ||
| 0x20 | ||
| 0x40 | Unknown (related to pause? seen: 0x4b while paused of used disk space only) | |
| 0x80 | Unknown (related to pause? seen: 0xcb after initialization of used disk space only) |
BitLocker External Key (BEK) file
A BitLocker External Key (BEK) file is commonly 156 bytes in size and consists of:
- a file header
- an array of metadata entries
BEK file header
The BEK file header is similar to the metadata header. The BEK file header is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Metadata size, which includes the size value | |
| 4 | 4 | 1 | Format version |
| 8 | 4 | 48 | Metadata header size |
| 12 | 4 | Unknown (Metadata size copy) | |
| 16 | 16 | Volume identifier, which contains a GUID | |
| 32 | 4 | Next nonce counter | |
| 36 | 4 | Encryption method | |
| 40 | 8 | Creation time, which contains a FILETIME |
The key identifier in the file must match the key identifier in the Volume Master Key (VMK).
BEK metadata entry
The format of a BEK metadata entry is similar to the format of a metadata entry.
The metadata in a BEK file consists of an external key, which contains 256-bits of unprotected key data.
The identifier of the VMK should match the identifier in the BEK file header.
Encrypt-on-Write (EOW)
Checksums use a CRC-32 with the polynominal 0xedb88320 and initial value 0.
Encrypt-on-Write (FVE-EOW) descriptor
The Encrypt-on-Write (FVE-EOW) descriptor is variable of size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Header | |||
| 0 | 8 | "FVE-EOW\x00" | Signature |
| 8 | 2 | 56 | Header size |
| 10 | 2 | Data size | |
| 12 | 4 | Logical sector size (or EOW data sector size?) | |
| 16 | 4 | Physical sector size (or block record size?) | |
| 20 | 4 | Relocation block size | |
| 24 | 4 | Encrypt-on-Write relocation log area size | |
| 28 | 4 | Encrypt-on-Write relocation log entry size | |
| 32 | 4 | Number of block map offsets | |
| 36 | 4 | Checksum of the data from the start of the Encrypt-on-Write descriptor upto data size with the checksum value set to 0 | |
| 40 | 8 | Encrypt-on-Write descriptor 1 offset | |
| 48 | 8 | Encrypt-on-Write descriptor 2 offset | |
| 56 | number x 8 | Array of 64-bit block map area offsets | |
| ... | ... | Unknown (empty values) | |
Note that Encrypt-on-Write descriptor 1 and 2 are copies and should reference the same block map areas.
Encrypt-on-Write block map area
The Encrypt-on-Write block map area is variable of size and consists of:
- Encrypt-on-Write block map (FVE-EOWBM)
- One or more Encrypt-on-Write block (map) records
Note that “FVE2.{09cf57b8-9e6c-43d4-ae1f-0408882a397d}.[1-6]” masks the Encrypt-on-Write block map area, with a 4096 aligment size.
Encrypt-on-Write block map (FVE-EOWBM)
The Encrypt-on-Write block map (FVE-EOWBM) is variable of size, stored as a (physical) sector, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Header | |||
| 0 | 10 | "FVE-EOWBM\x00" | Signature |
| 10 | 2 | 60 | Header size |
| 12 | 4 | Block map size, including the size of the block records | |
| 16 | 4 | Block map index, corresponds to the index in the offset array of the EOW data | |
| 20 | 8 | Volume region offset, region (or area) of the volume this block map represents | |
| 28 | 8 | Volume region size | |
| 36 | 8 | Encrypt-on-Write relocation log area offset | |
| 44 | 4 | Block record offset 1, relative to start of the block map | |
| 48 | 4 | Block record offset 2, relative to start of the block map | |
| 52 | 4 | Block record size | |
| 56 | 4 | Checksum, of the (physical) sector data with the checksum value set to 0 | |
| 60 | ... | Unknown (empty values) | |
Encrypt-on-Write block (map) record (FVE-EOWBR)
The Encrypt-on-Write block (map) record (FVE-EOWBR) is variable of size, stored as one or more (physical) sectors, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Header | |||
| 0 | 10 | "FVE-EOWBR\x00" | Signature |
| 10 | 2 | 36 | Header size |
| 12 | 4 | Physical sector size | |
| 16 | 4 | Unknown (bitmap size, in number of bits?) | |
| 20 | 4 | Sequence number | |
| 24 | 4 | 0 | Unknown |
| 28 | 4 | Unknown (flags?, seen 0 and 1) | |
| 32 | 4 | Checksum, of the (physical) sectors data with the checksum value set to 0 | |
| 36 | ... | Bitmap | |
| ... | ... | Unknown (empty values) | |
Encrypt-on-Write relocation log area
The Encrypt-on-Write Encrypt-on-Write relocation log area is variable of size and consists of:
- Encrypt-on-Write relocation log area (OLRDHEVF2) header
- Encrypt-on-Write relocation log entries
- Encrypt-on-Write relocation log entry descriptor
- encrypted sector data or 0-byte values
- backup of Encrypt-on-Write relocation log entry descriptor
- backup of Encrypt-on-Write relocation log area (OLRDHEVF2) header
- Unknown (empty values)
Note that “FVE2.{c9ca54a3-6983-46b7-8684-a7e5e23499e3}.[1-6]” masks the Encrypt-on-Write relocation log area, with a 4096 aligment size.
Encrypt-on-Write relocation log area (OLRDHEVF2) header
The Encrypt-on-Write relocation log area (OLRDHEVF2) header is variable of size, stored as a (logical) sector, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 10 | "OLRDHEVF2\x00" | Signature |
| 10 | 2 | 1 | Unknown |
| 12 | 2 | 2 | Unknown (Number of entries?) |
| 14 | 4 | Unknown (Logical sector or entry descriptor size?) | |
| 18 | 8 | (Encrypted) volume size | |
| 26 | 4 | Relocation block size | |
| 30 | 4 | Encrypt-on-Write relocation log entry size | |
| 34 | 4 | 32 | Unknown |
| 38 | 4 | 2 | Unknown |
| 42 | 8 | Volume region offset, region (or area) of the volume this relocation log represents |
Encrypt-on-Write relocation log entry descriptor
The Encrypt-on-Write relocation log entry descriptor is variable of size, stored as a (logical) sector, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 32 | Unknown |
| 2 | 2 | 1 | Unknown |
| 4 | 2 | Unknown (0 if unencrypted region?) | |
| 6 | 2 | 0 | Unknown |
| 8 | 4 | 0 | Unknown |
| 12 | 8 | Volume region offset, region (or area) of the volume this relocation log entry represents, or 0 if the region is not used by the relocation log | |
| 20 | 4 | (Used) encrypted sector data size | |
| 38 | 4 | Checksum of the encrypted sectors data | |
| 42 | 4 | Unknown (checksum of the unencrypted sectors data?) |
Note that an Encrypt-on-Write relocation log entry descriptor can be empty (filled with 0-byte values).
Encrypt-on-Write encrypted sector data
- 16 bytes of encrypted sector data per sector? decryption sector key is the corresponding volume region offset
TODO: determine if 32 is a compression factor, given 16 x 32 = 512
References
BSD disklabel format
The BSD disklabel format is a partitioning schema mainly used by BSD operating systems.
Overview
A BSD disklabel consists of one or more partition entries, labeled alphabetically from “a” to “h” (or “p” in some BSD variants).
Note that BSD disklabel originally contained 8 entries for describing partitions and some BSD variants have since increased this to 16 partitions.
Certain labels have a predefined meaning, such as:
- “a” is the “root” partition
- “b” is the “swap” partition
- “c” is the volume used by disklabel
- “d” is the entire physical disk
Note that information about partition “d” is not stored in the corresponding BSD disklabel partition entry and typically filled with 0-byte values.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | N/A |
| Character strings | ASCII |
The number of bytes per sector is 512.
BSD disklabel
The BSD disklabel is stored at offset 512. It can be preceded by a MBR with a single partition of type 0xa5 (FreeBSD).
The BSD disklabel is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "WEV\x82" | Signature |
| 4 | 2 | Drive type | |
| 6 | 2 | Controller specific drive sub type | |
| 8 | 16 | Drive type name, which contains an ASCII string | |
| 24 | 16 | Unknown (Pack identifier?), which contains an ASCII string | |
| 40 | 4 | Bytes per sector | |
| 44 | 4 | (Data) Sectors per track | |
| 48 | 4 | Tracks per cylinder | |
| 52 | 4 | (Data) Cylinders per unit | |
| 56 | 4 | (Data) Sectors per cylinder | |
| 60 | 4 | (Data) Sectors per unit | |
| 64 | 2 | Spare sectors per track | |
| 66 | 2 | Spare sectors per cylinder | |
| 68 | 4 | Alternate cylinders per unit | |
| 72 | 2 | Unknown (Rotational speed?) | |
| 74 | 2 | Unknown (Hardware sector interleave?) | |
| 76 | 2 | Unknown (Sector 0 skew per track?) | |
| 78 | 2 | Unknown (Sector 0 skew per cylinder?) | |
| 80 | 4 | Unknown (Head switch time in microseconds?) | |
| 84 | 4 | Unknown (Track-to-track seek time in microseconds?) | |
| 88 | 4 | Flags | |
| 92 | 5 x 4 | Unknown (Drive-type specific information?) | |
| 112 | 5 x 4 | Unknown (Reserved) | |
| 132 | 4 | "WEV\x82" | Signature |
| 136 | 2 | Checksum, which contains a XOR of the BSD disklabel | |
| 138 | 2 | Number of partition entries, should not exceed 16 (MAXPARTITIONS) | |
| 140 | 4 | Boot area size in bytes | |
| 144 | 4 | Maximum superblock size in bytes | |
| 148 | number of partitions x 16 | Array of partition entries |
Note that the number of partition entries contains the total number of entries in the array, not the number of partitions in use.
The checksum is calculated as following:
- set the checksum value to 0
- XOR every 16-bit value in the disklabel
Drive types
| Value | Identifier | Description |
|---|---|---|
| 1 | DTYPE_SMD | SMD, XSMD |
| 2 | DTYPE_MSCP | MSCP |
| 3 | DTYPE_DEC | DEC (rk, rl) |
| 4 | DTYPE_SCSI | SCSI |
| 5 | DTYPE_ESDI | ESDI |
| 6 | DTYPE_ST506 | ST506 |
| 7 | DTYPE_HPIB | CS/80 on HP-IB |
| 8 | DTYPE_HPFL | HP Fiber-link |
| 10 | DTYPE_FLOPPY | Floppy drive |
Flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | D_REMOVABLE | Removable media |
| 0x00000002 | D_ECC | Media supports error-correction codes (ECC) |
| 0x00000004 | D_BADSECT | Media suppors bad sectro forwarding |
| 0x00000008 | D_RAMDISK | Emulated media using RAM |
| 0x00000010 | D_CHAIN | Media can do back-to-back transfers |
Partition entry
The partition entry is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of sectors | |
| 4 | 4 | Start sector | |
| 8 | 4 | File system (basic) fragment size | |
| 12 | 1 | File system type | |
| 13 | 1 | File system fragments per block | |
| 14 | 2 | Unknown (File system specific value) |
Note that an emtpy partition entry consists of 0-byte values.
File system types
| Value | Identifier | Description |
|---|---|---|
| 0 | FS_UNUSED | Unused |
| 1 | FS_SWAP | Swap |
| 2 | FS_V6 | 6th edition |
| 3 | FS_V7 | 7th edition |
| 4 | FS_SYSV | System V |
| 5 | FS_V71K | 7th edition with 1 KiB blocks |
| 6 | FS_V8 | 8th edition with 4 KiB blocks |
| 7 | FS_BSDFFS | BSD 4.2 fast file system (FFS) |
| 8 | FS_MSDOS | MS-DOS file system |
| 9 | FS_BSDLFS | BSD 4.4 log-structured file system |
| 10 | FS_OTHER | Other (unspecified) file system |
| 11 | FS_HPFS | OS/2 high-performance file system (HPFS) |
| 12 | FS_ISO9660 | ISO 9660 (CD-ROM) file system |
| 13 | FS_BOOT | Boot code |
| 14 | Unknown (Vinum) |
References
- FreeBSD Manual Pages
- BSD disklabel, by Wikipedia
Core Storage
Core Storage is a logical volume management system for Mac OS and was introduced in Mac OS X Lion (10.7).
TODO: complete documentation.
GUID Partition Table (GPT) format
The GUID Partition Table (GPT) is a partitioning schema that is the successor to the Master Boot Record (MBR) Partition Table for Intel x86 based computers.
Overview
A GUID Partition Table (GPT) consists of:
- A protective or hybrid Master Boot Record (MBR) stored in block (LBA) 0
- A GPT partition table header stored in block (LBA) 1
- GPT partition entries stored in blocks (LBA) 2 - 33
- partitions area
- GPT partitions
- MBR partitions if hybrid MBR/GPT
- backup GPT partition entries (typically stored the blocks (LBA) before the last block -33 - -2)
- A backup GPT partition table header (typically stored in the last block (LBA) -1)
The GPT partition table header signature can be used to determine the block (LBA) (or sector) size.
Note that the maximum number of partitions is typically 128, but can be 256 as well.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | N/A |
| Character strings | UTF-16 little-endian without byte order mark (BOM) |
Master Boot Record (MBR)
Hybrid Master Boot Record (MBR)
In hybrid configuration both GPT and MBR are used concurrently. Depending on the operating system one might have precedence over the other.
Protective Master Boot Record (MBR)
The Protective Master Boot Record (MBR) is an MBR with a single partition of type “EFI GPT protective partition” (0xee) that allocated as much of the drive as possible.
GPT partition table header
The GPT partition table header is 92 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "EFI PART" | Signature |
| 8 | 2 | 0 | Minor format version |
| 10 | 2 | 1 | Major format version |
| 12 | 4 | 92 | Header data size, which contains the size of the GPT partition table header data |
| 16 | 4 | Header data checksum | |
| 20 | 4 | 0 | Unknown (Reserved) |
| 24 | 8 | Partition header block number (LBA) | |
| 32 | 8 | Backup partition header block number (LBA) | |
| 40 | 8 | Partitions area start block number (LBA) | |
| 48 | 8 | Partitions area end block number (LBA), where the block number is included in the partitions area block range | |
| 56 | 16 | Disk identifier (GUID) | |
| 72 | 8 | Partition entries start block number (LBA) | |
| 80 | 4 | Number of partition entries | |
| 84 | 4 | 128 | Partition entry data size |
| 88 | 4 | Partition entries data checksum | |
| 92 | ... | 0 | Unknown (Reserved) |
The partition entries start block number (LBA) of the backup GPT partition table header points to backup GPT partition entries.
Note that the number of partition entries value contains the number of available partition entries not the number of used partition entries. Empty partition entries have a unused entry partition type identifier.
Checksum calculation
The CRC-32 algorithm with polynominal 0x04c11db7 and initial value of 0 is used to calculate the checksums.
The checksum is calculated over the 92 bytes of the table header data, where the header data checkum value is considered to be 0 during calculation.
GPT partition entries
GPT Partition entry
The GPT partition entry is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Partition type identifier (GUID) | |
| 16 | 16 | Partition identifier (GUID) | |
| 32 | 8 | Partition start block number (LBA) | |
| 40 | 8 | Partition end block number (LBA), where the block number is included in the partition block range | |
| 48 | 8 | Attribute flags | |
| 56 | 72 | Partition name, which contains a UTF-16 little-endian string |
Partition types
| Value | Identifier | Description |
|---|---|---|
| 00000000-0000-0000-0000-000000000000 | Unused entry | |
| 024dee41-33e7-11d3-9d69-0008c781f39f | MBR partition scheme | |
| c12a7328-f81f-11d2-ba4b-00a0c93ec93b | EFI System | |
| 21686148-6449-6e6f-744e-656564454649 | BIOS boot partition | |
| d3bfe2de-3daf-11df-ba40-e3a556d89593 | Intel Fast Flash (iFFS) partition (for Intel Rapid Start technology) | |
| f4019732-066e-4e12-8273-346c5641494f | Sony boot partition | |
| bfbfafe7-a34f-448a-9a5b-6213eb736c22 | Lenovo boot partition | |
| Windows | ||
| e3c9e316-0b5c-4db8-817d-f92df00215ae | Microsoft reserved | |
| ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 | (Microsoft) Basic data | |
| 5808c8aa-7e8f-42e0-85d2-e1e90434cfb3 | Logical Disk Manager (LDM) metadata partition | |
| af9b60a0-1431-4f62-bc68-3311714a69ad | Logical Disk Manager data partition | |
| de94bba4-06d1-4d40-a16a-bfd50179d6ac | Windows recovery environment | |
| 37affc90-ef7d-4e96-91c3-2d7ae055b174 | IBM General Parallel File System (GPFS) partition | |
| e75caf8f-f680-4cee-afa3-b001e56efc2d | Storage Spaces partition | |
| HP-UX | ||
| 75894c1e-3aeb-11d3-b7c1-7b03a0000000 | Data partition | |
| e2a1e728-32e3-11d6-a682-7b03a0000000 | Service Partition | |
| Linux | ||
| 0fc63daf-8483-4772-8e79-3d69d8477de4 | Linux filesystem data | |
| a19d880f-05fc-4d3b-a006-743f0f84911e | RAID partition | |
| 44479540-f297-41b2-9af7-d131d5f0458a | Root partition (x86) | |
| 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 | Root partition (x86-64) | |
| 69dad710-2ce4-4e3c-b16c-21a1d49abed3 | Root partition (32-bit ARM) | |
| b921b045-1df0-41c3-af44-4c6f280d3fae | Root partition (64-bit ARM/AArch64) | |
| 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f | Swap partition | |
| e6d6d379-f507-44c2-a23c-238f2a3df928 | Logical Volume Manager (LVM) partition | |
| 933ac7e1-2eb4-4f13-b844-0e14e2aef915 | /home partition | |
| 3b8f8425-20e0-4f3b-907f-1a25a76f98e8 | /srv (server data) partition | |
| 7ffec5c9-2d00-49b7-8941-3ea10a5586b7 | Plain dm-crypt partition | |
| ca7d7ccb-63ed-4c53-861c-1742536059cc | LUKS partition | |
| 8da63339-0007-60c0-c436-083ac8230908 | Reserved | |
| FreeBSD | ||
| 83bd6b9d-7f41-11dc-be0b-001560b84f0f | Boot partition | |
| 516e7cb4-6ecf-11d6-8ff8-00022d09712b | Data partition | |
| 516e7cb5-6ecf-11d6-8ff8-00022d09712b | Swap partition | |
| 516e7cb6-6ecf-11d6-8ff8-00022d09712b | Unix File System (UFS) partition | |
| 516e7cb8-6ecf-11d6-8ff8-00022d09712b | Vinum volume manager partition | |
| 516e7cba-6ecf-11d6-8ff8-00022d09712b | ZFS partition | |
| Darwin / Mac OS | ||
| 48465300-0000-11aa-aa11-00306543ecac | Hierarchical File System Plus (HFS+) partition | |
| 7c3457ef-0000-11aa-aa11-00306543ecac | Apple APFS | |
| 55465300-0000-11aa-aa11-00306543ecac | Apple UFS container | |
| 6a898cc3-1dd2-11b2-99a6-080020736631 | ZFS | |
| 52414944-0000-11aa-aa11-00306543ecac | Apple RAID partition | |
| 52414944-5f4f-11aa-aa11-00306543ecac | Apple RAID partition, offline | |
| 426f6f74-0000-11aa-aa11-00306543ecac | Apple Boot partition (Recovery HD) | |
| 4c616265-6c00-11aa-aa11-00306543ecac | Apple Label | |
| 5265636f-7665-11aa-aa11-00306543ecac | Apple TV Recovery partition | |
| 53746f72-6167-11aa-aa11-00306543ecac | Apple Core Storage (i.e. Lion FileVault) partition | |
| b6fa30da-92d2-4a9a-96f1-871ec6486200 | SoftRAID_Status | |
| 2e313465-19b9-463f-8126-8a7993773801 | SoftRAID_Scratch | |
| fa709c7e-65b1-4593-bfd5-e71d61de9b02 | SoftRAID_Volume | |
| bbba6df5-f46f-4a89-8f59-8765b2727503 | SoftRAID_Cache | |
| Solaris / illumos | ||
| 6a82cb45-1dd2-11b2-99a6-080020736631 | Boot partition | |
| 6a85cf4d-1dd2-11b2-99a6-080020736631 | Root partition | |
| 6a87c46f-1dd2-11b2-99a6-080020736631 | Swap partition | |
| 6a8b642b-1dd2-11b2-99a6-080020736631 | Backup partition | |
| 6a898cc3-1dd2-11b2-99a6-080020736631 | /usr partition | |
| 6a8ef2e9-1dd2-11b2-99a6-080020736631 | /var partition | |
| 6a90ba39-1dd2-11b2-99a6-080020736631 | /home partition | |
| 6a9283a5-1dd2-11b2-99a6-080020736631 | Alternate sector | |
| 6a8d2ac7-1dd2-11b2-99a6-080020736631 | Reserved partition | |
| 6a945a3b-1dd2-11b2-99a6-080020736631 | Reserved partition | |
| 6a96237f-1dd2-11b2-99a6-080020736631 | Reserved partition | |
| 6a9630d1-1dd2-11b2-99a6-080020736631 | Reserved partition | |
| 6a980767-1dd2-11b2-99a6-080020736631 | Reserved partition | |
| NetBSD | ||
| 49f48d32-b10e-11dc-b99b-0019d1879648 | Swap partition | |
| 49f48d5a-b10e-11dc-b99b-0019d1879648 | FFS partition | |
| 49f48d82-b10e-11dc-b99b-0019d1879648 | LFS partition | |
| 49f48daa-b10e-11dc-b99b-0019d1879648 | RAID partition | |
| 2db519c4-b10f-11dc-b99b-0019d1879648 | Concatenated partition | |
| 2db519ec-b10f-11dc-b99b-0019d1879648 | Encrypted partition | |
| Chrome OS | ||
| fe3a2a5d-4f32-41a7-b725-accc3285a309 | Chrome OS kernel | |
| 3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec | Chrome OS rootfs | |
| 2e0a753d-9e48-43b0-8337-b15192cb1b5e | Chrome OS future use | |
| Container Linux by CoreOS | ||
| 5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6 | /usr partition (coreos-usr) | |
| 3884dd41-8582-4404-b9a8-e9b84f2df50e | Resizable rootfs (coreos-resize) | |
| c95dc21a-df0e-4340-8d7b-26cbfa9a03e0 | OEM customizations (coreos-reserved) | |
| be9067b9-ea49-4f15-b4f6-f36f8c9e1818 | Root filesystem on RAID (coreos-root-raid) | |
| Haiku | ||
| 42465331-3ba3-10f1-802a-4861696b7521 | Haiku BFS | |
| MidnightBSD | ||
| 85d5e45e-237c-11e1-b4b3-e89a8f7fc3a7 | Boot partition | |
| 85d5e45a-237c-11e1-b4b3-e89a8f7fc3a7 | Data partition | |
| 85d5e45b-237c-11e1-b4b3-e89a8f7fc3a7 | Swap partition | |
| 0394ef8b-237e-11e1-b4b3-e89a8f7fc3a7 | Unix File System (UFS) partition | |
| 85d5e45c-237c-11e1-b4b3-e89a8f7fc3a7 | Vinum volume manager partition | |
| 85d5e45d-237c-11e1-b4b3-e89a8f7fc3a7 | ZFS partition | |
| Ceph | ||
| 45b0969e-9b03-4f30-b4c6-b4b80ceff106 | Journal | |
| 45b0969e-9b03-4f30-b4c6-5ec00ceff106 | dm-crypt journal | |
| 4fbd7e29-9d25-41b8-afd0-062c0ceff05d | OSD | |
| 4fbd7e29-9d25-41b8-afd0-5ec00ceff05d | dm-crypt OSD | |
| 89c57f98-2fe5-4dc0-89c1-f3ad0ceff2be | Disk in creation | |
| 89c57f98-2fe5-4dc0-89c1-5ec00ceff2be | dm-crypt disk in creation | |
| cafecafe-9b03-4f30-b4c6-b4b80ceff106 | Block | |
| 30cd0809-c2b2-499c-8879-2d6b78529876 | Block DB | |
| 5ce17fce-4087-4169-b7ff-056cc58473f9 | Block write-ahead log | |
| fb3aabf9-d25f-47cc-bf5e-721d1816496b | Lockbox for dm-crypt keys | |
| 4fbd7e29-8ae0-4982-bf9d-5a8d867af560 | Multipath OSD | |
| 45b0969e-8ae0-4982-bf9d-5a8d867af560 | Multipath journal | |
| cafecafe-8ae0-4982-bf9d-5a8d867af560 | Multipath block | |
| 7f4a666a-16f3-47a2-8445-152ef4d03f6c | Multipath block | |
| ec6d6385-e346-45dc-be91-da2a7c8b3261 | Multipath block DB | |
| 01b41e1b-002a-453c-9f17-88793989ff8f | Multipath block write-ahead log | |
| cafecafe-9b03-4f30-b4c6-5ec00ceff106 | dm-crypt block | |
| 93b0052d-02d9-4d8a-a43b-33a3ee4dfbc3 | dm-crypt block DB | |
| 306e8683-4fe2-4330-b7c0-00a917c16966 | dm-crypt block write-ahead log | |
| 45b0969e-9b03-4f30-b4c6-35865ceff106 | dm-crypt LUKS journal | |
| cafecafe-9b03-4f30-b4c6-35865ceff106 | dm-crypt LUKS block | |
| 166418da-c469-4022-adf4-b30afd37f176 | dm-crypt LUKS block DB | |
| 86a32090-3647-40b9-bbbd-38d8c573aa86 | dm-crypt LUKS block write-ahead log | |
| 4fbd7e29-9d25-41b8-afd0-35865ceff05d | dm-crypt LUKS OSD | |
| OpenBSD | ||
| 824cc7a0-36a8-11e3-890a-952519ad3f61 | Data partition | |
| QNX | ||
| cef5a9ad-73bc-4601-89f3-cdeeeee321a1 | Power-safe (QNX6) file system | |
| Plan 9 | ||
| c91818f9-8025-47af-89d2-f030d7000c2c | Plan 9 partition | |
| VMware ESX | ||
| 9d275380-40ad-11db-bf97-000c2911d1b8 | vmkcore (coredump partition) | |
| aa31e02a-400f-11db-9590-000c2911d1b8 | VMFS filesystem partition | |
| 9198effc-31c0-11db-8f78-000c2911d1b8 | VMware Reserved | |
| Android-IA | ||
| 2568845d-2332-4675-bc39-8fa5a4748d15 | Bootloader | |
| 114eaffe-1552-4022-b26e-9b053604cf84 | Bootloader2 | |
| 49a4d17f-93a3-45c1-a0de-f50b2ebe2599 | Boot | |
| 4177c722-9e92-4aab-8644-43502bfd5506 | Recovery | |
| ef32a33b-a409-486c-9141-9ffb711f6266 | Misc | |
| 20ac26be-20b7-11e3-84c5-6cfdb94711e9 | Metadata | |
| 38f428e6-d326-425d-9140-6e0ea133647c | System | |
| a893ef21-e428-470a-9e55-0668fd91a2d9 | Cache | |
| dc76dda9-5ac1-491c-af42-a82591580c0d | Data | |
| ebc597d0-2053-4b15-8b64-e0aac75f4db1 | Persistent | |
| c5a0aeec-13ea-11e5-a1b1-001e67ca0c3c | Vendor | |
| bd59408b-4514-490d-bf12-9878d963f378 | Config | |
| 8f68cc74-c5e5-48da-be91-a0c8c15e9c80 | Factory | |
| 9fdaa6ef-4b3f-40d2-ba8d-bff16bfb887b | Factory (alt) | |
| 767941d0-2085-11e3-ad3b-6cfdb94711e9 | Fastboot / Tertiary | |
| ac6d7924-eb71-4df8-b48d-e267b27148ff | OEM | |
| Android 6.0+ ARM | ||
| 19a710a2-b3ca-11e4-b026-10604b889dcf | Android Meta | |
| 193d1ea4-b3ca-11e4-b075-10604b889dcf | Android EXT | |
| Open Network Install Environment (ONIE) | ||
| 7412f7d5-a156-4b13-81dc-867174929325 | Boot | |
| d4e6e2cd-4469-46f3-b5cb-1bff57afc149 | Config | |
| PowerPC | ||
| 9e1a2d38-c612-4316-aa26-8b49521e5a8b | PReP boot | |
| freedesktop.org OSes (Linux, etc.) | ||
| bc13c2ff-59e6-4262-a352-b275fd6f7172 | Shared boot loader configuration | |
| Atari TOS | ||
| 734e5afe-f61a-11e6-bc64-92361f002671 | Basic data partition (GEM, BGM, F32) | |
Partition attribute flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Partition is required by the platform, e.g. an OEM partition | |
| 0.1 | 1 bit | EFI firmware should ignore the content of the partition | |
| 0.2 | 1 bit | Partition contains bootable legacy BIOS, equivalent to MBR active flag | |
| 0.3 | 45 bits | Unknown (Reserved) | |
| 6.0 | 16 bits | Flags specific to the partition type |
Microsoft basic partition type attribute flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 7.4 | 1 bit | Partition is read-only | |
| 7.5 | 1 bit | Partition is a shadow copy (of another partition) | |
| 7.6 | 1 bit | Partition is hidden | |
| 7.7 | 1 bit | Partition should not have a drive letter assigned (no auto-mount) |
ChromeOS partition type attribute flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 6.0 | 4 bits | Priority, where 15 is thehighest priority, 1 is the lowest and 0 indicates the partition is not bootable | |
| 6.4 | 4 bits | Number of tries to attempt to boot from the partition | |
| 7.0 | 1 bit | Partition was previously successfully booted from |
Linux Logical Volume Manager (LVM) format
The Linux Logical Volume Manager (LVM) format is used by the Logical Volume Manager (LVM) on Linux.
Overview
A Linux LVM consist of:
- Volume group
- Physical volumes
- Logical volumes
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | POSIX timestamp in local timezone and/or ctime date and time string |
| Character strings | ASCII string with an end-of-string character |
According to “RHEL 5 - Logical Volume Manager Administration” the number of bytes per sector is 512.
Checksums use a “weak” CRC-32, which is a CRC-32 without the initial and final XOR with 0xffffffff, using the polynominal 0xedb88320 and initial value 0xf597a6cf.
Terminology
| Term | Description |
|---|---|
| logical extent (LE) | An extent (data range) that makes up the logical volume |
| logical volume (LV) | A volume within the LVM, comparable to a partition in a MBR or GPT volume system |
| physical extent (PE) | An extent (data range) that makes up the physical volume |
| physical volume (PV) | Typically a physical volume is a hard disk, though it may well just be any other device that behaves like a hard disk, such as a software RAID device |
| volume group (VG) | A collection of Logical Volumes and Physical Volumes |
Physical volume
A physical volume consist of:
- Empty sector
- The physical volume label header
- The physical volume header
- data area descriptor list
- metadata area descriptor list
- The physical volume header
- The metadata area
- Data area (or data extents)
Physical volume label
The physical volume label is stored in the second sector of the physical volume. The physical volume label is currently 512 bytes in size and consists of:
- physical volume label header
- physical volume header
Note that according to “RHEL 5 - Logical Volume Manager Administration” the physical volume label can be stored in any of the first 4 sectors.
Physical volume label header
The physical volume label header (struct label_header) is 32 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | "LABELONE" | Signature (or identifier) |
| 8 | 8 | Sector number, which contains the sector number of the physical volume label header | |
| 16 | 4 | Checksum, which contains a CRC-32 for offset 20 to end of the physical volume label sector | |
| 20 | 4 | Data offset (or header size), which contains an offset in bytes relative from the start of the physical volume label header | |
| 24 | 8 | "LVM2\x20001" | Type indicator |
Physical volume header
The physical volume header (struct pv_header) is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 32 | Physical volume identifier, which contains a UUID stored as an ASCII string | |
| 32 | 8 | Physical volume size, in number of bytes | |
| 40 | ... | List of data area descriptors, where the last descriptor in the list is terminator and consists of 0-byte values | |
| ... | ... | List of metadata area descriptors, where the last descriptor in the list is terminator and consists of 0-byte values |
The physical volume identifier can be used to uniquely identify a physical volume. The physical volume identifier is stored as “9LBcEB7PQTGIlLI0KxrtzrynjuSL983W” but is equivalent to its formatted variant “9LBcEB-7PQT-GIlL-I0Kx-rtzr-ynju-SL983W”, which is used in the metadata.
Note that the data area size can be 0, and currently it is assumed that this represents all remaining (available) space within the physical volume.
Data area descriptor
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Data area offset, which contains an offset in bytes relative to the start of the physical volume | |
| 8 | 8 | Data area size, in number of bytes |
The metadata area
The metadata area consist of:
- Metadata area header
- Metadata
According to “RHEL 5 - Logical Volume Manager Administration” the metadata area is a circular buffer. New metadata is appended to the old metadata and then the pointer to the start of it is updated. The metadata area, therefore, can contain copies of older versions of the metadata.
Metadata area header
The metadata area header (struct mda_header) is 512 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum, which contains the CRC-32 for offset 4 to end of the metadata area header | |
| 4 | 16 | "\x20LVM2\x20x[5A%r0N*>" | Signature |
| 20 | 4 | 1 | Format version |
| 24 | 8 | Metadata area offset, which contains an offset in bytes relative to the start of the physical volume | |
| 32 | 8 | Metadata area size, in number of bytes | |
| 40 | 4 x 24 = 96 | List of raw location descriptors, where the last descriptor in the list is terminator and consists of 0-byte values | |
| 136 | 376 | 0 | Unknown (unused) |
Raw location descriptor
The raw location descriptor (struct raw_locn) is 24 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Data area offset, which contains an offset in bytes relative to the start of the metadata area | |
| 8 | 8 | Data area size, in number of bytes | |
| 16 | 4 | Checksum, which contains the CRC-32 of the data area described by the raw location descriptor | |
| 20 | 4 | Flags |
Note that the data area size can be 0. It is assumed it represents the remaining available data.
Raw location descriptor flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | RAW_LOCN_IGNORED | The raw location descriptor should be ignored |
Metadata
The metadata consist of:
- Volume group main section
- Physical volumes object
- Physical volume object
- Logical volumes object
- Logical volume object
- Segment object
- Logical volume object
- Physical volumes object
- Top level properties
According to “RHEL 5 - Logical Volume Manager Administration” by default, an identical copy of the metadata is maintained in every metadata area in every physical volume within the volume group. The metadata is stored as ASCII.
The metadata can also be stored in a stand-alone file.
Example
# Generated by LVM2: Tue Jan 30 16:28:15 2007
contents = "Text Format Volume Group"
version = 1
description = "Created *before* executing 'lvextend -L+5G /dev/myvg/mylv /dev/sdc'"
creation_host = "tng3-1" # Linux tng3-1 2.6.18-8.el5 #1 SMP Fri Jan 26 14:15:21 EST 2007 i686
creation_time = 1170196095 # Tue Jan 30 16:28:15 2007
myvg {
id = "0zd3UT-wbYT-lDHq-lMPs-EjoE-0o18-wL28X4"
seqno = 3
status = ["RESIZEABLE", "READ", "WRITE"]
extent_size = 8192 # 4 Megabytes
max_lv = 0
max_pv = 0
physical_volumes {
pv0 {
id = "ZBW5qW-dXF2-0bGw-ZCad-2RlV-phwu-1c1RFt"
device = "/dev/sda" # Hint only
status = ["ALLOCATABLE"]
dev_size = 35964301 # 17.1491 Gigabytes
pe_start = 384
pe_count = 4390 # 17.1484 Gigabytes
}
...
}
logical_volumes {
mylv {
id = "GhUYSF-qVM3-rzQo-a6D2-o0aV-LQet-Ur9OF9"
status = ["READ", "WRITE", "VISIBLE"]
segment_count = 2
segment1 {
start_extent = 0
extent_count = 1280 # 5 Gigabytes
type = "striped"
stripe_count = 1 # linear
stripes = [
"pv0", 0
]
}
segment2 {
start_extent = 1280
extent_count = 1280 # 5 Gigabytes
type = "striped"
stripe_count = 1 # linear
stripes = [
"pv1", 0
]
}
}
}
}
Properties
The metadata is textual and uses properties, where a property is defined as:
<identifier> = <value>
The identifier contains an unique name of the property and <value> is one of the following types:
| Value | Description |
|---|---|
| [0-9]+ | An integer |
| "..." | A string |
| ["...", "...", ...] | A list (or array) of strings |
Note that white space, such as space and new line characters, seem to be ignored.
The # character is used for comments. A comment continues to the end-of-line.
Note that for now it is assumed that the # character is not allowed to be used in any of the values.
Top level properties
| Value | Description |
|---|---|
| "contents" | The contents of the metadata area, which contains the string "Text Format Volume Group" |
| "creation_host" | The hostname of the system on which metadata area was created, which can contain a trailing comment that contains the output equivalent to "uname -a" |
| "creation_time" | The creation time of the metadata area, which contains an interger containing the number of seconds since January 1, 1970 00:00:00 UTC and can contain a trailing comment that contains the creation time as a ctime (function) string in UTC |
| "description" | Unknown (Description of the metadata area?) |
| <volume group name> | Volume group object |
| "version" | The metadata area version, which contains an integer value of 1 |
Note it is assumed that there can only be 1 volume group object.
Volume group and logical volume names
- Allowed characters: “a-z”, “A-Z”, “0-9”, “_”, “+”, “.”, and “-” (hypen)
- A volume group name should not start with a hypen
- A logical volume name can start with a hypen, but it is reserved for logical volumes that have been removed but are stil being tracked by the format
Note that some implementations use lv_ as the prefix for a logical volume, however this is not enforced by the format.
Volume group object
The volume group object contains the following properties:
| Value | Description |
|---|---|
| "extent_size" | The size of an extent, in number of sectors |
| "flags" | Flags |
| "format" | Optional format identifier, such as "lvm2" |
| "id" | Volume group identifier (VG UUID), which contains an ASCII string in the following format: fg1fKZ-xoHz-CfAD-yQPx-l2HL-Y7kA-9kJ9LD |
| "logical_volumes" | Logical volumes object |
| "max_lv" | Maximum number of logical volumes |
| "max_pv" | Maximum number of physical volumes |
| "metadata_copies" | Unknown (The number of metadata copies?) |
| "physical_volumes" | Physical volumes object |
| "seqno" | Metadata sequence number |
| "status" | The status flags, which contains a list of strings |
Physical volumes object
The physical volumes object contains the following properties:
| Value | Description |
|---|---|
| pv# | Individual physical volume object, where # is a place holder for a the physical volume number e.g. pv0. 0 appears to be the first number that is used |
Physical volume object
Each physical volume object contains the following properties:
| Value | Description |
|---|---|
| "device" | The device filename, which contains an ASCII string, e.g. /dev/dm-0 |
| "device_id" | Unknown (device identifier "/tmp/lvm.raw") |
| "device_id_type" | Unknown (device type "loop_file") |
| "dev_size" | The physical volume size including non-usable space, in number of sectors |
| "flags" | Flags |
| "id" | Physical volume identifier (PV UUID), which contains an ASCII string in the following format: 9LBcEB-7PQT-GIlL-I0Kx-rtzr-ynju-SL983W |
| "pe_count" | The number of (allocated) extents in the physical volume |
| "pe_start" | The start extent, which contains an offset in bytes relative from the start of the physical volume |
| "status" | The status flags, which contains a list of strings |
Logical volumes object
The logical volumes object contains the following properties:
| Value | Description |
|---|---|
| <logical volume name> | Individual logical volume object, where <name> is a place holder for a the logical volume name |
Logical volume object
Each logical volume object contains the following properties:
| Value | Description |
|---|---|
| "creation_host" | The hostname of the system on which the logical volume was created |
| "creation_time" | The creation time of the metadata area, which contains an interger containing the number of seconds since January 1, 1970 00:00:00 UTC and can contain a trailing comment that contains the creation time as a ctime (function) string in UTC |
| "flags" | Flags |
| "id | Physical volume identifier (PV UUID), which contains an ASCII string in the following format: 9LBcEB-7PQT-GIlL-I0Kx-rtzr-ynju-SL983W |
| "segment_count" | The number of segments |
| "segment#" | Individual segment objects, where # is a place holder for the segment number e.g. segment1. 1 appears to be the first number that is used |
| "status" | Status flags, which contains a list of strings |
Segment object
Each segment object contains the following properties:
| Value | Description |
|---|---|
| "extent_count" | The number of extents in the segment (or current logical extent) |
| "start_extent" | The start extent of the segment, which contains an offset in number of extents relative from the start of the segment |
| "stripe_count" | The number of stripes in the segment, where 1 represents linear striping |
| "stripes" | Stripes list |
| "type" | Segment type |
Segment types
| Value | Description |
|---|---|
| cache | Segment that is used as a cache, e.g. SSD for a HDD |
| cache-pool | Segment used as a cache for both data and metadata |
| error | Segment that fails read and write operations, intended for testing |
| free | Unallocated segment |
| integrity | Segment with integrity checksum |
| linear | Linear physical volume |
| mirror | Segment that mirrors data across multiple physical volumes (comparable to RAID1) |
| raid0 | Segment that contains a stripe of data (RAID0) |
| raid0_meta | RAID0 physical volume that contains data and metadata |
| raid1 | Segment that mirrors data across multiple physical volumes (RAID1) |
| raid10 | Segment that contains a stripe of data (RAID0) that mirrors data across multiple physical volumes (RAID1) |
| raid10_near | RAID10 segment that copies data sectors near each other |
| raid4 | Segment with stripes across multiple physical volumes with parity data (RAID4) |
| raid5 | Segment with stripes across multiple physical volumes with parity data (RAID5) |
| raid5_la | RAID5 variant that uses left asymmetric layout |
| raid5_ls | RAID5 variant that uses left symmetric layout |
| raid5_n | RAID5 variant with dedicated parity layout |
| raid5_ra | RAID5 variant that uses right asymmetric layout |
| raid5_rs | RAID5 variant that uses right symmetric layout |
| raid6 | Segment with stripes across multiple physical volumes with dual parity data (RAID6) |
| raid6_la_6 | RAID6 variant that uses left asymmetric layout |
| raid6_ls_6 | RAID6 variant that uses left symmetric layout |
| raid6_n_6 | RAID6 variant with dedicated parity layout |
| raid6_nc | RAID6 variant that uses no-change layout |
| raid6_nr | RAID6 variant that uses non-rotating layout |
| raid6_ra_6 | RAID6 variant that uses right asymmetric layout |
| raid6_rs_6 | RAID6 variant that uses right symmetric layout |
| raid6_zr | RAID6 variant that uses zero-restart layout |
| snapshot | Segment that contains a snapshot |
| striped | Segment that contains a stripe of data (comparable to RAID0) |
| thin | Segment that allocated on write |
| thin-pool | Segment that manages a shared pool of data and metadata |
| vdo | Virtual Data Optimizer (VDO) physical volume |
| vdo-pool | Virtual Data Optimizer (VDO) pool |
| writecache | Segment that is used as a write-only cache |
| zero | Segment that is sparse (filled with 0-byte values) |
Note that a comparable list can be retrieved using
lvm segtypes.
Stripes list
stripes = [
<physical volume name>, <start extent number>
]
Where:
- <physical volume name> is a string containing the physical volume name e.g. “pv0”.
- <start extent number> the segment start extent number relative from the start of the data area.
start_extent_offset = (
(start_extent_number * extent_size * sector_size) + physical_volume_data_area_start_offset
)
Status flags
| Value | Description |
|---|---|
| ALLOCATABLE | Is allocatable (physical volume only) |
| RESIZEABLE | Can be re-sized (volume group only) |
| READ | Can be read |
| VISIBLE | Is visible (logical volume only). Hidden if not set. |
| WRITE | Can be written |
Flags
TODO: complete section
Comments
Textual metadata such as:
# Generated by LVM2 version 2.02.39 (2008-06-27): Sat Jan 17 11:45:29 2009
References
Linux Unified Key Setup (LUKS) Disk Encryption format
Linux Unified Key Setup (LUKS) Disk Encryption is commonly used by Linux to encrypt storage media volumes.
Overview
There are 2 versions of the Linux Unified Key Setup (LUKS) Disk Encryption format, each with a separate layout.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | ASCII string with an end-of-string character |
Layout version 1
A LUKS version 1 encrypted volume consist of:
- volume header
- 8 x key slots
- split master key material
- encrypted (volume) data
The total reserved size of the LUKS metadata (volume header and split master key material) seems to commonly be 2 MiB.
The number of bytes per sector is 512.
Layout version 2
A LUKS version 2 encrypted volume consist of:
- metadata area
- backup metadata area
- backup volume header
- backup JSON area
- keyslots area
- encrypted (volume) data
The number of bytes per sector is stored in the JSON metadata and can be 512, 1024, 2048, or 4096.
Keys
To encrypt storage media LUKS Disk Encryption uses different kind of keys.
Master Key (MK)
The Master Key (MK) is derived from the Split Master Key (SMK). The size of the MK is dependent on the master key size value in the volume header. Commonly the MK is 128-bit or 256-bit of size. The MK is used to de/encrypt the encrypted (volume) data.
Split Master Key (SMK)
The Split Master Key (SMK) is stored encrypted with a specific user key (UK) in the split master key material. The size of the key material and hence the SMK is the size of the Master Key (MK) times the number of stripes.
The MK is determined from the SMK using the anti-forensic (AF) diffuser using the hashing method.
The resulting MK can be validated with the master key validation hash stored in the volume header. The validation hash can be calculated using the PBKDF2 algorithm with:
- The hashing method stored in the volume header (format version 1) or metadata (format version 2).
- The number of iterations as stored in the volume header.
- A salt, as stored in the volume header.
- The master key as the input data.
User Key (UK)
The User Key (UK) is derived from the user password. The UK is used to de/encrypt the corresponding split master key material.
The user key is calculated using the PBKDF2 algorithm with:
- The hashing method stored in the volume header (format version 1) or metadata (format version 2).
- The number of iterations as stored in the corresponding key slot.
- A salt, as stored in the corresponding key slot.
- The password string as the input data (bytes).
- A (output) key size that is the same as that of the Master Key (MK).
Encryption methods
LUKS supports multiple encryption methods, different encryption chaining modes and initialization vector modes.
Initialization vector modes
The null initialization vector mode
In the null initialization vector mode the initialization vector (IV) is filled with 0‑byte values.
The plain initialization vector modes
In the plain and plain64 initialization vector mode the initialization vector (IV) is filled with respectively a 32-bit or 64-bit little-endian representation of the corresponding sector number padded with 0-byte values.
The sector number is relative to the start of the data not relative to the start of the volume header.
The encrypted sector-salt initialization vector (ESSIV) mode
Int the encrypted sector-salt initialization vector (ESSIV) mode the initialization vector (IV) is determined by:
- hashing the encryption key with hashing method defined in the initialization vector mode options.
- encrypting the little-endian representation of the corresponding sector number padded with 0-byte values with the hash of the encryption key.
Note that the sector number is relative to the start of the data not relative to the start of the volume header.
The benbi initialization vector mode
In the benbi initialization vector mode the initialization vector (IV) is filled with a 64-bit big-endian representation of the corresponding cipher block (or narrow block)-count (starting at 1) padded with 0-byte values.
The sector number is relative to the start of the data not relative to the start of the volume header.
The cipher block-count is calculated as:
cipher_block_count = (sector_number << (log2(bytes_per_sectory) - log2(iv_size))) + 1
Benbi is presumably the abbreviation of big-endian numeric block index, or equivalent.
The lmk initialization vector mode
TODO: complete section
AES-CBC
Decryption uses:
- AES-CBC with Master Key (MK) decryption of sector data
- The initialization vector of the AES-CBC is dependent on the initialization vector mode defined in the volume header. In recent versions of Linux, AES-CBC is combined with the ESSIV initialization vector mode by default.
- The initialization vector is 16 bytes in size.
AES-ECB
Decryption uses:
- AES-ECB with Master Key (MK) decryption of sector data
- No initialization vector is used.
- The initialization vector is 16 bytes in size.
AES-XTS
TODO: complete section
- The initialization vector is 16 bytes in size.
Anubis
TODO: complete section
Default encryption mode is cbc-plain Size of initialization vector?
Blowfish
TODO: complete section
Default encryption mode is cbc-plain Size of initialization vector?
Cast5
TODO: complete section
RFC 2144 Size of initialization vector?
Cast6
TODO: complete section
RFC 2612 Default encryption mode is cbc-plain Size of initialization vector?
Serpent
TODO: complete section
Default encryption mode is cbc-plain Size of initialization vector?
Twofish
TODO: complete section
Default encryption mode is cbc-plain Size of initialization vector?
Volume header
Volume header - format version 1
The volume header - format version 1 is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 6 | "LUKS\xba\xbe" | Signature |
| 6 | 2 | 1 | Format version |
| 8 | 32 | Encryption method (Cipher name), which contains an ASCII string with an end-of-string character | |
| 40 | 32 | Encryption mode (Cipher mode), which contains an ASCII string with an end-of-string character | |
| 72 | 32 | Hashing method, which contains an ASCII string with an end-of-string character | |
| 104 | 4 | Encrypted data start sector | |
| 108 | 4 | Master key size, in number of bytes | |
| 112 | 20 | Master key validation hash | |
| 132 | 32 | Master key derivation salt | |
| 164 | 4 | Master key derivation number of iterations | |
| 168 | 40 | Volume identifier, which contains an ASCII string with an end-of-string character that consists of a lower-case UUID | |
| 208 | 8 x 48 | Array of key slots | |
| 592 | 3504 | Unknown (empty values) |
The hashing method is used for the user key calculation and the anti-forensic (AF) diffuser.
Volume header - format version 2
The volume header - format version 2 (or binary header) is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 6 | "LUKS\xba\xbe" | Signature |
| 6 | 2 | 2 | Format version |
| 8 | 8 | Metadata area size, which consists of the size of the volume header and JSON area | |
| 16 | 8 | Epoch (or sequence identifier) | |
| 24 | 48 | Volume label, which contains an ASCII string with an end-of-string character | |
| 72 | 32 | Metadata area checksum method (or algorithm), which contains an ASCII string with an end-of-string character | |
| 104 | 64 | Salt | |
| 168 | 40 | Volume identifier, which contains an ASCII string with an end-of-string character that consists of a lower-case UUID | |
| 208 | 48 | Unknown (subsystem), which contains an ASCII string with an end-of-string character | |
| 256 | 8 | Metadata area offset, which is relative from the start of the storage media in which the LUKS volume is stored | |
| 264 | 184 | Unknown (padding), which according to "LUKS2 On-Disk Format Specification" this must be filled with 0-byte values | |
| 448 | 64 | Metadata area checksum | |
| 512 | 7 x 512 = 3584 | Unknown (padding), which according to "LUKS2 On-Disk Format Specification" this must be filled with 0-byte values |
JSON area
The JSON area is stored directly after the volume header and must be 4096-byte aligned. The JSON area is of variable size and constists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | JSON string, which contains an ASCII string with an end-of-string character | |
| ... | ... | Unknown (padding), which according to "LUKS2 On-Disk Format Specification" this must be filled with 0-byte values |
Example
{
"keyslots": {
"0": {
"type": "luks2",
"key_size": 32,
"af": {
"type": "luks1",
"stripes": 4000,
"hash": "sha1"
},
"area": {
"type": "raw",
"offset": "32768",
"size": "131072",
"encryption": "aes-ecb",
"key_size": 32
},
"kdf": {
"type": "argon2i",
"time": 6,
"memory": 1048576,
"cpus": 4,
"salt": "X3OghBqUPLPkYuaFlSu4w/4VsRlRNDBzN+IW5Y5JQSU="
}
}
},
"tokens": {},
"segments": {
"0": {
"type": "crypt",
"offset": "16777216",
"size": "dynamic",
"iv_tweak": "0",
"encryption": "aes-ecb",
"sector_size": 512
}
},
"digests": {
"0": {
"type": "pbkdf2",
"keyslots": [
"0"
],
"segments": [
"0"
],
"hash": "sha1",
"iterations": 154931,
"salt": "wxT97+jYHKhAat3rZb6XXuwXVRn3DM7tvGy8+ZukM38=",
"digest": "WHT1SoOLP3tummIDhiNTxP39dfw="
}
},
"config": {
"json_size": "12288",
"keyslots_size": "16744448"
}
}
Top level properties
| Value | Description |
|---|---|
| "config" | Config object |
| "digests" | One or more digests object |
| "keyslots" | One or more keyslots object |
| "segments" | One ore more segments object |
| "tokens" | Zero or more tokens object |
Config object
| Value | Description |
|---|---|
| "flags" | List of strings |
| "json_size" | String containing an integer |
| "keyslots_size" | String containing an integer |
| "requirements" |
Digests object
TODO: complete section
| Value | Description |
|---|---|
| "digest" | |
| "hash" | type "pbkdf2" |
| "iterations" | type "pbkdf2" |
| "keyslots" | |
| "salt" | |
| "segments" | |
| "type" |
Keyslots object
Contains zero or more keyslot object.
Keyslot object
TODO: complete section
| Value | Description |
|---|---|
| "af" | Anti-forensics (diffuser) object, which should only be present in type "luks2" |
| "area" | Key slot area object |
| "direction" | Re-encryption direction, which should only be present in type "reencrypt" |
| "kdf" | Key derivation object, which should only be present in type "luks2" |
| "key_size" | Key size, in number of bytes |
| "mode" | Re-encryption mode, which should only be present in type "reencrypt" |
| "priority" | Priority, where 0 represents "ignore", 1 "normal" and 2 "high" |
| "type" | Keyslot type, which can be "luks2" or "reencrypt" |
Anti-forensics (diffuser) object
TODO: complete section
| Value | Description |
|---|---|
| "hash" | type "luks1" |
| "stripes" | type "luks1" |
| "type" | Anti-forensics type, which can be "luks1" |
Keyslot area object
TODO: complete section
| Value | Description |
|---|---|
| "encryption" | type "raw" |
| "hash" | types "checksum" and "datashift-checksum" |
| "key_size" | type "raw" |
| "offset" | |
| "sector_size" | types "checksum" and "datashift-checksum" |
| "shift_size" | types "datashift", "datashift-checksum" and "datashift-journal" |
| "size" | |
| "type" | Area type, which can be "raw", "checksum", "none", "journal" |
Key derivation object
TODO: complete section
| Value | Description |
|---|---|
| "cpus" | types "argon2i" and "argon2id" |
| "hash" | type "pbkdf2" |
| "iterations" | type "pbkdf2" |
| "memory" | types "argon2i" and "argon2id" |
| "salt" | |
| "time" | types "argon2i" and "argon2id" |
| "type" | The PBKDF type, which can be "pbkdf2", "argon2i" and "argon2id" |
Segments object
TODO: complete section
| Value | Description |
|---|---|
| "encryption" | type "crypt" |
| "flags" | |
| "integrity" | type "crypt" |
| "iv_tweak" | type "crypt" |
| "offset" | |
| "sector_size" | type "crypt" |
| "size" | |
| "type" | Segment type, which can be "linear" or "crypt" |
Segment integrity object
TODO: complete section
| Value | Description |
|---|---|
| "journal_encryption" | |
| "journal_integrity" | |
| "key_size" | |
| "type" |
Tokens object
TODO: complete section
| Value | Description |
|---|---|
| "key_description" | type "luks2-keyring" |
| "keyslots" | |
| "type" |
Backup metadata area
To make recovery easier the backup metadata area starts at a fixed offset:
| Offset | Maximum JSON area size |
|---|---|
| 16384 (0x004000) | 12 KiB |
| 32768 (0x008000) | 28 KiB |
| 65536 (0x010000) | 60 KiB |
| 131072 (0x020000) | 124 KiB |
| 262144 (0x040000) | 252 KiB |
| 524288 (0x080000) | 508 KiB |
| 1048576 (0x100000) | 1020 KiB |
| 2097152 (0x200000) | 2044 KiB |
| 4194304 (0x400000) | 4092 KiB |
Backup volume header - format version 2
The backup (or secondary) volume header - format version 2 is the same as the Volume header - format version 2 with a different signature: “SKUL\xba\xbe”.
Keyslots
Key slot - format version 1
The key slot - format version 1 is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | State (of key slot), where 0x0000dead represents inactive (dead) and 0x00ac71f3 represents active | |
| 4 | 4 | Key material number of iterations | |
| 8 | 32 | Key material salt | |
| 40 | 4 | Key material start sector | |
| 44 | 4 | Key material number of (anti-forensic) stripes |
String identifiers
LUKS uses various string values as identifiers.
Note that it is assumed that these identifiers are case insensitive.
Encryption method
Known values of encryption method are:
| Value | Description |
|---|---|
| arc4 | Alleged RC4 (ARC4) |
| aes | Advanced Encryption Standard (AES) |
| anubis | Anubis |
| blowfish | Blowfish |
| cast5 | Cast5 (RFC 2144) |
| cast6 | Cast6 (RFC 2612) |
| serpent | Serpent |
| tnepres | Reversed variant of Serpent |
| twofish | Twofish |
Encryption mode
The encryption mode consists of a string in the format:
chaining_mode[-initialization_vector_mode[:initialization_vector_options]]
Chaining mode
Known values of chaining mode are:
| Value | Description |
|---|---|
| cbc | Cipher-block chaining (CBC) |
| ecb | Electronic codebook (ECB), which should not have a initialization vector mode set |
| xts | XEX-based tweaked-codebook mode with ciphertext stealing (XTS) |
Note that it is assumed that these identifiers are case insensitive.
TODO: determine ctr and lrw
Initialization vector mode and options
Known values of initialization vector mode and options are:
| Value | Description |
|---|---|
| benbi | The initialization vector is the 64-bit big-endian cipher block (or narrow block)-count (starting at 1) |
| essiv | Encrypted sector-salt initialization vector (ESSIV). The "essiv" initialization vector mode requires a hash algorithm to be defined as an initialization vector option. This is specified in the form "essiv:hash", e.g. "essiv:sha256" |
| lmk | Compatible implementation of the block chaining mode used by the Loop-AES block device encryption system |
| null | The initialization vector is always zero |
| plain | The initialization vector is the 32-bit little-endian version of the sector number, padded with zeros if necessary |
| plain64 | The initialization vector is the 64-bit little-endian version of the sector number, padded with zeros if necessary |
| plumb | Unknown |
Hashing method
Known values of hashing method are:
| Value | Description |
|---|---|
| ripemd160 | RIPEMD-160 |
| sha1 | SHA-1 |
| sha224 | SHA-224 |
| sha256 | SHA-256 |
| sha512 | SHA-512 |
| wd256 | Unknown |
The hashing method must at least produce 20 bytes of hash data. Therefore hashing methods like ghash and MD5 are unsupported.
Format edge cases and corruption scenarios
Uninitialized encrypted volume data
Running “cryptsetup luksFormat” will not initialize the encrypted volume data, the data is initialized on write. The uninitialized encrypted data is treated as-is on decryption.
References
- LUKS On-Disk Format Specification, by Clemens Fruhwirth
- LUKS2 On-Disk Format Specification, by Milan Broz
Master Boot Record (MBR) partition table format
The Master Boot Record (MBR) partition table is mainly used on the family of Intel x86 based computers.
Overview
A MBR partition table consists of:
- Master Boot Record (MBR)
- Extended Partition Records (EPRs)
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | N/A |
| Character strings | N/A |
Terminology
| Term | Description |
|---|---|
| Physical block | A fixed location on the storage media defined by the storage media |
| Logical block | An abstract location on the storage media defined by software |
Sector size(s)
Traditionally the size of sector is 512 bytes, but modern hard disk drives use 4096 bytes. The linux fdisk utility supports sector sizes of: 512, 1024, 2048 and 4096.
The location of of the “boot signature” of the MBR does not indicate the sector size. Methods to derive the sector size from the data:
- check the “boot signature” of the first EPR, if present
- check the content of well known partition types
Cylinder Head Sector (CHS) address
The Cylinder Head Sector (CHS) address is 24 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 8 bits | Head | |
| 1.0 | 6 bits | Sector | |
| 1.5 | 10 bits | Cylinder |
The logical block address (LBA) can be determined from the CHS with the following calculation:
lba = (((cylinder * heads_per_cylinder) + head) * sectors_per_track) + (sector - 1)
The Master Boot Record (MBR)
The Master Boot Record (MBR) is a data structure that describes the properties of the storage medium and its partitions.
The classical MBR can only contain 4 partition table entries. Additional partition entries must be stored using extended partition records (EPR). The classical MBR has evolved into different variants like:
- The modern MBR
- The Advanced Active Partitions (AAP) MBR
- The NEWLDR MBR
- The AST/NEC MS-DOS and SpeedStor MBR
- The Disk Manager MBR
The classical MBR
The classical MBR is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 446 | The boot (loader) code | |
| 446 | 16 | Partition table entry 1 | |
| 462 | 16 | Partition table entry 2 | |
| 478 | 16 | Partition table entry 3 | |
| 494 | 16 | Partition table entry 4 | |
| 510 | 2 | "\x55\xaa" | The (boot) signature |
The modern MBR
The modern MBR is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 218 | The first part of the boot (loader) code | |
| Disk timestamp used by Microsoft Windows 95, 98 and ME | |||
| 218 | 2 | 0x0000 | Unknown (Reserved) |
| 220 | 1 | Unknown (Original physical drive), which contains a value that ranges from 0x80 to 0xff, where 0x80 is the first drive, 0x81 the second, etc. | |
| 221 | 1 | Seconds, which contains a value that ranges from 0 to 59 | |
| 222 | 1 | Minutes, which contains a value that ranges from 0 to 59 | |
| 223 | 1 | Hours, which contains a value that ranges from 0 to 23 | |
| Without disk identity | |||
| 224 | 222 | The second part of the boot (loader) code | |
| With disk identity, used by UEFI, Microsoft Windows NT or later | |||
| 224 | 216 | The second part of the boot (loader) code | |
| 440 | 4 | Disk identity (signature) | |
| 444 | 2 | 0x0000 or 0x5a5a | copy-protection marker |
| Common | |||
| 446 | 16 | Partition table entry 1 | |
| 462 | 16 | Partition table entry 2 | |
| 478 | 16 | Partition table entry 3 | |
| 494 | 16 | Partition table entry 4 | |
| 510 | 2 | "\x55\xaa" | The (boot) signature |
The extended partition record
The extended partition record (EPR) (also referred to as extended boot record (EBR)) starts with a 64 byte (extended) partition record (EPR) like the MBR. This partition table contains information about the logical partition (volume) and additional extended partition tables.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 446 | 0x00 | Unknown (Unused), which should contain zero bytes |
| 446 | 16 | Partition table entry 1, contains the logical partition | |
| 462 | 16 | Partition table entry 2, which should contain an extended partition (of type 0x05) or contain zero bytes if unused | |
| 478 | 16 | 0x00 | Partition table entry 3, which should be unused and contain zero bytes |
| 494 | 16 | 0x00 | Partition table entry 4, which should be unused and contain zero bytes |
| 510 | 2 | "\x55\xaa" | Signature |
The second partition entry contains an extended partition which points to the next EPR. The LBA addresses in the EPR are relative to the start of the first EPR.
The first EPR typically has a partition type of “Extended (CHS)” (0x05) or “Extended (LBA)” (0x0f).
Note that an extended boot record can be empty.
The partition table entry
The partition table entry is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Partition flags | |
| 1 | 3 | The partition start CHS address, which is relative from the start of the MBR | |
| 4 | 1 | Partition type | |
| 5 | 3 | The partition end CHS address, which is relative from the start of the MBR | |
| 8 | 4 | The partition start LBA (sector address) | |
| 12 | 4 | Size of the partition in number of sectors |
For partition table entry 1 (logical partition), the partition start LBA is relative from the start of the current extended partition record.
For partition table entry 2 (next extended partition), the partition start LBA is relative from the start of the first extended partition record.
Partition flags
The partition flags consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x80 | Partition is boot-able |
Partition types
The partition types consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x00 | Empty | |
| 0x01 | FAT12 (CHS) | |
| 0x02 | XENIX root | |
| 0x02 | XENIX user | |
| 0x04 | FAT16 (16 MiB -32 MiB CHS) | |
| 0x05 | Extended (CHS) | |
| 0x06 | FAT16 (32 MiB - 2 GiB CHS) | |
| 0x07 | HPFS/NTFS | |
| 0x08 | AIX | |
| 0x09 | AIX bootable | |
| 0x0a | OS/2 Boot Manager | |
| 0x0b | FAT32 (CHS) | |
| 0x0c | FAT32 (LBA) | |
| 0x0e | FAT16 (32 MiB - 2 GiB LBA) | |
| 0x0f | Extended (LBA) | |
| 0x10 | OPUS | |
| 0x11 | Hidden FAT12 (CHS) | |
| 0x12 | Compaq diagnostics | |
| 0x14 | Hidden FAT16 (16 MiB - 32 MiB CHS) | |
| 0x16 | Hidden FAT16 (32 MiB - 2 GiB CHS) | |
| 0x17 | Hidden HPFS/NTFS | |
| 0x18 | AST SmartSleep | |
| 0x1b | Hidden FAT32 (CHS) | |
| 0x1c | Hidden FAT32 (LBA) | |
| 0x1e | Hidden FAT16 (32 MiB - 2 GiB LBA) | |
| 0x24 | NEC DOS | |
| 0x27 | Unknown (PackardBell recovery/installation partition) | |
| 0x39 | Plan 9 | |
| 0x3c | PartitionMagic recovery | |
| 0x40 | Venix 80286 | |
| 0x41 | PPC PReP Boot | |
| 0x42 | SFS or LDM: Microsoft MBR (Dynamic Disk) | |
| 0x4d | QNX4.x | |
| 0x4e | QNX4.x 2nd part | |
| 0x4f | QNX4.x 3rd part | |
| 0x50 | OnTrack DM | |
| 0x51 | OnTrack DM6 Aux1 | |
| 0x52 | CP/M | |
| 0x53 | OnTrack DM6 Aux3 | |
| 0x54 | OnTrackDM6 | |
| 0x55 | EZ-Drive | |
| 0x56 | Golden Bow | |
| 0x5c | Priam Edisk | |
| 0x61 | SpeedStor | |
| 0x63 | GNU HURD or SysV | |
| 0x64 | Novell Netware 286 | |
| 0x65 | Novell Netware 386 | |
| 0x70 | DiskSecure Multi-Boot | |
| 0x75 | PC/IX | |
| 0x78 | XOSL | |
| 0x80 | Old Minix | |
| 0x81 | Minix / old Linux | |
| 0x82 | Solaris x86 or Linux swap | |
| 0x83 | Linux | |
| 0x84 | Hibernation or OS/2 hidden C: drive | |
| 0x85 | Linux extended | |
| 0x86 | NTFS volume set | |
| 0x87 | NTFS volume set | |
| 0x8e | Linux LVM | |
| 0x93 | Amoeba | |
| 0x94 | Amoeba BBT | |
| 0x9f | BSD/OS | |
| 0xa0 | IBM Thinkpad hibernation | |
| 0xa1 | Hibernation | |
| 0xa5 | FreeBSD | |
| 0xa6 | OpenBSD | |
| 0xa7 | NeXTSTEP | |
| 0xa8 | Mac OS X | |
| 0xa9 | NetBSD | |
| 0xab | Mac OS X Boot | |
| 0xaf | Mac OS X | |
| 0xb7 | BSDI | |
| 0xb8 | BSDI swap | |
| 0xbb | Boot Wizard hidden | |
| 0xc1 | DRDOS/sec (FAT-12) | |
| 0xc4 | DRDOS/sec (FAT-16 < 32M) | |
| 0xc6 | DRDOS/sec (FAT-16) | |
| 0xc7 | Syrinx | |
| 0xda | Non-FS data | |
| 0xdb | CP/M / CTOS / ... | |
| 0xde | Dell Utility | |
| 0xdf | BootIt | |
| 0xe1 | DOS access | |
| 0xe3 | DOS R/O | |
| 0xe4 | SpeedStor | |
| 0xeb | BeOS | |
| 0xee | EFI GPT protective partition | |
| 0xef | EFI system partition (FAT) | |
| 0xf0 | Linux/PA-RISC boot | |
| 0xf1 | SpeedStor | |
| 0xf2 | DOS secondary | |
| 0xf4 | SpeedStor | |
| 0xfb | VMWare file system | |
| 0xfc | VMWare swap | |
| 0xfd | Linux RAID auto-detect | |
| 0xfe | LANstep | |
| 0xff | BBT |
SGI (Silicon Graphics) disklabel format
The SGI disklabel format is a partitioning schema mainly used by the SGI Irix operating system.
Overview
A SGI disklabel consists of one or more partition entries.
Certain paritions have a predefined meaning, such as:
- entry 9 the volume header (partition type 0);
- entry 11 the entire volume (partition type 6).
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | N/A |
| Character strings | ASCII |
SGI disklabel
The SGI disklabel (or volume header) is stored at offset 0.
The SGI disklabel is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\x0b\xe5\xa9\x41" | Signature |
| 4 | 2 | Root partition number | |
| 6 | 2 | Swap partition number | |
| 8 | 16 | ARCS boot file name, with consists of an ASCII string | |
| 24 | 48 | Device parameters | |
| 72 | 15 x 16 = 240 | Array of volume descriptors | |
| 312 | 16 x 12 = 192 | Array of partition entries | |
| 504 | 4 | Checksum | |
| 508 | 4 | Unknown (padding) |
Device parameters
The device parameters are 48 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Skew | |
| 1 | 1 | Gap 1 size | |
| 2 | 1 | Gap 2 size | |
| 3 | 1 | Number of spare cylinders (per volume) | |
| 4 | 2 | Number of (physical) cylinders | |
| 6 | 2 | Heads per volume | |
| 8 | 2 | Tracks per cylinder | |
| 10 | 1 | Unknown (cmd_tag_queue_depth) | |
| 11 | 3 | Unknown (unused) | |
| 14 | 2 | Sectors per track | |
| 16 | 2 | Bytes per sector | |
| 18 | 2 | Unknown (ilfact) | |
| 20 | 4 | Unknown (flags) | |
| 24 | 4 | Unknown (datarate) | |
| 28 | 4 | Unknown (retries_on_error) | |
| 32 | 4 | Unknown (ms_per_word) | |
| 36 | 2 | Unknown (xylogics_gap1) | |
| 38 | 2 | Unknown (xylogics_syncdelay) | |
| 40 | 2 | Unknown (xylogics_readdelay) | |
| 42 | 2 | Unknown (xylogics_gap2) | |
| 44 | 2 | Unknown (xylogics_readgate) | |
| 46 | 2 | Unknown (xylogics_writecont) |
Volume descriptor
The volume descriptors contain a flat file system, where each individual descriptor describes a execute boot file. These execute boot files are stored within the volume header partition.
The volume descriptor is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | File name, with consists of an ASCII string | |
| 8 | 4 | Start sector number, relative to the start of the volume header partition | |
| 12 | 4 | Size, in number of bytes |
Partition entry
The partition entry is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of sectors, relative to the start of the SGI disklabel | |
| 4 | 4 | Start sector number | |
| 8 | 4 | Partition type |
Partition types
| Value | Identifier | Description |
|---|---|---|
| 0 | SGI_VOLHDR | Volume header |
| 1 | SGI_TRKREPL | Track Replacements |
| 2 | SGI_SECREPL | Sector Replacements |
| 3 | SGI_SWAP | IRIX Swap |
| 4 | SGI_BSD or SGI_RAW | SGI BSD or raw |
| 5 | SGI_SYSV or SGI_BOARD | SGI SystemV, board or overlay |
| 6 | SGI_VOLUME | Entire Volume |
| 7 | SGI_EFS | IRIX EFS |
| 8 | SGI_LVOL | SGI Logical Volume |
| 9 | SGI_RVOL | SGI raw logical volume |
| 10 | SGI_XFS | XFS |
| 11 | SGI_XFSLOG | XFS journal |
| 12 | SGI_XLV | SGI XLV Volume Manager |
| 13 | SGI_XVM | SGI XVM Volume Manager |
Volume Shadow Snapshot (volsnap) format
As of Windows Vista the Volume Shadow Snapshot (VSS) stores persistent shadow copies on the local NTFS volume.
Overview
According to “Shadow Copies and Shadow Copy Sets” a shadow copy is a snapshot of a volume. A shadow copy can be part of a set which contains a collection of shadow copies of various volumes, taken at the same time.
Volume Shadow Snapshot (VSS) can use different providers to store shadow copies, this document focuses on the “Microsoft Software Shadow Copy provider 1.0” (GUID: b5946137-7b9f-4925-af80-51abd60b20d5) and will refer to it as volsnap. The volsnap provider stores the copies on the local volume using 16 KiB blocks.
Volsnap uses the GUID 3808876b-c176-4e48-b7ae-04046e6cc752 to identify its data or metadata files. It leverages several metadata files in “\System Volume Information” directory:
- Volsnap catalog; stored in the metadata file named {%VOLSNAPGUID%}
- Volsnap store; stored in the metadata file named {%GUID%}{%VOLSNAPGUID%}
Where %VOLSNAPGUID% (_VSP_DIFF_AREA_FILE_GUID) contains the volsnap identifier and %GUID% contains a time/MAC based GUID.
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | FILETIME in UTC |
| Character strings | UCS-2 little-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
Volume header
The volsnap volume header is part of the NTFS volume header (or $Boot metadata file). The volsnap volume header data is stored at offset 7680 (0x1e00) of the volume and is at least 100 bytes in size, but presumably 512 bytes, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | volsnap identifier, which contains a GUID | |
| 16 | 4 | Format version | |
| 20 | 4 | 0x01 | Record type |
| 24 | 8 | 0x1e00 | Current offset, which is relative to the start of the volume |
| 32 | 8 | 0x1e00 | Unknown (Next offset?), which is relative to the start of the volume |
| 40 | 8 | Unknown (empty value) | |
| 48 | 8 | Catalog offset, which is relative to the start of the volume or contains 0 if there is no catalog | |
| 56 | 8 | Maximum size, in number of bytes or contains 0 if unbounded | |
| 64 | 16 | Volume identifierwhich contains a GUID | |
| 80 | 16 | Shadow copy storage volume identifier, which contains a GUID | |
| 96 | 4 | Unknown | |
| 100 | 412 | Unknown (empty values) |
Version
| Value | Identifier | Description |
|---|---|---|
| 1 | Windows Vista, Windows 7 | |
| 2 | Windows 8 |
Catalog
The catalog contains information about the individual stores. The catalog consists of one or more catalog blocks. Each catalog block is 16384 (0x4000) bytes in size and consists of:
- catalog block header
- an array of catalog entries
The volsnap catalog metadata files contains the catalog blocks stored directly after one-and-other.
If the volume does not contain a catalog when there are no snapshots (stored) but volsnap is enabled.
Catalog block header
The catalog block header is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | volsnap identifier, which contains a GUID | |
| 16 | 4 | 0x01 | Version |
| 20 | 4 | 0x02 | Record type |
| 24 | 8 | Relative (catalog block) offset, which is relative to the start of the first catalog block | |
| 32 | 8 | Current (catalog block) offset, which is relative to the start of the volume | |
| 40 | 8 | Next (catalog block) offset, which is relative to the start of the volume or contains 0 if this is the last block | |
| 48 | 80 | Unknown (empty values) |
Catalog entry
Each catalog entry consists of a catalog entry type 0x02. A corresponding type 0x03 is required if the shadow copy is stored in a store, which is the case as of Windows Vista.
Note that a Windows 2003 R2 catalog does not contain catalog entry type 0x03.
TODO: Determine how Windows 2003 R2 volumes store the snapshot data
The type 0x02 and type 0x03 entries are not necessarily stored directly after one-and-other and can be scattered over the catalog. For now it is assumed that entry type 0x02 must be defined before entry type 0x03.
Also these entries are not necessarily stored in order of age.
There can be unused catalog entries (of type 0x01) as well. Empty catalog entries seem to consist entirely of 0-bytes.
Unused catalog entry (type 0x01)
An unused catalog entry (type 0x01) is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0x01 | Catalog entry type |
| 8 | 120 | Unknown (empty values) |
Catalog entry type 0x02
A catalog entry type 0x02 is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0x02 | Catalog entry type |
| 8 | 8 | Volume size | |
| 16 | 16 | Store identifier, which contains a GUID that is used in the store filename | |
| 32 | 8 | Unknown (Sequence number) | |
| 40 | 8 | Unknown (Flags?), seen 0x40 in Windows in Vista and 7 and 0x440 in Windows 8 (file backup?) | |
| 48 | 8 | Shadow copy creation time, which contains a FILETIME | |
| 56 | 72 | Unknown (empty values) |
Catalog entry type 0x03
A catalog entry type 0x03 is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | 0x03 | Catalog entry type |
| 8 | 8 | Store block list offset, which is relative to the start of the volume | |
| 16 | 16 | Store identifier, which contains a GUID, that is used in the store filename | |
| 32 | 8 | Store header offset, which is relative to the start of the volume | |
| 40 | 8 | Store block range list offset, which is relative to the start of the volume | |
| 48 | 8 | Store (current) bitmap offset, which is relative to the start of the volume | |
| 56 | 8 | NTFS (metadata) file reference | |
| 64 | 8 | Unknown (Allocated size) | |
| 72 | 8 | Store previous bitmap offset, which is relative to the start of the volume or contains 0 if not used | |
| 80 | 8 | Unknown (store index?) | |
| 88 | 40 | Unknown (empty) |
Store
The store contains information about the shadow volume; it actually contains copies of previous versions of data blocks on the volume.
The stores must be applied starting with the most recent on top of the current volume. E.g. if there are 3 stores and we want to access the state of the oldest (number 1) we must first apply the changes in store 3 over the current volume, the changes in store 2 over the resulting volume, and finally the changes in store 1 over the resulting volume.
The store consists of:
- store header
- store block list
- store block range list
- store bitmaps
- data blocks
Store block header
The store block header is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | volsnap identifier, which contains a GUID | |
| 16 | 4 | 0x01 | Version |
| 20 | 4 | Record type | |
| 24 | 8 | Relative (block) offset, which is relative to the start of the store | |
| 32 | 8 | Current (block) offset, which is relative to the start of the volume | |
| 40 | 8 | Next (block) offset, which is relative to the start of the volume or contains 0 if this is the last block | |
| 48 | 8 | Size of store information, whichis only used in first block header and should be 0 in successive block headers | |
| 56 | 72 | Unknown (empty value) |
Store block record types
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | Unknown | |
| 0x0001 | Volume header | |
| 0x0002 | Catalog block header | |
| 0x0003 | Block descriptor list (Diff area table) | |
| 0x0004 | Store header | |
| 0x0005 | Unknown (Store block ranges list) | |
| 0x0006 | Store bitmap |
Store information
The store information is stored directly after the store header.
The store information is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Unknown (identifier?), which contains a GUID | |
| 16 | 16 | Shadow copy identifier, which contains a GUID | |
| 32 | 16 | Shadow copy set identifier, which contains a GUID | |
| 48 | 4 | Snapshot context | |
| 52 | 4 | Unknown (Provider?) | |
| 56 | 4 | Attribute flags | |
| 60 | 4 | Unknown (empty values) | |
| 64 | 2 | Operating machine string size, in number of bytes | |
| 66 | (size) | Operating machine string, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | 2 | Service machine string size, in number of bytes | |
| ... | (size) | Service machine string, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | ... | Unknown (empty value) |
Note that the difference between the operating machine and the service machine is currently unknown.
Store snapshot context
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | VSS_CTX_BACKUP (or Backup) | Standard backup context |
| 0x00000009 | VSS_CTX_APP_ROLLBACK (or ApplicationRollback) | Persistent shadow copy |
| 0x0000000d | VSS_CTX_CLIENT_ACCESSIBLE_WRITERS (or ClientAccessibleWriters) | Read-only shadow copy created with writer involvement |
| 0x00000010 | VSS_CTX_FILE_SHARE_BACKUP | Non-persistent shadow copy created |
| 0x00000019 | VSS_CTX_NAS_ROLLBACK | Persistent shadow copy of a NAS volume |
| 0x0000001d | VSS_CTX_CLIENT_ACCESSIBLE | Read-only shadow copy for Shared Folders |
| 0xffffffff | VSS_CTX_ALL | All types of shadow copy are available for administrative operations |
Note that the store snapshot context value is a combination of (some of the) store attribute flags.
Store attribute flags
“VSS_VOLUME_SNAPSHOT_ATTRIBUTES enumeration (vss.h)” refers to the store attribute flags as _VSS_VOLUME_SNAPSHOT_ATTRIBUTES.
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | VSS_VOLSNAP_ATTR_PERSISTENT | The shadow copy is persistent across reboots |
| 0x00000002 | VSS_VOLSNAP_ATTR_NO_AUTORECOVERY (or VSS_VOLSNAP_ATTR_READ_WRITE) | Auto-recovery is disabled, which appears to not be shown by vssadmin |
| 0x00000004 | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE | The specified shadow copy is a client-accessible |
| 0x00000008 | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE | The shadow copy is not automatically deleted when the shadow copy requester process ends (no auto release) |
| 0x00000010 | VSS_VOLSNAP_ATTR_NO_WRITERS | No writers are involved in creating the shadow copy |
| 0x00000020 | VSS_VOLSNAP_ATTR_TRANSPORTABLE | The shadow copy is to be transported and therefore should not be exposed locally |
| 0x00000040 | VSS_VOLSNAP_ATTR_NOT_SURFACED | The shadow copy is not currently exposed (surfaced) |
| 0x00000080 | VSS_VOLSNAP_ATTR_NOT_TRANSACTED | Not transacted, which appears to not be shown by vssadmin |
| 0x00010000 | VSS_VOLSNAP_ATTR_HARDWARE_ASSISTED | Indicates that a given provider is a hardware provider |
| 0x00020000 | VSS_VOLSNAP_ATTR_DIFFERENTIAL | Indicates that a given provider uses differential data or a copy-on-write mechanism to implement shadow copies |
| 0x00040000 | VSS_VOLSNAP_ATTR_PLEX | Indicates that a given provider uses a PLEX or mirrored split mechanism to implement shadow copies |
| 0x00080000 | VSS_VOLSNAP_ATTR_IMPORTED | The shadow copy of the volume was imported onto this machine |
| 0x00100000 | VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY | The shadow copy is locally exposed |
| 0x00200000 | VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY | The shadow copy is remotely exposed |
| 0x00400000 | VSS_VOLSNAP_ATTR_AUTORECOVER | Indicates that the writer will need to auto-recover the on post snapshot |
| 0x00800000 | VSS_VOLSNAP_ATTR_ROLLBACK_RECOVERY | Indicates that the writer will need to auto-recover the on post snapshot if the snapshot is used for rollback |
| 0x01000000 | VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT | Delayed post snapshot, which is reserved for system use and appears to not be shown by vssadmin |
| 0x02000000 | VSS_VOLSNAP_ATTR_TXF_RECOVERY | Indicates that Transactional NTFS (TxF) recovery should be enforced during shadow copy creation, which appears to be not shown by vssadmin |
Store block list
The store block list contains information about the data block ranges used by the snapshot.
The store block list is stored in blocks of 16384 (0x4000) bytes. Each store block list block consists of:
- a store block header of type 3
- an array of store block descriptors
Block descriptor
The block descriptor is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Original data block offset, which is relative to the start of the volume | |
| 8 | 8 | Relative store data block offset, which is relative to the start of the store. TODO: determine if the lower bits are used for different purpose | |
| 16 | 8 | Store data block offset, which is relative to the start of the volume | |
| 24 | 4 | Flags | |
| 28 | 4 | Allocation bitmap, which is used if flag 0x02 is set, otherwise is should contain a value of 0 |
Store block descriptor flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Is forwarder. The absolute offset is set to 0 and the relative offset maps to the original offset of the next block | |
| 0x00000002 | Overlay. The block descriptor is an overlay. The allocation bitmap value contains information about the block fill | |
| 0x00000004 | Not used. If set, the block is ignored | |
| 0x00000008 | Unknown | |
| 0x00000010 | Unknown | |
| 0x00000020 | Unknown | |
| 0x00000040 | Unknown | |
| 0x00000080 | Unknown |
Successive block descriptors
Note that this section is not complete yet, since the meaning of several flags is unknown.
Successive block descriptors with the same original offset are be handled differently based on their flags and position in the block list. The block list is scanned front to back.
For the new block descriptor:
* If the not used flag is set (0x04):
* Ignore the new block descriptor
* If the overlay flag (0x02) is not set:
* If there is a corresponding block descriptor in the reverse block list:
Meaning that the original offset (of the new block descriptor) matches
the relative offset of a forwarder block descriptor in the reverse block
list.
* Replace the original offset with that of the forwarder block
descriptor in the reverse block list.
* Remove the forwarder block descriptor from the reverse block list.
* If the forwarder flag (0x01) (of the new block descriptor) is set:
* If the original offset (of the new block descriptor) is the same
as the relative offset:
* Ignore the new block descriptor
* If no previous block descriptor was found:
* Add the new block descriptor to the block list.
* Else:
* If the overlay flag (0x02) is set:
The new block descriptor contains an overlay. The allocation bitmap
contains information about which part of the block is used. Every bit
in the allocation bitmap signifies a block of 512 bytes. The LSB in
the allocation bitmap represent the first 512 bytes in the block.
Normally the relative offset is should not be 1, but this seems to be
ignored if it is.
* If an existing overlay block descriptor was defined:
* Extended the existing overlay.
Normally the relative offset should be 1 and the original offset
should match that of the existing overlay block descriptor. If
not these values seem to be ignored and the existing overlay
is extended with the allocation bitmap in the new block descriptor.
* Else:
* Replace the existing block descriptor. Existing overlay block
descriptors are applied to the new block descriptor.
* If the forwarder flag (0x01) is set:
* If no previous reverse block descriptor was found:
* Add the new block descriptor to the reverse block list.
* Else:
* Replace the existing reverse block descriptor.
Store block range list
The store block range list contains information about the data block ranges used by the store itself. It is probably used to maintain these ranges on the volume layer, since the corresponding NTFS file entry data runs are applied on the file system layer.
The store block range list is stored in blocks of 16384 (0x4000) bytes. Each store block range list block consists of:
- a store block header of type 5
- an array of store block range list entries
Store block range entry
The store block range entry is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Store (block range start) offset, which is relative to the start of the volume | |
| 8 | 8 | Relative (block range start) offset, which is relative to the start of the store | |
| 16 | 8 | Block range size |
Store bitmap
The store bitmap contains information about the allocation of blocks.
The store bitmap is stored in blocks of 16384 (0x4000) bytes. Each store bitmap block consists of:
- a store block header of type 6
- a bitmap
Store (current) bitmap data
Every bit in the store (current) bitmap represents a block of 16384 (0x4000) bytes, where the LSB is the first bit in a byte.
If a bit is set, the corresponding block is considered not in-use (or not allocated) by the store.
The use of this bitmap is described in the section: reading snapshot data.
Store previous bitmap data
Every bit in the store previous bitmap represents a block of 16384 (0x4000) bytes, where the LSB is the first bit in a byte.
If a bit is set, the corresponding block is not in-use (or not allocated) by the previous store.
Note that the first store can also contain a previous bitmap if an older store before it was removed.
The use of this bitmap is described in the section: reading snapshot data.
Store data block
The store data is stored in blocks of 16384 (0x4000) bytes.
Reading snapshot data
For the size of the data that will fit in the buffer:
* If the block offset has a corresponding block descriptor:
* The data is defined by block descriptor and has a maximum size accordingly
* If this is the active store and the block has an overlay:
* If the overlay applies:
* use the overlay block descriptor
* If the forwarder flag (0x01) is set
and there is a next store:
* read the block from the next store using the relative store offset
* Else:
* read the block from the current volume using the store offset
* Else:
* If there is a next store:
* read the block from the next store
* Else if the block offset has a corresponding reverse block descriptor:
* read the block from the current volume
* Else if the active store is the most recent (last) store
and the block is flagged in the current bitmap
and ( the store has no previous bitmap
or the block is flagged in the previous bitmap ):
* zero-fill the block
* Else:
* read the block from the current volume
* Increment the block offset with the size of the block data that was read
Note that on Windows the actual behavior of unused block is undefined. A read of a corresponding block will return successful but will not alter the buffer passed to the read. For sanitation purposes Keramics will zero-fill the block.
Format edge cases and corruption scenarios
This chapter contains several corruption scenarios that have been encountered “in the wild”.
Catalog volume size out of bounds
Note that this currently considered a corruption scenario future findings may or may not prove otherwise.
The volume size of one of the catalog entries exceeds the size of the underlying volume and does not corresponds with the volume size defined by the rest of the catalog entries.
Scope snapshots
Technically scoped snaphots are a feature of volsnapa as of of Windows 8 or Windws Server 2012 and not a corruption scenario. It has been captured as a corruption scenario since it leads to some interesting side effects within file content of the snapshot.
Scope snapshots functionality can be controlled via the Windows Registry value:
Key path: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore
Value name: ScopeSnapshots
Per “Scoped Snapshots vmicvss Error 13 on Server 2012, Windows 8” a scope snapshot is a special volume snapshot for volsnap performance, which is mainly used by Windows critical updates. Scope means the volsnap only creates Copy on Write (Shadow) volume for the files that are involved in the updates instead of all the files on the volume.
References
- Shadow Copies and Shadow Copy Sets
- Scoped Snapshots vmicvss Error 13 on Server 2012, Windows 8, by M. Berry
- VSS_VOLUME_SNAPSHOT_ATTRIBUTES enumeration (vss.h)
File system formats
A file system format is used to manage the storage of files.
Terminology
- File entry (file system entry): an object that represent an element within the file system, such as a file or directory. A file system typically stores metadata of a file entry, such as the name, size, permissions, date and time values, and location of the content.
- Data fork (or data stream): a file system object that represents the content of a file entry. NTFS and HFS support multiple data forks (or data streams) for an individual file entry.
- Extended attribute: A file system object that represents additional (or extended) metadata of an individual file entry.
- Reparse point: a file system object that redirects to another location or implementation (filter driver), such as Windows Overlay Filter (WOF) compression. NTFS and ReFS support reparse points.
- Data range: a contiguous storage space, such as a file system extent or sequence of blocks or sectors. Can be compressed, encrypted or sparse.
Formats
- Apple File System (APFS)
- Apple File System Compression (decmpfs)
- Extended File System (ext)
- Extensible File Allocation Table (exFAT)
- File Allocation Table (FAT)
- Hierarchical File System (HFS)
- Macintosh File System (MFS)
- New Technologies File System (NTFS)
- X File System (XFS)
Also see: hybrid volume and file system formats
Apple File System Compression (decmpfs)
Hierarchical File System (HFS) and Apple File System (APFS) use Apple File System Compression (decmpfs) to compress file contents.
decmpfs is sometimes referred to as AFSC (Apple File System Compression) or HFS/HFS+ compression and was introduced in Mac OS X 10.6 (Snow Leopard).
Overview
An Apple File System Compression (decmpfs) compressed file consists of:
- an extended attribute named “com.apple.decmpfs”
- compressed file content data
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
decmpfs extended attribute
The decmpfs extended attribute consists of:
- decmpfs header
- optional compressed data
decmpfs header
The decmpfs header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "fpmc" | Signature |
| 4 | 4 | Compression method | |
| 8 | 8 | Uncompressed data size |
Note that the signature is likely stored in little-endian and represents “cmpf”.
Compression methods
| Value | Identifier | Description |
|---|---|---|
| 1 | CMP_Type1 | Unknown (uncompressed extended attribute data) |
| 3 | kAFSCTypeZLibChunk | zlib compressed extended attribute data, where the compressed data is stored in the extended attribute after the compressed data header |
| 4 | kAFSCTypeZLib | 64k chunked zlib compressed resource fork, where the compressed data is stored in the resource fork |
| 5 | Unknown (sparse compressed extended attribute data), where the uncompressed data contains 0-byte values. According to copyfile.c specifies de-dup within the generation store | |
| 6 | Unknown (unused) | |
| Added in Mac OS X Yosemite (10.10) | ||
| 7 | kAFSCTypeLZVNChunk | LZVN compressed extended attribute data, where the compressed data is stored in the extended attribute after the compressed data header |
| 8 | kAFSCTypeLZVN | 64k chunked LZVN compressed resource fork, where the compressed data is stored in the resource fork |
| 9 | kAFSCTypeRawChunk | Uncompressed (raw) extended attribute data |
| 10 | kAFSCTypeRaw | 64k chunked uncompressed (raw) data resource fork, where the compressed data is stored in the resource fork |
| Added in Mac OS X El Capitan (10.11) | ||
| 11 | kAFSCTypeLZFSEChunk | LZFSE compressed extended attribute data, where the compressed data is stored in the extended attribute after the compressed data header |
| 12 | kAFSCTypeLZFSE | 64k chunked LZFSE compressed resource fork, where the compressed data is stored in the resource fork |
| Added in macOS Ventura (13.0) | ||
| 13 | kAFSCTypeLZBitmapChunk | LZBITMAP compressed extended attribute data, where the compressed data is stored in the extended attribute after the compressed data header |
| 14 | kAFSCTypeLZBitmap | LZBITMAP compressed resource fork, where the compressed data is stored in the resource fork |
| 255 | CMP_MAX | Maximum supported compression method |
| 0x80000001 | DATALESS_CMPFS_TYPE | Unknown (faulting file or dataless file or directory) |
| 0x80000002 | DATALESS_PKG_CMPFS_TYPE | Unknown (dataless package) |
Note that copyfile.c indicates faulting files are deprecated since Mac OS X Yosemite (10.10).
Compressed file content data
The location of the compressed file content data depends on the compression method:
- sparse data
- compressed data stored in extended attribute
- compressed data stored in resource fork
Sparse data
Compression method 5 has been observed to be used for sparse data, the file content data contains 0-byte values.
There are 12 bytes stored after the decmpfs compressed data header that consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (Seen: 1) | |
| 4 | 4 | Unknown | |
| 8 | 4 | Unknown (Seen: 0) |
Compressed data stored in extended attribute
Compression method 3, 5, 7, 9 and 11 store the compressed file content data in the extended attribute after the decmpfs compressed data header.
The compressed data consist of 1 compressed data block.
Compressed data stored in resource fork
Compression method 4, 8, 10 and 12 store the compressed file content data in the resource fork of the file.
The compressed data starts with metadata that contains the offsets of the compressed data blocks.
LZFSE compressed data
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 x ... | Array of compressed data block offsets, where an offset is relative from the start of the LZFSE compressed data | |
| ... | ... | LZFSE compressed data blocks |
LZFSE compressed data block
If the first byte in the LZFSE compressed data block is 0xff, the block contains uncompressed data, otherwise the block should start with a LZFSE block marker.
LZVN compressed data
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 x ... | Array of compressed data block offsets, where an offset is relative from the start of the LZVN compressed data | |
| ... | ... | LZVN compressed data blocks |
LZVN compressed data block
If the first byte in the LZVN compressed data block is 0x06 (end of stream oppcode), the block contains uncompressed data.
A compressed data block can contains a maximum of 65536 bytes of data. The compressed data block therefore should not exceed 65537 bytes in size.
Raw compressed data
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 x ... | Array of compressed data block offsets, where an offset is relative from the start of the raw compressed data | |
| ... | ... | raw compressed data blocks |
Raw compressed data block
If the first byte in the raw compressed data block is 0xcc, the block contains uncompressed data.
The behavior of other byte values is unknown, it has been observed that Mac OS returns no data.
zlib compressed data
- zlib compressed header
- zlib compressed data block descriptors
- zlib compressed data blocks
- zlib compressed footer
zlib compressed header
The zlib compressed header is 260 bytes size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 256 | Unknown (header size or offset?) |
| 4 | 4 | Compressed footer offset, where the offset is relative from the start of the zlib compressed data | |
| 8 | 4 | Unknown (total size - header size?) | |
| 12 | 4 | 50 | Compressed footer size |
| 16 | 240 | Unknown (empty values) | |
| 256 | 4 | Unknown |
Note that the values in the zlib compressed header are stored in big-endian.
zlib compressed data block descriptors
The zlib compressed data block descriptors are variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of block descriptors (offset and size tuples) | |
| 4 | 8 x ... | Array of compressed data block descriptors |
Note that the values in the zlib compressed data block descriptors are store in little-endian.
zlib compressed data block descriptor
The zlib compressed data block descriptor is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Compressed block offset, where the offset is relative from the start of the zlib compressed data block descriptors | |
| 4 | 4 | Compressed block size |
zlib compressed footer
The zlib compressed footer is 50 bytes size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 24 | Unknown (empty values) | |
| 24 | 2 | Unknown (signature offset?) | |
| 26 | 2 | Unknown (footer size?) | |
| 28 | 2 | Unknown | |
| 30 | 4 | "cmpf" | signature (DECMPFS_MAGIC) |
| 34 | 2 | Unknown (empty values?) | |
| 36 | 2 | Unknown | |
| 38 | 2 | Unknown | |
| 40 | 2 | Unknown (uncompressed block size?) | |
| 42 | 8 | Unknown (empty values) |
Note that the values in the zlib compressed header are stored in big-endian.
zlib compressed data block
If the first byte in the zlib compressed data block is 0xff, the block contains uncompressed data, otherwise the block should start with 0x78.
Extended File System (ext) format
The Extended File System (ext) is one of the more common file system used in Linux.
There are multiple version of ext.
| Version | Remarks |
|---|---|
| 1 | Introduced in April 1992 |
| 2 | Introduced in January 1993 |
| 3 | Introduced in November 2001, which featured journaling, dynamic growth and large directory indexing (HTree) |
| 4 | Introduces in October 2006 as unstable and becmae stable in October 2008, which featured extents and improved timestamps |
Overview
An Extended File System (ext) consists of:
- one or more block groups
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian, with the exception of UUID values that are stored in big-endian |
| Date and time values | number of seconds since January 1, 1970 00:00:00 (POSIX epoch), disregarding leap seconds. Or number of nanoseconds, when extra precision is enabled. Date and time values are stored in UTC |
| Character strings | UTF-8 or a narrow character (Single Byte Character (SBC) or Multi Byte Character (MBC)) stored using a system defined codepage |
Block group
A block group consists of:
- optional 1024 bytes of boot code or zero bytes (at offset: 0)
- optional superblock
- optional group descriptor table
- block bitmap
- inode bitmap
- allocated and unallocated blocks
The primary superblock is stored at offset 1024 relative from the start of the volume. Backup superblocks are stored at offset 1024 relative from the start of the block group if block size <= 1024 or otherwise at offset 0 from the start of the block group.
The group descriptor table is stored in the block after the superblock.
An ext2 file system with revision 0 stores a copy at the start of every block group, along with backups of the group descriptor table. Later revisions reduce the number of backup copies by only putting backups in specific groups (sparse superblock feature EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER).
Not all values in a backup superblock and backup group descriptor tables match those of the primary superblock and group descriptor table.
Note that backup superblocks can be empty (filled with 0-byte values) or contain remnant data on an Android ext file system with sparse_super.
Flex block groups
Flex (or flexible) block groups are a set of block groups that treated as a single logical block group. Metadata such as the superblock, group descriptors, data block bitmaps spans the entire logical block group and not the individual block groups part of the set.
Meta block groups
Meta block groups (META_BG) are a set (or cluster) of block groups, for which its group descriptor structures can be stored in a single block.
The first meta block group value in the superblock indicates what the first meta block group is to cluster as a meta block group. For example if the first meta block group value is 256, and the number of group descriptors that can be stored in a single block 64, then the group descriptors for the block groups [0, 16383] are stored in the group descriptor table after the primary superblock and corresponding locations of backups.
Successive group descriptor tables, for example [16384, 16447], are stored in the first block group of a meta block group and backups in the second and last block groups of the meta block group.
Blocks
The volume is devided in blocks:
block offset = block number * block size
The block size is defined in the superblock.
Note that mke2fs indicates the maximum block size is 65536.
The superblock
The ext2 superblock
The ext2 superblock is 208 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of inodes | |
| 4 | 4 | Number of blocks | |
| 8 | 4 | Number of reserved blocks. Reserved blocks are used to prevent the file system from filling up | |
| 12 | 4 | Number of unallocated blocks | |
| 16 | 4 | Number of unallocated inodes | |
| 20 | 4 | First data block number. The block number is relative from the start of the volume | |
| 24 | 4 | Block size, which contains the number of bits to shift 1024 to the MSB (left) | |
| 28 | 4 | Fragment size, which contains the number of bits to shift 1024 to the MSB (left) | |
| 32 | 4 | Number of blocks per block group | |
| 36 | 4 | Number of fragments per block group | |
| 40 | 4 | Number of inodes per block group | |
| 44 | 4 | Last mount time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 48 | 4 | Last written time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 52 | 2 | The (current) mount count | |
| 54 | 2 | Maximum mount count | |
| 56 | 2 | "\x53\xef" | Signature |
| 58 | 2 | File system state flags | |
| 60 | 2 | Error-handling status | |
| 62 | 2 | Minor format revision | |
| 64 | 4 | Last consistency check time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 68 | 4 | Consistency check interval, which which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 72 | 4 | Creator operating system | |
| 76 | 4 | Format revision | |
| 80 | 2 | Reserved block owner (or user) identifier (UID) | |
| 82 | 2 | Reserved block group identifier (GID) | |
| Dynamic inode information, if major version is EXT2_DYNAMIC_REV | |||
| 84 | 4 | First non-reserved inode | |
| 88 | 2 | Inode size. Note that the inode size must be a power of 2 larger or equal to 128, the maximum supported by mke2fs is 1024 | |
| 90 | 2 | Block group, which contains a block group number | |
| 92 | 4 | Compatible feature flags | |
| 96 | 4 | Incompatible feature flags | |
| 100 | 4 | Read-only compatible feature flags | |
| 104 | 16 | File system identifier, which contains a big-endian UUID | |
| 120 | 16 | Volume label, which contains a narrow character string without end-of-string character | |
| 136 | 64 | Last mount path, which contains a narrow character string without end-of-string character | |
| 200 | 4 | Algorithm usage bitmap | |
| Performance hints, if EXT2_COMPAT_PREALLOC is set | |||
| 204 | 1 | Number of pre-allocated blocks per file | |
| 205 | 1 | Number of pre-allocated blocks per directory | |
| 206 | 2 | Unknown (padding) | |
The ext3 superblock
The ext3 superblock is 336 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of inodes | |
| 4 | 4 | Number of blocks | |
| 8 | 4 | Number of reserved blocks. Reserved blocks are used to prevent the file system from filling up | |
| 12 | 4 | Number of unallocated blocks | |
| 16 | 4 | Number of unallocated inodes | |
| 20 | 4 | First data block number. The block number is relative from the start of the volume | |
| 24 | 4 | Block size, which contains the number of bits to shift 1024 to the MSB (left) | |
| 28 | 4 | Fragment size, which contains the number of bits to shift 1024 to the MSB (left) | |
| 32 | 4 | Number of blocks per block group | |
| 36 | 4 | Number of fragments per block group | |
| 40 | 4 | Number of inodes per block group, which can be 0 in combination with EXT3_FEATURE_INCOMPAT_JOURNAL_DEV | |
| 44 | 4 | Last mount time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 48 | 4 | Last written time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 52 | 2 | The (current) mount count | |
| 54 | 2 | Maximum mount count | |
| 56 | 2 | "\x53\xef" | Signature |
| 58 | 2 | File system state flags | |
| 60 | 2 | Error-handling status | |
| 62 | 2 | Minor format revision | |
| 64 | 4 | Last consistency check time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 68 | 4 | Consistency check interval, which which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 72 | 4 | Creator operating system | |
| 76 | 4 | Format revision | |
| 80 | 2 | Reserved block owner (or user) identifier (UID) | |
| 82 | 2 | Reserved block group identifier (GID) | |
| Dynamic inode information, if major version is EXT2_DYNAMIC_REV | |||
| 84 | 4 | First non-reserved inode | |
| 88 | 2 | Inode size. Note that the inode size must be a power of 2 larger or equal to 128, the maximum supported by mke2fs is 1024 | |
| 90 | 2 | Block group, which contains a block group number | |
| 92 | 4 | Compatible feature flags | |
| 96 | 4 | Incompatible feature flags | |
| 100 | 4 | Read-only compatible feature flags | |
| 104 | 16 | File system identifier, which contains a big-endian UUID | |
| 120 | 16 | Volume label, which contains a narrow character string without end-of-string character | |
| 136 | 64 | Last mount path, which contains a narrow character string without end-of-string character | |
| 200 | 4 | Algorithm usage bitmap | |
| Performance hints, if EXT2_COMPAT_PREALLOC is set | |||
| 204 | 1 | Number of pre-allocated blocks per file | |
| 205 | 1 | Number of pre-allocated blocks per directory | |
| 206 | 2 | Unknown (padding) | |
| Journalling support, if EXT3_FEATURE_COMPAT_HAS_JOURNAL is set | |||
| 208 | 16 | Journal identifier, which contains a big-endian UUID | |
| 224 | 4 | Journal inode | |
| 228 | 4 | Unknown (Journal device) | |
| 232 | 4 | Unknown (Head of orphan inode list). The orphan inode list is a list of inodes to delete | |
| 236 | 4 x 4 | hash-tree seed | |
| 252 | 1 | Default hash version | |
| 253 | 1 | Journal backup type | |
| 254 | 2 | Group descriptor size | |
| 256 | 4 | Default mount options | |
| 260 | 4 | First meta block group (or metablock) | |
| 264 | 4 | File system creation time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 268 | 17 x 4 | Backup journal inodes | |
The ext4 superblock
The superblock is 1024 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of inodes | |
| 4 | 4 | Number of blocks, which contains the lower 32-bit of the value | |
| 8 | 4 | Number of reserved blocks, which contains the lower 32-bit of the value. Reserved blocks are used to prevent the file system from filling up | |
| 12 | 4 | Number of unallocated blocks, which contains the lower 32-bit of the value | |
| 16 | 4 | Number of unallocated inodes, which contains the lower 32-bit of the value | |
| 20 | 4 | Root group block number. The block number is relative from the start of the volume | |
| 24 | 4 | Block size, which contains the number of bits to shift 1024 to the most-significant-bit (MSB) | |
| 28 | 4 | Fragment size, which contains the number of bits to shift 1024 to the most-significant-bit (MSB) | |
| 32 | 4 | Number of blocks per block group | |
| 36 | 4 | Number of fragments per block group | |
| 40 | 4 | Number of inodes per block group, which can be 0 in combination with EXT4_FEATURE_INCOMPAT_JOURNAL_DEV | |
| 44 | 4 | Last mount time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 48 | 4 | Last written time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 52 | 2 | The (current) mount count | |
| 54 | 2 | Maximum mount count | |
| 56 | 2 | "\x53\xef" | Signature |
| 58 | 2 | File system state flags | |
| 60 | 2 | Error-handling status | |
| 62 | 2 | Minor format revision | |
| 64 | 4 | Last consistency check time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 68 | 4 | Consistency check interval, which which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 72 | 4 | Creator operating system | |
| 76 | 4 | Format revision | |
| 80 | 2 | Reserved block owner (or user) identifier (UID) | |
| 82 | 2 | Reserved block group identifier (GID) | |
| Dynamic inode information, if major version is EXT2_DYNAMIC_REV | |||
| 84 | 4 | First non-reserved inode | |
| 88 | 2 | Inode size. Note that the inode size must be a power of 2 larger or equal to 128, the maximum supported by mke2fs is 1024 | |
| 90 | 2 | Block group | |
| 92 | 4 | Compatible feature flags | |
| 96 | 4 | Incompatible feature flags | |
| 100 | 4 | Read-only compatible feature flags | |
| 104 | 16 | File system identifier, which contains a big-endian UUID | |
| 120 | 16 | Volume label, which contains a narrow character string without end-of-string character | |
| 136 | 64 | Last mount path, which contains a narrow character string without end-of-string character | |
| 200 | 4 | Algorithm usage bitmap | |
| Performance hints, if EXT2_COMPAT_PREALLOC is set | |||
| 204 | 1 | Number of pre-allocated blocks per file | |
| 205 | 1 | Number of pre-allocated blocks per directory | |
| 206 | 2 | Unknown (padding) | |
| Journalling support, if EXT3_FEATURE_COMPAT_HAS_JOURNAL is set | |||
| 208 | 16 | Journal identifier, which contains a big-endian UUID | |
| 224 | 4 | Journal inode | |
| 228 | 4 | Unknown (Journal device) | |
| 232 | 4 | Unknown (Head of orphan inode list). The orphan inode list is a list of inodes to delete | |
| 236 | 4 x 4 | hash-tree seed | |
| 252 | 1 | Default hash version | |
| 253 | 1 | Journal backup type | |
| 254 | 2 | Group descriptor size | |
| 256 | 4 | Default mount options | |
| 260 | 4 | First meta block group (or metablock) | |
| 264 | 4 | File system creation time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 268 | 17 x 4 | Backup journal inodes | |
| If 64-bit support (EXT4_FEATURE_INCOMPAT_64BIT) is enabled | |||
| 336 | 4 | Number of blocks, which contains the upper 32-bit of the value | |
| 340 | 4 | Number of reserved blocks, which contains the upper 32-bit of the value | |
| 344 | 4 | Number of unallocated blocks, which contains the upper 32-bit of the value | |
| 348 | 2 | Minimum inode size | |
| 350 | 2 | Reserved inode size | |
| 352 | 4 | Miscellaneous flags | |
| 356 | 2 | RAID stride | |
| 358 | 2 | Multiple mount protection (MMP) update interval in seconds | |
| 360 | 8 | Block for multi-mount protection | |
| 368 | 4 | Unknown (blocks on all data disks (N*stride)) | |
| 372 | 1 | Number of block groups per flex block group, which is stored as: 2 ^ value | |
| 373 | 1 | Checksum type | |
| 374 | 1 | Unknown (encryption level) | |
| 375 | 1 | Unknown (padding) | |
| 376 | 8 | Unknown (s_kbytes_written) | |
| 384 | 4 | Inode number of active snapshot | |
| 388 | 4 | Identifier of active snapshot | |
| 392 | 8 | Unknown (reserved s_snapshot_r_blocks_count) | |
| 400 | 4 | Inode number of snapshot list head | |
| 404 | 4 | Unknown (s_error_count) | |
| 408 | 4 | Unknown (s_first_error_time) | |
| 412 | 4 | Unknown (s_first_error_ino) | |
| 416 | 8 | Unknown (s_first_error_block) | |
| 424 | 32 | Unknown (s_first_error_func) | |
| 456 | 4 | Unknown (s_first_error_line) | |
| 460 | 4 | Unknown (s_last_error_time) | |
| 464 | 4 | Unknown (s_last_error_ino) | |
| 468 | 4 | Unknown (s_last_error_line) | |
| 472 | 8 | Unknown (s_last_error_block) | |
| 480 | 32 | Unknown (s_last_error_func) | |
| 512 | 64 | Unknown (s_mount_opts) | |
| 576 | 4 | Unknown (s_usr_quota_inum) | |
| 580 | 4 | Unknown (s_grp_quota_inum) | |
| 584 | 4 | Unknown (s_overhead_clusters) | |
| 588 | 2 x 4 | Unknown (s_backup_bgs) | |
| 596 | 4 | Unknown (s_encrypt_algos) | |
| 600 | 16 | Unknown (s_encrypt_pw_salt) | |
| 616 | 4 | Unknown (s_lpf_ino) | |
| 620 | 4 | Unknown (s_prj_quota_inum) | |
| 624 | 4 | Metadata checksum seed | |
| 628 | 1 | Unknown (s_wtime_hi) | |
| 629 | 1 | Unknown (s_mtime_hi) | |
| 630 | 1 | Unknown (s_mkfs_time_hi) | |
| 631 | 1 | Unknown (s_lastcheck_hi) | |
| 632 | 1 | Unknown (s_first_error_time_hi) | |
| 633 | 1 | Unknown (s_last_error_time_hi) | |
| 634 | 1 | Unknown (s_first_error_errcode) | |
| 635 | 1 | Unknown (s_last_error_errcode) | |
| 636 | 2 | Unknown (s_encoding) | |
| 638 | 2 | Unknown (s_encoding_flags) | |
| 640 | 4 | Unknown (s_orphan_file_inum) | |
| 644 | 94 x 4 = 376 | Unknown (reserved) | |
| 1020 | 4 | Checksum | |
If checksum type is CRC-32C, the checksum is stored as 0xffffffff - CRC-32C.
Note that some versions of mkfs.ext set the file system creation time even for ext2 and when EXT3_FEATURE_COMPAT_HAS_JOURNAL is not set.
TODO: Is the only way to determine the file system version the compatibility and equivalent flags?
Checksum calculation
If checksum type is CRC-32C, the CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated over the 1020 bytes of data of the suberblock.
Metadata checksum seed calculation
If checksum type is CRC-32C, the CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated over:
- the 16 byte file system identifier in the superblock
If EXT4_FEATURE_INCOMPAT_CSUM_SEED is set the metadata checksum seed value stored in the superblock should be used instead of calculating it based on the file system identifier.
If checksum type is CRC-32C, the metadata checksum seed is stored as:
0xffffffff - CRC-32C
File system state flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | Is clean | |
| 0x0002 | Has errors | |
| 0x0004 | Recovering orphan inodes |
Error-handling status
| Value | Identifier | Description |
|---|---|---|
| 1 | Continue | |
| 2 | Remount as read-only | |
| 3 | Panic |
Creator operating system
| Value | Identifier | Description |
|---|---|---|
| 0 | Linux | |
| 1 | GNU Hurd | |
| 2 | Masix | |
| 3 | FreeBSD | |
| 4 | Lites |
Format revision
| Value | Identifier | Description |
|---|---|---|
| 0 | EXT2_GOOD_OLD_REV | Original version with a fixed inode size of 128 bytes |
| 1 | EXT2_DYNAMIC_REV | Version with dynamic inode size support |
Compatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | EXT2_COMPAT_PREALLOC | Pre-allocate directory blocks, which is intended to reduce fragmentation |
| 0x00000002 | EXT2_FEATURE_COMPAT_IMAGIC_INODES | Has AFS server inodes |
| 0x00000004 | EXT3_FEATURE_COMPAT_HAS_JOURNAL | Has a journal |
| 0x00000008 | EXT2_FEATURE_COMPAT_EXT_ATTR | Has extended attributes |
| 0x00000010 | EXT2_FEATURE_COMPAT_RESIZE_INO, EXT2_FEATURE_COMPAT_RESIZE_INODE | Is resizeable, the file system has reserved GDT blocks for expansion, which also requires RO_COMPAT_SPARSE_SUPER |
| 0x00000020 | EXT2_FEATURE_COMPAT_DIR_INDEX | Has indexed directories |
| 0x00000040 | COMPAT_LAZY_BG | Unknown (Lazy block group) |
| 0x00000080 | COMPAT_EXCLUDE_INODE | Unknown (Exclude inode), which is not yet implemented and intended for a future file system snapshot feature |
| 0x00000100 | COMPAT_EXCLUDE_BITMAP | Unknown (Exclude bitmap), which is not yet implemented and intended for a future file system snapshot feature |
| 0x00000200 | EXT4_FEATURE_COMPAT_SPARSE_SUPER2 | Has sparse superblock version 2 |
| 0x00000400 | EXT4_FEATURE_COMPAT_FAST_COMMIT | Unknown (fast commit) |
| 0x00000800 | EXT4_FEATURE_COMPAT_STABLE_INODES | Unknown (stable inodes) |
| 0x00001000 | EXT4_FEATURE_COMPAT_ORPHAN_FILE | Has orphan file |
Note that EXT2_FEATURE_COMPAT_, EXT3_FEATURE_COMPAT_, EXT4_FEATURE_COMPAT_ and COMPAT_ can be used interchangeably.
Incompatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | EXT2_FEATURE_INCOMPAT_COMPRESSION | Has compression, which is not yet implemented |
| 0x00000002 | EXT2_FEATURE_INCOMPAT_FILETYPE | Directory entry has file type |
| 0x00000004 | EXT3_FEATURE_INCOMPAT_RECOVER | Needs recovery |
| 0x00000008 | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV | Journal device |
| 0x00000010 | EXT2_FEATURE_INCOMPAT_META_BG | Has meta (or metadata) block groups |
| 0x00000040 | EXT4_FEATURE_INCOMPAT_EXTENTS | Has extents |
| 0x00000080 | EXT4_FEATURE_INCOMPAT_64BIT | Has 64-bit support, which supports more than 2^32 blocks |
| 0x00000100 | EXT4_FEATURE_INCOMPAT_MMP | Multiple mount protection |
| 0x00000200 | EXT4_FEATURE_INCOMPAT_FLEX_BG | Has flex (or flexible) block groups |
| 0x00000400 | EXT4_FEATURE_INCOMPAT_EA_INODE | Has large inodes, which are larger than 128 bytes |
| 0x00001000 | EXT4_FEATURE_INCOMPAT_DIRDATA | Data in directory entry, which is not yet implemented |
| 0x00002000 | EXT4_FEATURE_INCOMPAT_CSUM_SEED, EXT4_FEATURE_INCOMPAT_BG_USE_META_CSUM | Initial metadata checksum value (or seed) is stored in the superblock |
| 0x00004000 | EXT4_FEATURE_INCOMPAT_LARGEDIR | Large directory >2GB or 3-level hash tree (HTree) |
| 0x00008000 | EXT4_FEATURE_INCOMPAT_INLINE_DATA | Has data stored in inode |
| 0x00010000 | EXT4_FEATURE_INCOMPAT_ENCRYPT | Has encrypted inodes |
| 0x00020000 | EXT4_FEATURE_INCOMPAT_CASEFOLD | Hash case folding |
Note that EXT2_FEATURE_INCOMPAT_, EXT3_FEATURE_INCOMPAT_, EXT4_FEATURE_INCOMPAT_ and INCOMPAT_ can be used interchangeably.
Read-only compatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER | Has sparse superblocks and group descriptor tables. If set a superblock is stored in block groups 0, 1 and those that are powers of 3, 5 and 7. If not set a superblock is stored in every block group |
| 0x00000002 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | Contains large files |
| 0x00000004 | EXT2_FEATURE_RO_COMPAT_BTREE_DIR | Intended for hash-tree directory (or directory B-tree), which is not yet implemented |
| 0x00000008 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE | Has huge file support |
| 0x00000010 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM | Has group descriptors with checksums |
| 0x00000020 | EXT4_FEATURE_RO_COMPAT_DIR_NLINK | The ext3 32000 subdirectory limit does not apply. A directory's number of links will be set to 1 if it is incremented past 64999 |
| 0x00000040 | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | Has large inodes. The size of an inode can be larger than 128 bytes |
| 0x00000080 | EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT | Has snapshots, which is not yet implemented and intended for a future file system snapshot feature |
| 0x00000100 | EXT4_FEATURE_RO_COMPAT_QUOTA | Quota is handled transactionally with the journal |
| 0x00000200 | EXT4_FEATURE_RO_COMPAT_BIGALLOC | Has big block allocation bitmaps. Block allocation bitmaps are tracked in units of clusters (of blocks) instead of blocks |
| 0x00000400 | EXT4_FEATURE_RO_COMPAT_METADATA_CSUM | File system metadata has checksums |
| 0x00000800 | EXT4_FEATURE_RO_COMPAT_REPLICA | Supports replicas |
| 0x00001000 | EXT4_FEATURE_RO_COMPAT_READONLY | Read-only file system image |
| 0x00002000 | EXT4_FEATURE_RO_COMPAT_PROJECT | File system tracks project quotas |
| 0x00004000 | EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS | File system has (read-only) shared blocks |
| 0x00008000 | EXT4_FEATURE_RO_COMPAT_VERITY | Unknown (Verity inodes may be present on the filesystem) |
| 0x00010000 | EXT4_FEATURE_RO_COMPAT_ORPHAN_PRESENT | Orphan file may be non-empty |
Note that EXT2_FEATURE_RO_COMPAT_, EXT3_FEATURE_RO_COMPAT_, EXT4_FEATURE_RO_COMPAT_ and RO_COMPAT_ are used interchangeably.
In some ext file systems, such as those used by ChromeOS, it has been observed that the upper 8-bits of the read-only compatible feature flags are set as in 0xff000003. debugfs identifies these as FEATURE_R24 - FEATURE_R31.
Checksum types
| Value | Identifier | Description |
|---|---|---|
| 1 | EXT4_CRC32C_CHKSUM | CRC-32C (or CRC32-C), which uses the Castagnoli polynomial (0x1edc6f41) |
The group descriptor table
The group descriptor table is stored in the block following the superblock.
The group descriptor table consist of:
- one or more group descriptors
The ext2 and ext3 group descriptor
The ext2 and ext3 group descriptor is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Block bitmap block number. The block number is relative from the start of the volume | |
| 4 | 4 | Inode bitmap block number. The block number is relative from the start of the volume | |
| 8 | 4 | Inode table block number. The block number is relative from the start of the volume | |
| 12 | 2 | Number of unallocated blocks | |
| 14 | 2 | Number of unallocated inodes | |
| 16 | 2 | Number of directories | |
| 18 | 2 | Unknown (padding) | |
| 20 | 3 x 4 | Unknown (reserved) |
Note that it has been observed that implementations that support ext4 can set a value in the padding. It is currently assumed that this value contains block group flags.
The ext4 group descriptor
The ext4 group descriptor is 68 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Block bitmap block number, which contains the lower 32-bit of the value. The block number is relative from the start of the volume | |
| 4 | 4 | Inode bitmap block number, which contains the lower 32-bit of the value. The block number is relative from the start of the volume | |
| 8 | 4 | Inode table block number, which contains the lower 32-bit of the value. The block number is relative from the start of the volume | |
| 12 | 2 | Number of unallocated blocks, which contains the lower 16-bit of the value | |
| 14 | 2 | Number of unallocated inodes, which contains the lower 16-bit of the value | |
| 16 | 2 | Number of directories, which contains the lower 16-bit of the value | |
| 18 | 2 | Block group flags | |
| 20 | 4 | Exclude bitmap block number, which contains the lower 32-bit of the value. The block number is relative from the start of the volume | |
| 24 | 2 | Block bitmap checksum, which contains the lower 16-bit of the value | |
| 26 | 2 | Inode bitmap checksum, which contains the lower 16-bit of the value | |
| 28 | 2 | Number of unused inodes, which contains the lower 16-bit of the value | |
| 30 | 2 | Checksum | |
| If 64-bit support (EXT4_FEATURE_INCOMPAT_64BIT) is enabled and group descriptor size > 32 | |||
| 32 | 4 | Block bitmap block number, which contains the upper 32-bit of the value. The block number is relative from the start of the volume | |
| 36 | 4 | Inode bitmap block number, which contains the upper 32-bit of the value. The block number is relative from the start of the volume | |
| 40 | 4 | Inode table block number, which contains the upper 32-bit of the value. The block number is relative from the start of the volume | |
| 44 | 2 | Number of unallocated blocks, which contains the upper 16-bit of the value | |
| 46 | 2 | Number of unallocated inodes, which contains the upper 16-bit of the value | |
| 48 | 2 | Number of directories, which contains the upper 16-bit of the value | |
| 50 | 2 | Number of unused inodes, which contains the upper 16-bit of the value | |
| 52 | 4 | Exclude bitmap block number, which contains the upper 32-bit of the value. The block number is relative from the start of the volume | |
| 56 | 2 | Block bitmap checksum, which contains the upper 16-bit of the value | |
| 60 | 2 | Inode bitmap checksum, which contains the upper 16-bit of the value | |
| 64 | 4 | Unknown (padding) | |
If checksum type is CRC-32C, the checksum is stored as the lower 16-bits of 0xffffffff - CRC-32C, otherwise the checksum is stored as a CRC-16.
Checksum calculation
If checksum type is CRC-32C, the CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated over:
- the 16 byte file system identifier in the superblock
- the group number as a 32-bit little-endian integer
- the data of the group descriptor with the checksum set to 0-byte values
TODO: describe the block bitmap checksum calculation: crc32c(s_uuid+grp_num+bbitmap)
TODO: describe the inode bitmap checksum calculation: crc32c(s_uuid+grp_num+ibitmap)
Block group flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | EXT4_BG_INODE_UNINIT | The inode table and bitmap are not initialized |
| 0x0002 | EXT4_BG_BLOCK_UNINIT | The block bitmap is not initialized |
| 0x0004 | EXT4_BG_INODE_ZEROED | The inode table is filled with 0 |
Direct and indirect blocks
Direct blocks are blocks that part of the data stream of a file entry.
A direct block number is 0 that is part of the data stream represents a sparse data block.
Indirect blocks are blocks that refer to blocks containing direct or indirect block numbers. There are multiple levels of indirect block:
- indirect blocks (level 1), that refer to direct blocks
- double indirect blocks (level 2), that refer to indirect blocks
- triple indirect blocks (level 3), that refer to double indirect blocks
An indirect block number is 0 that is part of the data stream represents sparse data blocks.
Extents
Extents were introduced in ext4 and are controlled by EXT4_FEATURE_INCOMPAT_EXTENTS.
Extents form an extent B-tree, where:
- extent indexes are stored in the branch nodes and
- extent descriptors are stored in the leaf nodes.
An extents B-tree node consists of:
- extents header
- extents entries
- extents footer
Note that inodes can have an implicit last sparse extent if the the inode data size is greater than the total data size defined by the extent descriptors.
The ext4 extents header
The ext4 extents header (ext4_extent_header) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "\x0a\xf3" | Signature |
| 2 | 2 | Number of entries | |
| 4 | 2 | Maximum number of entries | |
| 6 | 2 | Depth, where 0 reprensents a leaf node and 1 to 5 different levels of branch nodes | |
| 8 | 4 | Generation, which is used by Lustre, but not by standard ext4 |
The ext4 extent descriptor
The ext4 extent descriptor (ext4_extent) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Logical block number | |
| 4 | 2 | Number of blocks | |
| 6 | 2 | Upper 16-bits of physical block number | |
| 8 | 4 | Lower 32-bits of physical block number |
If number of blocks > 32768 the extent is considered “uninitialized” which is (as far as currently known) comparable to extent being sparse. The number of blocks of the sparse extent can be determined as following:
sparse_number_of_blocks = number_of_blocks - 32768
Sparse extents can exist between the extent descriptors. In such a case the logical block number will not align with the information from the previous extent descriptors.
Note that the native Linux ext implementation expects the extents to be stored in order of logical block number.
The ext4 extents index
The ext4 extent index (ext4_extent_idx) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Logical block number, which contains the first logical block number of next depth extents block | |
| 4 | 4 | Lower 32-bits of physical block number, which contains the block number of the next depth extents block | |
| 8 | 2 | Upper 16-bits of physical block number, which contains the block number of the next depth extents block | |
| 10 | 2 | Unknown (unused) |
The ext4 extents footer
The ext4 extents footer (ext4_extent_tail) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Checksum of an extents block, which contains a CRC32 |
The inode
The size of the inode is defined in the superblock when dynamic inode information is present.
Note that the ext4 inode format can be used on ext2 formatted file system. This was observed in combination with format revision 1 and inode size > 128 created by mkfs.ext2.
The ext2 inode
The ext2 inode is 128 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | File mode, which contains file type and permissions | |
| 2 | 2 | Lower 16-bits of owner (or user) identifier (UID) | |
| 4 | 4 | Data size | |
| 8 | 4 | (last) access time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 12 | 4 | (last) inode change (or modification) time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 16 | 4 | (last) content modification time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 20 | 4 | Deletion time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 24 | 2 | Lower 16-bits of group identifier (GID) | |
| 26 | 2 | Number of (hard) links | |
| 28 | 4 | Numer of blocks | |
| 32 | 4 | Flags | |
| 36 | 4 | Unknown (reserved) | |
| 40 | 12 x 4 | Array of direct block numbers. A block number is relative from the start of the volume | |
| 88 | 4 | Indirect block number. A block number is relative from the start of the volume | |
| 92 | 4 | Double indirect block number. A block number is relative from the start of the volume | |
| 96 | 4 | Triple indirect block number. A block number is relative from the start of the volume | |
| 100 | 4 | NFS generation number | |
| 104 | 4 | File ACL (or extended attributes) block number | |
| 108 | 4 | Unknown (Directory ACL) | |
| 112 | 4 | Fragment block address | |
| 116 | 1 | Fragment block index | |
| 117 | 1 | Fragment size | |
| 118 | 2 | Unknown (padding) | |
| 120 | 2 | Upper 16-bits of owner (or user) identifier (UID) | |
| 122 | 2 | Upper 16-bits of group identifier (GID) | |
| 124 | 4 | Unknown (reserved) |
Note that for a character and block device the first 2 bytes of the array of direct block numbers contain the minor and major device number respectively.
If the inode size is larger than 128 bytes, the additional data can be stored using an ext4 inode extension.
The ext3 inode
The ext3 inode is 132 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | File mode, which contains file type and permissions | |
| 2 | 2 | Lower 16-bits of owner (or user) identifier (UID) | |
| 4 | 4 | Data size | |
| 8 | 4 | (last) access time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 12 | 4 | (last) inode change (or modification) time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 16 | 4 | (last) content modification time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 20 | 4 | Deletion time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 24 | 2 | Lower 16-bits of group identifier (GID) | |
| 26 | 2 | Number of (hard) links | |
| 28 | 4 | Numer of blocks | |
| 32 | 4 | Flags | |
| 36 | 4 | Unknown (reserved) | |
| 40 | 12 x 4 | Array of direct block numbers. A block number is relative from the start of the volume | |
| 88 | 4 | Indirect block number. A block number is relative from the start of the volume | |
| 92 | 4 | Double indirect block number. A block number is relative from the start of the volume | |
| 96 | 4 | Triple indirect block number. A block number is relative from the start of the volume | |
| 100 | 4 | NFS generation number | |
| 104 | 4 | File ACL (or extended attributes) block number | |
| 108 | 4 | Unknown (Directory ACL) | |
| 112 | 4 | Fragment block address | |
| 116 | 1 | Fragment block index | |
| 117 | 1 | Fragment size | |
| 118 | 2 | Unknown (padding) | |
| 120 | 2 | Upper 16-bits of owner (or user) identifier (UID) | |
| 122 | 2 | Upper 16-bits of group identifier (GID) | |
| 124 | 4 | Unknown (reserved) | |
| Extension (if inode size > 128) | |||
| 128 | 2 | Extended inode size | |
| 130 | 2 | Unknown (padding) | |
Note that for a character and block device the first 2 bytes of the array of direct block numbers contain the minor and major device number respectively.
If the inode size is larger than 128 bytes, the additional data can be stored using an ext4 inode extension.
The ext4 inode
The ext4 inode is 160 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | File mode, which contains file type and permissions | |
| 2 | 2 | Lower 16-bits of owner (or user) identifier (UID) | |
| 4 | 4 | Lower 32-bits of data size | |
| If EXT4_EA_INODE_FL is not set | |||
| 8 | 4 | (last) access time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 12 | 4 | (last) inode change (or modification) time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 16 | 4 | (last) content modification time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| If EXT4_EA_INODE_FL is set | |||
| 8 | 4 | Unknown (extended attribute value data checksum) | |
| 12 | 4 | Unknown (lower 32-bits of extended attribute reference count) | |
| 16 | 4 | Unknown (inode number that owns the extended attribute) | |
| Common | |||
| 20 | 4 | Deletion time, which contains the number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) | |
| 24 | 2 | Lower 16-bits of group identifier (GID) | |
| 26 | 2 | Number of (hard) links | |
| 28 | 4 | Lower 32-bits of number of blocks | |
| 32 | 4 | Flags | |
| If EXT4_EA_INODE_FL is not set | |||
| 36 | 4 | Lower 32-bits of version | |
| If EXT4_EA_INODE_FL is set | |||
| 36 | 4 | Unknown (upper 32-bits of extended attribute reference count) | |
| If EXT4_EXTENTS_FL and EXT4_INLINE_DATA_FL are not set | |||
| 40 | 12 x 4 | Array of direct block numbers. A block number is relative from the start of the volume | |
| 88 | 4 | Indirect block number. A block number is relative from the start of the volume | |
| 92 | 4 | Double indirect block number. A block number is relative from the start of the volume | |
| 96 | 4 | Triple indirect block number. A block number is relative from the start of the volume | |
| If EXT4_EXTENTS_FL is set | |||
| 40 | 12 | Extents header | |
| 52 | 4 x 12 | extent descriptors or extents indexes | |
| If EXT4_INLINE_DATA_FL is set | |||
| 40 | 60 | File content data | |
| Common | |||
| 100 | 4 | NFS generation number | |
| 104 | 4 | Lower 32-bits of file ACL (or extended attributes) block number | |
| 108 | 4 | Upper 32-bits of data size | |
| 112 | 4 | Fragment block address | |
| 116 | 2 | Upper 16-bits of number of blocks | |
| 118 | 2 | Upper 16-bits of file ACL (or extended attributes) block number | |
| 120 | 2 | Upper 16-bits of owner (or user) identifier (UID) | |
| 122 | 2 | Upper 16-bits of group identifier (GID) | |
| 124 | 2 | Lower 16-bits of checksum | |
| 126 | 2 | Unknown (reserved) | |
| Extension (if inode size > 128) | |||
| 128 | 2 | Extended inode size, which can vary, values of 4, 28 and 32 have been observed | |
| 130 | 2 | Upper 16-bits of checksum | |
| 132 | 4 | (last) inode change (or modification) time extra precision | |
| 136 | 4 | (last) content modification time extra precision | |
| 140 | 4 | (last) access time extra precision | |
| 144 | 4 | Creation time | |
| 148 | 4 | Creation time extra precision | |
| 152 | 4 | Upper 32-bits of version | |
| 156 | 4 | Unknown (i_projid) | |
If checksum type is CRC-32C, the checksum is stored as 0xffffffff - CRC-32C.
Note that for a character and block device the first 2 bytes of the array of direct block numbers contain the minor and major device number respectively.
Checksum calculation
If checksum type is CRC-32C, the CRC32-C algorithm with the Castagnoli polynomial (0x1edc6f41) and initial value of 0 is used to calculate the checksum.
The checksum is calculated from:
- the 16 byte file system identifier in the superblock
- the inode number as a 32-bit little-endian integer
- the NFS generation number in the inode as a 32-bit little-endian integer
- the data of the inode with the lower and upper part of the checksum set to 0-byte values.
Extra precision
The ext4 extra precision is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 2 bits | Extra epoch value | |
| 0.2 | 30 bits | Fraction of second in nanoseconds |
The 34 bits extra precision timestamp (in number of seconds) can be calculated as following:
extra_precision_timestamp = (extra_epoch_value * 0x100000000) + timestamp
Notes
It has been observed that when EXT4_EA_INODE_FL is set the (last) modification time can contain a valid timestamp.
According to The Linux Kernel documentation
For backward compatibility with older versions of this feature, the i_mtime/i_generation may store a back-reference to the inode number and i_generation of the one owning inode (in cases where the EA inode is not referenced by multiple inodes) to verify that the EA inode is the correct one being accessed.
File mode
| Value | Identifier | Description |
|---|---|---|
| Access other, Bitmask: 0x0007 (S_IRWXO) | ||
| 0x0001 | S_IXOTH | X-access for other |
| 0x0002 | S_IWOTH | W-access for other |
| 0x0004 | S_IROTH | R-access for other |
| Access group, Bitmask: 0x0038 (S_IRWXG) | ||
| 0x0008 | S_IXGRP | X-access for group |
| 0x0010 | S_IWGRP | W-access for group |
| 0x0020 | S_IRGRP | R-access for group |
| Access owner (or user), Bitmask: 0x01c0 (S_IRWXU) | ||
| 0x0040 | S_IXUSR | X-access for owner (or user) |
| 0x0080 | S_IWUSR | W-access for owner (or user) |
| 0x0100 | S_IRUSR | R-access for owner (or user) |
| Other | ||
| 0x0200 | S_ISTXT | Sticky bit |
| 0x0400 | S_ISGID | Set group identifer (GID) on execution |
| 0x0800 | S_ISUID | Set owner (or user) identifer (UID) on execution |
| Type of file, Bitmask: 0xf000 (S_IFMT) | ||
| 0x1000 | S_IFIFO | Named pipe (FIFO) |
| 0x2000 | S_IFCHR | Character device |
| 0x4000 | S_IFDIR | Directory |
| 0x6000 | S_IFBLK | Block device |
| 0x8000 | S_IFREG | Regular file |
| 0xa000 | S_IFLNK | Symbolic link |
| 0xc000 | S_IFSOCK | Socket |
Inode flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | EXT2_SECRM_FL, EXT3_SECRM_FL, EXT4_SECRM_FL, EXT4_INODE_SECRM | Secure deletion |
| 0x00000002 | EXT2_UNRM_FL, EXT3_UNRM_FL, EXT4_UNRM_FL, EXT4_INODE_UNRM | Undelete |
| 0x00000004 | EXT2_COMPR_FL, EXT3_COMPR_FL, EXT4_COMPR_FL, EXT4_INODE_COMPR | Compressed file, which is not yet implemented |
| 0x00000008 | EXT2_SYNC_FL, EXT3_SYNC_FL, EXT4_SYNC_FL, EXT4_INODE_SYNC | Synchronous updates |
| 0x00000010 | EXT2_IMMUTABLE_FL, EXT3_IMMUTABLE_FL, EXT4_IMMUTABLE_FL, EXT4_INODE_IMMUTABLE | Immutable file |
| 0x00000020 | EXT2_APPEND_FL, EXT3_APPEND_FL, EXT4_APPEND_FL, EXT4_INODE_APPEND | Writes to file may only append |
| 0x00000040 | EXT2_NODUMP_FL, EXT3_NODUMP_FL, EXT4_NODUMP_FL, EXT4_INODE_NODUMP | Do not remove (or dump) file |
| 0x00000080 | EXT2_NOATIME_FL, EXT3_NOATIME_FL, EXT4_NOATIME_FL, EXT4_INODE_NOATIME | Do not update access time (atime) |
| 0x00000100 | EXT2_DIRTY_FL, EXT3_DIRTY_FL, EXT4_DIRTY_FL, EXT4_INODE_DIRTY | Dirty compressed file, which is not yet implemented |
| 0x00000200 | EXT2_COMPRBLK_FL, EXT3_COMPRBLK_FL, EXT4_COMPRBLK_FL, EXT4_INODE_COMPRBLK | One or more compressed clusters, which is not yet implemented |
| 0x00000400 | EXT2_NOCOMP_FL, EXT3_NOCOMP_FL, EXT4_NOCOMPR_FL, EXT4_INODE_NOCOMPR | Do not compress, which is not yet implemented |
| ext2 and ext3 | ||
| 0x00000800 | EXT2_ECOMPR_FL, EXT3_ECOMPR_FL | Encrypted Compression error |
| ext4 | ||
| 0x00000800 | EXT4_ENCRYPT_FL, EXT4_INODE_ENCRYPT | Encrypted file |
| Common | ||
| 0x00001000 | EXT2_BTREE_FL, EXT2_INDEX_FL, EXT3_INDEX_FL, EXT4_INDEX_FL, EXT4_INODE_INDEX | Hash-indexed directory (previously referred to as B-tree format) |
| 0x00002000 | EXT2_IMAGIC_FL, EXT3_IMAGIC_FL, EXT4_IMAGIC_FL, EXT4_INODE_IMAGIC | AFS directory |
| 0x00004000 | EXT2_JOURNAL_DATA_FL, EXT3_JOURNAL_DATA_FL, EXT4_JOURNAL_DATA_FL, EXT4_INODE_JOURNAL_DATA | File data must be written using the journal |
| 0x00008000 | EXT2_NOTAIL_FL, EXT3_NOTAIL_FL, EXT4_NOTAIL_FL, EXT4_INODE_NOTAIL | File tail should not be merged, which is not used by ext4 |
| 0x00010000 | EXT2_DIRSYNC_FL, EXT3_DIRSYNC_FL, EXT4_DIRSYNC_FL, EXT4_INODE_DIRSYNC | Directory entries should be written synchronously (dirsync) |
| 0x00020000 | EXT2_TOPDIR_FL, EXT3_TOPDIR_FL, EXT4_TOPDIR_FL, EXT4_INODE_TOPDIR | Top of directory hierarchy |
| ext4 | ||
| 0x00040000 | EXT4_HUGE_FILE_FL, EXT4_INODE_HUGE_FILE | Is a huge file |
| 0x00080000 | EXT4_EXTENTS_FL, EXT4_INODE_EXTENTS | Inode uses extents |
| 0x00100000 | EXT4_INODE_VERITY | Verity protected inode |
| 0x00200000 | EXT4_EA_INODE_FL, EXT4_INODE_EA_INODE | Inode used for large extended attribute |
| 0x00400000 | EXT4_EOFBLOCKS_FL, EXT4_INODE_EOFBLOCKS | Blocks allocated beyond EOF |
| 0x01000000 | EXT4_SNAPFILE_FL | Inode is a snapshot |
| 0x02000000 | EXT4_INODE_DAX | Inode is direct-access (DAX) |
| 0x04000000 | EXT4_SNAPFILE_DELETED_FL | Snapshot is being deleted |
| 0x08000000 | EXT4_SNAPFILE_SHRUNK_FL | Snapshot shrink has completed |
| 0x10000000 | EXT4_INLINE_DATA_FL, EXT4_INODE_INLINE_DATA | Inode has inline data |
| 0x20000000 | EXT4_PROJINHERIT_FL, EXT4_INODE_PROJINHERIT | Create sub file entries with the same project identifier |
| 0x40000000 | EXT4_INODE_CASEFOLD | Casefolded directory |
| 0x80000000 | EXT4_INODE_RESERVED | Unknown (reserved) |
Reserved inode numbers
| Value | Identifier | Description |
|---|---|---|
| 1 | EXT2_BAD_INO, EXT3_BAD_INO, EXT4_BAD_INO | Bad blocks inode |
| 2 | EXT2_ROOT_INO, EXT3_ROOT_INO, EXT4_ROOT_INO | Root inode |
| 3 | EXT4_USR_QUOTA_INO | Owner (or user) quota inode |
| 4 | EXT4_GRP_QUOTA_INO | Group quota inode |
| 5 | EXT2_BOOT_LOADER_INO, EXT3_BOOT_LOADER_INO, EXT4_BOOT_LOADER_INO | Boot loader inode |
| 6 | EXT2_UNDEL_DIR_INO, EXT3_UNDEL_DIR_INO, EXT4_UNDEL_DIR_INO | Undelete directory inode |
| 7 | EXT3_RESIZE_INO, EXT4_RESIZE_INO | Reserved group descriptors inode |
| 8 | EXT3_JOURNAL_INO, EXT4_JOURNAL_INO | Journal inode |
Inline data
ext4 supports storing file entry data inline when the inode flag EXT4_INLINE_DATA_FL is set.
Note that inodes can have an implicit last sparse extent if the the inode data size is greater than 60 bytes.
Huge files
TODO: complete section
Directory entries
Directories entries are stored in the data blocks of a directory inode. The directory entries can be stored in multiple ways:
- as linear directory entries
- as inline data directory entries
- as hash-tree directory entries
Linear directory entries
Linear directories entries are stored in a series of allocation blocks.
Linear directory entries contain:
- directory entry for “.” (self)
- directory entry for “..” (parent)
- directory entry for other file system entries
The directory entry
The directory entry is of variable size, at most 263 bytes, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Inode number | |
| 4 | 2 | Directory entry size, which must be a multitude of 4 | |
| 6 | 1 | Name size, which contains the size of the name without the end-of-string character and has a maximum of 255 | |
| 7 | 1 | File type | |
| 8 | ... | Name, which contains a narrow character string without end-of-string character |
Older directory entry structures considered the name size a 16-bit value, but the upper byte was never used.
The name can contain any character value except the path segment separator (‘/’) and the NUL-character (‘\0’).
File types
| Value | Identifier | Description |
|---|---|---|
| 0 | EXT2_FT_UNKNOWN | Unknown |
| 1 | EXT2_FT_REG_FILE | Regular file |
| 2 | EXT2_FT_DIR | Directory |
| 3 | EXT2_FT_CHRDEV | Character device |
| 4 | EXT2_FT_BLKDEV | Block device |
| 5 | EXT2_FT_FIFO | FIFO queue |
| 6 | EXT2_FT_SOCK | Socket |
| 7 | EXT2_FT_SYMLINK | Symbolic link |
Inline data directory entries
ext4 supports storing the directory entries as inline data when the inode flag EXT4_INLINE_DATA_FL is set.
The inline data directory entries is of variable size, at most 60 bytes, and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Parent inode number | |
| 4 | ... | Array of directory entries |
Hash tree directory entries
The data of the hash tree (HTree) is stored in the data blocs or extent defined by the directory inode. The hash-indexed directory entries are read-compatible with the linear directory entry.
Hash tree root
The hash tree root consists of:
- dx_root
- directory entry for “.” (self)
- directory entry for “..” (parent)
- dx_root_info
- Array of dx_entry
- directory entry for other file system entries
dx_root_info
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0 | Unknown (reserved) |
| 4 | 1 | Hash method (or version) | |
| 5 | 1 | 8 | Root information size |
| 6 | 1 | Number of indirect levels in the hash tree | |
| 7 | 1 | Unknown (unused flags) |
dx_entry
TODO: complete section
struct dx_entry
{
__le32 hash;
__le32 block;
};
Symbolic links
If the target path of a symbolic link is less than 60 characters long, it is stored in the 60 bytes in the inode that are normally used for the 12 direct and 3 indirect block numbers. If the target path is longer than 60 characters, a block is allocated, and the block contains the target path. The inode data size contains the length of the target path.
Extended attributes
Extended attributes can be stored:
- in the inode block after the inode data
- in the block referenced by the file ACL (or extended attributes) block number, if not 0
Note that both should be read to get the all the extended attributes.
Extended attributes consists of:
- An extended attributes header
- Extended attributes entries with a terminator
The extended attributes inode header
The extended attributes inode header (ext2_xattr_ibody_header, ext3_xattr_ibody_header, ext4_xattr_ibody_header) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\x00\x00\x02\xea" | Signature |
The extended attributes block header
The ext2 and ext3 extended attributes block header
The ext2 and ext3 extended attributes block header (ext2_xattr_header, ext3_xattr_header) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\x00\x00\x02\xea" | Signature |
| 4 | 4 | Unknown (reference count) | |
| 8 | 4 | Number of blocks | |
| 12 | 4 | Attributes hash | |
| 16 | 4 x 4 | Unknown (reserved) |
The ext4 extended attributes block header
The ext4 extended attributes block header (ext4_xattr_header) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\x00\x00\x02\xea" | Signature |
| 4 | 4 | Unknown (Reference count) | |
| 8 | 4 | Number of blocks | |
| 12 | 4 | Attributes hash | |
| 16 | 4 | Checksum | |
| 20 | 3 x 4 | Unknown (reserved) |
The extended attributes entry
The extended attributes entry (ext2_xattr_entry, ext3_xattr_entry, ext4_xattr_entry) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Name size, which contains the size of the name without the end-of-string character | |
| 1 | 1 | Name index | |
| 2 | 2 | Value data offset, which contains the offset of the value data relative from the start of the extended attributes block or after the extended attributes signature in the inode block data | |
| 4 | 4 | Value data inode number, which contains the inode number that contains the value data or 0 to indicate the current block | |
| 8 | 4 | Value data size | |
| 12 | 4 | Unknown (Attribute hash) | |
| 16 | ... | Name string, which contains an ASCII string without end-of-string character and can be empty, for example in combination with a prefix or with an encrypted file | |
| ... | ... | 32-bit alignment padding |
The last extended attributes entry has the first 4 values set to 0 (8 bytes) and is used as a terminator.
Note that some implementations of older Android versions of ext appear to only set the first 4 bytes to 0 for the terminator.
The extended attribute name index
The name index indicates the prefix of the extended attribute name.
| Name index | Name prefix | Description |
|---|---|---|
| 0 | "" | No prefix |
| 1 | "user." | |
| 2 | "system.posix_acl_access" | |
| 3 | "system.posix_acl_default" | |
| 4 | "trusted." | |
| 6 | "security." | |
| 7 | "system." | |
| 8 | "system.richacl" |
Journal
The journal was introduced in ext3.
TODO: complete section
Exclude bitmap
TODO: complete section
Note that the excluded bitmap is used for snapshots.
Format edge cases and corruption scenarios
File entry with invalid extents header signature
File content inaccessible but file entry metadata and extended attributes accessible.
References
- ext4 Data Structures and Algorithms, by the Linux kernel documentation
Extensible File Allocation Table (exFAT) file system format
The Extensible File Allocation Table (exFAT) file system format is a successor of the File Allocation Table (FAT) file system format.
Overview
An exFAT file system consists of:
- Main boot region (11 sectors)
- boot sector (or boot record)
- 7 extended boot sectors; can contain a sector signature (“\x55\xaa”)
- OEM parameters sector; can contain a sector signature (“\x55\xaa”)
- Reserved sector; can contain a sector signature (“\x55\xaa”)
- boot checksum sector
- Backup boot region (11 sectors)
- boot sector (or boot record)
- 7 extended boot sectors; can contain a sector signature (“\x55\xaa”)
- OEM parameters sector; can contain a sector signature (“\x55\xaa”)
- Reserved sector; can contain a sector signature (“\x55\xaa”)
- boot checksum sector
- File Allocation Table region
- Aligment padding
- First cluster block allocation tables
- Zero or more backup block allocation tables
- Data region
- Aligment padding
- Cluster heap
- File and directory data
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | FAT date and time, in local time with UTC offset |
| Character strings | UCS-2 little-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
Boot record
The boot record is stored in the first sector of the volume.
The boot record is at least 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x76\x90" | Boot entry point (JMP +120, NOP) |
| 3 | 8 | "EXFAT\x20\x20\x20" | File system signature (or OEM name) |
| 11 | 53 | 0 | Unknown (reserved), which must be 0 |
| 64 | 8 | Partition offset | |
| 72 | 8 | Total number of sectors | |
| 80 | 4 | Cluster block allocation table start sector | |
| 84 | 4 | Cluster block allocation table size, in number of sectors, which must be non 0 | |
| 88 | 4 | Cluster heap start sector | |
| 92 | 4 | Number of clusters | |
| 96 | 4 | Root directory start cluster | |
| 100 | 4 | Volume serial number | |
| 104 | 1 | Format revision minor number | |
| 105 | 1 | 1 | Format revision major number |
| 106 | 2 | Volume flags | |
| 108 | 1 | Bytes per sector, which is stored as 2^n, for example 9 is 2^9 = 512. The bytes per sector value must be 512, 1024, 2048 or 4096 | |
| 109 | 1 | Sectors per cluster block, which is stored as 2^n, for example 3 is 2^3 = 8. The sectors per cluster block must be 1 upto 32M (2^25) | |
| 110 | 1 | Number of cluster block allocation tables | |
| 111 | 1 | Drive number | |
| 112 | 1 | Unknown (percent in use), which contains the percentage of allocated cluster blocks in the cluster heap of 0xff if not available | |
| 113 | 7 | Unknown (reserved) | |
| 120 | 390 | Used for boot code | |
| 510 | 2 | "\x55\xaa" | Sector signature |
Volume flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | ActiveFat | Active FAT, where 0 represents the first FAT |
| 0x0002 | VolumeDirty | Is dirty |
| 0x0004 | MediaFailure | Has media failures |
| 0x0008 | ClearToZero | Must be cleared |
| 0xfff0 | Unknown (reserved) |
Boot checksum sector
The boot checksum sector is at least 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Boot checksum | |
| 4 | 4 | Copy of boot checksum | |
| 8 | 4 | Copy of boot checksum | |
| 12 | 4 | Copy of boot checksum | |
| 16 | 496 | Unknown (empty values) |
checksum = 0
for index in range(0, 11 * bytes_per_sector):
# Ignore the volume flags and percent in use values.
if index in (106, 107, 112):
continue
carry = 0x80000000 if (checksum & 1) else 0
checksum = carry + (checksum >> 1) + sectors[index]
checksum &= 0xFFFFFFFF
Cluster block allocation table
A cluster block allocation table consists of:
- One ore more cluster block allocation table entries
Cluster block allocation table entry
A cluster block allocation table entry is 32 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 32 bits | Data cluster number |
Where the data cluster number has the following meanings:
| Value(s) | Description |
|---|---|
| 0x00000000 | Unused (free) cluster |
| 0x00000001 | Unknown (invalid) |
| 0x00000002 - 0xffffffef | Used cluster |
| 0xfffffff0 - 0xfffffff6 | Reserved |
| 0xfffffff7 | Bad cluster |
| 0xfffffff8 - 0xffffffff | End of cluster chain |
Cluster heap
A cluster heap consists of:
- One ore more sector allocation table entries
Sector allocation table entry
A sector allocation table entry is 32 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 32 bits | Data sector number |
Directory
A directory consists of:
- Zero or more directory entries
- Terminator directory entry
Directory entry
A directory entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Entry type | |
| 1 | 1 | Entry flags | |
| 1 | 19 | Entry data | |
| 20 | 4 | Data start cluster | |
| 24 | 8 | Data size |
Directory entry type
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 5 bits | Type type code | |
| 0.5 | 1 bit | Is non-critical (also referred to as type importance) | |
| 0.6 | 1 bit | Is secondary entry (also referred to as type category) | |
| 0.7 | 1 bit | In use |
| Value | Description |
|---|---|
| 0x00 | Terminator directory entry |
| 0x01 - 0x7f | Unused |
| 0x80 | Invalid |
| 0x81 - 0xff | Used |
Directory entry type codes
| Value | Description | ||
|---|---|---|---|
| Critical and primary | |||
| 0x81 | Allocation bitmap | ||
| 0x82 | Case folding mappings | ||
| 0x83 | Volume label | ||
| 0x85 | File entry | ||
| Non-critical and primary | |||
| 0xa0 | Volume identifier | ||
| 0xa1 | TexFAT padding | ||
| Critical and secondary | |||
| 0xc0 | Data stream | ||
| 0xc1 | File (entry) name | ||
| Non-critical and secondary | |||
| 0xe0 | Vendor extension | ||
| 0xe1 | Vendor allocation | ||
Directory entry flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Unknown (AllocationPossible) | |
| 0.1 | 1 bit | Continuous allocation (NoFatChain), if set do not use the cluster block allocation table | |
| 0.2 | 6 bits | Unknown |
offset = ( ( cluster_block_number - 2 ) * cluster_block_size ) + cluster_chain_offset
Allocation bitmap record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0x81 | Entry type |
| 1 | 1 | Bitmap flags | |
| 2 | 18 | 0 | Unknown (Reserved) |
| 20 | 4 | Data start cluster | |
| 24 | 8 | Data size |
Allocation bitmap flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Unknown (BitmapIdentifier) | |
| 0.1 | 7 bits | Unknown (reserved) |
Case folding mappings record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0x82 | Entry type |
| 1 | 3 | 0 | Unknown (Reserved) |
| 4 | 4 | Checksum of the case folding mappings data | |
| 8 | 12 | 0 | Unknown (Reserved) |
| 20 | 4 | Data start cluster | |
| 24 | 8 | Data size |
Volume label record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0x83 | Entry type |
| 1 | 1 | Name size, in number of characters | |
| 2 | 22 | Name string, which contains an UCS-2 little-endian string without an end-of-string character | |
| 24 | 8 | 0 | Unknown (Reserved) |
Note that the volume label record should only be stored in the first and/or second directory entry of the root directory.
File entry record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0x85 | Entry type |
| 1 | 1 | Entry flags | |
| 2 | 2 | Entry set checksum | |
| 4 | 2 | File attribute flags | |
| 6 | 2 | 0 | Unknown (Reserved) |
| 8 | 2 | Creation time | |
| 10 | 2 | Creation date | |
| 12 | 2 | Last modification time | |
| 14 | 2 | Last modification date | |
| 16 | 2 | Last access time | |
| 18 | 2 | Last access date | |
| 20 | 1 | Creation time fraction of seconds, which contains fraction of 2-seconds in 10 ms intervals | |
| 21 | 1 | Last modification time fraction of seconds, which contains fraction of 2-seconds in 10 ms intervals | |
| 22 | 1 | Creation time UTC offset, which contains number of 15 minute intervals of the time relative to UTC, where an MSB of 1 indicates the offset is valid (and 0 invalid) | |
| 23 | 1 | Last modification time UTC offset, which contains number of 15 minute intervals of the time relative to UTC, where an MSB of 1 indicates the offset is valid (and 0 invalid) | |
| 24 | 1 | Last access time UTC offset, which contains number of 15 minute intervals of the time relative to UTC, where an MSB of 1 indicates the offset is valid (and 0 invalid) | |
| 25 | 7 | 0 | Unknown (Reserved) |
Volume identifier record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0xa0 | Entry type |
| 1 | 1 | Entry flags | |
| 2 | 2 | Entry set checksum | |
| 4 | 2 | Unknown (Flags) | |
| 6 | 16 | Volume identifier, which contains a GUID | |
| 22 | 10 | 0 | Unknown (Reserved) |
Data stream record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0xc0 | Entry type |
| 1 | 1 | Entry flags | |
| 2 | 1 | 0 | Unknown (Reserved) |
| 3 | 1 | Name size, in number of characters | |
| 4 | 2 | Name hash | |
| 6 | 2 | 0 | Unknown (Reserved) |
| 8 | 8 | Valid data size | |
| 16 | 4 | 0 | Unknown (Reserved) |
| 20 | 4 | Data start cluster | |
| 24 | 8 | Data size |
File name record
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 0xc1 | Entry type |
| 1 | 1 | Entry flags | |
| 2 | 30 | Name string, which contains an UCS-2 little-endian string without an end-of-string character |
File attribute flags
| Value | Description |
|---|---|
| 0x0001 | Read-only |
| 0x0002 | Hidden |
| 0x0004 | System |
| 0x0008 | Is volume label |
| 0x0010 | Is directory |
| 0x0020 | Archive |
| 0x0040 | Is device |
| 0x0080 | Unused (reserved) |
Checksum
The checksum calculation uses Rotate Right by 1 (ROR1).
checksum = 0
for byte_value in data:
msb = 0x80000000 if (checksum & 1) else 0
checksum = (msb + (checksum >> 1) + byte_value) & 0xffffffff
References
- exFAT file system specification, by Microsoft
- exFAT, by Wikipedia
File Allocation Table (FAT) file system format
The File Allocation Table (FAT) is widely used a file sytem and is the default file system for DOS and Windows.
There are multiple known variants or derivatives of FAT, such as:
- (original) 8-bit FAT
- FAT-12
- FAT-16
- FAT-32
- exFAT
Overview
A FAT file system consists of:
- One or more reserved sectors
- a boot record (or boot sector)
- file system informartion for FAT-32
- One or more cluster block allocation tables
- Root directory data for FAT-12 and FAT-16
- File and directory data
Note that FAT-32 stores the root directory as part of the file and directory data.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | FAT date and time, in local time |
| Character strings | A narrow character Single Byte Character (SBC) ASCII string |
Terminology
| Term | Description |
|---|---|
| Hidden sectors | The sectors stored before the FAT volume, such as those used to store a partition table |
Determing the FAT format version
To distinguish between FAT-12, FAT-16 and FAT-32, compute the number of clusters in the data area:
data_area_size = total_number_of_sectors - (number_of_reserved_sectors + (
number_of_allocation_tables * allocation_table_size) + size_of_root_directory)
number_of_clusters = round down (data_area_size / sectors_per_cluster)
- FAT-12 is used if the number of clusters is less than 4085
- FAT-16 is used if the number of clusters is less than 65525
- FAT-32 is used otherwise
Boot record
The boot record is stored in the first sector of the volume.
FAT-12 and FAT-16 boot record
The FAT-12 and FAT-16 boot record is at least 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x3c\x90" | Boot entry point (JMP +62, NOP) |
| 3 | 8 | File system signature (or OEM name) | |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector, which must be 512, 1024, 2048 or 4096 | |
| 13 | 1 | Sectors per cluster block, which must be 1, 2, 4, 8, 16, 32, 64 or 128 | |
| 14 | 2 | Number of reserved sectors (reserved region), which starts at the first sector of the volume (sector 0) and must be 1 or more (typically 1 or 32) | |
| 16 | 1 | Number of cluster block allocation tables, which must be 1 or more (typically 2) | |
| 17 | 2 | Number of root directory entries | |
| 19 | 2 | Total number of sectors (16-bit) | |
| 21 | 1 | Media descriptor | |
| 22 | 2 | Cluster block allocation table size (16-bit), in number of sectors | |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | Number of sectors per track | |
| 26 | 2 | Number of heads | |
| 28 | 4 | Number of hidden sectors | |
| 32 | 4 | Total number of sectors (32-bit) | |
| 36 | 1 | Drive number | |
| 37 | 1 | 0 | Unknown (reserved for Windows NT) |
| 38 | 1 | Extended boot signature | |
| If extended boot signature == 0x29 | |||
| 39 | 4 | Volume serial number, which can be derived from the system current date and time | |
| 43 | 11 | Volume label, which contains a narrow character string or "NO\x20NAME\x20\x20\x20\x20" if not set | |
| 54 | 8 | "FAT12\x20\x20\x20" or "FAT16\x20\x20\x20" | File system hint, which is informational and not required |
| If extended boot signature != 0x29 | |||
| 39 | 23 | Unknown | |
| Common | |||
| 62 | 448 | Used for boot code | |
| 510 | 2 | "\x55\xaa" | Sector signature |
Note that the sector signature must be set at offset 512 but in addition can be set in the last 2 bytes of the sector.
FAT-32 boot record
The FAT-32 boot record is at least 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | "\xeb\x58\x90" | Boot entry point (JMP +90, NOP) |
| 3 | 8 | File system signature (or OEM name) | |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector, which must be 512, 1024, 2048 or 4096 | |
| 13 | 1 | Sectors per cluster block, which must be 1, 2, 4, 8, 16, 32, 64 or 128 | |
| 14 | 2 | Number of reserved sectors (reserved region), which starts at the first sector of the volume (sector 0) and must be 1 or more (typically 1 or 32) | |
| 16 | 1 | Number of cluster block allocation tables, which must be 1 or more (typically 2) | |
| 17 | 2 | 0 | Number of root directory entries, which must be 0 for FAT-32 |
| 19 | 2 | 0 | Total number of sectors (16-bit), which must be 0 for FAT-32 |
| 21 | 1 | Media descriptor | |
| 22 | 2 | 0 | Cluster block allocation table size (16-bit), in number of sectors, which must be 0 for FAT-32 |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | Number of sectors per track | |
| 26 | 2 | Number of heads | |
| 28 | 4 | Number of hidden sectors | |
| 32 | 4 | Total number of sectors (32-bit) | |
| 36 | 4 | Cluster block allocation table size (32-bit), in number of sectors, which must be non 0 for FAT-32 | |
| 40 | 2 | Extended flags | |
| 42 | 1 | 0 | Format revision minor number |
| 43 | 1 | 0 | Format revision major number |
| 44 | 4 | Root directory start cluster | |
| 48 | 2 | File system information (FSINFO) sector number | |
| 50 | 2 | Boot record sector number | |
| 52 | 12 | 0 | Unknown (reserved) |
| 64 | 1 | Drive number | |
| 65 | 1 | 0 | Unknown (reserved for Windows NT) |
| 66 | 1 | Extended boot signature | |
| If extended boot signature == 0x29 | |||
| 67 | 4 | Volume serial number, which can be derived from the system current date and time | |
| 71 | 11 | Volume label, which contains a narrow character string or "NO\x20NAME\x20\x20\x20\x20" if not set | |
| 82 | 8 | "FAT32\x20\x20\x20" | File system hint, which is informational and not required |
| If extended boot signature != 0x29 | |||
| 67 | 23 | Unknown | |
| Common | |||
| 90 | 420 | Used for boot code | |
| 510 | 2 | "\x55\xaa" | Sector signature |
Note that the sector signature must be set at offset 512 but in addition can be set in the last 2 bytes of the sector.
FAT-32 extended flags
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 4 bits | Active FAT, where 0 represents the first FAT | |
| 0.4 | 3 bits | Unknown (reserved) | |
| 0.7 | 1 bit | FAT mirroring disabled | |
| 1.0 | 8 bits | Unknown (reserved) |
OEM names
| Value | Description |
|---|---|
| "MSWIN4.1" | |
| "MSDOS 5.0" |
Media descriptors
| Value | Identifier | Description |
|---|---|---|
| 0xe5 | ||
| 0xed | ||
| 0xee | ||
| 0xef | ||
| 0xf0 | removable media | |
| 0xf4 | ||
| 0xf5 | ||
| 0xf8 | fixed (non-removable) media | |
| 0xf9 | ||
| 0xfa | ||
| 0xfb | ||
| 0xfc | ||
| 0xfd | ||
| 0xfe | ||
| 0xff |
Cluster block allocation table
A cluster block allocation table consists of:
- One ore more cluster block allocation table entries
FAT 12 cluster block allocation table entry
A FAT 12 cluster block allocation table entry is 12 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 12 bits | Data cluster number |
Where the data cluster number has the following meanings:
| Value(s) | Description |
|---|---|
| 0x000 | Unused (free) cluster |
| 0x001 | Unknown (invalid) |
| 0x002 - 0xfef | Used cluster |
| 0xff0 - 0xff6 | Reserved |
| 0xff7 | Bad cluster |
| 0xff8 - 0xfff | End of cluster chain |
FAT 16 cluster block allocation table entry
A FAT 16 cluster block allocation table entry is 16 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 bits | Data cluster number |
Where the data cluster number has the following meanings:
| Value(s) | Description |
|---|---|
| 0x0000 | Unused (free) cluster |
| 0x0001 | Unknown (invalid) |
| 0x0002 - 0xffef | Used cluster |
| 0xfff0 - 0xfff6 | Reserved |
| 0xfff7 | Bad cluster |
| 0xfff8 - 0xffff | End of cluster chain |
FAT 32 cluster block allocation table entry
A FAT 32 cluster block allocation table entry is 32 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 32 bits | Data cluster number |
Note that only the lower 28-bits are used
Where the data cluster number has the following meanings:
| Value(s) | Description |
|---|---|
| 0x00000000 | Unused (free) cluster |
| 0x00000001 | Unknown (invalid) |
| 0x00000002 - 0x0fffffef | Used cluster |
| 0x0ffffff0 - 0x0ffffff6 | Reserved |
| 0x0ffffff7 | Bad cluster |
| 0x0ffffff8 - 0x0fffffff | End of cluster chain |
| 0x10000000 - 0xffffffff | Unknown |
Directory
A directory consists of:
- self (“.”) directory entry (not used in root directory)
- parent (“..”) directory entry (not used in root directory)
- Zero or more directory entries
- Terminator directory entry
Directory entry
Determining the root directory location
first_allocation_table_offset = number_of_reserved_sectors * bytes_per_sector
FAT-12 and FAT-16 root directory
root_directory_start_offset = first_allocation_table_offset + (
number_of_allocation_tables * allocation_table_size * bytes_per_sector)
first_cluster_offset = directory_start_sector + (number_of_root_directory_entries * 32)
FAT-32 root directory
first_cluster_offset = first_allocation_table_sector + (
number_of_allocation_tables * allocation_table_size * bytes_per_sector)
root_directory_start_offset = first_cluster_sector + (
(root_directory_cluster - 2) * number_of_sectors_per_cluster)
FAT-12 and FAT-16 directory entry
A FAT-12 and FAT-16 directory entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Name, which is padded with spaces and the first character can have a special meaning | |
| 8 | 3 | Extension, which is padded with spaces | |
| 11 | 1 | File attribute flags | |
| 12 | 1 | Flags | |
| 13 | 1 | Creation time fraction of seconds, which contains fraction of 2-seconds in 10 ms intervals | |
| 14 | 2 | Creation time | |
| 16 | 2 | Creation date | |
| 18 | 2 | Last access date | |
| 20 | 2 | Unknown (OS/2 extended attribute) | |
| 22 | 2 | Last modification time | |
| 24 | 2 | Last modification date | |
| 26 | 2 | Data stream start cluster | |
| 28 | 4 | Data stream data size |
FAT-32 directory entry
A FAT-32 directory entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Name, which is padded with spaces and the first character can have a special meaning | |
| 8 | 3 | Extension, which is padded with spaces | |
| 11 | 1 | File attribute flags | |
| 12 | 1 | Flags | |
| 13 | 1 | Creation time fraction of seconds, which contains fraction of 2-seconds in 10 ms intervals | |
| 14 | 2 | Creation time | |
| 16 | 2 | Creation date | |
| 18 | 2 | Last access date | |
| 20 | 2 | Data stream data size, which contains the upper 16-bit of the value | |
| 22 | 2 | Last modification time | |
| 24 | 2 | Last modification date | |
| 26 | 2 | Data stream start cluster, which contains the lower 16-bit of the value | |
| 28 | 4 | Data stream data size |
Short (or 8.3) file name
A FAT short (or 8.3) file name is stored in an OEM character set (codepage). The first character can have a special meaning.
Valid FAT short file name characters are:
| Value | Description |
|---|---|
| 'A-Z' | Upper case character |
| '0-9' | Numeric character |
| ' ' | Space, where trailing spaces are considered padding and therefore ignored |
| '.' | Dot, with the exception of "." and "..", where trailing dot characters are ignored |
| '!' | Exclamation mark |
| '#' | Hash |
| '$' | Dollar sign |
| '%' | Percent sign |
| '&' | Ampersand |
| ''' | Single quote |
| '(' | Left parenthesis |
| ')' | Right parenthesis |
| '-' | Hyphen |
| '@' | At sign |
| '^' | Caret |
| '_' | Underscore |
| '`' | Grave accent |
| '{' | Left curly brace |
| '}' | Right curly brace |
| '~' | Tilde |
| 0x80 - 0xff | Extended ASCII character, which are codepage dependent |
Note that other characters such as plus sign (‘+’) have been observed in FAT short file names.
First character
| Value | Description |
|---|---|
| 0x00 | Last (or terminator) directory entry |
| 0x01 - 0x13 | VFAT long file name directory entry |
| 0x05 | Directory entry pending deallocation (deprecated since DOS 3.0) or substitution of a 0xe5 value |
| 0x41 - 0x54 | Last VFAT long file name directory entry |
| 0xe5 | Unallocated directory entry |
File attribute flags
| Value | Description |
|---|---|
| 0x01 | Read-only |
| 0x02 | Hidden |
| 0x04 | System |
| 0x08 | Is volume label |
| 0x10 | Is directory |
| 0x20 | Archive |
| 0x40 | Is device |
| 0x80 | Unused (reserved) |
Flags
| Value | Description |
|---|---|
| 0x01 | Data is EFS encrypted |
| 0x02 | Data contains large EFS header |
| 0x08 | Name should be represented in lower case |
| 0x10 | Extension should be represented in lower case |
VFAT long file name entry
VFAT long file names entries are stored in directory entries. Multiple VFAT long file name entries can be used to store a single long file name, where the highest (last) sequence number is stored first. A maximum of 20 VFAT long file name entries can be used to store a long file name of 255 UCS-2 characters.
VFAT long file names are stored using UCS-2 little-endian, which allows for unpaired Unicode surrogates such as “U+d800” and “U+dc00”
VFAT long file name entries are stored before the directory entry containing the short file name and additional file entry information.
A VFAT long file name entry is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Sequence number | |
| 1 | 10 | First name segment string, which contains 5 UCS-2 string characters | |
| 11 | 1 | 0x0f | Unknown (attributes) |
| 12 | 1 | 0x00 | Unknown (type) |
| 13 | 1 | Checksum of the short (8.3) file name | |
| 14 | 12 | Second name segment string, which contains 6 UCS-2 string characters | |
| 26 | 2 | 0 | Unknown (first cluster) |
| 28 | 4 | Third name segment string, which contains 2 UCS-2 string characters |
Note that unused characters in the VFAT long file segment strings after the end-of-string character (0x0000) are padded with 0xffff.
VFAT long file name sequence number
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 5 bits | Number | |
| 0.5 | 1 bit | 0 | Unknown (reserved) |
| 0.6 | 1 bit | 0 | Unknown (last logical, first physical LFN entry) |
| 0.7 | 1 bit | 0 | Unknown |
References
- Microsoft Extensible Firmware Initiative FAT32 File System Specification, by Microsoft
- Design of the FAT file system, by Wikipedia
- File Allocation Table, by Wikipedia
Hierarchical File System (HFS) format
The Hierarchical File System (HFS) was the default file system for Mac OS after Macintosh File System (MFS) and before Apple File System (APFS).
Note that this document uses Mac OS to refer to the Macintosh Operating System in general, instead of specific versions like Mac OS X or macOS. Mac OS X is used to refer to version of Mac OS 10.0 or later.
There are multiple known variants or derivatives of HFS, such as:
- HFS
- HFS+ 8.10, used by Mac OS 8.1 to 9.2.2
- HFS+ 10.0, introduced in Mac OS 10.0
- HFSX, introduced in Mac OS 10.3
Note that HFS can be referred to as “HFS Standard” and HFS+ or HFSX as “HFS Extended”.
HFSX (or HFS/X) is an extension to HFS+ to allow additional features that are incompatible with HFS+. One such feature is case-sensitive file names. A HFSX volume may be either case-sensitive or case-insensitive. Case sensitivity (or lack thereof) applies to all file and directory names on the volume.
Overview
| Feature | HFS | HFS+ and HFSX |
|---|---|---|
| Maximum file size | 231 (2 GiB) | 263 (8 EiB) |
| Maximum file name size | 31 characters | 255 characters |
| Maximum number of blocks | 216 (65535 bytes) | 232 (4294967296 bytes) |
| Character set | narrow character with codepage | Unicode UTF-16 big-endian |
| Time stamps | In local time | In UTC |
| Catalog B-tree file node size | 512 bytes | 4096 bytes |
| File attributes | none | Basic and extended |
HFS
A HFS file system consists of:
- optional MFS boot block
- master directory block (MDB)
- volume bitmap
- extents overflow file
- catalog file
- optional backup (or alternate) master directory block (MDB)
The backup master directory block (MDB), is stored in the last 2 sectors of the volume.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | HFS timestamp, in local time |
| Character strings | Narrow character (Single Byte Character (SBC) or Multi Byte Character (MBC)) stored using a system defined codepage |
HFS+ and HFSX
A HFS+ or HFSX file system consists of:
- reserved (or unused) blocks
- volume header
- allocation file
- extents overflow file
- catalog file
- optional attributes file
- optional startup file
- optional backup (or alternate) volume header
The backup volume header, is stored in the last 1024 bytes of the volume.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | HFS timestamp, in UTC |
| Character strings | UTF-16 big-endian |
Terminology
| Term | Description |
|---|---|
| Clump size | Size of the group of (allocation) blocks (or clump), in bytes, to avoid fragmentation |
Unicode strings
Unicode strings are stored as UTF-16 big-endian in Normalization Form Canonical Decomposition (NFD) based on Unicode 3.2, with exclusions. Unicode values in the ranges U+2000 - U+2FFF, U+F900 - U+FAFF and U+2F800 - U+2FAFF are not decomposed.
On Mac OS 8.1 through 10.2.x decomposition was based on Unicode 2.1.
TODO: determine what the impact of the different Unicode versions is.
Note that based on observations on Mac OS 10.15.7 on HFS+ the range U+1D000 - U+1D1FF is excluded from decomposition and U+2400 is replaced by U+0.
HFS timestamp
Date and time values are stored as an unsigned 32-bit integer containing the number of seconds since January 1, 1904 at 00:00:00 (midnight), where:
- MFS and HFS use local time;
- HFS+ and HFSX use Coordinated Universal Time (UTC).
This document will refer to both forms as HFS timestamp.
The maximum representable date is February 6, 2040 at 06:28:15 UTC.
The HFS timestamp does not account for leap seconds. It includes a leap day in every year that is evenly divisible by 4. This is sufficient given that the range of representable dates does not contain 1900 or 2100, neither of which have leap days.
File names
TN1150 states that HFS file names are compared in case-insensitive assuming a MacRoman encoding.
| Upper case | Lower case |
|---|---|
| 0x41 - 0x5a (A - Z) | 0x61 - 0x7a (a - z) |
| 0x80 (Ä) | 0x8a (ä) |
| 0x81 (Å) | 0x8c (å) |
| 0x82 (Ç) | 0x8d (ç) |
| 0x83 (É) | 0x8e (é) |
| 0x84 (Ñ) | 0x96 (ñ) |
| 0x85 (Ö) | 0x9a (ö) |
| 0x86 (Ü) | 0x9f (ü) |
| 0xae (Æ) | 0xbe (æ) |
| 0xaf (Ø) | 0xbf (ø) |
| 0xcb (À) | 0x88 (à) |
| 0xcc (Ã) | 0x8b (ã) |
| 0xcd (Õ) | 0x9b (õ) |
| 0xce (Œ) | 0xcf (œ) |
| 0xd9 (Ÿ) | 0xd8 (ÿ) |
| 0xe5 (Â) | 0x89 (â) |
| 0xe6 (Ê) | 0x90 (ê) |
| 0xe7 (Á) | 0x87 (á) |
| 0xe8 (Ë) | 0x91 (ë) |
| 0xe9 (È) | 0x8f (è) |
| 0xea (Í) | 0x92 (í) |
| 0xeb (Î) | 0x94 (î) |
| 0xec (Ï) | 0x95 (ï) |
| 0xed (Ì) | 0x93 (ì) |
| 0xee (Ó) | 0x97 (ó) |
| 0xef (Ô) | 0x99 (ô) |
| 0xf1 (Ò) | 0x98 (ò) |
| 0xf2 (Ú) | 0x9c (ú) |
| 0xf3 (Û) | 0x9e (û) |
| 0xf4 (Ù) | 0x9d (ù) |
HFS+ allows for the “/” character in file names. On Mac OS, Finder this will be represented as a “/” but in Terminal it is replaced by “:” since the same character is used as path segment separator. A file name with a “:” created in Terminal will be shown as “/” in Finder. Finder does not allow the creation of a file containing “:” in the name. A symbolic link created in Terminal to a file with a “:” in name will not convert the “:” character in the link target data. The Linux HFS+ implementation appears to apply a similar conversion logic as Terminal.
B-tree files
HFS, HFS+ and HFSX use multiple B-trees files.
A B-tree file consists of fixed sized nodes:
- header node
- map nodes
- index (root and branch) nodes
- leaf nodes
Note that only the data fork of a B-tree file is used. The resource fork should be unused.
The size of a B-tree file can be calculated in the following manner:
size = number_of_nodes * node_size
Node size
The node size is determined when the B-tree file is created.
| Feature | HFS | HFS+ and HFSX |
|---|---|---|
| Node size | 512 bytes | where the value must be a power of 2 in the range 512 - 32768 |
In a HFS+ the B-tree node size is stored in the header node.
Default node sizes:
| Feature | HFS | HFS+ and HFSX |
|---|---|---|
| catalog file | 512 | 4 KiB (8 KiB in Mac OS X) |
| extents overflow file | 512 | 1 KiB (4 KiB in Mac OS X) |
| attributes file | N/A | 4 KiB |
B-tree (file) node
A B-tree file node consists of:
- node descriptor
- node records
- node record offsets
The first node in the file is referenced by node number 0.
The node offset relative to the start of the file and can be calculated in the following manner:
node_offset = node_number * node_size
B-tree node descriptor
The B-tree node descriptor (BTNodeDescriptor) is 14 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Next tree node number (forward link), which contains 0 if empty | |
| 4 | 4 | Previous tree node number (backward link), which contains 0 if empty | |
| 8 | 1 | Node type, which consists of a signed 8-bit integer | |
| 9 | 1 | Node level, which consists of a signed 8-bit integer | |
| 10 | 2 | Number of records | |
| 12 | 2 | 0 | Unknown (Reserved), should contain 0 |
The root node level is 0, with a maximum depth of 8.
B-tree node types
| Value | Identifier | Description |
|---|---|---|
| -1 | kBTLeafNode | leaf node |
| 0 | kBTIndexNode | index node |
| 1 | kBTHeaderNode | header node |
| 2 | kBTMapNode | map node |
B-tree node record
The B-tree node record contains (leaf) data or a reference to an index node and consists of:
- a key
- value data
B-tree record offsets
The B-tree record offsets are an array of 16-bit integers relative from the start of the B-tree node descriptor. The first record offset is found at node size - 2, e.g. 512 - 2 = 510, the second 2 bytes before that, e.g. 508, etc.
An additional record offset is added at the end to signify the start of the free space.
Note that the record offsets are not necessarily stored in linear order.
B-tree header node
The B-tree header node is stored in the first node of the B-tree file and contains 3 records:
- the B-tree header record;
- the user data record, which consist of 128 bytes (reserved within HFS);
- the B-tree map record.
Note that the records in the B-tree header node do not have keys.
B-tree header record
The B-tree header record (BTHeaderRec) is 106 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Depth of the tree | |
| 2 | 4 | Root node number | |
| 6 | 4 | Number of data records contained in leaf nodes | |
| 10 | 4 | First leaf node number | |
| 14 | 4 | Last leaf node number | |
| 18 | 2 | Node size, in bytes, where the value must be a power of 2 in the range 512 - 32768 | |
| 20 | 2 | Maximum key size, in bytes | |
| 22 | 4 | Number of nodes | |
| 26 | 4 | Number of unused nodes | |
| HFS | |||
| 30 | 76 | Unknown (Reserved) | |
| HFS+/HFSX | |||
| 30 | 2 | Unknown (Reserved) | |
| 32 | 4 | Clump size, in bytes | |
| 36 | 1 | B-tree file type | |
| 37 | 1 | Key comparision method | |
| 38 | 4 | Flags (or attributes) | |
| 42 | 16 x 4 = 64 | Unknown (Reserved) | |
TODO: does the number of data records equal the number of leaf nodes?
File type
| Value | Identifier | Description |
|---|---|---|
| 0x00 | Control file | |
| 0x80 | First user B-tree type | |
| 0xff | Reserved B-tree type |
Key comparision methodtype
| Value | Identifier | Description |
|---|---|---|
| 0x00 | Unknown (not set), observed on HFS standard, HFS+ and an empty HFSX file system | |
| 0xbc | Binary compare (case-sensitive) | |
| 0xcf | Unicode case folding (case-insensitive) |
Flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | kBTBadCloseMask | Bad close, which indicates that the B-tree was not closed properly and should be checked for consistency (Not used by HFS+ and HFSX) |
| 0x00000002 | kBTBigKeysMask | Big keys, which indicates the key data size value of the keys in index and leaf nodes is 16-bit integer, otherwise, it is an 8-bit integer (Must be set for HFS+ and HFSX) |
| 0x00000004 | kBTVariableIndexKeysMask | Variable-size (index) keys, which indicates that the keys in index nodes occupy the number of bytes indicated by their key size; otherwise, the keys in index nodes always occupy maximum key size (must be set for the HFS+ and HFSX Catalog B-tree, and cleared for the HFS+ and HFSX Extents overflow B-tree) |
B-tree map record
The B-tree map record contains of a bitmap that indicates which nodes in the B-tree file are used and which are not. If a bit is set, then the corresponding node in the B-tree file is in use.
The bitmap is 256 bytes in size and can represent a maximum of 2048 nodes. If more nodes are needed a map node is used to store additional mappings.
The map node
If a B-tree file contains more than 2048 nodes, which are enough for about 8000 files, a map node is used to store additional node-mapping information.
The next tree node value in the B-tree node descriptor of the header node is used to refer to the first map node.
A map node consists of a B-tree node descriptor and one B-tree map record. The map record is 494 bytes in size 512 - (14 + 2) and can therefore contain mapping information for 3952 nodes.
If a B-tree contains more than 6000 nodes (enough for about 25000 files) a second map node is needed. The next tree node value in the B-tree node descriptor of the first map node is used to refer to the second.
If more map nodes are required, each additional map node is similarly linked to the previous one.
The root node
The root node is the start of the B-tree structure; usually the root node is an index node, but it might be a leaf node if there are no index nodes.
The root node number is stored in the B-tree header record and is 0 if the B-tree is empty.
The index node
The records stored in an index node are called pointer records. A pointer record consists of a key followed by the node number of the corresponding node. The size of the key varies according to the type of B-tree file.
- In a catalog file, the search key is a combination of the file or directory name and the parent identifier of that file or directory.
- In an extents overflow file, the search key is a combination of that file’s type, its file identifier and the index of the first block in the extent.
The immediate descendants of an index node are called the children of the index node. An index node can have from 1 to 15 children, depending on the size of the pointer records that the index node contains.
The leaf node
The leaf nodes contain data records. The structure of the leaf node data records varies according to the type of B-tree.
- In an extents overflow file, the leaf node data records consist of a key and an extent record.
- In a catalog file, the leaf node data records can be any one of four kinds of records.
HFS Master Directory Block (MDB)
The primary Master Directory Block (MDB) (or volume information block (VIB)) is located at offset 1024 of the volume.
The MDB is 162 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "BD" (or "\x42\x44") | Volume signature |
| 2 | 4 | Creation time, which contains a HFS timestamp in local time | |
| 6 | 4 | (last) modification time, which contains a HFS timestamp in local time | |
| 10 | 2 | Volume attribute flags | |
| 12 | 2 | Number of files in the root directory | |
| 14 | 2 | Volume bitmap block number, contains a block number relative from the start of the volume, where 0 is the first block number, typically 3 | |
| 16 | 2 | Next allocation search block number | |
| 18 | 2 | Number of blocks, where a volume can contain at most 65535 blocks | |
| 20 | 4 | Block size (drAlBlkSiz), in bytes, must be a multitude of 512 | |
| 24 | 4 | Clump size, in bytes | |
| 28 | 2 | Data area start sector (drAlBlSt), contains a sector number relative from the start of the volume, where 0 is the first sector number and bytes per sector is 512 | |
| 30 | 4 | Next available catalog node identifier (CNID), which can be a directory or file record identifier | |
| 34 | 2 | Number of unused blocks | |
| 36 | 1 | Volume label size, with a maximum of 27 | |
| 37 | 27 | Volume label | |
| 64 | 4 | (last) backup time, which contains a HFS timestamp in local time | |
| 68 | 2 | Backup sequence number | |
| 70 | 4 | Volume write count, which contains the number of times the volume has been written to | |
| 74 | 4 | Extents overflow file clump size, in bytes | |
| 78 | 4 | Catalog file clump size, in bytes | |
| 82 | 2 | Number of sub directories in the root directory | |
| 84 | 4 | Total number of files, which does not include file system metadata files | |
| 88 | 4 | Total number of directories (folders), which does not include the root folder | |
| 92 | 32 | Finder information | |
| 124 | 2 | Embedded volume signature (drEmbedSigWord, previously used for drVCSize) | |
| 126 | 4 | Embedded volume extent descriptor (drEmbedExtent, previously used for drVBMCSize and drCtlCSize) | |
| 130 | 4 | Extents overflow file size | |
| 134 | 12 | Extents overflow file extents record | |
| 146 | 4 | Catalog file size | |
| 150 | 12 | Catalog file extents record |
Note that the volume modification time is not necessarily the data and time when the volume was last flushed.
Notes
TODO: check
- drVCSize => Volume cache block size (16-bit)
- drVBMCSize => Volume bitmap cache block size (16-bit)
- drCtlCSize => Common volume cache block size (16-bit)
HFS Volume Bitmap
The volume bitmap is used to keep track of block allocation. The bitmap contains one bit for each block in the volume.
- If a bit is set, the corresponding block is currently in use by some file.
- If a bit is clear, the corresponding block is not currently in use by any file and is available.
The volume bitmap does not indicate which files occupy which blocks. The actual file-mapping information in maintained in two locations:
- in the corresponding catalog entry;
- in the corresponding extents overflow file entry.
The size of the volume bitmap depends on the number of blocks in the volume.
A 800 KiB floppy disk with a block size of 512 bytes has a volume bitmap size of:
((800 * 1024) / (512 * 8)) = 1600 bits (200 bytes).
A 32 MiB volume containing 32 MiB with a block size of 512 bytes has a volume bitmap size of:
((32 * 1024 * 1024) / (512 * 8)) = 65536 bits (8192 bytes).
The number of blocks in the volume in the MDB consists of a 16-bit integer, so no more than 65535 blocks can be addressed. The volume bitmap is never larger than 8192 bytes (or 16 physical blocks). For volumes containing more than 32 MiB of space, the block size must be increased.
A volume containing 40 MiB of space must have an block size that is at least 2 x 512 bytes.
A volume containing 80 MiB of space must have an block size that is at least 3 x 512 bytes.
HFS+ and HFSX Volume Header
The volume header (HFSPlusVolumeHeader) replaces the master directory block (MDB). The volume header starts at offset 1024 of the volume.
The block containing the first 1536 bytes (reserved space plus volume header) are marked as used in the allocation file.
The volume header is 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "H+" (or "\x48\x2b") or "HX" (or "\x48\x58") | Volume signature, where "H+" (kHFSPlusSigWord) is used for HFS+ and "HX" (kHFSXSigWord) for HFSX |
| 2 | 2 | Format version, where 4 (kHFSPlusVersion) is used for HFS+ and 5 (kHFSXVersion) for HFSX | |
| 4 | 4 | Volume attribute flags | |
| 8 | 4 | Last mounted version | |
| 12 | 4 | Journal information block number, contains a block number relative from the start of the volume | |
| 16 | 4 | Creation time, which contains a HFS timestamp in UTC | |
| 20 | 4 | (last) content modification time, which contains a HFS timestamp in UTC | |
| 24 | 4 | (last) backup time, which contains a HFS timestamp in UTC | |
| 28 | 4 | Checked time, which contains a HFS timestamp in UTC | |
| 32 | 4 | Total number of files, which does not include file system metadata files | |
| 36 | 4 | Total number of directories (folders), which does not include the root folder | |
| 40 | 4 | Block size, in bytes | |
| 44 | 4 | Total number of blocks | |
| 48 | 4 | Number of unused blocks | |
| 52 | 4 | Next allocation search block number (nextAllocation) | |
| 56 | 4 | Clump size, in bytes, of a resource fork | |
| 60 | 4 | Clump size, in bytes, of a data fork | |
| 64 | 4 | Next available catalog node identifier (CNID), which can be a directory or file record identifier | |
| 68 | 4 | Volume write count, which contains the number of times the volume has been written to | |
| 72 | 8 | Encodings bitmap | |
| 80 | 32 | Finder information | |
| 112 | 80 | Allocation file fork descriptor | |
| 192 | 80 | Extents overflow file fork descriptor | |
| 272 | 80 | Catalog file fork descriptor | |
| 352 | 80 | Attributes file fork descriptor | |
| 432 | 80 | Startup file fork descriptor |
Total number of blocks
For a disk whose size is an even multiple of the block size, all areas on the disk are included in an block, including the volume header and backup volume header. For a disk whose size is not an even multiple of the block size, only the blocks that will fit entirely on the disk are counted here. The remaining space at the end of the disk is not used by the volume format (except for storing the backup volume header, as described above).
Volume attribute flags
The volume attributes flags are specified as following.
| Value | Identifier | Description |
|---|---|---|
| 0x00000080 | kHFSVolumeHardwareLockBit | Volume hardware lock, set if the volume is write-protected due to a hardware setting |
| 0x00000100 | kHFSVolumeUnmountedBit | Volume unmounted, set if the volume was correctly flushed before being unmounted or ejected |
| 0x00000200 | kHFSVolumeSparedBlocksBit | Volume spared blocks, set if there are any records in the extents overflow file for bad blocks |
| 0x00000400 | kHFSVolumeNoCacheRequiredBit | Volume no cache required, set if the blocks from this volume should not be cached |
| 0x00000800 | kHFSBootVolumeInconsistentBit | Boot volume inconsistent, set if the volume was mounted for writing |
| 0x00001000 | kHFSCatalogNodeIDsReusedBit | Catalog node identifiers reused, set when the next catalog identifier value overflows 32 bits, forcing smaller catalog node identifiers to be reused |
| 0x00002000 | kHFSVolumeJournaledBit | Journaled, set if the file system uses a journal |
| 0x00004000 | kHFSVolumeInconsistentBit | Unknown (Reserved) |
| 0x00008000 | kHFSVolumeSoftwareLockBit | Volume software lock, set if the volume is write-protected due to a software setting |
| 0x40000000 | kHFSContentProtectionBit | Unknown (Reserved) |
| 0x80000000 | kHFSUnusedNodeFixBit | Unknown (Reserved) |
Last mounted version
| Value | Identifier | Description |
|---|---|---|
| "8.10" | used by Mac OS 8.1 to 9.2.2 | |
| "10.0" | kHFSPlusMountVersion | used by Mac OS X |
| "FSK!" or "fsck" | used by fsck_hfs on Mac OS X | |
| "HFSJ" | kHFSJMountVersion | used by journaled HFS+ or HFSX |
Links
TODO: add text about HFS standard
HFS+ supports both hard links and symbolic links.
Hard links to directories are not supported (allowed).
Hard Links
Hard links in HFS+/HFSX are represented by multiple different types of file records:
- one indirect node file record, named “iNode#”, where # is the link reference. This file contains the content of the file shared by the hard links.
- one or more hard link file records, that reference the indirect node file record.
Note that indirect node records named “dir_#” and “temp#” also have been observed.
Indirect node files are stored in a file system metadata directory referred to as the metadata directory with the name “/\u{2400}\u{2400}\u{2400}\u{2400}HFS+ Private Data”.
The link reference corresponds to the catalog node identifier (CNID) of the indirect node file, where 0 is not a valid link reference.
Note that TN1150 states that a new link reference randomly chosen from the range 100 to 1073741923. However link references that fall outside of this range have been observed such as “iNode20”.
The special permission data of the hard link file records contains the link reference if:
- the catalog file record flag kHFSHasLinkChainMask is set;
- and the first 8 bytes of the file information contains “hlnkhfs+”
| Value | Identifier | Description |
|---|---|---|
| "hlnk" | kHardLinkFileType | Hard link file type |
| "hfs+" | kHFSPlusCreator | Hard link file creator |
The hard link file’s creation date should be set to the creation date of the metadata directory, but the creation date may also be set to the creation date of the volume’s root directory though this is deprecated.
Device identifier
The Special permission data contains the device identifier. The device identifier can be stored in different formats, such as: “native”, “386bsd”, “4bsd”, “bsdos”, “freebsd”, “hpux”, “isc”, “linux”, “netbsd”, “osf1”, “sco”, “solaris”, “sunos”, “svr3”, “svr4” and “ultrix”.
The “native” and “hpux” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Major device number | |
| 1 | 3 | Minor device number |
The “386bsd”, “4bsd”, “freebsd”, “isc”, “linux”, “netbsd”, “sco”, “sunos”, “svr3” and “ultrix” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0 | Unknown |
| 2 | 1 | Major device number | |
| 3 | 1 | Minor device number |
The “solaris” and “svr4” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 18 bits | Minor device number | |
| 2.2 | 14 bits | Major device number |
The “bsdos” and “osf1” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 20 bits | Minor device number | |
| 2.4 | 12 bits | Major device number |
The “bsdos” alternative device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 8 bits | Sub unit number | |
| 1.0 | 12 bits | Unit number | |
| 2.4 | 12 bits | Major device number |
Symbolic Links
The data fork of a symbolic link contains the path of the directory or file it refers to.
On HFS+/HFSX the symbolic link target contains a POSIX pathname, as used by the Mac OS BSD and Cocoa programming interfaces; not a traditional Mac OS or Carbon, path.
The path is stored as an UTF-8 encoded string without an end-of-string character. The length of the path should be 1024 bytes or less. The path may be full or partial, with or without a leading forward slash.
The first 8 bytes of the file information should contain “slnkrhap”.
| Value | Identifier | Description |
|---|---|---|
| "slnk" | kSymLinkFileType | Symbolic link file type |
| "rhap" | kSymLinkCreator | Symbolic link file creator |
The resource fork of a symbolic link is reserved and should be 0 bytes in size.
The catalog file
The catalog file is a B-tree file used to maintain information about the hierarchy of files and directories of a volume.
The block number of the first file extent of the catalog file (the header node) is stored in the master directory block (HFS) or the volume header (HFS+). The B-tree structure is described in section: B-tree files.
Each node in the catalog file is assigned a unique catalog node identifier (CNID). The CNID is used for both directory and file identifiers. For any given file or directory the parent identifier is the CNID of the parent directory. The first 16 CNIDs are reserved for use by Apple and include the following standard assignments:
| CNID | Identifier | Assignment |
|---|---|---|
| 0 | Unknown (Reserved) | |
| 1 | kHFSRootParentID | Parent identifier of the root directory (folder) |
| 2 | kHFSRootFolderID | Directory identifier of the root directory (folder) |
| 3 | kHFSExtentsFileID | Extents overflow file |
| 4 | kHFSCatalogFileID | Catalog file |
| 5 | kHFSBadBlockFileID | Bad allocation block file |
| 6 | kHFSAllocationFileID | Allocation file (HFS+) |
| 7 | kHFSStartupFileID | Startup file (HFS+) |
| 8 | kHFSAttributesFileID | Attributes file (HFS+) |
| 14 | kHFSRepairCatalogFileID | Used temporarily by fsck_hfs when rebuilding the catalog file |
| 15 | kHFSBogusExtentFileID | Bogus extent file, which is used temporarily during exchange files operations |
| 16 | kHFSFirstUserCatalogNodeID | First available CNID for user's files and folders |
Catalog file keys
In a catalog file a key consists of:
- parent directory identifier
- (optional) file or directory name
The volume reference number is not included in the search key.
Text encoding hint
| Encoding type | Value | Encodings bitmap number |
|---|---|---|
| MacRoman | 0 | 0 |
| MacJapanese | 1 | 1 |
| MacChineseTrad | 2 | 2 |
| MacKorean | 3 | 3 |
| MacArabic | 4 | 4 |
| MacHebrew | 5 | 5 |
| MacGreek | 6 | 6 |
| MacCyrillic | 7 | 7 |
| MacDevanagari | 9 | 9 |
| MacGurmukhi | 10 | 10 |
| MacGujarati | 11 | 11 |
| MacOriya | 12 | 12 |
| MacBengali | 13 | 13 |
| MacTamil | 14 | 14 |
| MacTelugu | 15 | 15 |
| MacKannada | 16 | 16 |
| MacMalayalam | 17 | 17 |
| MacSinhalese | 18 | 18 |
| MacBurmese | 19 | 19 |
| MacKhmer | 20 | 20 |
| MacThai | 21 | 21 |
| MacLaotian | 22 | 22 |
| MacGeorgian | 23 | 23 |
| MacArmenian | 24 | 24 |
| MacChineseSimp | 25 | 25 |
| MacTibetan | 26 | 26 |
| MacMongolian | 27 | 27 |
| MacEthiopic | 28 | 28 |
| MacCentralEurRoman | 29 | 29 |
| MacVietnamese | 30 | 30 |
| MacExtArabic | 31 | 31 |
| MacSymbol | 33 | 33 |
| MacDingbats | 34 | 34 |
| MacTurkish | 35 | 35 |
| MacCroatian | 36 | 36 |
| MacIcelandic | 37 | 37 |
| MacRomanian | 38 | 38 |
| MacFarsi | 140 | 49 |
| MacUkrainian | 152 | 48 |
HFS catalog key
The HFS catalog key is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Key data size, in bytes, which consists of a signed 8-bit integer | |
| If key data size >= 6 | |||
| 1 | 1 | Unknown (Reserved) | |
| 2 | 4 | Parent identifier (CNID) | |
| 6 | 1 | Name size without the end-of-string character | |
| 7 | ... | Name string, which contains a narrow character string without end-of-string character | |
| ... | ... | Unknown (Alignment padding) | |
Note that a key data size of 0 indicates a records that is no longer in use.
The catalog node name always is stored as 32 bytes and therefore the maximum key size within an index node should be 37. In a leaf node the catalog node name varies in size.
Keys in a leaf node must be stored 16-bit aligned within the node data. The size of the alignment padding is not included in the key data size.
HFS+ and HFSX catalog key
The HFS+ and HFSX catalog key is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Key data size, in bytes | |
| If key data size >= 4 | |||
| 2 | 4 | Parent identifier, which contains a CNID | |
| If key data size >= 6 | |||
| 6 | 2 | Number of characters in the name string | |
| 8 | ... | Name string, which contains an UTF-16 big-endian string without end-of-string character | |
Note that the characters ‘:’ and U+2400 are stored as ‘/’ and U+0 respectively and must be converted before comparision.
The catalog data
A catalog leaf node can contain four different types of records:
- a folder record, which contains information about a single directory.
- a file record, which contains information about a single file.
- a folder thread record, which provides a link between a directory and its parent directory.
- a file thread record, which provides a link between a file and its parent directory.
The thread records are used to find the name and directory identifier of the parent of a given file or directory.
Each catalog data record consists of:
- the catalog data record header;
- the catalog data record data.
The catalog data record header
HFS catalog data record header
The HFS catalog data record header is 2 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Record type, which consists of a signed 8-bit integer | |
| 1 | 1 | 0x00 | Unknown (Reserved), which consists of a signed 8-bit integer |
Note that to distinguish between HFS and HFS+ record types, record type should be treated as a 16-bit big-endian value.
HFS+ and HFSX catalog data record header
The HFS+ and HFSX catalog data record header is 2 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Record type |
The catalog data record types
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | kHFSPlusFolderRecord | HFS+/HFSX Folder record |
| 0x0002 | kHFSPlusFileRecord | HFS+/HFSX File record |
| 0x0003 | kHFSPlusFolderThreadRecord | HFS+/HFSX Folder thread record |
| 0x0004 | kHFSPlusFileThreadRecord | HFS+/HFSX File thread record |
| 0x0100 | kHFSFolderRecord (or cdrDirRec) | HFS Folder record |
| 0x0200 | kHFSFileRecord (or cdrFilRec) | HFS File record |
| 0x0300 | kHFSFolderThreadRecord (or cdrThdRec) | HFS Folder thread record |
| 0x0400 | kHFSFileThreadRecord (or cdrFThdRec) | HFS File thread record |
The catalog folder record
HFS catalog folder record
The HFS catalog folder record (cdrDirRec, kHFSFolderRecord) is 70 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0100 | Record type |
| 2 | 2 | Folder flags | |
| 4 | 2 | Number of directory entries (valence) | |
| 6 | 4 | Identifier (CNID) | |
| 10 | 4 | Creation time, which contains a HFS timestamp in local time | |
| 14 | 4 | (last) content modification time, which contains a HFS timestamp in local time | |
| 18 | 4 | (last) backup time, which contains a HFS timestamp in local time | |
| 22 | 16 | Folder information | |
| 38 | 16 | Extended folder information | |
| 54 | 4 x 4 = 16 | Unknown (Reserved), which consists of an array of 32-bit integer values |
HFS catalog folder record flags
Not defined. The HFS catalog folder record appears to always have a corresponding folder thread record.
HFS+ and HFSX catalog folder record
The HFS+ and HFSX catalog folder record (HFSPlusCatalogFolder) is 88 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0001 | Record type |
| 2 | 2 | Flags | |
| 4 | 4 | Number of directory entries (valence) | |
| 8 | 4 | Identifier (CNID) | |
| 12 | 4 | Creation time, which contains a HFS timestamp in UTC | |
| 16 | 4 | (last) content modification time, which contains a HFS timestamp in UTC | |
| 20 | 4 | (last) record (or attribute) modification (or change) time, which contains a HFS timestamp in UTC | |
| 24 | 4 | (last) access time, which contains a HFS timestamp in UTC | |
| 28 | 4 | (last) backup time, which contains a HFS timestamp in UTC | |
| Permissions | |||
| 32 | 4 | Owner identifier | |
| 36 | 4 | Group identifier | |
| 40 | 1 | Administration flags | |
| 41 | 1 | Owner flags | |
| 42 | 2 | File mode | |
| 44 | 4 | Special permission data | |
| Folder information | |||
| 48 | 16 | Folder information | |
| Extended folder information | |||
| 64 | 16 | Extended folder information | |
| 80 | 4 | Text encoding hint | |
| 84 | 4 | 0x00 | Unknown (Reserved) |
The catalog file record
HFS catalog file record
The HFS catalog file record (cdrFilRec, kHFSFileRecord) is 102 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0200 | Record type |
| 2 | 1 | Flags, which consists of a signed 8-bit integer | |
| 3 | 1 | 0x00 | File type, which consists of a signed 8-bit integer and should contain 0 |
| 4 | 16 | File information | |
| 20 | 4 | Identifier (CNID) | |
| 24 | 2 | Data fork block number | |
| 26 | 4 | Data fork size | |
| 30 | 4 | Data fork allocated size | |
| 34 | 2 | Resource fork block number | |
| 36 | 4 | Resource fork size | |
| 40 | 4 | Resource fork allocated size | |
| 44 | 4 | Creation time, which contains a HFS timestamp in local time | |
| 48 | 4 | (last) content modification time, which contains a HFS timestamp in local time | |
| 52 | 4 | (last) backup time, which contains a HFS timestamp in local time | |
| 56 | 16 | Extended file information | |
| 72 | 2 | Clump size | |
| 74 | 12 | Data fork extents record | |
| 86 | 12 | Resource fork extents record | |
| 98 | 4 | 0x00 | Unknown (Reserved) |
TODO: determine if the data and resource fork block number values are used
HFS catalog file record flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | File is locked and cannot be written to | |
| 0x0002 | Has thread record | |
| 0x0080 | kHFSHasDateAddedMask | Had added time |
HFS+ and HFSX catalog file record
The HFS+ and HFSX catalog file record (kHFSPlusFileRecord) is 248 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0002 | Record type |
| 2 | 2 | Flags | |
| 4 | 4 | 0x00 | Unknown (Reserved) |
| 8 | 4 | Identifier (CNID) | |
| 12 | 4 | Creation time, which contains a HFS timestamp in UTC | |
| 16 | 4 | (last) content modification time, which contains a HFS timestamp in UTC | |
| 20 | 4 | (last) record (or attribute) modification time, which contains a HFS timestamp in UTC | |
| 24 | 4 | (last) access time, which contains a HFS timestamp in UTC | |
| 28 | 4 | (last) backup time, which contains a HFS timestamp in UTC | |
| Permissions | |||
| 32 | 4 | Owner identifier | |
| 36 | 4 | Group identifier | |
| 40 | 1 | Administration flags | |
| 41 | 1 | Owner flags | |
| 42 | 2 | File mode | |
| 44 | 4 | Special permission data | |
| File information | |||
| 48 | 16 | File information (or user information) | |
| Extended file information | |||
| 64 | 16 | Extended file information (or finder information) | |
| 80 | 4 | Text encoding hint | |
| 84 | 4 | 0x00 | Unknown (Reserved) |
| 88 | 80 | Data fork descriptor | |
| 168 | 80 | Resource fork descriptor | |
HFS+ catalog file record flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | kHFSFileLockedMask | File is locked and cannot be written to |
| 0x0002 | kHFSThreadExistsMask | Has thread record, which should be always set for a file record on HFS+/HSFX |
| 0x0004 | kHFSHasAttributesMask | Has extended attributes |
| 0x0008 | kHFSHasSecurityMask | Has ACLs |
| 0x0010 | kHFSHasFolderCountMask | Has number of sub-folder |
| 0x0020 | kHFSHasLinkChainMask | Has a hard link target (link chain), where the CNID of the hard link target is stored in the special permission data |
| 0x0040 | kHFSHasChildLinkMask | Has a child that is a directory link |
| 0x0080 | kHFSHasDateAddedMask | Had added time, where the extended folder of file information contains the time the folder or file was added (date_added) |
| 0x0100 | kHFSFastDevPinnedMask | Unknown |
| 0x0200 | kHFSDoNotFastDevPinMask | Unknown |
| 0x0400 | kHFSFastDevCandidateMask | Unknown |
| 0x0800 | kHFSAutoCandidateMask | Unknown |
The catalog thread record
The file thread record is similar to the folder thread record except that it refers to a file, instead of a directory.
HFS catalog file thread record
The HFS catalog thread record (kHFSFolderThreadRecord (or cdrThdRec), kHFSFileThreadRecord (or cdrFThdRec)) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0300 or 0x0400 | Record type |
| 2 | 2 x 4 = 8 | 0x00 | Unknown (Reserved), which consists of an array of 32-bit integer values |
| 10 | 4 | Parent identifier (CNID) | |
| 14 | 1 | Number of characters in the name string, with a maximum of 31 | |
| 15 | ... | Name string, which contains a narrow character string without end-of-string character |
HFS+ and HFSX catalog file thread record
The HFS+ and HFSX catalog thread record (kHFSPlusFolderThreadRecord, kHFSPlusFileThreadRecord) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0x0003 or 0x0004 | Record type |
| 2 | 2 | 0x00 | Unknown (Reserved), which consists of a unsigned 16-bit integer |
| 4 | 4 | Parent identifier (CNID) | |
| 8 | 2 | Number of characters in the name string, with a maximum of 255 | |
| 10 | ... | Name string, which contains an UTF-16 big-endian string without end-of-string character |
Permissions
For each file and folder HFS+ maintains basic access permissions record for each file and folder. These are similar to basic Unix file permissions.
TODO: add note about permissions on HFS
Owner and group identifier
The Mac OS X user ID of the owner of the file or folder. Mac OS X versions prior to 10.3 treats user ID 99 as if it was the user ID of the user currently logged in to the console. If no user is logged in to the console, user ID 99 is treated as user ID 0 (root). Mac OS X version 10.3 treats user ID 99 as if it was the user ID of the process making the call (in effect, making it owned by everyone simultaneously). These substitutions happen at run-time. The actual user ID on disk is not changed.
The Mac OS X group ID of the group associated with the file or folder. Mac OS X typically maps group ID 99 to the group named “unknown.” There is no run-time substitution of group IDs in Mac OS X.
Administration flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | SF_ARCHIVED | File has been archived |
| 0x02 | SF_IMMUTABLE | File is immutable and may not be changed |
| 0x04 | SF_APPEND | Writes to file may only append |
Owner flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | UF_NODUMP | Do not backup (dump) this file |
| 0x02 | UF_IMMUTABLE | File is immutable and may not be changed |
| 0x04 | UF_APPEND | Writes to file may only append |
| 0x08 | UF_OPAQUE | Directory is opaque |
File mode
| Value | Identifier | Description |
|---|---|---|
| 0xf000 (0170000) | S_IFMT | File type bitmask |
| 0x1000 (0010000) | S_IFIFO | Named pipe |
| 0x2000 (0020000) | S_IFCHR | Character-special file (Character device) |
| 0x4000 (0040000) | S_IFDIR | Directory |
| 0x6000 (0060000) | S_IFBLK | Block-special file (Block device) |
| 0x8000 (0100000) | S_IFREG | Regular file |
| 0xa000 (0120000) | S_IFLNK | Symbolic link |
| 0xc000 (0140000) | S_IFSOCK | Socket |
| 0xe000 (0160000) | S_IFWHT | Whiteout, which is a file entry that covers up all entries of a particular name from lower branches |
HFS+ uses the BSD file type and mode bits. Note that the constants from the header shown below are in octal (base eight), not hexadecimal.
| Octal value | Identifier | Description |
|---|---|---|
| 0004000 | S_ISUID | Set user identifier on execution |
| 0002000 | S_ISGID | Set group identifier on execution |
| 0001000 | S_ISTXT | Sticky bit |
| 0000700 | S_IRWXU | Read, write and execute access for owner |
| 0000400 | S_IRUSR | Read access for owner |
| 0000200 | S_IWUSR | Write access for owner |
| 0000100 | S_IXUSR | Execute access for owner |
| 0000070 | S_IRWXG | Read, write and execute access for group |
| 0000040 | S_IRGRP | Read access for group |
| 0000020 | S_IWGRP | Write access for group |
| 0000010 | S_IXGRP | Execute access for group |
| 0000007 | S_IRWXO | Read, write and execute access for other |
| 0000004 | S_IROTH | Read access for other |
| 0000002 | S_IWOTH | Write access for other |
| 0000001 | S_IXOTH | Execute access for other |
Note that if the sticky bit is set for a directory, then Mac OS restricts movement, deletion, and renaming of files in that directory. Files may be removed or renamed only if the user has write access to the directory; and is the owner of the file or the directory, or is the super-user.
HFS+ file special permission data
The special permission data is used to store the following information:
- hard link reference (iNodeNum)
- number of (hard) links (linkCount) in indirect node files
- device numbers of block (S_IFBLK) and character (S_IFCHR) devices files
File system hierarchy
File and folder records have a search key with a non-empty name string. In thread records the name string in the search key is empty. E.g. to list the file entries in a directory:
- find all the file or folder records given the parent CNID
Finding a file or directory by its CNID is a two-step process:
- use the CNID to look up the thread record for the file or directory
- use the thread record to look up the file or folder record
File forks
Forks in HFS and HFS+ can be compared to data streams in NTFS. In HFS+ the fork values are grouped in a separate fork descriptor structure. HFS+ also defines extended attributes (named forks). These are not stored in the catalog file but in the attributes file.
HFS+ fork descriptor structure
HFS+ maintains information about file contents using the HFS+ fork descriptor structure (HFSPlusForkData).
The fork descriptor structure is 80 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Size, in bytes | |
| 8 | 4 | Clump size, in bytes | |
| 12 | 4 | Number of blocks | |
| 16 | 64 | Data extents record |
The extents overflow file
In HFS and HFS+ extents (contiguous ranges of blocks) are used to track which blocks belong to a file. The first three (HFS) and eight (HFS+) are stored in the catalog file. Additional extents are stored in the extents overflow file.
The structure of an extents overflow file is relatively simple compared to that of a catalog file. The function of the extents overflow file is to store those file extents that are not contained in the master directory block (MDB) or volume header and the catalog file
Note that the file system B-tree files can have additional extents in the extents overflow file. This has been observed with the attributes file. It is currently unknown if the extents (overflow) file itself can have overflow extents.
The extents overflow key (record)
Disks initialized using the enhanced Disk Initialization Manager introduced in system software version might contain extent records for some blocks that do not belong to any actual file in the file system. These extent records have been marked as a bad block (CNID 5). See the chapter “Disk Initialization Manager” in this book for details on bad block sparing.
The key has been selected so that the extent records for a particular fork are grouped together in the B-tree, right next to all the extent records for the other fork of the file. The fork offset of the preceding extent record is needed to determine the key of the next extent record
In an extents overflow file the search key consists of:
- fork type
- file identifier
- first block in the extent
HFS extents overflow key (record)
The HFS extents overflow key (record) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | 7 | Key data size, in bytes, which consists of a signed 8-bit integer |
| 1 | 1 | Fork type, which consists of a signed 8-bit integer | |
| 2 | 4 | File identifier (CNID) | |
| 6 | 2 | Logical block number |
The first 8 extents in a fork are held in its catalog file record. So the number of extent records for a fork is:
(number_of_extents - 3 + 2) / 4
HFS+ and HFSX extents overflow key (record)
The HFS+ and HFSX extents overflow key (record) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 10 | Key data size, in bytes, which consists of an unsigned 16-bit integer |
| 2 | 1 | Fork type, which consists of a signed 8-bit integer | |
| 3 | 1 | 0x00 | Unknown (Padding) |
| 4 | 4 | File identifier (CNID) | |
| 8 | 4 | Logical block number |
The first 8 extents in a fork are held in its catalog file record. So the number of extent records for a fork is:
(number_of_extents - 8 + 7) / 8
HFS fork types
| Value | Identifier | Description |
|---|---|---|
| -1 (0xff) | Resource fork | |
| 0 (0x00) | Data fork |
The extent (data) record
An extent is a contiguous range of blocks that have been allocated to an individual file. An extent is represented by an extent descriptor.
HFS extents record
The HFS extents record (HFSExtentRecord) is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 x 4 = 12 | Array of HFS extent descriptors |
HFS extent descriptor
The HFS extents descriptor (HFSExtentDescriptor) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Physical block number, which contains a block number relative from the start of the data area | |
| 2 | 2 | Number of blocks |
extent_offset = (data_area_block_number + extent_block_number) * block_size
An unused extent descriptor should have both the block number and number of blocks set to 0.
HFS+ and HFSX extents record
The HFS+ and HFSX extents record (HFSPlusExtentRecord) is 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 x 8 = 64 | Array of HFS+ extent descriptors |
HFS+ and HFSX extent descriptor
The HFS+ and HFSX extents descriptor (HFSPlusExtentDescriptor) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Physical block number, which contains a block number relative from the start of the volume | |
| 4 | 4 | Number of blocks |
extent_offset = extent_block_number * block_size
An unused extent descriptor should have both the block number and number of blocks set to 0.
Bad Block File
The extents overflow file is also used to hold information about the bad blocks; refered to as the bad block file. The bad block file is used to mark areas on the disk as bad, unable to be used for storing data; typically to map out bad sectors on the storage medium.
Typically, blocks are larger than sectors. If a single sector is found to be bad, the entire block is unusable. The bad block file is sometimes used to mark blocks as unusable when they are not bad, e.g. in the HFS wrapper.
Bad block extent records are always assumed to reference the data fork (fork type of 0).
Allocation (bitmap) file
The allocation file is uzed to keep track of whether each block in a volume is currently allocated to some file system structure or not. The contents of the allocation file is a bitmap. The bitmap contains one bit for each block in the volume.
- If a bit is set, the corresponding block is currently in use by some file system structure.
- If a bit is clear, the corresponding block is not currently in use, and is available for allocation.
The size of the allocation file depends on the number of blocks in the volume, which in turn depends both on the size of the disk and on the size of the volume’s blocks. For example, a volume on a 1 GB disk and having an block size of 4 KB needs an allocation file size of 256 Kbits (32 KiB, or 8 blocks). Since the allocation file itself is allocated using blocks, it always occupies an integral number of blocks (its size may be rounded up).
The allocation file may be larger than the minimum number of bits required for the given volume size. Any unused bits in the bitmap must be set to 0.
Each byte in the allocation file holds the state of eight blocks. The byte at offset X into the file contains the allocation state of allocations blocks (N x 8) through (N x 8 + 7). Within each byte, the most significant bit holds information about the block with the lowest number, the least significant bit holds information about the block with the highest number. Listing 1 shows how you would test whether an block is in use, assuming that you’ve read the entire allocation file into memory.
Method to determine whether a block is used:
byte_value = allocation_file_data[block_number // 8]
is_used = (byte_value & (1 << (7 - (block_number % 8)))) != 0
Attributes file
The attributes file is a B-tree file used to store extended attributes.
The location of the attributes file can be found in the HFS+ and HFSX volume header.
Attributes file keys
An attributes file key (HFSPlusAttrKey) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Key data size, in bytes | |
| If key data size >= 12 | |||
| 2 | 2 | 0 | Unknown (padding) |
| 4 | 4 | Identifier (CNID) | |
| 8 | 4 | Logical block number | |
| 12 | 2 | Number of characters in the name string | |
| 14 | ... | Name string, which contains an UTF-16 big-endian string without end-of-string character | |
Note that the name of an extended attribute appears to be case senstive even on a case insensitive file system.
The attributes file data
The attributes file defines two types of attributes:
- Fork data attributes, which are used for attributes whose data is large. The attribute’s data is stored in extents on the volume and the attribute merely contains a reference to those extents.
- Extension attributes, which are used to augment fork descriptor structure, allowing a forks to have more than eight extents.
Attributes file data record header
Each attributes file data record starts with a type value, which describes the type of attribute data record.
The attributes file data record header is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Record type |
The attributes data record types
| Value | Identifier | Description |
|---|---|---|
| 0x00000010 | kHFSPlusAttrInlineData | Attribute record with inline data |
| 0x00000020 | kHFSPlusAttrForkData | Attribute record with fork descriptor |
| 0x00000030 | kHFSPlusAttrExtents | Extents overflow record for a kHFSPlusAttrForkData record |
Note that at the moment it is unclear when an attribute record of type kHFSPlusAttrExtents is created and how it should be handled.
The inline data attribute record
The inline data attribute record (HFSPlusAttrData) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0x00000010 | Record type |
| 4 | 2 x 4 = 8 | 0 | Unknown (reserved) |
| 12 | 4 | Attribute data size | |
| 16 | ... | Attribute data |
Note that is also a HFSPlusAttrInlineData record, which is obsolete and has not been observed to be used.
The fork descriptor attribute record
The fork descriptor attribute record (HFSPlusAttrForkData) is 88 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0x00000020 | Record type |
| 4 | 4 | 0 | Unknown (reserved) |
| 8 | 80 | Attribute fork descriptor |
The extents attribute record
The extents attribute record (HFSPlusAttrExtents) is 72 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 0x00000030 | Record type |
| 4 | 4 | 0 | Unknown (reserved) |
| 8 | 64 | Attribute extents record |
Startup file
The startup file is a file system metadata file intended to hold information needed when booting a system that does not have built-in (ROM) support for HFS+ (or HFSX). A boot loader can find the startup file without full knowledge of the format using the first eight extents of the startup file located in the volume header.
Format wise it is valid for the startup file to contain more than eight extents, but in doing so the purpose of the startup file is defeated.
Next allocation search
The next block number is used by Mac OS as a hint for where to start searching for available blocks when allocating space for a file.
Metadata zone and hot files
In Mac OS X 10.3 a metadata zone was instroduced to store certain file system metadata, such as allocation bitmap file, extents overflow file, and the catalog file, the journal file and frequently used small files (also referred to as “hot files”) near each other to reduces seek time for typical accesses.
Hot File B-tree
The hot file B-tree is a file named “.hotfiles.btree” stored the root directory.
Journal
A HFS+ (or HFSX) volume may have an optional journal to speed recovery when mounting a volume that was not unmounted safely. The purpose of the journal is to ensure that when a group of related changes are being made, that either all of those changes are actually made, or none of them are made. The journal makes it quick and easy to restore the volume structures to a consistent state, without having to scan all of the structures. The journal is used only for the volume structures and metadata; it does not protect the contents of a fork.
The volume header specifies if journalling is activated.
The journal data stuctures consist of:
- a journal information block, contains the location and size of the journal header and journal buffer;
- a journal header, describes which part of the journal buffer is active and contains transactions waiting to be committed;
- a journal buffer, a cyclic buffer to hold the file system meta data transactions.
On HFS+ volumes, the journal information block is stored as a file. The name of that file is “.journal_info_block” and it is stored in the volume’s root directory.
The journal header and journal buffer are stored together in a different file named “.journal”, also in the volume’s root directory. Each of these files are contiguous on disk, they occupy exactly one extent.
The volume header contains the extent of the journal information block file. The journal information block contains the location of the journal file.
Journal information block
The journal information block describes where the journal header and journal buffer are stored. The journal information block is stored at the start of the block referred to by the volume header.
The journal information block is 44 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Journal flags | |
| 4 | 8 x 4 = 32 | Device signature | |
| 36 | 8 | Journal header offset | |
| 44 | 8 | Journal size, in bytes, which includes the size of the journal header and the journal buffer, but not the journal information block | |
| 52 | 32 x 4 = 128 | 0x00 | Unknown (Reserved) |
Journal flags
The journal flags consist of the following values:
| Value(s) | Description |
|---|---|
| 0x00000001 | On volume, where the journal header offset is relative to the start of the volume |
| 0x00000002 | On other device, where the device signature identifies the device containing the journal and the journal header offset is relative to the start of the device |
| 0x00000004 | Needs initialization, to indicate that there are no valid transactions in the journal and needs to be initialized |
Note that according to TN1150 journals stored on a separate device are not supported.
The journal header
The journal header is 44 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "\x4a\x4e\x4c\x78" | Signature |
| 4 | 4 | "\x12\x34\x56\x78" | Byte order (or endian) signature |
| 8 | 8 | First transaction start offset | |
| 16 | 8 | Next transaction start offset | |
| 24 | 8 | Journal size, in bytes, which includes the size of the journal header and buffer | |
| 32 | 4 | Journal block header size, in bytes, typically ranges from 4096 to 16384 | |
| 36 | 4 | checksum | |
| 40 | 4 | Journal header size, in bytes, typically the size of one sector |
First and next transaction offset
The first transaction offset contains the offset in bytes from the start of the journal header to the start of the first (oldest) transaction.
The next transaction offset contains the offset in bytes from the start of the journal header to the end of the last (newest) transaction. Note that this field may be less than the start field, indicating that the transactions wrap around the end of the journal’s circular buffer. If end equals start, then the journal is empty, and there are no transactions that need to be replayed.
Journal transactions
A single transaction is stored in the journal as several blocks. These blocks include both the data to be written and the location where that data is to be written. This is represented on storage medium by a block list header, which describes the number and sizes of the blocks, immediately followed by the contents of those blocks.
Since block list headers are of limited size, a single transaction may consist of several block list headers and their associated block contents. If the next value in the first block information structure is non-zero, then the next block list header is a continuation of the same transaction.
The journal buffer is treated as a circular buffer. When reading or writing the journal buffer, the I/O operation must stop at the end of the journal buffer and resume (wrap around) immediately following the journal header. Block list headers or the contents of blocks may wrap around in this way. Only a portion of the journal buffer is active at any given time; this portion is indicated by the start and end fields of the journal header. The part of the journal buffer that is not active contains no meaningful data, and must be ignored.
To prevent ambiguity when start equals end, the journal is never allowed to be perfectly full (all of the journal buffer used by block lists and blocks). If the journal was perfectly full, and start was not equal to jhdr_size, then end would be equal to start. You would then be unable to differentiate between an empty and full journal.
When the journal is not empty (contains transactions), it must be replayed to be sure the volume is consistent. That is, the data from each of the transactions must be written to the correct blocks on disk.
The journal block list header
The block list header describes a list of blocks included in a transaction. A transaction may include several block lists if it modifies more blocks than can be represented in a single block list.
The journal block list header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Maximum number of journal blocks | |
| 2 | 2 | Number of journal blocks following the journal block header, typically 1 | |
| 4 | 4 | Block list size, in bytes, which includess the size of the header and blocks | |
| 8 | 4 | Checksum | |
| 12 | 4 | 0x00 | Unknown (Alignment padding) |
| 16 | ... | Journal block information array |
Note that the number of journal blocks includes the first journal block, The first journal block is reserved to be used when multiple blocks need to be chained, therefore the number of journal blocks actually containing data is minus one (-1).
Journal block information
The journal block information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Block sector number | |
| 8 | 4 | Block size, in bytes | |
| 12 | 4 | Next journal block |
Journal checksum
The journal header and block list header both contain checksum values. The checksums are verified as part of a basic consistency check of these journal data structures. To verify the checksum, temporarily set the checksum field to 0 and then call the hfs_plus_calculate_checksum routine as specified below.
uint32_t hfs_plus_calculate_checksum(
uint8_t *buffer,
size_t buffer_size )
{
size_t buffer_offset = 0;
uint32_t checksum = 0;
for( buffer_offset = 0;
buffer_offset < buffer_size;
buffer_offset++)
{
checksum = ( checksum << 8 ) ^ ( checksum + buffer[ buffer_offset ] );
}
return( ~checksum );
}
Application specific data structures
HFS, HFS+ and HFSX contain application specific data structures.
Finder information
The finder information in the master directory block (MDB) and volume header consists of an array of 32-bit values. This array contains information used by the Mac OS Finder and the system software boot process.
| Array entry | Description |
|---|---|
| 0 | Bootable system directory identifier (CNID), i.e. "System Folder" in Mac OS 8 or 9, or "/System/Library/CoreServices" in Mac OS X. Typically 3 or 5, is 0 if the volume is not bootable |
| 1 | Startup application parent identifier (CNID), i.e. "Finder". Is 0 if the volume is not bootable |
| 2 | Directory identifier (CNID) to display in Finder on mount, or 0 if none |
| 3 | Directory identifier (CNID) of a bootable Mac OS 8 or 9 System Folder, or 0 if none |
| 4 | Unknown (Reserved) |
| 5 | Directory identifier (CNID) of a bootable Mac OS X system, the "/System/Library/CoreServices" directory, or 0 if none |
| 6 and 7 | Mac OS X Volume Status Database (VSDB) volume identifier |
To convert the VSDB volume identifier into a UUID:
- Append the volume identifier to HFS_UUID_NAMESPACE_ID (B3E20F39-F292-11D6-97A4-00306543ECAC)
- Calculate the MD5 digest hash of the byte sequence
- Convert the MD5 digest hash into the UUID, see below
data = bytes.fromhex("b3e20f39f29211d697a400306543ecac") + volume_identifier
hash = hashlib.md5(data).hexdigest()
part1 = hash[0:8]
part2 = hash[8:12]
part3 = "".join(["3", hash[13:16]])
digit = (int(hash[16], 16) & 3) | 8
part4 = "".join([f"{digit:x}", hash[17:20])
part5 = hash[20:]
uuid = "-".join([part1, part2, part3, part4, part5])
Note that on Mac OS the finder information can be viewed using
bless --info volume_mount_path.
File information
HFS file information
The HFS file information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 x 1 = 4 | File type, which consists of an array of unsigned 8-bit integers | |
| 4 | 4 x 1 = 4 | File creator, which consists of an array of unsigned 8-bit integers | |
| 8 | 2 | Finder flags | |
| 10 | 4 | Location within the parent, which contains x and y-coordinate values. If set to {0, 0}, the Finder will place the item automatically | |
| 14 | 2 | File icon window, which contains the window in which the file's icon appears |
HFS extended file information
The HFS extended file information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Finder icon identifier | |
| 2 | 3 x 2 = 6 | Unknown (Reserved), which consists of an array of signed 16-bit integers | |
| 8 | 1 | Extended finder script code flags | |
| 9 | 1 | Extended finder flags | |
| 10 | 2 | Finder comment identifier, which consists of a signed 16-bit integer | |
| 12 | 4 | Put away folder identifier (CNID) |
HFS+ and HFSX file information
The HFS+ and HFSX file information (FileInfo) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 x 1 = 4 | File type, which consists of an array of unsigned 8-bit integers | |
| 4 | 4 x 1 = 4 | File creator, which consists of an array of unsigned 8-bit integers | |
| 8 | 2 | Finder flags | |
| 10 | 4 | Location within the parent, which contains x and y-coordinate values. If set to {0, 0}, the Finder will place the item automatically | |
| 14 | 2 | Unknown (Reserved) |
HFS+ and HFSX extended file information
The HFS+ and HFSX extended file information (ExtendedFileInfo) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (Reserved) | |
| If kHFSHasDateAddedMask is not set | |||
| 4 | 4 | Unknown (Reserved) | |
| If kHFSHasDateAddedMask is set | |||
| 4 | 4 | Added time, which contains a POSIX timestamp in UTC | |
| Common | |||
| 8 | 2 | Extended finder flags | |
| 10 | 2 | Unknown (Reserved), which consists of a signed 16-bit integer | |
| 12 | 4 | Put away folder identifier (CNID) | |
Folder information
HFS folder information
The HFS folder information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Window position and dimension (boundaries), which contains the top, left, bottom, right-coordinate values | |
| 8 | 2 | Finder flags | |
| 10 | 4 | Location within the parent, which contains x and y-coordinate values. If set to {0, 0}, the Finder will place the item automatically | |
| 14 | 2 | Folder view |
HFS extended folder information
The HFS extended folder information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Scroll position for icon view, which contains x and y-coordinate values | |
| If kHFSHasDateAddedMask is not set | |||
| 4 | 4 | Open folder identifier chain, which consists of a signed 32-bit integer | |
| If kHFSHasDateAddedMask is set | |||
| 4 | 4 | Added time, which contains a POSIX timestamp in UTC | |
| Common | |||
| 8 | 1 | Extended finder script code flags | |
| 9 | 1 | Extended finder flags | |
| 10 | 2 | Finder comment identifier, which consists of a signed 16-bit integer | |
| 12 | 4 | Put away folder identifier (CNID) | |
HFS+ and HFSX folder information
The HFS+ and HFSX folder information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Window position and dimension (boundaries), which contains the top, left, bottom, right-coordinate values | |
| 8 | 2 | Finder flags | |
| 10 | 4 | Location within the parent, which contains x and y-coordinate values. If set to {0, 0}, the Finder will place the item automatically | |
| 14 | 2 | Unknown (Reserved) |
HFS+ and HFSX extended folder information
The HFS+ and HFSX extended folder information is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Scroll position for icon view, which contains x and y-coordinate values | |
| 4 | 4 | Unknown (Reserved), which consists of a signed 32-bit integer | |
| 8 | 2 | Extended finder flags | |
| 10 | 2 | Unknown (Reserved), which consists of a signed 16-bit integer | |
| 12 | 4 | Put away folder identifier (CNID) |
Finder flags
The finder flags consists of the following values:
| Value(s) | Applies to | Description |
|---|---|---|
| 0x0001 | Files and folders | Is on desktop |
| 0x000e | Files and folders | Color |
| 0x0040 | Files | Is shared |
| 0x0080 | Files | Has no INITs |
| 0x0100 | Files | Has been inited |
| 0x0400 | Files and folders | Has custom icon |
| 0x0800 | Files | Is stationary |
| 0x1000 | Files and folders | Name locked |
| 0x2000 | Files | Has bundle |
| 0x4000 | Files and folders | Is invisible |
| 0x8000 | Files | Is alias |
Extended finder flags
The extended finder flags consists of the following values:
| Value(s) | Description |
|---|---|
| 0x0004 | Has routing information |
| 0x0100 | Has custom badge resource |
| 0x8000 | Extended flags are invalid, which indicates that set the other extended flags should be ignored |
Notes
struct Point {
SInt16 v;
SInt16 h;
};
typedef struct Point Point;
struct Rect {
SInt16 top;
SInt16 left;
SInt16 bottom;
SInt16 right;
};
typedef struct Rect Rect;
/* OSType is a 32-bit value made by packing four 1-byte characters
together. */
typedef UInt32 FourCharCode;
typedef FourCharCode OSType;
File content
HFS supports multiple ways to store file content:
- Data fork
- Resource fork
- Extended attribute (named fork)
- Apple File System Compression (decmpfs)
Data fork
The file content size is stored in the data fork descriptor of the catalog file record.
The extents of the file content are stored in the fork descriptor and extents overflow file.
Resource fork
The file content size is stored in the resource fork descriptor of the catalog file record.
The extents of the file content are stored in the fork descriptor and extents overflow file.
Note that the resource fork is sometimes represented as an extended attribute with the name: “com.apple.ResourceFork”.
Extended attribute (named fork)
Extended attributes, also referred to as named forks, are stored in the HFS+ attributes file.
Apple File System Compression (decmpfs)
The file has an extended attribute named “com.apple.decmpfs” and the content is stored using Apple File System Compression (decmpfs).
HFS wrapper
A HFS wrapped HFS+ file system consists of a HFS file system where the master directory block (MDB) contains:
- an embedded volume signature of “H+”;
- an embedded volume extent descriptor that describes the blocks in which the embedded volume is stored;
- the embedded volume extent is marked in the bad block file.
Note that Mac OS supports a HFS+ to be wrapped in a HFS volume, but not HFSX.
embedded volume offset = (
(data area start sector * 512) + (embedded volume block number * block size)
)
References
- hfs_format.h
- Data Organization on Volumes, by Apple Inc.
- Technical Note TN1150: HFS plus volume format, by Apple Inc.
Macintosh File System (MFS)
The Macintosh File System (MFS) is the first file system created for Mac OS, intended for 400 KiB floppy disks.
Overview
A MFS file system consists of:
- optional boot block
- master directory block (MDB)
- file directory area
- data area
- optional backup (or alternate) master directory block (MDB)
The backup master directory block (MDB), is stored in the last 2 sectors of the volume.
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | TODO |
| Character strings | Narrow character (Single Byte Character (SBC) or Multi Byte Character (MBC)) stored using a system defined codepage |
Terminology
| Term | Description |
|---|---|
| Clump size | Size of the group of (allocation) blocks (or clump), in bytes, to avoid fragmentation |
Boot Block
If a volume is bootable, the first 2 blocks of the volume contain boot block. The boot block consists of:
- boot block header
- boot code
- unknown (filler)
Boot Block Header
The boot block header is 138 or 144 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "LK" (or "\x4c\x4b") | Boot block signature |
| 2 | 4 | Boot code entry point | |
| 6 | 1 | Flags | |
| 7 | 1 | Format version | |
| 8 | 2 | Page flags (or Secondary Sound and Video Pages) | |
| 10 | 1 | System file name size, with a maximum of 15 | |
| 11 | 15 | System file name | |
| 26 | 1 | Finder (or shell) file name size, with a maximum of 15 | |
| 27 | 15 | Finder (or shell) file name, typically "Finder" | |
| 42 | 1 | Debugger file name size, with a maximum of 15 | |
| 43 | 15 | Debugger file name, typically "Macsbug" | |
| 58 | 1 | Disassembler (or second debugger) file name size, with a maximum of 15 | |
| 59 | 15 | Disassembler (or second debugger) file name, typically "Disassembler" | |
| 74 | 1 | Startup screen file name size, with a maximum of 15 | |
| 75 | 15 | Startup screen file name, typically "StartUpScreen" | |
| 90 | 1 | Startup (or bootup) file name size, with a maximum of 15 | |
| 91 | 15 | Startup (or bootup) file name, typically "Finder" | |
| 106 | 1 | Clipboard (or scrap) file name size, with a maximum of 15 | |
| 107 | 15 | Clipboard (or scrap) file name, typically "Clipboard" | |
| 122 | 2 | Number of allocated file control blocks (FCBs) | |
| 124 | 2 | Number of elements in the event queue, typically 20 | |
| 126 | 4 | System heap size on Macintosh computer with 128 KiB of RAM | |
| 130 | 4 | System heap size on Macintosh computer with 256 KiB of RAM | |
| 134 | 4 | System heap size on Macintosh computer with +512 KiB of RAM | |
| Newer boot block header format | |||
| 138 | 4 | Additional system heap space | |
| 140 | 4 | Fraction of available RAM for the system heap | |
Note that “LK” presumably is short for “Larry Kenyon” who originally designed MFS.
Boot code entry point
The boot code entry point contains machine-language instructions that translate to:
BRA.S *+ 0x90
Or for older versions of the boot block header:
BRA.S *+ 0x88
BRA.W *+ 0x88
BRA $88(PC) * $6000,$0086
This instruction jumps to the main boot code following the boot block header.
This field is ignored, however, if bit 6 is clear in the high-order byte of the boot block version number or if the low-order byte contains 0x0d.
Boot Block Header Flags
| Bit(s) | Description |
|---|---|
| 0 - 4 | Unknown (Reserved), should contain 0 |
| 5 | Use relative system heap sizing |
| 6 | Execute boot code |
| 7 | Newer boot block header format is used |
If bit 7 of the flag byte is clear, then bits 5 and 6 are ignored and the version number is set in the format version value.
If the format version value is:
- less than 21, the values in the system heap size on 128K Mac and 256K Mac should be ignored and the value in system heap size on all machines should be used.
- 13 the boot code should be executed using the value in boot code entry point.
- greater than or equal to 21 the value in system heap size on all machines should be used.
If bit 7 of the flag byte is set
- bit 6 should be used to determine whether to execute the boot code using the value in boot code entry point.
- bit 5 should be used to determine whether to use relative System heap sizing. If bit 5 is
- clear the value in system heap size on all machines should be used.
- is set the System heap is extended by the value in the additional system heap space plus the fraction of available RAM for the system heap.
Master Directory Block (MDB)
The Master Directory Block (MDB) is located at offset 1024 of the volume and consists of:
- master directory block header
- block map
Master Directory Block (MDB) header
The Master Directory Block (MDB) header is 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "\xd2\xd7" | Volume signature |
| 2 | 4 | Creation date and time, which contains a HFS timestamp in local time | |
| 6 | 4 | Last modification date and time, which contains a HFS timestamp in local time | |
| 10 | 2 | Volume attribute flags | |
| 12 | 2 | Number of files in the root directory | |
| 14 | 2 | File directory area sector number, contains a sector number relative from the start of the volume, where 0 is the first sector number | |
| 16 | 2 | File directory area size, in number of sectors | |
| 18 | 2 | Number of blocks | |
| 20 | 4 | Block size, in bytes, must be a multitude of 512 | |
| 24 | 4 | Clump size, in bytes | |
| 28 | 2 | Data area sector number, contains a sector number relative from the start of the volume, where 0 is the first sector number | |
| 30 | 4 | Next available file identifier | |
| 34 | 2 | Number of unused blocks | |
| 36 | 1 | Volume label size, with a maximum of 27 | |
| 37 | 27 | Volume label |
Block map
TODO: describe similar to FAT-12 block allocation table
File Directory Area
The file directory area consists of:
- one or more file directory entries, where an individual file directory entry does not span multiple blocks
File Directory Entry
A file directory entry is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Flags, where 0x80 indicates the file directory entry is in use | |
| 1 | 1 | 0 | Format version |
| 2 | 4 | "\x3f\x3f\x3f\x3f" | File type |
| 6 | 4 | File creator | |
| 10 | 2 | Finder flags | |
| 12 | 4 | Window position and dimension (boundaries), which contains the top, left, bottom, right-coordinate values | |
| 16 | 2 | Folder file identifier, where 0 represents the main volume, -2 the desktop, -3 the trash, otherwise, if positive, a file identifier | |
| 18 | 4 | File identifier | |
| 22 | 2 | Data fork block number, contains 0 if the file entry has no data fork | |
| 24 | 4 | Data fork size, in bytes | |
| 28 | 4 | Data fork allocated size, in bytes | |
| 32 | 2 | Resource fork block number, contains 0 if the file entry has no resource fork | |
| 34 | 4 | Resource fork size, in bytes | |
| 38 | 4 | Resource fork allocated size, in bytes | |
| 42 | 4 | Creation date and time, which contains a HFS timestamp in local time | |
| 46 | 4 | (Content) modification date and time, which contains a HFS timestamp in local time | |
| 50 | 1 | File name size, with a maximum of 255 | |
| 51 | ... | File name | |
| ... | ... | 16-bit alignment padding |
New Technologies File System (NTFS) format
The New Technologies File System (NTFS) format is the primary file system for Microsoft Windows versions that are based on Windows NT.
Overview
An New Technologies File System (NTFS) consists of:
- boot record
- boot loader
- Master File Table (MFT)
- Mirror Master File Table (MFT)
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | FILETIME in UTC |
| Character strings | UCS-2 little-endian, which allows for unpaired Unicode surrogates such as "U+d800" and "U+dc00" |
Versions
| Format version | Remarks |
|---|---|
| 1.0 | Introduced in Windows NT 3.1 |
| 1.1 | Introduced in Windows NT 3.5, also seen to be used by Windows NT 3.1 |
| 1.2 | Introduced in Windows NT 3.51 |
| 3.0 | Introduced in Windows 2000 |
| 3.1 | Introduced in Windows XP |
Note that the format versions mentioned above are the version as used by NTFS. Another common versioning schema uses the Windows version, e.g. NTFS 5.0 is the version of NTFS used on Windows XP which is version 3.1 in schema mentioned above.
Windows does not necessarily uses the latest format version, e.g. Windows 10 (1809) has been observed to use NTFS version 1.2 for 64k cluster block size.
Terminology
Cluster
NTFS refers to it file system blocks as clusters. Note that these are not the same as the physical clusters of a harddisk. For clarity this document will refer to these as cluster blocks. In other sources they are also referred to as logical clusters.
Typically a cluster block is 8 sectors (or 8 x 512 = 4096 bytes) in size. A cluster block number is relative to the start of the boot record.
Virtual cluster
The term virtual cluster refers to cluster blocks which are relative to the start of a data stream.
Long and short (file) name
In Windows terminology the name of a file (or directory) can either be short or long. The short name is an equivalent of the file name in the (DOS) 8.3 format. The long name is actual the (full) name of the file. The term long refers to the aspect that the name is longer than the short variant. Because most documentation refer to the (full) name as the long name, for clarity sake so will this document.
Metadata files
NTFS uses the Master File Table (MFT) to store information about files and directories. The MFT entries reference the different volume and file system metadata. There are several predefined metadata files.
The following metadata files are predefined and use a fixed MFT entry number.
| MFT entry number | File name | Description |
|---|---|---|
| 0 | "$MFT" | Master File Table |
| 1 | "$MFTMirr" | Back up of the first 4 entries of the Master File Table |
| 2 | "$LogFile" | Metadata transaction journal |
| 3 | "$Volume" | Volume information |
| 4 | "$AttrDef" | MFT entry attribute definitions |
| 5 | "." | Root directory |
| 6 | "$Bitmap" | Cluster block allocation bitmap |
| 7 | "$Boot" | Boot record (or boot code) |
| 8 | "$BadClus" | Bad clusters |
| Used in NTFS version 1.2 and earlier | ||
| 9 | "$Quota" | Quota information |
| Used in NTFS version 3.0 and later | ||
| 9 | "$Secure" | Security and access control information |
| Common | ||
| 10 | "$UpCase" | Case folding mappings |
| 11 | "$Extend" | A directory containing extended metadata files |
| 12-15 | Unknown (Reserved), which are marked as in-use but are empty | |
| 16-23 | Unused, which are marked as unused | |
| Used in NTFS version 3.0 and later | ||
| 24 | "$Extend$Quota" | Quota information |
| 25 | "$Extend$ObjId" | Unique file identifiers for distributed link tracking |
| 26 | "$Extend$Reparse" | Backreferences to reparse points |
| Transactional NTFS metadata files, which have been observed in Windows Vista and later | ||
| 27 | "$Extend$RmMetadata" | Resource manager metadata directory |
| 28 | "$Extend$RmMetadata$Repair" | Repair information |
| 29 or 30 | "$Extend$RmMetadata$TxfLog" | Transactional NTFS (TxF) log metadata directory |
| 30 or 31 | "$Extend$RmMetadata$Txf" | Transactional NTFS (TxF) metadata directory |
| 31 or 32 | "$Extend$RmMetadata$TxfLog$Tops" | TxF Old Page Stream (TOPS) file, which is used to store data that has been overwritten inside a currently active transaction |
| 32 or 33 | "$Extend$RmMetadata$TxfLog$TxfLog.blf" | Transactional NTFS (TxF) base log metadata file |
| Observed in Windows 10 and later | ||
| 29 | "$Extend$Deleted" | Temporary location for files that have an open handle but a request has been made to delete them |
| Common | ||
| ... | A file or directory | |
The following metadata files are predefined, however the MFT entry number is commonly used but not fixed.
| MFT entry number | File name | Description |
|---|---|---|
| "$Extend$UsnJrnl" | USN change journal |
The boot record
The boot record is stored at the start of the volume (in the $Boot metadata file) and contains:
- the file system signature
- the BIOS parameter block
- the boot loader
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 3 | Boot entry point | |
| 3 | 8 | "NTFS\x20\x20\x20\x20" | File system signature (Also known as OEM identifier or dummy identifier) |
| DOS version 2.0 BIOS parameter block (BPB) | |||
| 11 | 2 | Bytes per sector. Note that the following values are supported by mkntfs: 256, 512, 1024, 2048 and 4096 | |
| 13 | 1 | Number of sectors per cluster block | |
| 14 | 2 | 0 | Unknown (Reserved Sectors), which is not used by NTFS and must be 0 |
| 16 | 1 | 0 | Number of cluster block allocation tables, which is not used by NTFS and must be 0 |
| 17 | 2 | 0 | Number of root directory entries, which is not not used by NTFS and must be 0 |
| 19 | 2 | 0 | Number of sectors (16-bit), which is not used by NTFS must be 0 |
| 21 | 1 | Media descriptor | |
| 22 | 2 | 0 | Cluster block allocation table size (16-bit) in number of sectors, which is not used by NTFS and must be 0 |
| DOS version 3.4 BIOS parameter block (BPB) | |||
| 24 | 2 | 0x3f | Sectors per track, which is not used by NTFS |
| 26 | 2 | 0xff | Number of heads, which is not used by NTFS |
| 28 | 4 | 0x3f | Number of hidden sectors, which is not used by NTFS |
| 32 | 4 | 0x00 | Number of sectors (32-bit), which is not used by NTFS must be 0 |
| NTFS version 8.0 BIOS parameter block (BPB) or extended BPB, which was introduced in Windows NT 3.1 | |||
| 36 | 1 | 0x80 | Unknown (Disc unit number), which is not used by NTFS |
| 37 | 1 | 0x00 | Unknown (Flags), which is not used by NTFS |
| 38 | 1 | 0x80 | Unknown (BPB version signature byte), which is not used by NTFS |
| 39 | 1 | 0x00 | Unknown (Reserved), which is not used by NTFS |
| 40 | 8 | Number of sectors (64-bit) | |
| 48 | 8 | Master File Table (MFT) cluster block number | |
| 56 | 8 | Mirror MFT cluster block number | |
| 64 | 4 | MFT entry size | |
| 68 | 4 | Index entry size | |
| 72 | 8 | Volume serial number | |
| 80 | 4 | 0 | Checksum, which is not used by NTFS |
| Common | |||
| 84 | 426 | Boot code | |
| 510 | 2 | "\x55\xaa" | The (boot) signature |
Note that the number of sectors can be 1 less then the value indicated in the partition table.
Boot entry point
The boot entry point often contains a jump instruction to the boot code at offset 84 followed by a no-operation, e.g.
eb52 jmp 0x52
90 nop
Number of sectors per cluster block
The number of sectors per cluster block value as used by mkntfs is defined as following:
- Values 0 to 128 represent sizes of 0 to 128 sectors.
- Values 244 to 255 represent sizes of
2^(256-n)sectors. - Other values are unknown.
Cluster block size
The cluster block size can be determined as following:
cluster block size = bytes per sector x sectors per cluster block
Different NTFS implementations support different cluster block sizes. Known supported cluster block size:
| Cluster block size | Bytes per sector | Supported by |
|---|---|---|
| 256 | 256 | mkntfs |
| 512 | 256 - 512 | mkntfs, ntfs3g, Windows |
| 1024 | 256 - 1024 | mkntfs, ntfs3g, Windows |
| 2048 | 256 - 2048 | mkntfs, ntfs3g, Windows |
| 4096 | 256 - 4096 | mkntfs, ntfs3g, Windows |
| 8192 | 256 - 4096 | mkntfs, ntfs3g, Windows |
| 16K (16384) | 256 - 4096 | mkntfs, ntfs3g, Windows |
| 32K (32768) | 256 - 4096 | mkntfs, ntfs3g, Windows |
| 64K (65536) | 256 - 4096 | mkntfs, ntfs3g, Windows |
| 128K (131072) | 256 - 4096 | mkntfs, ntfs3g, Windows 10 (1903) |
| 256K (262144) | 256 - 4096 | mkntfs, ntfs3g, Windows 10 (1903) |
| 512K (524288) | 256 - 4096 | mkntfs, ntfs3g, Windows 10 (1903) |
| 1M (1048576) | 256 - 4096 | mkntfs, ntfs3g, Windows 10 (1903) |
| 2M (2097152) | 512 - 4096 | mkntfs, ntfs3g, Windows 10 (1903) |
Note that Windows 10 (1903) requires the partition containing the NTFS file system to be aligned with the cluster block size. For example for a cluster block size of 128k the partition must 128 KiB aligned. The default partition partition alignment appears to be 64 KiB.
mkntfs restricts the cluster size to:
bytes_per_sector >= cluster_block_size > 4096 * bytes_per_sector
Master File Table (MFT) offset
The Master File Table (MFT) offset can be determined as following:
mft_offset = boot_record_offset + (mft_cluster_block_number * cluster_block_size)
The lower 32-bit part of the NTFS volume serial number is the Windows API (WINAPI) volume serial number. This can be determined by comparing the output of:
fsutil fsinfo volumeinfo C:
fsutil fsinfo ntfsinfo C:
Master File Table (MFT) and index entry size
The Master File Table (MFT) entry size and index entry size are defined as following:
- Values 0 to 127 represent sizes of 0 to 127 cluster blocks.
- Values 128 to 255 represent sizes of 2^(256-n) bytes or 2^(-n) if considered as a signed byte.
- Other values are not considered valid.
BitLocker Drive Encryption (BDE)
BitLocker Drive Encryption (BDE) uses the file system signature: “-FVE-FS-”. Where FVE is an abbreviation of Full Volume Encryption.
The data structures of BDE on Windows Vista and 7 differ.
A Windows Vista BDE volume starts with:
eb 52 90 2d 46 56 45 26 46 53 2d
A Windows 7 BDE volume starts with:
eb 58 90 2d 46 56 45 26 46 53 2d
BDE is largely a stand-alone but has some integration with NTFS.
TODO: link to BDE format documentation
Volume Shadow Snapshots (VSS)
Volume Shadow Snapshots (VSS) uses the GUID 3808876b-c176-4e48-b7ae-04046e6cc752 (stored in little-endian) to identify its data.
VSS is largely a stand-alone but has some integration with NTFS.
TODO: link to VSS format documentation
Media descriptor
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Sides, where single-sided (0) and double-sided (1) | |
| 0.1 | 1 bit | Track size, where 9 sectors per track (0) and 8 sectors per track (1) | |
| 0.2 | 1 bit | Density, where 80 tracks (0) and 40 tracks (1) | |
| 0.3 | 1 bit | Type, where Fixed disc (0) and Removable disc (1) | |
| 0.4 | 4 bits | Always set to 1 |
The boot loader
| Offset | Size | Value | Description |
|---|---|---|---|
| 512 | Windows NT (boot) loader (NTLDR/BOOTMGR) |
The Master File Table (MFT)
The MFT consist of an array of MFT entries. The offset of the MFT table can be found in the volume header and the size of the MFT is defined by the MFT entry of the $MFT metadata file.
Note that the MFT can consists of multiple data ranges, defined by the data runs in the $MFT metadata file.
MFT entry
Although the size of a MFT entry is defined in the volume header is commonly 1024 bytes in size and consists of:
- The MFT entry header
- The fix-up values
- An array of MFT attribute values
- Padding, which should contain 0-byte values
Note that the MFT entry can be filled entirely with 0-byte values. Seen in Windows XP for MFT entry numbers 16 - 23.
MFT entry header
The MFT entry header (FILE_RECORD_SEGMENT_HEADER) is 42 or 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| MULTI_SECTOR_HEADER | |||
| 0 | 4 | "BAAD", "FILE" | Signature |
| 4 | 2 | The fix-up values (or update sequence array) offset, which contain an offset relative from the start of the MFT entry | |
| 6 | 2 | The number of fix-up values (or update sequence array size) | |
| Common | |||
| 8 | 8 | Metadata transaction journal sequence number, which contains a $LogFile Sequence Number (LSN) | |
| 16 | 2 | Sequence (number) | |
| 18 | 2 | Reference (link) count | |
| 20 | 2 | Attributes offset (or first attribute offset), which contains an offset relative from the start of the MFT entry | |
| 22 | 2 | MFT entry flags | |
| 24 | 4 | Used size in bytes | |
| 28 | 4 | MFT entry size in bytes | |
| 32 | 8 | Base record file reference | |
| 40 | 2 | First available attribute identifier | |
| If NTFS version is 3.0 | |||
| 42 | 2 | Unknown (wfixupPattern) | |
| 44 | 4 | Unknown | |
| If NTFS version is 3.1 | |||
| 42 | 2 | Unknown (wfixupPattern) | |
| 44 | 4 | MFT entry number | |
“BAAD” signature
According to NTFS documentation if during chkdsk, when a multi-sector item is found where the multi-sector header does not match the values at the end of the sector, it marks the item as “BAAD” and fill it with 0-byte values except for a fix-up value at the end of the first sector of the item. The “BAAD” signature has been seen to be used on Windows NT4 and XP.
Sequence number
According to FILE_RECORD_SEGMENT_HEADER structure the sequence number is incremented each time that a file record segment is freed; it is 0 if the segment is not used.
Base record file reference
The base record file reference is used to store additional attributes for another MFT entry, e.g. for attribute lists.
MFT entry flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | FILE_RECORD_SEGMENT_IN_USE, MFT_RECORD_IN_USE | In use |
| 0x0002 | FILE_FILE_NAME_INDEX_PRESENT, FILE_NAME_INDEX_PRESENT, MFT_RECORD_IS_DIRECTORY | Has file name (or $I30) index. When this flag is set the file entry represents a directory |
| 0x0004 | MFT_RECORD_IN_EXTEND | Unknown. According to ntfs_layout.h this is set for all system files present in the $Extend directory |
| 0x0008 | MFT_RECORD_IS_VIEW_INDEX | Is index. When this flag is set the file entry represents an index. According to ntfs_layout.h this is set for all indices other than $I30 |
The fix-up values
The fix-up values are of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Fix-up placeholder value | |
| 2 | 2 x number of fix-up values | Fix-up (original) value array |
On disk the last 2 bytes for each 512 byte block is replaced by the fix-up placeholder value. The original value is stored in the corresponding fix-up (original) value array entry.
Note that there can be more fix-up values than the number of 512 byte blocks in the data.
According to MULTI_SECTOR_HEADER structure the update sequence array must end before the last USHORT value in the first sector. It also states that the update sequence array size value contains the number of bytes, but based on analysis of data samples it seems to be more likely to the number of words.
In NT4 (version 1.2) the MFT entry is 42 bytes in size and the fix-up values are stored at offset 42. This is likely where the name wfixupPattern originates from.
TODO: provide examples on applying the fix-up values.
The file reference
The file reference (FILE_REFERENCE or MFT_SEGMENT_REFERENCE) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 6 | MFT entry number | |
| 6 | 2 | Sequence number |
Note that the index value in the MFT entry is 32-bit in size.
MFT attribute
The MFT attribute consist of:
- the attribute header
- the attribute resident or non-resident data
- the attribute name
- Unknown data, likely alignment padding (4-byte alignment)
- resident attribute data or non-resident attribute data runs
- alignment padding (8-byte alignment), can contain remnant data
MFT attribute header
The MFT attribute header (ATTRIBUTE_RECORD_HEADER) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Attribute type (or type code) | |
| 4 | 4 | Attribute size (or record length), which includes the 8 bytes of the attribute type and size | |
| 8 | 1 | Non-resident flag (or form code), where RESIDENT_FORM (0) and NONRESIDENT_FORM (1) | |
| 9 | 1 | Name size (or name length), which contains the number of characters without the end-of-string character | |
| 10 | 2 | Name offset, which contains an offset relative from the start of the MFT attribute | |
| 12 | 2 | Attribute data flags | |
| 14 | 2 | Attribute identifier (or instance), which contains an unique identifier to distinguish between attributes that contain segmented data |
MFT attribute data flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | Is LZNT1 compressed | |
| 0x00ff | ATTRIBUTE_FLAG_COMPRESSION_MASK | |
| 0x4000 | ATTRIBUTE_FLAG_ENCRYPTED | Is encrypted |
| 0x8000 | ATTRIBUTE_FLAG_SPARSE | Is sparse |
TODO: determine the meaning of compression flag in the context of resident $INDEX_ROOT. Do the data flags have a different meaning for different attributes?
Resident MFT attribute
The resident MFT attribute data is present when the non-resident flag is not set (0). The resident data is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Data size (or value length) | |
| 4 | 2 | Data offset (or value size), which contains an offset relative from the start of the MFT attribute | |
| 6 | 1 | Indexed flag | |
| 7 | 1 | 0x00 | Unknown (Padding) |
TODO: determine the meaning of indexed flag bits, other than the LSB
Non-resident MFT attribute
The non-resident MFT attribute data is present when the non-resident flag is set (1). The non-resident data is 48 or 56 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | First (or lowest) Virtual Cluster Number (VCN) of the data | |
| 8 | 8 | Last (or highest) Virtual Cluster Number (VCN) of the data | |
| 16 | 2 | Data runs offset (or mappings pairs offset), which contains an offset relative from the start of the MFT attribute | |
| 18 | 2 | Compression unit size, which contains the compression unit size as 2^(n) number of cluster blocks | |
| 20 | 4 | Unknown (Padding) | |
| 24 | 8 | Allocated data size (or allocated length), which contains the allocated data size in number of bytes. This value is not valid if the first VCN is nonzero | |
| 32 | 8 | Data size (or file size), which contains the data size in number of bytes. This value is not valid if the first VCN is nonzero | |
| 40 | 8 | Valid data size (or valid data length), which contains the valid data size in number of bytes. This value is not valid if the first VCN is nonzero | |
| If compression unit size > 0 | |||
| 48 | 8 | Compressed data size | |
The total size of the data runs should be larger or equal to the data size.
Note that Windows will fill data beyond the valid data size with 0-byte values. The data size remains unchanged. This applies to compressed and uncompressed data. If the first VCN is zero a valid data size of 0 represents a file entirely filled with 0-byte values.
TODO: determine the meaning of a VCN of -1
For more information about compressed MFT attributes see compression.
Attribute name
The attribute name is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Name, which contains an UCS-2 little-endian string without end-of-string character |
Data runs
The data runs are stored in a variable size (data) runlist. This runlist consists of runlist elements.
A runlist element is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 4 bits | Number of cluster blocks value size, which contains the number of bytes used to store the data run size | |
| 0.4 | 4 bits | Cluster block number value size, which contains the number of bytes used to store the data run size | |
| 1 | Size value size | Data run number of cluster blocks, which contains the number of cluster blocks | |
| ... | Cluster block number value size | Data run cluster block number |
The data run cluster block number is a singed value, where the MSB is the singed bit, e.g. if the data run cluster block contains “dbc8” it corresponds to the 64-bit value 0xffffffffffffdbc8.
The first data run offset contains the absolute cluster block number where successive data run offsets are relative to the last data run offset.
Note that the cluster block number byte size is the first nibble when reading the byte stream, but here it is represented as the upper nibble of the first byte.
The last runlist element is (0, 0), which is stored as a 0-byte value.
According to NTFS documentation the size of the runlist is rounded up to the next multitude of 4 bytes, but based on analysis of data samples it seems that the size of the trailing data can be even larger than 3 and are not always 0-byte values.
TODO: provide examples of data runs
Sparse data runs
The MFT attribute data flag (ATTRIBUTE_FLAG_SPARSE) indicates if the data stream is sparse or not, where the runlist can contain both sparse and non-sparse data runs.
A sparse data run has a cluster block number value size of 0, representing there is no offset (cluster block number). A sparse data run is filled with 0-byte values.
Compressed data streams also define sparse data runs without setting the ATTRIBUTE_FLAG_SPARSE flag.
Note that $BadClus:$Bad also defines a data run with a cluster block number value size of 0, without setting the ATTRIBUTE_FLAG_SPARSE flag.
Compresssed data runs
The MFT attribute data flags (0x00ff) indicate if the data stream is compressed or not.
Windows supports compressed data runs for NTFS file systems with a cluster block size of 4096 bytes or less.
Windows 10 supports Windows Overlay Filter (WOF) compressed data, which stores the LZXPRESS Huffman or LZX compressed data in alternate data stream named WofCompressedData and links it to the default data stream using a reparse point.
The data is stored in compression unit blocks. A compression unit typically consists of 16 cluster blocks. However the actual value is stored in the non-resident MFT attribute.
Also see compression.
The attributes
Known attribute types
The attribute types are stored in the $AttrDef metadata file.
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | Unused | |
| 0x00000010 | $STANDARD_INFORMATION | Standard information |
| 0x00000020 | $ATTRIBUTE_LIST | Attributes list |
| 0x00000030 | $FILE_NAME | The file or directory name |
| Used in NTFS version 1.2 and earlier | ||
| 0x00000040 | $VOLUME_VERSION | Volume version |
| Used in NTFS version 3.0 and later | ||
| 0x00000040 | $OBJECT_ID | Object identifier |
| Common | ||
| 0x00000050 | $SECURITY_DESCRIPTOR | Security descriptor |
| 0x00000060 | $VOLUME_NAME | Volume label |
| 0x00000070 | $VOLUME_INFORMATION | Volume information |
| 0x00000080 | $DATA | Data stream |
| 0x00000090 | $INDEX_ROOT | Index root |
| 0x000000a0 | $INDEX_ALLOCATION | Index allocation |
| 0x000000b0 | $BITMAP | Bitmap |
| Used in NTFS version 1.2 and earlier | ||
| 0x000000c0 | $SYMBOLIC_LINK | Symbolic link |
| Used in NTFS version 3.0 and later | ||
| 0x000000c0 | $REPARSE_POINT | Reparse point |
| Common | ||
| 0x000000d0 | $EA_INFORMATION | (HPFS) extended attribute information |
| 0x000000e0 | $EA | (HPFS) extended attribute |
| Used in NTFS version 1.2 and earlier | ||
| 0x000000f0 | $PROPERTY_SET | Property set |
| Used in NTFS version 3.0 and later | ||
| 0x00000100 | $LOGGED_UTILITY_STREAM | Logged utility stream |
| Common | ||
| 0x00001000 | First user defined attribute | |
| 0xffffffff | End of attributes marker | |
Attribute chains
Multiple attributes can be chained to make up a single attribute data stream, e.g. the attributes:
- $INDEX_ALLOCATION ($I30) VCN: 0
- $INDEX_ALLOCATION ($I30) VCN: 596
The first attribute will contain the size of the data defined by all the attributes and successive attributes should have a size of 0.
It is assumed that the attributes in a chain must be continuous and defined in-order.
The standard information attribute
The standard information attribute ($STANDARD_INFORMATION) contains the basic file entry metadata. It is stored as a resident MFT attribute.
The standard information data (STANDARD_INFORMATION) is either 48 or 72 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Creation date and time, which contains a FILETIME | |
| 8 | 8 | Last modification (or last written) dat and time, which contains a FILETIME | |
| 16 | 8 | MFT entry last modification date and time, which contains a FILETIME | |
| 24 | 8 | Last access date and time, which contains a FILETIME | |
| 32 | 4 | File attribute flags | |
| 36 | 4 | Unknown (Maximum number of versions) | |
| 40 | 4 | Unknown (Version number) | |
| 44 | 4 | Unknown (Class identifier) | |
| If NTFS version 3.0 or later | |||
| 48 | 4 | Owner identifier | |
| 52 | 4 | Security descriptor identifier, which contains the entry number in the security ID index ($Secure:$SII). Also see Access Control | |
| 56 | 8 | Quota charged | |
| 64 | 8 | Update Sequence Number (USN) | |
Note that MFT entries have been observed without a $STANDARD_INFORMATION attribute, but with other attributes such as $FILE_NAME and an $I30 index.
Recent version of NTFS support case-sentive file names. If a directory is case-sensitive the corresponding $STANDARD_INFORMATION attribute will have a maximum number of versions of 0 and a version number of 1.
The attribute list attribute
The attribute list attribute ($ATTRIBUTE_LIST) is used to store MFT attributes outside the MFT entry, e.g. when the MFT entry is too small to store all the attributes.
The entries in the list reference the location of MFT attributes. The attribute list attribute can be stored as either a resident (for a small amount of data) or non-resident MFT attribute.
Note that MFT entry 0 also can contain an attribute list and allows to store listed attributes beyond the first data run.
The attribute list
An attribute list consists of:
- one or more attribute list entries
The attribute list entry
An attribute list entry (ATTRIBUTE_LIST_ENTRY) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Attribute type (or type code) | |
| 4 | 2 | Size (or record length), which includes the 6 bytes of the attribute type and size | |
| 6 | 1 | Name size (or name length), which contains the number of characters without the end-of-string character | |
| 7 | 1 | Name offset, which contains an offset relative from the start of the attribute list entry | |
| 8 | 8 | Data first (or lowest) VCN | |
| 16 | 8 | File reference (or segment reference), which contains a reference to the MFT entry that contains (part of) the attribute data | |
| 24 | 2 | Attribute identifier (or instance), which contains an unique identifier to distinguish between attributes that contain segmented data | |
| 26 | ... | Name, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | ... | alignment padding (8-byte alignment), can contain remnant data |
The file name attribute
The file name attribute ($FILE_NAME) contains the basic file system information, like the parent file entry, various date and time values and name. It is stored as a resident MFT attribute.
The file name data (FILE_NAME) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Parent file reference | |
| 8 | 8 | Creation date and time, which contains a FILETIME | |
| 16 | 8 | Last modification (or last written) date and time, which contains a FILETIME | |
| 24 | 8 | MFT entry last modification date and time, which contains a FILETIME | |
| 32 | 8 | Last access date and time, which contains a FILETIME | |
| 40 | 8 | Allocated (or reserved) file size | |
| 48 | 8 | Data size | |
| 56 | 4 | File attribute flags | |
| If FILE_ATTRIBUTE_REPARSE_POINT is set | |||
| 60 | 4 | Reparse point tag | |
| If FILE_ATTRIBUTE_REPARSE_POINT is not set | |||
| 60 | 4 | Unknown (extended attribute data size) | |
| Common | |||
| 64 | 1 | Name string size, which contains the number of characters without the end-of-string character | |
| 65 | 1 | Namespace of the name string | |
| 66 | ... | Name, which contains an UCS-2 little-endian string without end-of-string character | |
An MFT attribute can contain multiple file name attributes, e.g. for a separate (long) name and short name.
In several cases on a Vista NTFS volume the MFT entry contained both a DOS & Windows and POSIX name space $FILE_NAME attribute. However the directory entry index ($I30) of the parent directory only contained the DOS & Windows name.
In case of a hard link the MFT entry will contain additional file name attributes with the parent file reference of each hard link.
Namespace
| Value | Identifier | Description |
|---|---|---|
| 0 | POSIX | Case-sensitive character set that consists of all Unicode characters except for: "\0" (zero character), "/" (forward slash). The ":" (colon) is valid for NTFS but not for Windows |
| 1 | FILE_NAME_NTFS, WINDOWS | Case-insensitive sub set of the POSIX character set that consists of all Unicode characters except for: " * / : < > ? \ | +. Note that names cannot end with a "." (dot) or " " (space) |
| 2 | FILE_NAME_DOS, DOS | Case-insensitive sub set of the WINDOWS character set that consists of all upper case ASCII characters except for: " * + , / : ; < = > ? \. Note that the name must follow the 8.3 format |
| 3 | DOS_WINDOWS | Both the DOS and WINDOWS names are identical, which is the same as the DOS character set, with the exception that lower case is used as well |
Note that the Windows API function CreateFile allows to create case-sensitive file names when the flag FILE_FLAG_POSIX_SEMANTICS is set.
Long to short name conversion
A short name can be determined from a long name with the following approach. In the long name:
- ignore Unicode characters beyond the first 8-bit (extended ASCII)
- ignore control characters and spaces (character < 0x20)
- ignore non-allowed characters
" * + , / : ; < = > ? \ - ignore dots except the last one, which is used for the extension
- make all letters upper case
Additional observations:
[or]are replaced by an underscore (_)
Make the name unique:
- use the characters 1 to 6 add ~1 and if the long name has an extension add the a dot and its first 3 letters, e.g. “Program Files” becomes “PROGRA~1” or “ ~PLAYMOVIE.REG“ becomes “~PLAYM~1.REG”
- if the name already exists try ~2 up to ~9, e.g. “Program Data”, in the same directory as “Program Files”, becomes “PROGRA~2”
- if the name already exists use a 16-bit hexadecimal value for characters 3 to 6 with ~1, e.g. “x86_microsoft-windows-r..ry-editor.resources_31bf3856ad364e35_6.0.6000.16386_en-us_f89a7b0005d42fd4” in a directory with a lot of file names starting with “x86_microsoft”, becomes “X8FCA6~1.163”
TODO: determine if the behavior is dependent on a setting that can be changed with fsutil
The volume version attribute
The volume version attribute ($VOLUME_VERSION) contains volume version.
TODO: complete section. Need a pre NTFS version 3.0 volume with this attribute. $AttrDef indicates the attribute to be 8 bytes in size.
The object identifier attribute
The object identifier attribute ($OBJECT_ID) contains distributed link tracker properties. It is stored as a resident MFT attribute.
The object identifier attribute data is either 16 or 64 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Droid file identifier, which contains a GUID | |
| 16 | 16 | Birth droid volume identifier, which contains a GUID | |
| 32 | 16 | Birth droid file identifier, which contains a GUID | |
| 48 | 16 | Birth droid domain identifier, which contains a GUID |
Droid in this context refers to CDomainRelativeObjId.
The security descriptor attribute
TODO: determine if this override any value in $Secure:$SDS?
The security descriptor attribute ($SECURITY_DESCRIPTOR) contains a Windows NT security descriptor. It can be stored as either a resident (for a small amount of data) and non-resident MFT attribute.
TODO: link to security descriptor format documentation
The volume name attribute
The volume name attribute ($VOLUME_NAME) contains the volume label. It is stored as a resident MFT attribute.
The volume name attribute data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | ... | Volume label, which contains an UCS-2 little-endian string without end-of-string character |
The volume name attribute is used in the $Volume metadata file MFT entry.
The volume information attribute
The volume information attribute ($VOLUME_INFORMATION) contains information about the volume. It is stored as a resident MFT attribute.
The volume information attribute data is 12 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Unknown | |
| 8 | 1 | Major format version | |
| 9 | 1 | Minor format version | |
| 10 | 2 | Volume flags |
The volume information attribute is used in the $Volume metadata file MFT entry.
Volume flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | VOLUME_IS_DIRTY | Is dirty |
| 0x0002 | VOLUME_RESIZE_LOG_FILE | Re-size journal ($LogFile) |
| 0x0004 | VOLUME_UPGRADE_ON_MOUNT | Upgrade on next mount |
| 0x0008 | VOLUME_MOUNTED_ON_NT4 | Mounted on Windows NT 4 |
| 0x0010 | VOLUME_DELETE_USN_UNDERWAY | Delete USN in progress |
| 0x0020 | VOLUME_REPAIR_OBJECT_ID | Repair object identifiers |
| 0x0080 | Unknown | |
| 0x4000 | VOLUME_CHKDSK_UNDERWAY | chkdsk in progress |
| 0x8000 | VOLUME_MODIFIED_BY_CHKDSK | Modified by chkdsk |
The data stream attribute
The data stream attribute ($DATA) contains the file data. It can be stored as either a resident (for a small amount of data) and non-resident MFT attribute.
Multiple data attributes for the same data stream can be used in the attribute list to define different parts of the data stream data. The first data stream attribute will contain the size of the entire data stream data. Other data stream attributes should have a size of 0. Also see attribute chains.
The index root attribute
The index root attribute ($INDEX_ROOT) contains the root of the index tree. It is stored as a resident MFT attribute.
Also see the index and the index root.
The index allocation attribute
The index allocation attribute ($INDEX_ALLOCATION) contains an array of index entries. It is stored as a non-resident MFT attribute.
The index allocation attribute itself does not define which attribute type it contains in the index value data. For this information it needs the corresponding index root attribute.
Multiple index allocation attributes for the same index can be used in the attribute list to define different parts of the index allocation data. The first index allocation attribute will contain the size of the entire index allocation data. Other index allocation attributes should have a size of 0. Also see attribute chains.
Also see the index.
The bitmap attribute
The bitmap attribute ($BITMAP) contains the allocation bitmap. It can be stored as either a resident (for a small amount of data) and non-resident MFT attribute.
It is used to maintain information about which entry is used and which is not. Every bit in the bitmap represents an entry. The index is stored byte-wise with the LSB of the byte corresponds to the first allocation element. The allocation element can represent different things:
- an MFT entry in the MFT (nameless) bitmap;
- an index entry in an index ($I30).
The allocation element is allocated if the corresponding bit contains 1 or unallocated if 0.
The symbolic link attribute
The symbolic link attribute ($SYMBOLIC_LINK) contains a symbolic link.
TODO: complete section. Need a pre NTFS version 3.0 volume with this attribute. $AttrDef indicates the attribute is of variable size.
The reparse point attribute
The reparse point attribute ($REPARSE_POINT) contains information about a file system-level link. It is stored as a resident MFT attribute.
Als see the reparse point.
The (HPFS) extended attribute information
The (HPFS) extended attribute information ($EA_INFORMATION) contains information about the extended attribute ($EA).
The extended attribute information data is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Size of an extended attribute entry | |
| 2 | 2 | Number of extended attributes which have the NEED_EA flag set | |
| 4 | 4 | Size of the extended attribute ($EA) data |
The (HPFS) extended attribute
The (HPFS) extended attribute ($EA) contains the extended attribute data.
The extended attribute data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Offset to next extended attribute entry, where the offset is relative from the start of the extended attribute data | |
| 4 | 1 | Extended attribute flags | |
| 5 | 1 | Number of characters of the extended attribute name | |
| 6 | 2 | Value data size | |
| 8 | ... | The extended attribute name, which contains an ASCII string | |
| ... | ... | Value data | |
| ... | ... | Unknown |
TODO: determine if the name is 2-byte aligned
Extended attribute flags
| Value | Identifier | Description |
|---|---|---|
| 0x80 | NEED_EA | Unknown (Need EA) flag |
TODO: determine what the NEED_EA flag is used for
UNITATTR extended attribute value data
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (equivalent of st_mode?) |
The property set attribute
The property set attribute ($PROPERTY_SET) contains a property set.
TODO: complete section. Need a pre NTFS version 3.0 volume with this attribute. $AttrDef does not seem to always define this attribute.
The logged utility stream attribute
TODO: complete section
| Value | Identifier | Description |
|---|---|---|
| $EFS | Encrypted NTFS (EFS) | |
| $TXF_DATA | Transactional NTFS (TxF) |
The attribute types
The attribute types are stored in the $AttrDef metadata file.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 128 | Attribute which contains an UCS-2 little-endian string with end-of-string character. Unused bytes are filled with 0-byte values | |
| 128 | 4 | Attribute type (or type code) | |
| 132 | 8 | Unknown | |
| 140 | 4 | Unknown (flags?) | |
| 144 | 8 | Unknown (minimum attribute size?) | |
| 152 | 8 | Unknown (maximum attribute size?) |
The index
The index structures are used for various purposes one of which are the directory entries.
The root of the index is stored in index root. The index root attribute defines which type of attribute is stored in the index and the root index node.
If the index is too large part of the index is stored in an index allocation attribute with the same attribute name. The index allocation attribute defines a data stream which contains index entries. Each index entry contains an index node.
An index consists of a tree, where both the branch and index leaf nodes contain the actual data. E.g. in case of a directory entries index, any node that contains index value data make up for the directory entries.
The index value data in a branch node signifies the upper bound of the values in the that specific branch. E.g. if directory entries index branch node contains the name “textfile.txt” all names in that index branch are smaller than “textfile.txt”.
Note the actual sorting order is dependent on the collation type defined in the index root attribute.
The index allocation attribute is accompanied by a bitmap attribute with the corresponding attribute name. The bitmap attribute defines the allocation of virtual cluster blocks within the index allocation attribute data stream. Every bit in the bitmap represents a block the size of an index entry.
Note that the index allocation attribute can be present even though it is not used.
Common used indexes
Indexes commonly used by NTFS are:
| Value | Identifier | Description |
|---|---|---|
| $I30 | Directory entries (used by directories) | |
| $SDH | Security descriptor hashes (used by $Secure) | |
| $SII | Security descriptor identifiers (used by $Secure) | |
| $O | Object identifiers (used by $ObjId) | |
| $O | Owner identifiers (used by $Quota) | |
| $Q | Quotas (used by $Quota) | |
| $R | Reparse points (used by $Reparse) |
The index root
The index root consists of:
- index root header
- index node header
- an array of index values
The index root header
The index root header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Attribute type, which contains the type of the indexed attribute or 0 if none | |
| 4 | 4 | Collation type, which contains a value to indicate the ordering of the index entries | |
| 8 | 4 | Index entry size | |
| 12 | 4 | Number of cluster blocks per index entry |
Note that for NTFS version 1.2 the index entry size does not have to match the index entry size in the volume header. The correct size seems to be the value in the index root header.
Collation type
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | COLLATION_BINARY | Binary, where the first byte is most significant |
| 0x00000001 | COLLATION_FILENAME | UCS-2 strings case-insensitive, where the case folding is stored in $UpCase |
| 0x00000002 | COLLATION_UNICODE_STRING | UCS-2 strings case-sensitive, where upper case letters should come first |
| 0x00000010 | COLLATION_NTOFS_ULONG | Unsigned 32-bit little-endian integer |
| 0x00000011 | COLLATION_NTOFS_SID | NT security identifier (SID) |
| 0x00000012 | COLLATION_NTOFS_SECURITY_HASH | Security hash first, then NT security identifier |
| 0x00000013 | COLLATION_NTOFS_ULONGS | An array of unsigned 32-bit little-endian integer values |
The index entry
The index entry consists of:
- the index entry header
- the index node header
- The fix-up values
- alignment padding (8-byte alignment), contains zero-bytes
- an array of index values
The index entry header
The index entry header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "INDX" | Signature |
| 4 | 2 | The fix-up values offset, which contains an offset relative from the start of the index entry header | |
| 6 | 2 | The number of fix-up values | |
| 8 | 8 | Metadata transaction journal sequence number, which contains a $LogFile Sequence Number (LSN) | |
| 16 | 8 | Virtual Cluster Number (VCN) of the index entry |
Note that there can be more fix-up values than supported by the index entry data size.
The index node header
The index node header is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Index values offset, where the offset is relative from the start of the index node header | |
| 4 | 4 | Index node size, where the value includes the size of the index node header | |
| 8 | 4 | Allocated index node size, where the value includes the size of the index node header | |
| 12 | 4 | Index node flags |
In an index entry (index allocation attribute) the index node size includes the size of the fix-up values and the alignment padding following it.
The remainder of the index node contains remnant data and/or zero-byte values.
The index node flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Is branch node, which is used to indicate if the node is a branch node that has sub nodes |
The index value
The index value is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | File reference | |
| 8 | 2 | Size, which includes the 10 bytes of the file reference and size | |
| 10 | 2 | Key data size | |
| 12 | 4 | Index value flags | |
| If index key data size > 0 | |||
| 16 | ... | Key data | |
| ... | ... | Data | |
| If index value flag 0x00000001 (is branch node) is set | |||
| ... | 8 | Sub node Virtual Cluster Number (VCN) | |
The index values are stored 8 byte aligned.
Note that some other sources define the index value flags as a 16-bit value followed by 2 bytes of padding.
The index value flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | Has sub node, when set the index value contains a sub node Virtual Cluster Number (VCN) | |
| 0x00000002 | Is last, when set the index value is the last in the index values array |
Index key and value data
Directory entry index value
The MFT attribute name of the directory entry index is: $I30.
The directory entry index value contains a file name attribute in the index key data.
Note that the index value data can contain remnant data.
The short and long names of the same file have a separate index values. The short name uses the DOS name space and the long name the WINDOWS name space. Index values with a single name use either the POSIX or DOS_WINDOWS name space.
A hard link to a file in the same directory has separate index values.
Security descriptor hash index value
The MFT attribute name of the security descriptor hash index is: $SDH. It appears to only to be used by the $Secure metadata file.
Also see the security descriptor hash index value.
Security descriptor identifier index value
The MFT attribute name of the security descriptor identifier index is: $SII. It appears to only to be used by the $Secure metadata file.
Also see the security descriptor identifier index value.
Compression
Compressed data-runs
NTFS compression groups 16 cluster blocks together. This group of 16 cluster blocks also named a compression unit, which is either “compressed” or uncompressed.
The term compressed is quoted here because the group of cluster blocks can also contain uncompressed data. A group of cluster blocks is “compressed” when it is compressed size is smaller than its uncompressed data size. Within a group of cluster blocks each of the 16 blocks is “compressed” individually.
The compression unit size is stored in the non-resident MFT attribute. The maximum uncompressed data size is always the cluster size (in most case 4096).
Note that a resident $DATA attribute with the compression type in the data flags is stored uncompressed.
The data runs in the $DATA attribute define cluster block ranges, e.g.
21 02 35 52
This data run defines 2 data blocks starting at block number 21045 followed by 14 sparse blocks. The total number of blocks in the compression unit is 16. Compressed data is stored in the first 2 blocks and the 14 sparse blocks are only there to make sure the data runs add up to the compression unit size. They do not define actual sparse data.
Another example:
21 40 37 52
This data run defines 64 data blocks starting at block number 21047. Since this data run is larger than the compression unit size the data is stored uncompressed.
If the data run was e.g. 60 data blocks followed by 4 sparse blocks the first 3 compression units (blocks 1 to 48) would be uncompressed and the last compression unit (blocks 49 to 64) would be compressed.
Also “sparse data” and “sparse compression unit” data runs can be mixed. If in the previous example the 60 data blocks would be followed by 20 sparse blocks the last compression unit (blocks 65 to 80) would be sparse.
A compression unit can consists of multiple compressed data runs, e.g. 1 data block followed by 4 data blocks followed by 11 sparse blocks. Data runs have been observed where the last data run size does not align with the compression unit size.
The sparse blocks data run can be stored in a subsequent attribute in an attribute chain and can be stored in multiple data runs.
NTFS compression stores the “compressed” data in blocks. Each block has a 2 byte block header.
The block is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Block size | |
| 2 | compressed data size | Uncompressed or LZNT1 compressed data |
The upper 4 bits of the block size are used as flags:
| Bit(s) | Description |
|---|---|
| 0 - 11 | Compressed data size |
| 12 - 14 | Unknown |
| 15 | Data is compressed |
TODO: link to LZNT1 documentation
Windows Overlay Filter (WOF) compressed data
A MFT entry that contains Windows Overlay Filter (WOF) compressed data has the following attributes:
- reparse point attribute with tag 0x80000017, which defines the compression method
- a nameless data attribute that is sparse and contains the uncompressed data size
- a data attribute named WofCompressedData that contains LZXPRESS Huffman or LZX compressed data
| Offset | Size | Value | Description |
|---|---|---|---|
| Chunk offset table | |||
| 0 | ... | Array of 32-bit of 64-bit compressed data chunk offsets, where the offset is relative from the start of the data chunks | |
| Data chunks | |||
| ... | ... | One or more compressed or uncompressed data chunks | |
Note that if the chunk size equals the size of the uncompressed data the chunk is stored (as-is) uncompressed.
The size of the chunk offset table is:
number of chunk offsets = uncompressed size / compression unit size
The offset of the first compressed data chunk is at the end of the chunk offset table and is not stored in the chunk offset table.
If the uncompressed size of a chunk is smaller than the compression unit size the chunk is stored uncompressed.
Also see Windows Overlay Filter (WOF) compression method.
The reparse point
The reparse point is used to create file system-level links. Reparse data is stored in the reparse point attribute. The reparse point data (REPARSE_DATA_BUFFER) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Reparse point tag | |
| 4 | 2 | Reparse data size | |
| 6 | 2 | 0 | Unknown (Reserved) |
| 8 | ... | Reparse data |
TODO: determine if non-native (Microsoft) reparse points are stored with their GUID
The reparse point tag
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 16 bits | Type | |
| 2.0 | 12 bits | Unknown (Reserved) | |
| 3.4 | 4 bits | Flags |
Reparse point tag flags
| Value | Identifier | Description |
|---|---|---|
| 0x1 | Unknown (Reserved) | |
| 0x2 | Is alias (Name surrogate bit), when this bit is set, the file or directory represents another named entity in the system | |
| 0x4 | Is high-latency media (Reserved) | |
| 0x8 | Is native (Microsoft-bit) |
Known reparse point tags
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | IO_REPARSE_TAG_RESERVED_ZERO | Unknown (Reserved) |
| 0x00000001 | IO_REPARSE_TAG_RESERVED_ONE | Unknown (Reserved) |
| 0x00000002 | IO_REPARSE_TAG_RESERVED_TWO | Unknown (Reserved) |
| 0x80000005 | IO_REPARSE_TAG_DRIVE_EXTENDER | Used by Home server drive extender |
| 0x80000006 | IO_REPARSE_TAG_HSM2 | Used by Hierarchical Storage Manager Product |
| 0x80000007 | IO_REPARSE_TAG_SIS | Used by single-instance storage (SIS) filter driver |
| 0x80000008 | IO_REPARSE_TAG_WIM | Used by the WIM Mount filter |
| 0x80000009 | IO_REPARSE_TAG_CSV | Used by Clustered Shared Volumes (CSV) version 1 |
| 0x8000000a | IO_REPARSE_TAG_DFS | Used by the Distributed File System (DFS) |
| 0x8000000b | IO_REPARSE_TAG_FILTER_MANAGER | Used by filter manager test harness |
| 0x80000012 | IO_REPARSE_TAG_DFSR | Used by the Distributed File System (DFS) |
| 0x80000013 | IO_REPARSE_TAG_DEDUP | Used by the Data Deduplication (Dedup) |
| 0x80000014 | IO_REPARSE_TAG_NFS | Used by the Network File System (NFS) |
| 0x80000015 | IO_REPARSE_TAG_FILE_PLACEHOLDER | Used by Windows Shell for placeholder files |
| 0x80000016 | IO_REPARSE_TAG_DFM | Used by Dynamic File filter |
| 0x80000017 | IO_REPARSE_TAG_WOF | Used by Windows Overlay Filter (WOF), for either WIMBoot or compression |
| 0x80000018 | IO_REPARSE_TAG_WCI | Used by Windows Container Isolation (WCI) |
| 0x8000001b | IO_REPARSE_TAG_APPEXECLINK | Used by Universal Windows Platform (UWP) packages to encode information that allows the application to be launched by CreateProcess |
| 0x8000001e | IO_REPARSE_TAG_STORAGE_SYNC | Used by the Azure File Sync (AFS) filter |
| 0x80000020 | IO_REPARSE_TAG_UNHANDLED | Used by Windows Container Isolation (WCI) |
| 0x80000021 | IO_REPARSE_TAG_ONEDRIVE | Unknown (Not used) |
| 0x80000023 | IO_REPARSE_TAG_AF_UNIX | Used by the Windows Subsystem for Linux (WSL) to represent a UNIX domain socket |
| 0x80000024 | IO_REPARSE_TAG_LX_FIFO | Used by the Windows Subsystem for Linux (WSL) to represent a UNIX FIFO (named pipe) |
| 0x80000025 | IO_REPARSE_TAG_LX_CHR | Used by the Windows Subsystem for Linux (WSL) to represent a UNIX character special file |
| 0x80000036 | IO_REPARSE_TAG_LX_BLK | Used by the Windows Subsystem for Linux (WSL) to represent a UNIX block special file |
| 0x9000001c | IO_REPARSE_TAG_PROJFS | Used by the Windows Projected File System filter, for files managed by a user mode provider such as VFS for Git |
| 0x90001018 | IO_REPARSE_TAG_WCI_1 | Used by Windows Container Isolation (WCI) |
| 0x9000101a | IO_REPARSE_TAG_CLOUD_1 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000201a | IO_REPARSE_TAG_CLOUD_2 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000301a | IO_REPARSE_TAG_CLOUD_3 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000401a | IO_REPARSE_TAG_CLOUD_4 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000501a | IO_REPARSE_TAG_CLOUD_5 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000601a | IO_REPARSE_TAG_CLOUD_6 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000701a | IO_REPARSE_TAG_CLOUD_7 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000801a | IO_REPARSE_TAG_CLOUD_8 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000901a | IO_REPARSE_TAG_CLOUD_9 | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000a01a | IO_REPARSE_TAG_CLOUD_A | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000b01a | IO_REPARSE_TAG_CLOUD_B | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000c01a | IO_REPARSE_TAG_CLOUD_C | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000d01a | IO_REPARSE_TAG_CLOUD_D | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000e01a | IO_REPARSE_TAG_CLOUD_E | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0x9000f01a | IO_REPARSE_TAG_CLOUD_F | Used by the Cloud Files filter, for files managed by a sync engine such as OneDrive |
| 0xa0000003 | IO_REPARSE_TAG_MOUNT_POINT | Junction (or mount point) |
| 0xa000000c | IO_REPARSE_TAG_SYMLINK | Symbolic link |
| 0xa0000010 | IO_REPARSE_TAG_IIS_CACHE | Used by Microsoft Internet Information Services (IIS) caching |
| 0xa0000019 | IO_REPARSE_TAG_GLOBAL_REPARSE | Used by NPFS to indicate a named pipe symbolic link from a server silo into the host silo |
| 0xa000001a | IO_REPARSE_TAG_CLOUD | Used by the Cloud Files filter, for files managed by a sync engine such as Microsoft OneDrive |
| 0xa000001d | IO_REPARSE_TAG_LX_SYMLINK | Used by the Windows Subsystem for Linux (WSL) to represent a UNIX symbolic link |
| 0xa000001f | IO_REPARSE_TAG_WCI_TOMBSTONE | Used by Windows Container Isolation (WCI) |
| 0xa0000022 | IO_REPARSE_TAG_PROJFS_TOMBSTONE | Used by the Windows Projected File System filter, for files managed by a user mode provider such as VFS for Git |
| 0xa0000027 | IO_REPARSE_TAG_WCI_LINK | Used by Windows Container Isolation (WCI) |
| 0xa0001027 | IO_REPARSE_TAG_WCI_LINK_1 | Used by Windows Container Isolation (WCI) |
| 0xc0000004 | IO_REPARSE_TAG_HSM | Used by Hierarchical Storage Manager Product |
| 0xc0000014 | IO_REPARSE_TAG_APPXSTRM | Unknown (Not used) |
Junction or mount point reparse data
A reparse point with tag IO_REPARSE_TAG_MOUNT_POINT (0xa0000003) contains junction or mount point reparse data. The junction or mount point reparse data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Substitute name offset, where the offset is relative from the start of the reparse name data | |
| 2 | 2 | Substitute name size in bytes, where the size of the end-of-string character is not included | |
| 4 | 2 | Display name offset, where the offset is relative from the start of the reparse name data | |
| 6 | 2 | Display name size in bytes, where the size of the end-of-string character is not included | |
| Reparse name data | |||
| 8 | ... | Substitute name, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | ... | Display name, which contains an UCS-2 little-endian string without end-of-string character | |
Note that it is currently unclear if the names contain an end-of-string character or if they are followed by alignment padding.
TODO: determine what character values like 0x0002 represent in the substitute name
00000010: 5c 00 3f 00 3f 00 02 00 43 00 3a 00 5c 00 55 00 \.?.?... C.:.\.U.
00000020: 73 00 65 00 72 00 73 00 5c 00 74 00 65 00 73 00 s.e.r.s. \.t.e.s.
00000030: 74 00 5c 00 44 00 6f 00 63 00 75 00 6d 00 65 00 t.\.D.o. c.u.m.e.
00000040: 6e 00 74 00 73 00 00 00 n.t.s...
Symbolic link reparse data
A reparse point with tag IO_REPARSE_TAG_SYMLINK (0xa000000c) contains symbolic link reparse data. The symbolic link reparse data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Substitute name offset, where the offset is relative from the start of the reparse name data | |
| 2 | 2 | Substitute name size in bytes | |
| 4 | 2 | Display name offset, where the offset is relative from the start of the reparse name data | |
| 6 | 2 | Display name size, in bytes | |
| 8 | 4 | Symbolic link flags | |
| Reparse name data | |||
| 12 | ... | Substitute name, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | ... | Display name, which contains an UCS-2 little-endian string without end-of-string character | |
Symbolic link flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | SYMLINK_FLAG_RELATIVE | The substitute name is a path name relative to the directory containing the symbolic link |
Windows Overlay Filter (WOF) reparse data
A reparse point with tag IO_REPARSE_TAG_WOF (0x80000017) contains Windows Overlay Filter (WOF) reparse data. The Windows Overlay Filter (WOF) reparse data is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| External provider information | |||
| 0 | 4 | 1 | Unknown (WOF version) |
| 4 | 4 | 2 | Unknown (WOF provider) |
| Internal provider information | |||
| 8 | 4 | 1 | Unknown (file information version) |
| 12 | 4 | Compression method | |
Windows Overlay Filter (WOF) compression method
| Value | Identifier | Description |
|---|---|---|
| 0 | LZXPRESS Huffman with 4k window (compression unit) | |
| 1 | LZX with 32k window (compression unit) | |
| 2 | LZXPRESS Huffman with 8k window (compression unit) | |
| 3 | LZXPRESS Huffman with 16k window (compression unit) |
TODO: link to LZXPRESS Huffman and LZX documentation
Windows Container Isolation (WCI) reparse data
A reparse point with tag IO_REPARSE_TAG_WCI (0x80000018) contains Windows Container Isolation (WCI) reparse data. The Windows Container Isolation (WCI) reparse data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | 1 | Version |
| 4 | 4 | 0 | Unknown (reserved) |
| 8 | 16 | Look-up identifier, which contains a GUID | |
| 24 | 2 | Name size in bytes | |
| 26 | ... | Name, which contains an UCS-2 little-endian string without end-of-string character |
The cluster block allocation bitmap
The metadata file $Bitmap contains the cluster block allocation bitmap.
Every bit in the allocation bitmap represents a block the size of the cluster block, where the LSB is the first bit in a byte.
TODO: describe what the $SRAT data stream is used for.
Access control
The $Secure metadata file contains the security descriptors used for access control.
| Type | Name | Description |
|---|---|---|
| Data | $SDS | Security descriptor data stream, which contains all the Security descriptors on the volume |
| Index | $SDH | Security descriptor hash index |
| Index | $SII | Security descriptor identifier index, which contains the mapping of the security descriptor identifier (in $STANDARD_INFORMATION) to the offset of the security descriptor data (in $Secure:$SDS) |
Security descriptor hash ($SDH) index
The security descriptor hash index value
| Offset | Size | Value | Description |
|---|---|---|---|
| Key data | |||
| 0 | 4 | Security descriptor hash | |
| 4 | 4 | Security descriptor identifier | |
| Value data | |||
| 8 | 4 | Security descriptor hash | |
| 12 | 4 | Security descriptor identifier | |
| 16 | 8 | Security descriptor data offset (in $SDS) | |
| 24 | 4 | Security descriptor data size (in $SDS) | |
| 28 | 4 | Unknown | |
Security descriptor identifier ($SII) index
The security descriptor identifier index value
| Offset | Size | Value | Description |
|---|---|---|---|
| Key data | |||
| 0 | 4 | Security descriptor identifier | |
| Value data | |||
| 4 | 4 | Security descriptor hash | |
| 8 | 4 | Security descriptor identifier | |
| 12 | 8 | Security descriptor data offset (in $SDS) | |
| 20 | 4 | Security descriptor data size (in $SDS) | |
TODO: describe the hash algorithm
Security descriptor ($SDS) data stream
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Security descriptor hash | |
| 4 | 4 | Security descriptor identifier | |
| 12 | 8 | Security descriptor data offset (in $SDS) | |
| 20 | 4 | Security descriptor data size (in $SDS) | |
| 24 | ... | Security descriptor data | |
| ... | ... | Alignment padding (2-byte alignment) |
TODO: link to security descriptor format documentation
The object identifiers
$ObjID:$O
| Offset | Size | Value | Description |
|---|---|---|---|
| Key data | |||
| 0 | 16 | File (or object) identifier, which contains a GUID | |
| Value data | |||
| 4 | 8 | File reference | |
| 12 | 16 | Birth droid volume identifier, which contains a GUID | |
| 28 | 16 | Birth droid file (or object) identifier, which contains a GUID | |
| 44 | 16 | Birth droid domain identifier, which contains a GUID | |
Metadata transaction journal (log file)
TODO: complete section
The metadata file $LogFile contains the metadata transaction journal and consists of:
- Log File Service restart page header
- The fix-up values
Log File service restart page header
The Log File service restart page header (LFS_RESTART_PAGE_HEADER) is 30 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| MULTI_SECTOR_HEADER | |||
| 0 | 4 | "CHKD", "RCRD", "RSTR" | Signature |
| 4 | 2 | The fix-up values (or update sequence array) offset, which contain an offset relative from the start of the restart page header | |
| 6 | 2 | The number of fix-up values (or update sequence array size) | |
| Common | |||
| 8 | 8 | Checkdisk last LSN | |
| 16 | 4 | System page size | |
| 20 | 4 | Log page size | |
| 24 | 2 | Restart offset | |
| 26 | 2 | Minor format version | |
| 28 | 2 | Major format version | |
Log File service restart page versions
| Major format version | Remarks |
|---|---|
| -1 | Beta Version |
| 0 | Transition |
| 1 | Update sequence support |
USN change journal
The metadata file $Extend$UsnJrnl contains the USN change journal. It is a sparse file in which NTFS stores records of changes to files and directories. Applications make use of the journal to respond to file and directory changes as they occur, like e.g. the Windows File Replication Service (FRS) and the Windows (Desktop) Search service.
The USN change journal consists of:
- the $UsnJrnl:$Max data stream, containing metadata like the maximum size of the journal
- the $UsnJrnl:$J data stream, containing the update (or change) entries. The $UsnJrnl:$J data stream is sparse.
USN change journal metadata
The USN change journal metadata is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Maximum size in bytes | |
| 8 | 8 | Allocation (size) delta in bytes | |
| 16 | 8 | Update (USN) journal identifier, which contains a FILETIME | |
| 24 | 8 | Unknown (empty) |
USN change journal entries
The $UsnJrnl:$J data stream consists of an array of USN change journal entries. The USN change journal entries are stored on a per block-basis and 8-byte aligned. Therefore the remainder of the block can contain 0-byte values.
TODO: describe journal block size
Once the stream reaches maximum size the earliest USN change journal entries are removed from the stream and replaced with a sparse data run.
USN change journal entry
The USN change journal entry (USN_RECORD_V2) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Entry (or record) size | |
| 4 | 2 | 2 | Major format version |
| 6 | 2 | 0 | Minor format version |
| 8 | 8 | File reference | |
| 16 | 8 | Parent file reference | |
| 24 | 8 | Update sequence number (USN), which contains the file offset of the USN change journal entry which is used as a unique identifier | |
| 32 | 8 | Update date and time, which contains a FILETIME | |
| 40 | 4 | Update reason flags | |
| 44 | 4 | Update source flags | |
| 48 | 4 | Security descriptor identifier, which contains the entry number in the security ID index ($Secure:$SII). Also see Access Control | |
| 52 | 4 | File attribute flags | |
| 56 | 2 | Name size in bytes | |
| 58 | 2 | Name offset, which is relative from the start of the USN change journal entry | |
| 60 | (name size) | Name, which contains an UCS-2 little-endian string without end-of-string character | |
| ... | ... | 0x00 | Unknown (Padding) |
Update reason flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | USN_REASON_DATA_OVERWRITE | The data in the file or directory is overwritten |
| 0x00000002 | USN_REASON_DATA_EXTEND | The file or directory is extended |
| 0x00000004 | USN_REASON_DATA_TRUNCATION | The file or directory is truncated |
| 0x00000010 | USN_REASON_NAMED_DATA_OVERWRITE | One or more named data streams ($DATA attributes) of file were overwritten |
| 0x00000020 | USN_REASON_NAMED_DATA_EXTEND | One or more named data streams ($DATA attributes) of file were extended |
| 0x00000040 | USN_REASON_NAMED_DATA_TRUNCATION | One or more named data streams ($DATA attributes) of a file were truncated |
| 0x00000100 | USN_REASON_FILE_CREATE | The file or directory was created |
| 0x00000200 | USN_REASON_FILE_DELETE | The file or directory was deleted |
| 0x00000400 | USN_REASON_EA_CHANGE | The extended attributes of the file were changed |
| 0x00000800 | USN_REASON_SECURITY_CHANGE | The access rights (security descriptor) of a file or directory were changed |
| 0x00001000 | USN_REASON_RENAME_OLD_NAME | The name changed, where the USN change journal entry contains the old name |
| 0x00002000 | USN_REASON_RENAME_NEW_NAME | The name changed, where the USN change journal entry contains the new name |
| 0x00004000 | USN_REASON_INDEXABLE_CHANGE | Content indexed status changed. The file attribute FILE_ATTRIBUTE_NOT_CONTENT_INDEXED was changed |
| 0x00008000 | USN_REASON_BASIC_INFO_CHANGE | Basic file or directory attributes changed. One or more file or directory attributes were changed e.g. read-only, hidden, system, archive, or sparse attribute, or one or more time stamps |
| 0x00010000 | USN_REASON_HARD_LINK_CHANGE | A hard link was created or deleted |
| 0x00020000 | USN_REASON_COMPRESSION_CHANGE | The file or directory was compressed or decompressed |
| 0x00040000 | USN_REASON_ENCRYPTION_CHANGE | The file or directory was encrypted or decrypted |
| 0x00080000 | USN_REASON_OBJECT_ID_CHANGE | The object identifier of a file or directory was changed |
| 0x00100000 | USN_REASON_REPARSE_POINT_CHANGE | The reparse point that in a file or directory was changed, or a reparse point was added to or deleted from a file or directory |
| 0x00200000 | USN_REASON_STREAM_CHANGE | A named data stream ($DATA attribute) is added to or removed from a file, or a named stream is renamed |
| 0x00400000 | USN_REASON_TRANSACTED_CHANGE | Unknown |
| 0x80000000 | USN_REASON_CLOSE | The file or directory was closed |
Update source flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | USN_SOURCE_DATA_MANAGEMENT | The operation added a private data stream to a file or directory. The modifications did not change the application data |
| 0x00000002 | USN_SOURCE_AUXILIARY_DATA | The operation was caused by the operating system. Although a write operation is performed on the item, the data was not changed |
| 0x00000004 | USN_SOURCE_REPLICATION_MANAGEMENT | The operation was caused by file replication |
Alternate data streams (ADS)
| Data stream name | Description |
|---|---|
| "♣BnhqlkugBim0elg1M1pt2tjdZe", "♣SummaryInformation", "{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}" | Used to store properties, where ♣ (black club) is Unicode character U+2663 |
| "{59828bbb-3f72-4c1b-a420-b51ad66eb5d3}.XPRESS" | Used during remote differential compression |
| "AFP_AfpInfo", "AFP_Resource" | Used to store Macintosh operating system property lists |
| "encryptable" | Used to store attributes relating to thumbnails in the thumbnails database |
| "favicon" | Used to store favorite icons for web pages |
| "ms-properties" | Used to store properties |
| "OECustomProperty" | Used to store custom properties related to email files |
| "Zone.Identifier" | Used to store the Internet Explorere URL security zone of the origin |
ms-properties
The ms-properties alternate data stream contains a Windows Serialized Property Store (SPS).
TODO: link to Windows Serialized Property Store (SPS) format documentation
Zone.Identifier
The Zone.Identifier alternate data stream contains ASCII text in the form:
[ZoneTransfer]
ZoneId=3
Where ZoneId refers to the Internet Explorer URL security zone of the origin.
Transactional NTFS (TxF)
As of Vista Transactional NTFS (TxF) was added.
In TxF the resource manager (RM) keeps track of transactional metadata and log files. The TxF related metadata files are stored in the metadata directory:
$Extend\$RmMetadata
Resource manager repair information
The resource manager repair information metadata file “$Extend$RmMetadata$Repair” consists of the following data streams:
- the default (unnamed) data stream
- the $Config data stream, contains the resource manager repair configuration information
TODO: determine the purpose of the default (unnamed) data stream
Resource manager repair configuration information
TODO: complete section
The $Repair:$Config data streams contains:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown | |
| 4 | 4 | Unknown |
Transactional NTFS (TxF) metadata directory
TODO: complete section
The transactional NTFS (TxF) metadata directory “$Extend$RmMetadata$Txf” is used to isolate files for delete or overwrite operations.
TxF Old Page Stream (TOPS) file
The TxF Old Page Stream (TOPS) file “$Extend$RmMetadata$TxfLog$Tops” consists of the following data streams:
- the default (unnamed) data stream, contains metadata about the resource manager, such as its GUID, its CLFS log policy, and the LSN at which recovery should start
- the $T data stream, contains the file data that is partially overwritten by a transaction as opposed to a full overwrite, which would move the file into the Transactional NTFS (TxF) metadata directory
TxF Old Page Stream (TOPS) metadata
TODO: complete section
The $Tops default (unnamed) data streams contains:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Unknown | |
| 2 | 2 | Size of TOPS metadata | |
| 4 | 4 | Unknown (Number of resource managers/streams?) | |
| 8 | 16 | Resource Manager (RM) identifier, which contains a GUID | |
| 24 | 8 | Unknown (empty) | |
| 32 | 8 | Base (or log start) LSN of TxFLog stream | |
| 40 | 8 | Unknown | |
| 48 | 8 | Last flushed LSN of TxFLog stream | |
| 56 | 8 | Unknown | |
| 64 | 8 | Unknown (empty) | |
| 72 | 8 | Unknown (Restart LSN?) | |
| 80 | 20 | Unknown |
TxF Old Page Stream (TOPS) file data
The $Tops:$T data streams contains the file data that is partially overwritten by a transaction. It consists of multiple pending transaction XML-documents.
TODO: describe start of each sector containing 0x0001
A pending transaction XML-document starts with an UTF-8 byte-order-mark. Is roughly contains the following data:
<?xml version='1.0' encoding='utf-8'?>
<PendingTransaction Version="2.0" Identifier="...">
<Transactions>
<Transaction TransactionId="...">
<Install Application="..., Culture=..., Version=..., PublicKeyToken=...,
ProcessorArchitecture=..., versionScope=..."
RefGuid="..."
RefIdentifier="..."
RefExtra="..."/>
...
</Transaction>
</Transactions>
<ChangeList>
<Change Family="..., Culture=..., PublicKeyToken=...,
ProcessorArchitecture=..., versionScope=..."
New="..."/>
...
</ChangeList>
<POQ>
<BeginTransaction id="..."/>
<CreateFile path="..."
fileAttribute="..."/>
<DeleteFile path="..."/>
<MoveFile source="..." destination="..."/>
<HardlinkFile source="..." destination="..."/>
<SetFileInformation path="..."
securityDescriptor="binary base64:..."
flags="..."/>
<CreateKey path="..."/>
<SetKeyValue path="..."
name="..."
type="..."
encoding="base64"
value="..."/>
<DeleteKeyValue path="..."
name="..."/>
...
</POQ>
<InstallerQueue Length="...">
<Action Installer="..."
Mode="..."
Phase="..."
Family="..., Culture=..., PublicKeyToken=...,
ProcessorArchitecture=..., versionScope=..."
Old="..."
New="..."/>
...
</InstallerQueue >
</PendingTransaction>
Transactional NTFS (TxF) Common Log File System (CLFS) files
TxF uses a Common Log File System (CLFS) log store and the logged utility stream attribute named $TXF_DATA.
TODO: link to CLFS format documentation
The base log file (BLF) of the TxF log store is:
$Extend\$RmMetadata\$TxfLog\TxfLog.blf
Commonly the corresponding container files are:
$Extend\$RmMetadata\$TxfLog\TxfLogContainer00000000000000000001
$Extend\$RmMetadata\$TxfLog\TxfLogContainer00000000000000000002
TxF uses a multiplexed log store which contains the following streams:
- the KtmLog stream used for Kernel Transaction Manager (KTM) metadata records
- TxfLog stream, which contains the TxF log records.
Transactional data logged utility stream attribute
The transactional data ($TXF_DATA) logged utility stream attribute is 56 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 6 | Unknown (remnant data) | |
| 6 | 8 | Resource manager root file reference, which contains an NTFS file reference that refers to the MFT | |
| 14 | 8 | Unknown (USN index?) | |
| 22 | 8 | File identifier (TxID), which contains a TxF file identifier | |
| 30 | 8 | Data LSN, which contains a CLFS LSN of file data transaction records | |
| 38 | 8 | Metadata LSN, which contains a CLFS LSN of file system metadata transaction records | |
| 46 | 8 | Directory index LSN, which contains a CLFS LSN of directory index transaction records | |
| 54 | 2 | Unknown (Flags?) |
Note that a single MFT entry can contain multiple Transactional data logged utility stream attributes.
Windows definitions
File attribute flags
The file attribute flags consist of the following values:
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | FILE_ATTRIBUTE_READONLY | Is read-only |
| 0x00000002 | FILE_ATTRIBUTE_HIDDEN | Is hidden |
| 0x00000004 | FILE_ATTRIBUTE_SYSTEM | Is a system file or directory |
| 0x00000008 | Is a volume label, which is not used by NTFS | |
| 0x00000010 | FILE_ATTRIBUTE_DIRECTORY | Is a directory, which is not used by NTFS |
| 0x00000020 | FILE_ATTRIBUTE_ARCHIVE | Should be archived |
| 0x00000040 | FILE_ATTRIBUTE_DEVICE | Is a device, which is not used by NTFS |
| 0x00000080 | FILE_ATTRIBUTE_NORMAL | Is normal file. Note that none of the other flags should be set |
| 0x00000100 | FILE_ATTRIBUTE_TEMPORARY | Is temporary |
| 0x00000200 | FILE_ATTRIBUTE_SPARSE_FILE | Is a sparse file |
| 0x00000400 | FILE_ATTRIBUTE_REPARSE_POINT | Is a reparse point or symbolic link |
| 0x00000800 | FILE_ATTRIBUTE_COMPRESSED | Is compressed |
| 0x00001000 | FILE_ATTRIBUTE_OFFLINE | Is offline. The data of the file is stored on an offline storage |
| 0x00002000 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | Do not index content. The content of the file or directory should not be indexed by the indexing service |
| 0x00004000 | FILE_ATTRIBUTE_ENCRYPTED | Is encrypted |
| 0x00008000 | Unknown (seen on Windows 95 FAT) | |
| 0x00010000 | FILE_ATTRIBUTE_VIRTUAL | Is virtual |
The following flags are mainly used in the file name attribute and sparsely in the standard information attribute. It could be that they have a different meaning in both types of attributes or that the standard information flags are not updated. For now the latter is assumed.
| Value | Identifier | Description |
|---|---|---|
| 0x10000000 | Unknown (Is directory or has $I30 index? Note that an $Extend directory without this flag has been observed) | |
| 0x20000000 | Is index view |
Format edge cases and corruption scenarios
Data steam with inconsistent data flags
An MFT entry contains an $ATTRIBUTE_LIST attribute that contains multiple $DATA attributes. The $DATA attributes define a LZNT1 compressed data stream though only the first $DATA attribute has the compressed data flag set.
MFT entry: 220 information:
Is allocated : true
File reference : 220-59
Base record file reference : Not set (0)
Journal sequence number : 51876429013
Number of attributes : 5
Attribute: 1
Type : $STANDARD_INFORMATION (0x00000010)
Creation time : Jun 05, 2019 06:56:26.032730300 UTC
Modification time : Oct 05, 2019 06:56:04.150940700 UTC
Access time : Oct 05, 2019 06:56:04.150940700 UTC
Entry modification time : Oct 05, 2019 06:56:04.150940700 UTC
Owner identifier : 0
Security descriptor identifier : 5862
Update sequence number : 11553149976
File attribute flags : 0x00000820
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Is compressed (FILE_ATTRIBUTE_COMPRESSED)
Attribute: 2
Type : $ATTRIBUTE_LIST (0x00000020)
Attribute: 3
Type : $FILE_NAME (0x00000030)
Parent file reference : 33996-57
Creation time : Jun 05, 2019 06:56:26.032730300 UTC
Modification time : Oct 05, 2019 06:56:03.510061800 UTC
Access time : Oct 05, 2019 06:56:03.510061800 UTC
Entry modification time : Oct 05, 2019 06:56:03.510061800 UTC
File attribute flags : 0x00000020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Namespace : POSIX (0)
Name : setupapi.dev.20191005_085603.log
Attribute: 4
Type : $DATA (0x00000080)
Data VCN range : 513 - 1103
Data flags : 0x0000
Attribute: 5
Type : $DATA (0x00000080)
Data VCN range : 0 - 512
Data size : 4487594 bytes
Data flags : 0x0001
Note that it is unclear if this is a format edge case or corruption scenario.
Directory entry with outdated file reference
The directory entry “\ProgramData\McAfee\Common Framework\Task\5.ini”
File entry:
Path : \ProgramData\McAfee\Common Framework\Task\5.ini
File reference : 51106-400
Name : 5.ini
Parent file reference : 65804-10
Size : 723
Creation time : Sep 16, 2011 20:47:54.561041200 UTC
Modification time : Apr 07, 2012 21:07:02.684060000 UTC
Access time : Apr 07, 2012 21:07:02.652810200 UTC
Entry modification time : Apr 07, 2012 21:07:02.684060000 UTC
File attribute flags : 0x00002020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Content should not be indexed (FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
The corresponding MFT entry:
MFT entry: 51106 information:
Is allocated : true
File reference : 51106-496
Base record file reference : Not set (0)
Journal sequence number : 0
Number of attributes : 3
Attribute: 1
Type : $STANDARD_INFORMATION (0x00000010)
Creation time : Sep 16, 2011 20:47:54.561041200 UTC
Modification time : Apr 07, 2012 21:07:02.684060000 UTC
Access time : Apr 07, 2012 21:07:02.652810200 UTC
Entry modification time : Apr 07, 2012 21:07:02.684060000 UTC
Owner identifier : 0
Security descriptor identifier : 1368
Update sequence number : 1947271600
File attribute flags : 0x00002020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Content should not be indexed (FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
Attribute: 2
Type : $FILE_NAME (0x00000030)
Parent file reference : 65804-10
Creation time : Sep 16, 2011 20:47:54.561041200 UTC
Modification time : Apr 07, 2012 21:07:02.652810200 UTC
Access time : Apr 07, 2012 21:07:02.652810200 UTC
Entry modification time : Apr 07, 2012 21:07:02.652810200 UTC
File attribute flags : 0x00002020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Content should not be indexed (FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
Namespace : DOS and Windows (3)
Name : 1.ini
Attribute: 3
Type : $DATA (0x00000080)
Data size : 723 bytes
Data flags : 0x0000
TODO: determine if $LogFile could be used to recover from this corruption scenario
Directory entry referencing MFT entry with base record
The directory entry “\System Volume Information\DFSR\Config\Replica_F68AA759-069D-48B1-BE6F-205F26A792D0.XML”.
File entry:
Path : \System Volume Information\DFSR\Config\Replica_F68AA759-069D-48B1-BE6F-205F26A792D0.XML
File reference : 77019-345
Name : Replica_F68AA759-069D-48B1-BE6F-205F26A792D0.XML
Parent file reference : 19780-3
Size : 0
The corresponding MFT entry:
MFT entry: 77019 information:
Is allocated : true
File reference : 77019-345
Base record file reference : 296-623
Journal sequence number : 24747653240
Number of attributes : 1
Attribute: 1
Attribute type : $DATA (0x00000080)
Data VCN range : 0 - 249
Data size : 1023546 bytes
Data flags : 0x0000
The parent MFT entry:
MFT entry: 296 information:
Is allocated : true
File reference : 296-623
Base record file reference : Not set (0)
Journal sequence number : 24747648692
Number of attributes : 4
Attribute: 1
Attribute type : $STANDARD_INFORMATION (0x00000010)
Creation time : Dec 05, 2009 20:39:34.765625000 UTC
Modification time : Apr 08, 2012 17:33:45.756367200 UTC
Access time : Apr 08, 2012 06:06:11.653828200 UTC
Entry modification time : Apr 08, 2012 17:33:45.756367200 UTC
Owner identifier : 0
Security descriptor identifier : 408
Update sequence number : 6855921000
File attribute flags : 0x00000020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Attribute: 2
Attribute type : $ATTRIBUTE_LIST (0x00000020)
Data size : 96 bytes
Number of entries : 3
Entry: 0 : $STANDARD_INFORMATION (0x00000010) in file reference: 296-623
Entry: 1 : $FILE_NAME (0x00000030) in file reference: 296-623
Entry: 2 : $DATA (0x00000080) in file reference: 77019-345
Attribute: 3
Attribute type : $FILE_NAME (0x00000030)
Parent file reference : 1371-1
Creation time : Dec 05, 2009 20:39:34.765625000 UTC
Modification time : Apr 08, 2012 06:06:11.653828200 UTC
Access time : Apr 08, 2012 06:06:11.653828200 UTC
Entry modification time : Apr 08, 2012 06:06:11.653828200 UTC
File attribute flags : 0x00000020
Should be archived (FILE_ATTRIBUTE_ARCHIVE)
Name space : DOS and Windows (3)
Name : winlogon.log
Attribute: 4
Attribute type : $DATA (0x00000080)
Data VCN range : 0 - 249
Data size : 1023546 bytes
Data flags : 0x0000
Note it is currently assumed this is a corruption scenario and the data is part of the attribute list of MFT entry 296 not of the file “Replica_F68AA759-069D-48B1-BE6F-205F26A792D0.XML”. The VOLUME_IS_DIRTY volume flag was not set.
LZNT1 compressed block with data size of 0
Not sure if this is a corruption scenario or a data format edge case.
A compression unit (index 30) consisting of the following data runs:
reading data run: 60.
data run:
00000000: 11 01 01 ...
value sizes : 1, 1
number of cluster blocks : 1 (size: 4096)
cluster block number : 687143 (1) (offset: 0xa7c27000)
reading data run: 61.
data run:
00000000: 01 0f ..
value sizes : 1, 0
number of cluster blocks : 15 (size: 61440)
cluster block number : 0 (0) (offset: 0x00000000)
Is sparse
Contains the following data:
a7c27000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
...
a7c27ff0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
This relates to an empty LZNT1 compressed block.
compressed data offset : 0 (0x00000000)
compression chunk header : 0x0000
compressed chunk size : 1
signature value : 0
is compressed flag : 0
It was observed in 2 differnt NTFS implementations that the entire block is filled with 0-byte values.
TODO: verify behavior of Windows NTFS implementation.
Truncated LZNT1 compressed block
Not sure if this is a corruption scenario or a data format edge case.
A compression unit (index 0) consisting of the following data runs:
reading data run: 0.
data run:
00000000: 31 08 48 d8 01 1.H..
value sizes : 1, 3
number of cluster blocks : 8 (size: 32768)
cluster block number : 120904 (120904) (offset: 0x1d848000)
reading data run: 1.
data run:
00000000: 01 08 ..
value sizes : 1, 0
number of cluster blocks : 8 (size: 32768)
cluster block number : 0 (0) (offset: 0x00000000)
Is sparse
Contains the following data:
1d848000 bd b7 50 44 46 50 00 01 00 01 00 40 e0 00 07 0b |..PDFP.....@....|
...
1d84c000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
1d84fff0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
This relates to a LZNT1 compressed block that appears to be truncated at offset 16384 (0x00004000).
compressed data offset : 16384 (0x00004000)
compression flag byte : 0x00
Different behavior was observed in 2 differnt NTFS implementations:
- one implementation fills the compressed block with the uncompressed data it could read and the rest with with 0-byte values
- another implementation seems to provide the data that was already in its buffer
TODO: verify behavior of Windows NTFS implementation.
References
- How NTFS Works, by Microsoft
- Master File Table, by Microsoft
- NTFS Attribute Types, by Microsoft
- File Attribute Constants, by Microsoft
- Reparse Tags, by Microsoft
- NTFS documentation, by Richard Russon
- ATTRIBUTE_LIST_ENTRY structure, by Microsoft
- ATTRIBUTE_RECORD_HEADER structure, by Microsoft
- FILE_RECORD_SEGMENT_HEADER structure, by Microsoft
- MULTI_SECTOR_HEADER structure, by Microsoft
- REPARSE_DATA_BUFFER structure (ntifs.h), by Microsoft
- REPARSE_DATA_BUFFER_EX structure (ntifs.h), by Microsoft
- USN_RECORD_V2, by Microsoft
- Zone.Identifier Stream Name, by Microsoft
- the Internet Explorer URL security zone, by Microsoft
- ntfs_layout.h, by Anton Altaparmakov
X File System (XFS)
The X File System (XFS) is a file system that originates from SGI but is used in various Linux distributions like RHEL. Some sources indicate that X was a a placeholder for a name that never given.
Overview
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | number of seconds since January 1, 1970 00:00:00 UTC (POSIX epoch) and fraction of second in number of nanoseconds, or in bigtime (number of nanoseconds since December 13, 1901 20:45:52 UTC) |
| Character strings | UTF-8 or a narrow character (Single Byte Character (SBC) or Multi Byte Character (MBC)) stored using a system defined codepage |
Terminology
Absolute and relative inode numbers
A relative inode number is an inode number used within a specific allocation group. An absolute inode number combines the allocation group number and the relative inode number.
number_of_relative_inode_number_bits = (
allocation_group_size_log2 + number_of_inodes_per_block_log2
)
absolute_inode_number = (
(allocation_group_number << number_of_relative_inode_number_bits) | relative_inode_number
)
File system block number
A relative block number is a block number relative to the start of an allocation group. A file system block number (xfs_fsblock_t) combines the allocation group number and the relative block number.
number_of_relative_block_number_bits = allocation_group_size_log2
file_system_block_number = (
(allocation_group_number << number_of_relative_block_number_bits) | relative_block_number
)
file_offset = (allocation_group_block_number + relative_block_number) * block_size
The allocation group
An allocation group consists of:
- a sector containing a superblock
- a sector containing free block information
- a sector containing inode tree information
- a sector containing internal free list
- blocks containing
- root of the inode tree
- root of the free space B+ tree
- free list
- inodes table
The superblock
The XFS superblock (xfs_sb_t) is (at least) 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "XFSB" | Signature |
| 4 | 4 | Block size, which is typicaly 4096 bytes (4 KiB) and can range from 512 to 65536 bytes | |
| 8 | 8 | Total number of blocks | |
| 16 | 8 | Number of real-time (device) blocks | |
| 24 | 8 | Number of real-time (device) extents | |
| 32 | 16 | File system (or volume) identifier, which contains an UUID | |
| 48 | 8 | Journal block number, which contains a file system block number or 0 if the journal is stored on a separate device | |
| 56 | 8 | Root directory (absolute) inode number, which contains -1 (0xffffffffffffffff) if not set | |
| 64 | 8 | Real-time bitmap extents inode number, which contains -1 (0xffffffffffffffff) if not set | |
| 72 | 8 | Real-time bitmap summary inode number, which contains -1 (0xffffffffffffffff) if not set | |
| 80 | 4 | Real-time extent size, in number of blocks | |
| 84 | 4 | Allocation group size, in number of blocks | |
| 88 | 4 | Number of allocation groups | |
| 92 | 4 | Real-time bitmap size, in number of blocks | |
| 96 | 4 | Journal size, in number of blocks | |
| 100 | 2 | Format version and feature flags | |
| 102 | 2 | Sector size (in bytes) | |
| 104 | 2 | Inode size (in bytes), which can range from 256 to 2048 | |
| 106 | 2 | Number of inodes per block | |
| 108 | 12 | Volume label (or name) | |
| 120 | 1 | Block size in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 121 | 1 | Sector size in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 122 | 1 | Inode size in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 123 | 1 | Number of inodes per block in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 124 | 1 | Allocation group size in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 125 | 1 | Number of real-time (device) extents in log2, where value = ( 2 ^ value in log2 ) or 0 if value in log2 is 0 | |
| 126 | 1 | Creation flag, which contains a value to indicate file system is being created | |
| 127 | 1 | Inodes percentage, which contains the percentage of the maximum space of the volume to use for inodes | |
| Only used in the first superblock | |||
| 128 | 8 | Number of inodes | |
| 136 | 8 | Number of free inodes | |
| 144 | 8 | Number of free data blocks | |
| 152 | 8 | Number of free real-time extents | |
| Only used if the XFS_SB_VERSION_QUOTABIT feature flag is set | |||
| 160 | 8 | User quota inode number | |
| 168 | 8 | Group (or project) quota inode number | |
| 176 | 2 | Quota flags | |
| Common | |||
| 178 | 1 | Miscellaneous flags | |
| 179 | 1 | 0 | Unknown (reserved or shared version number) |
| Only used if the XFS_SB_VERSION_ALIGNBIT feature flag is set | |||
| 180 | 4 | Inode chunk alignment size, in number of blocks | |
| Common | |||
| 184 | 4 | Stripe (or RAID) unit size, in number of blocks | |
| 188 | 4 | Stripe (or RAID) width, in number of blocks | |
| 192 | 1 | Directory block size in log2, in number of blocks | |
| 193 | 1 | Journal device sector size in log2 | |
| 194 | 2 | Journal device sector size (in bytes) | |
| Only used if the XFS_SB_VERSION_LOGV2BIT feature flag is set | |||
| 196 | 4 | Journal device stripe or RAID unit size | |
| Common | |||
| 200 | 4 | Secondary feature flags | |
| 204 | 4 | Copy of secondary feature flags, which was introduced to work-around 64-bit alignment errors | |
| If superblock format version >= 5 (XFS_SB_VERSION_5) | |||
| 208 | 4 | (Read-write) compatible feature flags | |
| 212 | 4 | Read-only compatible feature flags | |
| 216 | 4 | (Read-write) incompatible feature flags | |
| 220 | 4 | Journal (read-write) incompatible feature flags | |
| 224 | 4 | Checksum of the superblock | |
| 228 | 4 | Unknown (Sparse inode chunk alignment in number of blocks) | |
| 232 | 4 | Project quota inode number | |
| 236 | 8 | Journal log sequence number (LSN) of the last superblock update | |
| Only used if the XFS_SB_FEAT_INCOMPAT_META_UUID incompatible feature flag is set | |||
| 244 | 16 | Metadata identifier, which contains an UUID | |
| Only used if the XFS_SB_FEAT_RO_COMPAT_RMAPBT incompatible feature flag is set | |||
| 260 | 8 | Real-time Reverse Mapping B+tree inode number | |
| 268 | 244 | Unknown (empty values) | |
Note that the allocation group size and allocation group size in log2 are not necessarily equivalent.
Format version and feature flags
The 4 LSB contain the version the remaining bits are used to store feature flags.
| Version | Identifier | Introduced in |
|---|---|---|
| First generation | ||
| 1 | XFS_SB_VERSION_1 | Introduced in Irix 5.3 |
| 2 | XFS_SB_VERSION_2 | Introduced in Irix 6.2, added extended attribute support |
| 3 | XFS_SB_VERSION_3 | Introduced in Irix 6.2, added inode version 2 support |
| Second generation | ||
| 4 | XFS_SB_VERSION_4 | Introduced in Irix 6.2, added directory version 2 support |
| Third generation | ||
| 5 | XFS_SB_VERSION_5 | Introduced in Linux 3.10 |
| Value | Identifier | Description |
|---|---|---|
| Introduced in XFS_SB_VERSION_2 | ||
| 0x0010 | XFS_SB_VERSION_ATTRBIT | Inodes support extended attributes |
| Introduced in XFS_SB_VERSION_3 | ||
| 0x0020 | XFS_SB_VERSION_NLINKBIT | Inodes use a 32-bit number of links value |
| Introduced in XFS_SB_VERSION_4 | ||
| 0x0040 | XFS_SB_VERSION_QUOTABIT | Quotas enabled |
| 0x0080 | XFS_SB_VERSION_ALIGNBIT | Use inode chunk alignment |
| 0x0100 | XFS_SB_VERSION_DALIGNBIT | Has underlying stripe or RAID. The Stripe (or RAID) unit size and width values in the superblock should be set |
| 0x0200 | XFS_SB_VERSION_SHAREDBIT | Unknown (set if reserved shared version is used) |
| 0x0400 | XFS_SB_VERSION_LOGV2BIT | Has version 2 journaling logs |
| 0x0800 | XFS_SB_VERSION_SECTORBIT | Sector size is not 512 bytes |
| 0x1000 | XFS_SB_VERSION_EXTFLGBIT | Unwritten extents are used, which should always be set |
| 0x2000 | XFS_SB_VERSION_DIRV2BIT | Version 2 directories are used |
| 0x4000 | XFS_SB_VERSION_BORGBIT | Unknown (ASCII only case-insensitive) |
| 0x8000 | XFS_SB_VERSION_MOREBITSBIT | Secondary feature flags are used |
Secondary feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | XFS_SB_VERSION2_RESERVED1BIT | Unknown (reserved) |
| 0x00000002 | XFS_SB_VERSION2_LAZYSBCOUNTBIT | Has lazy global counters. Free space and inode values are only tracked in the primary superblock |
| 0x00000004 | XFS_SB_VERSION2_RESERVED4BIT | Unknown (reserved) |
| 0x00000008 | XFS_SB_VERSION2_ATTR2BIT | Version 2 extended attributes are used |
| 0x00000010 | XFS_SB_VERSION2_PARENTBIT | Inodes have a parent pointer |
| 0x00000080 | XFS_SB_VERSION2_PROJID32BIT | Has 32-bit project identifiers |
| 0x00000100 | XFS_SB_VERSION2_CRCBIT | Has metadata checksums |
| 0x00000200 | XFS_SB_VERSION2_FTYPE | Directory entries contain a file type |
Miscellaneous flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | XFS_SBF_READONLY | Read-only file system |
Quota flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | XFS_UQUOTA_ACCT | User quota accounting is enabled |
| 0x0002 | XFS_UQUOTA_ENFD | User quotas are enforced |
| 0x0004 | XFS_UQUOTA_CHKD | User quotas have been checked and updated on disk |
| 0x0008 | XFS_PQUOTA_ACCT | Project quota accounting is enabled |
| 0x0010 | XFS_OQUOTA_ENFD | Other (group/project) quotas are enforced |
| 0x0020 | XFS_OQUOTA_CHKD | Other (group/project) quotas have been checked |
| 0x0040 | XFS_GQUOTA_ACCT | Group quota accounting is enabled |
| 0x0080 | XFS_GQUOTA_ENFD | Group quotas are enforced |
| 0x0100 | XFS_GQUOTA_CHKD | Group quotas have been checked |
| 0x0200 | XFS_PQUOTA_ENFD | Project quotas are enforced |
| 0x0400 | XFS_PQUOTA_CHKD | Project quotas have been checked |
Compatible feature flags
Currently no compatible feature flags are defined.
Read-only compatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | XFS_SB_FEAT_RO_COMPAT_FINOBT | Has free inode btree |
| 0x00000002 | XFS_SB_FEAT_RO_COMPAT_RMAPBT | Has reverse map btree |
| 0x00000004 | XFS_SB_FEAT_RO_COMPAT_REFLINK | Has reflinked files |
| 0x00000008 | XFS_SB_FEAT_RO_COMPAT_INOBTCNT | Has inobt block counts |
Incompatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | XFS_SB_FEAT_INCOMPAT_FTYPE | Has filetype in dirent |
| 0x00000002 | XFS_SB_FEAT_INCOMPAT_SPINODES | Has sparse inode chunks |
| 0x00000004 | XFS_SB_FEAT_INCOMPAT_META_UUID | Use a metadata identifier |
| 0x00000008 | XFS_SB_FEAT_INCOMPAT_BIGTIME | Inode (v3) contains bigtime timestamps |
| 0x00000010 | XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | Needs repair |
| 0x00000020 | XFS_SB_FEAT_INCOMPAT_NREXT64 | Inode (v3) contains a 64-bit number of data extents and 32-bit number of (extended) attribute extent values |
| 0x00000040 | XFS_SB_FEAT_INCOMPAT_EXCHRANGE | Has exchangerange |
| 0x00000080 | XFS_SB_FEAT_INCOMPAT_PARENT | Has parent directory reference attributes |
| 0x00000100 | XFS_SB_FEAT_INCOMPAT_METADIR | Has metadata directory (tree) |
| 0x00000200 | XFS_SB_FEAT_INCOMPAT_ZONED | Has zoned RT allocator |
| 0x00000400 | XFS_SB_FEAT_INCOMPAT_ZONE_GAPS | RTGs have LBA gaps |
Journal incompatible feature flags
Currently no journal incompatible feature flags are defined.
Free block information
The free block information stores references:
- the block offset B+ tree, that tracks the free space by block number
- the block count B+ tree, that tracks the size of the free space block
The free block information (xfs_agf_t) is 64 or 224 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "XAGF" | Signature |
| 4 | 4 | 1 | Version |
| 8 | 4 | Sequence number, which contains the allocation group number of the corresponding sector | |
| 12 | 4 | Unknown (Allocation group size), in number of blocks | |
| 16 | 4 | Free space counts B+ tree root block number | |
| 20 | 4 | Free space sizes B+ tree root block number | |
| 24 | 4 | Unknown (reserved) | |
| 28 | 4 | Free space counts B+ tree height/depth | |
| 32 | 4 | Free space sizes B+ tree height/depth | |
| 36 | 4 | Unknown (reserved) | |
| 40 | 4 | Index of the first "free list" block | |
| 44 | 4 | Index of the last "free list" block | |
| 48 | 4 | "Free list" size, in number of blocks | |
| 52 | 4 | Number of free blocks in the allocation group | |
| 56 | 4 | Longest contiguous free space in the allocation group, in number of blocks | |
| Only used if the XFS_SB_VERSION2_LAZYSBCOUNTBIT feature flag is set | |||
| 60 | 4 | Number of blocks used for the free space B+ trees | |
| If superblock format version >= 5 (XFS_SB_VERSION_5) | |||
| 64 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 80 | 4 | Unknown (Size of the reverse mapping B+ tree in blocks) | |
| 84 | 4 | Unknown (Size of the reference count B+ tree in blocks) | |
| 88 | 4 | Reverse mapping B+ tree root block number, which contains a block number relative to the start of the allocation group | |
| 92 | 4 | Reference count B+ tree root block number, which contains a block number relative to the start of the allocation group | |
| 96 | 14 x 8 | Unknown (reserved) | |
| 208 | 8 | Log sequence number | |
| 216 | 4 | Unknown (Checksum of the free sector) | |
| 220 | 4 | Unknown (reserved) | |
Free list
A free list consists of:
- As of version 5, free list header
- Array of free block numbers
Free list header
The free list header is 36 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "AGFL" | Signature |
| 4 | 4 | Sequence number, which contains the allocation group number of the corresponding sector | |
| 8 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 24 | 8 | Log sequence number | |
| 32 | 4 | Checksum |
TODO: describe sb_uuid or sb_meta_uuid
Inode information
The inode information (xfs_agi_t) is (at least) 512 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "XAGI" | Signature |
| 4 | 4 | 1 | Version |
| 8 | 4 | Sequence number, which contains the allocation group number of the corresponding sector | |
| 12 | 4 | Unknown (Allocation group size), in number of blocks | |
| 16 | 4 | Number of inodes in the allocation group | |
| 20 | 4 | Inode tree root block number, which contains a block number relative to the start of the allocation group | |
| 24 | 4 | Inode tree height/depth | |
| 28 | 4 | Number of unused (free) inodes in the allocation group | |
| 32 | 4 | First inode number of the last allocated inode chunk, which contains an inode number relative to the allocation group | |
| 36 | 4 | -1 (0xffffffff) | Unknown |
| 40 | 64 x 4 | Hash table of 32-bit unlinked (deleted) inode numbers that are still being referenced, which contains -1 (0xffffffff) if not set | |
| If superblock format version >= 5 (XFS_SB_VERSION_5) | |||
| 296 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 312 | 4 | Checksum | |
| 316 | 4 | Unknown (padding) | |
| 320 | 8 | Log sequence number | |
| 328 | 4 | Free inode tree root block number, which contains a block number relative to the start of the allocation group | |
| 332 | 4 | Free inode tree height/depth | |
| 336 | 4 | Unknown | |
| 340 | 4 | Unknown | |
| 344 | 168 | Unknown (empty values) | |
B+ tree
XFS uses B+ trees to store various types of information. There are 2 different types of B+ trees, namely:
- Free space block B+ tree
- Inode tree
- Reference count B+ tree
B+ tree block
A B+ tree block consists of:
- B+ tree block header
- Array of branch or leaf block records
B+ tree block header
B+ tree block header 32-bit
The B+ tree block header 32-bit (xfs_btree_sblock_t or xfs_btree_iblock_t) is 16 or 56 bytes of size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Signature | |
| 4 | 2 | Level (or depth/height), which contains 0 for a leaf block | |
| 6 | 2 | Number of records | |
| 8 | 4 | Previous B+ tree block number, which is relative to the start of the allocation group or contains -1 (0xffffffff) if not set | |
| 12 | 4 | Next B+ tree block number, which is relative to the start of the allocation group or contains -1 (0xffffffff) if not set | |
| If superblock format version >= 5 (XFS_SB_VERSION_5) | |||
| 16 | 8 | Block number | |
| 24 | 8 | Log sequence number | |
| 32 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 48 | 4 | Owner allocation group, which contains the allocation group the block is part of | |
| 52 | 4 | Checksum | |
B+ tree block header 64-bit
The B+ tree block header 64-bit (xfs_btree_lblock_t) is 24 or 72 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Signature | |
| 4 | 2 | Level (or depth/height), where 0 represents a leaf block | |
| 6 | 2 | Number of records | |
| 8 | 8 | Previous B+ tree block number, which is relative to the start of the allocation group or contains -1 (0xffffffffffffffff) if not set | |
| 16 | 8 | Next B+ tree block number, which is relative to the start of the allocation group or contains -1 (0xffffffffffffffff) if not set | |
| If superblock format version >= 5 (XFS_SB_VERSION_5) | |||
| 24 | 8 | Block number | |
| 32 | 8 | Log sequence number | |
| 40 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 56 | 8 | Owner allocation group, which contains the allocation group the block is part of | |
| 64 | 4 | Checksum | |
| 68 | 4 | 0 | Unknown (padding) |
B+ tree block extended header
TODO: complete section
TODO: determine where this is defined, it seems to be represented in the examples.
B+ tree block header signatures
| Signature | Description |
|---|---|
| "AB3B" | Free space block offset B+ tree (file system version 5) |
| "AB3C" | Free space block count B+ tree (file system version 5) |
| "ABTB" | Free space block offset B+ tree |
| "ABTC" | Free space block count B+ tree |
| "FIB3" | Free inode B+tree (file system version 5) |
| "FIBT" | Free inode B+tree |
| "IAB3" | (Allocated) inode B+tree (file system version 5) |
| "IABT" | (Allocated) inode B+tree |
| "R3FC" | Reference count B+ tree (file system version 5) |
Free space B+ tree
TODO: complete section
Free space B+ tree branch node record
The free space B+ tree branch node record (xfs_alloc_ptr_t) is 4 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown |
Free space B+ tree leaf node record
The free space B+ tree leaf node record (xfs_alloc_key_t) is 8 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (ar_startblock) | |
| 4 | 4 | Unknown (ar_blockcount) |
Inode tree
The inode tree uses the B+ tree block header 32-bit.
Inode tree branch node
The inode tree branch node consists of:
- node header
- array of inode tree branch node entry keys
- array of inode tree branch node entry values
The number of key-value pairs is calculated as following:
number_of_key_value_pairs = node_records_data_size / 8
Inode tree branch node key
The inode tree branch node key (xfs_inobt_key_t) is 4 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of the first inode in the branch, which contains an inode number relative to the allocation group |
Note that the inode number of the last key can be 0.
Inode tree branch node value
The inode tree branch node key is 4 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Block number of the inode tree sub node, which contains a block number relative to the start of the allocation group |
Inode tree leaf node
The inode tree branch node consists of:
- node header
- array of inode tree leaf node entry records
Inode tree leaf node record
The inode tree leaf node record (xfs_inobt_rec_t) is 16 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of the first inode of the inode chunk, which contains an inode number relative to the allocation group | |
| 4 | 4 | Number of unused (free) inodes of the inode chunk | |
| 8 | 8 | Inode chunk allocation bitmap, which tracks which inodes of the inode chunk are unused (free) |
The inode chunk is a group of 64 inodes. The file offset of the inode chunk is calculated as following:
file_offset = allocation_group_file_offset + (inode_number * inode_size)
Inode
The inode can be followed by:
- data fork (descriptor)
- device identifier (fork type is XFS_DINODE_FMT_DEV)
- inline data fork (fork type is XFS_DINODE_FMT_LOCAL)
- extents list data fork (fork type is XFS_DINODE_FMT_EXTENTS)
- extents tree data fork (fork type is XFS_DINODE_FMT_BTREE)
- optional (extended) attributes data fork (descriptor)
- inline attributes fork (fork type is XFS_DINODE_FMT_LOCAL)
- extents list attributes fork (fork type is XFS_DINODE_FMT_EXTENTS)
- extents tree attributes fork (fork type is XFS_DINODE_FMT_BTREE)
Inode version 1
The inode version 1 (xfs_dinode_core_t) is 100 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "IN" | Signature |
| 2 | 2 | File mode, which contains file type and permissions | |
| 4 | 1 | 1 | Format version |
| 5 | 1 | (Data) fork type | |
| 6 | 2 | Number of links | |
| 8 | 4 | Owner (or user) identifier (UID) | |
| 12 | 4 | Group identifier (GID) | |
| 16 | 14 | 0 | Unknown (contains data in XFS_SB_VERSION_1) |
| 30 | 2 | Flush counter, which contains a value that is incremented on flush | |
| 32 | 4 | (last) access time, which contains a POSIX timestamp in seconds | |
| 36 | 4 | (last) access time fraction of second, which contains number of nanoseconds | |
| 40 | 4 | (last) modification time, which contains a POSIX timestamp in seconds | |
| 44 | 4 | (last) modification time fraction of second, which contains number of nanoseconds | |
| 48 | 4 | (last) inode change time, which contains a POSIX timestamp in seconds | |
| 52 | 4 | (last) inode change time fraction of second, which contains number of nanoseconds | |
| 56 | 8 | (Data) size | |
| 64 | 8 | Number of (data) blocks | |
| 72 | 4 | Extent size | |
| 76 | 4 | Number of data extents | |
| 80 | 2 | Number of (extended) attributes extents, which can contain 0 if an attributes fork of type XFS_DINODE_FMT_EXTENTS is empty | |
| 82 | 1 | (Extended) attributes fork descriptor offset, which contains an offset (value x 8) relative to the end of the inode | |
| 83 | 1 | (Extended) attributes fork type | |
| 84 | 4 | Unknown (DMAPI event mask) | |
| 88 | 2 | Unknown (DMAPI state) | |
| 90 | 2 | Inode flags | |
| 92 | 4 | Generation number | |
| Non-inode core field | |||
| 96 | 4 | Unknown (next unlinked inode), which contains -1 (0xffffffff) if not set | |
Inode version 2
The inode version 2 (xfs_dinode_core_t) is 100 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "IN" | Signature |
| 2 | 2 | File mode, which contains file type and permissions | |
| 4 | 1 | 2 | Format version |
| 5 | 1 | (Data) fork type | |
| 6 | 2 | Unknown | |
| 8 | 4 | Owner (or user) identifier (UID) | |
| 12 | 4 | Group identifier (GID) | |
| 16 | 4 | Number of links | |
| 20 | 2 | Project identifier | |
| 22 | 8 | 0 | Unknown (padding) |
| 30 | 2 | Flush counter, which contains a value that is incremented on flush | |
| 32 | 4 | (last) access time, which contains a POSIX timestamp in seconds | |
| 36 | 4 | (last) access time fraction of second, which contains number of nanoseconds | |
| 40 | 4 | (last) modification time, which contains a POSIX timestamp in seconds | |
| 44 | 4 | (last) modification time fraction of second, which contains number of nanoseconds | |
| 48 | 4 | (last) inode change time, which contains a POSIX timestamp in seconds | |
| 52 | 4 | (last) inode change time fraction of second, which contains number of nanoseconds | |
| 56 | 8 | (Data) size | |
| 64 | 8 | Number of (data) blocks | |
| 72 | 4 | Extent size | |
| 76 | 4 | Number of data extents | |
| 80 | 2 | Number of (extended) attributes extents, which can contain 0 if an attributes fork of type XFS_DINODE_FMT_EXTENTS is empty | |
| 82 | 1 | (Extended) attributes fork descriptor offset, which contains an offset (value x 8) relative to the end of the inode | |
| 83 | 1 | (Extended) attributes fork type | |
| 84 | 4 | Unknown (DMAPI event mask) | |
| 88 | 2 | Unknown (DMAPI state) | |
| 90 | 2 | Inode flags | |
| 92 | 4 | Generation number | |
| Non-inode core field | |||
| 96 | 4 | Unknown (next unlinked inode), which contains -1 (0xffffffff) if not set | |
Inode version 3
The inode version 3 (xfs_dinode_core_t) is 176 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | "IN" | Signature |
| 2 | 2 | File mode, which contains file type and permissions | |
| 4 | 1 | 3 | Format version |
| 5 | 1 | (Data) fork type | |
| 6 | 2 | Unknown | |
| 8 | 4 | Owner (or user) identifier (UID) | |
| 12 | 4 | Group identifier (GID) | |
| 16 | 4 | Number of links | |
| 20 | 2 | Project identifier lower 16-bit | |
| 22 | 2 | Project identifier upper 16-bit | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_NREXT64 not is set | |||
| 24 | 8 | 0 | Unknown (padding) |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_NREXT64 is set | |||
| 24 | 8 | Number of data extents (64-bit) | |
| Common | |||
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_BIGTIME not is set | |||
| 32 | 4 | (last) access time, which contains a POSIX timestamp in seconds | |
| 36 | 4 | (last) access time fraction of second, which contains number of nanoseconds | |
| 40 | 4 | (last) modification time, which contains a POSIX timestamp in seconds | |
| 44 | 4 | (last) modification time fraction of second, which contains number of nanoseconds | |
| 48 | 4 | (last) inode change time, which contains a POSIX timestamp in seconds | |
| 52 | 4 | (last) inode change time fraction of second, which contains number of nanoseconds | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_BIGTIME is set | |||
| 32 | 8 | (last) access time, which contains a bigtime timestamp | |
| 40 | 8 | (last) modification time, which contains a bigtime timestamp | |
| 48 | 8 | (last) inode change time, which contains a bigtime timestamp | |
| Common | |||
| 56 | 8 | (Data) size | |
| 64 | 8 | Number of (data) blocks | |
| 72 | 4 | Extent size | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_NREXT64 not is set | |||
| 76 | 4 | Number of data extents | |
| 80 | 2 | Number of (extended) attributes extents, which can contain 0 if an attributes fork of type XFS_DINODE_FMT_EXTENTS is empty | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_NREXT64 is set | |||
| 76 | 4 | Number of (extended) attributes extents (32-bit), which can contain 0 if an attributes fork of type XFS_DINODE_FMT_EXTENTS is empty | |
| 80 | 2 | Unknown (padding) | |
| Common | |||
| 82 | 1 | (Extended) attributes fork descriptor offset, which contains an offset (value x 8) relative to the end of the inode | |
| 83 | 1 | (Extended) attributes fork type | |
| 84 | 4 | Unknown (DMAPI event mask) | |
| 88 | 2 | Unknown (DMAPI state) | |
| 90 | 2 | Inode flags | |
| 92 | 4 | Generation number | |
| Pre version 3 non-inode core field | |||
| 96 | 4 | Unknown (next unlinked inode), which contains -1 (0xffffffff) if not set | |
| Introduced in version 3 | |||
| 100 | 4 | Checksum | |
| 104 | 8 | Change count, which contains the number of changes made to the inode | |
| 112 | 8 | Log sequence number | |
| 120 | 8 | Extended inode flags | |
| 128 | 4 | Copy-on-write (COW) extent size | |
| 132 | 12 | Unknown (padding) | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_BIGTIME not is set | |||
| 144 | 4 | Creation time, which contains a POSIX timestamp in seconds | |
| 148 | 4 | Creation time fraction of second, which contains number of nanoseconds | |
| If incompatible feature flag XFS_SB_FEAT_INCOMPAT_BIGTIME is set | |||
| 144 | 8 | Creation time, which contains a bigtime timestamp | |
| Common | |||
| 152 | 8 | Inode number, which contains an absolute inode number | |
| 160 | 16 | Inode type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
File mode
| Value | Identifier | Description |
|---|---|---|
| Access other, bitmask: 0x0007 (S_IRWXO) | ||
| 0x0001 | S_IXOTH | X-access for other |
| 0x0002 | S_IWOTH | W-access for other |
| 0x0004 | S_IROTH | R-access for other |
| Access group, bitmask: 0x0038 (S_IRWXG) | ||
| 0x0008 | S_IXGRP | X-access for group |
| 0x0010 | S_IWGRP | W-access for group |
| 0x0020 | S_IRGRP | R-access for group |
| Access owner (or user), bitmask: 0x01c0 (S_IRWXU) | ||
| 0x0040 | S_IXUSR | X-access for owner (or user) |
| 0x0080 | S_IWUSR | W-access for owner (or user) |
| 0x0100 | S_IRUSR | R-access for owner (or user) |
| Other | ||
| 0x0200 | S_ISTXT | Sticky bit |
| 0x0400 | S_ISGID | Set group identifer (GID) on execution |
| 0x0800 | S_ISUID | Set owner (or user) identifer (UID) on execution |
| Type of file, bitmask: 0xf000 (S_IFMT) | ||
| 0x1000 | S_IFIFO | Named pipe (FIFO) |
| 0x2000 | S_IFCHR | Character device |
| 0x4000 | S_IFDIR | Directory |
| 0x6000 | S_IFBLK | Block device |
| 0x8000 | S_IFREG | Regular file |
| 0xa000 | S_IFLNK | Symbolic link |
| 0xc000 | S_IFSOCK | Socket |
Fork type
| Value | Identifier | Description |
|---|---|---|
| 0 | XFS_DINODE_FMT_DEV | Device identifier is stored inline (in the inode) |
| 1 | XFS_DINODE_FMT_LOCAL | Data is stored inline (in the inode) |
| 2 | XFS_DINODE_FMT_EXTENTS | Data is referrenced by extents stored in an extents list |
| 3 | XFS_DINODE_FMT_BTREE | Data is referrence by extents stored in an extents tree |
| 4 | XFS_DINODE_FMT_UUID | Unknown (currently not used) |
| 5 | XFS_DINODE_FMT_RMAP | Data is referrence by a reverse mapping |
Inode flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | XFS_DIFLAG_REALTIME | The data is located on the real-time device |
| 0x0002 | XFS_DIFLAG_PREALLOC | The extents have been preallocated |
| 0x0004 | XFS_DIFLAG_NEWRTBM | Uses the new real-time bitmap format |
| 0x0008 | XFS_DIFLAG_IMMUTABLE | Immutable (cannot be modified) |
| 0x0010 | XFS_DIFLAG_APPEND | Append only |
| 0x0020 | XFS_DIFLAG_SYNC | Use synchronous write |
| 0x0040 | XFS_DIFLAG_NOATIME | Do not update access time (atime) |
| 0x0080 | XFS_DIFLAG_NODUMP | Do not "dump", which indicates that xfsdump should ignore the file |
| 0x0100 | XFS_DIFLAG_RTINHERIT | Sub directories inherit XFS_DIFLAG_REALTIME |
| 0x0200 | XFS_DIFLAG_PROJINHERIT | Sub directories inherit the project identifier |
| 0x0400 | XFS_DIFLAG_NOSYMLINKS | No symbolic links can be created for sub directories |
| 0x0800 | XFS_DIFLAG_EXTSIZE | Has extent size |
| 0x1000 | XFS_DIFLAG_EXTSZINHERIT | Sub directories inherit extent size |
| 0x2000 | XFS_DIFLAG_NODEFRAG | Do not defragment |
| 0x4000 | XFS_DIFLAG_FILESTREAM | Unknown (Use filestream allocator) |
Extents list
The extents list consists of:
- one or more packed extents
Packed extent
The packed extent (xfs_bmbt_rec_t) is 128 bits of size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 21 bits | Number of blocks | |
| 2.4 | 52 bits | Physical block number, which contains a file system block number | |
| 9.1 | 54 bits | Logical block number | |
| 15.7 | 1 bit | Uninitialized (unwritten) extent |
Note that uninitialized extents are treated as sparse extents when read.
Extents tree
Extents tree root node
The root node of the extents tree is stored in the inode and equivalent to an extents tree branch node.
The number of key-value pairs is calculated as following:
number_of_key_value_pairs = (node_data_size - 4) / 16
Where “node data size” is (attributes_fork_descriptor_offset * 8) if the value is not 0, or
otherwise the remaining inode block size.
Extents tree sub node block
An extents tree sub nodes is stored in a B+ tree block.
The extents tree uses the B+ tree block header 64-bit.
Extents tree sub node block header
The sub node block header (xfs_bmbt_block_t) is equivalent to B+ tree block header.
Extents tree sub node block header signatures
| Signature | Description |
|---|---|
| "BMA3" | Extents tree sub node block ((file system version 5) |
| "BMAP" | Extents tree sub node block |
Extents tree branch node
The extents tree branch node record consists of:
- node header
- array of extents tree branch node entry keys
- array of extents tree branch node entry values
The number of key-value pairs is calculated as following:
number_of_key_value_pairs = node_records_data_size / 16
Extents tree branch node header
The branch node header (xfs_bmdr_block_t) is 4 byte of size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Node level | |
| 2 | 2 | Number of used key-value pairs in the node |
Extents tree branch node entry key
The branch node entry key (xfs_bmbt_key_t) is 8 byte of size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Data offset |
Extents tree branch node entry value
The branch node entry value (xfs_bmbt_ptr_t or xfs_bmdr_ptr_t) is 8 byte of size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Block number of the extents tree sub node, which contains a file system block number |
Extents tree leaf block node
The extents tree leaf block node consists of:
- one or more packed extents
Directory entries
Directories entries are stored in the data fork of a directory inode. The directory entries can be stored in multiple ways:
- as a short-form directory table
- as an extent-based directory list if the XFS_SB_VERSION_DIRV2BIT flag in the superblock is set
- as an extent-based directory tree if the XFS_SB_VERSION_DIRV2BIT flag in the superblock is not set
Short-form directory table
The short-form directory table (xfs_dir_sf_t or xfs_dir2_sf_t) is stored in the inode (as inline data), where fork type is XFS_DINODE_FMT_LOCAL. The short-form directory table consist of:
- Short-form directory table header
- Short-form directory table entries
The XFS_SB_VERSION_DIRV2BIT flag in the superblock indicates if version 2 is used.
Short-form directory table header version 1
The short-form directory table header version 1 (xfs_dir_sf_hdr_t) is 9 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Parent inode number, which contains an absolute inode number | |
| 8 | 1 | Number of entries |
Short-form directory table header version 2
The short-form directory table header version 2 (xfs_dir2_sf_hdr_t) is 6 or 10 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Number of 32-bit entries | |
| 1 | 1 | Number of 64-bit entries | |
| 2 | 4 or 8 | Parent inode number, which contains an absolute inode number |
Note that if the inode numbers are stored as 32-bit values then number of 32-bit entries is set and number of 64-bit entries must be 0. If the inode numbers are stored as 64-bit values then number of 64-bit entries is set and number of 32-bit entries must be 0.
Short-form directory table entry version 1
The short-form directory table entry version 1 (xfs_dir_sf_entry_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Inode number, which contains an absolute inode number | |
| 9 | 1 | Name size, which does not include the end-of-string character | |
| 10 | ... | Name |
Short-form directory table entry version 2
The short-form directory table entry version 2 (xfs_dir2_sf_entry_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Name size, which does not include the end-of-string character | |
| 1 | 2 | Unknown (offset, tag) | |
| 3 | ... | Name | |
| Only present if XFS_SB_VERSION2_FTYPE is set | |||
| ... | 1 | File type | |
| Common | |||
| ... | 4 or 8 | Inode number, which contains an absolute inode number | |
Note that file type seems to be present on format version even if XFS_SB_VERSION2_FTYPE is not set.
Directory list
A directory list (xfs_dir2_block_t) consist of one or more elements (block) that consist of:
- a directory list element header
- array of used and unused directory entries
- hash values of the entries
- a directory list element footer
Directory list element header
Directory list element header version 2
The directory list element header version 2 (xfs_dir2_data_hdr_t) is 16 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "XD2B" or "XD2D" | Signature |
| 4 | 4 x 3 | Array of free regions in the block |
Directory list element header version 3
The directory list element header version 3 (xfs_dir3_data_hdr_t) is 64 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header (xfs_dir3_blk_hdr_t) | |||
| 0 | 4 | "XDB3" or "XDD3" | Signature |
| 4 | 4 | Checksum | |
| 8 | 8 | Block number | |
| 16 | 8 | Log sequence number | |
| 24 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 40 | 8 | Owner inode number, which contains the absolute inode number the block is part of | |
| 48 | 4 x 3 | Array of free regions in the block | |
| 60 | 4 | Unknown (padding) | |
Directory list element header version signatures
| Signature | Description |
|---|---|
| "XD2B" | Version 2 directory entries B+ tree (single block) |
| "XD2D" | Version 2 directory entries B+ tree (multi block) |
| "XD2F" | Version 2 directory free space B+ tree |
| "XDB3" | Version 3 directory entries B+ tree (single block) |
| "XDD3" | Version 3 directory entries B+ tree (multi block) |
| "XDF3" | Version 3 directory free space B+ tree |
Block free region version 2
The block free region version 2 (xfs_dir2_data_free_t) is 4 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Free region offset, which is relative to the start of the directory block | |
| 2 | 2 | Free region size |
Directory list element entries
Directory list element entry version 2
The directory list element entry version 2 (xfs_dir2_data_entry_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Inode number, which contains an absolute inode number | |
| 8 | 1 | Name size, which does not include the end-of-string character | |
| 9 | ... | Name | |
| Only present if XFS_SB_VERSION2_FTYPE is set | |||
| ... | 1 | Unknown (ftype) | |
| Common | |||
| ... | ... | Unknown (8-byte alignment padding?) | |
| ... | 2 | Unknown (offset, tag) | |
Unused directory list element entry version 2
The unused directory list element entry version 2 (xfs_dir2_data_unused_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0xffff | Signature (free tag) |
| 2 | 2 | Entry size, which contains the size of the unused entry including the size of the signature and entry size | |
| 4 | 2 | Unknown (padding) | |
| ... | 2 | Unknown (offset, tag) |
Directory list element hash value
The directory list element hash value (xfs_dir_leaf_entry_t or xfs_dir2_leaf_entry_t) is 8 bytes of size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Hash value of the name of the directory entry | |
| 4 | 4 | Entry offset, which is relative to the start of the block |
Directory list element footer
Directory list element footer version 2
The directory list element footer version 2 (xfs_dir2_block_tail_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Number of used entries | |
| 4 | 4 | Number of unused entries |
Directory tree
The first block in the extents is the root block.
Directory tree branch node
A directory tree branch node consist of:
- a directory tree branch node header
- array of directory tree branch node entries
Directory tree branch node header
A directory tree branch node header is 16 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 12 | File system block header version 1 with signature 0xfebe | |
| Block tree branch header version 1 | |||
| 12 | 2 | Number of entries | |
| 14 | 2 | Node level | |
Directory tree branch node entry
A directory tree branch node entry is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Name hash | |
| 4 | 4 | Sub block number, which contains a block number relative to the start of the directory tree |
Directory leaf branch node
A directory leaf branch node consist of:
- a directory leaf branch node header
- array of directory tree leaf node entries
Directory tree leaf node
A directory tree leaf node (xfs_dir_leafblock_t) consist of:
- a directory tree leaf node header
- array of directory tree leaf node entries
- array of directory tree leaf node values
- a directory tree leaf node footer
Directory tree leaf node header version 1
A directory tree leaf node header version 1 (xfs_dir_leaf_hdr_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 12 | File system block header version 1 | |
| Block tree leaf header version 1 | |||
| 12 | 2 | Number of entries | |
| 14 | 2 | Used (block) data size, in number of bytes | |
| 16 | 2 | Used data offset | |
| 18 | 1 | Flag to indicate block compaction is needed | |
| 19 | 1 | Unknown (padding) | |
| 20 | 4 x 3 | Array of free regions in the block | |
Note that a directory tree leaf node header version 2 (xfs_dir2_leaf_hdr_t) is equivalent to version 1.
Directory tree leaf node entry version 1
The directory tree leaf node entry (xfs_dir_leaf_entry_t) is 8 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Name hash | |
| 4 | 2 | Value offset, which contains an offset relative to the start of the directory block | |
| 6 | 1 | Name size, which does not include the end-of-string character | |
| 7 | 1 | Unknown (padding) |
Note that a directory tree leaf node entry version 2 (xfs_dir2_leaf_entry_t) is equivalent to version 1.
Directory tree leaf node value
The directory tree leaf node value (xfs_dir_leaf_name_t) is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Inode number, which contains an absolute inode number | |
| 8 | ... | Name |
Directory tree leaf node footer version 1
A directory tree leaf node footer version 1 (xfs_dir_leaf_tail_t) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Unknown (bestcount) |
Note that a directory tree leaf node footer version 2 (xfs_dir2_leaf_tail_t) is equivalent to version 1.
Device identifier
Character and block devices identifiers are stored as inline data with fork type is XFS_DINODE_FMT_DEV.
The device identifier (xfs_dev_t) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 18 bits | Minor device number | |
| 2.2 | 14 bits | Major device number |
File content
XFS supports multiple ways to store file content:
- inline data (fork type is XFS_DINODE_FMT_LOCAL)
- extents defined by either an extents list (fork type is XFS_DINODE_FMT_EXTENTS) or an extent B+ tree (fork type is XFS_DINODE_FMT_BTREE)
Inline data
The file content data is stored in the inode data fork.
Extents
The file content data is stored in the block defined by the extents.
If the logical block numbers of successive extents are non-contiguous this means the file content data has an implicit sparse extent (or hole).
TODO: determine if the hole can be at the start or end of the file content data.
File system block B+ tree
The file system block B+ tree is a structure used to store the directory and attributes trees.
File system block header
File system block header version 1
If the superblock format version <= 4 the file system block header version 1 is used. The file system block header version 1 (xfs_da_blkinfo_t) is 12 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | (Logical) block number of the next B+ tree block at the same level | |
| 4 | 4 | (Logical) block number of the previous B+ tree block at the same level | |
| 8 | 2 | Signature | |
| 10 | 2 | Unknown (padding) |
Note that a file system block header version 2 is equivalent to version 1.
File system block header version 3
If the superblock format version >= 5 the file system block header version 3 is used. The file system block header version 3 (xfs_da3_blkinfo_t) is 56 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | (Logical) block number of the next B+ tree block at the same level | |
| 4 | 4 | (Logical) block number of the previous B+ tree block at the same level | |
| 8 | 2 | Signature | |
| 10 | 2 | Unknown (padding) | |
| 12 | 4 | Checksum | |
| 16 | 8 | Block number | |
| 24 | 8 | Log sequence number | |
| 32 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 48 | 8 | Owner inode number, which contains the absolute inode number the block is part of |
File system block header signatures
| Signature | Identifier | Description |
|---|---|---|
| 0x3bee | XFS_ATTR_LEAF_MAGIC | Attributes tree leaf node (file system version 5) |
| 0x3ebe | XFS_DA3_NODE_MAGIC | Directory or attributes tree branch node (file system version 5) |
| 0xd2f1 | XFS_DIR2_LEAF1_MAGIC | |
| 0xd2ff | XFS_DIR2_LEAFN_MAGIC | |
| 0xfbee | XFS_ATTR_LEAF_MAGIC | Attributes tree leaf node |
| 0xfebe | XFS_DA_NODE_MAGIC | Directory or attributes tree branch node |
| 0xfeeb | XFS_DIR_LEAF_MAGIC | Directory tree leaf node |
Symbolic links
A symbolic link can be maximum 1024 bytes (XFS_SYMLINK_MAXLEN).
Extended attributes
Extended attributes are stored in the attributes fork of an inode. The extended attributes can be stored in multiple ways:
- as a short-form attributes table
- as an extent-based attributes tree
The start of the attributes fork can be determined using the attributes fork descriptor offset.
Short-form attributes table
If the inode attributes fork type is XFS_DINODE_FMT_LOCAL the extended attributes are stored in a short-form attributes table (xfs_attr_shortform) inline in the attribtes fork. The short-form attributes table consist of:
- a short-form attributes table header
- one or more short-form attributes table entries
The short-form attributes table header
The short-form attributes table header (xfs_attr_sf_hdr) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Data size, which contains the size of the short form attributes table data | |
| 2 | 1 | Number of entries | |
| 3 | 1 | Unknown (padding?) |
Note that the size of the short-form attribute header deviates from
[SGI18]based on analysis of test data.
The short-form attribute entry
The short-form attributes table entry (xfs_attr_sf_entry) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Name size | |
| 1 | 1 | Value data size | |
| 2 | 1 | Attribute flags | |
| 3 | ... | Name string, which contains an ASCII string without end-of-string character | |
| ... | ... | Value data |
Attributes tree
If the inode attributes fork type is XFS_DINODE_FMT_BTREE the extended attributes are stored in an attributes tree. The attributes fork contains an extents tree.
The first block in the extents is the B+ tree root block.
Attributes tree branch node
An attributes tree branch node consist of:
- a attributes tree branch node header
- array of attribute branch node entry
Attributes tree branch node header
Attributes tree branch node header version 2
If the superblock format version <= 4 the attributes tree branch node header version 2 is used. The attributes tree branch node header version 2 (xfs_da_blkinfo_t) is 16 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 12 | File system block header version 1 with signature 0xfebe | |
| Block tree branch header version 1 | |||
| 12 | 2 | Number of entries | |
| 14 | 2 | Node level | |
Attributes tree branch node header version 3
If the superblock format version >= 5 the attributes tree branch node header version 3 is used. The attributes tree branch node header version 3 (xfs_da3_blkinfo_t) is 64 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 56 | File system block header version 3 with signature 0x3ebe | |
| Block tree branch header version 3 | |||
| 56 | 2 | Number of entries | |
| 58 | 2 | Node level | |
| 60 | 4 | Unknown (padding) | |
Attributes tree branch node block entry
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Name hash | |
| 4 | 4 | Sub block number, which is relative to the start of the attributes tree |
Attributes tree leaf node
If the inode attributes fork type is XFS_DINODE_FMT_EXTENTS the extended attributes are stored as attributes tree. The attributes fork contains an extents list.
Attributes tree (xfs_attr_leafblock_t or xfs_attr3_leafblock_t) consist of:
- attributes tree leaf node header
- array of block entries
- array of local or remote attribute block values
If more than one block is needed to store the extended attributes an attributes tree is used.
Note that since extended attributes were introduced in superblock format version 2 there are no version 1 structures.
Attributes tree leaf node header
Attributes tree leaf node header version 2
If the superblock format version <= 4 the attributes tree leaf node header version 2 is used. The attributes tree leaf node header version 2 (xfs_attr_leaf_hdr_t) is 32 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 12 | File system block header version 1 with signature 0xfbee | |
| Block tree leaf header version 1 | |||
| 12 | 2 | Number of entries | |
| 14 | 2 | Used (block) data size, in number of bytes | |
| 16 | 2 | Used data offset | |
| 18 | 1 | Flag to indicate block compaction is needed | |
| 19 | 1 | Unknown (padding) | |
| 20 | 4 x 3 | Array of free regions in the block | |
Attributes tree leaf node header version 3
If the superblock format version >= 5 the attributes tree leaf node header version 3 is used. The attributes tree leaf node header version 3 (xfs_attr3_leaf_hdr_t) is 80 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 56 | File system block header version 3 with signature 0x3bee | |
| Block tree leaf header version 3 | |||
| 56 | 2 | Number of entries | |
| 58 | 2 | Used (block) data size, in number of bytes | |
| 60 | 2 | Used data offset | |
| 62 | 1 | Flag to indicate block compaction is needed | |
| 63 | 1 | Unknown (padding) | |
| 64 | 4 x 3 | Array of free regions in the block | |
| 76 | 4 | Unknown (padding) | |
Attributes tree leaf node entry
The attributes tree leaf node entry (xfs_attr_leaf_entry_t) is 8 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Name hash | |
| 4 | 2 | Value offset, which is relative to the start of the leaf node | |
| 6 | 1 | Attribute flags | |
| 7 | 1 | Unknown (padding) |
Attributes tree leaf node value
If the attributes tree leaf node entry flag XFS_ATTR_LOCAL is set the attribute value is stored as local attribute value otherwise as remote attribute value. The value data of remote attribute value are stored in a remote attribute value data block.
Local attribute value
The local attributes value (xfs_attr_leaf_name_local_t) is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Value data size | |
| 2 | 1 | Name size | |
| 3 | ... | Name string, which contains an ASCII string without end-of-string character |
Remote attribute value
The remote attributes value (xfs_attr_leaf_name_remote_t) is of variable size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Value data block number, which is relative to the start of the attributes tree | |
| 4 | 4 | Value data size | |
| 8 | 1 | Name size | |
| 9 | ... | Name string, which contains an ASCII string without end-of-string character |
The attribute flags
The attribute flags indicate the prefix (or namespace) of the attribute name.
| Value | Identifier | Name prefix | Description |
|---|---|---|---|
| 0x00 | "user." | The attribute is part of the user namespace | |
| 0x01 | XFS_ATTR_LOCAL | The attribute value is contained within the current block, which should be ignored in an attributes table | |
| 0x02 | XFS_ATTR_ROOT | "trusted." | The attribute is part of the trusted namespace |
| 0x04 | XFS_ATTR_SECURE | "secure." | The attribute is part of the secure namespace |
| 0x08 | XFS_ATTR_PARENT | The attribute contains a parent directory reference, where the attribute name contains the file tentry name and the attribute value data a reference to the parent directory | |
| 0x80 | XFS_ATTR_INCOMPLETE | The attribute is being modified |
Remote attribute value data block
If the superblock format version <= 4 the attribute value data is stored directly in remote attribute value data blocks.
If the superblock format version >= 5 each individual remote attribute value data block will start with a remote attribute value data block header version 3 followed by attribute value data.
The attributes extents contain the physical location of the individual remote attribute value data blocks.
Remote attribute value data block header version 3
The remote attribute value data block header (xfs_attr3_rmt_hdr) is 52 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | "XARM" | Signature |
| 4 | 4 | Value data offset | |
| 8 | 4 | Value data size | |
| 12 | 4 | Checksum of the remote attribute value data block | |
| 16 | 16 | Block type identifier, which contains an UUID that should correspond to sb_uuid or sb_meta_uuid | |
| 32 | 8 | Owner inode number, which contains the absolute inode number the block is part of | |
| 40 | 8 | Block number | |
| 48 | 8 | Log sequence number |
Parent directory attribute value data
The parent directory attribute value data (xfs_parent_rec) is 12 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Parent directory inode number | |
| 8 | 4 | Parent directory generation number |
References
Hybrid volume and file system formats
A hybrid volume and file system format is a format that combines traits of both a volume and file system.
Formats
Apple File System (APFS)
The Apple File System (APFS) is a volume and file system mainly used on platforms such as Mac OS and iOS. APFS supersedes the Hierarchical File System (HFS) and was introduced in macOS High Sierra (10.13) and iOS 10.3.
Overview
APFS consists of:
- A container
- Zero or more logical volumes that contain a file system
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
| Date and time values | number of nanoseconds since January 1, 1970 00:00:00 UTC (POSIX epoch), disregarding leap seconds |
| Character strings | Unicode strings are stored in UTF-8 |
Note that date and values are signed integers to represent dates before January 1, 1970. Other sources are known to claim the date and time values are unsigned including Apple’s own Apple File System Reference documentation.
Note that (some) sources claim that APFS uses Unicode version 9.0. Support for codepoints of more recent Unicode versions has been observed.
Terminology
| Term | Description |
|---|---|
| Physical volume | A volume in which the APFS container is stored |
| Logical volume | A volume in which an APFS file system is stored |
Keys
To encrypt storage media APFS uses different kind of keys.
Volume master key
The Volume Master Key (VMK) is used to encrypt the data of a specific volume.
Volume key
For every volume on an Mac OS system with APFS, APFS provides for a volume password to unlock the encrypted data. The volume password is used to determine a volume key.
Encryption methods
APFS uses the AES-XTS encryption method to encrypt the key bag, file system metadata and content data.
AES-XTS
The AES-XTS encryption method uses:
- a primary key (key 1) to encrypt/decrypt the data (the whitened plaintext/ciphertext).
- a secondary key (key 2) to encrypt/ decrypt the tweak value, also referred to as the tweak key. The encrypted tweak value is used to whiten the plaintext/ciphertext.
- a tweak value
The cipher block size is 128 bytes.
The container key bag is encrypted using the “container identifier” of the container as both the primary and tweak key. The sector number, relative to the start of the container, is used as the tweak value.
Note that when a T2 chip is present, it is currently assumed that the T2 is used to encrypted the container key bag instead of the “container identifier”.
The unit size is the sector size, which is assumed to be 512 bytes also for 4 KiB sector media.
The volume key bag is encrypted using the “volume identifier” of the corresponding key bag entry, as both the primary and tweak key. The sector number, relative to the start of the container, is used as the tweak value.
The file system B-tree is encrypted using the volume master key and the sector number, relative to the start of the container, is used as the tweak value.
Objects
APFS uses the “object” data type to distinguish between different data types.
Object header
The object header (obj_phys_t) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Object checksum (o_cksum), which contains 0 if not set | |
| 8 | 8 | Object identifier (o_oid) | |
| 16 | 8 | Object transaction identifier (o_xid), which contains the identifier of the most recent transaction that this object was modified in | |
| 24 | 4 | Object type (o_type) | |
| 28 | 4 | Object subtype (o_subtype) |
Object checksum
The checksum algorithm:
Fletcher-64 checksum of the data without the object checksum value and an initial value of 0
checksum_lower_32bit = 0xffffffff - ((fletcher_lower_32bit + fletcher_upper_32bit) mod 0xffffffff)
checksum_upper_32bit = 0xffffffff - ((fletcher_lower_32bit + checksum_lower_32bit) mod 0xffffffff)
checksum = (checksum_upper_32bit << 32) | checksum_lower_32bit
Object identifiers
- For a physical object, its identifier is the logical block address on disk where the object is stored.
- For an ephemeral object, its identifier is a number.
- For a virtual object, its identifier is a number.
| Value | Identifier | Description |
|---|---|---|
| 0 | OID_INVALID | Invalid |
| 1 | OID_NX_SUPERBLOCK | Container superblock |
| 1024 | OID_RESERVED_COUNT | Number of reserved object identifiers |
Object types
The object type (o_type) value consists of a type and flags.
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | OBJECT_TYPE_INVALID | Invalid. For a subtype this value represents not set or not specified |
| 0x00000001 | OBJECT_TYPE_NX_SUPERBLOCK | Container superblock |
| 0x00000002 | OBJECT_TYPE_BTREE | B-tree (root) |
| 0x00000003 | OBJECT_TYPE_BTREE_NODE | B-tree node |
| 0x00000004 | Unknown (MTree?) | |
| 0x00000005 | OBJECT_TYPE_SPACEMAN | Space manager header |
| 0x00000006 | OBJECT_TYPE_SPACEMAN_CAB | Space manager chunk information address block |
| 0x00000007 | OBJECT_TYPE_SPACEMAN_CIB | Space manager chunk information block |
| 0x00000008 | OBJECT_TYPE_SPACEMAN_BITMAP | Space manager bitmap |
| 0x00000009 | OBJECT_TYPE_SPACEMAN_FREE_QUEUE | Space manager free queue |
| 0x0000000a | OBJECT_TYPE_EXTENT_LIST_TREE | Extent list tree |
| 0x0000000b | OBJECT_TYPE_OMAP | Object map |
| 0x0000000c | OBJECT_TYPE_CHECKPOINT_MAP | Checkpoint map |
| 0x0000000d | OBJECT_TYPE_FS | Volume (or file system) superblock |
| 0x0000000e | OBJECT_TYPE_FS | File system tree |
| 0x0000000f | OBJECT_TYPE_BLOCKREFTREE | Extent-reference tree |
| 0x00000010 | OBJECT_TYPE_SNAPMETATREE | Snapshot metadata tree |
| 0x00000011 | OBJECT_TYPE_NX_REAPER | Reaper |
| 0x00000012 | OBJECT_TYPE_NX_REAP_LIST | Reaper list |
| 0x00000013 | OBJECT_TYPE_OMAP_SNAPSHOT | Object map snapshot |
| 0x00000014 | OBJECT_TYPE_EFI_JUMPSTART | EFI jumpstart |
| 0x00000015 | OBJECT_TYPE_FUSION_MIDDLE_TREE | Fusion middle tree |
| 0x00000016 | OBJECT_TYPE_NX_FUSION_WBC | Fusion write-back cache |
| 0x00000017 | OBJECT_TYPE_NX_FUSION_WBC_LIST | Fusion write-back cache list |
| 0x00000018 | OBJECT_TYPE_ER_STATE | Unknown (ER state?) |
| 0x00000019 | OBJECT_TYPE_GBITMAP | Unknown (G Bitmap?) |
| 0x0000001a | OBJECT_TYPE_GBITMAP_TREE | Unknown (G Bitmap tree?) |
| 0x0000001b | OBJECT_TYPE_GBITMAP_BLOCK | Unknown (G Bitmap block?) |
| 0x000000ff | OBJECT_TYPE_TEST | Unknown (test?) |
| 0x0000ffff | OBJECT_TYPE_MASK | Object type bitmask |
| Flags used in combination with some of the object types | ||
| 0x08000000 | OBJ_NONPERSISTENT | Unknown (Non-persistent?) |
| 0x10000000 | OBJ_ENCRYPTED | Is encrypted |
| 0x20000000 | OBJ_NOHEADER | Has no object (obj_phys_t) header |
| 0x00000000 | OBJ_VIRTUAL | Is virtual object |
| 0x40000000 | OBJ_PHYSICAL | Is physical object |
| 0x80000000 | OBJ_EPHEMERAL | Is ephemeral object |
| 0xffff0000 | OBJECT_TYPE_FLAGS_MASK | Object type flags bitmask |
| 0xc0000000 | OBJ_STORAGETYPE_MASK | Object storage type bitmask |
| 0xf8000000 | OBJECT_TYPE_FLAGS_DEFINED_MASK | Unknown |
| Object types without flags | ||
| 0x6b657973 ("syek") | Container key bag | |
| 0x72656373 ("scer") | Volume key bag | |
Object subtypes
The object subtype is used by specific object types such as:
- B-tree root
- B-tree node
The object subtypes are the same as the Object types.
B-tree
A B-tree consists of:
- B-tree root object
- Zero or more B-tree node objects
B-tree root or node object
A B-tree root or node (or object) consists of:
- Object header
- B-tree node header
- B-tree entries (table of contents)
- keys data, where the first key is stored after the entries in increasing order
- Optional key free list
- unused data
- Optional value free list
- values data, where the first value is stored before the footer in descending order
- Optional B-tree footer, which is only stored in the root node
Note that the Apple File System Reference documentation combines the B-tree object and B-tree node header into a single structure referred to as btree_node_phys_t.
B-tree root object header
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header (btn_o) | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x00000002 or 0x40000002 | Object type |
| 28 | 4 | Object subtype | |
Note that object type can be 0x00000000 if the B-tree is empty.
B-tree node object header
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header (btn_o) | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x00000003 or 0x40000003 | Object type |
| 28 | 4 | Object subtype | |
B-tree node header
The B-tree node header is stored after the B-tree root or node object.
The B-tree node header is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Flags (btn_flags) | |
| 2 | 2 | Level (btn_level) | |
| 4 | 4 | Number of keys in the node (btn_nkeys) | |
| Table space (btn_table_space) | |||
| 8 | 2 | Entries data offset, which contains an offset relative to the end of the B-tree node header or -1 (0xffff) if not set (invalid) | |
| 10 | 2 | Entries data size, in number of bytes | |
| Free space (btn_free_space) | |||
| 12 | 2 | Unused data offset, which contains an offset relative to the end of the entries data or -1 (0xffff) if not set (invalid) | |
| 14 | 2 | Unused data size, in number of bytes | |
| Key free list (btn_key_free_list) | |||
| 16 | 2 | Unused key list offset, which contains an offset relative to unknown or -1 (0xffff) if not set (invalid) | |
| 18 | 2 | Unused key list size, in number of bytes | |
| Value free list (btn_val_free_list) | |||
| 20 | 2 | Unused value list offset, which contains an offset relative to unknown or -1 (0xffff) if not set (invalid) | |
| 22 | 2 | Unused value list size, in number of bytes | |
B-tree node flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | BTNODE_ROOT | Is root |
| 0x0002 | BTNODE_LEAF | Is leaf |
| 0x0004 | BTNODE_FIXED_KV_SIZE | Has a fixed-size entry (key and value) |
| 0x0008 | BTNODE_HASHED | B-tree branch nodes contain a hash of their sub nodes |
| 0x0010 | BTNODE_NOHEADER | The B-tree node are stored without object header, where the object header is filled with 0-byte values |
| 0x8000 | BTNODE_CHECK_KOFF_INVAL | In transient state, which is used for in-memory purposes only |
B-tree entries
The B-tree entries are stored after the B-tree node header.
Fixed-size B-tree entry
The fixed-size B-tree entry is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Key data offset (key_offs), which contains an offset relative to the end of the entries data | |
| 2 | 2 | Value data offset (value_offs), which contains a reversed offset relative to the start of the B-tree footer |
Variable-size B-tree entry
The variable-size B-tree entry is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Key data offset (key_offs), which contains an offset relative to the end of the entries data | |
| 2 | 2 | Key data size (key_len), in number of bytes | |
| 4 | 2 | Value data offset (value_offs), which contains a reversed offset relative to the start of the B-tree footer | |
| 6 | 2 | Value data size (value_len), in number of bytes |
B-tree footer
The B-tree footer is stored at the end of the block that contains the B-tree root object.
The B-tree footer (btree_info_t) is 40 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Static information (btree_info_fixed_t) | |||
| 0 | 4 | Flags (bt_flags) | |
| 4 | 4 | Node size (bt_node_size), in number of bytes | |
| 8 | 4 | Key size (bt_key_size), in number of bytes and is set to 0 if key has a variable size | |
| 12 | 4 | Value size (bt_val_size), in number of bytes and is set to 0 if value has a variable size | |
| 16 | 4 | Maximum key size (bt_longest_key), in number of bytes | |
| 20 | 4 | Maximum value size (bt_longest_val), in number of bytes | |
| 24 | 8 | Total number of keys (bt_key_count) | |
| 32 | 8 | Total number of nodes (bt_node_count) | |
B-tree flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | BTREE_UINT64_KEYS | Unknown |
| 0x00000002 | BTREE_SEQUENTIAL_INSERT | Unknown |
| 0x00000004 | BTREE_ALLOW_GHOSTS | Unknown |
| 0x00000008 | BTREE_EPHEMERAL | Unknown |
| 0x00000010 | BTREE_PHYSICAL | Unknown |
| 0x00000020 | BTREE_NONPERSISTENT | Unknown |
| 0x00000040 | BTREE_KV_NONALIGNED | Unknown |
| 0x00000080 | BTREE_HASHED | B-tree branch nodes contain a hash of their sub nodes |
| 0x00000100 | BTREE_NOHEADER | The B-tree node are stored without object header, where the object header is filled with 0-byte values |
The container
APFS stores volumes inside a container. The maximum number of volumes is dependent on the size of the container.
| Container size | Maximum number of volumes |
|---|---|
| 1 GiB | 2 |
| 2 GiB | 4 |
| 5 GiB | 10 |
| 10 GiB | 20 |
| 20 GiB | 40 |
| 100 GiB | 100 |
| 12 TiB | 100 |
| 1.2 PiB | 100 |
| 7.5 EiB | 100 |
The container consists of:
- current container superblock
- stored in the container checkpoint descriptor area:
- current checkpoint map
- previous checkpoint map(s)
- previous container superblock(s)
- stored in the container:
- space manager
- container object map
- reaper
- crypto key
- zero or more volumes
- Unknown: backup of current container superblock?
Container superblock
The container superblock (nx_superblock_t) is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x80000001 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | "NXSB" | Signature (nx_magix) |
| 36 | 4 | Block size (nx_block_size), in number of bytes | |
| 40 | 8 | Number of blocks (nx_block_count) | |
| 48 | 8 | Container feature flags (nx_features) | |
| 56 | 8 | Read-only compatible feature flags (nx_readonly_compatible_features) | |
| 64 | 8 | Incompatible feature flags (nx_incompatible_features) | |
| 72 | 16 | Container identifier (nx_uuid), which contains a big-endian UUID | |
| 88 | 8 | Next (available) object identifier (nx_next_oid) | |
| 96 | 8 | Next (available) transaction identifier (nx_next_xid) | |
| 104 | 4 | Checkpoint descriptor area number of blocks (nx_xp_desc_blocks), which contains the size of the checkpoint descriptor area and the MSB is a flag | |
| 108 | 4 | Checkpoint data area number of blocks (nx_xp_data_blocks), which contains the size of the checkpoint data area and the MSB is a flag | |
| 112 | 8 | Checkpoint descriptor area block number (nx_xp_desc_base), where the block number is relative to the start of the container of the checkpoint descriptor area if the MSB of nx_xp_desc_blocks is not set, otherwise the value contains the physical object identifier of a checkpoint descriptor area B-tree | |
| 120 | 8 | Checkpoint data area block number (nx_xp_data_base), where the block number is relative to the start of the container of the checkpoint data area if the MSB of nx_xp_data_blocks is not set | |
| 128 | 4 | Next available index in the checkpoint descriptor area (nx_xp_desc_next) | |
| 132 | 4 | Next available index in the checkpoint data area (nx_xp_data_next) | |
| 136 | 4 | Index of the checkpoint in the checkpoint descriptor area (nx_xp_desc_index) | |
| 140 | 4 | Size of the checkpoint in the checkpoint descriptor area, in number of blocks (nx_xp_desc_len) | |
| 144 | 4 | Index of the checkpoint in the checkpoint data area (nx_xp_data_index) | |
| 148 | 4 | Size of the checkpoint in the checkpoint data area, in number of blocks (nx_xp_data_len) | |
| 152 | 8 | Space manager object identifier (nx_spaceman_oid), where the object identifier can be resolved in the checkpoint map | |
| 160 | 8 | Object map block number (nx_omap_oid), where the block number is relative to the start of the container of the object map | |
| 168 | 8 | Reaper object identifier (nx_reaper_oid), where the object identifier can be resolved in the checkpoint map | |
| 176 | 4 | Unknown (reserved for testing) (nx_test_type) | |
| 180 | 4 | Maximum number of volumes (nx_max_file_systems) supported by the container | |
| 184 | 100 x 8 = 800 | Array of volume object identifiers (nx_fs_oid), which can be resolved to a "physical" location using the object map | |
| 984 | 32 x 8 = 256 | Container counters (nx_counters) | |
| Reserved (or blocked out) data area (nx_blocked_out_prange) | |||
| 1240 | 8 | Reserved data area block number (nx_blocked_out_base), which contains a block number relative to the start of the container | |
| 1248 | 8 | Reserved data area number of blocks (nx_blocked_out_blocks) | |
| 1256 | 8 | Eviction tree (physical) object identifier (nx_evict_mapping_tree_oid) | |
| 1264 | 8 | Container flags (nx_flags) | |
| 1272 | 8 | EFI jumpstart (physical) object identifier (nx_efi_jumpstart), which contains a block number relative to the start of the container | |
| 1280 | 16 | Fusion set identifier (nx_fusion_uuid), which contains a big-endian UUID | |
| Container key bag area (nx_keylocker) | |||
| 1296 | 8 | Container key bag block number (nx_keybag_base), which contains a block number relative to the start of the container | |
| 1304 | 8 | Container key bag number of blocks (nx_keybag_blocks) | |
| 1312 | 4 x 8 = 32 | Ephemeral information (nx_ephemeral_info) | |
| 1344 | 8 | Unknown (reserved for testing) (nx_test_oid) | |
| 1352 | 8 | Fusion middle tree block number (nx_fusion_mt_oid), which contains a block number relative to the start of the container | |
| 1360 | 8 | Fusion write-back cache state object identifier (nx_fusion_wbc_oid), where the object identifier can be resolved in the checkpoint map | |
| Fusion write-back cache area (nx_fusion_wbc) | |||
| 1368 | 8 | Fusion write-back cache area block number (nx_fusion_wbc_base), which contains a block number relative to the start of the container | |
| 1376 | 8 | Fusion write-back cache area number of blocks (nx_fusion_wbc_blocks) | |
| 1384 | 8 | Newest version of software that mounted the container (nx_newest_mounted_version) | |
| Media key area (nx_mkb_locker) | |||
| 1392 | 8 | Media key area block number, which contains a block number relative to the start of the container | |
| 1400 | 8 | Media key area number of blocks | |
| 1408 | 2688 | Unknown (empty values) | |
Note that NXSB presumably is an abbreviation of NX superblock. At this point it is unclear what NX stands for.
Container flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | NX_RESERVED_1 | Unknown (reserved) |
| 0x00000002 | NX_RESERVED_2 | Unknown (reserved) |
| 0x00000004 | NX_CRYPTO_SW | The encryption is performed in software |
Container feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | NX_FEATURE_DEFRAG | Supports defragmentation |
| 0x0000000000000002 | NX_FEATURE_LCFD | Uses low-capacity Fusion Drive mode |
Container read-only compatible feature flags
Current no read-only compatible feature flags are defined.
Container incompatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | NX_INCOMPAT_VERSION1 | Pre-release version 1 of APFS |
| 0x0000000000000002 | NX_INCOMPAT_VERSION2 | Release version 2 of APFS |
| 0x0000000000000100 | NX_INCOMPAT_FUSION | Supports Fusion Drives |
Note that according to the Apple File System Reference documentation the pre-release version 1 and release version 2 are incompatble.
Container counters
| Value | Identifier | Description |
|---|---|---|
| 0 | NX_CNTR_OBJ_CKSUM_SET | Number of times a checksum has been calculated when wrting to disk |
| 1 | NX_CNTR_OBJ_CKSUM_FAIL | Number of checksum errors when reading from disk |
Note that the other 30 counters are presumed to be unused at this point.
Checkpoint map
The checkpoint map contains a mapping between container metadata object identifiers and their location in the container.
Note that multiple successive checkpoint map objects can be used to store a check point map.
Checkpoint map object
The checkpoint map object (checkpoint_map_phys_t) is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x4000000c | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Flags (cpm_flags) | |
| 36 | 4 | Number of entries (cpm_count) | |
| 40 | 101 x 40 = 4040 | Array of checkpoint map entries (cpm_map) | |
| 4080 | 16 | Unknown (empty values) | |
Checkpoint flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | CHECKPOINT_MAP_LAST | Last checkpoint map object |
Checkpoint map entry
The checkpoint map entry (checkpoint_mapping_t) is 40 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Object type (cpm_type) | |
| 4 | 4 | Object subtype (cpm_subtype) | |
| 8 | 4 | Size (cpm_size), in number of bytes | |
| 12 | 4 | Unknown (padding) (cpm_pad) | |
| 16 | 8 | File system object identifier (cpm_fs_oid) | |
| 24 | 8 | (Container) object identifier (cpm_oid) | |
| 32 | 8 | Physical address (cpm_paddr), which contains a block number relative to the start of the container |
Object map
The object map contains a mapping between object identifiers and their “physical” location.
The object map consists of:
- object map (object)
- object map B-tree
Object map object
The object map object (omap_phys_t) is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x4000000b | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Flags (om_flags) | |
| 36 | 4 | Number of snapshots (om_snap_count) | |
| 40 | 4 | Object map B-tree type (om_tree_type) | |
| 44 | 4 | Object map snapshots B-tree type (om_snapshot_tree_type) | |
| 48 | 8 | Object map B-tree (root node) block number (om_tree_oid), which contains a block number relative to the start of the container | |
| 56 | 8 | Object map snapshots B-tree (root node) block number (om_snapshot_tree_oid), which contains a block number relative to the start of the container | |
| 64 | 8 | Most recent snapshot object identifier (om_most_recent_snap) | |
| 72 | 8 | Unknown transaction identifier (om_pending_revert_min) | |
| 80 | 8 | Unknown transaction identifier (om_pending_revert_max) | |
| 88 | 4008 | Unknown (empty values) | |
Object map flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | OMAP_MANUALLY_MANAGED | No snapshot support |
| 0x00000002 | OMAP_ENCRYPTING | Encryption in progress |
| 0x00000004 | OMAP_DECRYPTING | Decryption in progress |
| 0x00000008 | OMAP_KEYROLLING | Re-encryption with new key in progress |
| 0x00000010 | OMAP_CRYPTO_GENERATION | Encryption configuration has changed |
Object map B-tree
The object map values are stored in a B-tree.
Object map B-tree key
The object map B-tree key (omap_key_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Key object identifier (ok_oid) | |
| 8 | 8 | Key object transaction identifier (ok_xid) |
Object map B-tree branch node value
An object map B-tree node contains branch node values if BTNODE_LEAF is not set. The corresponding object map B-tree key represents the first key in the branch.
An object map B-tree branch node value is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Sub node block number, which contains a block number relative to the start of the container |
Object map value
An object map B-tree node contains object map values if BTNODE_LEAF is set.
The object map value (omap_val_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Value object flags (ov_flags) | |
| 4 | 4 | Value object size (ov_size), in number of bytes | |
| 8 | 8 | Value object physical address (ov_paddr), which contains a block number relative to the start of the container |
Object map value flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | OMAP_VAL_DELETED | Unknown |
| 0x00000002 | OMAP_VAL_SAVED | Unknown |
| 0x00000004 | OMAP_VAL_ENCRYPTED | Unknown |
| 0x00000008 | OMAP_VAL_NOHEADER | Unknown |
| 0x00000010 | OMAP_VAL_CRYPTO_GENERATION | Unknown |
Notes
TODO: document omap_snapshot_t TODO: document Object Map Reaper Phases
Space manager
The space manager (spaceman_phys_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header (sm_o) | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x80000005 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Block size (sm_block_size), in number of bytes | |
| 36 | 4 | Number of blocks per chunk (sm_blocks_per_chunk) | |
| 40 | 4 | Number of chunks per chunk information block (CIB) (sm_chunks_per_cib) | |
| 44 | 4 | Number of chunk information blocks (CIBs) per chunk information address block (CAB) (sm_cibs_per_cab) | |
| Space manager devices (sm_dev) | |||
| 48 | 48 | Main device (SD_MAIN), which contains a Space manager device | |
| 96 | 48 | Tier2 device (SD_TIER2), which contains a Space manager device | |
| 144 | 4 | Flags | |
| 148 | 4 | Unknown (sm_ip_bm_tx_multiplier) | |
| 152 | 8 | Unknown (sm_ip_block_count) | |
| 160 | 4 | Unknown (sm_ip_bm_size_in_blocks) | |
| 164 | 4 | Unknown (sm_ip_bm_block_count) | |
| 168 | 8 | Unknown (sm_ip_bm_base) | |
| 176 | 8 | Unknown (sm_ip_base) | |
| 184 | 8 | Unknown (sm_fs_reserve_block_count) | |
| 192 | 8 | Unknown (sm_fs_reserve_alloc_count) | |
| Space manager free queues (sm_fq) | |||
| 200 | 40 | Unknown space free queue (SFQ_IP) | |
| 240 | 40 | Main space free queue (SFQ_MAIN) | |
| 280 | 40 | Tier2 space free queue (SFQ_TIER2) | |
| 320 | 2 | Unknown (sm_ip_bm_free_head) | |
| 322 | 2 | Unknown (sm_ip_bm_free_tail) | |
| 324 | 4 | Unknown (sm_ip_bm_xid_offset), which contains an offset in bytes relative to the start of the space manager | |
| 328 | 4 | Unknown (sm_ip_bitmap_offset), which contains an offset in bytes relative to the start of the space manager | |
| 332 | 4 | Unknown (sm_ip_bm_free_next_offset), which contains an offset in bytes relative to the start of the space manager | |
| 336 | 4 | 1 | Unknown (sm_version) |
| 340 | 4 | Unknown (sm_struct_size) | |
| Space manager data zone (sm_datazone) | |||
| 344 | 8 x 72 | Main allocation zones | |
| 920 | 8 x 72 | Tier2 allocation zones | |
| 1492 | ... | Unknown (data) | |
Space manager flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | SM_FLAG_VERSIONED | Unknown |
Space manager device
A space manager device (spaceman_device_t) is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Number of blocks (sm_block_count) | |
| 8 | 8 | Number of chunks (sm_chunk_count) | |
| 16 | 4 | Number of chunk information blocks (CIBs) (sm_cib_count) | |
| 20 | 4 | Number of chunk information address blocks (CABs) (sm_cab_count) | |
| 24 | 8 | Number of unused blocks (sm_free_count) | |
| 32 | 4 | Unknown (sm_addr_offset), which contains an offset in bytes relative to the start of the space manager | |
| 36 | 4 | Unknown (sm_reserved) | |
| 40 | 8 | Unknown (sm_reserved2) |
Space manager free queue
A space manager free queue (spaceman_free_queue_t) is 40 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Unknown (sfq_count) | |
| 8 | 8 | Space manager free queue tree object identifier (sfq_tree_oid) | |
| 16 | 8 | Space manager free queue oldest transaction identifier (sfq_oldest_xid) | |
| 24 | 2 | Unknown (sfq_tree_node_limit) | |
| 26 | 2 | Unknown (sfq_pad16) | |
| 28 | 4 | Unknown (sfq_pad32) | |
| 32 | 8 | Unknown (sfq_reserved) |
Space manager allocation zone
A space manager allocation zone (spaceman_allocation_zone_info_phys_t) is 72 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Current allocation zone boundaries (saz_current_boundaries) | |
| 8 | 7 x 8 | Previous allocation zone boundaries (saz_previous_boundaries) | |
| 64 | 2 | Unknown (saz_zone_id) | |
| 66 | 2 | Unknown (saz_previous_boundary_index) | |
| 68 | 4 | Unknown (saz_reserved) |
Space manager zone_boundaries
A space manager zone boundaries (spaceman_allocation_zone_boundaries_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Unknown (saz_zone_start) | |
| 8 | 8 | Unknown (saz_zone_end) |
Notes
sm_addr_offset points to block number which points to a OBJECT_TYPE_SPACEMAN_CIB block. Probably an OBJECT_TYPE_SPACEMAN_CAB block when necessary.
Chunk information address block
The chunk information address block (cib_addr_block_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header (cab_o) | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x40000006 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Unknown (cab_index) | |
| 36 | 4 | Number of chunk information blocks (CIBs) (cab_cib_count) | |
| Chunk information block physical addresses (cab_cib_addr) | |||
| 40 | 8 x Number of CIBs | Physical address of chunk information blocks (CIB) | |
Chunk information block
The chunk information block (chunk_info_block_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header (cib_o) | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x40000007 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Unknown (cib_index) | |
| 36 | 4 | Number of chunk information entries (cib_chunk_info_count) | |
| Chunk information entries (cib_chunk_info) | |||
| 40 | 8 x Number of entries | Array of chunk information entries | |
Chunk information entry
The chunk information entry (chunk_info_t) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Unknown (ci_xid) | |
| 8 | 8 | Unknown (ci_addr) | |
| 16 | 4 | Unknown (ci_block_count) | |
| 20 | 4 | Unknown (ci_free_count) | |
| 24 | 8 | Unknown (ci_bitmap_addr) |
Reaper
The reaper is of unknown size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x80000011 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 8 | Unknown | |
| 8 | Unknown | ||
| 8 | Unknown | ||
| 8 | Unknown | ||
| 4 | Unknown | ||
| 4 | Unknown | ||
| 4 | Unknown | ||
| 4 | Unknown | ||
| 8 | Unknown | ||
| 8 | Unknown | ||
| 8 | Unknown | ||
| 4 | Unknown | ||
| 4 | Unknown | ||
Reaper list
The reaper list entry is of unknown size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x80000012 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | Unknown | |
| 36 | 4 | Unknown | |
| 40 | 4 | Unknown | |
| 44 | 4 | Unknown (max_record_count) | |
| 48 | 4 | Unknown (record_count) | |
| 52 | 4 | Unknown (first_index) | |
| 56 | 4 | Unknown (last_index) | |
| 60 | 4 | Unknown (free_index) | |
| 64 | 100 x ... | Array of reaper list entries (nrle) | |
Reaper list entry
The reaper list entry is 40 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Forward link (fwlink) | |
| 4 | 4 | Unknown | |
| 8 | 4 | Type (type) | |
| 12 | 4 | Block size (blksize), in number of bytes | |
| 16 | 8 | Object identifier (oid) | |
| 24 | 8 | Physical address (paddr), which contains a block number relative to the start of the container | |
| 32 | 8 | Object transaction identifier (xid) |
Key bag
The key bag consists of:
- Container or volume key bag object
- Key bag header
- Key bag entries
Container key bag object
The container key bag object contains key data of the container.
The container key bag object is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x6b657973 ("syek") | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
Volume key bag object
The volume key bag object contains key data of a specific volume.
The volume key bag object is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x72656373 ("scer") | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
Key bag header
The key bag header (kb_locker_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 2 | Format version (kl_version) |
| 2 | 2 | Number of entries (kl_nkeys) | |
| 4 | 4 | Key bag data size (kl_nbytes), in number of bytes and includes the size of key bag header | |
| 8 | 8 | Unknown (padding) |
Key bag entries
A key bag entry consists of:
- a key bag entry header
- a key bag entry data
- alignment padding
The key bag entry header specifies the type of the key bag entry data.
The key bag entries are 16-byte aligned.
Key bag entry header
The key bag entry header (keybag_entry_t) is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 16 | Volume identifer (ke_uuid), which contains a big-endian UUID | |
| 16 | 2 | Entry type (ke_tag) | |
| 18 | 2 | Entry data size (ke_keylen), in number of bytes | |
| 20 | 4 | Unknown (padding) |
Key bag entry types
Container key bag entry types
| Value | Identifier | Description |
|---|---|---|
| 0x00 | KB_TAG_UNKNOWN | Unknown |
| 0x01 | KB_TAG_WRAPPING_KEY (or KB_TAG_RESERVED_1) | Wrapping key |
| 0x02 | KB_TAG_VOLUME_KEY | Volume master key, which contains a Key encrypted key (KEK) packed object |
| 0x03 | KB_TAG_VOLUME_UNLOCK_RECORDS | Volume key bag extent |
| 0x04 | KB_TAG_VOLUME_PASSPHRASE_HINT | Passphrase hint |
| 0x05 | KB_TAG_WRAPPING_M_KEY | Key used to wrap a media key |
| 0x06 | KB_TAG_VOLUME_M_KEY | Key used to wrap a media key |
| 0xf8 | KB_TAG_USER_PAYLOAD (or KB_TAG_RESERVED_F8) | Unknown (user payload) |
The volume master key is encryped with a volume key.
Volume key bag entry types
| Value | Identifier | Description |
|---|---|---|
| 3 | Volume key, which contains a Key encrypted key (KEK) packed object | |
| 4 | Password hint, which contains a string without end-of-string character |
The volume key is encryped with an user key.
Key bag packed object
The packed object consist of an object packed value that embeds attribute packed values.
Key bag packed value
The key bag packed value is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Value tag (or value type), where the most-significant bit represents a flag | |
| 1 | 1 | Value data size, in number of bytes, where the most-significant bit represents a flag | |
| ... | ... | Value data |
A packed value with a tag and size of 0 signifies the end of the packed values.
Note that the meaning of the value tags differ per packed object type.
Key encrypted key (KEK) packed object
The packed object value tag of a key encrypted key is 0x30 and contains the following attribute value tags:
| Value | Identifier | Description |
|---|---|---|
| 0x80 | Unknown | |
| 0x81 | HMAC | |
| 0x82 | Unknown (salt?) | |
| 0xa3 | Wrapped Key Encryption Key (KEK) packed object |
Wrapped Key Encryption Key (KEK) packed object
The packed object value tag of a wrapped kek encrypted key is 0xa3 and contains the following attribute value tags:
| Value | Identifier | Description |
|---|---|---|
| 0x80 | Unknown | |
| 0x81 | Volume identifer, which contains a big-endian UUID | |
| 0x82 | Wrapped Key Encryption Key (KEK) metadata | |
| 0x83 | Wrapped Key Encryption Key (KEK) data | |
| 0x84 | Number of iterations for the PBKDF2 algorithm | |
| 0x85 | Salt for the PBKDF2 algorithm |
Wrapped Key Encryption Key (KEK) metadata
The Wrapped Key Encryption Key (KEK) metadata is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Encryption method | |
| 4 | 2 | Unknown | |
| 6 | 1 | Unknown | |
| 7 | 1 | Unknown |
Encryption methods
| Value | Identifier | Description |
|---|---|---|
| 0 | Unknown (AES-256) | |
| 2 | Unknown (AES-128 FVDE (CoreStorage FileVault) compatible) | |
| 16 | Unknown (AES-256), which has been observed in combination with recovery password protected volume key |
Key bag data extent
The key bag data extent is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Key bag block number | |
| 8 | 8 | Key bag number of blocks |
Volume
The volume consists of:
- volume superblock
- volume object map
- …
Note that an APFS volume has a corresponding “synthesized” device file though this cannot be directly read.
Volume superblock
The volume superblock (apfs_superblock_t) is 4096 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x0000000d or 0x4000000d (for snapshots) | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | "APSB" | Signature (apfs_magic) |
| 36 | 4 | File system index (apfs_fs_index) | |
| 40 | 8 | Volume feature flags (apfs_features) | |
| 48 | 8 | Read-only compatible feature flags (apfs_readonly_compatible_features) | |
| 56 | 8 | Incompatible feature flags (apfs_incompatible_features) | |
| 64 | 8 | (Last) unmount date and time (apfs_unmount_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 72 | 8 | Number of reserved blocks (apfs_reserve_block_count) | |
| 80 | 8 | Number of quota blocks (apfs_quota_block_count) | |
| 88 | 8 | Number of allocated blocks (apfs_fs_alloc_count) | |
| 96 | 20 | Encryption state (apfs_meta_crypto) | |
| 116 | 4 | File system root tree object type (apfs_root_tree_type) | |
| 120 | 4 | Extent-reference tree object type (apfs_extentref_tree_type) | |
| 124 | 4 | Snapshot metadata tree object type (apfs_snap_meta_tree_type) | |
| 128 | 8 | Object map block number (apfs_omap_oid), which contains a block number relative to the start of the container of the object_map | |
| 136 | 8 | File system root tree object identifier (apfs_root_tree_oid) | |
| 144 | 8 | Extent-reference tree block number (apfs_extentref_tree_oid) | |
| 152 | 8 | Snapshot metadata tree block number (apfs_snap_meta_tree_oid) | |
| 160 | 8 | Rollback transaction identifier (apfs_revert_to_xid) | |
| 168 | 8 | Rollback (physical) object identifier (apfs_revert_to_sblock_oid) | |
| 176 | 8 | Next (available) file system object identifier (apfs_next_obj_id), where the upper 32-bit can contain 0xffffffff | |
| 184 | 8 | Number of files (apfs_num_files) | |
| 192 | 8 | Number of directories (apfs_num_directories) | |
| 200 | 8 | Number of symbolic links (apfs_num_symlinks) | |
| 208 | 8 | Number of other file system objects (apfs_num_other_fsobjects) | |
| 216 | 8 | Number of snapshots (apfs_num_snapshots) | |
| 224 | 8 | Total number of blocks allocated (apfs_total_blocks_alloced) | |
| 232 | 8 | Total number of blocks freed (apfs_total_blocks_freed) | |
| 240 | 16 | Volume identifier (apfs_vol_uuid), which contains a big-endian UUID | |
| 256 | 8 | Modification date and time (apfs_last_mod_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 264 | 8 | Volume flags (apfs_fs_flags) | |
| 272 | 48 | Creation change information (apfs_formatted_by) | |
| 320 | 8 x 48 = 384 | 8 most recent modification change information (apfs_modified_by) | |
| 704 | 256 | Volume label (or name) (apfs_volname) | |
| 960 | 4 | Next (available) document identifier (apfs_next_doc_id) | |
| 964 | 2 | Volume role flags (apfs_role) | |
| 966 | 2 | Unknown (reserved) | |
| 968 | 8 | Active snapshot transaction identifier (apfs_root_to_xid) | |
| 976 | 8 | Encryption progress state (apfs_er_state_oid) | |
| 984 | 8 | Largest clone object identifier (apfs_cloneinfo_id_epoch) | |
| 992 | 8 | Largest clone transaction identifier (apfs_cloneinfo_xid) | |
| 1000 | 8 | Extended snapsnot metadata (virtual) object identifier (apfs_snap_meta_ext_oid) | |
| 1008 | 16 | Volume group identifier (apfs_volume_group_id), which contains a big-endian UUID | |
| 1024 | 8 | Integrity metadata (virtual) object identifier (apfs_integrity_meta_oid) | |
| 1032 | 8 | Extent tree (virtual) object identifier (apfs_fext_tree_oid) | |
| 1040 | 4 | Extent tree object type (apfs_fext_tree_type) | |
| 1044 | 4 | Unknown (reserved_type) | |
| 1048 | 8 | Unknown (reserved_oid) | |
| 1056 | 80 | Unknown | |
| 1136 | 2960 | Unknown (empty values) | |
Encryption state
The encryption state (wrapped_meta_crypto_state_t) is 20 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Major format version (major_version) | |
| 2 | 2 | Minor format version (minor_version) | |
| 4 | 4 | Flags (cpflags) | |
| 8 | 4 | Unknown (persistent_class) | |
| 12 | 4 | Unknown (key_os_version) | |
| 16 | 2 | Unknown (key_revision) | |
| 18 | 2 | Unknown (unused) |
Encryption state flags
TODO: complete this section.
Change information
The change information (apfs_modified_by_t) is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 32 | Application (id), which consist of a string with the first 31 characters of the name and version of the application that changed the file system and 0 if not set | |
| 32 | 8 | Change date and time (timestamp), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 40 | 8 | Change object transaction number (last_xid) or 0 if not set |
Volume flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | APFS_FS_UNENCRYPTED | Volume is unencrypted |
| 0x0000000000000002 | APFS_FS_EFFACEABLE (or APFS_FS_RESERVED_2) | Unknown (Volume supports effaceable storage?) |
| 0x0000000000000004 | APFS_FS_RESERVED_4 | Unknown (reserved) |
| 0x0000000000000008 | APFS_FS_ONEKEY | Volume uses software encryption with a single key (volume master key) |
| 0x0000000000000010 | APFS_FS_SPILLEDOVER | Volume has run out of allocated space on the solid-state drive |
| 0x0000000000000020 | APFS_FS_RUN_SPILLOVER_CLEANER | Volume has spilled over and the spillover cleaner must be run |
| 0x0000000000000040 | APFS_FS_ALWAYS_CHECK_EXTENTREF | Volume extent reference tree must be consulted before overwriting an extent |
| 0x0000000000000080 | APFS_FS_RESERVED_80 | Unknown (reserved) |
| 0x0000000000000080 | APFS_FS_RESERVED_100 | Unknown (reserved) |
Volume features flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | APFS_FEATURE_DEFRAG_PRERELEASE | Unknown |
| 0x0000000000000002 | APFS_FEATURE_HARDLINK_MAP_RECORDS | Unknown |
| 0x0000000000000004 | APFS_FEATURE_DEFRAG | Unknown |
| 0x0000000000000008 | APFS_FEATURE_STRICTATIME | Unknown |
| 0x0000000000000010 | APFS_FEATURE_VOLGRP_SYSTEM_INO_SPACE | Unknown |
Volume read-only compatible feature flags
Current no read-only compatible feature flags are defined
Volume incompatible feature flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | APFS_INCOMPAT_CASE_INSENSITIVE | Filenames are case insensitive |
| 0x0000000000000002 | APFS_INCOMPAT_DATALESS_SNAPS | Volume contains one or more snapshots without data |
| 0x0000000000000004 | APFS_INCOMPAT_ENC_ROLLED | Encryption keys of the volume have been changed |
| 0x0000000000000008 | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE | Filenames are normalization insensitive |
| 0x0000000000000010 | APFS_INCOMPAT_INCOMPLETE_RESTORE | Unknown |
| 0x0000000000000020 | APFS_INCOMPAT_SEALED_VOLUME | Unknown |
| 0x0000000000000040 | APFS_INCOMPAT_RESERVED_40 | Unknown |
Volume role flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | APFS_VOL_ROLE_NONE | None |
| 0x0001 | APFS_VOL_ROLE_SYSTEM | System |
| 0x0002 | APFS_VOL_ROLE_USER | User |
| 0x0004 | APFS_VOL_ROLE_RECOVERY | Recovery |
| 0x0008 | APFS_VOL_ROLE_VM | VM |
| 0x0010 | APFS_VOL_ROLE_PREBOOT | Preboot |
| 0x0020 | APFS_VOL_ROLE_INSTALLER | Installer |
File system B-tree
The file system structures are stored in a B-tree.
The file system B-tree uses identifiers similar to catalog identifiers (CNIDs) on Hierarchical File System (HFS). In this document these identifiers are referred to as File System object identifiers (FSOIDs) to contrast other object identifiers (OIDs).
| FSOID | Identifier | Assignment |
|---|---|---|
| 0 | Unknown (Reserved) | |
| 1 | Parent identifier of the root directory (folder), nameless | |
| 2 | Directory identifier of the root directory (folder), named "root" | |
| 3 | Unknown, named "private-dir" |
File system B-tree key
The file system B-tree key is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object identifier and type (obj_id_and_type) | |||
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | File system data type | |
| 8 | ... | Optional additional key data dependent on the data type | |
File system data types
| Value | Identifier | Description |
|---|---|---|
| 0x0 | APFS_TYPE_ANY | Unknown (Any) |
| 0x1 | APFS_TYPE_SNAP_METADATA | Snapshot metadata |
| 0x2 | APFS_TYPE_EXTENT | Extent |
| 0x3 | APFS_TYPE_INODE | Inode |
| 0x4 | APFS_TYPE_XATTR | Extended attribute (xattr) |
| 0x5 | APFS_TYPE_SIBLING_LINK | Sibling link |
| 0x6 | APFS_TYPE_DSTREAM_ID | Data stream identifier |
| 0x7 | APFS_TYPE_CRYPTO_STATE | Encryption state |
| 0x8 | APFS_TYPE_FILE_EXTENT | File extent |
| 0x9 | APFS_TYPE_DIR_REC | Directory record |
| 0xa | APFS_TYPE_DIR_STATS | Directory stats |
| 0xb | APFS_TYPE_SNAP_NAME | Snapshot name |
| 0xc | APFS_TYPE_SIBLING_MAP | Sibling map |
| 0xf | APFS_TYPE_INVALID | Invalid |
File system B-tree branch node value
A file system B-tree node contains branch node values if BTNODE_LEAF is not set. The corresponding file system B-tree key represents the first key in the branch.
A file system B-tree branch node value is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | B-tree sub node object identifier, which can be resolved to a "physical" location using the object map |
Snapshot metadata
The snapshot metadata value (j_snap_metadata_val_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Extent-reference tree block number, which contains a block number relative to the start of the container | |
| 8 | 8 | Volume superblock block number, which contains a block number relative to the start of the container | |
| 16 | 8 | Creation time, which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 24 | 8 | Change (or last modification) time, which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 32 | 8 | Unknown (inum) | |
| 40 | 4 | Extent-reference tree object type (extentref_tree_type) | |
| 44 | 4 | Flags | |
| 48 | 2 | Name string size (name_len), in number of bytes, which includes the size of the end-of-string character | |
| 50 | ... | Name string (name), which contains an UTF-8 encoded string with an end-of-string character |
Snapshot metadata flags
| Value | Identifier | Description |
|---|---|---|
| 0x00000001 | SNAP_META_PENDING_DATALESS | Unknown |
Extent
Extent key data
The extent key data (j_phys_ext_key_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x2 | File system data type |
Extent value data
The extent value data (j_phys_ext_val_t) is 20 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Extent size and data type (len_and_kind) | |||
| 0 | 60 bits | Extent size, in number of bytes | |
| 7.4 | 4 bits | File system data type | |
| 8 | 8 | File system object identifier of owner (owning_obj_id) | |
| 16 | 4 | Reference count (refcnt) | |
Inode
Inode key data
The inode key data (j_inode_key_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x3 | File system data type |
Inode value data
The inode value data (j_inode_val_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Parent file system object identifier (parent_id) | |
| 8 | 8 | Data stream file system object identifier (private_id), which contains the file system object identifier of the file extents that make up the data stream | |
| 16 | 8 | Creation date and time (create_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 24 | 8 | Modification date and time (mod_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 32 | 8 | Inode change date and time (change_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 40 | 8 | Access date and time (access_time), which consists of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 48 | 8 | Inode flags (internal_flags) | |
| 56 | 4 | Number of children (nchildren) or number of (hard) links (nlink) | |
| 60 | 4 | Unknown (default_protection_class) | |
| 64 | 4 | Unknown (write_generation_counter) | |
| 68 | 4 | BSD file entry flags (bsd_flags) | |
| 72 | 4 | Owner user identifier (owner) | |
| 76 | 4 | Group identifier (gid) | |
| 80 | 2 | File mode | |
| 82 | 2 | Unknown (pad1) | |
| 84 | 8 | Unknown (pad2) | |
| 92 | ... | Extended fields (xfields) |
Note that Mac OS stat command treats nchildren equivalent to nlink.
Inode flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000000000000001 | INODE_IS_APFS_PRIVATE | The inode is used internally, typically for a data stream |
| 0x0000000000000002 | INODE_MAINTAIN_DIR_STATS | The inode tracks the size of all of its children |
| 0x0000000000000004 | INODE_DIR_STATS_ORIGIN | The inode has the INODE_MAINTAIN_DIR_STATS flag set explicitly, not due to inheritance |
| 0x0000000000000008 | INODE_PROT_CLASS_EXPLICIT | The inode data protection class was set explicitly when the inode was created |
| 0x0000000000000010 | INODE_WAS_CLONED | The inode was created by cloning another inode |
| 0x0000000000000020 | INODE_FLAG_UNUSED | Unknown (Reserved) |
| 0x0000000000000040 | INODE_HAS_SECURITY_EA | The inode has an access control list (security extended attribute) |
| 0x0000000000000080 | INODE_BEING_TRUNCATED | The inode was truncated |
| 0x0000000000000100 | INODE_HAS_FINDER_INFO | The inode has a Finder info extended field |
| 0x0000000000000200 | INODE_IS_SPARSE | The inode has a sparse byte count extended field |
| 0x0000000000000400 | INODE_WAS_EVER_CLONED | The inode has been cloned at least once |
| 0x0000000000000800 | INODE_ACTIVE_FILE_TRIMMED | The inode is an overprovisioning file that has been trimmed |
| 0x0000000000001000 | INODE_PINNED_TO_MAIN | The inode file content is always on the main storage device. This flag is used for Fusion drives where the main storage is a solid-state drive |
| 0x0000000000002000 | INODE_PINNED_TO_TIER2 | The inode file content is always on the secondary storage device. This flag is used for Fusion drives where the secondary storage is a (magnetic) hard drive |
| 0x0000000000004000 | INODE_HAS_RSRC_FORK | The inode has a resource fork |
| 0x0000000000008000 | INODE_NO_RSRC_FORK | The inode does not have a resource fork |
| 0x0000000000010000 | INODE_ALLOCATION_SPILLEDOVER | The inode file content has some space allocated outside of the preferred storage tier for that file |
File modes
| Value | Identifier | Description |
|---|---|---|
| 0xf000 (0170000) | S_IFMT | File type bitmask |
| 0x1000 (0010000) | S_IFIFO | Named pipe |
| 0x2000 (0020000) | S_IFCHR | Character-special file (Character device) |
| 0x4000 (0040000) | S_IFDIR | Directory |
| 0x6000 (0060000) | S_IFBLK | Block-special file (Block device) |
| 0x8000 (0100000) | S_IFREG | Regular file |
| 0xa000 (0120000) | S_IFLNK | Symbolic link |
| 0xc000 (0140000) | S_IFSOCK | Socket |
| 0xe000 (0160000) | S_IFWHT | Whiteout |
A whiteout is a file entry that covers up all entries of a particular name from lower branches.
BSD file entry flags
The BSD file entry flags are defined in the <sys/stat.h> header file.
| Value | Identifier | Description |
|---|---|---|
| 0x0000ffff | UF_SETTABLE | bitmask of owner changeable flags |
| 0x00000001 | UF_NODUMP | do not dump file entry |
| 0x00000002 | UF_IMMUTABLE | file entry is immutable and may not be changed |
| 0x00000004 | UF_APPEND | writes to file entry may only append |
| 0x00000008 | UF_OPAQUE | directory is opaque wrt. union |
| 0x00000010 | UF_NOUNLINK | file entry may not be removed or renamed, which is not implement in Mac OS |
| 0x00000020 | UF_COMPRESSED | file entry is compressed |
| 0x00000040 | UF_TRACKED | notify about file entry changes |
| 0x00000080 | UF_DATAVAULT | entitlement required for reading and writing |
| 0x00008000 | UF_HIDDEN | file entry is hidden |
| 0xffff0000 | SF_SETTABLE | bitmask of superuser changeable flags |
| 0x001f0000 | SF_SUPPORTED | bitmask of superuser supported flags |
| 0x00010000 | SF_ARCHIVED | file entry is archived |
| 0x00020000 | SF_IMMUTABLE | file entry is immutable and may not be changed |
| 0x00040000 | SF_APPEND | writes to file entry may only append |
| 0x00080000 | SF_RESTRICTED | entitlement required for writing |
| 0x00100000 | SF_NOUNLINK | file entry may not be removed, renamed or used as mount point |
| 0x00200000 | SF_SNAPSHOT | snapshot inode, which is not implement in Mac OS |
Extended attribute
Extended attribute key data
The extended attribute key data (j_xattr_key_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x4 | File system data type |
| 8 | 2 | Name string size (name_len), in number of bytes, which includes the size of the end-of-string character | |
| 10 | ... | Name string (name), which contains an UTF-8 encoded string with an end-of-string character |
Note that the name of an extended attribute appears to be case senstive even on a case insensitive file system.
Extended attribute value data
The extended attribute value data (j_xattr_val_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Flags (flags) | |
| 2 | 2 | Extended attribute data size, in number of bytes | |
| 4 | ... | Extended attribute data |
Note that extended attribute data size can contain 0 if the extended attribute flag XATTR_DATA_EMBEDDED is set.
Extended attribute names
| Name | Description |
|---|---|
| com.apple.assetsd.dbRebuildInProgress | |
| com.apple.assetsd.dbRebuildUuid | |
| com.apple.assetsd.thumbnailCameraPreviewImageAssetID | |
| com.apple.assetsd.UUID | |
| com.apple.decmpfs | Apple File System Compression (decmpfs) extended attribute |
| com.apple.FinderInfo | |
| com.apple.fs.symlink | Symbolic link |
| com.apple.genstore.info | |
| com.apple.genstore.origdisplayname | |
| com.apple.genstore.orig_perms_v1 | |
| com.apple.genstore.origposixname | |
| com.apple.GeoServices.SHA1 | |
| com.apple.installd.installType | |
| com.apple.installd.uniqueInstallID | |
| com.apple.lastuseddate#PS | |
| com.apple.metadata:_kMDItemUserTags | |
| com.apple.metadata:com_apple_backup_excludeItem | |
| com.apple.metadata:kMDItemDownloadedDate | |
| com.apple.metadata:kMDItemWhereFroms | |
| com.apple.metadata:kMDLabel_fwlfb7nbt2o7degof3q2o2btjy | |
| com.apple.quarantine | |
| com.apple.ResourceFork | Resource fork |
| com.apple.rootless | |
| com.apple.system.Security | |
| com.apple.TextEncoding | |
| LastUpgradeCheck | |
| lock | |
| org.chromium.crashpad.database.initialized |
Extended attribute flags
| Value | Identifier | Description |
|---|---|---|
| 0x0001 | XATTR_DATA_STREAM | Extended attribute data is stored in a data stream, where the attribute data contains an 8-byte file system object identifier of the corresponding data stream |
| 0x0002 | XATTR_DATA_EMBEDDED | Extended attribute data is stored directly in the record |
| 0x0004 | XATTR_FILE_SYSTEM_OWNED | Extended attribute is part of the file system, such as "com.apple.fs.symlink" |
| 0x0008 | XATTR_RESERVED_8 | Unknown (Reserved) |
Extended attribute data stream
The extended attribute data stream (j_xattr_dstream_t) is 48 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Data stream file system object identifier (xattr_obj_id), which contains the file system object identifier of the file extents that make up the data stream | |
| 8 | 40 | Data stream descriptor |
Sibling link
Sibling link key data
The sibling link key data (j_sibling_key_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x4 | File system data type |
| 8 | 8 | Sibling map identifier (sibling_id), which contains the file system object identifier of the sibling map record |
Sibling link value data
The sibling link value data (j_sibling_val_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Parent file system object identifier (parent_id) | |
| 8 | 2 | Name string size (name_len), in number of bytes, which includes the size of the end-of-string character | |
| 10 | ... | Name string (name), which contains an UTF-8 encoded string with an end-of-string character |
Data stream identifier
Data stream identifier key data
The data stream key data (j_dstream_id_key_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x6 | File system data type |
Data stream identifier value data
The data stream value data (j_dstream_id_val_t) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Reference count (refcnt) |
File extent
File extent key data
The file extent key data (j_file_extent_key_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x8 | File system data type |
| 8 | 8 | Logical extent offset (or address) (logical_addr), which contains an offset relative to the start of the file entry data |
File extent value data
The file extent value data (j_file_extent_val_t) is 24 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Extent size and flags (len_and_flags) | |||
| 0 | 7 | Extent size, in number of bytes | |
| 7 | 1 | Flags | |
| 8 | 8 | Physical block number (phys_block_num), which contains a block number relative to the start of the container | |
| 16 | 8 | Encryption identifier (crypto_id), which contains an unknown value and 0 if not set | |
File extent flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | Unknown (Is encrypted?) |
Note that according to the Apple File System Reference documentation there are currently no flags defined. The reference documentation also refers to
len_and_flagsaslen_and_kindinterchangeably.
Directory record
The directory record can have 2 different types of keys:
- Key with name
- Key with name and hash
Note that apprears that current APFS file system use a key with name and hash. Apple File System Reference documentation does not indicate how to distinguish between the two, but one method is to compare calculated and stored size of the key data.
Note that B-tree branch nodes are sorted using the case-sensitive name, even when the file system is case-insensitive.
Directory record key data with name
The directory record key data with name (j_drec_key_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object identifier and type (hdr) | |||
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x9 | File system data type |
| 8 | 2 | Name string size (name_len), in number of bytes, which includes the size of the end-of-string character | |
| 10 | ... | Name string (name), which contains an UTF-8 encoded string with an end-of-string character | |
Directory record key data with name and hash
The directory record key data with name and hash (j_drec_hashed_key_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object identifier and type (hdr) | |||
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x9 | File system data type |
| Name string size and hash (name_len_and_hash) | |||
| 8 | 11 bits | Name string size, in number of bytes, which includes the size of the end-of-string character | |
| 9.3 | 21 bits | Name hash | |
| 12 | ... | Name string (name), which contains an UTF-8 encoded string with an end-of-string character | |
Directory record value data
The directory record value data (j_drec_val_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | File system object identifier of the directory entry (file_id) | |
| 8 | 8 | Date and time the directory entry was added (date_added), which consist of a signed integer that contains the number of nanoseconds since January 1, 1970 00:00:00 UTC or 0 if not set | |
| 16 | 2 | Directory entry flags | |
| 18 | ... | Extended fields (xfields) |
Directory entry flags
| Value | Identifier | Description |
|---|---|---|
| 0x0000 | DT_UNKNOWN | Unknown |
| 0x0001 | DT_FIFO | Named pipe |
| 0x0002 | DT_CHR | Character-special file (Character device) |
| 0x0004 | DT_DIR | Directory |
| 0x0006 | DT_BLK | Block-special file (Block device) |
| 0x0008 | DT_REG | Regular file |
| 0x000a | DT_LNK | Symbolic link |
| 0x000c | DT_SOCK | Socket |
| 0x000e | DT_WHT | Whiteout |
| 0x000f | DREC_TYPE_MASK | Directory type bitmask |
| 0x0010 | RESERVED_10 | Unknown (reserved) |
A whiteout is a file entry that covers up all entries of a particular name from lower branches.
Directory entry name hash
The name hash of a directory entry is calculated as following:
- If the file system is case-insensitive represent the name in lower-case
- Represent the name as an Unicode string in Normalization Form Canonical Decomposition (NFD)
- Format the Unicode string as a little-endian UTF-32 stream without a byte-order-mark or end-of-string character
- Calculate a CRC-32c checksum of the UTF-32 stream with an initial checksum of 0xffffffff (-1)
- The lower 22-bits of checksum form the hash
The CRC-32 calculation uses the Castagnoli polynomial (0x1edc6f41), also known as CRC-32C (or CRC32-C). The CRC-32 calculation does not use the XOR with 0xffffffff before and after the calculation, which is also referred to as weak CRC-32 calculation.
Directory stats
Directory stats key data
The directory stats key data (j_dir_stats_key_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0xa | File system data type |
Directory stats value data
The directory stats value data (j_dir_stats_val_t) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Number of children (num_children) | |
| 8 | 8 | Total size (total_size), in number of bytes | |
| 16 | 8 | Parent directory file system object identifier (chained_key) | |
| 24 | 8 | Generation count (gen_count) |
Snapshot name
The snapshot name (j_snap_name_val_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | Snapshot metdata object identifier | |
| 7.4 | 4 bits | 0x1 | File system data type |
Sibling map
Sibling map key data
The sibling map key data (j_sibling_map_key_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 60 bits | File system object identifier (FSOID) | |
| 7.4 | 4 bits | 0x4 | File system data type |
Sibling map value data
The sibling map value data (j_sibling_map_val_t) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | File system object identifier (file_id) |
Extended fields
Directory entries and inodes use extended fields to store additional attributes, such as the filename.
The extended fields (xf_blob_t) consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | Number of extended fields (xf_num_exts) | |
| 2 | 2 | Extended fields data size (xf_used_data), in number of bytes | |
| Extended fields data (xf_data) | |||
| 4 | ... | Array of extended field descriptors | |
| ... | ... | Extended fields data | |
Note that extended field values are stored 8-byte aligned in the extended field value data.
Extended field descriptor
An extended field descriptor (x_field_t) is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Extended field type (x_type) | |
| 1 | 1 | Extended field flags (x_flags) | |
| 2 | 2 | Extended field data size (x_size), in number of bytes |
Extended field types
Directory record extended field types
| Value | Identifier | Description |
|---|---|---|
| 1 | DREC_EXT_TYPE_SIBLING_ID | Hard link sibling identifier, where the extended field data contains a 64-bit integer value |
Inode extended field types
| Value | Identifier | Description |
|---|---|---|
| 1 | INO_EXT_TYPE_SNAP_XID | Transaction identifier of a snapshot, where the extended field data contains a 64-bit integer value |
| 2 | INO_EXT_TYPE_DELTA_TREE_OID | Object identifier of the snapshot extent delta list, where the extended field data contains a 64-bit integer value |
| 3 | INO_EXT_TYPE_DOCUMENT_ID | Document identifier, where the extended field data contains a 32-bit integer value |
| 4 | INO_EXT_TYPE_NAME | Filename, where the extended field data contains an UTF-8 string with end-of-string character |
| 5 | INO_EXT_TYPE_PREV_FSIZE | Previous file size, in number of bytes, where the extended field data contains a 64-bit integer value |
| 6 | INO_EXT_TYPE_RESERVED_6 | Unknown (Reserved) |
| 7 | INO_EXT_TYPE_FINDER_INFO | Finder information, where the extended field data contains a 32-bit integer value |
| 8 | INO_EXT_TYPE_DSTREAM | Data stream, where the extended field data contains a data stream descriptor |
| 9 | INO_EXT_TYPE_RESERVED_9 | Unknown (Reserved) |
| 10 | INO_EXT_TYPE_DIR_STATS_KEY | Directory statistics; it is unknown if the extended field data contains an object identifier of the directory statistics or a j_dir_stats_val_t structure, seen 8 byte value |
| 11 | INO_EXT_TYPE_FS_UUID | Mounted file system identifier, where the extended field data contains a 128-bit UUID value |
| 12 | INO_EXT_TYPE_RESERVED_12 | Unknown (Reserved) |
| 13 | INO_EXT_TYPE_SPARSE_BYTES | Number of sparse bytes in the data stream, where the extended field data contains a 64-bit integer value |
| 14 | INO_EXT_TYPE_RDEV | Block or character device identifier, where the extended field data contains a 32-bit integer value |
| 15 | INO_EXT_TYPE_PURGEABLE_FLAGS | Information about a purgeable file; unknown, defined as reserved, seen 8 byte value |
| 16 | INO_EXT_TYPE_ORIG_SYNC_ROOT_ID | Unknown (Inode number of the sync-root hierarchy) |
Extended field flags
| Value | Identifier | Description |
|---|---|---|
| 0x01 | XF_DATA_DEPENDENT | Contents of the extended field is dependent on the data stream (file contents) |
| 0x02 | XF_DO_NOT_COPY | Do not duplicate the extended field when copied |
| 0x04 | XF_RESERVED_4 | Unknown (Reserved) |
| 0x08 | XF_CHILDREN_INHERIT | Newly created sub directory entries (children) inherit the extended field |
| 0x10 | XF_USER_FIELD | Extended field was added by an user-space program |
| 0x20 | XF_SYSTEM_FIELD | Extended field was added by the system (kernel) |
| 0x40 | XF_RESERVED_40 | Unknown (Reserved) |
| 0x80 | XF_RESERVED_80 | Unknown (Reserved) |
Device identifier
The device identifier can be stored in different formats, such as: native, 386bsd, 4bsd, bsdos, freebsd, hpux, isc, linux, netbsd, osf1, sco, solaris, sunos, svr3, svr4 and ultrix.
The “native” and “hpux” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 | Major device number | |
| 1 | 2 | 0 | Unknown |
| 3 | 1 | Minor device number |
The “386bsd”, “4bsd”, “freebsd”, “isc”, “linux”, “netbsd”, “sco”, “sunos”, “svr3” and “ultrix” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 2 | 0 | Unknown |
| 2 | 1 | Major device number | |
| 3 | 1 | Minor device number |
The “solaris” and “svr4” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 18 bits | Minor device number | |
| 2.2 | 14 bits | Major device number |
The “bsdos” and “osf1” device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 20 bits | Minor device number | |
| 2.4 | 12 bits | Major device number |
The “bsdos” alternative device identifier is 4 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 8 bits | Sub unit number | |
| 1.0 | 12 bits | Unit number | |
| 2.4 | 12 bits | Major device number |
Data stream descriptor
The data stream descriptor (j_dstream_t) is 40 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Used size (size), in number of bytes | |
| 8 | 8 | Allocated size (alloced_size), in number of bytes | |
| 16 | 8 | (Default) encryption identifier (default_crypto_id) | |
| 24 | 8 | Total number of bytes written to data stream (total_bytes_written) | |
| 32 | 8 | Total number of bytes read from data stream (total_bytes_written) |
File content
APFS supports multiple ways to store file content:
- Data fork
- Compressed data extended attribute
- Compressed data extended attribute with resource fork
- Resource fork
- Extended attribute (named fork)
Data fork
The file content size is stored in an INO_EXT_TYPE_DSTREAM inode extended field type.
The file content data can be located through the file extents for the data stream file system object identifier in the file system tree.
If the volume is encrypted the file content is encrypted with the encryption identifier in defined by the file extent.
If the inode flag INODE_IS_SPARSE is set the file contains one or more spare file extents. A sparse file extent has a physical block number of 0.
Compressed data extended attribute
The file content data and size are stored in the compressed data header of a “com.apple.decmpfs” extended attribute.
Note it is currently assumed that the file entry either must not have a data fork or it the data fork must be of size 0.
Also see: Apple File System Compression (decmpfs).
Compressed data extended attribute with resource fork
The file content size is stored in the compressed data header of a “com.apple.decmpfs” extended attribute.
The file content data is stored in a “com.apple.ResourceFork” extended attribute.
Note it is currently assumed that the file entry either must not have a data fork or it the data fork must be of size 0.
Also see: Apple File System Compression (decmpfs).
Resource fork
TODO: complete this section.
Extended attribute (named fork)
TODO: complete this section.
EFI jumpstart
The EFI jumpstart (nx_efi_jumpstart_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x00000014 | Object type |
| 28 | 4 | 0x00000000 | Object subtype |
| Object values | |||
| 32 | 4 | "RDSJ" | Signature (nej_magic) |
| 36 | 4 | 1 | Format version (nej_version) |
| 40 | 4 | Unknown (nej_efi_file_len?) | |
| 44 | 4 | Number of extents (nej_num_extents) | |
| 48 | 16 x 8 | Unknown (nej_reserved?) | |
| 176 | number of extents x 16 | EFI jumpstart extents (nej_rec_extents), which contains the location where the EFI driver is stored | |
EFI jumpstart extent
The EFI jumpstart extent (prange_t) is 16 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Block number | |
| 8 | 8 | Number of blocks |
Extent-reference tree
TODO: complete this section.
Snapshots
TODO: complete this section.
Snapshot metadata tree
The snapshot metadata tree consists of:
- snapshot metadata tree (object)
- snapshot metadata B-tree
Snapshot metadata tree object
The snapshot metadata tree object is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x40000002 or 0x40000003 | Object type |
| 28 | 4 | 0x00000010 | Object subtype |
Snapshot metadata B-tree
The object map values are stored in B-tree.
Snapshot metadata B-tree key
The snapshot metadata B-tree key (j_snap_metadata_key_t or j_snap_name_key_t) is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Key object identifier (hdr) | |
| If key object identifier data type is APFS_TYPE_SNAP_NAME | |||
| 8 | ... | Snapshot name string, which contains an UTF-8 encoded string with an end-of-string character | |
Snapshot metadata B-tree branch node value
A snapshot metadata B-tree node contains branch node values if BTNODE_LEAF is not set. The corresponding inapshot metadata B-tree key represents the first key in the branch.
A snapshot metadata B-tree branch node value is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 8 | Sub node block number, which contains a block number relative to the start of the container |
Snapshot metadata B-tree leaf node value
The contents of a snapshot metadata B-tree leaf node depends on the file system data type of the key object identifier.
| Value | Description |
|---|---|
| APFS_TYPE_SNAP_METADATA | Snapshot metadata object identifier |
| APFS_TYPE_SNAP_NAME | Snapshot name |
Fusion drives
A Fusion drive consists of a main SSD and a tier2 magnetic disk that together form one logical APFS container.
Fusion middle tree
TODO: complete this section.
| Offset | Size | Value | Description |
|---|---|---|---|
| Object header | |||
| 0 | 8 | Object checksum, which contains 0 if not set | |
| 8 | 8 | Object identifier | |
| 16 | 8 | Object transaction identifier (xid) | |
| 24 | 4 | 0x40000002 | Object type |
| 28 | 4 | 0x00000015 | Object subtype |
| Object values | |||
| ... | ... | Unknown | |
Format edge cases and corruption scenarios
Container key bag is hardware encrypted but volume is not encrypted
Seen in APFS containers created by certain digital forensics tools. The container key bag is either hardware encrypted or contains random data but the volume is not encrypted.
Notes
TODO describe evict_mapping_val_t
References
- Apple File System Reference, by Apple
Assorted formats
- Apple Data Compression (ADC)
- LZFSE compressed data
- LZVN compressed data
- Property list (plist)
- Zlib compressed data
Apple Data Compression (ADC) data format
ADC compression is used in various data formats used on Mac OS, including Universal Disk Image Format (UDIF) files (.dmg).
Overview
ADC compressed data consist of:
- one or more chunks
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
ADC chunk
An ADC chunk is of variable size and consists of consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 1 bit | Literal chunk flag | |
| If literal chunk flag is set (1) | |||
| 0.1 | 7 bits | Literal data size, in number of bytes, where size = value + 1 | |
| 1.0 | ... | Literal (uncompressed) data | |
| If literal chunk flag is not set (0) | |||
| 0.1 | 1 bit | Extended-size chunk flags | |
| If extended-size chunk flag is not set (0) | |||
| 0.2 | 4 bits | Compressed data size, in number of bytes, where size = value + 3 | |
| 0.6 | 10 bits | Compressed data distance, where 0 is the offset of the last previously uncompressed byte | |
| If extended-size chunk flag is set (1) | |||
| 0.2 | 6 bits | Compressed data size, in number of bytes, where size = value + 4 | |
| 1.0 | 16 bits | Compressed data distance, where 0 is the offset of the last previously uncompressed byte | |
LZFSE compressed data format
LZFSE compression is used in various data formats used on Mac OS, including Universal Disk Image Format (UDIF) files (.dmg) and Apple File System Compression (decmpfs), which is used in Hierarchical File System (HFS) and Apple File System (APFS).
Overview
LZFSE compressed data stream consist of:
- one or more blocks
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | little-endian |
LZFSE block
A LZFSE block is of variable size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 4 | Block signature | |
| 4 | ... | Block data |
Block signatures
| Value | Identifier | Description |
|---|---|---|
| 0x00000000 | LZFSE_NO_BLOCK_MAGIC | Invalid |
| 0x24787662 ("bvx$") | LZFSE_ENDOFSTREAM_BLOCK_MAGIC | End-of-stream block (marker) |
| 0x2d787662 ("bvx-") | LZFSE_UNCOMPRESSED_BLOCK_MAGIC | Uncompressed (raw) block |
| 0x31787662 ("bvx1") | LZFSE_COMPRESSEDV1_BLOCK_MAGIC | LZFSE compressed block with uncompressed tables |
| 0x32787662 ("bvx2") | LZFSE_COMPRESSEDV2_BLOCK_MAGIC | LZFSE compressed block with compressed tables |
| 0x6e787662 ("bvxn") | LZFSE_COMPRESSEDLZVN_BLOCK_MAGIC | LZVN compressed block |
End-of-stream block
An end-of-stream block is 4 bytes in size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header | |||
| 0 | 4 | "bvx$" | Block signature |
Uncompressed block
An uncompressed block is of variable size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header | |||
| 0 | 4 | "bvx-" | Block signature |
| 4 | 4 | Uncompressed data size | |
| 8 | ... | Uncompressed data | |
LZFSE compressed block with uncompressed tables
A LZFSE compressed block with uncompressed tables (lzfse_compressed_block_header_v1) is of variable size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header | |||
| 0 | 4 | "bvx1" | Block signature |
| 4 | 4 | Uncompressed data size | |
| 8 | 4 | Compressed data size | |
| 12 | 4 | Number of literals | |
| 16 | 4 | Number of L, M, D values | |
| 20 | 4 | Number of bytes used to encode literals | |
| 24 | 4 | Number of bytes used to encode matches | |
| 28 | 4 | Unknown (Final accum_nbits for literals stream) | |
| 32 | 2 | First literal state | |
| 34 | 2 | Second literal state | |
| 36 | 2 | Third literal state | |
| 38 | 2 | Fourth literal state | |
| 40 | 4 | Unknown (accum_nbits for the l, m, d stream) | |
| 44 | 2 | L value state | |
| 46 | 2 | M value state | |
| 48 | 2 | D value state | |
| 50 | 720 | Frequency table | |
| 770 | ... | encoded literals | |
| ... | ... | encoded L, M, D values | |
LZFSE compressed block with compressed tables
A LZFSE compressed block with compressed tables (lzfse_compressed_block_header_v2) is of variable size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header | |||
| 0 | 4 | "bvx2" | Block signature |
| 4 | 4 | Uncompressed data size | |
| 3 x 64-bit packed fields | |||
| 8.0 | 20 bits | Number of literals | |
| 10.4 | 20 bits | Number of bytes used to encode literals | |
| 13.0 | 20 bits | Number of L, M, D values | |
| 15.4 | 3 bits | Unknown (Final accum_nbits for literals stream) | |
| 14.7 | 1 bit | Unknown (unused) | |
| 16.0 | 10 bits | First literal state | |
| 17.2 | 10 bits | Second literal state | |
| 18.4 | 10 bits | Third literal state | |
| 19.6 | 10 bits | Fourth literal state | |
| 21.0 | 20 bits | Number of bytes used to encode matches | |
| 22.4 | 3 bits | Unknown (accum_nbits for the l, m, d stream) | |
| 23.7 | 1 bit | Unknown (unused) | |
| 24.0 | 32 bits | Block header size | |
| 28.0 | 10 bits | L value state | |
| 29.2 | 10 bits | M value state | |
| 30.4 | 10 bits | D value state | |
| 31.6 | 2 bits | Unknown (unused) | |
| If block header size > 32 | |||
| 32 | ... | Bit stream containing Huffman encoded frequency table | |
| Common | |||
| ... | ... | encoded literals | |
| ... | ... | encoded L, M, D values | |
Note that if the block header size is 32 the block does not contain frequency tables.
LZVN compressed block
A LZVN compressed block is of variable size and consits of:
| Offset | Size | Value | Description |
|---|---|---|---|
| Block header | |||
| 0 | 4 | "bvxn" | Block signature |
| 4 | 4 | Uncompressed data size | |
| 8 | 4 | Compressed data size | |
| 12 | ... | LZVN compressed data | |
LZFSE frequency table
A LZFSE frequency table consist of 360 16-bit values:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 20 | Literal run-lengths (L stream) frequency values | |
| 20 | 20 | Match sizes (M stream) frequency values | |
| 40 | 64 | Match distances (D stream) frequency values | |
| 104 | 256 | Literal frequency values |
Decoding the Huffman encoded frequency table
TODO: describe how to decode the Huffman encoded frequency table.
5-bits encoded value, special cases 8 and 14
lzfse_freq_nbits_table[32] = {
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14,
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14 };
lzfse_freq_value_table[32] = {
0, 2, 1, 4, 0, 3, 1, -1, 0, 2, 1, 5, 0, 3, 1, -1,
0, 2, 1, 6, 0, 3, 1, -1, 0, 2, 1, 7, 0, 3, 1, -1 };
LZVN compressed data format
LZVN compression is used in various data formats used on Mac OS, including Apple File System Compression (decmpfs), which is used in Hierarchical File System (HFS) and Apple File System (APFS).
Overview
LZVN compressed data consist of:
- a stream of opcodes
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
Opcodes
The opcodes consist of:
| Oppcode | Data | Description |
|---|---|---|
| 0xe0 | 11100000 LLLLLLLL <literal> | Large literal of size: L + 16 |
| 0xe1 - 0xef | 1110LLLL <literal> | Small literal of size: L |
| 0x00 - 0x05, 0x08 - 0x0d, 0x10 - 0x15, 0x18 - 0x1d, 0x20 - 0x25, 0x28 - 0x2d, 0x30 - 0x35, 0x38 - 0x3d, 0x40 - 0x45, 0x48 - 0x4d, 0x50 - 0x55, 0x58 - 0x5d, 0x60 - 0x65, 0x68 - 0x6d, 0x80 - 0x85, 0x88 - 0x8d, 0x90 - 0x95, 0x98 - 0x9d, 0xc0 - 0xc5, 0xc8 - 0xcd | LLMMMDDD DDDDDDDD <literal> | Small distance: M + 3 |
| 0xa0 - 0xbf | 101LLMMM DDDDDDMM DDDDDDDD <literal> | Medium distance: M + 3 |
| 0x07, 0x0f, 0x17, 0x1f, 0x27, 0x2f, 0x37, 0x3f, 0x47, 0x4f, 0x57, 0x5f, 0x67, 0x6f, 0x87, 0x8f, 0x97, 0x9f, 0xc7, 0xcf | LLMMM111 DDDDDDDD DDDDDDDD <literal> | Large distance: M + 3 |
| 0x46, 0x4e, 0x56, 0x5e, 0x66, 0x6e, 0x86, 0x8e, 0x96, 0x9e, 0xc6, 0xce | LLMMM110 | Previous distance |
| 0x0e, 0x16 | None (nop) | |
| 0x06 | End of stream (eos) | |
| 0xf1 - 0xff | 1111MMMM | Small match |
| 0xf0 | 11110000 MMMMMMMM | Large match: M + 16 |
| 0x1e, 0x26, 0x2e, 0x36, 0x3e, 0x70 - 0x7f, 0xd0 - 0xdf | Invalid |
Property list (plist) format
The property list (plist) formats are used to store various kinds of data, for example configuration data. The format is know to be used stand-alone as well as embedded in other data formats.
Overview
Known plist formats are:
- ASCII plist format
- Binary plist format
- XML plist format
TODO: What about other plist formats like JSON?
Value types
| Type | Description |
|---|---|
| array | Collection of plist values without key |
| boolean | Boolean value |
| data | Binary data |
| date | Date and time value |
| dictionary | Collection of plist values with key |
| integer | Signed integer value |
| real | Floating-point value |
| string | String value |
ASCII plist format
TODO: complete section
Binary plist format
A binary plist file consists of:
- header
- object table
- offset table
- trailer
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
| Date and time values | Number of seconds since Jan 1, 2001 00:00:00 UTC |
| Character strings | UTF-16 big-endian |
Binary plist header
The binary plist header (CFBinaryPlistHeader) is 8 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 6 | "bplist" | Signature |
| 6 | 2 | Format version |
Format versions
| Version | Description |
|---|---|
| "00" | Supported as of Tiger |
| "01" | Supported as of Leopard |
| "0x" | Supported as of Snow Leopard, where x is any character |
Object table
The object table consists of:
- zero or more objects
Objects are of variable size and consist of:
- an object maker byte
- (optional) object data
Object marker byte
| Value | Identifier | Description |
|---|---|---|
| 0x00 | kCFBinaryPlistMarkerNull | Empty value (NULL) |
| 0x08 | kCFBinaryPlistMarkerFalse | Boolean False |
| 0x09 | kCFBinaryPlistMarkerTrue | Boolean True |
| 0x0f | kCFBinaryPlistMarkerFill | Unknown (Fill byte?) |
| 0x1# | kCFBinaryPlistMarkerInt | Integer, where 2^# is the number of bytes |
| 0x2# | kCFBinaryPlistMarkerInt | Floating point, where 2^# is the number of bytes |
| 0x33 | kCFBinaryPlistMarkerDate | Date and time value, which is stored as a 64-bits floating point that contains the number of seconds since Jan 1, 2001 00:00:00 UTC |
| 0x4# | kCFBinaryPlistMarkerData | Binary data, where # is the number of bytes. If # is 15 then the object marker byte is followed by a 32-bit integer that contains the size of the data |
| 0x5# | kCFBinaryPlistMarkerASCIIString | ASCII string, where # is the number of characters. If # is 15 then the object marker byte is followed by an integer object that contains the number of characters in the string. The string is stored in ASCII (with codepage?) without an end-of-string marker |
| 0x6# | kCFBinaryPlistMarkerUnicode16String | Unicode string, where # is the number of characters. If # is 15 then the object marker byte is followed by an integer object that contains the number of characters in the string. The string is stored in UTF-16 big-endian without an end-of-string marker |
| 0x7# | Unused | |
| 0x8# | kCFBinaryPlistMarkerUID | UID, where # + 1 is the number of bytes |
| 0x9# | Unused | |
| 0xa# | kCFBinaryPlistMarkerArray | Array of objects, where # is the number of elements. If # is 15 then the object marker byte is followed by an integer object that contains the number of elements in the array |
| 0xb# | Unused | |
| 0xc# | kCFBinaryPlistMarkerSet | Set of objects, where # is the number of elements. If # is 15 then the object marker byte is followed by an integer object that contains the number of ele,emts in the set |
| 0xd# | kCFBinaryPlistMarkerDict | Dictionary of key value pairs, where # is the number of key value pairs. If # is 15 then the object marker byte is followed by an integer object that contains the number of key value pairs in the dictionary |
| 0xe# | Unused | |
| 0xf# | Unused |
Array object
The array object consists of:
- array object marker with number of elements
- array of object references that identify the element objects.
- the element object data
The byte size of the object reference is defined in the trailer. An object reference of 1 will refer to the first object in the (object) offset table.
Set object
The set object consists of:
- set object marker with number of elements
- array of object references that identify the element objects.
- the element object data
The byte size of the object reference is defined in the trailer. An object reference of 1 will refer to the first object in the (object) offset table.
Dictionary object
The dictionary object consists of:
- dictionary object marker with number of key and value pairs
- array of key references that identify key objects.
- array of object references that identify the value objects.
- the key/value object data
The byte size of the key and object reference is defined in the trailer. A key and object reference of 1 will refer to the first object in the (object) offset table.
(Object) offset table
The offset table consists of an array of offsets. The trailer defines:
- The location of the offset table
- The offset byte size
- The number of offsets in the table
The offset values are relative from the start of the file.
Binary plist trailer
The binary plist trailer (CFBinaryPlistTrailer) is 32 bytes in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 5 x 1 | 0 | Unknown (0-byte values) |
| 5 | 1 | 0 | Unknown (Sort version) |
| 6 | 1 | Offset byte size | |
| 7 | 1 | Key and object reference byte size | |
| 8 | 8 | Number of objects | |
| 16 | 8 | Root (or top-level) object | |
| 24 | 8 | Offset table offset, where the offset is relative to the start of the file |
XML plist format
A XML plist file consists of:
- optional XML declaration
- optional Document Type Definition (DTD)
- plist root XML element
- key-value pair XML elements
For example:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="1.0">
...
</plist>
Zlib compressed data format
Zlib compression is commonly used in file formats. The zlib compressed data format, as defined in RFC1950, allows for multiple techniques but only the Deflate compression method, a variation of LZ77, is used.
Overview
Zlib compressed data consist of:
- data header
- compressed data
- Adler-32 checksum of the uncompressed data
Characteristics
| Characteristics | Description |
|---|---|
| Byte order | big-endian |
Data header
The data header is 2 or 6 bytes in size and consist of:
| Offset | Size | Value | Description |
|---|---|---|---|
| The bit values are stored a 8-bit values | |||
| 0.0 | 4 bits | Compression method | |
| 0.4 | 4 bits | Compression information | |
| Flags | |||
| 1.0 | 5 bits | Check bits | |
| 1.5 | 1 bit | Preset dictionary flag | |
| 1.6 | 2 bits | Compression level. The compression level is used mainly for re-compression | |
| If the dictionary identifier flag is set | |||
| 2 | 4 | Preset dictionary identifier, which contains an Adler-32 used to identifier the preset dictionary | |
| Common | |||
| ... | ... | Compressed data | |
| ... | 4 | Checksum, which contains an Adler-32 of the compressed data | |
The check bits value must be such that when the first 2 bytes are represented as a 16-bit unsigned integer in big-endian byte order the value is a multiple of 31, such that:
((first * 256) + second) % 31 = 0
Compression method
| Value | Identifier | Description |
|---|---|---|
| 8 | Deflate (RFC1951), with a maximum window size of 32 KiB | |
| 15 | Reserved for additional header data |
Note that RFC1950 only defines 8 as a valid compression method.
Compression information
The value of the compression information is dependent on the compression method.
Compression information - compression method 8 (Deflate)
For compression method 8 (Deflate) the compression information contains the base-2 logarithm of the LZ77 window size minus 8.
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.0 | 4 bits | Window size, which consists of a base-2 logarithm (2n), with a maximum value of 7 (32 KiB) |
To determine the corresponding window size:
1 << (7 + 8)
E.g. a compression information value of 7 indicates a 32768 bytes window size. Values larger than 7 are not allowed according to RFC1950 and thus the maximum window size is 32768 bytes.
Compression level
| Value | Identifier | Description |
|---|---|---|
| 0 | Fastest | |
| 1 | Fast | |
| 2 | Default | |
| 3 | Slowest, maximum compression |
Compressed data
Deflate compressed data
The deflate compressed data consists of one or more deflate compressed blocks. Each block consists of:
- block header
- block data
Note that a block can reference uncompressed data that is stored in a previous block.
Block header
The block header is 3 bits in size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0 | 1 bit | Last block (in stream) marker, where 1 represents the last block and 0 otherwise | |
| 0.1 | 2 bits | Block type |
Block types
| Value | Identifier | Description |
|---|---|---|
| 0 | Uncompressed (or stored) block | |
| 1 | Fixed Huffman compressed block | |
| 2 | Dynamic Huffman compressed block | |
| 3 | Reserved (not used) |
Uncompressed block data
The uncompressed block data is of variable size and consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.3 | 5 bits | Empty values (not used) | |
| 1 | 2 | Uncompressed data size | |
| 3 | 2 | Copy of uncompressed data size, which contains a 1s complement of the uncompressed data size | |
| 5 | ... | Uncompressed data |
The uncompressed data size can range between 0 and 65535 bytes.
Huffman compressed block data
The uncompressed block data is of variable size and consists of:
- Optional dynamic Huffman table
- Encoded bit-stream
- End-of-stream (or end-of-block or end-of-data) marker
Dynamic Huffman table
The dynamic Huffman table consists of:
| Offset | Size | Value | Description |
|---|---|---|---|
| 0.3 | 5 bits | Number of literal codes, which is value + 257. The number of literal codes must be smaller than 286 | |
| 1.0 | 5 bits | Number of distance codes, which is value + 1. The number of distance codes must be smaller than 30 | |
| 1.5 | 4 bits | The number of Huffman codes for the code sizes, which is value + 4 | |
| 2.1 | ... | The code sizes | |
| ... | ... | Huffman encoded stream of the Huffman codes for the literals | |
| ... | ... | Huffman encoded stream of the Huffman codes for the distances |
A single code size value is 3 bits of size. A value of 0 means the code size is not used in the Huffman encoding of the literal and distance codes.
The codes size values are stored in the following sequence:
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
The first value applies to a code size of 16, the second to 17, etc. Code sizes that are not stored default to 0.
The code size values are used to construct the code sizes Huffman table. This must be a complete Huffman table which is used to decode the literal and distance codes. The corresponding codes size Huffman encoding is defined as:
| Value | Identifier | Description |
|---|---|---|
| 0 - 15 | Represents a code size of 0 - 15 | |
| 16 | Copy the previous code size 3 - 6 times. The next 2 bits indicate repeat length (0 = 3, ... , 3 = 6), e.g. codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will expand to 12 code lengths of 8 (1 + 6 + 5) | |
| 17 | Repeat a code length of 0 for 3 - 10 times (3 bits of length) | |
| 18 | Repeat a code length of 0 for 11 - 138 times (7 bits of length) |
Both the literal and distance Huffman codes are stored Huffman encoded using the code sizes Huffman table. Code sizes that are not stored default to 0. The code size for the literal code 256 (end-of-block) should be set and thus not 0.
Encoded bit-stream
The encoded bit-stream is stored in 8-bit integers, where bit values are stored back-to-front. So that 3 least-significant bits (LSB) would represent a 3-bit value at the start of the -stream. Note that the LSB of the 3-bit value is the LSB of the byte value.
Deflate uses a Huffman tree of 288 Huffman codes (or symbols) where the values:
- 0 - 255; represent the literal byte values: 0 - 255
- 256: represents the end of (compressed) stream (or block)
- 257 - 285 (combined with extra-bits): represent a (size, offset) tuple (or match length) of 3 - 258 bytes
- 286, 287: are not used (reserved) and their use is considered illegal although the values are still part of the tree
This document refers to this Huffman tree as the literals Huffman tree.
The bits in the encoded bit-stream correspond to values in the literals Huffman tree. If a symbol is found that represents a compression size and offset tuple (or match length code) the bits following the literals symbol contains a distance (Huffman) code. The match length coedes might require additional (or extra) bits to store the length (or size).
The distances Huffman tree contains space for 32 symbols. The distance codes might require additional (or extra) bits to store the distance.
Literal codes
The literal codes consist of:
| Value | Identifier | Description |
|---|---|---|
| 0x00 – 0xff | literal byte values | |
| 0x100 | end-of-block marker | |
| 0 additional bits | ||
| 0x101 | Size of 3 | |
| 0x102 | Size of 4 | |
| 0x103 | Size of 5 | |
| 0x104 | Size of 6 | |
| 0x105 | Size of 7 | |
| 0x106 | Size of 8 | |
| 0x107 | Size of 9 | |
| 0x108 | Size of 10 | |
| 1 additional bit | ||
| 0x109 | Size of 11 to 12 | |
| 0x10a | Size of 13 to 14 | |
| 0x10b | Size of 15 to 16 | |
| 0x10c | Size of 17 to 18 | |
| 2 additional bits | ||
| 0x10d | Size of 19 to 22 | |
| 0x10e | Size of 23 to 26 | |
| 0x10f | Size of 27 to 30 | |
| 0x110 | Size of 31 to 34 | |
| 3 additional bits | ||
| 0x111 | Size of 35 to 42 | |
| 0x112 | Size of 43 to 50 | |
| 0x113 | Size of 51 to 58 | |
| 0x114 | Size of 59 to 66 | |
| 4 additional bits | ||
| 0x115 | Size of 67 to 82 | |
| 0x116 | Size of 83 to 98 | |
| 0x117 | Size of 99 to 114 | |
| 0x118 | Size of 115 to 130 | |
| 5 additional bits | ||
| 0x119 | Size of 131 to 162 | |
| 0x11a | Size of 163 to 194 | |
| 0x11b | Size of 195 to 226 | |
| 0x11c | Size of 227 to 257 | |
| 0 additional bits | ||
| 0x11d | Size of 258 | |
Distance codes
The distance codes consist of:
| Value | Identifier | Description |
|---|---|---|
| 0 | distance of 1 | |
| 1 | distance of 2 | |
| 2 | distance of 3 | |
| 3 | distance of 4 | |
| 1 additional bit | ||
| 4 | distance of 5 - 6 | |
| 5 | distance of 7 - 8 | |
| 2 additional bits | ||
| 6 | distance of 9 - 12 | |
| 7 | distance of 13 - 16 | |
| 3 additional bits | ||
| 8 | distance of 17 - 24 | |
| 9 | distance of 25 - 32 | |
| 4 additional bits | ||
| 10 | distance of 33 - 48 | |
| 11 | distance of 49 - 64 | |
| 5 additional bits | ||
| 12 | distance of 65 - 96 | |
| 13 | distance of 97 - 128 | |
| 6 additional bits | ||
| 14 | distance of 129 - 192 | |
| 15 | distance of 193 - 256 | |
| 7 additional bits | ||
| 16 | distance of 257 - 384 | |
| 17 | distance of 385 - 512 | |
| 8 additional bits | ||
| 18 | distance of 513 - 768 | |
| 19 | distance of 769 - 1024 | |
| 9 additional bits | ||
| 20 | distance of 1025 - 1536 | |
| 21 | distance of 1537 - 2048 | |
| 10 additional bits | ||
| 22 | distance of 2049 - 3072 | |
| 23 | distance of 3073 - 4096 | |
| 11 additional bits | ||
| 24 | distance of 4097 - 6144 | |
| 25 | distance of 6145 - 8192 | |
| 12 additional bits | ||
| 26 | distance 8193 - 12288 | |
| 27 | distance 12289 - 16384 | |
| 13 additional bits | ||
| 28 | distance 16385 - 24576 | |
| 29 | distance 24577 - 32768 | |
| other | ||
| 30-31 | not used, reserved and illegal but still part of the tree | |
TODO: complete this section
Additional bits
The additional bits are stored in big-endian (MSB first) and indicate the index into the corresponding array of size values (or base size + additional size).
| Value | Identifier | Description |
|---|---|---|
| 0 additional bits | ||
| 0 | Offset of 1 | |
| 1 | Offset of 2 | |
| 2 | Offset of 3 | |
| 3 | Offset of 4 | |
| 1 additional bit | ||
TODO: complete this section
Decompression
The decompression in pseudo code:
if( block_header.type == HUFFMANN_FIXED )
{
initialize the fixed Huffman trees
}
do
{
read block_header from input stream
if( block_header.type == UNCOMPRESSED )
{
align with next byte
read and check block_header.size and block_header.size_copy
read data of block_header.size
}
else
{
if( block_header.type == HUFFMANN_DYNAMIC )
{
read the dynamic Huffman trees (see subsection below)
}
loop (until end of block code recognized)
{
decode literal/length value from input stream
if( value < 256 )
{
copy value (literal byte) to output stream
}
else if value = end of block (256)
{
break from loop
}
else (value = 257..285)
{
decode distance from input stream
move backwards distance bytes in the output
stream, and copy length bytes from this
position to the output stream.
}
}
}
}
while( block_header.last_block_flag == 0 );
Adler-32 checksum
Zlib provides a highly optimized version of the algorithm provided below.
uint32_t adler32(
uint8_t *buffer,
size_t buffer_size,
uint32_t previous_key )
{
size_t buffer_iterator = 0;
uint32_t lower_word = previous_key & 0xffff;
uint32_t upper_word = ( previous_key >> 16 ) & 0xffff;
for( buffer_iterator = 0;
buffer_iterator < buffer_size;
buffer_iterator++ )
{
lower_word += buffer[ buffer_iterator ];
upper_word += lower_word;
if( ( buffer_iterator != 0 )
&& ( ( buffer_iterator % 0x15b0 == 0 )
|| ( buffer_iterator == buffer_size - 1 ) ) )
{
lower_word = lower_word % 0xfff1;
upper_word = upper_word % 0xfff1;
}
}
return( ( upper_word << 16 ) | lower_word );
}