diff --git a/block/src/async_io.rs b/block/src/async_io.rs index fb60fde71..52f642908 100644 --- a/block/src/async_io.rs +++ b/block/src/async_io.rs @@ -58,10 +58,12 @@ impl AsRawFd for BorrowedDiskFd<'_> { pub trait DiskFile: Send { /// Returns the logical disk size a guest will see. /// - /// For raw formats, this is equal to the physical size. For file formats + /// For raw formats, this is equal to [`Self::physical_size`]. For file formats /// that wrap disk images in a container (e.g. QCOW2), this refers to the /// effective size that the guest will see. fn logical_size(&mut self) -> DiskFileResult; + /// Returns the physical size of the underlying file. + fn physical_size(&mut self) -> DiskFileResult; fn new_async_io(&self, ring_depth: u32) -> DiskFileResult>; fn topology(&mut self) -> DiskTopology { DiskTopology::default() diff --git a/block/src/fixed_vhd.rs b/block/src/fixed_vhd.rs index 35364906a..aa9bd9530 100644 --- a/block/src/fixed_vhd.rs +++ b/block/src/fixed_vhd.rs @@ -78,6 +78,14 @@ impl BlockBackend for FixedVhd { fn logical_size(&self) -> Result { Ok(self.size) } + + /// Returns the physical size of the underlying file. + fn physical_size(&self) -> Result { + self.file + .metadata() + .map(|m| m.len()) + .map_err(crate::Error::GetFileMetadata) + } } impl Clone for FixedVhd { diff --git a/block/src/fixed_vhd_async.rs b/block/src/fixed_vhd_async.rs index d56c1fafc..df596a616 100644 --- a/block/src/fixed_vhd_async.rs +++ b/block/src/fixed_vhd_async.rs @@ -27,6 +27,10 @@ impl DiskFile for FixedVhdDiskAsync { Ok(self.0.logical_size().unwrap()) } + fn physical_size(&mut self) -> DiskFileResult { + Ok(self.0.physical_size().unwrap()) + } + fn new_async_io(&self, ring_depth: u32) -> DiskFileResult> { Ok(Box::new( FixedVhdAsync::new( diff --git a/block/src/fixed_vhd_sync.rs b/block/src/fixed_vhd_sync.rs index 07702c56e..fd44adfd0 100644 --- a/block/src/fixed_vhd_sync.rs +++ b/block/src/fixed_vhd_sync.rs @@ -27,6 +27,16 @@ impl DiskFile for FixedVhdDiskSync { Ok(self.0.logical_size().unwrap()) } + fn physical_size(&mut self) -> DiskFileResult { + self.0.physical_size().map_err(|e| { + let io_inner = match e { + crate::Error::GetFileMetadata(e) => e, + _ => unreachable!(), + }; + DiskFileError::Size(io_inner) + }) + } + fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult> { Ok(Box::new( FixedVhdSync::new(self.0.as_raw_fd(), self.0.logical_size().unwrap()) diff --git a/block/src/lib.rs b/block/src/lib.rs index 756163afe..34c96eea7 100644 --- a/block/src/lib.rs +++ b/block/src/lib.rs @@ -836,10 +836,12 @@ pub fn detect_image_type(f: &mut File) -> std::io::Result { pub trait BlockBackend: Read + Write + Seek + Send + Debug { /// Returns the logical disk size a guest will see. /// - /// For raw formats, this is equal to the physical_size. For file formats + /// For raw formats, this is equal to [`Self::physical_size`]. For file formats /// that wrap disk images in a container (e.g. QCOW2), this refers to the /// effective size that the guest will see. fn logical_size(&self) -> Result; + /// Returns the physical size of the underlying file. + fn physical_size(&self) -> Result; } #[derive(Debug)] diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index 0293d97b1..5e1ec8d54 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -1811,6 +1811,12 @@ impl BlockBackend for QcowFile { fn logical_size(&self) -> std::result::Result { Ok(self.virtual_size()) } + + fn physical_size(&self) -> std::result::Result { + self.raw_file + .physical_size() + .map_err(crate::Error::GetFileMetadata) + } } // Returns an Error if the given offset doesn't align to a cluster boundary. diff --git a/block/src/qcow/qcow_raw_file.rs b/block/src/qcow/qcow_raw_file.rs index 1b869b459..8d5dba2e4 100644 --- a/block/src/qcow/qcow_raw_file.rs +++ b/block/src/qcow/qcow_raw_file.rs @@ -179,6 +179,10 @@ impl QcowRawFile { self.file.seek(SeekFrom::Start(address))?; self.file.write_all(&data[0..cluster_size]) } + + pub fn physical_size(&self) -> Result { + self.file.metadata().map(|m| m.len()) + } } impl Clone for QcowRawFile { diff --git a/block/src/qcow/raw_file.rs b/block/src/qcow/raw_file.rs index 060fcaa2e..b80a30adc 100644 --- a/block/src/qcow/raw_file.rs +++ b/block/src/qcow/raw_file.rs @@ -357,6 +357,10 @@ impl BlockBackend for RawFile { fn logical_size(&self) -> std::result::Result { Ok(self.metadata().map_err(crate::Error::RawFileError)?.len()) } + + fn physical_size(&self) -> std::result::Result { + Ok(self.metadata().map_err(crate::Error::RawFileError)?.len()) + } } impl Clone for RawFile { diff --git a/block/src/qcow_sync.rs b/block/src/qcow_sync.rs index 241bb5216..0efc106fc 100644 --- a/block/src/qcow_sync.rs +++ b/block/src/qcow_sync.rs @@ -34,6 +34,16 @@ impl DiskFile for QcowDiskSync { .map_err(DiskFileError::Size) } + fn physical_size(&mut self) -> DiskFileResult { + self.qcow_file.physical_size().map_err(|e| { + let io_inner = match e { + crate::Error::GetFileMetadata(e) => e, + _ => unreachable!(), + }; + DiskFileError::Size(io_inner) + }) + } + fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult> { Ok(Box::new(QcowSync::new(self.qcow_file.clone())) as Box) } diff --git a/block/src/raw_async.rs b/block/src/raw_async.rs index 30ff85384..f62c563d2 100644 --- a/block/src/raw_async.rs +++ b/block/src/raw_async.rs @@ -32,6 +32,13 @@ impl DiskFile for RawFileDisk { .map_err(DiskFileError::Size) } + fn physical_size(&mut self) -> DiskFileResult { + self.file + .metadata() + .map(|m| m.len()) + .map_err(DiskFileError::Size) + } + fn new_async_io(&self, ring_depth: u32) -> DiskFileResult> { Ok(Box::new( RawFileAsync::new(self.file.as_raw_fd(), ring_depth) diff --git a/block/src/raw_async_aio.rs b/block/src/raw_async_aio.rs index 1669bb51b..ad41872c7 100644 --- a/block/src/raw_async_aio.rs +++ b/block/src/raw_async_aio.rs @@ -35,6 +35,13 @@ impl DiskFile for RawFileDiskAio { .map_err(DiskFileError::Size) } + fn physical_size(&mut self) -> DiskFileResult { + self.file + .metadata() + .map(|m| m.len()) + .map_err(DiskFileError::Size) + } + fn new_async_io(&self, ring_depth: u32) -> DiskFileResult> { Ok(Box::new( RawFileAsyncAio::new(self.file.as_raw_fd(), ring_depth) diff --git a/block/src/raw_sync.rs b/block/src/raw_sync.rs index b7d652a56..0f9ce0702 100644 --- a/block/src/raw_sync.rs +++ b/block/src/raw_sync.rs @@ -32,6 +32,13 @@ impl DiskFile for RawFileDiskSync { .map_err(DiskFileError::Size) } + fn physical_size(&mut self) -> DiskFileResult { + self.file + .metadata() + .map(|m| m.len()) + .map_err(DiskFileError::Size) + } + fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult> { Ok(Box::new(RawFileSync::new(self.file.as_raw_fd())) as Box) } diff --git a/block/src/vhdx/mod.rs b/block/src/vhdx/mod.rs index 5bcb198f2..f8d404fc5 100644 --- a/block/src/vhdx/mod.rs +++ b/block/src/vhdx/mod.rs @@ -205,6 +205,13 @@ impl BlockBackend for Vhdx { fn logical_size(&self) -> std::result::Result { Ok(self.virtual_disk_size()) } + + fn physical_size(&self) -> std::result::Result { + self.file + .metadata() + .map(|m| m.len()) + .map_err(crate::Error::GetFileMetadata) + } } impl Clone for Vhdx { diff --git a/block/src/vhdx_sync.rs b/block/src/vhdx_sync.rs index cba2ad054..47e7539dc 100644 --- a/block/src/vhdx_sync.rs +++ b/block/src/vhdx_sync.rs @@ -31,6 +31,16 @@ impl DiskFile for VhdxDiskSync { Ok(self.vhdx_file.virtual_disk_size()) } + fn physical_size(&mut self) -> DiskFileResult { + self.vhdx_file.physical_size().map_err(|e| { + let io_inner = match e { + Error::GetFileMetadata(e) => e, + _ => unreachable!(), + }; + DiskFileError::Size(io_inner) + }) + } + fn new_async_io(&self, _ring_depth: u32) -> DiskFileResult> { Ok( Box::new(VhdxSync::new(self.vhdx_file.clone()).map_err(DiskFileError::NewAsyncIo)?)