From 264013b4243c00355dab68aedd7e9c868c104326 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Thu, 12 Mar 2026 23:45:35 +0100 Subject: [PATCH] block: disk_file: Add DiskBackend dispatch enum Introduce DiskBackend with two variants: - Legacy: wraps Box for existing formats - Next: wraps Box Methods return BlockResult, with DiskFileError converted up to BlockError on the Legacy path. The Next path passes through directly with zero conversion overhead. This is a transitional type. Once all formats implement AsyncFullDiskFile, DiskBackend and Legacy are removed and callers hold Box directly. Signed-off-by: Anatol Belski --- block/src/disk_file.rs | 83 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/block/src/disk_file.rs b/block/src/disk_file.rs index 9e057bd05..fea7243ab 100644 --- a/block/src/disk_file.rs +++ b/block/src/disk_file.rs @@ -34,8 +34,10 @@ //! `&mut self`. Errors are returned as [`BlockResult`]. use std::fmt::Debug; +use std::io; -use crate::async_io::{AsyncIo, BorrowedDiskFd}; +use crate::async_io::{self, AsyncIo, BorrowedDiskFd}; +use crate::error::{BlockError, BlockErrorKind}; use crate::{BlockResult, DiskTopology}; /// Reported capacity of a disk image. @@ -156,3 +158,82 @@ pub trait AsyncFullDiskFile: FullDiskFile + AsyncDiskFile {} /// Blanket implementation: any type implementing both [`FullDiskFile`] /// and [`AsyncDiskFile`] automatically satisfies [`AsyncFullDiskFile`]. impl AsyncFullDiskFile for T {} + +/// A disk backend that dispatches to either the existing [`async_io::DiskFile`] +/// trait or the next-generation [`AsyncFullDiskFile`] trait. +pub enum DiskBackend { + /// Existing disk file backend (raw, vhd, vhdx, etc.). + Legacy(Box), + /// Next-generation disk file backend (qcow2, and more formats as they migrate). + Next(Box), +} + +impl DiskBackend { + pub fn logical_size(&mut self) -> BlockResult { + match self { + Self::Legacy(d) => d + .logical_size() + .map_err(|e| BlockError::new(BlockErrorKind::Io, io::Error::other(e))), + Self::Next(d) => d.logical_size(), + } + } + + pub fn physical_size(&mut self) -> BlockResult { + match self { + Self::Legacy(d) => d + .physical_size() + .map_err(|e| BlockError::new(BlockErrorKind::Io, io::Error::other(e))), + Self::Next(d) => d.physical_size(), + } + } + + pub fn topology(&mut self) -> DiskTopology { + match self { + Self::Legacy(d) => d.topology(), + Self::Next(d) => d.topology(), + } + } + + pub fn supports_sparse_operations(&self) -> bool { + match self { + Self::Legacy(d) => d.supports_sparse_operations(), + Self::Next(d) => d.supports_sparse_operations(), + } + } + + pub fn supports_zero_flag(&self) -> bool { + match self { + Self::Legacy(d) => d.supports_zero_flag(), + Self::Next(d) => d.supports_zero_flag(), + } + } + + pub fn fd(&mut self) -> BorrowedDiskFd<'_> { + match self { + Self::Legacy(d) => d.fd(), + Self::Next(d) => d.fd(), + } + } + + pub fn new_async_io(&self, ring_depth: u32) -> BlockResult> { + match self { + Self::Legacy(d) => d + .new_async_io(ring_depth) + .map_err(|e| BlockError::new(BlockErrorKind::Io, io::Error::other(e))), + Self::Next(d) => d.new_async_io(ring_depth), + } + } + + pub fn resize(&mut self, new_size: u64) -> BlockResult<()> { + match self { + Self::Legacy(d) => d.resize(new_size).map_err(|e| match e { + async_io::DiskFileError::Unsupported => BlockError::new( + BlockErrorKind::UnsupportedFeature, + io::Error::other("resize not supported"), + ), + _ => BlockError::new(BlockErrorKind::Io, io::Error::other(e)), + }), + Self::Next(d) => d.resize(new_size), + } + } +}