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 <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-03-20 17:37:19 +01:00
committed by Rob Bradford
parent 8cd2c957ef
commit 3422a8b258
2 changed files with 71 additions and 0 deletions

View File

@@ -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")]

70
block/src/qcow_async.rs Normal file
View File

@@ -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<QcowMetadata>,
backing_file: Option<Arc<dyn BackingRead>>,
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<Self> {
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();
}
}