block: vhdx: Flatten internal and worker modules

Remove the internal and worker submodule layers from the VHDX format
directory. The bat, header, io, and metadata parsers move up as
direct children, internal/mod.rs becomes parser.rs, and the sync
backend moves up as engine_sync.rs. The declaration only
worker/mod.rs is dropped.

The public types are surfaced at the vhdx module level, so callers
use block::formats::vhdx instead of reaching into the internal
module.

Assisted-by: Claude:Opus-4.8
Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-07-07 23:14:33 +02:00
committed by Rob Bradford
parent 6e0c39964a
commit 039b4e6013
8 changed files with 56 additions and 64 deletions
@@ -14,17 +14,17 @@ use super::metadata::DiskSpec;
use crate::aligned_file::AlignedFile; use crate::aligned_file::AlignedFile;
// Payload BAT Entry States // Payload BAT Entry States
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0; pub(super) const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
pub const PAYLOAD_BLOCK_UNDEFINED: u64 = 1; pub(super) const PAYLOAD_BLOCK_UNDEFINED: u64 = 1;
pub const PAYLOAD_BLOCK_ZERO: u64 = 2; pub(super) const PAYLOAD_BLOCK_ZERO: u64 = 2;
pub const PAYLOAD_BLOCK_UNMAPPED: u64 = 3; pub(super) const PAYLOAD_BLOCK_UNMAPPED: u64 = 3;
pub const PAYLOAD_BLOCK_FULLY_PRESENT: u64 = 6; pub(super) const PAYLOAD_BLOCK_FULLY_PRESENT: u64 = 6;
pub const PAYLOAD_BLOCK_PARTIALLY_PRESENT: u64 = 7; pub(super) const PAYLOAD_BLOCK_PARTIALLY_PRESENT: u64 = 7;
// Mask for the BAT state // Mask for the BAT state
pub const BAT_STATE_BIT_MASK: u64 = 0x07; pub(super) const BAT_STATE_BIT_MASK: u64 = 0x07;
// Mask for the offset within the file in units of 1 MB // Mask for the offset within the file in units of 1 MB
pub const BAT_FILE_OFF_MASK: u64 = 0xFFFFFFFFFFF00000; pub(super) const BAT_FILE_OFF_MASK: u64 = 0xFFFFFFFFFFF00000;
#[sorted] #[sorted]
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -39,14 +39,14 @@ pub enum VhdxBatError {
WriteBat(#[source] io::Error), WriteBat(#[source] io::Error),
} }
pub type Result<T> = result::Result<T, VhdxBatError>; pub(super) type Result<T> = result::Result<T, VhdxBatError>;
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct BatEntry(pub u64); pub(super) struct BatEntry(pub u64);
impl BatEntry { impl BatEntry {
// Read all BAT entries presented on the disk and insert them to a vector // Read all BAT entries presented on the disk and insert them to a vector
pub fn collect_bat_entries( pub(super) fn collect_bat_entries(
f: &AlignedFile, f: &AlignedFile,
disk_spec: &DiskSpec, disk_spec: &DiskSpec,
bat_entry: &RegionTableEntry, bat_entry: &RegionTableEntry,
@@ -79,7 +79,7 @@ impl BatEntry {
} }
// Routine for writing BAT entries to the disk // Routine for writing BAT entries to the disk
pub fn write_bat_entries( pub(super) fn write_bat_entries(
f: &AlignedFile, f: &AlignedFile,
bat_offset: u64, bat_offset: u64,
bat_entries: &[BatEntry], bat_entries: &[BatEntry],
@@ -11,9 +11,9 @@ use std::sync::{Arc, Mutex};
use vmm_sys_util::eventfd::EventFd; use vmm_sys_util::eventfd::EventFd;
use crate::async_io::{AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult}; use crate::async_io::{AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult};
use crate::formats::vhdx::internal::Vhdx; use crate::formats::vhdx::Vhdx;
pub struct VhdxSync { pub(super) struct VhdxSync {
vhdx_file: Arc<Mutex<Vhdx>>, vhdx_file: Arc<Mutex<Vhdx>>,
eventfd: EventFd, eventfd: EventFd,
completion_list: VecDeque<AsyncIoCompletion>, completion_list: VecDeque<AsyncIoCompletion>,
@@ -21,7 +21,7 @@ pub struct VhdxSync {
} }
impl VhdxSync { impl VhdxSync {
pub fn new(vhdx_file: Arc<Mutex<Vhdx>>, size: u64) -> Self { pub(super) fn new(vhdx_file: Arc<Mutex<Vhdx>>, size: u64) -> Self {
VhdxSync { VhdxSync {
vhdx_file, vhdx_file,
eventfd: EventFd::new(libc::EFD_NONBLOCK) eventfd: EventFd::new(libc::EFD_NONBLOCK)
@@ -120,7 +120,7 @@ mod tests {
use super::*; use super::*;
use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoOperation, OwnedIoBuffer}; use crate::async_io::{AsyncIo, AsyncIoError, AsyncIoOperation, OwnedIoBuffer};
use crate::formats::vhdx::internal::Vhdx; use crate::formats::vhdx::Vhdx;
use crate::formats::vhdx::test_util::create_dynamic_vhdx; use crate::formats::vhdx::test_util::create_dynamic_vhdx;
fn make_vhdx_sync(tf: &TempFile) -> (VhdxSync, u64) { fn make_vhdx_sync(tf: &TempFile) -> (VhdxSync, u64) {
@@ -21,7 +21,7 @@ const REGION_SIGN: u32 = 0x6967_6572; // "regi"
const FILE_START: u64 = 0; // The first element const FILE_START: u64 = 0; // The first element
const HEADER_1_START: u64 = 64 * 1024; // Header 1 start in Bytes const HEADER_1_START: u64 = 64 * 1024; // Header 1 start in Bytes
const HEADER_2_START: u64 = 128 * 1024; // Header 2 start in Bytes const HEADER_2_START: u64 = 128 * 1024; // Header 2 start in Bytes
pub const REGION_TABLE_1_START: u64 = 192 * 1024; // Region 1 start in Bytes pub(super) const REGION_TABLE_1_START: u64 = 192 * 1024; // Region 1 start in Bytes
const REGION_TABLE_2_START: u64 = 256 * 1024; // Region 2 start in Bytes const REGION_TABLE_2_START: u64 = 256 * 1024; // Region 2 start in Bytes
const HEADER_SIZE: u64 = 4 * 1024; // Each header is 64 KiB, but only first 4 kiB contains info const HEADER_SIZE: u64 = 4 * 1024; // Each header is 64 KiB, but only first 4 kiB contains info
@@ -84,16 +84,16 @@ pub enum VhdxHeaderError {
WriteHeader(#[source] io::Error), WriteHeader(#[source] io::Error),
} }
pub type Result<T> = result::Result<T, VhdxHeaderError>; pub(super) type Result<T> = result::Result<T, VhdxHeaderError>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FileTypeIdentifier { pub(super) struct FileTypeIdentifier {
pub _signature: u64, pub _signature: u64,
} }
impl FileTypeIdentifier { impl FileTypeIdentifier {
/// Reads the File Type Identifier structure from a reference VHDx file /// Reads the File Type Identifier structure from a reference VHDx file
pub fn new(f: &AlignedFile) -> Result<FileTypeIdentifier> { pub(super) fn new(f: &AlignedFile) -> Result<FileTypeIdentifier> {
let mut buf = [0u8; size_of::<u64>()]; let mut buf = [0u8; size_of::<u64>()];
f.read_exact_at(&mut buf, FILE_START) f.read_exact_at(&mut buf, FILE_START)
.map_err(VhdxHeaderError::ReadFileTypeIdentifier)?; .map_err(VhdxHeaderError::ReadFileTypeIdentifier)?;
@@ -108,7 +108,7 @@ impl FileTypeIdentifier {
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes)] #[derive(Clone, Copy, Debug, FromBytes, Immutable, IntoBytes)]
pub struct Header { pub(super) struct Header {
pub signature: u32, pub signature: u32,
pub checksum: u32, pub checksum: u32,
pub sequence_number: u64, pub sequence_number: u64,
@@ -123,7 +123,7 @@ pub struct Header {
impl Header { impl Header {
/// Reads the Header structure from a reference VHDx file /// Reads the Header structure from a reference VHDx file
pub fn new(f: &AlignedFile, start: u64) -> Result<Header> { pub(super) fn new(f: &AlignedFile, start: u64) -> Result<Header> {
// Read the whole header into a buffer. We will need it for // Read the whole header into a buffer. We will need it for
// calculating checksum. // calculating checksum.
let mut buffer = [0; HEADER_SIZE as usize]; let mut buffer = [0; HEADER_SIZE as usize];
@@ -199,7 +199,7 @@ struct RegionTableHeader {
impl RegionTableHeader { impl RegionTableHeader {
/// Reads the Region Table Header structure from a reference VHDx file /// Reads the Region Table Header structure from a reference VHDx file
pub fn new(f: &AlignedFile, start: u64) -> Result<RegionTableHeader> { pub(crate) fn new(f: &AlignedFile, start: u64) -> Result<RegionTableHeader> {
// Read the whole header into a buffer. We will need it for calculating // Read the whole header into a buffer. We will need it for calculating
// checksum. // checksum.
let mut buffer = [0u8; REGION_SIZE as usize]; let mut buffer = [0u8; REGION_SIZE as usize];
@@ -234,7 +234,7 @@ fn ranges_overlap(a_start: u64, a_end: u64, b_start: u64, b_end: u64) -> bool {
a_start < b_end && b_start < a_end a_start < b_end && b_start < a_end
} }
pub struct RegionInfo { pub(super) struct RegionInfo {
pub bat_entry: RegionTableEntry, pub bat_entry: RegionTableEntry,
pub mdr_entry: RegionTableEntry, pub mdr_entry: RegionTableEntry,
pub region_entries: BTreeMap<u64, u64>, pub region_entries: BTreeMap<u64, u64>,
@@ -243,7 +243,7 @@ pub struct RegionInfo {
impl RegionInfo { impl RegionInfo {
/// Collect all entries in a BTreeMap from the Region Table and identifies /// Collect all entries in a BTreeMap from the Region Table and identifies
/// BAT and metadata regions /// BAT and metadata regions
pub fn new(f: &AlignedFile, region_start: u64, entry_count: u32) -> Result<RegionInfo> { pub(super) fn new(f: &AlignedFile, region_start: u64, entry_count: u32) -> Result<RegionInfo> {
let mut bat_entry: Option<RegionTableEntry> = None; let mut bat_entry: Option<RegionTableEntry> = None;
let mut mdr_entry: Option<RegionTableEntry> = None; let mut mdr_entry: Option<RegionTableEntry> = None;
@@ -321,7 +321,7 @@ impl RegionInfo {
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Clone, Copy, Debug, FromBytes)] #[derive(Clone, Copy, Debug, FromBytes)]
pub struct RegionTableEntry { pub(super) struct RegionTableEntry {
guid: [u8; 16], guid: [u8; 16],
pub file_offset: u64, pub file_offset: u64,
pub length: u32, pub length: u32,
@@ -335,7 +335,7 @@ enum HeaderNo {
/// Contains the information from the header of a VHDx file /// Contains the information from the header of a VHDx file
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct VhdxHeader { pub(super) struct VhdxHeader {
_file_type_identifier: FileTypeIdentifier, _file_type_identifier: FileTypeIdentifier,
header_1: Header, header_1: Header,
header_2: Header, header_2: Header,
@@ -345,7 +345,7 @@ pub struct VhdxHeader {
impl VhdxHeader { impl VhdxHeader {
/// Creates a VhdxHeader from a reference to a file /// Creates a VhdxHeader from a reference to a file
pub fn new(f: &AlignedFile) -> Result<VhdxHeader> { pub(super) fn new(f: &AlignedFile) -> Result<VhdxHeader> {
Ok(VhdxHeader { Ok(VhdxHeader {
_file_type_identifier: FileTypeIdentifier::new(f)?, _file_type_identifier: FileTypeIdentifier::new(f)?,
header_1: Header::new(f, HEADER_1_START)?, header_1: Header::new(f, HEADER_1_START)?,
@@ -415,14 +415,14 @@ impl VhdxHeader {
VhdxHeader::update_header(f, Ok(header_1), Ok(header_2), guid) VhdxHeader::update_header(f, Ok(header_1), Ok(header_2), guid)
} }
pub fn update(&mut self, f: &AlignedFile) -> Result<()> { pub(super) fn update(&mut self, f: &AlignedFile) -> Result<()> {
let headers = VhdxHeader::update_headers(f, Ok(self.header_1), Ok(self.header_2), 0)?; let headers = VhdxHeader::update_headers(f, Ok(self.header_1), Ok(self.header_2), 0)?;
self.header_1 = headers.0; self.header_1 = headers.0;
self.header_2 = headers.1; self.header_2 = headers.1;
Ok(()) Ok(())
} }
pub fn region_entry_count(&self) -> u32 { pub(super) fn region_entry_count(&self) -> u32 {
self.region_table_1.entry_count self.region_table_1.entry_count
} }
} }
@@ -33,7 +33,7 @@ pub enum VhdxIoError {
WriteBat(#[source] VhdxBatError), WriteBat(#[source] VhdxBatError),
} }
pub type Result<T> = result::Result<T, VhdxIoError>; pub(super) type Result<T> = result::Result<T, VhdxIoError>;
macro_rules! align { macro_rules! align {
($n:expr, $align:expr) => {{ $n.div_ceil($align) * $align }}; ($n:expr, $align:expr) => {{ $n.div_ceil($align) * $align }};
@@ -51,7 +51,7 @@ struct Sector {
impl Sector { impl Sector {
/// Translate sector index and count of data in file to actual offsets and /// Translate sector index and count of data in file to actual offsets and
/// BAT index. /// BAT index.
pub fn new( pub(crate) fn new(
disk_spec: &DiskSpec, disk_spec: &DiskSpec,
bat: &[BatEntry], bat: &[BatEntry],
sector_index: u64, sector_index: u64,
@@ -86,7 +86,7 @@ impl Sector {
/// VHDx IO read routine: requires relative sector index and count for the /// VHDx IO read routine: requires relative sector index and count for the
/// requested data. /// requested data.
pub fn read( pub(super) fn read(
f: &AlignedFile, f: &AlignedFile,
buf: &mut [u8], buf: &mut [u8],
disk_spec: &DiskSpec, disk_spec: &DiskSpec,
@@ -138,7 +138,7 @@ pub fn read(
/// VHDx IO write routine: requires relative sector index and count for the /// VHDx IO write routine: requires relative sector index and count for the
/// requested data. /// requested data.
pub fn write( pub(super) fn write(
f: &AlignedFile, f: &AlignedFile,
buf: &[u8], buf: &[u8],
disk_spec: &mut DiskSpec, disk_spec: &mut DiskSpec,
@@ -21,7 +21,7 @@ const METADATA_TABLE_MAX_SIZE: usize = METADATA_ENTRY_SIZE * (METADATA_MAX_ENTRI
const METADATA_FLAGS_IS_REQUIRED: u32 = 0x04; const METADATA_FLAGS_IS_REQUIRED: u32 = 0x04;
pub const BLOCK_SIZE_MIN: u32 = 1 << 20; // 1 MiB pub(super) const BLOCK_SIZE_MIN: u32 = 1 << 20; // 1 MiB
const BLOCK_SIZE_MAX: u32 = 256 << 20; // 256 MiB const BLOCK_SIZE_MAX: u32 = 256 << 20; // 256 MiB
const MAX_SECTORS_PER_BLOCK: u64 = 1 << 23; const MAX_SECTORS_PER_BLOCK: u64 = 1 << 23;
@@ -93,10 +93,10 @@ pub enum VhdxMetadataError {
UnsupportedFlag, UnsupportedFlag,
} }
pub type Result<T> = result::Result<T, VhdxMetadataError>; pub(super) type Result<T> = result::Result<T, VhdxMetadataError>;
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct DiskSpec { pub(super) struct DiskSpec {
pub disk_id: u128, pub disk_id: u128,
pub image_size: u64, pub image_size: u64,
pub block_size: u32, pub block_size: u32,
@@ -112,7 +112,7 @@ pub struct DiskSpec {
impl DiskSpec { impl DiskSpec {
/// Parse all metadata from the provided file and store info in DiskSpec /// Parse all metadata from the provided file and store info in DiskSpec
/// structure. /// structure.
pub fn new(f: &AlignedFile, metadata_region: &RegionTableEntry) -> Result<DiskSpec> { pub(super) fn new(f: &AlignedFile, metadata_region: &RegionTableEntry) -> Result<DiskSpec> {
let mut disk_spec = DiskSpec::default(); let mut disk_spec = DiskSpec::default();
let mut metadata_presence: u16 = 0; let mut metadata_presence: u16 = 0;
let mut offset = 0; let mut offset = 0;
@@ -270,7 +270,7 @@ struct MetadataTableHeader {
} }
impl MetadataTableHeader { impl MetadataTableHeader {
pub fn new(buffer: &[u8]) -> Result<MetadataTableHeader> { pub(crate) fn new(buffer: &[u8]) -> Result<MetadataTableHeader> {
let metadata_table_header = MetadataTableHeader::read_from_bytes(buffer).unwrap(); let metadata_table_header = MetadataTableHeader::read_from_bytes(buffer).unwrap();
if metadata_table_header.signature != METADATA_SIGN { if metadata_table_header.signature != METADATA_SIGN {
@@ -291,7 +291,7 @@ impl MetadataTableHeader {
#[repr(C, packed)] #[repr(C, packed)]
#[derive(Default, Debug, Clone, Copy, FromBytes)] #[derive(Default, Debug, Clone, Copy, FromBytes)]
pub struct MetadataTableEntry { pub(super) struct MetadataTableEntry {
item_id: [u8; 16], item_id: [u8; 16],
offset: u32, offset: u32,
length: u32, length: u32,
+11 -7
View File
@@ -9,19 +9,23 @@
//! Provides [`VhdxDisk`], the `DiskFile` wrapper for dynamic VHDX //! Provides [`VhdxDisk`], the `DiskFile` wrapper for dynamic VHDX
//! images. //! images.
pub mod internal; mod bat;
mod engine_sync;
mod header;
mod io;
mod metadata;
mod parser;
#[cfg(test)] #[cfg(test)]
pub(crate) mod test_util; mod test_util;
pub(crate) mod worker;
use std::fs::File; use std::fs::File;
use std::io; use std::io::Error as IoError;
use std::os::fd::AsRawFd; use std::os::fd::AsRawFd;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
pub use internal::{Vhdx, VhdxError}; pub use parser::{Vhdx, VhdxError};
use self::worker::sync::VhdxSync; use self::engine_sync::VhdxSync;
use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError}; use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError};
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp}; use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
use crate::{Error, disk_file}; use crate::{Error, disk_file};
@@ -92,7 +96,7 @@ impl disk_file::Resizable for VhdxDisk {
fn resize(&mut self, _size: u64) -> BlockResult<()> { fn resize(&mut self, _size: u64) -> BlockResult<()> {
Err(BlockError::new( Err(BlockError::new(
BlockErrorKind::UnsupportedFeature, BlockErrorKind::UnsupportedFeature,
DiskFileError::ResizeError(io::Error::other("resize not supported for VHDX")), DiskFileError::ResizeError(IoError::other("resize not supported for VHDX")),
) )
.with_op(ErrorOp::Resize)) .with_op(ErrorOp::Resize))
} }
@@ -13,17 +13,12 @@ use std::result;
use remain::sorted; use remain::sorted;
use thiserror::Error; use thiserror::Error;
use self::bat::{BatEntry, VhdxBatError}; use super::bat::{BatEntry, VhdxBatError};
use self::header::{RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError}; use super::header::{self, RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError};
use self::io::VhdxIoError; use super::io::{self, VhdxIoError};
use self::metadata::{DiskSpec, VhdxMetadataError}; use super::metadata::{DiskSpec, VhdxMetadataError};
use crate::aligned_file::AlignedFile; use crate::aligned_file::AlignedFile;
mod bat;
mod header;
mod io;
mod metadata;
#[sorted] #[sorted]
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum VhdxError { pub enum VhdxError {
@@ -43,7 +38,7 @@ pub enum VhdxError {
WriteFailed(#[source] VhdxIoError), WriteFailed(#[source] VhdxIoError),
} }
pub type Result<T> = result::Result<T, VhdxError>; pub(super) type Result<T> = result::Result<T, VhdxError>;
#[derive(Debug)] #[derive(Debug)]
pub struct Vhdx { pub struct Vhdx {
-7
View File
@@ -1,7 +0,0 @@
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0
//! Sync I/O worker for VHDX images.
pub(crate) mod sync;