mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2025-01-20 19:45:32 +00:00
452af9b17c
Microsoft’s VHDx block device format specification is implemented here as a crate. This commit includes the implementation for the fixed and dynamic formats, where the other format is known as differencing. The vhdx_header.rs, vhdx_bat.rs, vhdx_metadata.rs implements parser and manipulators for the VHDx header, Block Allocation Table, and metadata, respectively, for the VHDx file. The vhdx_io.rs implements read and write routines for the VHDx file. The vhdx.rs implements the Vhdx structure, which provides the wrapper functions for standard I/O operations like read, write, and seek. Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com> Signed-off-by: Fazla Mehrab <akm.fazla.mehrab@intel.com>
105 lines
3.3 KiB
Rust
105 lines
3.3 KiB
Rust
// Copyright © 2021 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
use crate::vhdx_header::RegionTableEntry;
|
|
use crate::vhdx_metadata::DiskSpec;
|
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
use remain::sorted;
|
|
use std::fs::File;
|
|
use std::io::{self, Seek, SeekFrom};
|
|
use std::mem::size_of;
|
|
use thiserror::Error;
|
|
|
|
// Payload BAT Entry States
|
|
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
|
|
pub const PAYLOAD_BLOCK_UNDEFINED: u64 = 1;
|
|
pub const PAYLOAD_BLOCK_ZERO: u64 = 2;
|
|
pub const PAYLOAD_BLOCK_UNMAPPED: u64 = 3;
|
|
pub const PAYLOAD_BLOCK_FULLY_PRESENT: u64 = 6;
|
|
pub const PAYLOAD_BLOCK_PARTIALLY_PRESENT: u64 = 7;
|
|
|
|
// Mask for the BAT state
|
|
pub const BAT_STATE_BIT_MASK: u64 = 0x07;
|
|
// Mask for the offset within the file in units of 1 MB
|
|
pub const BAT_FILE_OFF_MASK: u64 = 0xFFFFFFFFFFF00000;
|
|
|
|
#[sorted]
|
|
#[derive(Error, Debug)]
|
|
pub enum VhdxBatError {
|
|
#[error("Invalid BAT entry")]
|
|
InvalidBatEntry,
|
|
#[error("Invalid BAT entry count")]
|
|
InvalidEntryCount,
|
|
#[error("Failed to read BAT entry {0}")]
|
|
ReadBat(#[source] io::Error),
|
|
#[error("Failed to write BAT entry {0}")]
|
|
WriteBat(#[source] io::Error),
|
|
}
|
|
|
|
pub type Result<T> = std::result::Result<T, VhdxBatError>;
|
|
|
|
#[derive(Default, Clone, Debug)]
|
|
pub struct BatEntry(pub u64);
|
|
|
|
impl BatEntry {
|
|
// Read all BAT entries presented on the disk and insert them to a vector
|
|
pub fn collect_bat_entries(
|
|
f: &mut File,
|
|
disk_spec: &DiskSpec,
|
|
bat_entry: &RegionTableEntry,
|
|
) -> Result<Vec<BatEntry>> {
|
|
let entry_count = BatEntry::calculate_entries(
|
|
disk_spec.block_size,
|
|
disk_spec.virtual_disk_size,
|
|
disk_spec.chunk_ratio,
|
|
);
|
|
if entry_count as usize > (bat_entry.length as usize / size_of::<BatEntry>()) {
|
|
return Err(VhdxBatError::InvalidEntryCount);
|
|
}
|
|
|
|
let mut bat: Vec<BatEntry> = Vec::with_capacity(bat_entry.length as usize);
|
|
let offset = bat_entry.file_offset;
|
|
for i in 0..entry_count {
|
|
f.seek(SeekFrom::Start(offset + i * size_of::<u64>() as u64))
|
|
.map_err(VhdxBatError::ReadBat)?;
|
|
|
|
let bat_entry = BatEntry(
|
|
f.read_u64::<LittleEndian>()
|
|
.map_err(VhdxBatError::ReadBat)?,
|
|
);
|
|
bat.insert(i as usize, bat_entry);
|
|
}
|
|
|
|
Ok(bat)
|
|
}
|
|
|
|
// Calculate the number of entries in the BAT
|
|
fn calculate_entries(block_size: u32, virtual_disk_size: u64, chunk_ratio: u64) -> u64 {
|
|
let data_blocks_count = div_round_up!(virtual_disk_size, block_size as u64);
|
|
data_blocks_count + (data_blocks_count - 1) / chunk_ratio
|
|
}
|
|
|
|
// Routine for writing BAT entries to the disk
|
|
pub fn write_bat_entries(
|
|
f: &mut File,
|
|
bat_offset: u64,
|
|
bat_entries: &[BatEntry],
|
|
) -> Result<()> {
|
|
for i in 0..bat_entries.len() as u64 {
|
|
f.seek(SeekFrom::Start(bat_offset + i * size_of::<u64>() as u64))
|
|
.map_err(VhdxBatError::WriteBat)?;
|
|
let bat_entry = match bat_entries.get(i as usize) {
|
|
Some(entry) => entry.0,
|
|
None => {
|
|
return Err(VhdxBatError::InvalidBatEntry);
|
|
}
|
|
};
|
|
|
|
f.write_u64::<LittleEndian>(bat_entry)
|
|
.map_err(VhdxBatError::WriteBat)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|