use crate::error::{Result, VhdError};
pub const FOOTER_SIZE: usize = 512;
pub const COOKIE: &[u8; 8] = b"conectix";
pub const CURRENT_VERSION: u32 = 0x0001_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiskType {
Fixed = 2,
Dynamic = 3,
}
#[derive(Debug, Clone)]
pub struct VhdFooter {
pub disk_type: DiskType,
pub current_size: u64, pub data_offset: u64, }
impl VhdFooter {
pub fn parse(data: &[u8]) -> Result<Self> {
if data.len() < FOOTER_SIZE {
return Err(VhdError::FileTooSmall);
}
let footer = &data[data.len() - FOOTER_SIZE..];
if &footer[0..8] != COOKIE {
return Err(VhdError::BadCookie);
}
let version = u32::from_be_bytes(footer[12..16].try_into().unwrap());
if version != CURRENT_VERSION {
return Err(VhdError::UnsupportedVersion(version));
}
let data_offset = u64::from_be_bytes(footer[16..24].try_into().unwrap());
let current_size = u64::from_be_bytes(footer[40..48].try_into().unwrap());
let disk_type_raw = u32::from_be_bytes(footer[60..64].try_into().unwrap());
let disk_type = match disk_type_raw {
2 => DiskType::Fixed,
3 => DiskType::Dynamic,
4 => return Err(VhdError::DifferencingNotSupported),
other => return Err(VhdError::UnknownDiskType(other)),
};
let stored_checksum = u32::from_be_bytes(footer[64..68].try_into().unwrap());
let computed = checksum(footer);
if stored_checksum != computed {
return Err(VhdError::ChecksumMismatch {
expected: stored_checksum,
actual: computed,
});
}
Ok(VhdFooter { disk_type, current_size, data_offset })
}
}
fn checksum(footer: &[u8]) -> u32 {
let mut sum: u32 = 0;
for (i, &byte) in footer.iter().enumerate() {
if (64..68).contains(&i) {
continue;
}
sum = sum.wrapping_add(u32::from(byte));
}
!sum
}
#[cfg(any(test, feature = "test-helpers"))]
pub fn test_fixed_footer(virtual_size: u64) -> Vec<u8> {
let mut footer = vec![0u8; FOOTER_SIZE];
footer[0..8].copy_from_slice(COOKIE);
footer[8..12].copy_from_slice(&0x0000_0002u32.to_be_bytes());
footer[12..16].copy_from_slice(&CURRENT_VERSION.to_be_bytes());
footer[16..24].copy_from_slice(&0xFFFF_FFFF_FFFF_FFFFu64.to_be_bytes());
footer[32..40].copy_from_slice(&virtual_size.to_be_bytes());
footer[40..48].copy_from_slice(&virtual_size.to_be_bytes());
footer[60..64].copy_from_slice(&2u32.to_be_bytes());
let cs = checksum(&footer);
footer[64..68].copy_from_slice(&cs.to_be_bytes());
footer
}