From 3422a8b25853469b8185b87abbedd161ebd7aa0d Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 20 Mar 2026 17:37:19 +0100 Subject: [PATCH] block: qcow: Add QcowDiskAsync struct stub Introduce the device level handle for the async QCOW2 backend. QcowDiskAsync mirrors QcowDiskSync. It parses the image, resolves the backing chain and wraps QcowMetadata in an Arc for sharing across virtio queues. No trait impls yet, just the struct, constructor, Drop and Debug. Signed-off-by: Anatol Belski --- block/src/lib.rs | 1 + block/src/qcow_async.rs | 70 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 block/src/qcow_async.rs 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(); + } +}