diff --git a/block/src/lib.rs b/block/src/lib.rs index ad25f4593..c98f20ae8 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -18,6 +18,7 @@ pub mod fixed_vhd; pub mod fixed_vhd_async; pub mod fixed_vhd_sync; pub mod qcow; +pub mod qcow_async; pub(crate) mod qcow_common; pub mod qcow_sync; #[cfg(feature = "io_uring")] diff --git a/block/src/qcow_async.rs b/block/src/qcow_async.rs new file mode 100644 index 000000000..71bc6f474 --- /dev/null +++ b/block/src/qcow_async.rs @@ -0,0 +1,70 @@ +// Copyright © 2021 Intel Corporation +// +// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause + +//! QCOW2 async disk backend. + +use std::fmt; +use std::fs::File; +use std::sync::Arc; + +use crate::error::{BlockErrorKind, BlockResult, ErrorOp}; +use crate::qcow::backing::shared_backing_from; +use crate::qcow::metadata::{BackingRead, QcowMetadata}; +use crate::qcow::qcow_raw_file::QcowRawFile; +use crate::qcow::{MAX_NESTING_DEPTH, RawFile, parse_qcow}; + +/// Device level handle for a QCOW2 image. +/// +/// Owns the parsed metadata and backing file chain. One instance is +/// created per disk and shared across virtio queues. +pub struct QcowDiskAsync { + metadata: Arc, + backing_file: Option>, + sparse: bool, + data_raw_file: QcowRawFile, +} + +impl fmt::Debug for QcowDiskAsync { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("QcowDiskAsync") + .field("sparse", &self.sparse) + .field("has_backing", &self.backing_file.is_some()) + .finish_non_exhaustive() + } +} + +impl QcowDiskAsync { + pub fn new( + file: File, + direct_io: bool, + backing_files: bool, + sparse: bool, + ) -> BlockResult { + let max_nesting_depth = if backing_files { MAX_NESTING_DEPTH } else { 0 }; + let (inner, backing_file, sparse) = + parse_qcow(RawFile::new(file, direct_io), max_nesting_depth, sparse).map_err(|e| { + let e = if !backing_files && matches!(e.kind(), BlockErrorKind::Overflow) { + e.with_kind(BlockErrorKind::UnsupportedFeature) + } else { + e + }; + e.with_op(ErrorOp::Open) + })?; + let data_raw_file = inner.raw_file.clone(); + Ok(QcowDiskAsync { + metadata: Arc::new(QcowMetadata::new(inner)), + backing_file: backing_file.map(shared_backing_from).transpose()?, + sparse, + data_raw_file, + }) + } +} + +impl Drop for QcowDiskAsync { + fn drop(&mut self) { + self.metadata.shutdown(); + } +}