use crate::backing::Backing;
use crate::bytes::{le_arr16, le_u32, le_u64};
use crate::error::{Result, VhdxError};
use crate::header::{crc32c, parse_active_header, VhdxHeader};
pub(crate) fn apply(data: &mut [u8]) -> Result<()> {
let header = parse_active_header(data)?;
if header.log_length == 0 || header.log_guid == [0u8; 16] {
return Ok(());
}
let log_start = header.log_offset as usize;
let log_end = log_start.saturating_add(header.log_length as usize);
if data.len() < log_end {
return Err(VhdxError::OffsetOutOfBounds);
}
let entries = collect_entries(&data[log_start..log_end], &header.log_guid);
for entry in entries {
apply_entry(data, &entry);
}
Ok(())
}
#[derive(Debug, Default, Clone)]
pub(crate) struct LogOverlay {
runs: Vec<(u64, Vec<u8>)>,
}
impl LogOverlay {
fn push(&mut self, file_offset: u64, bytes: Vec<u8>) {
if bytes.is_empty() {
return;
}
self.runs.push((file_offset, bytes));
}
pub(crate) fn patch(&self, buf: &mut [u8], read_off: u64) {
if self.runs.is_empty() || buf.is_empty() {
return;
}
let read_end = read_off.saturating_add(buf.len() as u64);
for (run_start, run) in &self.runs {
let run_start = *run_start;
let run_end = run_start.saturating_add(run.len() as u64);
let ov_start = run_start.max(read_off);
let ov_end = run_end.min(read_end);
if ov_start >= ov_end {
continue;
}
let dst = (ov_start - read_off) as usize;
let src = (ov_start - run_start) as usize;
let n = (ov_end - ov_start) as usize;
buf[dst..dst + n].copy_from_slice(&run[src..src + n]);
}
}
}
pub(crate) fn build_overlay(backing: &Backing, header: &VhdxHeader) -> Result<LogOverlay> {
if header.log_length == 0 || header.log_guid == [0u8; 16] {
return Ok(LogOverlay::default());
}
let log_start = header.log_offset;
let log_end = log_start.saturating_add(u64::from(header.log_length));
if backing.len() < log_end {
return Err(VhdxError::OffsetOutOfBounds);
}
let log = backing.read_exact_at(log_start, header.log_length as usize)?;
if log.len() < header.log_length as usize {
return Err(VhdxError::OffsetOutOfBounds);
}
let entries = collect_entries(&log, &header.log_guid);
let mut overlay = LogOverlay::default();
for entry in entries {
overlay_entry(&mut overlay, &entry);
}
Ok(overlay)
}
fn collect_entries(log: &[u8], expected_guid: &[u8; 16]) -> Vec<Vec<u8>> {
let mut entries: Vec<(u64, Vec<u8>)> = Vec::new();
let mut pos = 0usize;
loop {
if pos + 64 > log.len() {
break;
}
if &log[pos..pos + 4] != b"loge" {
break;
}
let entry_len = le_u32(log, pos + 8) as usize;
if entry_len < 64 || pos + entry_len > log.len() {
break;
}
let entry_bytes = log[pos..pos + entry_len].to_vec();
let stored = le_u32(&entry_bytes, 4);
let mut tmp = entry_bytes.clone();
tmp[4..8].fill(0);
if crc32c(&tmp) != stored {
pos += entry_len;
continue;
}
let entry_guid: [u8; 16] = le_arr16(&entry_bytes, 32);
if &entry_guid != expected_guid {
pos += entry_len;
continue;
}
let seq = le_u64(&entry_bytes, 16);
entries.push((seq, entry_bytes));
pos += entry_len;
}
entries.sort_by_key(|(seq, _)| *seq);
entries.into_iter().map(|(_, e)| e).collect()
}
fn apply_entry(data: &mut [u8], entry: &[u8]) {
let descriptor_count = le_u32(entry, 24) as usize;
let desc_start = 64;
let data_sector_base = desc_start + descriptor_count * 32;
let mut sector_idx = 0usize;
for i in 0..descriptor_count {
let d = &entry[desc_start + i * 32..desc_start + (i + 1) * 32];
match &d[0..4] {
b"desc" => {
let file_off = le_u64(d, 24) as usize;
let sector_off = data_sector_base + sector_idx * 4096;
if sector_off + 4096 <= entry.len() && file_off + 4096 <= data.len() {
data[file_off..file_off + 4096]
.copy_from_slice(&entry[sector_off..sector_off + 4096]);
}
sector_idx += 1;
}
b"zero" => {
let zero_len = le_u64(d, 8) as usize;
let file_off = le_u64(d, 16) as usize;
let end = file_off.saturating_add(zero_len).min(data.len());
if file_off < data.len() {
data[file_off..end].fill(0);
}
}
_ => {}
}
}
}
fn overlay_entry(overlay: &mut LogOverlay, entry: &[u8]) {
let descriptor_count = le_u32(entry, 24) as usize;
let desc_start = 64;
let data_sector_base = desc_start + descriptor_count * 32;
let mut sector_idx = 0usize;
for i in 0..descriptor_count {
let d = &entry[desc_start + i * 32..desc_start + (i + 1) * 32];
match &d[0..4] {
b"desc" => {
let file_off = le_u64(d, 24);
let sector_off = data_sector_base + sector_idx * 4096;
if sector_off + 4096 <= entry.len() {
overlay.push(file_off, entry[sector_off..sector_off + 4096].to_vec());
}
sector_idx += 1;
}
b"zero" => {
let zero_len = le_u64(d, 8) as usize;
let file_off = le_u64(d, 16);
if zero_len > 0 {
overlay.push(file_off, vec![0u8; zero_len]);
}
}
_ => {}
}
}
}