pub const NX_MAGIC: u32 = 0x4253_584E;
pub const NX_INCOMPAT_FUSION: u64 = 0x100;
pub const NX_MINIMUM_BLOCK_SIZE: u32 = 4096;
pub const NX_DEFAULT_BLOCK_SIZE: u32 = 4096;
pub const NX_MAXIMUM_BLOCK_SIZE: u32 = 65536;
pub const NX_MINIMUM_CONTAINER_SIZE: u64 = 1_048_576;
pub const NX_MAX_FILE_SYSTEMS: u32 = 100;
const XP_BLOCKS_TREE_FLAG: u32 = 0x8000_0000;
const OFF_MAGIC: usize = 32; const OFF_BLOCK_SIZE: usize = 36; const OFF_BLOCK_COUNT: usize = 40; const OFF_INCOMPAT_FEATURES: usize = 64; const OFF_UUID: usize = 72; const OFF_SPACEMAN_OID: usize = 152; const OFF_REAPER_OID: usize = 168; const OFF_XP_DESC_BLOCKS: usize = 104; const OFF_XP_DATA_BLOCKS: usize = 108; const OFF_XP_DESC_BASE: usize = 112; const OFF_XP_DATA_BASE: usize = 120; const OFF_OMAP_OID: usize = 160; const OFF_MAX_FILE_SYSTEMS: usize = 180; const OFF_FS_OID: usize = 184;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct NxSuperblock {
pub xid: u64,
pub uuid: [u8; 16],
pub block_size: u32,
pub block_count: u64,
pub incompatible_features: u64,
pub xp_desc_base: u64,
pub xp_desc_blocks: u32,
pub xp_data_base: u64,
pub xp_data_blocks: u32,
pub omap_oid: u64,
pub spaceman_oid: u64,
pub reaper_oid: u64,
pub fs_oids: Vec<u64>,
desc_is_tree: bool,
data_is_tree: bool,
}
impl NxSuperblock {
pub fn parse(block: &[u8]) -> crate::Result<Self> {
if block.len() < OFF_MAGIC + 4 {
return Err(crate::ApfsError::NoValidSuperblock {
checked: 1,
last_magic: 0,
});
}
let magic = crate::bytes::le_u32(block, OFF_MAGIC);
if magic != NX_MAGIC {
return Err(crate::ApfsError::NoValidSuperblock {
checked: 1,
last_magic: magic,
});
}
let stored = crate::object::fletcher64_stored(block);
let computed = crate::object::fletcher64_checksum(block);
if stored != computed {
let oid = crate::bytes::le_u64(block, 8);
return Err(crate::ApfsError::ChecksumMismatch {
block: oid,
stored,
computed,
});
}
let xid = crate::bytes::le_u64(block, 16);
let uuid = crate::bytes::arr::<16>(block, OFF_UUID);
let block_size = crate::bytes::le_u32(block, OFF_BLOCK_SIZE);
let block_count = crate::bytes::le_u64(block, OFF_BLOCK_COUNT);
let incompatible_features = crate::bytes::le_u64(block, OFF_INCOMPAT_FEATURES);
let desc_blocks_raw = crate::bytes::le_u32(block, OFF_XP_DESC_BLOCKS);
let data_blocks_raw = crate::bytes::le_u32(block, OFF_XP_DATA_BLOCKS);
let desc_is_tree = desc_blocks_raw & XP_BLOCKS_TREE_FLAG != 0;
let data_is_tree = data_blocks_raw & XP_BLOCKS_TREE_FLAG != 0;
let omap_oid = crate::bytes::le_u64(block, OFF_OMAP_OID);
let spaceman_oid = crate::bytes::le_u64(block, OFF_SPACEMAN_OID);
let reaper_oid = crate::bytes::le_u64(block, OFF_REAPER_OID);
let max_fs = crate::bytes::le_u32(block, OFF_MAX_FILE_SYSTEMS).min(NX_MAX_FILE_SYSTEMS);
let mut fs_oids = Vec::new();
for i in 0..max_fs as usize {
let oid = crate::bytes::le_u64(block, OFF_FS_OID + i * 8);
if oid != 0 {
fs_oids.push(oid);
}
}
Ok(Self {
xid,
uuid,
block_size,
block_count,
incompatible_features,
xp_desc_base: crate::bytes::le_u64(block, OFF_XP_DESC_BASE),
xp_desc_blocks: desc_blocks_raw & !XP_BLOCKS_TREE_FLAG,
xp_data_base: crate::bytes::le_u64(block, OFF_XP_DATA_BASE),
xp_data_blocks: data_blocks_raw & !XP_BLOCKS_TREE_FLAG,
omap_oid,
spaceman_oid,
reaper_oid,
fs_oids,
desc_is_tree,
data_is_tree,
})
}
#[must_use]
pub fn xp_desc_is_tree(&self) -> bool {
self.desc_is_tree
}
#[must_use]
pub fn xp_data_is_tree(&self) -> bool {
self.data_is_tree
}
}