mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: Move VHDX format files into formats/vhdx/
Move VHDX format implementation into a structured directory layout: vhdx/mod.rs -> formats/vhdx/internal/mod.rs (Vhdx) vhdx/vhdx_bat.rs -> formats/vhdx/internal/bat.rs vhdx/vhdx_header.rs -> formats/vhdx/internal/header.rs vhdx/vhdx_io.rs -> formats/vhdx/internal/io.rs vhdx/vhdx_metadata.rs -> formats/vhdx/internal/metadata.rs vhdx_sync.rs -> formats/vhdx/mod.rs (VhdxDisk) Extract VhdxSync from vhdx_sync.rs into formats/vhdx/worker/sync.rs. Drop the vhdx_ prefix from internal file names since the parent directory already provides the namespace. Update all internal cross references to use the new module paths. Re-export formats::vhdx as vhdx_sync in lib.rs for backward compatibility. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
1ca0c39b4d
commit
b595f1dbc3
@@ -22,8 +22,8 @@ use crate::disk_file::AsyncFullDiskFile;
|
|||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult};
|
use crate::error::{BlockError, BlockErrorKind, BlockResult};
|
||||||
use crate::formats::raw::{RawBackend, RawDisk};
|
use crate::formats::raw::{RawBackend, RawDisk};
|
||||||
use crate::formats::vhd::VhdDisk;
|
use crate::formats::vhd::VhdDisk;
|
||||||
|
use crate::formats::vhdx::VhdxDisk;
|
||||||
use crate::qcow_disk::QcowDisk;
|
use crate::qcow_disk::QcowDisk;
|
||||||
use crate::vhdx_sync::VhdxDisk;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ImageType, block_aio_is_supported, detect_image_type, open_disk_image, preallocate_disk,
|
ImageType, block_aio_is_supported, detect_image_type, open_disk_image, preallocate_disk,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,3 +9,4 @@
|
|||||||
|
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
pub mod vhd;
|
pub mod vhd;
|
||||||
|
pub mod vhdx;
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|||||||
use remain::sorted;
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::vhdx::vhdx_header::RegionTableEntry;
|
use super::header::RegionTableEntry;
|
||||||
use crate::vhdx::vhdx_metadata::DiskSpec;
|
use super::metadata::DiskSpec;
|
||||||
|
|
||||||
// Payload BAT Entry States
|
// Payload BAT Entry States
|
||||||
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
|
pub const PAYLOAD_BLOCK_NOT_PRESENT: u64 = 0;
|
||||||
@@ -341,7 +341,7 @@ impl RegionTableEntry {
|
|||||||
// SAFETY: the assertion above makes sure the buffer size is correct.
|
// SAFETY: the assertion above makes sure the buffer size is correct.
|
||||||
let mut region_table_entry: RegionTableEntry = unsafe { *(buffer.as_ptr().cast()) };
|
let mut region_table_entry: RegionTableEntry = unsafe { *(buffer.as_ptr().cast()) };
|
||||||
|
|
||||||
let uuid = crate::vhdx::uuid_from_guid(buffer);
|
let uuid = super::uuid_from_guid(buffer);
|
||||||
region_table_entry.guid = uuid;
|
region_table_entry.guid = uuid;
|
||||||
|
|
||||||
Ok(region_table_entry)
|
Ok(region_table_entry)
|
||||||
@@ -8,8 +8,8 @@ use std::io::{self, Read, Seek, SeekFrom, Write};
|
|||||||
use remain::sorted;
|
use remain::sorted;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::vhdx::vhdx_bat::{self, BatEntry, VhdxBatError};
|
use super::bat::{self, BatEntry, VhdxBatError};
|
||||||
use crate::vhdx::vhdx_metadata::{self, DiskSpec};
|
use super::metadata::{self, DiskSpec};
|
||||||
|
|
||||||
const SECTOR_SIZE: u64 = 512;
|
const SECTOR_SIZE: u64 = 512;
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ impl Sector {
|
|||||||
return Err(VhdxIoError::InvalidBatIndex);
|
return Err(VhdxIoError::InvalidBatIndex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
sector.file_offset = bat_entry & vhdx_bat::BAT_FILE_OFF_MASK;
|
sector.file_offset = bat_entry & bat::BAT_FILE_OFF_MASK;
|
||||||
if sector.file_offset != 0 {
|
if sector.file_offset != 0 {
|
||||||
sector.file_offset += sector.block_offset;
|
sector.file_offset += sector.block_offset;
|
||||||
}
|
}
|
||||||
@@ -108,12 +108,12 @@ pub fn read(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
match bat_entry & bat::BAT_STATE_BIT_MASK {
|
||||||
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
bat::PAYLOAD_BLOCK_NOT_PRESENT
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
| bat::PAYLOAD_BLOCK_UNDEFINED
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
| bat::PAYLOAD_BLOCK_UNMAPPED
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {}
|
| bat::PAYLOAD_BLOCK_ZERO => {}
|
||||||
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
||||||
f.seek(SeekFrom::Start(sector.file_offset))
|
f.seek(SeekFrom::Start(sector.file_offset))
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
f.read_exact(
|
f.read_exact(
|
||||||
@@ -122,7 +122,7 @@ pub fn read(
|
|||||||
)
|
)
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
}
|
}
|
||||||
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -162,13 +162,12 @@ pub fn write(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match bat_entry & vhdx_bat::BAT_STATE_BIT_MASK {
|
match bat_entry & bat::BAT_STATE_BIT_MASK {
|
||||||
vhdx_bat::PAYLOAD_BLOCK_NOT_PRESENT
|
bat::PAYLOAD_BLOCK_NOT_PRESENT
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNDEFINED
|
| bat::PAYLOAD_BLOCK_UNDEFINED
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_UNMAPPED
|
| bat::PAYLOAD_BLOCK_UNMAPPED
|
||||||
| vhdx_bat::PAYLOAD_BLOCK_ZERO => {
|
| bat::PAYLOAD_BLOCK_ZERO => {
|
||||||
let file_offset =
|
let file_offset = align!(disk_spec.image_size, metadata::BLOCK_SIZE_MIN as u64);
|
||||||
align!(disk_spec.image_size, vhdx_metadata::BLOCK_SIZE_MIN as u64);
|
|
||||||
let new_size = file_offset
|
let new_size = file_offset
|
||||||
.checked_add(disk_spec.block_size as u64)
|
.checked_add(disk_spec.block_size as u64)
|
||||||
.ok_or(VhdxIoError::InvalidDiskSize)?;
|
.ok_or(VhdxIoError::InvalidDiskSize)?;
|
||||||
@@ -176,12 +175,12 @@ pub fn write(
|
|||||||
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
|
f.set_len(new_size).map_err(VhdxIoError::ResizeFile)?;
|
||||||
disk_spec.image_size = new_size;
|
disk_spec.image_size = new_size;
|
||||||
|
|
||||||
let new_bat_entry = file_offset
|
let new_bat_entry =
|
||||||
| (vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT & vhdx_bat::BAT_STATE_BIT_MASK);
|
file_offset | (bat::PAYLOAD_BLOCK_FULLY_PRESENT & bat::BAT_STATE_BIT_MASK);
|
||||||
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
|
bat[sector.bat_index as usize] = BatEntry(new_bat_entry);
|
||||||
BatEntry::write_bat_entries(f, bat_offset, bat).map_err(VhdxIoError::WriteBat)?;
|
BatEntry::write_bat_entries(f, bat_offset, bat).map_err(VhdxIoError::WriteBat)?;
|
||||||
|
|
||||||
if file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
if file_offset < metadata::BLOCK_SIZE_MIN as u64 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,8 +191,8 @@ pub fn write(
|
|||||||
)
|
)
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
}
|
}
|
||||||
vhdx_bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
bat::PAYLOAD_BLOCK_FULLY_PRESENT => {
|
||||||
if sector.file_offset < vhdx_metadata::BLOCK_SIZE_MIN as u64 {
|
if sector.file_offset < metadata::BLOCK_SIZE_MIN as u64 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +203,7 @@ pub fn write(
|
|||||||
)
|
)
|
||||||
.map_err(VhdxIoError::ReadSectorBlock)?;
|
.map_err(VhdxIoError::ReadSectorBlock)?;
|
||||||
}
|
}
|
||||||
vhdx_bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
bat::PAYLOAD_BLOCK_PARTIALLY_PRESENT => {
|
||||||
return Err(VhdxIoError::UnsupportedMode);
|
return Err(VhdxIoError::UnsupportedMode);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@@ -11,7 +11,7 @@ use remain::sorted;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::vhdx::vhdx_header::RegionTableEntry;
|
use super::header::RegionTableEntry;
|
||||||
|
|
||||||
const METADATA_SIGN: u64 = 0x6174_6164_6174_656D;
|
const METADATA_SIGN: u64 = 0x6174_6164_6174_656D;
|
||||||
const METADATA_ENTRY_SIZE: usize = 32;
|
const METADATA_ENTRY_SIZE: usize = 32;
|
||||||
@@ -315,7 +315,7 @@ impl MetadataTableEntry {
|
|||||||
// SAFETY: the assertion above makes sure the buffer size is correct.
|
// SAFETY: the assertion above makes sure the buffer size is correct.
|
||||||
let mut metadata_table_entry: MetadataTableEntry = unsafe { *(buffer.as_ptr().cast()) };
|
let mut metadata_table_entry: MetadataTableEntry = unsafe { *(buffer.as_ptr().cast()) };
|
||||||
|
|
||||||
let uuid = crate::vhdx::uuid_from_guid(buffer);
|
let uuid = super::uuid_from_guid(buffer);
|
||||||
metadata_table_entry.item_id = uuid;
|
metadata_table_entry.item_id = uuid;
|
||||||
|
|
||||||
if metadata_table_entry.length > METADATA_LENGTH_MAX {
|
if metadata_table_entry.length > METADATA_LENGTH_MAX {
|
||||||
@@ -12,16 +12,16 @@ use remain::sorted;
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use self::bat::{BatEntry, VhdxBatError};
|
||||||
|
use self::header::{RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError};
|
||||||
|
use self::io::VhdxIoError;
|
||||||
|
use self::metadata::{DiskSpec, VhdxMetadataError};
|
||||||
use crate::BlockBackend;
|
use crate::BlockBackend;
|
||||||
use crate::vhdx::vhdx_bat::{BatEntry, VhdxBatError};
|
|
||||||
use crate::vhdx::vhdx_header::{RegionInfo, RegionTableEntry, VhdxHeader, VhdxHeaderError};
|
|
||||||
use crate::vhdx::vhdx_io::VhdxIoError;
|
|
||||||
use crate::vhdx::vhdx_metadata::{DiskSpec, VhdxMetadataError};
|
|
||||||
|
|
||||||
mod vhdx_bat;
|
mod bat;
|
||||||
mod vhdx_header;
|
mod header;
|
||||||
mod vhdx_io;
|
mod io;
|
||||||
mod vhdx_metadata;
|
mod metadata;
|
||||||
|
|
||||||
#[sorted]
|
#[sorted]
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@@ -65,7 +65,7 @@ impl Vhdx {
|
|||||||
|
|
||||||
let collected_entries = RegionInfo::new(
|
let collected_entries = RegionInfo::new(
|
||||||
&mut file,
|
&mut file,
|
||||||
vhdx_header::REGION_TABLE_1_START,
|
header::REGION_TABLE_1_START,
|
||||||
vhdx_header.region_entry_count(),
|
vhdx_header.region_entry_count(),
|
||||||
)
|
)
|
||||||
.map_err(VhdxError::ParseVhdxRegionEntry)?;
|
.map_err(VhdxError::ParseVhdxRegionEntry)?;
|
||||||
@@ -103,7 +103,7 @@ impl Read for Vhdx {
|
|||||||
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
let sector_count = (buf.len() as u64).div_ceil(self.disk_spec.logical_sector_size as u64);
|
||||||
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
let sector_index = self.current_offset / self.disk_spec.logical_sector_size as u64;
|
||||||
|
|
||||||
let result = vhdx_io::read(
|
let result = io::read(
|
||||||
&mut self.file,
|
&mut self.file,
|
||||||
buf,
|
buf,
|
||||||
&self.disk_spec,
|
&self.disk_spec,
|
||||||
@@ -141,7 +141,7 @@ impl Write for Vhdx {
|
|||||||
.map_err(|e| std::io::Error::other(format!("Failed to update VHDx header: {e}")))?;
|
.map_err(|e| std::io::Error::other(format!("Failed to update VHDx header: {e}")))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = vhdx_io::write(
|
let result = io::write(
|
||||||
&mut self.file,
|
&mut self.file,
|
||||||
buf,
|
buf,
|
||||||
&mut self.disk_spec,
|
&mut self.disk_spec,
|
||||||
111
block/src/formats/vhdx/mod.rs
Normal file
111
block/src/formats/vhdx/mod.rs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// Copyright © 2021 Intel Corporation
|
||||||
|
//
|
||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
//! VHDX disk format support.
|
||||||
|
//!
|
||||||
|
//! Provides [`VhdxDisk`], the `DiskFile` wrapper for dynamic VHDX
|
||||||
|
//! images.
|
||||||
|
|
||||||
|
pub mod internal;
|
||||||
|
pub(crate) mod worker;
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::os::fd::AsRawFd;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
pub use internal::VhdxError;
|
||||||
|
|
||||||
|
use self::internal::Vhdx;
|
||||||
|
use self::worker::sync::VhdxSync;
|
||||||
|
use crate::async_io::{AsyncIo, BorrowedDiskFd, DiskFileError};
|
||||||
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||||
|
use crate::{BlockBackend, Error, disk_file};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct VhdxDisk {
|
||||||
|
// FIXME: The Mutex serializes all VHDX I/O operations across queues, which
|
||||||
|
// is necessary for correctness but eliminates any parallelism benefit from
|
||||||
|
// multiqueue. Vhdx::clone() shares the underlying file description across
|
||||||
|
// threads, so concurrent I/O from multiple queues races on the file offset
|
||||||
|
// causing data corruption.
|
||||||
|
//
|
||||||
|
// A proper fix would require restructuring the VHDX I/O path so that data
|
||||||
|
// operations can proceed in parallel with independent file descriptors.
|
||||||
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VhdxDisk {
|
||||||
|
pub fn new(f: File) -> BlockResult<Self> {
|
||||||
|
Ok(VhdxDisk {
|
||||||
|
vhdx_file: Arc::new(Mutex::new(Vhdx::new(f).map_err(|e| {
|
||||||
|
let kind = match &e {
|
||||||
|
VhdxError::NotVhdx(_)
|
||||||
|
| VhdxError::ParseVhdxHeader(_)
|
||||||
|
| VhdxError::ParseVhdxMetadata(_)
|
||||||
|
| VhdxError::ParseVhdxRegionEntry(_) => BlockErrorKind::InvalidFormat,
|
||||||
|
VhdxError::ReadBatEntry(_) => BlockErrorKind::CorruptImage,
|
||||||
|
VhdxError::ReadFailed(_) | VhdxError::WriteFailed(_) => BlockErrorKind::Io,
|
||||||
|
};
|
||||||
|
BlockError::new(kind, e).with_op(ErrorOp::Open)
|
||||||
|
})?)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl disk_file::DiskSize for VhdxDisk {
|
||||||
|
fn logical_size(&self) -> BlockResult<u64> {
|
||||||
|
Ok(self.vhdx_file.lock().unwrap().virtual_disk_size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl disk_file::PhysicalSize for VhdxDisk {
|
||||||
|
fn physical_size(&self) -> BlockResult<u64> {
|
||||||
|
self.vhdx_file
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.physical_size()
|
||||||
|
.map_err(|e| match e {
|
||||||
|
Error::GetFileMetadata(io) => {
|
||||||
|
BlockError::new(BlockErrorKind::Io, Error::GetFileMetadata(io))
|
||||||
|
}
|
||||||
|
_ => unreachable!("unexpected error from Vhdx::physical_size(): {e}"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl disk_file::DiskFd for VhdxDisk {
|
||||||
|
fn fd(&self) -> BorrowedDiskFd<'_> {
|
||||||
|
BorrowedDiskFd::new(self.vhdx_file.lock().unwrap().as_raw_fd())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl disk_file::Geometry for VhdxDisk {}
|
||||||
|
|
||||||
|
impl disk_file::SparseCapable for VhdxDisk {}
|
||||||
|
|
||||||
|
impl disk_file::Resizable for VhdxDisk {
|
||||||
|
fn resize(&mut self, _size: u64) -> BlockResult<()> {
|
||||||
|
Err(BlockError::new(
|
||||||
|
BlockErrorKind::UnsupportedFeature,
|
||||||
|
DiskFileError::ResizeError(std::io::Error::other("resize not supported for VHDX")),
|
||||||
|
)
|
||||||
|
.with_op(ErrorOp::Resize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl disk_file::DiskFile for VhdxDisk {}
|
||||||
|
|
||||||
|
impl disk_file::AsyncDiskFile for VhdxDisk {
|
||||||
|
fn try_clone(&self) -> BlockResult<Box<dyn disk_file::AsyncDiskFile>> {
|
||||||
|
Ok(Box::new(VhdxDisk {
|
||||||
|
vhdx_file: Arc::clone(&self.vhdx_file),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_async_io(&self, _ring_depth: u32) -> BlockResult<Box<dyn AsyncIo>> {
|
||||||
|
Ok(Box::new(VhdxSync::new(Arc::clone(&self.vhdx_file))))
|
||||||
|
}
|
||||||
|
}
|
||||||
7
block/src/formats/vhdx/worker/mod.rs
Normal file
7
block/src/formats/vhdx/worker/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// 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;
|
||||||
@@ -5,106 +5,13 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Read, Seek, SeekFrom, Write};
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
use std::os::fd::AsRawFd;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use vmm_sys_util::eventfd::EventFd;
|
use vmm_sys_util::eventfd::EventFd;
|
||||||
|
|
||||||
use crate::async_io::{
|
use crate::async_io::{AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult};
|
||||||
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, BorrowedDiskFd,
|
use crate::formats::vhdx::internal::Vhdx;
|
||||||
DiskFileError,
|
|
||||||
};
|
|
||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
|
||||||
use crate::vhdx::{Vhdx, VhdxError};
|
|
||||||
use crate::{BlockBackend, Error, disk_file};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct VhdxDisk {
|
|
||||||
// FIXME: The Mutex serializes all VHDX I/O operations across queues, which
|
|
||||||
// is necessary for correctness but eliminates any parallelism benefit from
|
|
||||||
// multiqueue. Vhdx::clone() shares the underlying file description across
|
|
||||||
// threads, so concurrent I/O from multiple queues races on the file offset
|
|
||||||
// causing data corruption.
|
|
||||||
//
|
|
||||||
// A proper fix would require restructuring the VHDX I/O path so that data
|
|
||||||
// operations can proceed in parallel with independent file descriptors.
|
|
||||||
vhdx_file: Arc<Mutex<Vhdx>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VhdxDisk {
|
|
||||||
pub fn new(f: File) -> BlockResult<Self> {
|
|
||||||
Ok(VhdxDisk {
|
|
||||||
vhdx_file: Arc::new(Mutex::new(Vhdx::new(f).map_err(|e| {
|
|
||||||
let kind = match &e {
|
|
||||||
VhdxError::NotVhdx(_)
|
|
||||||
| VhdxError::ParseVhdxHeader(_)
|
|
||||||
| VhdxError::ParseVhdxMetadata(_)
|
|
||||||
| VhdxError::ParseVhdxRegionEntry(_) => BlockErrorKind::InvalidFormat,
|
|
||||||
VhdxError::ReadBatEntry(_) => BlockErrorKind::CorruptImage,
|
|
||||||
VhdxError::ReadFailed(_) | VhdxError::WriteFailed(_) => BlockErrorKind::Io,
|
|
||||||
};
|
|
||||||
BlockError::new(kind, e).with_op(ErrorOp::Open)
|
|
||||||
})?)),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl disk_file::DiskSize for VhdxDisk {
|
|
||||||
fn logical_size(&self) -> BlockResult<u64> {
|
|
||||||
Ok(self.vhdx_file.lock().unwrap().virtual_disk_size())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl disk_file::PhysicalSize for VhdxDisk {
|
|
||||||
fn physical_size(&self) -> BlockResult<u64> {
|
|
||||||
self.vhdx_file
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.physical_size()
|
|
||||||
.map_err(|e| match e {
|
|
||||||
Error::GetFileMetadata(io) => {
|
|
||||||
BlockError::new(BlockErrorKind::Io, Error::GetFileMetadata(io))
|
|
||||||
}
|
|
||||||
_ => unreachable!("unexpected error from Vhdx::physical_size(): {e}"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl disk_file::DiskFd for VhdxDisk {
|
|
||||||
fn fd(&self) -> BorrowedDiskFd<'_> {
|
|
||||||
BorrowedDiskFd::new(self.vhdx_file.lock().unwrap().as_raw_fd())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl disk_file::Geometry for VhdxDisk {}
|
|
||||||
|
|
||||||
impl disk_file::SparseCapable for VhdxDisk {}
|
|
||||||
|
|
||||||
impl disk_file::Resizable for VhdxDisk {
|
|
||||||
fn resize(&mut self, _size: u64) -> BlockResult<()> {
|
|
||||||
Err(BlockError::new(
|
|
||||||
BlockErrorKind::UnsupportedFeature,
|
|
||||||
DiskFileError::ResizeError(std::io::Error::other("resize not supported for VHDX")),
|
|
||||||
)
|
|
||||||
.with_op(ErrorOp::Resize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl disk_file::DiskFile for VhdxDisk {}
|
|
||||||
|
|
||||||
impl disk_file::AsyncDiskFile for VhdxDisk {
|
|
||||||
fn try_clone(&self) -> BlockResult<Box<dyn disk_file::AsyncDiskFile>> {
|
|
||||||
Ok(Box::new(VhdxDisk {
|
|
||||||
vhdx_file: Arc::clone(&self.vhdx_file),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_async_io(&self, _ring_depth: u32) -> BlockResult<Box<dyn AsyncIo>> {
|
|
||||||
Ok(Box::new(VhdxSync::new(Arc::clone(&self.vhdx_file))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct VhdxSync {
|
pub struct VhdxSync {
|
||||||
vhdx_file: Arc<Mutex<Vhdx>>,
|
vhdx_file: Arc<Mutex<Vhdx>>,
|
||||||
@@ -22,10 +22,6 @@ pub(crate) mod qcow_common;
|
|||||||
pub mod qcow_disk;
|
pub mod qcow_disk;
|
||||||
pub(crate) mod qcow_sync;
|
pub(crate) mod qcow_sync;
|
||||||
mod sparse;
|
mod sparse;
|
||||||
pub use formats::raw as raw_disk;
|
|
||||||
pub mod vhdx;
|
|
||||||
pub mod vhdx_sync;
|
|
||||||
|
|
||||||
use std::alloc::{Layout, alloc_zeroed};
|
use std::alloc::{Layout, alloc_zeroed};
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
@@ -37,6 +33,8 @@ use std::path::Path;
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{cmp, mem, result};
|
use std::{cmp, mem, result};
|
||||||
|
|
||||||
|
pub use formats::raw as raw_disk;
|
||||||
|
pub use formats::vhdx::internal as vhdx;
|
||||||
#[cfg(feature = "io_uring")]
|
#[cfg(feature = "io_uring")]
|
||||||
use io_uring::{IoUring, Probe, opcode};
|
use io_uring::{IoUring, Probe, opcode};
|
||||||
use libc::{
|
use libc::{
|
||||||
@@ -54,8 +52,8 @@ use vmm_sys_util::{aio, ioctl_io_nr, ioctl_ior_nr};
|
|||||||
|
|
||||||
use crate::async_io::AsyncIoError;
|
use crate::async_io::AsyncIoError;
|
||||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||||
|
use crate::formats::vhdx::VhdxError;
|
||||||
use crate::request::SECTOR_SIZE;
|
use crate::request::SECTOR_SIZE;
|
||||||
use crate::vhdx::VhdxError;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|||||||
Reference in New Issue
Block a user