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