Skip to main content

vhdx/
header.rs

1use crate::bytes::{le_arr16, le_u32, le_u64};
2use crate::error::{Result, VhdxError};
3
4/// Offsets per MS-VHDX spec §2.1 — each block occupies a 64 KB slot within
5/// the first 1 MB of the file (the "header section").
6pub const HEADER1_OFFSET: u64 = 0x0001_0000; // 64 KB
7pub const HEADER2_OFFSET: u64 = 0x0002_0000; // 128 KB
8pub const REGION_TABLE1_OFFSET: u64 = 0x0003_0000; // 192 KB
9pub const REGION_TABLE2_OFFSET: u64 = 0x0004_0000; // 256 KB
10
11pub const HEADER_SIGNATURE: &[u8; 4] = b"head";
12pub const HEADER_SIZE: usize = 4096;
13
14/// Parsed VHDX header (the active copy with highest valid sequence number).
15#[derive(Debug, Clone)]
16pub struct VhdxHeader {
17    pub sequence_number: u64,
18    pub log_guid: [u8; 16],
19    pub log_offset: u64,
20    pub log_length: u32,
21}
22
23/// Select the active header from a raw byte slice covering both header copies.
24/// Returns the header with the highest sequence number whose CRC32C is valid.
25pub fn parse_active_header(data: &[u8]) -> Result<VhdxHeader> {
26    let h1 = parse_one_header(data, HEADER1_OFFSET as usize);
27    let h2 = parse_one_header(data, HEADER2_OFFSET as usize);
28    match (h1, h2) {
29        (Ok(a), Ok(b)) => {
30            if a.sequence_number >= b.sequence_number {
31                Ok(a)
32            } else {
33                Ok(b)
34            }
35        }
36        (Ok(a), Err(_)) => Ok(a),
37        (Err(_), Ok(b)) => Ok(b),
38        (Err(_), Err(_)) => Err(VhdxError::NoValidHeader),
39    }
40}
41
42fn parse_one_header(data: &[u8], offset: usize) -> Result<VhdxHeader> {
43    let end = offset
44        .checked_add(HEADER_SIZE)
45        .ok_or(VhdxError::NoValidHeader)?;
46    if data.len() < end {
47        return Err(VhdxError::NoValidHeader);
48    }
49    let slice = &data[offset..end];
50    if &slice[0..4] != HEADER_SIGNATURE {
51        return Err(VhdxError::NoValidHeader);
52    }
53    if !verify_crc32c(slice) {
54        return Err(VhdxError::NoValidHeader);
55    }
56    let sequence_number = le_u64(slice, 8);
57    let log_guid: [u8; 16] = le_arr16(slice, 48);
58    let log_length = le_u32(slice, 68);
59    let log_offset = le_u64(slice, 72);
60    Ok(VhdxHeader {
61        sequence_number,
62        log_guid,
63        log_offset,
64        log_length,
65    })
66}
67
68fn verify_crc32c(block: &[u8]) -> bool {
69    let stored = le_u32(block, 4);
70    let mut buf = block.to_vec();
71    buf[4..8].fill(0);
72    crc32c(&buf) == stored
73}
74
75pub fn crc32c(data: &[u8]) -> u32 {
76    const POLY: u32 = 0x82F6_3B78;
77    let mut crc: u32 = 0xFFFF_FFFF;
78    for &byte in data {
79        crc ^= u32::from(byte);
80        for _ in 0..8 {
81            if crc & 1 != 0 {
82                crc = (crc >> 1) ^ POLY;
83            } else {
84                crc >>= 1;
85            }
86        }
87    }
88    crc ^ 0xFFFF_FFFF
89}