#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::io::Cursor;
use apfs_core::object::fletcher64_checksum;
use apfs_core::{ApfsContainer, ApfsError};
const HEAD: &[u8] = include_bytes!("../../tests/data/apfs_nxsb_head.bin");
const CHAIN: &[u8] = include_bytes!("../../tests/data/apfs_container_chain.bin");
const NX_INCOMPAT_FUSION: u64 = 0x100;
const OFF_INCOMPAT: usize = 64;
fn with_fusion_bit(image: &[u8]) -> Vec<u8> {
let mut img = image.to_vec();
let mut feats = u64::from_le_bytes(img[OFF_INCOMPAT..OFF_INCOMPAT + 8].try_into().unwrap());
feats |= NX_INCOMPAT_FUSION;
img[OFF_INCOMPAT..OFF_INCOMPAT + 8].copy_from_slice(&feats.to_le_bytes());
let cks = fletcher64_checksum(&img[..4096]);
img[0..8].copy_from_slice(&cks.to_le_bytes());
img
}
#[test]
fn open_resolves_live_superblock_geometry() {
let container = ApfsContainer::open(Cursor::new(HEAD)).expect("open container");
let nx = container.superblock();
assert_eq!(nx.block_size, 4096);
assert_eq!(nx.block_count, 32758);
assert_eq!(nx.xid, 2);
assert_eq!(nx.omap_oid, 343);
assert_eq!(nx.fs_oids, vec![1026]);
assert_eq!(
nx.uuid,
[
0x40, 0x11, 0x50, 0x33, 0x95, 0x23, 0x44, 0x96, 0x96, 0xa2, 0x0e, 0xda, 0xde, 0xec,
0xa5, 0x65
]
);
assert_eq!(container.live_superblock_paddr(), 4);
}
#[test]
fn open_fails_loud_on_fusion_container() {
let fusion = with_fusion_bit(HEAD);
match ApfsContainer::open(Cursor::new(fusion)) {
Err(ApfsError::UnsupportedFusion) => {}
Err(other) => panic!("expected UnsupportedFusion, got {other:?}"),
Ok(_) => panic!("expected UnsupportedFusion, but open() succeeded on a Fusion container"),
}
}
#[test]
fn open_does_not_flag_real_non_fusion_container() {
assert!(ApfsContainer::open(Cursor::new(HEAD)).is_ok());
}
#[test]
fn resolves_ephemeral_spaceman_and_reaper_paddrs() {
let container = ApfsContainer::open(Cursor::new(CHAIN)).expect("open container");
assert_eq!(container.spaceman_paddr(), Some(11));
assert_eq!(container.reaper_paddr(), Some(12));
let maps = container.checkpoint_mappings();
assert!(maps.iter().any(|m| m.paddr == 11));
assert!(maps.iter().any(|m| m.paddr == 12));
}
#[test]
fn open_fails_loud_on_garbage_source() {
let garbage = vec![0u8; 4096 * 2];
assert!(ApfsContainer::open(Cursor::new(garbage)).is_err());
}
#[test]
fn open_fails_loud_on_truncated_source() {
let tiny = vec![0u8; 16];
assert!(ApfsContainer::open(Cursor::new(tiny)).is_err());
}