/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 | | } |