Coverage Report

Created: 2025-12-12 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/crosvm/disk/src/qcow/qcow_raw_file.rs
Line
Count
Source
1
// Copyright 2018 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::fs::File;
6
use std::io;
7
use std::io::BufWriter;
8
use std::io::Read;
9
use std::io::Seek;
10
use std::io::SeekFrom;
11
use std::io::Write;
12
use std::mem::size_of;
13
use std::mem::size_of_val;
14
15
use base::FileReadWriteAtVolatile;
16
use base::VolatileSlice;
17
use base::WriteZeroesAt;
18
use zerocopy::IntoBytes;
19
20
/// A qcow file. Allows reading/writing clusters and appending clusters.
21
#[derive(Debug)]
22
pub struct QcowRawFile {
23
    file: File,
24
    cluster_size: u64,
25
    cluster_mask: u64,
26
}
27
28
impl QcowRawFile {
29
    /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
30
    /// a power of two.
31
1.26k
    pub fn from(file: File, cluster_size: u64) -> Option<Self> {
32
1.26k
        if cluster_size.count_ones() != 1 {
33
0
            return None;
34
1.26k
        }
35
1.26k
        Some(QcowRawFile {
36
1.26k
            file,
37
1.26k
            cluster_size,
38
1.26k
            cluster_mask: cluster_size - 1,
39
1.26k
        })
40
1.26k
    }
41
42
    /// Reads `count` 64 bit offsets and returns them as a vector.
43
    /// `mask` optionally ands out some of the bits on the file.
44
3.31k
    pub fn read_pointer_table(
45
3.31k
        &mut self,
46
3.31k
        offset: u64,
47
3.31k
        count: u64,
48
3.31k
        mask: Option<u64>,
49
3.31k
    ) -> io::Result<Vec<u64>> {
50
3.31k
        let mut table = vec![0; count as usize];
51
3.31k
        self.file.seek(SeekFrom::Start(offset))?;
52
3.27k
        self.file.read_exact(table.as_mut_bytes())?;
53
3.20k
        let mask = mask.unwrap_or(u64::MAX);
54
167M
        for ptr in &mut table {
55
167M
            *ptr = u64::from_be(*ptr) & mask;
56
167M
        }
57
3.20k
        Ok(table)
58
3.31k
    }
59
60
    /// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
61
    /// `mask` optionally ands out some of the bits on the file.
62
453
    pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
63
453
        let count = self.cluster_size / size_of::<u64>() as u64;
64
453
        self.read_pointer_table(offset, count, mask)
65
453
    }
66
67
    /// Writes `table` of u64 pointers to `offset` in the file.
68
    /// `non_zero_flags` will be ORed with all non-zero values in `table`.
69
    /// writing.
70
1.68k
    pub fn write_pointer_table(
71
1.68k
        &mut self,
72
1.68k
        offset: u64,
73
1.68k
        table: &[u64],
74
1.68k
        non_zero_flags: u64,
75
1.68k
    ) -> io::Result<()> {
76
1.68k
        self.file.seek(SeekFrom::Start(offset))?;
77
1.68k
        let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
78
97.5M
        for addr in table {
79
97.5M
            let val = if *addr == 0 {
80
96.4M
                0
81
            } else {
82
1.10M
                *addr | non_zero_flags
83
            };
84
97.5M
            buffer.write_all(&val.to_be_bytes())?;
85
        }
86
1.68k
        buffer.flush()?;
87
1.68k
        Ok(())
88
1.68k
    }
89
90
    /// Read a refcount block from the file and returns a Vec containing the block.
91
    /// Always returns a cluster's worth of data.
92
124k
    pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
93
124k
        let count = self.cluster_size / size_of::<u16>() as u64;
94
124k
        let mut table = vec![0; count as usize];
95
124k
        self.file.seek(SeekFrom::Start(offset))?;
96
124k
        self.file.read_exact(table.as_mut_bytes())?;
97
32.0M
        for refcount in &mut table {
98
31.9M
            *refcount = u16::from_be(*refcount);
99
31.9M
        }
100
124k
        Ok(table)
101
124k
    }
102
103
    /// Writes a refcount block to the file.
104
540k
    pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
105
540k
        self.file.seek(SeekFrom::Start(offset))?;
106
540k
        let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
107
140M
        for count in table {
108
139M
            buffer.write_all(&count.to_be_bytes())?;
109
        }
110
540k
        buffer.flush()?;
111
540k
        Ok(())
112
540k
    }
113
114
    /// Allocates a new cluster at the end of the current file, return the address.
115
1.49k
    pub fn add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>> {
116
        // Determine where the new end of the file should be and set_len, which
117
        // translates to truncate(2).
118
1.49k
        let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
119
1.49k
        let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
120
121
1.49k
        if new_cluster_address > max_valid_cluster_offset {
122
73
            return Ok(None);
123
1.41k
        }
124
125
1.41k
        self.file.set_len(new_cluster_address + self.cluster_size)?;
126
127
1.41k
        Ok(Some(new_cluster_address))
128
1.49k
    }
129
130
    /// Returns a reference to the underlying file.
131
0
    pub fn file(&self) -> &File {
132
0
        &self.file
133
0
    }
134
135
    /// Returns a mutable reference to the underlying file.
136
6.30k
    pub fn file_mut(&mut self) -> &mut File {
137
6.30k
        &mut self.file
138
6.30k
    }
139
140
    /// Returns the size of the file's clusters.
141
4.99k
    pub fn cluster_size(&self) -> u64 {
142
4.99k
        self.cluster_size
143
4.99k
    }
144
145
    /// Returns the offset of `address` within a cluster.
146
1.23k
    pub fn cluster_offset(&self, address: u64) -> u64 {
147
1.23k
        address & self.cluster_mask
148
1.23k
    }
149
150
    /// Zeros out a cluster in the file.
151
160
    pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
152
160
        let cluster_size = self.cluster_size as usize;
153
160
        self.file.write_zeroes_all_at(address, cluster_size)?;
154
160
        Ok(())
155
160
    }
156
157
    /// Writes
158
0
    pub fn write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()> {
159
0
        if (initial_data.len() as u64) < self.cluster_size {
160
0
            return Err(io::Error::new(
161
0
                io::ErrorKind::InvalidInput,
162
0
                "`initial_data` is too small",
163
0
            ));
164
0
        }
165
0
        let volatile_slice = VolatileSlice::new(&mut initial_data[..self.cluster_size as usize]);
166
0
        self.file.write_all_at_volatile(volatile_slice, address)
167
0
    }
168
}