use super::jrec::split_obj_id;
pub fn decode_snap_meta_key(buf: &[u8]) -> crate::Result<(u8, u64)> {
if buf.len() < 8 {
return Err(crate::Error::InvalidImage(
"apfs: snap-meta key shorter than j_key_t".into(),
));
}
let hdr = u64::from_le_bytes(buf[0..8].try_into().unwrap());
let (kind, lo) = split_obj_id(hdr);
Ok((kind, lo))
}
#[derive(Debug, Clone)]
pub struct SnapMetaVal {
pub extentref_tree_oid: u64,
pub sblock_oid: u64,
pub create_time: u64,
pub change_time: u64,
pub inum: u64,
pub extentref_tree_type: u32,
pub flags: u32,
pub name: String,
}
impl SnapMetaVal {
pub const FIXED_PREFIX: usize = 50;
pub fn decode(buf: &[u8]) -> crate::Result<Self> {
if buf.len() < Self::FIXED_PREFIX {
return Err(crate::Error::InvalidImage(
"apfs: j_snap_metadata_val too short".into(),
));
}
let extentref_tree_oid = u64::from_le_bytes(buf[0..8].try_into().unwrap());
let sblock_oid = u64::from_le_bytes(buf[8..16].try_into().unwrap());
let create_time = u64::from_le_bytes(buf[16..24].try_into().unwrap());
let change_time = u64::from_le_bytes(buf[24..32].try_into().unwrap());
let inum = u64::from_le_bytes(buf[32..40].try_into().unwrap());
let extentref_tree_type = u32::from_le_bytes(buf[40..44].try_into().unwrap());
let flags = u32::from_le_bytes(buf[44..48].try_into().unwrap());
let name_len = u16::from_le_bytes(buf[48..50].try_into().unwrap()) as usize;
let end = (50 + name_len).min(buf.len());
let raw = &buf[50..end];
let nul = raw.iter().position(|&b| b == 0).unwrap_or(raw.len());
let name = String::from_utf8_lossy(&raw[..nul]).into_owned();
Ok(Self {
extentref_tree_oid,
sblock_oid,
create_time,
change_time,
inum,
extentref_tree_type,
flags,
name,
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct SnapNameVal {
pub snap_xid: u64,
}
impl SnapNameVal {
pub const SIZE: usize = 8;
pub fn decode(buf: &[u8]) -> crate::Result<Self> {
if buf.len() < Self::SIZE {
return Err(crate::Error::InvalidImage(
"apfs: j_snap_name_val too short".into(),
));
}
Ok(Self {
snap_xid: u64::from_le_bytes(buf[0..8].try_into().unwrap()),
})
}
}
#[cfg(test)]
mod tests {
use super::super::jrec::{APFS_TYPE_SNAP_METADATA, OBJ_TYPE_SHIFT};
use super::*;
#[test]
fn decode_snap_meta_key_basic() {
let mut k = [0u8; 8];
let hdr = ((APFS_TYPE_SNAP_METADATA as u64) << OBJ_TYPE_SHIFT) | 42u64;
k.copy_from_slice(&hdr.to_le_bytes());
let (kind, xid) = decode_snap_meta_key(&k).unwrap();
assert_eq!(kind, APFS_TYPE_SNAP_METADATA);
assert_eq!(xid, 42);
}
#[test]
fn decode_snap_meta_val_with_name() {
let mut v = vec![0u8; SnapMetaVal::FIXED_PREFIX + 8];
v[8..16].copy_from_slice(&0x1234u64.to_le_bytes()); v[16..24].copy_from_slice(&100u64.to_le_bytes()); v[48..50].copy_from_slice(&5u16.to_le_bytes()); v[50..55].copy_from_slice(b"hi\0\0\0");
let m = SnapMetaVal::decode(&v).unwrap();
assert_eq!(m.sblock_oid, 0x1234);
assert_eq!(m.create_time, 100);
assert_eq!(m.name, "hi");
}
#[test]
fn decode_snap_name_val() {
let mut v = [0u8; 8];
v.copy_from_slice(&77u64.to_le_bytes());
let n = SnapNameVal::decode(&v).unwrap();
assert_eq!(n.snap_xid, 77);
}
}