Skip to main content

imago/qcow2/
compressed.rs

1//! Support for compressed clusters.
2
3use super::*;
4use crate::io_buffers::IoBuffer;
5use miniz_oxide::inflate::core::{decompress as inflate, DecompressorOxide};
6use miniz_oxide::inflate::TINFLStatus;
7
8#[maybe_async]
9impl<S: Storage + 'static, F: WrappedFormat<S> + 'static> Qcow2<S, F> {
10    /// Read one compressed cluster.
11    ///
12    /// Read the compressed data at `compressed_offset` of length `compressed_length` (which must
13    /// be the values from the L2 compressed cluster descriptor) into a bounce buffer, then
14    /// decompress it into `buf` (which must have a length of exactly one cluster).
15    pub(super) async fn read_compressed_cluster(
16        &self,
17        buf: &mut [u8],
18        compressed_offset: HostOffset,
19        compressed_length: u64,
20    ) -> io::Result<()> {
21        debug_assert!(buf.len() == self.header.cluster_size());
22
23        let storage = self.storage();
24
25        // Must fit (really shouldn’t be compressed if this exceeds the cluster size anyway)
26        let compressed_length = compressed_length.try_into().map_err(io::Error::other)?;
27        let mut compressed_buf = IoBuffer::new(compressed_length, storage.mem_align())?;
28        storage
29            .read(&mut compressed_buf, compressed_offset.0)
30            .await?;
31
32        let mut dec_ox = DecompressorOxide::new();
33        let (status, _read, written) =
34            inflate(&mut dec_ox, compressed_buf.as_ref().into_slice(), buf, 0, 0);
35
36        // Because `compressed_length` will generally exceed the actual length, `HasMoreOutput` is
37        // expected and can be ignored
38        if status != TINFLStatus::Done && status != TINFLStatus::HasMoreOutput {
39            return Err(io::Error::other(format!(
40                "Failed to decompress cluster (host offset {compressed_offset}+{compressed_length}): {status:?}",
41            )));
42        }
43        if written < buf.len() {
44            return Err(io::Error::other(format!(
45                "Failed to decompress cluster (host offset {compressed_offset}+{compressed_length}): Decompressed {written} bytes, expected {}",
46                buf.len(),
47            )));
48        }
49
50        Ok(())
51    }
52}