use crate::types::object::{ObjectHeader, ObjectType};
use crate::types::{le_u32, le_u64, take};
pub const VOLUME_MAGIC: [u8; 4] = *b"APSB";
pub const APFS_INCOMPAT_CASE_INSENSITIVE: u64 = 0x1;
pub const VOLUME_NAME_LENGTH: usize = 256;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VolumeSuperblock {
pub object: ObjectHeader,
pub fs_index: u32,
pub optional_features: u64,
pub readonly_compatible_features: u64,
pub incompatible_features: u64,
pub allocated_block_count: u64,
pub object_map_oid: u64,
pub root_tree_oid: u64,
pub extent_reference_tree_oid: u64,
pub snapshot_metadata_tree_oid: u64,
pub number_files: u64,
pub number_directories: u64,
pub number_symlinks: u64,
pub number_snapshots: u64,
pub volume_id: [u8; 16],
pub flags: u64,
pub volume_name: [u8; VOLUME_NAME_LENGTH],
}
impl VolumeSuperblock {
pub fn parse(data: &[u8]) -> crate::Result<Self> {
let object = ObjectHeader::parse(data)?;
if object.kind() != ObjectType::VolumeSuperblock as u16 {
return Err(crate::ApfsError::InvalidValue(
"volume superblock object type",
));
}
let magic = take::<4>(data, 32)?;
if magic != VOLUME_MAGIC {
return Err(crate::ApfsError::InvalidMagic {
expected: VOLUME_MAGIC,
actual: magic,
});
}
Ok(Self {
object,
fs_index: le_u32(data, 36)?,
optional_features: le_u64(data, 40)?,
readonly_compatible_features: le_u64(data, 48)?,
incompatible_features: le_u64(data, 56)?,
allocated_block_count: le_u64(data, 88)?,
object_map_oid: le_u64(data, 128)?,
root_tree_oid: le_u64(data, 136)?,
extent_reference_tree_oid: le_u64(data, 144)?,
snapshot_metadata_tree_oid: le_u64(data, 152)?,
number_files: le_u64(data, 184)?,
number_directories: le_u64(data, 192)?,
number_symlinks: le_u64(data, 200)?,
number_snapshots: le_u64(data, 216)?,
volume_id: take(data, 240)?,
flags: le_u64(data, 264)?,
volume_name: take(data, 704)?,
})
}
pub const fn is_case_insensitive(&self) -> bool {
self.incompatible_features & APFS_INCOMPAT_CASE_INSENSITIVE != 0
}
pub fn name(&self) -> crate::Result<&str> {
let len = self
.volume_name
.iter()
.position(|byte| *byte == 0)
.unwrap_or(VOLUME_NAME_LENGTH);
core::str::from_utf8(&self.volume_name[..len])
.map_err(|_| crate::ApfsError::InvalidValue("volume name UTF-8"))
}
}