use aff4::Aff4Reader;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
fn corpus(name: &str) -> std::path::PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/data")
.join(name)
}
#[test]
fn corpus_base_linear_virtual_disk_size() {
let path = corpus("Base-Linear.aff4");
if !path.exists() {
return;
}
let reader = Aff4Reader::open(&path).expect("open Base-Linear.aff4");
assert_eq!(
reader.virtual_disk_size(),
268_435_456_u64,
"virtual_disk_size must come from the aff4:Map block (268435456 = 256 MiB)"
);
}
#[test]
fn corpus_base_linear_sector0_reads_ok() {
let path = corpus("Base-Linear.aff4");
if !path.exists() {
return;
}
let mut reader = Aff4Reader::open(&path).expect("open");
let mut buf = [0u8; 512];
reader.read_exact(&mut buf).expect("sector 0 must be readable");
assert_eq!(
buf,
[0u8; 512],
"sparse region (chunks 0-1) must read as zeros"
);
}
#[test]
fn corpus_base_linear_snappy_chunk_matches_reference() {
let path = corpus("Base-Linear.aff4");
if !path.exists() {
return;
}
let reference = reference_bytes_via_zip_snap(&path, 65536, 512);
let mut reader = Aff4Reader::open(&path).expect("open");
reader.seek(SeekFrom::Start(98304)).expect("seek to first non-sparse virtual region");
let mut buf = vec![0u8; 512];
reader
.read_exact(&mut buf)
.expect("first non-sparse Snappy chunk must be readable");
assert_eq!(
&buf,
&reference[..],
"bytes at virtual offset 98304 must match Snappy-decompressed ImageStream chunk 2"
);
}
fn reference_bytes_via_zip_snap(path: &Path, offset: u64, len: usize) -> Vec<u8> {
use zip::ZipArchive;
let file = std::fs::File::open(path).expect("open");
let mut archive = ZipArchive::new(file).expect("zip");
let segment0_suffix = format!("/{:08x}", 0u32);
let bevy_name = archive
.file_names()
.filter(|n| n.ends_with(&segment0_suffix) && !n.contains('.'))
.next()
.expect("no segment 0 bevy found")
.to_string();
let index_name = format!("{}.index", bevy_name);
let index_bytes = read_zip_entry(&mut archive, &index_name);
let bevy_bytes = read_zip_entry(&mut archive, &bevy_name);
let chunk_size = 32768usize;
let chunk_idx = (offset as usize) / chunk_size;
let offset_in_chunk = (offset as usize) % chunk_size;
let entry_size = 4usize;
let end =
u32::from_le_bytes(index_bytes[chunk_idx * entry_size..(chunk_idx + 1) * entry_size]
.try_into()
.unwrap()) as usize;
let start = if chunk_idx == 0 {
0
} else {
u32::from_le_bytes(
index_bytes[(chunk_idx - 1) * entry_size..chunk_idx * entry_size]
.try_into()
.unwrap(),
) as usize
};
let compressed = &bevy_bytes[start..end];
let mut dec = snap::raw::Decoder::new();
let decompressed = dec
.decompress_vec(compressed)
.expect("snap::raw decompress failed");
decompressed[offset_in_chunk..offset_in_chunk + len].to_vec()
}
fn read_zip_entry(archive: &mut zip::ZipArchive<std::fs::File>, name: &str) -> Vec<u8> {
let mut entry = archive.by_name(name).expect("zip entry not found");
let mut data = Vec::new();
entry.read_to_end(&mut data).expect("read zip entry");
data
}
#[test]
fn corpus_exabyte_sparse_virtual_size() {
let path = corpus("Base-ExabyteSparse.aff4");
if !path.exists() {
return;
}
let reader = Aff4Reader::open(&path).expect("open Base-ExabyteSparse.aff4");
assert_eq!(
reader.virtual_disk_size(),
9_223_372_036_854_775_296_u64,
"virtual_disk_size must come from aff4:Map block (size=9223372036854775296), \
not from the inner ImageStream block (size=4718592)"
);
}
#[test]
fn corpus_base_allocated_virtual_size() {
let path = corpus("Base-Allocated.aff4");
if !path.exists() {
return;
}
let reader = Aff4Reader::open(&path).expect("open Base-Allocated.aff4");
assert_eq!(
reader.virtual_disk_size(),
268_435_456_u64,
"virtual_disk_size must come from aff4:Map block (268435456), \
not from the inner ImageStream block (3964928)"
);
}