use crate::error::{Result, VmdkError};
pub const MAGIC: u32 = 0x564D_444B;
pub const VERSION: u32 = 1;
pub const VERSION_STREAM_OPT: u32 = 3;
pub const SECTOR_SIZE: u64 = 512;
pub const GD_AT_END: u64 = 0xffff_ffff_ffff_ffff;
pub struct SparseExtentHeader {
pub capacity: u64, pub grain_size: u64, pub descriptor_offset: u64, pub descriptor_size: u64, pub num_gtes_per_gt: u32,
pub gd_offset: u64, pub compressed: bool,
}
impl SparseExtentHeader {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < 512 {
return Err(VmdkError::FileTooSmall);
}
let magic = u32::from_le_bytes(data[0..4].try_into().expect("4 bytes"));
if magic != MAGIC {
return Err(VmdkError::BadMagic);
}
let version = u32::from_le_bytes(data[4..8].try_into().expect("4 bytes"));
if version != VERSION && version != VERSION_STREAM_OPT {
return Err(VmdkError::UnsupportedVersion(version));
}
let capacity = u64::from_le_bytes(data[12..20].try_into().expect("8 bytes"));
let grain_size = u64::from_le_bytes(data[20..28].try_into().expect("8 bytes"));
let descriptor_offset = u64::from_le_bytes(data[28..36].try_into().expect("8 bytes"));
let descriptor_size = u64::from_le_bytes(data[36..44].try_into().expect("8 bytes"));
let num_gtes_per_gt = u32::from_le_bytes(data[44..48].try_into().expect("4 bytes"));
let gd_offset = u64::from_le_bytes(data[56..64].try_into().expect("8 bytes"));
let compress_algorithm = u16::from_le_bytes(data[77..79].try_into().expect("2 bytes"));
match (version, compress_algorithm) {
(VERSION, 0) | (VERSION_STREAM_OPT, 1) => {}
_ => return Err(VmdkError::CompressedNotSupported),
}
if grain_size == 0 {
return Err(VmdkError::InvalidGeometry("grain_size must be > 0".into()));
}
if num_gtes_per_gt == 0 {
return Err(VmdkError::InvalidGeometry(
"num_gtes_per_gt must be > 0".into(),
));
}
Ok(SparseExtentHeader {
capacity,
grain_size,
descriptor_offset,
descriptor_size,
num_gtes_per_gt,
gd_offset,
compressed: compress_algorithm != 0,
})
}
}