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