#![allow(clippy::unwrap_used, clippy::expect_used)]
use std::io::{Read, Seek, SeekFrom, Write};
const MB: u64 = 0x0010_0000;
const BLOCK_SIZE: u32 = 32 * MB as u32; const VIRTUAL_DISK_SIZE: u64 = 4096 * MB; const LOG_OFFSET: u64 = MB;
const LOG_LENGTH: u32 = MB as u32;
const META_OFFSET: u64 = 2 * MB;
const META_LENGTH: u32 = MB as u32;
const BAT_OFFSET: u64 = 3 * MB;
const BAT_LENGTH: u32 = MB as u32;
const DATA_OFFSET: u64 = 256 * MB;
fn crc32c(data: &[u8]) -> u32 {
const POLY: u32 = 0x82F6_3B78;
let mut crc: u32 = 0xFFFF_FFFF;
for &byte in data {
crc ^= u32::from(byte);
for _ in 0..8 {
crc = if crc & 1 != 0 {
(crc >> 1) ^ POLY
} else {
crc >> 1
};
}
}
crc ^ 0xFFFF_FFFF
}
const BAT_GUID: [u8; 16] = [
0x66, 0x77, 0xC2, 0x2D, 0x23, 0xF6, 0x00, 0x42, 0x9D, 0x64, 0x11, 0x5E, 0x9B, 0xFD, 0x4A, 0x08,
];
const METADATA_GUID: [u8; 16] = [
0x06, 0xA2, 0x7C, 0x8B, 0x90, 0x47, 0x9A, 0x4B, 0xB8, 0xFE, 0x57, 0x5F, 0x05, 0x0F, 0x88, 0x6E,
];
const GUID_FILE_PARAMETERS: [u8; 16] = [
0x37, 0x67, 0xA1, 0xCA, 0x36, 0xFA, 0x43, 0x4D, 0xB3, 0xB6, 0x33, 0xF0, 0xAA, 0x44, 0xE7, 0x6B,
];
const GUID_VIRTUAL_DISK_SIZE: [u8; 16] = [
0x24, 0x42, 0xA5, 0x2F, 0x1B, 0xCD, 0x76, 0x48, 0xB2, 0x11, 0x5D, 0xBE, 0xD8, 0x3B, 0xF4, 0xB8,
];
const GUID_LOGICAL_SECTOR_SIZE: [u8; 16] = [
0x1D, 0xBF, 0x41, 0x81, 0x6F, 0xA9, 0x09, 0x47, 0xBA, 0x47, 0xF2, 0x33, 0xA8, 0xFA, 0xAB, 0x5F,
];
fn write_header(slot: &mut [u8], seq: u64) {
let h = &mut slot[..4096];
h[0..4].copy_from_slice(b"head");
h[8..16].copy_from_slice(&seq.to_le_bytes());
h[64..66].copy_from_slice(&1u16.to_le_bytes());
h[66..68].copy_from_slice(&1u16.to_le_bytes());
h[68..72].copy_from_slice(&LOG_LENGTH.to_le_bytes());
h[72..80].copy_from_slice(&LOG_OFFSET.to_le_bytes());
let c = crc32c(h);
h[4..8].copy_from_slice(&c.to_le_bytes());
}
fn write_region_table(rt: &mut [u8]) {
rt[0..4].copy_from_slice(b"regi");
rt[8..12].copy_from_slice(&2u32.to_le_bytes());
rt[16..32].copy_from_slice(&BAT_GUID);
rt[32..40].copy_from_slice(&BAT_OFFSET.to_le_bytes());
rt[40..44].copy_from_slice(&BAT_LENGTH.to_le_bytes());
rt[44..48].copy_from_slice(&1u32.to_le_bytes());
rt[48..64].copy_from_slice(&METADATA_GUID);
rt[64..72].copy_from_slice(&META_OFFSET.to_le_bytes());
rt[72..76].copy_from_slice(&META_LENGTH.to_le_bytes());
rt[76..80].copy_from_slice(&1u32.to_le_bytes());
let mut tmp = rt[..65536].to_vec();
tmp[4..8].fill(0);
let c = crc32c(&tmp);
rt[4..8].copy_from_slice(&c.to_le_bytes());
}
fn write_metadata(region: &mut [u8]) {
region[0..8].copy_from_slice(b"metadata");
region[10..12].copy_from_slice(&3u16.to_le_bytes());
const FP_OFF: u32 = 0x200;
const VDS_OFF: u32 = 0x210;
const LSS_OFF: u32 = 0x220;
region[32..48].copy_from_slice(&GUID_FILE_PARAMETERS);
region[48..52].copy_from_slice(&FP_OFF.to_le_bytes());
region[52..56].copy_from_slice(&8u32.to_le_bytes());
region[64..80].copy_from_slice(&GUID_VIRTUAL_DISK_SIZE);
region[80..84].copy_from_slice(&VDS_OFF.to_le_bytes());
region[84..88].copy_from_slice(&8u32.to_le_bytes());
region[96..112].copy_from_slice(&GUID_LOGICAL_SECTOR_SIZE);
region[112..116].copy_from_slice(&LSS_OFF.to_le_bytes());
region[116..120].copy_from_slice(&4u32.to_le_bytes());
region[FP_OFF as usize..FP_OFF as usize + 4].copy_from_slice(&BLOCK_SIZE.to_le_bytes());
region[VDS_OFF as usize..VDS_OFF as usize + 8]
.copy_from_slice(&VIRTUAL_DISK_SIZE.to_le_bytes());
region[LSS_OFF as usize..LSS_OFF as usize + 4].copy_from_slice(&512u32.to_le_bytes());
}
fn write_sparse_vhdx(path: &std::path::Path) -> std::io::Result<u64> {
let front_len = (DATA_OFFSET) as usize;
let mut front = vec![0u8; front_len.min(5 * MB as usize)];
front[0..8].copy_from_slice(b"vhdxfile");
write_header(&mut front[0x10000..0x20000], 1);
write_header(&mut front[0x20000..0x30000], 0);
write_region_table(&mut front[0x30000..0x40000]);
write_region_table(&mut front[0x40000..0x50000]);
write_metadata(
&mut front[META_OFFSET as usize..(META_OFFSET + u64::from(META_LENGTH)) as usize],
);
let bat_entry: u64 = (DATA_OFFSET >> 20) << 20 | 6;
front[BAT_OFFSET as usize..BAT_OFFSET as usize + 8].copy_from_slice(&bat_entry.to_le_bytes());
let mut f = std::fs::File::create(path)?;
f.write_all(&front)?;
f.seek(SeekFrom::Start(DATA_OFFSET))?;
let mut block = vec![0u8; BLOCK_SIZE as usize];
block[0] = 0x42;
f.write_all(&block)?;
let total = DATA_OFFSET + u64::from(BLOCK_SIZE);
f.flush()?;
Ok(total)
}
fn rss_kib() -> u64 {
let pid = std::process::id();
let out = std::process::Command::new("ps")
.args(["-o", "rss=", "-p", &pid.to_string()])
.output();
match out {
Ok(o) => String::from_utf8_lossy(&o.stdout)
.trim()
.parse::<u64>()
.unwrap_or(0),
Err(_) => 0,
}
}
fn main() {
let tmp = std::env::temp_dir().join("vhdx_bounded_memory_demo.vhdx");
let file_len = write_sparse_vhdx(&tmp).expect("write sparse image");
let rss_before = rss_kib();
let mut reader = vhdx::VhdxReader::open(&tmp).expect("bounded open");
let vsize = reader.virtual_disk_size();
let mut buf = vec![0u8; 8 * MB as usize];
reader.seek(SeekFrom::Start(0)).unwrap();
reader.read_exact(&mut buf).unwrap();
assert_eq!(buf[0], 0x42, "present block byte must read back");
let rss_after = rss_kib();
drop(buf);
let _ = std::fs::remove_file(&tmp);
let grew = rss_after.saturating_sub(rss_before);
println!("virtual disk size : {} MiB", vsize / MB);
println!(
"container file size: {} MiB (on disk, sparse)",
file_len / MB
);
println!("RSS before open : {rss_before} KiB");
println!("RSS after open+read: {rss_after} KiB (grew {grew} KiB)");
let file_kib = file_len / 1024;
assert!(
grew < file_kib,
"RSS grew {grew} KiB, must stay below container file size {file_kib} KiB \
(bounded — does not load the whole image)"
);
println!(
"OK: RSS growth {grew} KiB << container {file_kib} KiB << virtual {} KiB — bounded.",
vsize / 1024
);
}