• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 use base::FileReadWriteAtVolatile;
15 use base::WriteZeroesAt;
16 use data_model::VolatileSlice;
17 
18 /// A qcow file. Allows reading/writing clusters and appending clusters.
19 #[derive(Debug)]
20 pub struct QcowRawFile {
21     file: File,
22     cluster_size: u64,
23     cluster_mask: u64,
24 }
25 
26 impl QcowRawFile {
27     /// Creates a `QcowRawFile` from the given `File`, `None` is returned if `cluster_size` is not
28     /// a power of two.
from(file: File, cluster_size: u64) -> Option<Self>29     pub fn from(file: File, cluster_size: u64) -> Option<Self> {
30         if cluster_size.count_ones() != 1 {
31             return None;
32         }
33         Some(QcowRawFile {
34             file,
35             cluster_size,
36             cluster_mask: cluster_size - 1,
37         })
38     }
39 
40     /// Reads `count` 64 bit offsets and returns them as a vector.
41     /// `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>>42     pub fn read_pointer_table(
43         &mut self,
44         offset: u64,
45         count: u64,
46         mask: Option<u64>,
47     ) -> io::Result<Vec<u64>> {
48         let mut table = vec![0; count as usize];
49         self.file.seek(SeekFrom::Start(offset))?;
50         let mask = mask.unwrap_or(u64::max_value());
51         for ptr in &mut table {
52             let mut value = [0u8; 8];
53             self.file.read_exact(&mut value)?;
54             *ptr = u64::from_be_bytes(value) & mask;
55         }
56         Ok(table)
57     }
58 
59     /// Reads a cluster's worth of 64 bit offsets and returns them as a vector.
60     /// `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>>61     pub fn read_pointer_cluster(&mut self, offset: u64, mask: Option<u64>) -> io::Result<Vec<u64>> {
62         let count = self.cluster_size / size_of::<u64>() as u64;
63         self.read_pointer_table(offset, count, mask)
64     }
65 
66     /// Writes `table` of u64 pointers to `offset` in the file.
67     /// `non_zero_flags` will be ORed with all non-zero values in `table`.
68     /// writing.
write_pointer_table( &mut self, offset: u64, table: &[u64], non_zero_flags: u64, ) -> io::Result<()>69     pub fn write_pointer_table(
70         &mut self,
71         offset: u64,
72         table: &[u64],
73         non_zero_flags: u64,
74     ) -> io::Result<()> {
75         self.file.seek(SeekFrom::Start(offset))?;
76         let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u64>(), &self.file);
77         for addr in table {
78             let val = if *addr == 0 {
79                 0
80             } else {
81                 *addr | non_zero_flags
82             };
83             buffer.write_all(&val.to_be_bytes())?;
84         }
85         Ok(())
86     }
87 
88     /// Read a refcount block from the file and returns a Vec containing the block.
89     /// Always returns a cluster's worth of data.
read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>>90     pub fn read_refcount_block(&mut self, offset: u64) -> io::Result<Vec<u16>> {
91         let count = self.cluster_size / size_of::<u16>() as u64;
92         let mut table = vec![0; count as usize];
93         self.file.seek(SeekFrom::Start(offset))?;
94         for refcount in &mut table {
95             let mut value = [0u8; 2];
96             self.file.read_exact(&mut value)?;
97             *refcount = u16::from_be_bytes(value);
98         }
99         Ok(table)
100     }
101 
102     /// Writes a refcount block to the file.
write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()>103     pub fn write_refcount_block(&mut self, offset: u64, table: &[u16]) -> io::Result<()> {
104         self.file.seek(SeekFrom::Start(offset))?;
105         let mut buffer = BufWriter::with_capacity(table.len() * size_of::<u16>(), &self.file);
106         for count in table {
107             buffer.write_all(&count.to_be_bytes())?;
108         }
109         Ok(())
110     }
111 
112     /// 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>>113     pub fn add_cluster_end(&mut self, max_valid_cluster_offset: u64) -> io::Result<Option<u64>> {
114         // Determine where the new end of the file should be and set_len, which
115         // translates to truncate(2).
116         let file_end: u64 = self.file.seek(SeekFrom::End(0))?;
117         let new_cluster_address: u64 = (file_end + self.cluster_size - 1) & !self.cluster_mask;
118 
119         if new_cluster_address > max_valid_cluster_offset {
120             return Ok(None);
121         }
122 
123         self.file.set_len(new_cluster_address + self.cluster_size)?;
124 
125         Ok(Some(new_cluster_address))
126     }
127 
128     /// Returns a reference to the underlying file.
file(&self) -> &File129     pub fn file(&self) -> &File {
130         &self.file
131     }
132 
133     /// Returns a mutable reference to the underlying file.
file_mut(&mut self) -> &mut File134     pub fn file_mut(&mut self) -> &mut File {
135         &mut self.file
136     }
137 
138     /// Returns the size of the file's clusters.
cluster_size(&self) -> u64139     pub fn cluster_size(&self) -> u64 {
140         self.cluster_size
141     }
142 
143     /// Returns the offset of `address` within a cluster.
cluster_offset(&self, address: u64) -> u64144     pub fn cluster_offset(&self, address: u64) -> u64 {
145         address & self.cluster_mask
146     }
147 
148     /// Zeros out a cluster in the file.
zero_cluster(&mut self, address: u64) -> io::Result<()>149     pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> {
150         let cluster_size = self.cluster_size as usize;
151         self.file.write_zeroes_all_at(address, cluster_size)?;
152         Ok(())
153     }
154 
155     /// Writes
write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()>156     pub fn write_cluster(&mut self, address: u64, mut initial_data: Vec<u8>) -> io::Result<()> {
157         if (initial_data.len() as u64) < self.cluster_size {
158             return Err(io::Error::new(
159                 io::ErrorKind::InvalidInput,
160                 "`initial_data` is too small",
161             ));
162         }
163         let volatile_slice = VolatileSlice::new(&mut initial_data[..self.cluster_size as usize]);
164         self.file.write_all_at_volatile(volatile_slice, address)
165     }
166 }
167