Get a discount or gift!

APFS: Data Recovery Algorithm and File System Structure

ApFS offers an opportunity to restore certain states of the file system, including restoration of old or removed versions of files.. The container superblock contains a link to the element known as checkpoint. Such checkpoint refers to the preceding container superblock which stores the information on an older state of the file system.This way, several older states can be restored by analyzing the chain of the container superblock.

APFS: Data Recovery Algorithm and File System Structure

Contents:

Containers and volumes

The structure of ApFS file system is that of a B-tree, where the root directory containing data is the leaves of such tree. All branch nodes only contain links to the following node until they reach the leaf nodes. This file system uses containers as storage cells, and such cells can contain multiple ApFS volumes. Also, a container is the primary object for storing data. To contain one volume, it should be > 512 Mb, at least > 1024Mb is required to contain more than 2 volumes and so on.

The image below shows an overview of ApFS structure.

ApFS

Each element of this structure (except for the allocation file) starts with a 32-byte block header, which contains some general information about the block. The element that follows it is the body of the file system. It is composed of the following parts:

  • 0x01: Container Superblock
  • 0x02: Node
  • 0x05: Space manager
  • 0x07: Allocation Info File
  • 0x0C: Checkpoint
  • 0x0D: Volume Superblock

Containers are usually exactly the same as the GUID Partition Table (GPT) entries. They have their own crash protection and disk space allocation scheme. Each container contains one or more volumes or file systems, each of which has its own namespace, and a set of files and directories.

Although ApFS does not directly support software RAID, but it can be used with Apple RAID volumes to support Striping (RAID 0), Mirroring (RAID 1), and Concatenation (JBOD).

With a 64-bit index, ApFS volumes will support up to 9 quintillion (1018) files.

The new file system uses nanoseconds to set timestamps. In HFS+, timestamps were set to the nearest second. This will reduce the number of failures in data transfer and other file operations.

ApFS has a built-in encryption system and uses AES-XTS or AES-CBC systems, depending on the device. You can use several encryption keys to ensure data security even in the case of ”physical compromise” of the medium.

This is far from being a complete list of the innovations that ApFS can boast .

Partitions formatted in ApFS are not recognized by OS X 10.11 Yosemite and earlier versions of the operating system.

Block Header

Each element of the file system structure in ApFS starts with a block header. This header starts with a checksum. Other information in the header includes the copy-on-write version of the block, the block identifier (id) and the block type.

PositionSizeTypeID
08uint64Checksum
88uint64block ID
168uint64Version
242uint16Block type
262uint16Flags
284uint32Padding

From the table, we can see that 1uint = 1 bit, 8 bit = 1 byte, hence uint64 = 8, uint32 = 4, and uint16 = 2 bytes.

Container Superblock

The CS (Container Superblock) is the entry point to the file system. Because of the file system structure with containers and flexible volumes, allocation needs to be processed at the container level. The Container Superblock contains information on the block size, the number of blocks and pointers for this task in the space manager. In addition, the block identifiers (IDs) of all volumes are stored in the superblock. To map block IDs to block offsets a pointer to a B-tree block map is saved. This B-tree contains entries for each volume with its ID and offset. The Container Superblock is the highest level in the file system.

Here is how it looks like:

Offset (HEX)TypeIdDescription
0tApFS_COHHeader Container Object Header
20uint32MagicNumber (NXSB)The value to verify reading a Container Superblock.
24uint32BlockSizeContainer block size (bytes)
28uint64BlocksCountNumber of container blocks
30uint64FeaturesBitmap for container’s main features
38uint64ReadOnlyFeaturesBitmap for container’s main features (read-only)
40uint64IncompatibleFeaturesBitmap for container’s incompatible features
48tApFS_UuidUuidContainer’s UUID
58tApFS_IdentNextIdentNext identifier for new logical or virtual object
60tApFS_TransactionNextTransactionNext transaction number
68uint32DescriptorBlocksNumber of blocks used by descriptor
6Cuint32DataBlocksNumber of blocks used by data
70tApFS_AddressDescriptorBaseDescriptor base address or physical object identifier with address tree
78int32DataBaseDatabase address or physical object identifier with address tree
80uint32DescriptorNextNext descriptor index
84uint32DataNextNext data index
88uint32DescriptorIndexIndex of first valid element in descriptor segment
8Cuint32DescriptorLengthNumber of blocks in descriptor segment used by superblock
90uint32DataIndexIndex of first valid element in data segment
94uint32DataLengthNumber of blocks in data segment used by superblock
98tApFS_IdentSpaceManagerIdentIdentifier for Space Manager logical object
A0tApFS_IdentObjectsMapIdentIdentifier for container object map physical object
A8tApFS_IdentReaperIdentLogical object identifier
B0uint32ReservedForTesting
B4uint32MaximumVolumesMaximum possible number o volumes in container
8tApFS_IdentVolumesIdents[100]Volume array of virtual object identifiers
3D8uint64Counters[32]Array of counters storing container information
4D8tApFS_BlockRangeBlockedOutOfRangePhysical range of blocks that cannot be used
4E8tApFS_IdentMappingTreeIdentPhysical object identifier of the tree used to track objects moved from locked storage.
4F0uint64OtherFlagsBitmap of other container’s functions
4F8tApFS_AddressJumpstartEFIPhysical object ID with EFI driver data
500tApFS_UuidFusionUuidFusion container UUID or zero for non-Fusion containers
510tApFS_BlockRangeKeyLockerContainer key tag location
50uint64EphemeralInfo[4] Field array used to manage ephemeral data
540tApFS_IdentReservedForTesting
548tApFS_IdentFusionMidleTreeIdentFusion devices only
550tApFS_IdentFusionWriteBackIdentFusion devices only
558tApFS_BlockRangeFusionWriteBackBlocksBlocks used for cache area

With types defined:

uint8  tApFS_Uuid;
uint64 tApFS_Ident;
uint64 tApFS_Transaction;
int64  tApFS_Address;
uint64 tApFS_BTreeKey;

and

struct tApFS_BlockRange
{
    tApFS_Address       First;          // First block
    uint64              Count;          // Number of blocks
}
struct tApFS_COH
{
    uint64              CheckSum;       // Block checksum
    tApFS_Ident         Ident;          // Identifier
    tApFS_Transaction   Transaction;    // Object change transaction number
    uint16              Type;           // Object type
    uint16              Flags;          // Object flags
    uint32              SubType;        // Object subtype
};

with list of object types:

enum eApFS_ObjectType
{
    eApFS_ObjectType_01_SuperBlock            = 0x0001, //Container superblockа
    eApFS_ObjectType_02_BTreeRoot             = 0x0002, // B-Tree: root node
    eApFS_ObjectType_03_BTreeNode             = 0x0003, // B-Tree: non-root node
    eApFS_ObjectType_05_SpaceManager          = 0x0005, // Space manager
    eApFS_ObjectType_06_SpaceManagerCAB       = 0x0006, // Space manager: segments’ addresses
    eApFS_ObjectType_07_SpaceManagerCIB       = 0x0007, // Space manager: segments’ information
    eApFS_ObjectType_08_SpaceManagerBitmap    = 0x0008, // Free сspace bitmap used by Space manager
    eApFS_ObjectType_09_SpaceManagerFreeQueue = 0x0009, // Free space used by Space manager_ (keys - _tApFS_09_SpaceManagerFreeQueue_Key_, values - _tApFS_09_SpaceManagerFreeQueue_Value_)
    eApFS_ObjectType_0A_ExtentListTree        = 0x000A, // Extents’ list tree (keys – offset beginning extent_tApFS_Address_, value – physical data location _tApFS_BlockRange_)
    eApFS_ObjectType_0B_ObjectsMap            = 0x000B, // Type – Objects map; subType – Object map record tree (keys - _tApFS_0B_ObjectsMap_Key_, values - _tApFS_0B_ObjectsMap_Value_)
    eApFS_ObjectType_0C_CheckPointMap         = 0x000C, // Check point map 
    eApFS_ObjectType_0D_FileSystem            = 0x000D, // Volume file system
    eApFS_ObjectType_0E_FileSystemTree        = 0x000E, // File system tree (keys start from с _tApFS_BTreeKey_, describes key type and value)
    eApFS_ObjectType_0F_BlockReferenceTree    = 0x000F, // Block reference tree (keys - _tApFS_BTreeKey_, values - _tApFS_0F_BlockReferenceTree_Value_)
    eApFS_ObjectType_10_SnapshotMetaTree      = 0x0010, // Snapshot meta tree (keys - _tApFS_BTreeKey_, values - _tApFS_10_SnapshotMetaTree_Value_)
    eApFS_ObjectType_11_Reaper                = 0x0011, // Reaper
    eApFS_ObjectType_12_ReaperList            = 0x0012, // Reaper List
    eApFS_ObjectType_13_ObjectsMapSnapshot    = 0x0013, // Objects map snapshot tree (keys - _tApFS_Transaction_, values - _tApFS_13_ObjectsMapSnapshot_Value_)
    eApFS_ObjectType_14_JumpStartEFI          = 0x0014, // EFI Loader
    eApFS_ObjectType_15_FusionMiddleTree      = 0x0015, // Fusion devices tree to track SSD cached HDD, blocks (keys - _tApFS_Address_, values - _tApFS_15_FusionMiddleTree_Value_)
    eApFS_ObjectType_16_FusionWriteBack       = 0x0016, // Fusion devices writeback cache status
    eApFS_ObjectType_17_FusionWriteBackList   = 0x0017, // Fusion devices writeback cache list
    eApFS_ObjectType_18_EncryptionState       = 0x0018, // Encryption
    eApFS_ObjectType_19_GeneralBitmap         = 0x0019, // General Bitmap
    eApFS_ObjectType_1A_GeneralBitmapTree     = 0x001A, // General Bitmap Tree (keys - uint64, keys - uint64)
    eApFS_ObjectType_1B_GeneralBitmapBlock    = 0x001B, // General Bitmap Block
    eApFS_ObjectType_00_Invalid               = 0x0000, // Non-valid as a type or absent subtype
    eApFS_ObjectType_FF_Test                  = 0x00FF  // Reserved for testing (never stored on media)
    eApFS_ObjectType_FF_Test                  = 0x00FF  // Reserved for testing (never stored on media)
};

enum eApFS_ObjectFlag
{
    eApFS_ObjectFlag_Virtual         = 0x0000, // Virtual object
    eApFS_ObjectFlag_Ephemeral       = 0x8000, // Ephemeral object
    eApFS_ObjectFlag_Physical        = 0x4000, // Physical object
    eApFS_ObjectFlag_NoHeader        = 0x2000, // Object with no header _tApFS_ContainerObjectHeader_ (for example, Space (bitmap) manager bitmap)
    eApFS_ObjectFlag_Encrypted       = 0x1000, // Encrypted object
    eApFS_ObjectFlag_NonPersistent   = 0x0800, // Object with this flag is never saved on media
    eApFS_ObjectFlag_StorageTypeMask = 0xC000, // Bitmask (bitmask) for для accessing к object category flags
    eApFS_ObjectFlag_ValidMask       = 0xF800  // Valid flag bit mask
};

struct tApFS_0B_ObjectsMap_Key
{
    tApFS_Ident       ObjectIdent; // Object identifier
    tApFS_Transaction Transaction; // Transaction number
};

struct tApFS_0B_ObjectsMap_Value
{
    uint32       Flags;    // Flags
    uint32       Size;     // Object size in bytes (multiple of containerру block size)
    tApFS_Address Address; // Object address
};

struct tApFS_09_SpaceManagerFreeQueue_Key
{
    tApFS_Transaction sfqk_xid;
    tApFS_Address     sfqk_paddr;
};

struct tApFS_09_SpaceManagerFreeQueue_Value
{
    uint64            sfq_count;
    tApFS_Ident       sfq_tree_oid;
    tApFS_Transaction sfq_oldest_xid;
    uint16            sfq_tree_node_limit;
    uint16            sfq_pad16;
    uint32            sfq_pad32;
    uint64            sfq_reserved;
};

struct tApFS_10_SnapshotMetaTree_Value
{
    tApFS_Ident ExtentRefIdent;    // Identifier  of B-Tree physical object, that stores the extent information
    tApFS_Ident SuperBlockIdent;   // Superblock identifier
    uint64      CreatedTime;       // Snapshot creation time (in nanoseconds from midnight 01/01/1970)
    uint64      LastModifiedTime;  // Snapshot last modified  time (in nanoseconds from midnight 01/01/1970)
    uint64      iNum;
    uint32      ExtentRefTreeType; // Type of B-tree, that stores extent information
    uint16      NameLength;        // Snapshot name length (including end of line character)
    uint8       Name[];            // Snapshot name (ending with 0)
};

struct tApFS_13_ObjectsMapSnapshot_Value
{
    uint32      Flags;    // Snapshot flags
    uint32      Padding;  // Reserved (for adjustment)
    tApFS_Ident Reserved; // Reserved
};

struct tApFS_15_FusionMiddleTree_Value
{
    tApFS_Address fmv_lba;
    uint32        fmv_length;
    uint32        fmv_flags;
};

An example of ApFS structure:

An example of ApFS structure

Volume Superblock

The Volume Superblock exists for each volume within the file system. It contains the volume name, identifier (ID) and a timestamp. Similarly to the Container Superblock, it contains a pointer to a block map which represents block IDs to block offsets. Moreover, the Volume Superblock contains a pointer to the root directory which is stored as a node.

Offset (HEX)TypeIdDescription
0tApFS_COHHeader Container Object Header
20uint32 MagicNumber (APSB) A value that can be used to verify reading an instance of Volume Superblock
24uint32IndexInSuperBlock Object identifier index of this volume in the container volume array
28uint64Features Bitmap of the main features used by the volume
30uint64ReadOnlyFeatures Bitmap of the main features (read only) used by the volume
38uint64IncompatibleFeatures Bitmap of incompatible volume features
40uint64LastUnmountTime Time of the last volume dismount (in nanoseconds from midnight of 01/01/1970)
48uint64ReservedBlocksCount The number of blocks reserved to allocate the volume
50uint64QuotaBlocksCount The maximum number of blocks this volume can allocate
58uint64AllocatedCount The number of blocks currently allocated to the file system of this volume
60uint8 MetaCryptoState[20] Information about the key used to encrypt metadata for this volume (wrapped_meta_crypto_state_t instance)
74uint32RootTreeType Type of the root folder tree (typically: type of (eApFS_ObjectFlag_Virtual << 16) \ eApFS_ObjectType_02_BTreeRoot, subtype of eApFS_ObjectType_0E_FileSystemTree)
78uint32ExtentRefTreeType Type of extent binding tree (typically: type of (eApFS_ObjectFlag_Physical << 16) \ eApFS_ObjectType_02_BTreeRoot, subtype of OBJECT_TYPE_BLOCKREF)
7Cuint32SnapshotMetaTreeType Snapshot metadata tree type (typically: type of (eApFS_ObjectFlag_Physical << 16) \ eApFS_ObjectType_02_BTreeRoot, subtype of OBJECT_TYPE_BLOCKREF)
80tApFS_IdentObjectsMapIdent Identifier of the physical object of the volume object map
88tApFS_IdentRootTreeIdent Identifier of the virtual object of the root folder tree
90tApFS_IdentExtentRefTreeIdent Identifier of the physical object of the extent link tree
98tApFS_IdentSnapshotMetaTreeIdent Identifier of the snapshot metadata tree virtual object
A0tApFS_TransactionRevertToXid Transaction number of the snapshot to which the volume will be returned
A8tApFS_IdentRevertToSuperBlock Identifier of the physical object of the VS to which the volume will return
B0uint64NextObjectIdent The next identifier to be assigned to the file system object in the volume.
8uint64NumberOfFiles The number of regular files in the volume
C0uint64NumberOfDirectories The number of folders in the volume
C8uint64NumberOfSymbolicLinks Number of symbolic links in the volume
D0uint64NumberOfOtherObjects Number of other objects in the volume (not including x0B8_NumberOfFiles, x0C0_NumberOfDirectories and x0C8_NumberOfSymbolicLinks)
D8uint64NumberOfSnapshots Number of snapshots in this volume
E0uint64TotalBlocksAllocated Total number of blocks allocated by this volume
E8uint64TotalBlocksFreed Total number of blocks freed by this volume
F0tApFS_UuidUuid
100uint64LastModifiedTime Time of the last change of this volume (in nanoseconds from midnight 01/01/1970)
108uint64Flags
110tApFS_0D_FSMFormattedByInformation about the software that created this volume
140tApFS_0D_FSMModifiedBy[8] Information about the software that changed this volume
2C0uint8 VolumeName[256]UTF-8 String Zero Volume Name
3C0uint32NextDocumentIdent Next identifier of the document to be assigned (stored in the extended field APFS_0E_TYPE_DOCUMENT_ID of the document)
3C4uint16Role Volume Role Bitmap
3C6uint16Reserved
3C8 tApFS_TransactionRootToXid Snapshot transaction number for non-root or zero for root
3D0tApFS_IdentEncryptStateIdent Current state of encryption or decryption, or zero if no encryption

A detailed view of APFS:

A detailed APFS picture

Checkpoint

A checkpoint is a temporary state of the container. Each checkpoint is initialized with Container Superblock and the current state is usually the last. A checkpoint includes both the container and volume metadata. Restore points and snapshots are similar to each other. The main difference between a checkpoint and a snapshot is the user’s ability to restore the file system from stored snapshots using the file system’s API.

Checkpoint Superblock Descriptor

This block contains information about metadata structures in ApFS and is the preceding block to the Descriptor. The most important information in this block is the location of the Bitmap Structure (BMS), the former allocation file in HFS+.

Checkpoint superblock descriptor:

Checkpoint superblock descriptor

Bitmap Structures (bitmap or volume map)

Records of used and unused blocks. There is only one volume bitmap system that covers the whole container and is common to all volumes in the file system. ApFS uses a collection of blocks to store the Bitmap Structures (BMS).

In ApFS the bitmap is common to all volumes in the container. Each volume has a quote of the blocks in the container but the blocks are not in allocated areas. The link to the volume map is stored in Container Superblock Bitmap Descriptor, which contains the information about the topmost level of the structure, the Bitmap Descriptor (BMD) The picture below shows its basic structure. It is divided into levels where the BMD is on the top and sets the boundaries. At the bottom, you can see Bitmap Blocks that keep track of the blocks in the container. One byte in the BMB keeps track of eight blocks, where each bit provides the allocation status. Each bit is the status of a single block.

Bitmap Blocks

Tables

Tables are used in B-tree catalogs and extents, volume lists and the Object ID map.

Tables used in ApFS are small single block databases with slightly different purposes in the file system structures. The table type field is composed of 2 bytes located in the block with offset 0x20 directly after the node header. There are eight different table types, from 0 to 7. The next 2 bytes provide the table level from 0 and upwards. A level two table will have records referring to the basic level 1 table. Level 0 tables refer to blocks which often contain file metadata.

The table types are different in structure but the 24-byte table header is the same for all table types.

The picture below shows a sample table header structure:

The picture below shows a sample table header structure

Description of field meanings in the header structure:

Offset (HEX)FieldData typeDescription
20tableTypeuint16Possible values 0-7. It’s a table type 1
22tableLeveluint16Indicates a level of a B-Tree.
24tableRecordsuint16Number of records in the table
26Unknown 1uint16
28Unknown 2uint16
2AtableIndexSizeuint16Size of the table index area.
2CtableKeyAreaSizeuint16Size of the table key area.
2EtableFreeSpaceSizeuint16Size of the free area. The table data area ends at offset
0x38+tableIndexSize+tableKeyAreaSize+tableFreeSpaceSize. 0x38 + 0x80 + 0x170 + 0xd58.
30Unknown 3uint16
32Unknown 4uint16
34Unknown 5uint16
36Unknown 6uint16

A typical layout of different tables is shown here:

A typical layout for different tables

Not all of the elements in this picture are used in all the tables. It shows a complete block with the block/node header at the top. The rest of the block makes up the table.

The table header is followed by the record index. There are two types of them. The first is with 2 values only: offset in keys and offset in data section for each element of Uint16. The other uses 4 Uint16 values with offset and length for both key and data sections. The table record index contains information about keys and data records in the table. Another distinction between the table types is their use of footers.

Table types 1, 3, 5 and 7 use a 0x28 byte footer at the end of the block. In these tables all data offsets are related to offset 0xFD8, and the footer contains different values specific to the table type. Other table types have no footer and all references to the data section content are related to the end of the block.

In B-trees with several layers, table types 1, 3, 5 or 7 at the top-most level as they have a footer. The footer is used to store information about the complete B-tree. One of the values in the footer is the total number of records in the whole B-tree structure.

The table definition commences at offset 0x20 in the block with table type, number of rows, size of key section and gap between key and data section. After the table setup, the table row and column definitions are described from offset 0x38. The table contains a header, record definitions, key and data sections. Certain table types also have a footer. The header begins at offset 0x20 in the block and is 0x18 bytes in length. This table type header starts with a 16-bit value which represents the table type. It is followed by two bytes representing the level in the B-tree at which the table is used. The two subsequent bytes represent the number of rows in the table. The length of the record for scan definition is found at 0x2A followed by an Uint16 which records the length of the key section. This is followed by the gap between the key and data section. The table footer is always 0x28 bytes and always occupies the end of the block. Table indices are of 4 or 8 bytes each. On 8 byte indices, the two first Uint16 are the offset and length of the key record. The next two Uint16 are the offset and length of the data record in the table. Tables with 4 byte indices have two Uint16 values which is the offset to the key and data record. The data length in the two records is predefined. In tables with a footer the offset to the data record is relative to the start of the footer (0x28), and for the other table types it is relative to the end of the block.

Most of the values regarding table header and footer are clear at least for reading the type of table. Offset 0x18 in the footer (offset 0xFF in a 4 Kb block) is the number of records in the table and all underlying tables (if this is a table with level higher than 0 in offset 0x22). Offset 0x20 in the footer is the next record number in the table.

Table 0

Table type 0 is located in the B-tree catalog structure between leaf nodes and the root node. The values Unknown 3-6 appear to be the key offset and length, the data offset and length of the next available record. If there are no free index records the offsets are set to 0xFFFF and length 0x00.

The records in the table are four Uint16 values. The first 2 are the offset and length of value in key section and the next are the offset and value of the content in the data section.

An example of table type 0 could be Catalog Node ID and named key in the key section and Object ID in data section. This table does not have a footer.

Table 1

Table type 1 has a footer and the table index contains 4 16-bit values where the first 2 values are the offset to the record in the key section and the length of the record. The next 2 values provide the offset to the record in the data section and the length of this record. This table is frequently observed in both the B-tree Catalog structure and the Extents B-tree for the top-level node. Example values are Parent ID and a key name (file/folder name in the catalog structure and block start number in the Extent B-tree) in the key section and an Object ID when used as root-node in the Catalog structure or a block-number when used in the Extent B-tree.

Examples of this table are provided below:

Examples of this table are provided below

Table 2

Table type 2. Initially this table is identical to the previous one but has no footer. This table type is very frequently encountered in leaf nodes in the Catalog structure where the key section is represented with a Parent ID and key name.

Table type 3

This table type is similar to the previous one. The table index is the same as in table type 1. Typical values depend on the structure they are used in. In the catalog structure and the Extents B-tree this table is often used as top level node in small volumes where the root node serves both as root node and leaf node. In such example of use, the key record might be Parent ID. The name key and the data record might be a metadata file with large variations in size.

Other typical records could be Object ID and object type in the key record, with information extents on files and data records. Table type 3 has a footer.

Table example:

Table example

Table 4

Table type 4 is somewhat different from the previous ones. The table has no footer and the table index only has 2 values per record, the offset to the record in the key section and then 1 for the data section. The length of the content is fixed with 16 bytes in the key section and 8 bytes in the data section. Offsets in the data section are relative to the end of the block.

Table type 5

Table type 5 is very similar to type 4. The only difference is that this type has a footer and all offsets to data are from offset-0x28(beginning of the footer). The records in the key section are 16 bytes and 8 bytes in the data section. This table type is mostly observed at top level nodes in the Catalog structure and in larger containers with multilevel B-trees.

Table type 6

Table type 6 is very similar to type 4, too. The table index has only the offset to content in the key and data section and not the length. The lengths are predefined. Each record is 16 bytes. There is no footer for this type of table. This type of table is often observed in the leaf nodes in the Catalog structure. Typical key section content includes Object ID and Volume Checkpoint Superblock ID while the data section typically records the size of the data and a block number.

Table type 7

Table type 7 is very similar to type 6. The only difference is the footer that contains similar information to that described for table type 1. This table type is observed in a broad range of structures and is often encountered in the top most levels of multilayer structures or in single layer structures such as the Volume declarations.

Example

Example

Table summary

The following table shows the basic properties of the different table types:

TypeFooterKey section offsetKey section lengthData section offsetData section lengthKey lengthData length
0NOuint16uint16uint16uint16VariesPossible
1YESuint16uint16uint16uint16VariesPossible
2NOuint16uint16uint16uint16VariesPossible
3YESuint16uint16uint16uint16VariesPossible
4NOuint16uint1616 bytes8 bytes
5YESuint16uint1616 bytes8 bytes
6NOuint16uint1616 bytes16 bytes
7YESuint16uint1616 bytes16 bytes

One of the most important blocks in the B-tree Catalog structure is the root node which is the highest level in the folder structure. This node utilizes search keys of variable length. One of the improved features within ApFS is Fast Directory Searching (FDS). One of the values that is tightly connected with this feature is the count of all records in the tree structure located in the table footer.

In the B-tree catalog structure the root node has only 2 options in the selection of table to use since both of these have footers. Table type 3 acting as a root node is only observed in small containers with few files where the root node is also an index and leaf node.

In the B-tree Object Map only table type 5 is used for the root node, except in the case of very small structures where TYPE 7 may be encountered.

The interpretation of the tables shows that tables TYPE 0 and 2 have the same artifacts. The same is observed between tables TYPE 1 and 3. These tables appear to have a different purpose depending on which structure they are in.

Snapshots

Snapshots are read-only “pictures” of the file system in the volume. The operating system can use these snapshots to make backups more efficient, and due to using them, Time Machine will work faster. With ApFS support of Time Machine instant images, you no longer need to save several full copies of the file to your disk – it can simply track specific changes. For example, if you are editing a file, changing a single slide using HFS+ means saving two copies of the file, one in which your new changes are recorded, and one in case you want to return. In APFS, it simply saves the source file and records the differences between the source file and any updated versions, thus performing the same task in much less space. Just like the case with the improvements in Fusion Drive, the information takes up less space on the disk.

Although ApFSis considerably inferior in its capabilities to the 128-bit ZFS, which is supported by Linux, FreeBSD and other free operating systems, but on the part of Apple, this is a step in the right direction.

As mentioned above, Apple tried to port ZFS to OS X for a long time. Later OpenZFS was implemented for OS X (O3X) and MacZFX.

Nodes

Nodes are flexible containers that are used for storing different kinds of entries. They can be a part of a B-tree or exist on their own. Nodes can either contain flexible or fixed-size entries. A node starts with a list of pointers to the entry keys and entry records. This way, for each entry the node contains an entry header at the beginning of the node, an entry key in the middle of the node and an entry record at the end of the node.

Nodes
positionsizetypeID
04uint32alignment
44uint32ENTRY_COUNT
102uint16HEAD_SIZE
168entrymeta_entry
24entryentries (repeat entry_count times)

Node header:

OffsetfieldData typeComments
0ChecksumUint64Fletchers Checksum Algorithm
8IDUint64Object-ID or Block#
10CheckpointUint64
18UnknownUint16Possible level in B-Tree
1AUnknownUint16All observations show value 0x4000
1CUnknownUint16Flag?
1EUnknownUint16Often seen value 0x0b, 0x0e and 0x0f

Space Manager

Space Manager is used to manage allocated blocks in the ApFS container. It stores the number of free blocks and a pointer to the allocation info files.

positionsizetypeID
04uint32block size
168uint64totalblocks
408uint64freeblocks
1448uint64prev_allocationinfofile_block
3528uint64allocationinfofile_block

Allocation info file

The allocation info file works as a missing header for the allocation file. It stores the allocation files length, version and the offset of the allocation file.

positionsizetypeID
44uint32alloc_file_length
84uint32alloc_file_version
244uint32TOTAL_BLOCKS
284uint32free_blocks
324uint32allocationfile_block

B-tree file and folder

It records all files and folders in the volume. It performs the same role as the catalog file in HFS+.

Extents (B-tree extents)

A separate B-Tree of all extents per volume. Extents are references to file content with information about where the data content starts and the length of data in blocks. A file with some content will have at least one extent. A fragmented file will have multiple extents. The extent B-Tree is a separate structure.

In each file record, extents are defined per each of the files in the B-Tree. This separate extent structure is a part of the snapshot feature.

64-bit inodes (index descriptors)

64-bit inodes significantly increase the namespace compared to 32-bit identifiers in HFS+. The ApFS 64-bit file system supports more than 9 quintillion files on each volume. That ought to be enough for anybody, as Bill Gates said.

ApFS offers an opportunity to restore certain states of the file system, including restoration of old or removed versions of files. The container superblock contains a link to the element known as checkpoint. Such checkpoint refers to the preceding container superblock which stores the information about an older state of the file system. This way, we can try to restore several older states of the file system by analyzing this chain of superblocks inside the container.

Recommended For You