Skip to main content

imago/qcow2/
io_func.rs

1//! Special I/O functions.
2//!
3//! Most of I/O should be implemented in the generic
4//! [`imago::format::access`](crate::format::access) module, but some I/O needs to be done directly
5//! by image drivers (like handling compression).
6
7use super::*;
8use crate::io_buffers::IoBuffer;
9
10#[maybe_async]
11impl<S: Storage, F: WrappedFormat<S>> Qcow2<S, F> {
12    /// Read the special range at `offset`.
13    ///
14    /// Currently, the only special range we have are compressed clusters.
15    ///
16    /// If the range turns out not to be compressed (anymore) from some point on (e.g. because a
17    /// concurrent write has changed its mapping), stop there and return that unhandled tail of
18    /// `bufv` (which has not been written to) and its offset.
19    pub(super) async fn do_readv_special<'a>(
20        &self,
21        mut bufv: IoVectorMut<'a>,
22        mut offset: GuestOffset,
23    ) -> io::Result<Option<(IoVectorMut<'a>, u64)>> {
24        let mut saved_l2_table: Option<Arc<L2Table>> = None;
25        let cb = self.header.cluster_bits();
26
27        // Do everything cluster by cluster.
28        while !bufv.is_empty() {
29            let l2_table = if let Some(saved) = saved_l2_table.as_ref() {
30                saved
31            } else {
32                let Some(new_l2) = self.get_l2(offset, false).await? else {
33                    // No L2 table, so not compressed
34                    return Ok(Some((bufv, offset.0)));
35                };
36                saved_l2_table.get_or_insert(new_l2)
37            };
38
39            // Concurrent writes may replace the compressed mapping and free the compressed data,
40            // which may then be overwritten by something else; the guard prevents modifications
41            // until we are done reading
42            let guest_cluster = offset.cluster(cb);
43            let Some(compressed_guard) = l2_table.lock_compressed_read(guest_cluster).await? else {
44                return Ok(Some((bufv, offset.0)));
45            };
46
47            let chunk_length = offset.remaining_in_cluster(cb);
48            let (chunk, remainder) = bufv.split_at(chunk_length);
49            bufv = remainder;
50
51            let mut bounce_buffer_and_chunk = None;
52            let need_bounce_buffer = chunk.buffer_count() != 1
53                || offset.in_cluster_offset(cb) != 0
54                || chunk.len() != self.header.cluster_size() as u64;
55
56            let slice = if need_bounce_buffer {
57                let bounce_buffer = IoBuffer::new(self.header.cluster_size(), 1)?;
58                bounce_buffer_and_chunk = Some((bounce_buffer, chunk));
59                bounce_buffer_and_chunk.as_mut().unwrap().0.as_mut()
60            } else {
61                chunk.into_inner().pop().unwrap().into()
62            };
63
64            compressed_guard
65                .read_compressed(self.storage(), slice.into_slice())
66                .await?;
67            drop(compressed_guard);
68
69            if let Some((bounce_buffer, mut chunk)) = bounce_buffer_and_chunk {
70                let ofs = offset.in_cluster_offset(cb);
71                let end = ofs + chunk.len() as usize;
72                chunk.copy_from_slice(bounce_buffer.as_ref_range(ofs..end).into_slice());
73            }
74
75            let next_cluster = if let Some(next) = guest_cluster.next_in_l2(cb) {
76                next
77            } else {
78                saved_l2_table.take();
79                guest_cluster.first_in_next_l2(cb)
80            };
81            offset = next_cluster.offset(cb);
82        }
83
84        Ok(None)
85    }
86}