From d9b188c1be4457c75438bb86af8756c9178f565c Mon Sep 17 00:00:00 2001 From: Anatol Belski Date: Fri, 17 Apr 2026 19:50:03 +0200 Subject: [PATCH] block: qcow: Add pread_alloc and decompress_cluster helpers Add two reusable helpers for the compressed cluster read path: - pread_alloc(fd, offset, len) allocates a buffer and fills it with pread_exact, returning the owned Vec. - decompress_cluster(compressed, cluster_size, decoder) allocates the output buffer, decodes via the Decoder trait, and validates that the decoder produced exactly cluster_size bytes. These will be used by QcowSync, QcowAsync, Qcow2Backing, and the legacy QcowFile. Signed-off-by: Anatol Belski --- block/src/qcow/mod.rs | 2 +- block/src/qcow_common.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) 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;