mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Flatten internal and worker modules
Remove the internal and worker submodule layers from the QCOW2 format directory. The former internal files become direct children of qcow, with internal/mod.rs turning into parser.rs. The worker backends move up as engine_sync.rs and engine_uring.rs, and worker/mod.rs, which held only module declarations, is dropped. The public parser types are now surfaced at the qcow module level, so external callers use block::formats::qcow 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:
committed by
Rob Bradford
parent
b5aeabe77c
commit
7120311462
@@ -14,7 +14,7 @@ use std::sync::Arc;
|
||||
|
||||
use super::decoder::Decoder;
|
||||
use super::metadata::{BackingRead, ClusterReadMapping, QcowMetadata};
|
||||
use super::{BackingFile, BackingKind, Error as QcowError};
|
||||
use super::parser::{BackingFile, BackingKind, Error as QcowError};
|
||||
use crate::error::{BlockError, BlockErrorKind, BlockResult, ErrorOp};
|
||||
use crate::formats::qcow::common::decompress_cluster;
|
||||
|
||||
@@ -146,7 +146,7 @@ impl Drop for Qcow2Backing {
|
||||
}
|
||||
|
||||
/// Construct a thread safe backing file reader.
|
||||
pub fn shared_backing_from(bf: BackingFile) -> BlockResult<Arc<dyn BackingRead>> {
|
||||
pub(super) fn shared_backing_from(bf: BackingFile) -> BlockResult<Arc<dyn BackingRead>> {
|
||||
let (kind, virtual_size) = bf.into_kind();
|
||||
|
||||
let dup_fd = |fd: BorrowedFd<'_>| -> BlockResult<OwnedFd> {
|
||||
@@ -10,16 +10,14 @@
|
||||
|
||||
use std::io;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::internal;
|
||||
use super::internal::decoder::Decoder;
|
||||
use super::decoder::Decoder;
|
||||
|
||||
/// Decompress a full QCOW2 cluster from compressed data.
|
||||
///
|
||||
/// Returns a `cluster_size` byte buffer with the decompressed cluster
|
||||
/// content. Fails if the decoder does not produce exactly `cluster_size`
|
||||
/// bytes.
|
||||
pub fn decompress_cluster(
|
||||
pub(super) fn decompress_cluster(
|
||||
compressed: &[u8],
|
||||
cluster_size: usize,
|
||||
decoder: &dyn Decoder,
|
||||
@@ -43,8 +41,8 @@ pub(crate) mod unit_tests {
|
||||
use flate2::Compression;
|
||||
use flate2::write::DeflateEncoder;
|
||||
|
||||
use super::super::decoder::ZlibDecoder;
|
||||
use super::decompress_cluster;
|
||||
use super::internal::decoder::ZlibDecoder;
|
||||
|
||||
const COMPRESSED_FLAG: u64 = 1 << 62;
|
||||
const CLUSTER_USED_FLAG: u64 = 1 << 63;
|
||||
|
||||
@@ -18,7 +18,7 @@ pub enum Error {
|
||||
ZstdFillBuffer(#[source] io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub(super) type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Generic trait for decoding zlib/zstd formats
|
||||
pub trait Decoder: Send + Sync {
|
||||
@@ -26,7 +26,7 @@ pub trait Decoder: Send + Sync {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ZlibDecoder {}
|
||||
pub(super) struct ZlibDecoder {}
|
||||
|
||||
impl Decoder for ZlibDecoder {
|
||||
fn decode(&self, input: &[u8], output: &mut [u8]) -> Result<usize> {
|
||||
@@ -45,7 +45,7 @@ impl Decoder for ZlibDecoder {
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ZstdDecoder {}
|
||||
pub(super) struct ZstdDecoder {}
|
||||
|
||||
impl Decoder for ZstdDecoder {
|
||||
fn decode(&self, input: &[u8], output: &mut [u8]) -> Result<usize> {
|
||||
@@ -13,14 +13,14 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||
|
||||
use super::common::decompress_cluster;
|
||||
use super::internal::decoder::Decoder;
|
||||
use super::internal::metadata::{
|
||||
use super::decoder::Decoder;
|
||||
use super::metadata::{
|
||||
BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata,
|
||||
};
|
||||
use super::internal::qcow_raw_file::QcowRawFile;
|
||||
use super::qcow_raw_file::QcowRawFile;
|
||||
use crate::async_io::{AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult};
|
||||
|
||||
pub struct QcowSync {
|
||||
pub(super) struct QcowSync {
|
||||
metadata: Arc<QcowMetadata>,
|
||||
data_file: QcowRawFile,
|
||||
/// See the backing_file field on QcowDisk.
|
||||
@@ -322,10 +322,9 @@ mod unit_tests {
|
||||
use crate::error::BlockErrorKind;
|
||||
use crate::formats::qcow;
|
||||
use crate::formats::qcow::common::unit_tests::compress_allocated_clusters;
|
||||
use crate::formats::qcow::internal::{
|
||||
BackingFileConfig, Error as QcowError, ImageType, QcowHeader,
|
||||
use crate::formats::qcow::{
|
||||
BackingFileConfig, Error as QcowError, ImageType, QcowDisk, QcowHeader, QcowTempDisk,
|
||||
};
|
||||
use crate::formats::qcow::{QcowDisk, QcowTempDisk};
|
||||
|
||||
const TEST_L1_L2_ADDR_MASK: u64 = 0x00ff_ffff_ffff_fe00;
|
||||
const TEST_HEADER_L1_TABLE_OFFSET: u64 = 40;
|
||||
@@ -18,11 +18,11 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::write_zeroes::{PunchHole, WriteZeroesAt};
|
||||
|
||||
use super::common::decompress_cluster;
|
||||
use super::internal::decoder::Decoder;
|
||||
use super::internal::metadata::{
|
||||
use super::decoder::Decoder;
|
||||
use super::metadata::{
|
||||
BackingRead, ClusterReadMapping, ClusterWriteMapping, DeallocAction, QcowMetadata,
|
||||
};
|
||||
use super::internal::qcow_raw_file::QcowRawFile;
|
||||
use super::qcow_raw_file::QcowRawFile;
|
||||
use crate::async_io::{
|
||||
AsyncIo, AsyncIoCompletion, AsyncIoError, AsyncIoOperation, AsyncIoResult, UringDataIo,
|
||||
};
|
||||
@@ -36,7 +36,7 @@ use crate::async_io::{
|
||||
///
|
||||
/// Writes are synchronous because metadata allocation must complete
|
||||
/// before the host offset is known.
|
||||
pub struct QcowAsync {
|
||||
pub(super) struct QcowAsync {
|
||||
metadata: Arc<QcowMetadata>,
|
||||
// Drop before data_file so pending SQEs can be submitted while fd is valid.
|
||||
data_io: UringDataIo,
|
||||
@@ -483,8 +483,7 @@ mod unit_tests {
|
||||
use crate::async_io::{AsyncIoCompletion, AsyncIoOperation, GuestMemoryTarget, OwnedIoBuffer};
|
||||
use crate::disk_file::AsyncDiskFile;
|
||||
use crate::formats::qcow::common::unit_tests::compress_allocated_clusters;
|
||||
use crate::formats::qcow::internal::{BackingFileConfig, ImageType};
|
||||
use crate::formats::qcow::{QcowDisk, QcowTempDisk};
|
||||
use crate::formats::qcow::{BackingFileConfig, ImageType, QcowDisk, QcowTempDisk};
|
||||
|
||||
fn create_disk_with_data(
|
||||
file_size: u64,
|
||||
@@ -16,8 +16,9 @@ use bitflags::bitflags;
|
||||
use vmm_sys_util::file_traits::FileSync;
|
||||
|
||||
use super::decoder::{Decoder, ZlibDecoder, ZstdDecoder};
|
||||
use super::parser::{Error, Result};
|
||||
use super::qcow_raw_file::BeUint;
|
||||
use super::{Error, Result, div_round_up_u32, div_round_up_u64};
|
||||
use super::util::{div_round_up_u32, div_round_up_u64};
|
||||
use crate::aligned_file::AlignedFile;
|
||||
use crate::error::{BlockError, BlockErrorKind, BlockResult};
|
||||
|
||||
@@ -41,7 +41,7 @@ use super::{QcowHeader, refcount};
|
||||
/// the actual data I/O using its own per queue file descriptor without
|
||||
/// holding the metadata lock.
|
||||
#[derive(Debug)]
|
||||
pub enum ClusterReadMapping {
|
||||
pub(super) enum ClusterReadMapping {
|
||||
/// The cluster is not allocated and the guest should see zeros.
|
||||
/// This covers both truly unallocated clusters where the L1 or L2
|
||||
/// entry is zero and clusters with the ZERO flag set.
|
||||
@@ -76,7 +76,7 @@ pub enum ClusterReadMapping {
|
||||
/// the actual data I/O using its own per queue file descriptor without
|
||||
/// holding the metadata lock.
|
||||
#[derive(Debug)]
|
||||
pub enum ClusterWriteMapping {
|
||||
pub(super) enum ClusterWriteMapping {
|
||||
/// The write target is at the given host file offset.
|
||||
/// This covers both already allocated clusters and freshly allocated ones.
|
||||
/// The offset is the exact byte position combining cluster base and
|
||||
@@ -94,7 +94,7 @@ pub(crate) trait BackingRead: Send + Sync {
|
||||
|
||||
/// Action that the caller must perform after deallocate_bytes.
|
||||
#[derive(Debug)]
|
||||
pub enum DeallocAction {
|
||||
pub(super) enum DeallocAction {
|
||||
/// Punch a hole at the given host file offset for a full cluster.
|
||||
PunchHole { host_offset: u64, length: u64 },
|
||||
/// Write zeros at the given host file offset for a partial cluster.
|
||||
@@ -115,7 +115,7 @@ pub enum DeallocAction {
|
||||
/// L1 to L2 lookup, which completes under a shared read lock. Only
|
||||
/// cluster allocation, L2 cache eviction and resize take the exclusive
|
||||
/// write lock, so contention stays low and queues scale.
|
||||
pub struct QcowMetadata {
|
||||
pub(super) struct QcowMetadata {
|
||||
inner: RwLock<QcowState>,
|
||||
decoder: Arc<dyn Decoder>,
|
||||
}
|
||||
@@ -160,7 +160,7 @@ impl QcowMetadata {
|
||||
///
|
||||
/// The has_backing_file flag indicates whether a backing file exists,
|
||||
/// needed to distinguish zero versus backing for unallocated clusters.
|
||||
pub fn map_clusters_for_read(
|
||||
pub(super) fn map_clusters_for_read(
|
||||
&self,
|
||||
address: u64,
|
||||
total_length: usize,
|
||||
@@ -228,7 +228,7 @@ impl QcowMetadata {
|
||||
/// unallocated and a backing file exists, the caller should have already
|
||||
/// read the backing cluster data and pass it here. If None, the new
|
||||
/// cluster is zeroed.
|
||||
pub fn map_cluster_for_write(
|
||||
pub(super) fn map_cluster_for_write(
|
||||
&self,
|
||||
address: u64,
|
||||
backing_data: Option<Vec<u8>>,
|
||||
@@ -237,7 +237,7 @@ impl QcowMetadata {
|
||||
inner.map_write(address, backing_data)
|
||||
}
|
||||
|
||||
pub fn flush(&self) -> io::Result<()> {
|
||||
pub(super) fn flush(&self) -> io::Result<()> {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.sync_caches()?;
|
||||
let mut unref = mem::take(&mut inner.unref_clusters);
|
||||
@@ -247,7 +247,7 @@ impl QcowMetadata {
|
||||
|
||||
/// Flushes dirty metadata caches and clears the dirty bit for
|
||||
/// clean shutdown.
|
||||
pub fn shutdown(&self) {
|
||||
pub(super) fn shutdown(&self) {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
let _ = inner.sync_caches();
|
||||
let QcowState {
|
||||
@@ -265,7 +265,7 @@ impl QcowMetadata {
|
||||
/// clusters beyond the new size and risks data loss.
|
||||
///
|
||||
/// Returns an error if the new size is smaller than the current size.
|
||||
pub fn resize(&self, new_size: u64) -> io::Result<()> {
|
||||
pub(super) fn resize(&self, new_size: u64) -> io::Result<()> {
|
||||
let mut inner = self.inner.write().unwrap();
|
||||
inner.resize(new_size)
|
||||
}
|
||||
@@ -339,16 +339,16 @@ impl QcowMetadata {
|
||||
Ok(actions)
|
||||
}
|
||||
|
||||
pub fn virtual_size(&self) -> u64 {
|
||||
pub(super) fn virtual_size(&self) -> u64 {
|
||||
self.inner.read().unwrap().header.size
|
||||
}
|
||||
|
||||
pub fn cluster_size(&self) -> u64 {
|
||||
pub(super) fn cluster_size(&self) -> u64 {
|
||||
self.inner.read().unwrap().raw_file.cluster_size()
|
||||
}
|
||||
|
||||
/// Returns the shared decoder matching the image compression type.
|
||||
pub fn decoder(&self) -> Arc<dyn Decoder> {
|
||||
pub(super) fn decoder(&self) -> Arc<dyn Decoder> {
|
||||
Arc::clone(&self.decoder)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,19 @@
|
||||
//! Provides [`QcowDisk`], the `DiskFile` wrapper for QCOW2 images
|
||||
//! with backing file and compression support.
|
||||
|
||||
pub(crate) mod common;
|
||||
pub mod internal;
|
||||
pub mod worker;
|
||||
mod backing;
|
||||
mod common;
|
||||
mod decoder;
|
||||
mod engine_sync;
|
||||
#[cfg(feature = "io_uring")]
|
||||
mod engine_uring;
|
||||
mod header;
|
||||
mod metadata;
|
||||
mod parser;
|
||||
mod qcow_raw_file;
|
||||
mod refcount;
|
||||
mod util;
|
||||
mod vec_cache;
|
||||
|
||||
use std::fs::File;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
@@ -18,20 +28,22 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, io};
|
||||
|
||||
pub use parser::{
|
||||
BackingFileConfig, CompressionType, Error, ImageType, IncompatFeatures, MissingFeatureError,
|
||||
QcowHeader,
|
||||
};
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
use self::internal::backing::shared_backing_from;
|
||||
use self::internal::metadata::{BackingRead, QcowMetadata};
|
||||
use self::internal::qcow_raw_file::QcowRawFile;
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use self::internal::{BackingFileConfig, QcowHeader};
|
||||
use self::internal::{MAX_NESTING_DEPTH, parse_qcow};
|
||||
use self::backing::shared_backing_from;
|
||||
use self::engine_sync::QcowSync;
|
||||
#[cfg(feature = "io_uring")]
|
||||
use self::worker::async_uring::QcowAsync;
|
||||
use self::worker::sync::QcowSync;
|
||||
use self::engine_uring::QcowAsync;
|
||||
use self::metadata::{BackingRead, QcowMetadata};
|
||||
use self::parser::{MAX_NESTING_DEPTH, parse_qcow};
|
||||
use self::qcow_raw_file::QcowRawFile;
|
||||
use crate::aligned_file::AlignedFile;
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
use crate::async_io::GuestMemoryTarget;
|
||||
@@ -142,7 +154,7 @@ impl QcowDisk {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn metadata(&self) -> &QcowMetadata {
|
||||
fn metadata(&self) -> &QcowMetadata {
|
||||
&self.metadata
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
|
||||
pub(crate) mod backing;
|
||||
pub(crate) mod decoder;
|
||||
mod header;
|
||||
pub(crate) mod metadata;
|
||||
pub(crate) mod qcow_raw_file;
|
||||
mod refcount;
|
||||
mod util;
|
||||
mod vec_cache;
|
||||
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
use std::fs::{OpenOptions, read_link};
|
||||
@@ -21,24 +12,25 @@ use std::os::unix::fs::FileExt;
|
||||
use std::path::Path;
|
||||
use std::{io, result, str};
|
||||
|
||||
pub use header::{
|
||||
use log::warn;
|
||||
use remain::sorted;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use super::header::{
|
||||
BackingFileConfig, CompressionType, ImageType, IncompatFeatures, MissingFeatureError,
|
||||
QcowHeader,
|
||||
};
|
||||
use header::{
|
||||
use super::header::{
|
||||
COMPATIBLE_FEATURES_LAZY_REFCOUNTS, MAX_CLUSTER_BITS, MAX_QCOW_FILE_SIZE,
|
||||
MAX_RAM_POINTER_TABLE_SIZE, MIN_CLUSTER_BITS, QCOW_MAGIC, max_refcount_clusters,
|
||||
offset_is_cluster_boundary,
|
||||
};
|
||||
use log::warn;
|
||||
use qcow_raw_file::QcowRawFile;
|
||||
use refcount::RefCount;
|
||||
use remain::sorted;
|
||||
use thiserror::Error;
|
||||
pub(crate) use util::MAX_NESTING_DEPTH;
|
||||
use util::{L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, div_round_up_u32, div_round_up_u64};
|
||||
use vec_cache::{CacheMap, VecCache};
|
||||
|
||||
use super::qcow_raw_file::QcowRawFile;
|
||||
use super::refcount::RefCount;
|
||||
pub(crate) use super::util::MAX_NESTING_DEPTH;
|
||||
use super::util::{L1_TABLE_OFFSET_MASK, L2_TABLE_OFFSET_MASK, div_round_up_u64};
|
||||
use super::vec_cache::{CacheMap, VecCache};
|
||||
use super::{metadata, refcount};
|
||||
use crate::aligned_file::AlignedFile;
|
||||
use crate::error::{BlockError, BlockErrorKind, BlockResult};
|
||||
use crate::query_device_size;
|
||||
@@ -156,7 +148,7 @@ pub enum Error {
|
||||
WritingHeader(#[source] io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub(super) type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Concrete backing file variants.
|
||||
pub(crate) enum BackingKind {
|
||||
@@ -865,7 +857,7 @@ fn rebuild_refcounts(raw_file: &mut QcowRawFile, header: QcowHeader) -> BlockRes
|
||||
}
|
||||
|
||||
/// Detect the type of an image file by checking for a valid qcow2 header.
|
||||
pub fn detect_image_type(file: &mut AlignedFile) -> BlockResult<ImageType> {
|
||||
pub(super) fn detect_image_type(file: &mut AlignedFile) -> BlockResult<ImageType> {
|
||||
let mut magic_bytes = [0u8; 4];
|
||||
file.read_exact_at(&mut magic_bytes, 0)
|
||||
.map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ReadingHeader(e)))?;
|
||||
@@ -888,11 +880,11 @@ mod unit_tests {
|
||||
use vmm_sys_util::tempdir::TempDir;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
use super::header::{
|
||||
use super::super::header::{
|
||||
AUTOCLEAR_FEATURES_OFFSET, DEFAULT_CLUSTER_BITS, DEFAULT_REFCOUNT_ORDER,
|
||||
HEADER_EXT_BACKING_FORMAT, HEADER_EXT_END, V2_BARE_HEADER_SIZE, V3_BARE_HEADER_SIZE,
|
||||
};
|
||||
use super::util::ZERO_FLAG;
|
||||
use super::super::util::{self, ZERO_FLAG};
|
||||
use super::*;
|
||||
use crate::formats::qcow::{QcowDisk, QcowTempDisk};
|
||||
|
||||
@@ -149,7 +149,7 @@ fn write_refcount_subbyte<const BITS: usize>(
|
||||
|
||||
/// A qcow file. Allows reading/writing clusters and appending clusters.
|
||||
#[derive(Debug)]
|
||||
pub struct QcowRawFile {
|
||||
pub(super) struct QcowRawFile {
|
||||
file: AlignedFile,
|
||||
cluster_size: u64,
|
||||
cluster_mask: u64,
|
||||
@@ -161,7 +161,7 @@ pub struct QcowRawFile {
|
||||
impl QcowRawFile {
|
||||
/// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
|
||||
/// a power of two or refcount_bits is invalid.
|
||||
pub fn from(file: AlignedFile, cluster_size: u64, refcount_bits: u64) -> Option<Self> {
|
||||
pub(super) fn from(file: AlignedFile, cluster_size: u64, refcount_bits: u64) -> Option<Self> {
|
||||
if !cluster_size.is_power_of_two() {
|
||||
return None;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ impl QcowRawFile {
|
||||
|
||||
/// Reads `count` 64 bit offsets and returns them as a vector.
|
||||
/// `mask` optionally `&`s out some of the bits on the file.
|
||||
pub fn read_pointer_table(
|
||||
pub(super) fn read_pointer_table(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
count: u64,
|
||||
@@ -213,7 +213,11 @@ impl QcowRawFile {
|
||||
|
||||
/// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
|
||||
/// `mask` optionally `&`s out some of the bits on the file.
|
||||
pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
|
||||
pub(super) fn read_pointer_cluster(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
mask: Option<u64>,
|
||||
) -> io::Result<Vec<u64>> {
|
||||
let count = self.cluster_size / size_of::<u64>() as u64;
|
||||
self.read_pointer_table(offset, count, mask)
|
||||
}
|
||||
@@ -223,7 +227,7 @@ impl QcowRawFile {
|
||||
///
|
||||
/// The callback may perform metadata I/O on this `QcowRawFile`, so all
|
||||
/// entries are materialized before the final positional write.
|
||||
pub fn write_pointer_table<'a, T: Copy + 'a>(
|
||||
pub(super) fn write_pointer_table<'a, T: Copy + 'a>(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
entries: impl Iterator<Item = &'a T>,
|
||||
@@ -240,7 +244,7 @@ impl QcowRawFile {
|
||||
/// Writes a pointer table directly without transforming values.
|
||||
///
|
||||
/// Uses the same materialize-then-write path as `write_pointer_table`.
|
||||
pub fn write_pointer_table_direct<'a>(
|
||||
pub(super) fn write_pointer_table_direct<'a>(
|
||||
&mut self,
|
||||
offset: u64,
|
||||
entries: impl Iterator<Item = &'a u64>,
|
||||
@@ -255,18 +259,21 @@ impl QcowRawFile {
|
||||
/// Read a refcount block from the file and returns a Vec containing the block.
|
||||
/// Always returns a cluster's worth of data.
|
||||
#[inline]
|
||||
pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u64>> {
|
||||
pub(super) fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u64>> {
|
||||
(self.read_refcount_fn)(&mut self.file, offset, self.refcount_block_entries as usize)
|
||||
}
|
||||
|
||||
/// Writes a refcount block to the file.
|
||||
#[inline]
|
||||
pub fn write_refcount_block(&mut self, offset: u64, table: &[u64]) -> io::Result<()> {
|
||||
pub(super) fn write_refcount_block(&mut self, offset: u64, table: &[u64]) -> io::Result<()> {
|
||||
(self.write_refcount_fn)(&mut self.file, offset, table)
|
||||
}
|
||||
|
||||
/// Allocates a new cluster at the end of the current file, return the address.
|
||||
pub fn add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>> {
|
||||
pub(super) fn add_cluster_end(
|
||||
&mut self,
|
||||
max_valid_cluster_offset: u64,
|
||||
) -> io::Result<Option<u64>> {
|
||||
// Determine where the new end of the file should be and set_len, which
|
||||
// translates to truncate(2).
|
||||
let file_end: u64 = self.physical_size()?;
|
||||
@@ -282,44 +289,44 @@ impl QcowRawFile {
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying file.
|
||||
pub fn file(&self) -> &AlignedFile {
|
||||
pub(super) fn file(&self) -> &AlignedFile {
|
||||
&self.file
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying file.
|
||||
pub fn file_mut(&mut self) -> &mut AlignedFile {
|
||||
pub(super) fn file_mut(&mut self) -> &mut AlignedFile {
|
||||
&mut self.file
|
||||
}
|
||||
|
||||
/// Returns the size of the file's clusters.
|
||||
pub fn cluster_size(&self) -> u64 {
|
||||
pub(super) fn cluster_size(&self) -> u64 {
|
||||
self.cluster_size
|
||||
}
|
||||
|
||||
/// Returns the offset of `address` within a cluster.
|
||||
pub fn cluster_offset(&self, address: u64) -> u64 {
|
||||
pub(super) fn cluster_offset(&self, address: u64) -> u64 {
|
||||
address & self.cluster_mask
|
||||
}
|
||||
|
||||
/// Returns the base address of the cluster containing `address`.
|
||||
pub fn cluster_address(&self, address: u64) -> u64 {
|
||||
pub(super) fn cluster_address(&self, address: u64) -> u64 {
|
||||
address & !self.cluster_mask
|
||||
}
|
||||
|
||||
/// Zeros out a cluster in the file.
|
||||
pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
|
||||
pub(super) fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
|
||||
let cluster_size = self.cluster_size as usize;
|
||||
self.file.write_all_zeroes_at(address, cluster_size)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes
|
||||
pub fn write_cluster(&mut self, address: u64, data: &[u8]) -> io::Result<()> {
|
||||
pub(super) fn write_cluster(&mut self, address: u64, data: &[u8]) -> io::Result<()> {
|
||||
let cluster_size = self.cluster_size as usize;
|
||||
self.file.write_all_at(&data[0..cluster_size], address)
|
||||
}
|
||||
|
||||
pub fn physical_size(&self) -> io::Result<u64> {
|
||||
pub(super) fn physical_size(&self) -> io::Result<u64> {
|
||||
self.file.metadata().map(|m| m.len())
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,11 @@ pub enum Error {
|
||||
},
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
pub(super) type Result<T> = result::Result<T, Error>;
|
||||
|
||||
/// Represents the refcount entries for an open qcow file.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RefCount {
|
||||
pub(super) struct RefCount {
|
||||
ref_table: VecCache<u64>,
|
||||
refcount_table_offset: u64,
|
||||
refblock_cache: CacheMap<VecCache<u64>>,
|
||||
@@ -62,7 +62,7 @@ impl RefCount {
|
||||
/// `refcount_block_entries` indicates the number of refcounts in each refcount block.
|
||||
/// `refcount_bits` is the number of bits per refcount (1, 2, 4, 8, 16, 32, or 64).
|
||||
/// Each refcount table entry points to a refcount block.
|
||||
pub fn new(
|
||||
pub(super) fn new(
|
||||
raw_file: &mut QcowRawFile,
|
||||
refcount_table_offset: u64,
|
||||
refcount_table_entries: u64,
|
||||
@@ -95,12 +95,12 @@ impl RefCount {
|
||||
}
|
||||
|
||||
/// Returns the number of refcounts per block.
|
||||
pub fn refcounts_per_block(&self) -> u64 {
|
||||
pub(super) fn refcounts_per_block(&self) -> u64 {
|
||||
self.refcount_block_entries
|
||||
}
|
||||
|
||||
/// Returns the maximum valid cluster offset in the raw file for this refcount table.
|
||||
pub fn max_valid_cluster_offset(&self) -> u64 {
|
||||
pub(super) fn max_valid_cluster_offset(&self) -> u64 {
|
||||
self.max_valid_cluster_offset
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ impl RefCount {
|
||||
/// allocate a cluster or read the required one and call this function again with the cluster.
|
||||
/// On success, an optional address of a dropped cluster is returned. The dropped cluster can
|
||||
/// be reused for other purposes.
|
||||
pub fn set_cluster_refcount(
|
||||
pub(super) fn set_cluster_refcount(
|
||||
&mut self,
|
||||
raw_file: &mut QcowRawFile,
|
||||
cluster_address: u64,
|
||||
@@ -167,7 +167,7 @@ impl RefCount {
|
||||
|
||||
/// Flush the dirty refcount blocks. This must be done before flushing the table that points to
|
||||
/// the blocks.
|
||||
pub fn flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()> {
|
||||
pub(super) fn flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()> {
|
||||
// Write out all dirty L2 tables.
|
||||
for (table_index, block) in self.refblock_cache.iter_mut().filter(|(_k, v)| v.dirty()) {
|
||||
let addr = self.ref_table[*table_index];
|
||||
@@ -183,7 +183,7 @@ impl RefCount {
|
||||
|
||||
/// Flush the refcount table that keeps the address of the refcounts blocks.
|
||||
/// Returns true if the table changed since the previous `flush_table()` call.
|
||||
pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> {
|
||||
pub(super) fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> {
|
||||
if self.ref_table.dirty() {
|
||||
raw_file
|
||||
.write_pointer_table_direct(self.refcount_table_offset, self.ref_table.iter())?;
|
||||
@@ -195,7 +195,7 @@ impl RefCount {
|
||||
}
|
||||
|
||||
/// Gets the refcount for a cluster with the given address.
|
||||
pub fn get_cluster_refcount(
|
||||
pub(super) fn get_cluster_refcount(
|
||||
&mut self,
|
||||
raw_file: &mut QcowRawFile,
|
||||
address: u64,
|
||||
@@ -12,21 +12,21 @@ use std::slice::SliceIndex;
|
||||
|
||||
/// Trait that allows for checking if an implementor is dirty. Useful for types that are cached so
|
||||
/// it can be checked if they need to be committed to disk.
|
||||
pub trait Cacheable {
|
||||
pub(super) trait Cacheable {
|
||||
/// Used to check if the item needs to be written out or if it can be discarded.
|
||||
fn dirty(&self) -> bool;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Represents a vector that implements the `Cacheable` trait so it can be held in a cache.
|
||||
pub struct VecCache<T: 'static + Copy + Default> {
|
||||
pub(super) struct VecCache<T: 'static + Copy + Default> {
|
||||
vec: Box<[T]>,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<T: 'static + Copy + Default> VecCache<T> {
|
||||
/// Creates a `VecCache` that can hold `count` elements.
|
||||
pub fn new(count: usize) -> VecCache<T> {
|
||||
pub(super) fn new(count: usize) -> VecCache<T> {
|
||||
VecCache {
|
||||
vec: vec![Default::default(); count].into_boxed_slice(),
|
||||
dirty: true,
|
||||
@@ -34,14 +34,14 @@ impl<T: 'static + Copy + Default> VecCache<T> {
|
||||
}
|
||||
|
||||
/// Creates a `VecCache` from the passed in `vec`.
|
||||
pub fn from_vec(vec: Vec<T>) -> VecCache<T> {
|
||||
pub(super) fn from_vec(vec: Vec<T>) -> VecCache<T> {
|
||||
VecCache {
|
||||
vec: vec.into_boxed_slice(),
|
||||
dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
|
||||
pub(super) fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
|
||||
where
|
||||
I: SliceIndex<[T]>,
|
||||
{
|
||||
@@ -49,17 +49,17 @@ impl<T: 'static + Copy + Default> VecCache<T> {
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying vector.
|
||||
pub fn get_values(&self) -> &[T] {
|
||||
pub(super) fn get_values(&self) -> &[T] {
|
||||
&self.vec
|
||||
}
|
||||
|
||||
/// Mark this cache element as clean.
|
||||
pub fn mark_clean(&mut self) {
|
||||
pub(super) fn mark_clean(&mut self) {
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the vector.
|
||||
pub fn len(&self) -> usize {
|
||||
pub(super) fn len(&self) -> usize {
|
||||
self.vec.len()
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ impl<T: 'static + Copy + Default> VecCache<T> {
|
||||
/// No-op if `new_len <= self.len()`. Allocates a new buffer, copies
|
||||
/// existing data, and fills new elements with default values.
|
||||
/// Marks the cache as dirty.
|
||||
pub fn extend(&mut self, new_len: usize) {
|
||||
pub(super) fn extend(&mut self, new_len: usize) {
|
||||
if new_len <= self.vec.len() {
|
||||
return;
|
||||
}
|
||||
@@ -109,37 +109,37 @@ impl<T: 'static + Copy + Default> Deref for VecCache<T> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CacheMap<T: Cacheable> {
|
||||
pub(super) struct CacheMap<T: Cacheable> {
|
||||
capacity: usize,
|
||||
map: HashMap<usize, T>,
|
||||
}
|
||||
|
||||
impl<T: Cacheable> CacheMap<T> {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
pub(super) fn new(capacity: usize) -> Self {
|
||||
CacheMap {
|
||||
capacity,
|
||||
map: HashMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, key: usize) -> bool {
|
||||
pub(super) fn contains_key(&self, key: usize) -> bool {
|
||||
self.map.contains_key(&key)
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
pub(super) fn get(&self, index: usize) -> Option<&T> {
|
||||
self.map.get(&index)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
pub(super) fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
self.map.get_mut(&index)
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, usize, T> {
|
||||
pub(super) fn iter_mut(&mut self) -> IterMut<'_, usize, T> {
|
||||
self.map.iter_mut()
|
||||
}
|
||||
|
||||
// Check if the refblock cache is full and we need to evict.
|
||||
pub fn insert<F>(&mut self, index: usize, block: T, write_callback: F) -> io::Result<()>
|
||||
pub(super) fn insert<F>(&mut self, index: usize, block: T, write_callback: F) -> io::Result<()>
|
||||
where
|
||||
F: FnOnce(usize, T) -> io::Result<()>,
|
||||
{
|
||||
@@ -1,9 +0,0 @@
|
||||
// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(feature = "io_uring")]
|
||||
pub(crate) mod async_uring;
|
||||
pub(crate) mod sync;
|
||||
|
||||
pub(crate) use super::{common, internal};
|
||||
@@ -28,7 +28,7 @@ use std::str::FromStr;
|
||||
use std::{cmp, io, mem, result};
|
||||
|
||||
pub use aligned_file::AlignedFile;
|
||||
use formats::qcow::internal as qcow;
|
||||
use formats::qcow;
|
||||
#[cfg(feature = "io_uring")]
|
||||
use io_uring::{IoUring, Probe, opcode};
|
||||
use libc::{
|
||||
|
||||
@@ -14,7 +14,7 @@ use std::sync::mpsc::Receiver;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{cmp, fs, io, panic, thread};
|
||||
|
||||
use block::formats::qcow::internal::ImageType as QcowImageType;
|
||||
use block::formats::qcow::ImageType as QcowImageType;
|
||||
use test_infra::*;
|
||||
use vmm_sys_util::tempdir::TempDir;
|
||||
#[cfg(not(feature = "mshv"))]
|
||||
|
||||
@@ -15,8 +15,7 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use block::async_io::{AsyncIo, GuestMemoryTarget};
|
||||
use block::formats::qcow::internal::{BackingFileConfig, ImageType};
|
||||
use block::formats::qcow::{QcowDisk, QcowTempDisk};
|
||||
use block::formats::qcow::{BackingFileConfig, ImageType, QcowDisk, QcowTempDisk};
|
||||
use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
Reference in New Issue
Block a user