use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{Cursor, Read, Seek, SeekFrom};
use crate::error::{ApfsError, Result};
use crate::fletcher;
pub const OBJECT_TYPE_NX_SUPERBLOCK: u32 = 0x01;
pub const OBJECT_TYPE_BTREE: u32 = 0x02;
pub const OBJECT_TYPE_BTREE_NODE: u32 = 0x03;
pub const OBJECT_TYPE_SPACEMAN: u32 = 0x05;
pub const OBJECT_TYPE_OMAP: u32 = 0x0B;
pub const OBJECT_TYPE_CHECKPOINT_MAP: u32 = 0x0C;
pub const OBJECT_TYPE_FS: u32 = 0x0D;
pub const OBJ_PHYSICAL: u32 = 0x00000000;
pub const OBJ_VIRTUAL: u32 = 0x80000000;
pub const OBJ_EPHEMERAL: u32 = 0x40000000;
pub const OBJ_STORAGE_TYPE_MASK: u32 = 0xC0000000;
pub const OBJECT_TYPE_MASK: u32 = 0x0000FFFF;
#[derive(Debug, Clone)]
pub struct ObjectHeader {
pub checksum: u64, pub oid: u64, pub xid: u64, pub type_and_flags: u32, pub subtype: u32, }
impl ObjectHeader {
pub const SIZE: usize = 32;
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < Self::SIZE {
return Err(ApfsError::CorruptedData(format!(
"object header too short: {} bytes",
data.len()
)));
}
let mut cursor = Cursor::new(data);
Ok(ObjectHeader {
checksum: cursor.read_u64::<LittleEndian>()?,
oid: cursor.read_u64::<LittleEndian>()?,
xid: cursor.read_u64::<LittleEndian>()?,
type_and_flags: cursor.read_u32::<LittleEndian>()?,
subtype: cursor.read_u32::<LittleEndian>()?,
})
}
pub fn object_type(&self) -> u32 {
self.type_and_flags & OBJECT_TYPE_MASK
}
pub fn storage_type(&self) -> u32 {
self.type_and_flags & OBJ_STORAGE_TYPE_MASK
}
pub fn is_physical(&self) -> bool {
self.storage_type() == OBJ_PHYSICAL
}
}
pub fn read_object<R: Read + Seek>(
reader: &mut R,
block_number: u64,
block_size: u32,
) -> Result<(ObjectHeader, Vec<u8>)> {
let offset = block_number * block_size as u64;
reader.seek(SeekFrom::Start(offset))?;
let mut block = vec![0u8; block_size as usize];
reader.read_exact(&mut block)?;
if !fletcher::verify_object(&block) {
return Err(ApfsError::InvalidChecksum);
}
let header = ObjectHeader::parse(&block)?;
Ok((header, block))
}
pub fn read_block<R: Read + Seek>(
reader: &mut R,
block_number: u64,
block_size: u32,
) -> Result<Vec<u8>> {
let offset = block_number * block_size as u64;
reader.seek(SeekFrom::Start(offset))?;
let mut block = vec![0u8; block_size as usize];
reader.read_exact(&mut block)?;
Ok(block)
}