1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2018 The ChromiumOS Authors 2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file. 4*bb4ee6a4SAndroid Build Coastguard Worker 5*bb4ee6a4SAndroid Build Coastguard Worker use std::io; 6*bb4ee6a4SAndroid Build Coastguard Worker 7*bb4ee6a4SAndroid Build Coastguard Worker use libc::EINVAL; 8*bb4ee6a4SAndroid Build Coastguard Worker use remain::sorted; 9*bb4ee6a4SAndroid Build Coastguard Worker use thiserror::Error; 10*bb4ee6a4SAndroid Build Coastguard Worker 11*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::qcow_raw_file::QcowRawFile; 12*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::CacheMap; 13*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::Cacheable; 14*bb4ee6a4SAndroid Build Coastguard Worker use crate::qcow::vec_cache::VecCache; 15*bb4ee6a4SAndroid Build Coastguard Worker 16*bb4ee6a4SAndroid Build Coastguard Worker #[sorted] 17*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Error, Debug)] 18*bb4ee6a4SAndroid Build Coastguard Worker pub enum Error { 19*bb4ee6a4SAndroid Build Coastguard Worker /// `EvictingCache` - Error writing a refblock from the cache to disk. 20*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to write a refblock from the cache to disk: {0}")] 21*bb4ee6a4SAndroid Build Coastguard Worker EvictingRefCounts(io::Error), 22*bb4ee6a4SAndroid Build Coastguard Worker /// `InvalidIndex` - Address requested isn't within the range of the disk. 23*bb4ee6a4SAndroid Build Coastguard Worker #[error("address requested is not within the range of the disk")] 24*bb4ee6a4SAndroid Build Coastguard Worker InvalidIndex, 25*bb4ee6a4SAndroid Build Coastguard Worker /// `NeedCluster` - Handle this error by reading the cluster and calling the function again. 26*bb4ee6a4SAndroid Build Coastguard Worker #[error("cluster with addr={0} needs to be read")] 27*bb4ee6a4SAndroid Build Coastguard Worker NeedCluster(u64), 28*bb4ee6a4SAndroid Build Coastguard Worker /// `NeedNewCluster` - Handle this error by allocating a cluster and calling the function 29*bb4ee6a4SAndroid Build Coastguard Worker /// again. 30*bb4ee6a4SAndroid Build Coastguard Worker #[error("new cluster needs to be allocated for refcounts")] 31*bb4ee6a4SAndroid Build Coastguard Worker NeedNewCluster, 32*bb4ee6a4SAndroid Build Coastguard Worker /// `ReadingRefCounts` - Error reading the file in to the refcount cache. 33*bb4ee6a4SAndroid Build Coastguard Worker #[error("failed to read the file into the refcount cache: {0}")] 34*bb4ee6a4SAndroid Build Coastguard Worker ReadingRefCounts(io::Error), 35*bb4ee6a4SAndroid Build Coastguard Worker } 36*bb4ee6a4SAndroid Build Coastguard Worker 37*bb4ee6a4SAndroid Build Coastguard Worker pub type Result<T> = std::result::Result<T, Error>; 38*bb4ee6a4SAndroid Build Coastguard Worker 39*bb4ee6a4SAndroid Build Coastguard Worker /// Represents the refcount entries for an open qcow file. 40*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)] 41*bb4ee6a4SAndroid Build Coastguard Worker pub struct RefCount { 42*bb4ee6a4SAndroid Build Coastguard Worker ref_table: VecCache<u64>, 43*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_offset: u64, 44*bb4ee6a4SAndroid Build Coastguard Worker refblock_cache: CacheMap<VecCache<u16>>, 45*bb4ee6a4SAndroid Build Coastguard Worker refcount_block_entries: u64, // number of refcounts in a cluster. 46*bb4ee6a4SAndroid Build Coastguard Worker cluster_size: u64, 47*bb4ee6a4SAndroid Build Coastguard Worker max_valid_cluster_offset: u64, 48*bb4ee6a4SAndroid Build Coastguard Worker } 49*bb4ee6a4SAndroid Build Coastguard Worker 50*bb4ee6a4SAndroid Build Coastguard Worker impl RefCount { 51*bb4ee6a4SAndroid Build Coastguard Worker /// Creates a `RefCount` from `file`, reading the refcount table from `refcount_table_offset`. 52*bb4ee6a4SAndroid Build Coastguard Worker /// `refcount_table_entries` specifies the number of refcount blocks used by this image. 53*bb4ee6a4SAndroid Build Coastguard Worker /// `refcount_block_entries` indicates the number of refcounts in each refcount block. 54*bb4ee6a4SAndroid Build Coastguard Worker /// Each refcount table entry points to a refcount block. new( raw_file: &mut QcowRawFile, refcount_table_offset: u64, refcount_table_entries: u64, refcount_block_entries: u64, cluster_size: u64, ) -> io::Result<RefCount>55*bb4ee6a4SAndroid Build Coastguard Worker pub fn new( 56*bb4ee6a4SAndroid Build Coastguard Worker raw_file: &mut QcowRawFile, 57*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_offset: u64, 58*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_entries: u64, 59*bb4ee6a4SAndroid Build Coastguard Worker refcount_block_entries: u64, 60*bb4ee6a4SAndroid Build Coastguard Worker cluster_size: u64, 61*bb4ee6a4SAndroid Build Coastguard Worker ) -> io::Result<RefCount> { 62*bb4ee6a4SAndroid Build Coastguard Worker let ref_table = VecCache::from_vec(raw_file.read_pointer_table( 63*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_offset, 64*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_entries, 65*bb4ee6a4SAndroid Build Coastguard Worker None, 66*bb4ee6a4SAndroid Build Coastguard Worker )?); 67*bb4ee6a4SAndroid Build Coastguard Worker let max_valid_cluster_index = (ref_table.len() as u64) * refcount_block_entries - 1; 68*bb4ee6a4SAndroid Build Coastguard Worker let max_valid_cluster_offset = max_valid_cluster_index * cluster_size; 69*bb4ee6a4SAndroid Build Coastguard Worker Ok(RefCount { 70*bb4ee6a4SAndroid Build Coastguard Worker ref_table, 71*bb4ee6a4SAndroid Build Coastguard Worker refcount_table_offset, 72*bb4ee6a4SAndroid Build Coastguard Worker refblock_cache: CacheMap::new(50), 73*bb4ee6a4SAndroid Build Coastguard Worker refcount_block_entries, 74*bb4ee6a4SAndroid Build Coastguard Worker cluster_size, 75*bb4ee6a4SAndroid Build Coastguard Worker max_valid_cluster_offset, 76*bb4ee6a4SAndroid Build Coastguard Worker }) 77*bb4ee6a4SAndroid Build Coastguard Worker } 78*bb4ee6a4SAndroid Build Coastguard Worker 79*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the number of refcounts per block. refcounts_per_block(&self) -> u6480*bb4ee6a4SAndroid Build Coastguard Worker pub fn refcounts_per_block(&self) -> u64 { 81*bb4ee6a4SAndroid Build Coastguard Worker self.refcount_block_entries 82*bb4ee6a4SAndroid Build Coastguard Worker } 83*bb4ee6a4SAndroid Build Coastguard Worker 84*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the maximum valid cluster offset in the raw file for this refcount table. max_valid_cluster_offset(&self) -> u6485*bb4ee6a4SAndroid Build Coastguard Worker pub fn max_valid_cluster_offset(&self) -> u64 { 86*bb4ee6a4SAndroid Build Coastguard Worker self.max_valid_cluster_offset 87*bb4ee6a4SAndroid Build Coastguard Worker } 88*bb4ee6a4SAndroid Build Coastguard Worker 89*bb4ee6a4SAndroid Build Coastguard Worker /// Returns `NeedNewCluster` if a new cluster needs to be allocated for refcounts. If an 90*bb4ee6a4SAndroid Build Coastguard Worker /// existing cluster needs to be read, `NeedCluster(addr)` is returned. The Caller should 91*bb4ee6a4SAndroid Build Coastguard Worker /// allocate a cluster or read the required one and call this function again with the cluster. 92*bb4ee6a4SAndroid Build Coastguard Worker /// On success, an optional address of a dropped cluster is returned. The dropped cluster can 93*bb4ee6a4SAndroid Build Coastguard Worker /// be reused for other purposes. set_cluster_refcount( &mut self, raw_file: &mut QcowRawFile, cluster_address: u64, refcount: u16, mut new_cluster: Option<(u64, VecCache<u16>)>, ) -> Result<Option<u64>>94*bb4ee6a4SAndroid Build Coastguard Worker pub fn set_cluster_refcount( 95*bb4ee6a4SAndroid Build Coastguard Worker &mut self, 96*bb4ee6a4SAndroid Build Coastguard Worker raw_file: &mut QcowRawFile, 97*bb4ee6a4SAndroid Build Coastguard Worker cluster_address: u64, 98*bb4ee6a4SAndroid Build Coastguard Worker refcount: u16, 99*bb4ee6a4SAndroid Build Coastguard Worker mut new_cluster: Option<(u64, VecCache<u16>)>, 100*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<Option<u64>> { 101*bb4ee6a4SAndroid Build Coastguard Worker let (table_index, block_index) = self.get_refcount_index(cluster_address); 102*bb4ee6a4SAndroid Build Coastguard Worker 103*bb4ee6a4SAndroid Build Coastguard Worker let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?; 104*bb4ee6a4SAndroid Build Coastguard Worker 105*bb4ee6a4SAndroid Build Coastguard Worker // Fill the cache if this block isn't yet there. 106*bb4ee6a4SAndroid Build Coastguard Worker if !self.refblock_cache.contains_key(&table_index) { 107*bb4ee6a4SAndroid Build Coastguard Worker // Need a new cluster 108*bb4ee6a4SAndroid Build Coastguard Worker if let Some((addr, table)) = new_cluster.take() { 109*bb4ee6a4SAndroid Build Coastguard Worker self.ref_table[table_index] = addr; 110*bb4ee6a4SAndroid Build Coastguard Worker let ref_table = &self.ref_table; 111*bb4ee6a4SAndroid Build Coastguard Worker self.refblock_cache 112*bb4ee6a4SAndroid Build Coastguard Worker .insert(table_index, table, |index, evicted| { 113*bb4ee6a4SAndroid Build Coastguard Worker raw_file.write_refcount_block(ref_table[index], evicted.get_values()) 114*bb4ee6a4SAndroid Build Coastguard Worker }) 115*bb4ee6a4SAndroid Build Coastguard Worker .map_err(Error::EvictingRefCounts)?; 116*bb4ee6a4SAndroid Build Coastguard Worker } else { 117*bb4ee6a4SAndroid Build Coastguard Worker if block_addr_disk == 0 { 118*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::NeedNewCluster); 119*bb4ee6a4SAndroid Build Coastguard Worker } 120*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::NeedCluster(block_addr_disk)); 121*bb4ee6a4SAndroid Build Coastguard Worker } 122*bb4ee6a4SAndroid Build Coastguard Worker } 123*bb4ee6a4SAndroid Build Coastguard Worker 124*bb4ee6a4SAndroid Build Coastguard Worker // Unwrap is safe here as the entry was filled directly above. 125*bb4ee6a4SAndroid Build Coastguard Worker let dropped_cluster = if !self.refblock_cache.get(&table_index).unwrap().dirty() { 126*bb4ee6a4SAndroid Build Coastguard Worker // Free the previously used block and use a new one. Writing modified counts to new 127*bb4ee6a4SAndroid Build Coastguard Worker // blocks keeps the on-disk state consistent even if it's out of date. 128*bb4ee6a4SAndroid Build Coastguard Worker if let Some((addr, _)) = new_cluster.take() { 129*bb4ee6a4SAndroid Build Coastguard Worker self.ref_table[table_index] = addr; 130*bb4ee6a4SAndroid Build Coastguard Worker Some(block_addr_disk) 131*bb4ee6a4SAndroid Build Coastguard Worker } else { 132*bb4ee6a4SAndroid Build Coastguard Worker return Err(Error::NeedNewCluster); 133*bb4ee6a4SAndroid Build Coastguard Worker } 134*bb4ee6a4SAndroid Build Coastguard Worker } else { 135*bb4ee6a4SAndroid Build Coastguard Worker None 136*bb4ee6a4SAndroid Build Coastguard Worker }; 137*bb4ee6a4SAndroid Build Coastguard Worker 138*bb4ee6a4SAndroid Build Coastguard Worker self.refblock_cache.get_mut(&table_index).unwrap()[block_index] = refcount; 139*bb4ee6a4SAndroid Build Coastguard Worker Ok(dropped_cluster) 140*bb4ee6a4SAndroid Build Coastguard Worker } 141*bb4ee6a4SAndroid Build Coastguard Worker 142*bb4ee6a4SAndroid Build Coastguard Worker /// Flush the dirty refcount blocks. This must be done before flushing the table that points to 143*bb4ee6a4SAndroid Build Coastguard Worker /// the blocks. flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()>144*bb4ee6a4SAndroid Build Coastguard Worker pub fn flush_blocks(&mut self, raw_file: &mut QcowRawFile) -> io::Result<()> { 145*bb4ee6a4SAndroid Build Coastguard Worker // Write out all dirty L2 tables. 146*bb4ee6a4SAndroid Build Coastguard Worker for (table_index, block) in self.refblock_cache.iter_mut().filter(|(_k, v)| v.dirty()) { 147*bb4ee6a4SAndroid Build Coastguard Worker let addr = self.ref_table[*table_index]; 148*bb4ee6a4SAndroid Build Coastguard Worker if addr != 0 { 149*bb4ee6a4SAndroid Build Coastguard Worker raw_file.write_refcount_block(addr, block.get_values())?; 150*bb4ee6a4SAndroid Build Coastguard Worker } else { 151*bb4ee6a4SAndroid Build Coastguard Worker return Err(std::io::Error::from_raw_os_error(EINVAL)); 152*bb4ee6a4SAndroid Build Coastguard Worker } 153*bb4ee6a4SAndroid Build Coastguard Worker block.mark_clean(); 154*bb4ee6a4SAndroid Build Coastguard Worker } 155*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 156*bb4ee6a4SAndroid Build Coastguard Worker } 157*bb4ee6a4SAndroid Build Coastguard Worker 158*bb4ee6a4SAndroid Build Coastguard Worker /// Flush the refcount table that keeps the address of the refcounts blocks. 159*bb4ee6a4SAndroid Build Coastguard Worker /// Returns true if the table changed since the previous `flush_table()` call. flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool>160*bb4ee6a4SAndroid Build Coastguard Worker pub fn flush_table(&mut self, raw_file: &mut QcowRawFile) -> io::Result<bool> { 161*bb4ee6a4SAndroid Build Coastguard Worker if self.ref_table.dirty() { 162*bb4ee6a4SAndroid Build Coastguard Worker raw_file.write_pointer_table( 163*bb4ee6a4SAndroid Build Coastguard Worker self.refcount_table_offset, 164*bb4ee6a4SAndroid Build Coastguard Worker self.ref_table.get_values(), 165*bb4ee6a4SAndroid Build Coastguard Worker 0, 166*bb4ee6a4SAndroid Build Coastguard Worker )?; 167*bb4ee6a4SAndroid Build Coastguard Worker self.ref_table.mark_clean(); 168*bb4ee6a4SAndroid Build Coastguard Worker Ok(true) 169*bb4ee6a4SAndroid Build Coastguard Worker } else { 170*bb4ee6a4SAndroid Build Coastguard Worker Ok(false) 171*bb4ee6a4SAndroid Build Coastguard Worker } 172*bb4ee6a4SAndroid Build Coastguard Worker } 173*bb4ee6a4SAndroid Build Coastguard Worker 174*bb4ee6a4SAndroid Build Coastguard Worker /// Gets the refcount for a cluster with the given address. get_cluster_refcount( &mut self, raw_file: &mut QcowRawFile, address: u64, ) -> Result<u16>175*bb4ee6a4SAndroid Build Coastguard Worker pub fn get_cluster_refcount( 176*bb4ee6a4SAndroid Build Coastguard Worker &mut self, 177*bb4ee6a4SAndroid Build Coastguard Worker raw_file: &mut QcowRawFile, 178*bb4ee6a4SAndroid Build Coastguard Worker address: u64, 179*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<u16> { 180*bb4ee6a4SAndroid Build Coastguard Worker let (table_index, block_index) = self.get_refcount_index(address); 181*bb4ee6a4SAndroid Build Coastguard Worker let block_addr_disk = *self.ref_table.get(table_index).ok_or(Error::InvalidIndex)?; 182*bb4ee6a4SAndroid Build Coastguard Worker if block_addr_disk == 0 { 183*bb4ee6a4SAndroid Build Coastguard Worker return Ok(0); 184*bb4ee6a4SAndroid Build Coastguard Worker } 185*bb4ee6a4SAndroid Build Coastguard Worker if !self.refblock_cache.contains_key(&table_index) { 186*bb4ee6a4SAndroid Build Coastguard Worker let table = VecCache::from_vec( 187*bb4ee6a4SAndroid Build Coastguard Worker raw_file 188*bb4ee6a4SAndroid Build Coastguard Worker .read_refcount_block(block_addr_disk) 189*bb4ee6a4SAndroid Build Coastguard Worker .map_err(Error::ReadingRefCounts)?, 190*bb4ee6a4SAndroid Build Coastguard Worker ); 191*bb4ee6a4SAndroid Build Coastguard Worker let ref_table = &self.ref_table; 192*bb4ee6a4SAndroid Build Coastguard Worker self.refblock_cache 193*bb4ee6a4SAndroid Build Coastguard Worker .insert(table_index, table, |index, evicted| { 194*bb4ee6a4SAndroid Build Coastguard Worker raw_file.write_refcount_block(ref_table[index], evicted.get_values()) 195*bb4ee6a4SAndroid Build Coastguard Worker }) 196*bb4ee6a4SAndroid Build Coastguard Worker .map_err(Error::EvictingRefCounts)?; 197*bb4ee6a4SAndroid Build Coastguard Worker } 198*bb4ee6a4SAndroid Build Coastguard Worker Ok(self.refblock_cache.get(&table_index).unwrap()[block_index]) 199*bb4ee6a4SAndroid Build Coastguard Worker } 200*bb4ee6a4SAndroid Build Coastguard Worker 201*bb4ee6a4SAndroid Build Coastguard Worker // Gets the address of the refcount block and the index into the block for the given address. get_refcount_index(&self, address: u64) -> (usize, usize)202*bb4ee6a4SAndroid Build Coastguard Worker fn get_refcount_index(&self, address: u64) -> (usize, usize) { 203*bb4ee6a4SAndroid Build Coastguard Worker let block_index = (address / self.cluster_size) % self.refcount_block_entries; 204*bb4ee6a4SAndroid Build Coastguard Worker let refcount_table_index = (address / self.cluster_size) / self.refcount_block_entries; 205*bb4ee6a4SAndroid Build Coastguard Worker (refcount_table_index as usize, block_index as usize) 206*bb4ee6a4SAndroid Build Coastguard Worker } 207*bb4ee6a4SAndroid Build Coastguard Worker } 208