diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index 4fc4916f3..8c0abb5da 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -5,7 +5,7 @@ // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause pub(crate) mod backing; -mod decoder; +pub(crate) mod decoder; mod header; pub(crate) mod metadata; pub(crate) mod qcow_raw_file; diff --git a/block/src/qcow_common.rs b/block/src/qcow_common.rs index 08a1a9ca3..eeea8bf51 100644 --- a/block/src/qcow_common.rs +++ b/block/src/qcow_common.rs @@ -14,6 +14,8 @@ use std::cmp::min; use std::os::fd::RawFd; use std::{io, ptr, slice}; +use crate::qcow::decoder::Decoder; + // -- Position independent I/O helpers -- // // Duplicated file descriptors share the kernel file description and thus the @@ -44,6 +46,33 @@ pub fn pread_exact(fd: RawFd, buf: &mut [u8], offset: u64) -> io::Result<()> { Ok(()) } +/// Allocate a buffer and pread exactly `len` bytes at `offset`. +pub fn pread_alloc(fd: RawFd, offset: u64, len: usize) -> io::Result> { + let mut buf = vec![0u8; len]; + pread_exact(fd, &mut buf, offset)?; + Ok(buf) +} + +/// Decompress a full QCOW2 cluster from compressed data. +/// +/// Returns a `cluster_size` byte buffer with the decompressed cluster +/// content. Fails if the decoder does not produce exactly `cluster_size` +/// bytes. +pub fn decompress_cluster( + compressed: &[u8], + cluster_size: usize, + decoder: &dyn Decoder, +) -> io::Result> { + let mut decompressed = vec![0u8; cluster_size]; + let n = decoder + .decode(compressed, &mut decompressed) + .map_err(|_| io::Error::from_raw_os_error(libc::EIO))?; + if n != cluster_size { + return Err(io::Error::from_raw_os_error(libc::EIO)); + } + Ok(decompressed) +} + /// Write all bytes to fd at offset, looping on short writes. pub fn pwrite_all(fd: RawFd, buf: &[u8], offset: u64) -> io::Result<()> { let mut total = 0usize;