From f6ec817b9b24f8724b1d15b7905d6afc1d86ec91 Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Sat, 14 Mar 2026 00:00:14 +0100 Subject: [PATCH] block: qcow: Switch resize and grow_l1_table to BlockResult Switch resize and grow_l1_table from qcow::Result to BlockResult. All I/O error sites use explicit BlockError::new with the Io kind. The write_to call in grow_l1_table rewraps WritingHeader as ResizeIo to preserve the existing error semantics. The two resize tests that check for ShrinkNotSupported and ResizeWithBackingFile are updated to match on BlockErrorKind with downcast to inspect the underlying variant. Signed-off-by: Anatol Belski --- block/src/qcow/mod.rs | 58 +++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index c926642e6..75afd268d 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -868,7 +868,7 @@ impl QcowFile { /// if needed. Shrinking is not supported, as it could lead to data /// loss. Not supported when a backing file is present in that case /// an error is returned. - pub fn resize(&mut self, new_size: u64) -> Result<()> { + pub fn resize(&mut self, new_size: u64) -> BlockResult<()> { let current_size = self.virtual_size(); if new_size == current_size { @@ -876,11 +876,17 @@ impl QcowFile { } if new_size < current_size { - return Err(Error::ShrinkNotSupported); + return Err(BlockError::new( + BlockErrorKind::UnsupportedFeature, + Error::ShrinkNotSupported, + )); } if self.backing_file.is_some() { - return Err(Error::ResizeWithBackingFile); + return Err(BlockError::new( + BlockErrorKind::UnsupportedFeature, + Error::ResizeWithBackingFile, + )); } // Grow the L1 table if needed @@ -898,18 +904,20 @@ impl QcowFile { self.raw_file .file_mut() .rewind() - .map_err(Error::SeekingFile)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SeekingFile(e)))?; self.header .write_to(self.raw_file.file_mut()) .map_err(|e| match e { - Error::WritingHeader(io_err) => Error::ResizeIo(io_err), - other => other, + Error::WritingHeader(io_err) => { + BlockError::new(BlockErrorKind::Io, Error::ResizeIo(io_err)) + } + other => BlockError::new(BlockErrorKind::Io, other), })?; self.raw_file .file_mut() .sync_all() - .map_err(Error::SyncingHeader)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SyncingHeader(e)))?; Ok(()) } @@ -919,7 +927,7 @@ impl QcowFile { /// This allocates a new L1 table at file end (guaranteeing contiguity), /// copies existing entries, updates refcounts, and atomically switches /// to the new table. - fn grow_l1_table(&mut self, new_l1_size: u32) -> Result<()> { + fn grow_l1_table(&mut self, new_l1_size: u32) -> BlockResult<()> { let old_l1_size = self.header.l1_size; let old_l1_offset = self.header.l1_table_offset; let cluster_size = self.raw_file.cluster_size(); @@ -932,7 +940,7 @@ impl QcowFile { .raw_file .file_mut() .seek(SeekFrom::End(0)) - .map_err(Error::ResizeIo)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; let new_l1_offset = self.raw_file.cluster_address(file_size + cluster_size - 1); // Extend file to fit all L1 clusters @@ -940,12 +948,12 @@ impl QcowFile { self.raw_file .file_mut() .set_len(new_file_end) - .map_err(Error::SettingFileSize)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SettingFileSize(e)))?; // Set refcounts for the contiguous range for i in 0..new_l1_clusters { self.set_cluster_refcount(new_l1_offset + i * cluster_size, 1) - .map_err(Error::ResizeIo)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; } let mut new_l1_data = vec![0u64; new_l1_size as usize]; @@ -957,7 +965,7 @@ impl QcowFile { let refcount = self .refcounts .get_cluster_refcount(&mut self.raw_file, *l2_addr) - .map_err(Error::GettingRefcount)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::GettingRefcount(e)))?; *l2_addr = l1_entry_make(*l2_addr, refcount == 1); } } @@ -965,12 +973,12 @@ impl QcowFile { // Write the new L1 table to the file. self.raw_file .write_pointer_table_direct(new_l1_offset, new_l1_data.iter()) - .map_err(Error::ResizeIo)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::ResizeIo(e)))?; self.raw_file .file_mut() .sync_all() - .map_err(Error::SyncingHeader)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SyncingHeader(e)))?; self.header.l1_size = new_l1_size; self.header.l1_table_offset = new_l1_offset; @@ -978,13 +986,15 @@ impl QcowFile { self.raw_file .file_mut() .rewind() - .map_err(Error::SeekingFile)?; - self.header.write_to(self.raw_file.file_mut())?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SeekingFile(e)))?; + self.header + .write_to(self.raw_file.file_mut()) + .map_err(|e| BlockError::new(BlockErrorKind::Io, e))?; self.raw_file .file_mut() .sync_all() - .map_err(Error::SyncingHeader)?; + .map_err(|e| BlockError::new(BlockErrorKind::Io, Error::SyncingHeader(e)))?; // Free old L1 table clusters let old_l1_bytes = old_l1_size as u64 * size_of::() as u64; @@ -3141,7 +3151,12 @@ mod unit_tests { let result = q.resize(smaller_size); assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::ShrinkNotSupported)); + let err = result.unwrap_err(); + assert!(matches!(err.kind(), BlockErrorKind::UnsupportedFeature)); + assert!(matches!( + err.downcast_ref::(), + Some(Error::ShrinkNotSupported) + )); assert_eq!(q.virtual_size(), original_size); }); @@ -3172,7 +3187,12 @@ mod unit_tests { let result = overlay.resize(backing_size * 2); assert!(result.is_err()); - assert!(matches!(result.unwrap_err(), Error::ResizeWithBackingFile)); + let err = result.unwrap_err(); + assert!(matches!(err.kind(), BlockErrorKind::UnsupportedFeature)); + assert!(matches!( + err.downcast_ref::(), + Some(Error::ResizeWithBackingFile) + )); assert_eq!(overlay.virtual_size(), backing_size); }