// Copyright 2026 The Cloud Hypervisor Authors. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 //! Composable disk capability traits for the block crate. //! //! Small traits define individual capabilities: //! //! - [`DiskSize`] - reported capacity (logical size) //! - [`PhysicalSize`] - host allocation size //! - [`DiskFd`] - backing file descriptor access //! - [`Geometry`] - sector/cluster geometry (default 512B) //! - [`SparseCapable`] - sparse and zero flag support //! - [`Resizable`] - online resize //! //! [`DiskFile`] is a supertrait that bundles the universal capabilities //! (`DiskSize` + `Geometry`). [`FullDiskFile`] adds all optional //! capabilities. [`AsyncDiskFile`] extends `DiskFile` with async I/O //! construction for virtio queue workers. [`AsyncFullDiskFile`] //! combines both axes. //! //! ```text //! DiskFile: DiskSize + Geometry + Sync //! / \ //! FullDiskFile: AsyncDiskFile: //! DiskFile + PhysicalSize + DiskFile + Unpin //! DiskFd + SparseCapable + try_clone, create_async_io //! Resizable //! \ / //! AsyncFullDiskFile: FullDiskFile + AsyncDiskFile //! ``` //! //! Readonly accessors take `&self`. Only [`Resizable::resize`] requires //! `&mut self`. Errors are returned as [`BlockResult`]. use std::fmt::Debug; use crate::async_io::{AsyncIo, BorrowedDiskFd}; use crate::{BlockResult, DiskTopology}; /// Reported capacity of a disk image. pub trait DiskSize: Send + Debug { /// Virtual size of the disk image in bytes (reported capacity). fn logical_size(&self) -> BlockResult; } /// Host allocation size of a file-backed disk image. pub trait PhysicalSize: Send + Debug { /// Actual bytes occupied on the host filesystem. fn physical_size(&self) -> BlockResult; } /// Backing file descriptor access for disk images backed by a file. pub trait DiskFd: Send + Debug { /// Borrows the underlying file descriptor. fn fd(&self) -> BorrowedDiskFd<'_>; } /// Sector and cluster geometry of a disk image. /// /// Default returns `DiskTopology::default()` (512B logical/physical). pub trait Geometry: Send + Debug { /// Returns the disk topology. fn topology(&self) -> DiskTopology { DiskTopology::default() } } /// Sparse and zero flag support for thin provisioned disk images. pub trait SparseCapable: Send + Debug { /// Indicates support for sparse operations (punch hole, write zeroes, discard). fn supports_sparse_operations(&self) -> bool { false } /// Indicates support for a metadata level zero flag optimization in /// virtio `VIRTIO_BLK_T_WRITE_ZEROES` requests. When true, the format /// can mark regions as reading zeros via a metadata bit rather than /// writing actual zero bytes to disk. fn supports_zero_flag(&self) -> bool { false } } /// Live disk resize support. /// /// Implementations may return an error if the backend does not /// support resizing (e.g. fixed size formats). pub trait Resizable: Send + Debug { /// Resizes the disk image to the given size in bytes, if the backend supports it. fn resize(&mut self, size: u64) -> BlockResult<()>; } /// Supertrait bundling universal disk capabilities. /// /// Every disk format implements `DiskSize` and `Geometry`. /// `Sync` is required so that `Arc` can be shared /// across threads for concurrent readonly access. pub trait DiskFile: DiskSize + Geometry + Sync {} /// Full capability disk file trait. /// /// Bundles all optional capabilities on top of [`DiskFile`]: /// file descriptor access, physical size, sparse operations, and resize. /// Used by consumers that need feature negotiation without async I/O /// (e.g. vhost user block). pub trait FullDiskFile: DiskFile + PhysicalSize + DiskFd + SparseCapable + Resizable {} /// Blanket implementation: any type implementing all constituent traits /// automatically satisfies [`FullDiskFile`]. impl FullDiskFile for T {} /// Extended disk file trait for virtio queue workers. /// /// Adds cloning and async I/O construction on top of [`DiskFile`]. /// `Unpin` is required so trait objects can be moved freely. pub trait AsyncDiskFile: DiskFile + Unpin { /// Creates an independent handle for a queue worker. /// /// The clone shares internally reference counted state (e.g. /// `Arc`) with the original, but owns its own file /// descriptor and I/O completion resources. Each virtio queue /// gets one clone so that workers can operate in parallel /// without contending on I/O state. /// /// Returns `Box` (not `AsyncFullDiskFile`) /// because clones only serve as data plane handles for queue /// workers. The original remains the control plane for feature /// negotiation and configuration. fn try_clone(&self) -> BlockResult>; /// Constructs a per queue async I/O engine. /// /// # Arguments /// /// * `ring_depth` - maximum number of in flight I/O operations. /// Callers typically pass the virtio queue size. Must be greater /// than zero. Backends that do not use an async ring (e.g. sync /// fallback implementations) may ignore this value. fn create_async_io(&self, ring_depth: u32) -> BlockResult>; } /// Full capability async disk file trait. /// /// Combines [`FullDiskFile`] (all optional capabilities) with /// [`AsyncDiskFile`] (async I/O construction). This is the top level /// trait for virtio block devices that need both feature negotiation /// and async queue workers. /// /// The type narrowing on [`AsyncDiskFile::try_clone`] is intentional: /// clones only serve as data plane handles for queue workers, while /// the original `AsyncFullDiskFile` handle remains the control plane /// for feature negotiation and configuration. pub trait AsyncFullDiskFile: FullDiskFile + AsyncDiskFile {} /// Blanket implementation: any type implementing both [`FullDiskFile`] /// and [`AsyncDiskFile`] automatically satisfies [`AsyncFullDiskFile`]. impl AsyncFullDiskFile for T {}