mirror of
https://github.com/WerWolv/ImHex-Patterns.git
synced 2026-03-28 07:47:02 -05:00
* updated APFS hexpat * fix null feature --------- Co-authored-by: Hrant Tadevosyan <Hrant.Tadevosyan@connectwise.com>
1479 lines
41 KiB
Rust
1479 lines
41 KiB
Rust
#pragma author Hrant Tadevosyan (Axcient, now ConnectWise)
|
|
#pragma description Apple File System (APFS)
|
|
#pragma endian little
|
|
|
|
// refs:
|
|
// - https://developer.apple.com/support/downloads/Apple-File-System-Reference.pdf
|
|
// - https://github.com/sgan81/apfs-fuse
|
|
// - https://github.com/libyal/libfsapfs
|
|
|
|
import std.core;
|
|
import std.hash;
|
|
import std.mem;
|
|
import type.guid;
|
|
import type.magic;
|
|
import type.time;
|
|
|
|
using paddr_t = s64;
|
|
|
|
struct prange {
|
|
paddr_t pr_start_paddr;
|
|
u64 pr_block_count;
|
|
};
|
|
|
|
using uuid_t = type::GUID;
|
|
|
|
#define MAX_CKSUM_SIZE 8
|
|
|
|
using oid_t = u64;
|
|
using xid_t = u64;
|
|
|
|
#define OID_NX_SUPERBLOCK 1
|
|
#define OID_INVALID 0ULL
|
|
#define OID_RESERVED_COUNT 1024
|
|
|
|
#define OBJECT_TYPE_MASK 0x0000ffff
|
|
#define OBJECT_TYPE_FLAGS_MASK 0xffff0000
|
|
|
|
#define OBJ_STORAGETYPE_MASK 0xc0000000
|
|
#define OBJECT_TYPE_FLAGS_DEFINED_MASK 0xf8000000
|
|
|
|
enum o_type_id_t : u16 {
|
|
OBJECT_TYPE_NX_SUPERBLOCK = 0x0001,
|
|
OBJECT_TYPE_BTREE = 0x0002,
|
|
OBJECT_TYPE_BTREE_NODE = 0x0003,
|
|
OBJECT_TYPE_SPACEMAN = 0x0005,
|
|
OBJECT_TYPE_SPACEMAN_CAB = 0x0006,
|
|
OBJECT_TYPE_SPACEMAN_CIB = 0x0007,
|
|
OBJECT_TYPE_SPACEMAN_BITMAP = 0x0008,
|
|
OBJECT_TYPE_SPACEMAN_FREE_QUEUE = 0x0009,
|
|
OBJECT_TYPE_EXTENT_LIST_TREE = 0x000a,
|
|
OBJECT_TYPE_OMAP = 0x000b,
|
|
OBJECT_TYPE_CHECKPOINT_MAP = 0x000c,
|
|
OBJECT_TYPE_FS = 0x000d,
|
|
OBJECT_TYPE_FSTREE = 0x000e,
|
|
OBJECT_TYPE_BLOCKREFTREE = 0x000f,
|
|
OBJECT_TYPE_SNAPMETATREE = 0x0010,
|
|
OBJECT_TYPE_NX_REAPER = 0x0011,
|
|
OBJECT_TYPE_NX_REAP_LIST = 0x0012,
|
|
OBJECT_TYPE_OMAP_SNAPSHOT = 0x0013,
|
|
OBJECT_TYPE_EFI_JUMPSTART = 0x0014,
|
|
OBJECT_TYPE_FUSION_MIDDLE_TREE = 0x0015,
|
|
OBJECT_TYPE_NX_FUSION_WBC = 0x0016,
|
|
OBJECT_TYPE_NX_FUSION_WBC_LIST = 0x0017,
|
|
OBJECT_TYPE_ER_STATE = 0x0018,
|
|
OBJECT_TYPE_GBITMAP = 0x0019,
|
|
OBJECT_TYPE_GBITMAP_TREE = 0x001a,
|
|
OBJECT_TYPE_GBITMAP_BLOCK = 0x001b,
|
|
OBJECT_TYPE_ER_RECOVERY_BLOCK = 0x001c,
|
|
OBJECT_TYPE_SNAP_META_EXT = 0x001d,
|
|
OBJECT_TYPE_INTEGRITY_META = 0x001e,
|
|
OBJECT_TYPE_FEXT_TREE = 0x001f,
|
|
OBJECT_TYPE_RESERVED_20 = 0x0020,
|
|
OBJECT_TYPE_INVALID = 0x0000,
|
|
OBJECT_TYPE_TEST = 0x00ff,
|
|
OBJECT_TYPE_CONTAINER_KEYBAG = 0x7973, // ys -> keys
|
|
OBJECT_TYPE_CONTAINER_KEYBAG_2 = 0x6B65, // ke ->
|
|
//OBJECT_TYPE_VOLUME_KEYBAG = "recs",
|
|
//OBJECT_TYPE_MEDIA_KEYBAG = "mkey",
|
|
};
|
|
|
|
enum o_flag_id_t : u16 {
|
|
OBJ_VIRTUAL = 0x0000,
|
|
OBJ_EPHEMERAL = 0x8000,
|
|
OBJ_PHYSICAL = 0x4000,
|
|
OBJ_NOHEADER = 0x2000,
|
|
OBJ_ENCRYPTED = 0x1000,
|
|
OBJ_NONPERSISTENT = 0x0800,
|
|
};
|
|
|
|
struct o_type_t {
|
|
o_type_id_t t_type;
|
|
o_flag_id_t t_flag;
|
|
};
|
|
|
|
struct obj_phys_t {
|
|
u8 o_cksum[MAX_CKSUM_SIZE];
|
|
oid_t o_oid;
|
|
xid_t o_xid;
|
|
o_type_t o_type;
|
|
o_type_t o_subtype;
|
|
};
|
|
|
|
#define NX_MAGIC_RE "BSXN"
|
|
#define NX_MAGIC "NXSB"
|
|
#define NX_MAX_FILE_SYSTEMS 100
|
|
#define NX_EPH_INFO_COUNT 4
|
|
#define NX_EPH_MIN_BLOCK_COUNT 8
|
|
#define NX_MAX_FILE_SYSTEM_EPH_STRUCTS 4
|
|
#define NX_TX_MIN_CHECKPOINT_COUNT 4
|
|
#define NX_EPH_INFO_VERSION_1 1
|
|
|
|
#define NX_RESERVED_1 0x00000001LL
|
|
#define NX_RESERVED_2 0x00000002LL
|
|
#define NX_CRYPTO_SW 0x00000004LL
|
|
|
|
#define NX_FEATURE_DEFRAG 0x0000000000000001ULL
|
|
#define NX_FEATURE_LCFD 0x0000000000000002ULL
|
|
#define NX_SUPPORTED_FEATURES_MASK (NX_FEATURE_DEFRAG | NX_FEATURE_LCFD)
|
|
|
|
bitfield nx_features_t {
|
|
unsigned defrag : 1;
|
|
unsigned lcfd : 1;
|
|
padding : 62;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define NX_SUPPORTED_ROCOMPAT_MASK (0x0ULL)
|
|
|
|
bitfield nx_rocompat_features_t {
|
|
padding : 64;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define NX_INCOMPAT_VERSION1 0x0000000000000001ULL
|
|
#define NX_INCOMPAT_VERSION2 0x0000000000000002ULL
|
|
#define NX_INCOMPAT_FUSION 0x0000000000000100ULL
|
|
#define NX_SUPPORTED_INCOMPAT_MASK (NX_INCOMPAT_VERSION2 | NX_INCOMPAT_FUSION)
|
|
|
|
bitfield nx_incompat_features_t {
|
|
version1 : 1;
|
|
version2 : 1;
|
|
padding : 6;
|
|
fusion : 1;
|
|
padding : 55;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define NX_MINIMUM_BLOCK_SIZE 4096
|
|
#define NX_DEFAULT_BLOCK_SIZE 4096
|
|
#define NX_MAXIMUM_BLOCK_SIZE 65536
|
|
|
|
#define NX_MINIMUM_CONTAINER_SIZE 1048576
|
|
|
|
enum nx_counter_id_t : u64 {
|
|
NX_CNTR_OBJ_CKSUM_SET = 0,
|
|
NX_CNTR_OBJ_CKSUM_FAIL = 1,
|
|
NX_NUM_COUNTERS = 32
|
|
};
|
|
|
|
struct nx_superblock_t {
|
|
obj_phys_t nx_o;
|
|
type::Magic<NX_MAGIC> nx_magic;
|
|
u32 nx_block_size;
|
|
u64 nx_block_count;
|
|
nx_features_t nx_features;
|
|
nx_rocompat_features_t nx_readonly_compatible_features;
|
|
nx_incompat_features_t nx_incompatible_features;
|
|
uuid_t nx_uuid;
|
|
oid_t nx_next_oid;
|
|
xid_t nx_next_xid;
|
|
u32 nx_xp_desc_blocks;
|
|
u32 nx_xp_data_blocks;
|
|
paddr_t nx_xp_desc_base;
|
|
paddr_t nx_xp_data_base;
|
|
u32 nx_xp_desc_next;
|
|
u32 nx_xp_data_next;
|
|
u32 nx_xp_desc_index;
|
|
u32 nx_xp_desc_len;
|
|
u32 nx_xp_data_index;
|
|
u32 nx_xp_data_len;
|
|
oid_t nx_spaceman_oid;
|
|
oid_t nx_omap_oid;
|
|
oid_t nx_reaper_oid;
|
|
u32 nx_test_type;
|
|
u32 nx_max_file_systems;
|
|
oid_t nx_fs_oid[NX_MAX_FILE_SYSTEMS];
|
|
nx_counter_id_t nx_counters[nx_counter_id_t::NX_NUM_COUNTERS];
|
|
prange nx_blocked_out_prange;
|
|
oid_t nx_evict_mapping_tree_oid;
|
|
u64 nx_flags;
|
|
paddr_t nx_efi_jumpstart;
|
|
uuid_t nx_fusion_uuid;
|
|
prange nx_keylocker;
|
|
u64 nx_ephemeral_info[NX_EPH_INFO_COUNT];
|
|
oid_t nx_test_oid;
|
|
oid_t nx_fusion_mt_oid;
|
|
oid_t nx_fusion_wbc_oid;
|
|
prange nx_fusion_wbc;
|
|
u64 nx_newest_mounted_version;
|
|
prange nx_mkb_locker;
|
|
};
|
|
|
|
#define CHECKPOINT_MAP_LAST 0x00000001
|
|
|
|
struct checkpoint_mapping_t {
|
|
o_type_t cpm_type;
|
|
o_type_t cpm_subtype;
|
|
u32 cpm_size;
|
|
u32 cpm_pad;
|
|
oid_t cpm_fs_oid;
|
|
oid_t cpm_oid;
|
|
oid_t cpm_paddr;
|
|
};
|
|
|
|
struct checkpoint_map_phys_t {
|
|
obj_phys_t cpm_o;
|
|
u32 cpm_flags;
|
|
u32 cpm_count;
|
|
|
|
checkpoint_mapping_t cpm_map[cpm_count];
|
|
};
|
|
|
|
struct evict_mapping_val_t {
|
|
paddr_t dst_paddr;
|
|
u64 len;
|
|
};
|
|
|
|
struct omap_phys_t {
|
|
obj_phys_t om_o;
|
|
u32 om_flags;
|
|
u32 om_snap_count;
|
|
u32 om_tree_type;
|
|
u32 om_snapshot_tree_type;
|
|
oid_t om_tree_oid;
|
|
oid_t om_snapshot_tree_oid;
|
|
xid_t om_most_recent_snap;
|
|
xid_t om_pending_revert_min;
|
|
xid_t om_pending_revert_max;
|
|
};
|
|
|
|
struct omap_key_t {
|
|
oid_t ok_oid;
|
|
oid_t ok_xid;
|
|
};
|
|
|
|
struct omap_val_t {
|
|
u32 ov_flags;
|
|
u32 ov_size;
|
|
paddr_t ov_paddr;
|
|
};
|
|
|
|
struct omap_snapshot_t {
|
|
u32 oms_flags;
|
|
u32 oms_pad;
|
|
oid_t oms_oid;
|
|
};
|
|
|
|
struct chunk_info_t {
|
|
u64 ci_xid;
|
|
u64 ci_addr;
|
|
u32 ci_block_count;
|
|
u32 ci_free_count;
|
|
paddr_t ci_bitmap_addr;
|
|
};
|
|
|
|
struct chunk_info_block_t {
|
|
obj_phys_t cib_o;
|
|
u32 cib_index;
|
|
u32 cib_chunk_info_count;
|
|
chunk_info_t cib_chunk_info[cib_chunk_info_count];
|
|
};
|
|
|
|
struct cib_addr_block_t {
|
|
obj_phys_t cab_o;
|
|
u32 cab_index;
|
|
u32 cab_cib_count;
|
|
paddr_t cab_cib_addr[cab_cib_count];
|
|
};
|
|
|
|
struct spaceman_free_queue_key_t {
|
|
xid_t sfqk_xid;
|
|
paddr_t sfqk_paddr;
|
|
};
|
|
|
|
using spaceman_free_queue_val_t = u64;
|
|
struct spaceman_free_queue_entry_t {
|
|
spaceman_free_queue_key_t sfqe_key;
|
|
spaceman_free_queue_val_t sfqe_count;
|
|
};
|
|
|
|
struct spaceman_free_queue_t {
|
|
u64 sfq_count;
|
|
oid_t sfq_tree_oid;
|
|
xid_t sfq_oldest_xid;
|
|
u16 sfq_tree_node_limit;
|
|
u16 sfq_pad16;
|
|
u32 sfq_pad32;
|
|
u64 sfq_reserved;
|
|
};
|
|
|
|
struct spaceman_device_t {
|
|
u64 sm_block_count;
|
|
u64 sm_chunk_count;
|
|
u32 sm_cib_count;
|
|
u32 sm_cab_count;
|
|
u64 sm_free_count;
|
|
u32 sm_addr_offset;
|
|
u32 sm_reserved;
|
|
u64 sm_reserved2;
|
|
};
|
|
|
|
enum smdev_t : u32 {
|
|
SD_MAIN = 0,
|
|
SD_TIER2 = 1,
|
|
SD_COUNT = 2
|
|
};
|
|
|
|
enum sfq_t : u32 {
|
|
SFQ_IP = 0,
|
|
SFQ_MAIN = 1,
|
|
SFQ_TIER2 = 2,
|
|
SFQ_COUNT = 3
|
|
};
|
|
|
|
struct spaceman_allocation_zone_boundaries_t {
|
|
u64 saz_zone_start;
|
|
u64 saz_zone_end;
|
|
};
|
|
|
|
#define SM_ALLOCZONE_INVALID_END_BOUNDARY 0
|
|
#define SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES 7
|
|
struct spaceman_allocation_zone_info_phys_t {
|
|
spaceman_allocation_zone_boundaries_t saz_current_boundaries;
|
|
spaceman_allocation_zone_boundaries_t saz_previous_boundaries[SM_ALLOCZONE_NUM_PREVIOUS_BOUNDARIES];
|
|
u16 saz_zone_id;
|
|
u16 saz_previous_boundary_index;
|
|
u32 saz_reserved;
|
|
};
|
|
|
|
struct spaceman_allocation_zones_t {
|
|
spaceman_allocation_zone_info_phys_t sdz_allocation_zone_infos[smdev_t::SD_COUNT];
|
|
};
|
|
|
|
#define SM_DATAZONE_ALLOCZONE_COUNT 8
|
|
struct spaceman_datazone_info_phys_t {
|
|
spaceman_allocation_zones_t sdz_allocation_zones[SM_DATAZONE_ALLOCZONE_COUNT];
|
|
};
|
|
|
|
struct spaceman_phys_t {
|
|
obj_phys_t sm_o;
|
|
u32 sm_block_size;
|
|
u32 sm_blocks_per_chunk;
|
|
u32 sm_chunks_per_cib;
|
|
u32 sm_cibs_per_cab;
|
|
spaceman_device_t sm_dev[smdev_t::SD_COUNT];
|
|
u32 sm_flags;
|
|
u32 sm_ip_bm_tx_multiplier;
|
|
u64 sm_ip_block_count;
|
|
u32 sm_ip_bm_size_in_blocks;
|
|
u32 sm_ip_bm_block_count;
|
|
paddr_t sm_ip_bm_base;
|
|
paddr_t sm_ip_base;
|
|
u64 sm_fs_reserve_block_count;
|
|
u64 sm_fs_reserve_alloc_count;
|
|
spaceman_free_queue_t sm_fq[sfq_t::SFQ_COUNT];
|
|
u16 sm_ip_bm_free_head;
|
|
u16 sm_ip_bm_free_tail;
|
|
u32 sm_ip_bm_xid_offset;
|
|
u32 sm_ip_bitmap_offset;
|
|
u32 sm_ip_bm_free_next_offset;
|
|
u32 sm_version;
|
|
u32 sm_struct_size;
|
|
spaceman_datazone_info_phys_t sm_datazone;
|
|
};
|
|
|
|
struct nx_reaper_phys_t {
|
|
obj_phys_t nr_o;
|
|
u64 nr_next_reap_id;
|
|
u64 nr_completed_id;
|
|
oid_t nr_head;
|
|
oid_t nr_tail;
|
|
u32 nr_flags;
|
|
u32 nr_rlcount;
|
|
u32 nr_type;
|
|
u32 nr_size;
|
|
oid_t nr_fs_oid;
|
|
oid_t nr_oid;
|
|
xid_t nr_xid;
|
|
u32 nr_nrle_flags;
|
|
u32 nr_state_buffer_size;
|
|
u8 nr_state_buffer[nr_state_buffer_size];
|
|
};
|
|
|
|
struct nx_reap_list_entry_t {
|
|
u32 nrle_next;
|
|
u32 nrle_flags;
|
|
u32 nrle_type;
|
|
u32 nrle_size;
|
|
oid_t nrle_fs_oid;
|
|
oid_t nrle_oid;
|
|
xid_t nrle_xid;
|
|
};
|
|
|
|
struct nx_reap_list_phys_t {
|
|
obj_phys_t nrl_o;
|
|
oid_t nrl_next;
|
|
u32 nrl_flags;
|
|
u32 nrl_max;
|
|
u32 nrl_count;
|
|
u32 nrl_first;
|
|
u32 nrl_last;
|
|
u32 nrl_free;
|
|
nx_reap_list_entry_t nrl_entries[nrl_count];
|
|
};
|
|
|
|
enum nx_reap_phase_t : u32 {
|
|
APFS_REAP_PHASE_START = 0,
|
|
APFS_REAP_PHASE_SNAPSHOTS = 1,
|
|
APFS_REAP_PHASE_ACTIVE_FS = 2,
|
|
APFS_REAP_PHASE_DESTROY_OMAP = 3,
|
|
APFS_REAP_PHASE_DONE = 4,
|
|
};
|
|
|
|
struct keybag_entry_t {
|
|
uuid_t ke_uuid;
|
|
u16 ke_tag;
|
|
u16 ke_keylen;
|
|
padding[4];
|
|
u8 ke_keydata[];
|
|
};
|
|
|
|
struct kb_locker_t {
|
|
u16 kl_version;
|
|
u16 kl_nkeys;
|
|
u32 kl_nbytes;
|
|
padding[8];
|
|
u8 kl_entries[];
|
|
};
|
|
|
|
struct mk_obj_t {
|
|
u8 o_cksum[MAX_CKSUM_SIZE];
|
|
oid_t o_oid;
|
|
xid_t o_xid;
|
|
o_type_id_t o_type;
|
|
o_type_id_t o_subtype;
|
|
};
|
|
|
|
struct media_keybag_t {
|
|
mk_obj_t mk_obj;
|
|
kb_locker_t mk_locker;
|
|
};
|
|
|
|
using crypto_flags_t = u32;
|
|
using cp_key_class_t = u32;
|
|
using cp_key_os_version_t = u32;
|
|
using cp_key_revision_t = u16;
|
|
|
|
struct wrapped_crypto_state_t {
|
|
u16 major_version;
|
|
u16 minor_version;
|
|
crypto_flags_t cpflags;
|
|
cp_key_class_t persistent_class;
|
|
cp_key_os_version_t key_os_version;
|
|
cp_key_revision_t key_revision;
|
|
u16 key_len;
|
|
u8 persistent_key[0];
|
|
};
|
|
|
|
struct wrapped_meta_crypto_state_t {
|
|
u16 major_version;
|
|
u16 minor_version;
|
|
crypto_flags_t cpflags;
|
|
cp_key_class_t persistent_class;
|
|
cp_key_os_version_t key_os_version;
|
|
cp_key_revision_t key_revision;
|
|
u16 unused;
|
|
};
|
|
|
|
struct j_crypto_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
struct j_crypto_val_t {
|
|
u32 refcnt;
|
|
wrapped_crypto_state_t state;
|
|
};
|
|
|
|
#define APFS_MODIFIED_NAMELEN 32
|
|
struct apfs_modified_by_t {
|
|
u8 id[APFS_MODIFIED_NAMELEN];
|
|
u64 timestamp;
|
|
xid_t last_xid;
|
|
};
|
|
|
|
bitfield apfs_fs_flags_t {
|
|
APFS_FS_UNENCRYPTED : 1;
|
|
APFS_FS_RESERVED_2 : 1;
|
|
APFS_FS_RESERVED_4 : 1;
|
|
APFS_FS_ONEKEY : 1;
|
|
APFS_FS_SPILLEDOVER : 1;
|
|
APFS_FS_RUN_SPILLOVER_CLEANER : 1;
|
|
APFS_FS_ALWAYS_CHECK_EXTENTREF : 1;
|
|
APFS_FS_RESERVED_80 : 1;
|
|
APFS_FS_RESERVED_100 : 1;
|
|
padding : 55;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
bitfield apfs_incompatible_features_t {
|
|
APFS_INCOMPAT_CASE_INSENSITIVE : 1;
|
|
APFS_INCOMPAT_DATALESS_SNAPS : 1;
|
|
APFS_INCOMPAT_ENC_ROLLED : 1;
|
|
APFS_INCOMPAT_NORMALIZATION_INSENSITIVE : 1;
|
|
APFS_INCOMPAT_INCOMPLETE_RESTORE : 1;
|
|
APFS_INCOMPAT_SEALED_VOLUME : 1;
|
|
APFS_INCOMPAT_RESERVED_40 : 1;
|
|
padding : 57;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
apfs_incompatible_features_t apfs_incompatible_features_null;
|
|
|
|
#define APFS_MAGIC "BSPA"
|
|
#define APFS_MAGIC_LE "APSB"
|
|
#define APFS_MAX_HIST 8
|
|
#define APFS_VOLNAME_LEN 256
|
|
struct apfs_superblock_t {
|
|
obj_phys_t apfs_o;
|
|
type::Magic<APFS_MAGIC_LE> apfs_magic;
|
|
u32 apfs_fs_index;
|
|
u64 apfs_features;
|
|
u64 apfs_readonly_compatible_features;
|
|
apfs_incompatible_features_t apfs_incompatible_features;
|
|
u64 apfs_unmount_time;
|
|
u64 apfs_fs_reserve_block_count;
|
|
u64 apfs_fs_quota_block_count;
|
|
u64 apfs_fs_alloc_count;
|
|
wrapped_meta_crypto_state_t apfs_meta_crypto;
|
|
o_type_t apfs_root_tree_type;
|
|
o_type_t apfs_extentref_tree_type;
|
|
o_type_t apfs_snap_meta_tree_type;
|
|
oid_t apfs_omap_oid;
|
|
oid_t apfs_root_tree_oid;
|
|
oid_t apfs_extentref_tree_oid;
|
|
oid_t apfs_snap_meta_tree_oid;
|
|
xid_t apfs_revert_to_xid;
|
|
oid_t apfs_revert_to_sblock_oid;
|
|
u64 apfs_next_obj_id;
|
|
u64 apfs_num_files;
|
|
u64 apfs_num_directories;
|
|
u64 apfs_num_symlinks;
|
|
u64 apfs_num_other_fsobjects;
|
|
u64 apfs_num_snapshots;
|
|
u64 apfs_total_blocks_alloced;
|
|
u64 apfs_total_blocks_freed;
|
|
uuid_t apfs_vol_uuid;
|
|
u64 apfs_last_mod_time;
|
|
apfs_fs_flags_t apfs_fs_flags;
|
|
apfs_modified_by_t apfs_formatted_by;
|
|
apfs_modified_by_t apfs_modified_by[APFS_MAX_HIST];
|
|
char apfs_volname[APFS_VOLNAME_LEN];
|
|
u32 apfs_next_doc_id;
|
|
u16 apfs_role;
|
|
u16 reserved;
|
|
xid_t apfs_root_to_xid;
|
|
oid_t apfs_er_state_oid;
|
|
u64 apfs_cloneinfo_id_epoch;
|
|
u64 apfs_cloneinfo_xid;
|
|
oid_t apfs_snap_meta_ext_oid;
|
|
uuid_t apfs_volume_group_id;
|
|
oid_t apfs_integrity_meta_oid;
|
|
oid_t apfs_fext_tree_oid;
|
|
o_type_t apfs_fext_tree_type;
|
|
u32 reserved_type;
|
|
oid_t reserved_oid;
|
|
};
|
|
|
|
#define OBJ_ID_MASK 0x0FFFFFFFFFFFFFFF
|
|
#define OBJ_TYPE_MASK 0xF000000000000000
|
|
#define OBJ_TYPE_SHIFT 60
|
|
|
|
struct j_inode_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
bitfield j_inode_flags_t {
|
|
INODE_IS_APFS_PRIVATE : 1;
|
|
INODE_MAINTAIN_DIR_STATS : 1;
|
|
INODE_DIR_STATS_ORIGIN : 1;
|
|
INODE_PROT_CLASS_EXPLICIT : 1;
|
|
INODE_WAS_CLONED : 1;
|
|
INODE_FLAGS_UNUSED : 1;
|
|
INODE_HAS_SECURITY_EA : 1;
|
|
INODE_BEING_TRUNCATED : 1;
|
|
INODE_HAS_FINDER_INFO : 1;
|
|
INODE_IS_SPARSE : 1;
|
|
INODE_WAS_EVER_CLONED : 1;
|
|
INODE_ACTIVE_FILE_TRIMMED : 1;
|
|
INODE_PINNED_TO_MAIN : 1;
|
|
INODE_PINNED_TO_TIER2 : 1;
|
|
INODE_HAS_RSRC_FORK : 1;
|
|
INODE_NO_RSRC_FORK : 1;
|
|
INODE_ALLOCATION_SPILLEDOVER : 1;
|
|
INODE_FAST_PROMOTE : 1;
|
|
INODE_HAS_UNCOMPRESSED_SIZE : 1;
|
|
INODE_IS_PURGEABLE : 1;
|
|
INODE_WANTS_TO_BE_PURGEABLE : 1;
|
|
INODE_IS_SYNC_ROOT : 1;
|
|
INODE_SNAPSHOT_COW_EXEMPTION : 1;
|
|
padding : 41;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define INODE_INHERITED_INTERNAL_FLAGS (INODE_MAINTAIN_DIR_STATS | INODE_SNAPSHOT_COW_EXEMPTION)
|
|
#define INODE_CLONED_INTERNAL_FLAGS (INODE_HAS_RSRC_FORK | INODE_NO_RSRC_FORK | INODE_HAS_FINDER_INFO | INODE_SNAPSHOT_COW_EXEMPTION)
|
|
|
|
#define OWNING_OBJ_ID_INVALID ~0ULL
|
|
#define OWNING_OBJ_ID_UNKNOWN ~1ULL
|
|
|
|
#define JOBJ_MAX_KEY_SIZE 832
|
|
#define JOBJ_MAX_VALUE_SIZE 3808
|
|
|
|
#define MIN_DOC_ID = 3;
|
|
|
|
#define FEXT_CRYPTO_ID_IS_TWEAK 0x01
|
|
|
|
enum mode_t : u16 {
|
|
MODE_S_IFMT = 0o170000,
|
|
MODE_S_IFIFO = 0o010000,
|
|
MODE_S_IFCHR = 0o020000,
|
|
MODE_S_IFDIR = 0o040000,
|
|
MODE_S_IFBLK = 0o060000,
|
|
MODE_S_IFREG = 0o100000,
|
|
MODE_S_IFLNK = 0o120000,
|
|
MODE_S_IFSOCK = 0o140000,
|
|
MODE_S_IFWHT = 0o160000,
|
|
};
|
|
|
|
struct j_inode_val_t {
|
|
u64 parent_id;
|
|
u64 private_id;
|
|
type::time64_t create_time;
|
|
type::time64_t mod_time;
|
|
type::time64_t change_time;
|
|
type::time64_t access_time;
|
|
j_inode_flags_t internal_flags;
|
|
|
|
u32 nchildren; // or links
|
|
|
|
cp_key_class_t default_protection_class;
|
|
|
|
u32 write_generation_counter;
|
|
u32 bsd_flags;
|
|
u32 owner;
|
|
u32 group;
|
|
mode_t mode;
|
|
u16 pad1;
|
|
u64 uncompressed_size;
|
|
u8 xfields[1];
|
|
};
|
|
|
|
#define J_DREC_LEN_MASK 0x000003FF
|
|
#define J_DREC_HASH_MASK 0xFFFFFC00
|
|
#define J_DREC_HASH_SHIFT 10
|
|
|
|
struct j_drec_key_t {
|
|
// j_key_t hdr;
|
|
u16 name_len;
|
|
char name[name_len];
|
|
};
|
|
|
|
bitfield j_drec_hashed_len_t {
|
|
length : 10;
|
|
hash : 22;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]];
|
|
|
|
struct j_drec_hashed_key_t {
|
|
// j_key_t hdr;
|
|
j_drec_hashed_len_t name_len_and_hash;
|
|
char name[name_len_and_hash.length];
|
|
};
|
|
|
|
enum dir_rec_flags_type_t : u8 {
|
|
DT_UNKNOWN = 0,
|
|
DT_FIFO = 1,
|
|
DT_CHR = 2,
|
|
DT_DIR = 4,
|
|
DT_BLK = 6,
|
|
DT_REG = 8,
|
|
DT_LNK = 10,
|
|
DT_SOCK = 12,
|
|
DT_WHT = 14,
|
|
};
|
|
|
|
bitfield dir_rec_flags_t {
|
|
DREC_TYPE_MASK : 8;
|
|
RESERVED_10 : 1;
|
|
padding : 55;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
struct j_drec_val_t {
|
|
u64 file_id;
|
|
u64 date_added;
|
|
dir_rec_flags_t flags;
|
|
u8 xfields[];
|
|
};
|
|
|
|
struct j_dir_stats_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
struct j_dir_stats_val_t {
|
|
u64 num_children;
|
|
u64 total_size;
|
|
u64 chained_key;
|
|
u64 gen_count;
|
|
};
|
|
|
|
struct j_xattr_key_t {
|
|
// j_key_t hdr;
|
|
u16 name_len;
|
|
char name[name_len];
|
|
};
|
|
|
|
bitfield j_xattr_flags_t {
|
|
XATTR_DATA_STREAM : 1;
|
|
XATTR_DATA_EMBEDDED : 1;
|
|
XATTR_FILE_SYSTEM_OWNED : 1;
|
|
XATTR_RESERVED_8 : 1;
|
|
padding : 12;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]];
|
|
|
|
#define XATTR_MAX_EMBEDDED_SIZE 3804
|
|
#define SYMLINK_EA_NAME "com.apple.fs.symlink"
|
|
#define FIRMLINK_EA_NAME "com.apple.fs.firmlink"
|
|
#define APFS_COW_EXEMPT_COUNT_NAME "com.apple.fs.cow-exempt-file-count"
|
|
|
|
struct j_xattr_val_t {
|
|
j_xattr_flags_t flags;
|
|
u16 xdata_len;
|
|
u8 xdata[xdata_len];
|
|
};
|
|
|
|
enum j_obj_kinds_t : u8 {
|
|
APFS_KIND_ANY = 0,
|
|
APFS_KIND_NEW = 1,
|
|
APFS_KIND_UPDATE = 2,
|
|
APFS_KIND_DEAD = 3,
|
|
APFS_KIND_UPDATE_REFCNT = 4,
|
|
|
|
APFS_KIND_INVALID = 255
|
|
};
|
|
|
|
bitfield j_inode_bsd_flags {
|
|
APFS_UF_NODUMP : 1;
|
|
APFS_UF_IMMUTABLE : 1;
|
|
APFS_UF_APPEND : 1;
|
|
APFS_UF_OPAQUE : 1;
|
|
APFS_UF_NOUNLINK : 1;
|
|
APFS_UF_COMPRESSED : 1;
|
|
APFS_UF_TRACKED : 1;
|
|
APFS_UF_DATAVAULT : 1;
|
|
reserved : 7;
|
|
APFS_UF_HIDDEN : 1;
|
|
APFS_SF_ARCHIVED : 1;
|
|
APFS_SF_IMMUTABLE : 1;
|
|
APFS_SF_APPEND : 1;
|
|
APFS_SF_RESTRICTED : 1;
|
|
APFS_SF_NOUNLINK : 1;
|
|
APFS_SF_SNAPSHOT : 1;
|
|
APFS_SF_FIRMLINK : 1;
|
|
padding : 6;
|
|
APFS_SF_DATALESS : 1;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define INVALID_INO_NUM 0
|
|
#define ROOT_DIR_PARENT 1
|
|
#define ROOT_DIR_INO_NUM 2
|
|
#define PRIV_DIR_INO_NUM 3
|
|
#define SNAP_DIR_INO_NUM 6
|
|
#define PURGEABLE_DIR_INO_NUM 7
|
|
#define MIN_USER_INO_NUM 16
|
|
|
|
#define UNIFIED_ID_SPACE_MARK 0x0800000000000000
|
|
|
|
struct j_phys_ext_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
struct j_phys_ext_val_t {
|
|
u64 len_and_kind;
|
|
u64 owning_obj_id;
|
|
u32 refcnt;
|
|
};
|
|
|
|
#define PEXT_LEN_MASK 0x0fffffffffffffffULL
|
|
#define PEXT_KIND_MASK 0xf000000000000000ULL
|
|
#define PEXT_KIND_SHIFT 60
|
|
|
|
struct j_file_extent_key_t {
|
|
// j_key_t hdr;
|
|
u64 logical_addr;
|
|
};
|
|
|
|
struct j_file_extent_val_t {
|
|
u64 len_and_flags;
|
|
u64 phys_block_num;
|
|
u64 crypto_id;
|
|
};
|
|
|
|
#define J_FILE_EXTENT_LEN_MASK 0x00ffffffffffffffULL
|
|
#define J_FILE_EXTENT_FLAG_MASK 0xff00000000000000ULL
|
|
#define J_FILE_EXTENT_FLAG_SHIFT 56
|
|
|
|
struct j_dstream_id_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
struct j_dstream_id_val_t {
|
|
u32 refcnt;
|
|
};
|
|
|
|
struct j_dstream_t {
|
|
u64 size;
|
|
u64 alloced_size;
|
|
u64 default_crypto_id;
|
|
u64 total_bytes_written;
|
|
u64 total_bytes_read;
|
|
};
|
|
|
|
struct j_xattr_dstream_t {
|
|
u64 xattr_obj_id;
|
|
j_dstream_t dstream;
|
|
};
|
|
|
|
struct xf_blob_t {
|
|
u16 xf_num_exts;
|
|
u16 xf_used_data;
|
|
u8 xf_data[];
|
|
};
|
|
|
|
enum x_type_t : u8 {
|
|
DREC_EXT_TYPE_SIBLING_ID = 1,
|
|
|
|
INO_EXT_TYPE_SNAP_XID = 1,
|
|
INO_EXT_TYPE_DELTRA_TREE_OID = 2,
|
|
INO_EXT_TYPE_DOCUMENT_ID = 3,
|
|
INO_EXT_TYPE_NAME = 4,
|
|
INO_EXT_TYPE_PREV_FSIZE = 5,
|
|
INO_EXT_TYPE_RESERVED_6 = 6,
|
|
INO_EXT_TYPE_FINDER_INFO = 7,
|
|
INO_EXT_TYPE_DSTREAM = 8,
|
|
INO_EXT_TYPE_RESERVED_9 = 9,
|
|
INO_EXT_TYPE_DIR_STATS_KEY = 10,
|
|
INO_EXT_TYPE_FS_UUID = 11,
|
|
INO_EXT_TYPE_RESERVED_12 = 12,
|
|
INO_EXT_TYPE_SPARSE_BYTES = 13,
|
|
INO_EXT_TYPE_RDEV = 14,
|
|
INO_EXT_TYPE_PURGEABLE_FLAGS = 15,
|
|
INO_EXT_TYPE_ORIG_SYNC_ROOT_ID = 16
|
|
};
|
|
|
|
bitfield x_flags_t {
|
|
XF_DATA_DEPENDENT : 1;
|
|
XF_DO_NOT_COPY : 1;
|
|
XF_RESERVED_4 : 1;
|
|
XF_CHILDREN_INHERIT : 1;
|
|
XF_USER_FIELD : 1;
|
|
XF_SYSTEM_FIELD : 1;
|
|
XF_RESERVED_40 : 1;
|
|
XF_RESERVED_80 : 1;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]];
|
|
|
|
struct x_field_t {
|
|
x_type_t x_type;
|
|
x_flags_t x_flags;
|
|
u16 x_size;
|
|
};
|
|
|
|
struct j_sibling_key_t {
|
|
// j_key_t hdr;
|
|
u64 sibling_id;
|
|
};
|
|
|
|
struct j_sibling_val_t {
|
|
u64 parent_id;
|
|
u16 name_len;
|
|
char name[name_len];
|
|
};
|
|
|
|
struct j_sibling_map_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
struct j_sibling_map_val_t {
|
|
u64 file_id;
|
|
};
|
|
|
|
struct j_snap_metadata_key_t {
|
|
// j_key_t hdr;
|
|
};
|
|
|
|
bitfield snap_meta_flags_t {
|
|
SNAP_META_PENDING_DATALESS : 1;
|
|
SNAP_META_MERGE_IN_PROGRESS : 1;
|
|
padding : 30;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]];
|
|
|
|
struct j_snap_metadata_val_t {
|
|
oid_t extentref_tree_oid;
|
|
oid_t sblock_oid;
|
|
type::time64_t create_time;
|
|
type::time64_t change_time;
|
|
u64 inum;
|
|
o_type_t extentref_tree_type;
|
|
snap_meta_flags_t flags;
|
|
u16 name_len;
|
|
char name[name_len];
|
|
};
|
|
|
|
struct j_snap_name_key_t {
|
|
// j_key_t hdr;
|
|
u16 name_len;
|
|
char name[name_len];
|
|
};
|
|
|
|
struct j_snap_name_val_t {
|
|
xid_t snap_xid;
|
|
};
|
|
|
|
bitfield j_file_info_lba_t {
|
|
lba : 56;
|
|
type : 8;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
#define J_FILE_INFO_LBA_MASK 0x00FFFFFFFFFFFFFF
|
|
#define J_FILE_INFO_TYPE_MASK 0xFF00000000000000
|
|
#define J_FILE_INFO_TYPE_SHIFT 56
|
|
struct j_file_info_key_t {
|
|
// j_key_t hdr;
|
|
j_file_info_lba_t info_and_lba;
|
|
};
|
|
|
|
struct j_file_data_hash_val_t {
|
|
u16 hashed_len;
|
|
u8 hash_size;
|
|
u8 hash[hash_size];
|
|
};
|
|
|
|
struct j_file_info_val_t {
|
|
j_file_data_hash_val_t dhash;
|
|
};
|
|
|
|
enum j_obj_types_t : u8 {
|
|
APFS_TYPE_ANY = 0,
|
|
APFS_TYPE_SNAP_METADATA = 1,
|
|
APFS_TYPE_EXTENT = 2,
|
|
APFS_TYPE_INODE = 3,
|
|
APFS_TYPE_XATTR = 4,
|
|
APFS_TYPE_SIBLING_LINK = 5,
|
|
APFS_TYPE_DSTREAM_ID = 6,
|
|
APFS_TYPE_CRYPTO_STATE = 7,
|
|
APFS_TYPE_FILE_EXTENT = 8,
|
|
APFS_TYPE_DIR_REC = 9,
|
|
APFS_TYPE_DIR_STATS = 10,
|
|
APFS_TYPE_SNAP_NAME = 11,
|
|
APFS_TYPE_SIBLING_MAP = 12,
|
|
APFS_TYPE_FILE_INFO = 13,
|
|
APFS_TYPE_INVALID = 15,
|
|
};
|
|
|
|
bitfield j_key_t {
|
|
unsigned obj_id : 60;
|
|
j_obj_types_t obj_type : 4;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 64)]];
|
|
|
|
struct nloc_t {
|
|
u16 off;
|
|
u16 len;
|
|
};
|
|
|
|
struct kvloc_t {
|
|
nloc_t key_loc;
|
|
nloc_t val_loc;
|
|
};
|
|
|
|
struct kvoff_t {
|
|
u16 key_off;
|
|
u16 val_off;
|
|
};
|
|
|
|
struct kvgen_t<auto subtype, auto btn_level, auto btn_flags, auto key_area, auto val_area, auto vol_incomp> {
|
|
u16 key_off = 0;
|
|
u16 val_off = 0;
|
|
if (btn_flags.BTNODE_FIXED_KV_SIZE) {
|
|
kvoff_t range;
|
|
|
|
key_off = range.key_off;
|
|
val_off = range.val_off;
|
|
} else {
|
|
kvloc_t range;
|
|
|
|
key_off = range.key_loc.off;
|
|
val_off = range.val_loc.off;
|
|
}
|
|
|
|
match (subtype) {
|
|
(o_type_id_t::OBJECT_TYPE_OMAP): {
|
|
omap_key_t key @ key_area + key_off;
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
omap_val_t val @ val_area - val_off;
|
|
}
|
|
}
|
|
(o_type_id_t::OBJECT_TYPE_FSTREE | o_type_id_t::OBJECT_TYPE_BLOCKREFTREE | o_type_id_t::OBJECT_TYPE_SNAPMETATREE): {
|
|
j_key_t key @ key_area + key_off;
|
|
match (key.obj_type) {
|
|
(j_obj_types_t::APFS_TYPE_SNAP_METADATA): {
|
|
j_snap_metadata_key_t snap_meta_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_snap_metadata_val_t snap_meta_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_EXTENT): {
|
|
j_phys_ext_key_t phys_ext_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_phys_ext_val_t phys_ext_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_INODE): {
|
|
j_inode_key_t inode_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_inode_val_t inode_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_XATTR): {
|
|
j_xattr_key_t xattr_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_xattr_val_t xattr_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_SIBLING_LINK): {
|
|
j_sibling_key_t sibling_link_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_sibling_val_t sibling_link_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_DSTREAM_ID): {
|
|
j_dstream_id_key_t dstream_id_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_dstream_id_val_t dstream_id_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_CRYPTO_STATE): {
|
|
j_crypto_key_t crypto_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_crypto_val_t crypto_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_FILE_EXTENT): {
|
|
j_file_extent_key_t file_ext_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_file_extent_val_t file_ext_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_DIR_REC): {
|
|
if (vol_incomp.APFS_INCOMPAT_CASE_INSENSITIVE ||
|
|
vol_incomp.APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) {
|
|
j_drec_hashed_key_t drec_key @ key_area + key_off + sizeof (key);
|
|
} else {
|
|
j_drec_key_t drec_key @ key_area + key_off + sizeof (key);
|
|
}
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_drec_val_t drec_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_DIR_STATS): {
|
|
j_dir_stats_key_t dir_stats_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_dir_stats_val_t dir_stats_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_SNAP_NAME): {
|
|
j_snap_name_key_t snap_name_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_snap_name_val_t snap_name_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_SIBLING_MAP): {
|
|
j_sibling_key_t sibling_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_sibling_val_t sibling_val @ val_area - val_off;
|
|
}
|
|
}
|
|
(j_obj_types_t::APFS_TYPE_FILE_INFO): {
|
|
j_file_info_key_t file_info_key @ key_area + key_off + sizeof (key);
|
|
|
|
if (btn_level > 0) {
|
|
oid_t node_oid @ val_area - val_off;
|
|
} else {
|
|
j_file_info_val_t file_info_val @ val_area - val_off;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bitfield btn_flags_t {
|
|
BTNODE_ROOT : 1;
|
|
BTNODE_LEAF : 1;
|
|
BTNODE_FIXED_KV_SIZE : 1;
|
|
BTNODE_HASHED : 1;
|
|
BTNODE_NOHEADER : 1;
|
|
padding : 10;
|
|
BTNODE_CHECK_KOFF_INVAL : 1;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 16)]];
|
|
|
|
bitfield bt_flags_t {
|
|
BTREE_UINT64_KEYS : 1;
|
|
BTREE_SEQUENTIAL_INSERT : 1;
|
|
BTREE_ALLOW_GHOSTS : 1;
|
|
BTREE_EPHEMERAL : 1;
|
|
BTREE_PHYSICAL : 1;
|
|
BTREE_NONPERSISTENT : 1;
|
|
BTREE_KV_NONALIGNED : 1;
|
|
BTREE_HASHED : 1;
|
|
BTREE_NOHEADER : 1;
|
|
padding : 23;
|
|
} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 32)]];
|
|
|
|
struct btree_info_fixed_t {
|
|
bt_flags_t bt_flags;
|
|
u32 bt_node_size;
|
|
u32 bt_key_size;
|
|
u32 bt_val_size;
|
|
};
|
|
|
|
struct btree_info_t {
|
|
btree_info_fixed_t bt_fixed;
|
|
u32 bt_longest_key;
|
|
u32 bt_longest_val;
|
|
u64 bt_key_count;
|
|
u64 bt_node_count;
|
|
};
|
|
|
|
struct btree_node_phys_t<auto block_size, auto vol_incomp> {
|
|
u32 node_start = $;
|
|
|
|
obj_phys_t btn_o;
|
|
btn_flags_t btn_flags;
|
|
u16 btn_level;
|
|
u32 btn_nkeys;
|
|
nloc_t btn_table_space;
|
|
nloc_t btn_free_space;
|
|
nloc_t btn_key_free_list;
|
|
nloc_t btn_val_free_list;
|
|
|
|
if (btn_table_space.off > $) {
|
|
padding[btn_table_space.off - $];
|
|
}
|
|
u32 btn_toc_start = $;
|
|
|
|
padding[btn_table_space.len];
|
|
u32 key_area = $;
|
|
u32 val_area = node_start + block_size;
|
|
if (btn_flags.BTNODE_ROOT) {
|
|
val_area -= sizeof (btree_info_t);
|
|
}
|
|
|
|
u8 btn_key_area;
|
|
kvgen_t<btn_o.o_subtype.t_type, btn_level, btn_flags, key_area, val_area, vol_incomp> btn_toc[btn_nkeys] @ btn_toc_start;
|
|
|
|
padding[btn_free_space.off];
|
|
u8 btn_free_area_start;
|
|
|
|
std::mem::AlignTo<block_size>;
|
|
|
|
if (btn_flags.BTNODE_ROOT) {
|
|
btree_info_t info @ node_start + block_size - sizeof (btree_info_t);
|
|
}
|
|
};
|
|
|
|
#define BTREE_NODE_HASH_SIZE_MAX 64
|
|
struct btn_index_node_val_t {
|
|
oid_t binv_child_oid;
|
|
u8 binv_child_hash[BTREE_NODE_HASH_SIZE_MAX];
|
|
};
|
|
|
|
// ================== HELPERS ==================
|
|
fn fletcher64(u64 offset, u64 count, u64 init) {
|
|
u64 sum1 = init & 0xFFFFFFFF;
|
|
u64 sum2 = init >> 32;
|
|
|
|
for (u64 key = 0, key < count, key += 1) {
|
|
u32 data @ offset + key * sizeof (u32);
|
|
sum1 += data;
|
|
sum2 += sum1;
|
|
}
|
|
|
|
sum1 %= 0xFFFFFFFF;
|
|
sum2 %= 0xFFFFFFFF;
|
|
|
|
return (sum2 << 32) | sum1;
|
|
};
|
|
|
|
fn block_verify(u64 offset, u64 size) {
|
|
u64 chk @ offset;
|
|
if (chk == 0)
|
|
return false;
|
|
if (chk == 0xFFFFFFFFFFFFFFFF)
|
|
return false;
|
|
|
|
u64 cks = fletcher64(offset + MAX_CKSUM_SIZE, size / sizeof (u32) - 2, 0);
|
|
cks = fletcher64(offset, MAX_CKSUM_SIZE / sizeof (u32), cks);
|
|
|
|
std::assert(cks == 0, std::format("block verification failed, offset: 0x{:X}, size: 0x{:X}", offset, size));
|
|
};
|
|
|
|
fn object_get_latest(u64 offset, u64 count, u64 size, o_type_id_t type, u64 max = 0) {
|
|
if (max == 0) {
|
|
max = count;
|
|
}
|
|
|
|
u64 max_xid = 0;
|
|
u64 result = 0;
|
|
for (u64 address = offset, address <= (offset + count), address += 1) {
|
|
if ((address - offset) >= max) {
|
|
return result;
|
|
}
|
|
|
|
obj_phys_t object @ address * size;
|
|
|
|
if (object.o_type.t_type != type) {
|
|
continue;
|
|
}
|
|
|
|
if (max_xid < object.o_xid) {
|
|
max_xid = object.o_xid;
|
|
result = address * size;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
fn checkpoint_map_lookup(checkpoint_map_phys_t checkpoint_map, oid_t oid, o_type_id_t type) {
|
|
for (u64 iter = 0, iter < checkpoint_map.cpm_count, iter += 1) {
|
|
if (checkpoint_map.cpm_map[iter].cpm_oid == oid &&
|
|
checkpoint_map.cpm_map[iter].cpm_type.t_type == type) {
|
|
return checkpoint_map.cpm_map[iter];
|
|
}
|
|
}
|
|
|
|
checkpoint_mapping_t empty;
|
|
return empty;
|
|
};
|
|
|
|
fn keybag_is_encrypted(media_keybag_t keybag) {
|
|
bool is_decrypted =
|
|
keybag.mk_obj.o_type == o_type_id_t::OBJECT_TYPE_CONTAINER_KEYBAG &&
|
|
keybag.mk_obj.o_subtype == o_type_id_t::OBJECT_TYPE_CONTAINER_KEYBAG_2;
|
|
|
|
std::assert(is_decrypted, "encrypted keybags are not supported");
|
|
};
|
|
|
|
fn omap_node_lookup(paddr_t off, u64 block_size, oid_t oid, xid_t xid) {
|
|
while (true) {
|
|
btree_node_phys_t<block_size, apfs_incompatible_features_null> node @ off;
|
|
if (node.btn_nkeys <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
s64 beg = 0;
|
|
s64 end = node.btn_nkeys - 1;
|
|
s64 mid = 0;
|
|
s64 idx = 0;
|
|
while (beg <= end) {
|
|
mid = beg + (end - beg) / 2;
|
|
|
|
omap_key_t key = node.btn_toc[mid].key;
|
|
if (oid > key.ok_oid) {
|
|
beg += 1;
|
|
} else if (oid < key.ok_oid) {
|
|
end -= 1;
|
|
} else if (xid > key.ok_xid) {
|
|
beg += 1;
|
|
idx = mid;
|
|
} else if (xid < key.ok_xid) {
|
|
end -= 1;
|
|
idx = mid;
|
|
} else {
|
|
idx = mid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (node.btn_level > 0) {
|
|
off = node.btn_toc[idx].node_oid * block_size;
|
|
continue;
|
|
}
|
|
|
|
omap_key_t key = node.btn_toc[idx].key;
|
|
if (key.ok_oid == oid) {
|
|
return node.btn_toc[idx].val.ov_paddr;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
fn fstree_inode_lookup(
|
|
paddr_t root_off,
|
|
paddr_t omap_off,
|
|
u64 block_size,
|
|
u64 ino,
|
|
apfs_incompatible_features_t incomp,
|
|
xid_t xid
|
|
) {
|
|
u64 inode_key = (j_obj_types_t::APFS_TYPE_INODE << OBJ_TYPE_SHIFT) | (ino & OBJ_ID_MASK);
|
|
u64 node_off = root_off;
|
|
while (true) {
|
|
btree_node_phys_t<block_size, incomp> node @ node_off;
|
|
|
|
s64 beg = 0;
|
|
s64 end = node.btn_nkeys - 1;
|
|
s64 mid = 0;
|
|
s64 current = 0;
|
|
while (beg <= end) {
|
|
mid = beg + (end - beg) / 2;
|
|
u64 entry_key @ addressof (node.btn_toc[mid].key);
|
|
if ((entry_key & OBJ_ID_MASK) > (inode_key & OBJ_ID_MASK)) {
|
|
end -= 1;
|
|
} else if ((entry_key & OBJ_ID_MASK) < (inode_key & OBJ_ID_MASK)) {
|
|
beg += 1;
|
|
} else if (((entry_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) > ((inode_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT)) {
|
|
end -= 1;
|
|
current = mid;
|
|
} else if (((entry_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT) < ((inode_key & OBJ_TYPE_MASK) >> OBJ_TYPE_SHIFT)) {
|
|
beg += 1;
|
|
current = mid;
|
|
} else {
|
|
current = mid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (node.btn_level > 0) {
|
|
node_off = omap_node_lookup(
|
|
omap_off,
|
|
block_size,
|
|
node.btn_toc[current].node_oid,
|
|
xid) * superblock.nx_block_size;
|
|
continue;
|
|
}
|
|
|
|
u64 entry_key @ addressof (node.btn_toc[current].key);
|
|
if (entry_key != inode_key) {
|
|
return 0;
|
|
}
|
|
|
|
return addressof (node.btn_toc[current].inode_val);
|
|
}
|
|
};
|
|
|
|
// ================== PARSE ==================
|
|
nx_superblock_t initial_superblock @ 0x00;
|
|
nx_superblock_t superblock @ object_get_latest(
|
|
initial_superblock.nx_xp_desc_base,
|
|
initial_superblock.nx_xp_desc_blocks,
|
|
initial_superblock.nx_block_size,
|
|
o_type_id_t::OBJECT_TYPE_NX_SUPERBLOCK);
|
|
|
|
checkpoint_map_phys_t checkpoint_map @ (superblock.nx_xp_desc_base + superblock.nx_xp_desc_index) * superblock.nx_block_size;
|
|
|
|
omap_phys_t object_map @ superblock.nx_omap_oid * superblock.nx_block_size;
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> object_map_tree @ object_map.om_tree_oid * superblock.nx_block_size;
|
|
|
|
apfs_superblock_t volume @ omap_node_lookup(
|
|
addressof (object_map_tree),
|
|
superblock.nx_block_size,
|
|
superblock.nx_fs_oid[0],
|
|
superblock.nx_o.o_xid) * superblock.nx_block_size;
|
|
omap_phys_t volume_object_map @ volume.apfs_omap_oid * superblock.nx_block_size;
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> volume_object_map_tree @ volume_object_map.om_tree_oid * superblock.nx_block_size;
|
|
|
|
btree_node_phys_t<superblock.nx_block_size, volume.apfs_incompatible_features> volume_root_tree @ omap_node_lookup(
|
|
addressof (volume_object_map_tree),
|
|
superblock.nx_block_size,
|
|
volume.apfs_root_tree_oid,
|
|
volume.apfs_o.o_xid) * superblock.nx_block_size;
|
|
j_inode_val_t volume_root_folder @ fstree_inode_lookup(
|
|
addressof (volume_root_tree),
|
|
addressof (volume_object_map_tree),
|
|
superblock.nx_block_size,
|
|
ROOT_DIR_INO_NUM,
|
|
volume.apfs_incompatible_features,
|
|
volume.apfs_o.o_xid
|
|
);
|
|
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> volume_extentref_tree @ volume.apfs_extentref_tree_oid * superblock.nx_block_size;
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> volume_snapshot_tree @ volume.apfs_snap_meta_tree_oid * superblock.nx_block_size;
|
|
|
|
apfs_superblock_t snapshot_volume @ volume_snapshot_tree.btn_toc[0].snap_meta_val.sblock_oid * superblock.nx_block_size;
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> snapshot_extentref_tree @ volume_snapshot_tree.btn_toc[0].snap_meta_val.extentref_tree_oid * superblock.nx_block_size;
|
|
|
|
checkpoint_mapping_t spaceman_cp = checkpoint_map_lookup(checkpoint_map, superblock.nx_spaceman_oid, o_type_id_t::OBJECT_TYPE_SPACEMAN);
|
|
spaceman_phys_t spaceman @ spaceman_cp.cpm_paddr * superblock.nx_block_size;
|
|
|
|
checkpoint_mapping_t spaceman_freequeue_manager_cp = checkpoint_map_lookup(
|
|
checkpoint_map,
|
|
spaceman.sm_fq[sfq_t::SFQ_IP].sfq_tree_oid,
|
|
o_type_id_t::OBJECT_TYPE_BTREE);
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> spaceman_freequeue_manager @ spaceman_freequeue_manager_cp.cpm_paddr * superblock.nx_block_size;
|
|
|
|
checkpoint_mapping_t spaceman_freequeue_volume_cp = checkpoint_map_lookup(
|
|
checkpoint_map,
|
|
spaceman.sm_fq[sfq_t::SFQ_MAIN].sfq_tree_oid,
|
|
o_type_id_t::OBJECT_TYPE_BTREE);
|
|
|
|
btree_node_phys_t<superblock.nx_block_size, apfs_incompatible_features_null> spaceman_freequeue_volume @ spaceman_freequeue_volume_cp.cpm_paddr * superblock.nx_block_size;
|
|
|
|
paddr_t spaceman_main_cibs_addr @ addressof (spaceman) + spaceman.sm_dev[smdev_t::SD_MAIN].sm_addr_offset;
|
|
chunk_info_block_t spaceman_main_cibs[spaceman.sm_dev[smdev_t::SD_MAIN].sm_cib_count] @ spaceman_main_cibs_addr * superblock.nx_block_size;
|
|
|
|
paddr_t spaceman_tier2_cibs_addr @ addressof (spaceman) + spaceman.sm_dev[smdev_t::SD_TIER2].sm_addr_offset;
|
|
chunk_info_block_t spaceman_tier2_cibs[spaceman.sm_dev[smdev_t::SD_TIER2].sm_cib_count] @ spaceman_tier2_cibs_addr * superblock.nx_block_size;
|
|
|
|
checkpoint_mapping_t reaper_cp = checkpoint_map_lookup(checkpoint_map, superblock.nx_reaper_oid, o_type_id_t::OBJECT_TYPE_NX_REAPER);
|
|
nx_reaper_phys_t reaper @ reaper_cp.cpm_paddr * superblock.nx_block_size;
|
|
|
|
media_keybag_t container_keybag @ superblock.nx_keylocker.pr_start_paddr * superblock.nx_block_size;
|
|
|
|
// ================== VERIFY ==================
|
|
block_verify(addressof (initial_superblock), initial_superblock.nx_block_size);
|
|
block_verify(addressof (superblock), superblock.nx_block_size);
|
|
block_verify(addressof (object_map), superblock.nx_block_size);
|
|
block_verify(addressof (object_map_tree), superblock.nx_block_size);
|
|
block_verify(addressof (volume), superblock.nx_block_size);
|
|
block_verify(addressof (volume_object_map), superblock.nx_block_size);
|
|
block_verify(addressof (volume_root_tree), superblock.nx_block_size);
|
|
block_verify(addressof (spaceman), superblock.nx_block_size);
|
|
block_verify(addressof (reaper), superblock.nx_block_size);
|
|
block_verify(addressof (spaceman_freequeue_manager), superblock.nx_block_size);
|
|
block_verify(addressof (spaceman_freequeue_volume), superblock.nx_block_size);
|
|
block_verify(addressof (container_keybag), superblock.nx_block_size);
|
|
keybag_is_encrypted(container_keybag); |