xref: /aosp_15_r20/external/crosvm/disk/src/qcow/qcow_raw_file.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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::AsBytes;
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.
from(file: File, cluster_size: u64) -> Option<Self>31     pub fn from(file: File, cluster_size: u64) -> Option<Self> {
32         if cluster_size.count_ones() != 1 {
33             return None;
34         }
35         Some(QcowRawFile {
36             file,
37             cluster_size,
38             cluster_mask: cluster_size - 1,
39         })
40     }
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.
read_pointer_table( &mut self, offset: u64, count: u64, mask: Option<u64>, ) -> io::Result<Vec<u64>>44     pub fn read_pointer_table(
45         &mut self,
46         offset: u64,
47         count: u64,
48         mask: Option<u64>,
49     ) -> io::Result<Vec<u64>> {
50         let mut table = vec![0; count as usize];
51         self.file.seek(SeekFrom::Start(offset))?;
52         self.file.read_exact(table.as_bytes_mut())?;
53         let mask = mask.unwrap_or(u64::MAX);
54         for ptr in &mut table {
55             *ptr = u64::from_be(*ptr) & mask;
56         }
57         Ok(table)
58     }
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.
read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>>62     pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
63         let count = self.cluster_size / size_of::<u64>() as u64;
64         self.read_pointer_table(offset, count, mask)
65     }
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.
write_pointer_table( &mut self, offset: u64, table: &[u64], non_zero_flags: u64, ) -> io::Result<()>70     pub fn write_pointer_table(
71         &mut self,
72         offset: u64,
73         table: &[u64],
74         non_zero_flags: u64,
75     ) -> io::Result<()> {
76         self.file.seek(SeekFrom::Start(offset))?;
77         let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
78         for addr in table {
79             let val = if *addr == 0 {
80                 0
81             } else {
82                 *addr | non_zero_flags
83             };
84             buffer.write_all(&val.to_be_bytes())?;
85         }
86         buffer.flush()?;
87         Ok(())
88     }
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.
read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>>92     pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
93         let count = self.cluster_size / size_of::<u16>() as u64;
94         let mut table = vec![0; count as usize];
95         self.file.seek(SeekFrom::Start(offset))?;
96         self.file.read_exact(table.as_bytes_mut())?;
97         for refcount in &mut table {
98             *refcount = u16::from_be(*refcount);
99         }
100         Ok(table)
101     }
102 
103     /// Writes a refcount block to the file.
write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()>104     pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
105         self.file.seek(SeekFrom::Start(offset))?;
106         let mut buffer = BufWriter::with_capacity(size_of_val(table), &self.file);
107         for count in table {
108             buffer.write_all(&count.to_be_bytes())?;
109         }
110         buffer.flush()?;
111         Ok(())
112     }
113 
114     /// Allocates a new cluster at the end of the current file, return the address.
add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>>115     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         let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
119         let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
120 
121         if new_cluster_address > max_valid_cluster_offset {
122             return Ok(None);
123         }
124 
125         self.file.set_len(new_cluster_address + self.cluster_size)?;
126 
127         Ok(Some(new_cluster_address))
128     }
129 
130     /// Returns a reference to the underlying file.
file(&self) -> &File131     pub fn file(&self) -> &File {
132         &self.file
133     }
134 
135     /// Returns a mutable reference to the underlying file.
file_mut(&mut self) -> &mut File136     pub fn file_mut(&mut self) -> &mut File {
137         &mut self.file
138     }
139 
140     /// Returns the size of the file's clusters.
cluster_size(&self) -> u64141     pub fn cluster_size(&self) -> u64 {
142         self.cluster_size
143     }
144 
145     /// Returns the offset of `address` within a cluster.
cluster_offset(&self, address: u64) -> u64146     pub fn cluster_offset(&self, address: u64) -> u64 {
147         address & self.cluster_mask
148     }
149 
150     /// Zeros out a cluster in the file.
zero_cluster(&mut self, address: u64) -> io::Result<()>151     pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
152         let cluster_size = self.cluster_size as usize;
153         self.file.write_zeroes_all_at(address, cluster_size)?;
154         Ok(())
155     }
156 
157     /// Writes
write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()>158     pub fn write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()> {
159         if (initial_data.len() as u64) < self.cluster_size {
160             return Err(io::Error::new(
161                 io::ErrorKind::InvalidInput,
162                 "`initial_data` is too small",
163             ));
164         }
165         let volatile_slice = VolatileSlice::new(&mut initial_data[..self.cluster_size as usize]);
166         self.file.write_all_at_volatile(volatile_slice, address)
167     }
168 }
169