1use crate::error::{Result, VhdxError};
2
3pub const HEADER1_OFFSET: u64 = 0x0001_0000; pub const HEADER2_OFFSET: u64 = 0x0002_0000; pub const REGION_TABLE1_OFFSET: u64 = 0x0003_0000; pub const REGION_TABLE2_OFFSET: u64 = 0x0004_0000; pub const HEADER_SIGNATURE: &[u8; 4] = b"head";
11pub const HEADER_SIZE: usize = 4096;
12
13#[derive(Debug, Clone)]
15pub struct VhdxHeader {
16 pub sequence_number: u64,
17 pub log_guid: [u8; 16],
18 pub log_offset: u64,
19 pub log_length: u32,
20}
21
22pub fn parse_active_header(data: &[u8]) -> Result<VhdxHeader> {
25 let h1 = parse_one_header(data, HEADER1_OFFSET as usize);
26 let h2 = parse_one_header(data, HEADER2_OFFSET as usize);
27 match (h1, h2) {
28 (Ok(a), Ok(b)) => {
29 if a.sequence_number >= b.sequence_number {
30 Ok(a)
31 } else {
32 Ok(b)
33 }
34 }
35 (Ok(a), Err(_)) => Ok(a),
36 (Err(_), Ok(b)) => Ok(b),
37 (Err(_), Err(_)) => Err(VhdxError::NoValidHeader),
38 }
39}
40
41fn parse_one_header(data: &[u8], offset: usize) -> Result<VhdxHeader> {
42 let end = offset
43 .checked_add(HEADER_SIZE)
44 .ok_or(VhdxError::NoValidHeader)?;
45 if data.len() < end {
46 return Err(VhdxError::NoValidHeader);
47 }
48 let slice = &data[offset..end];
49 if &slice[0..4] != HEADER_SIGNATURE {
50 return Err(VhdxError::NoValidHeader);
51 }
52 if !verify_crc32c(slice) {
53 return Err(VhdxError::NoValidHeader);
54 }
55 let sequence_number = u64::from_le_bytes(slice[8..16].try_into().unwrap());
56 let log_guid: [u8; 16] = slice[48..64].try_into().unwrap();
57 let log_length = u32::from_le_bytes(slice[68..72].try_into().unwrap());
58 let log_offset = u64::from_le_bytes(slice[72..80].try_into().unwrap());
59 Ok(VhdxHeader {
60 sequence_number,
61 log_guid,
62 log_offset,
63 log_length,
64 })
65}
66
67fn verify_crc32c(block: &[u8]) -> bool {
68 let stored = u32::from_le_bytes(block[4..8].try_into().unwrap());
69 let mut buf = block.to_vec();
70 buf[4..8].fill(0);
71 crc32c(&buf) == stored
72}
73
74pub fn crc32c(data: &[u8]) -> u32 {
75 const POLY: u32 = 0x82F6_3B78;
76 let mut crc: u32 = 0xFFFF_FFFF;
77 for &byte in data {
78 crc ^= u32::from(byte);
79 for _ in 0..8 {
80 if crc & 1 != 0 {
81 crc = (crc >> 1) ^ POLY;
82 } else {
83 crc >>= 1;
84 }
85 }
86 }
87 crc ^ 0xFFFF_FFFF
88}