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 <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski
2026-04-17 19:50:03 +02:00
committed by Rob Bradford
parent 78a05ab0c1
commit d9b188c1be
2 changed files with 30 additions and 1 deletions

View File

@@ -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;

View File

@@ -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<Vec<u8>> {
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<Vec<u8>> {
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;