use crate::btree::{self, BTreeSubtype};
use crate::object::{fletcher64_checksum, fletcher64_stored, ObjPhys};
const OBJECT_TYPE_OMAP: u16 = 0xb;
const OFF_OM_FLAGS: usize = 32;
const OFF_OM_TREE_TYPE: usize = 40;
const OFF_OM_TREE_OID: usize = 48;
const OFF_OM_SNAPSHOT_TREE_OID: usize = 56;
const OMAP_PHYS_MIN_LEN: usize = OFF_OM_TREE_OID + 8;
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct OmapEntry {
pub oid: u64,
pub xid: u64,
pub paddr: u64,
pub size: u32,
pub flags: u32,
}
fn unexpected_omap_type(found: u32) -> crate::ApfsError {
crate::ApfsError::UnexpectedObjectType {
structure: "omap_phys",
expected: u32::from(OBJECT_TYPE_OMAP),
found,
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct ObjectMap {
flags: u32,
tree_type: u32,
tree_oid: u64,
snapshot_tree_oid: u64,
}
impl ObjectMap {
pub fn parse(block: &[u8]) -> crate::Result<Self> {
if block.len() < OMAP_PHYS_MIN_LEN {
return Err(unexpected_omap_type(0));
}
let Some(hdr) = ObjPhys::parse(block) else {
return Err(unexpected_omap_type(0)); };
if hdr.obj_type() != OBJECT_TYPE_OMAP {
return Err(unexpected_omap_type(hdr.obj_type_raw));
}
let stored = fletcher64_stored(block);
let computed = fletcher64_checksum(block);
if stored != computed {
return Err(crate::ApfsError::ChecksumMismatch {
block: hdr.oid,
stored,
computed,
});
}
Ok(Self {
flags: crate::bytes::le_u32(block, OFF_OM_FLAGS),
tree_type: crate::bytes::le_u32(block, OFF_OM_TREE_TYPE),
tree_oid: crate::bytes::le_u64(block, OFF_OM_TREE_OID),
snapshot_tree_oid: crate::bytes::le_u64(block, OFF_OM_SNAPSHOT_TREE_OID),
})
}
#[must_use]
pub fn tree_oid(&self) -> u64 {
self.tree_oid
}
#[must_use]
pub fn tree_type(&self) -> u32 {
self.tree_type
}
#[must_use]
pub fn flags(&self) -> u32 {
self.flags
}
#[must_use]
pub fn snapshot_tree_oid(&self) -> u64 {
self.snapshot_tree_oid
}
pub fn resolve<R: std::io::Read + std::io::Seek>(
&self,
reader: &mut R,
oid: u64,
xid: u64,
block_size: usize,
) -> crate::Result<OmapEntry> {
let mut best: Option<OmapEntry> = None;
btree::find_leaf(
reader,
self.tree_oid,
block_size,
BTreeSubtype::Omap,
|key| {
let k_oid = crate::bytes::le_u64(key, 0);
let k_xid = crate::bytes::le_u64(key, 8);
(k_oid, k_xid).cmp(&(oid, xid))
},
&mut |key, value| {
let k_oid = crate::bytes::le_u64(key, 0);
let k_xid = crate::bytes::le_u64(key, 8);
if k_oid != oid || k_xid > xid {
return;
}
let entry = OmapEntry {
oid: k_oid,
xid: k_xid,
flags: crate::bytes::le_u32(value, 0),
size: crate::bytes::le_u32(value, 4),
paddr: crate::bytes::le_u64(value, 8),
};
if best.is_none_or(|b| k_xid > b.xid) {
best = Some(entry);
}
},
)?;
best.ok_or(crate::ApfsError::OmapUnresolved { oid, xid })
}
}