diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs index 400e30a82..6effc468f 100644 --- a/block/src/qcow/mod.rs +++ b/block/src/qcow/mod.rs @@ -1204,6 +1204,10 @@ impl QcowFile { let l2_entry = self.l2_cache.get(l1_index).unwrap()[l2_index]; let cluster_addr = if l2_entry_is_compressed(l2_entry) { // Writing to compressed cluster. + + let (compressed_cluster_addr, compressed_cluster_size) = + l2_entry_compressed_cluster_layout(l2_entry, self.header.cluster_bits); + // Allocate new cluster, decompress into new cluster, then use // offset of new cluster. let decompressed_cluster = self.decompress_l2_cluster(l2_entry)?; @@ -1216,6 +1220,27 @@ impl QcowFile { if nwritten != decompressed_cluster.len() { return Err(std::io::Error::from_raw_os_error(EIO)); } + + // Decrement refcount for each cluster spanned by the old compressed data + let compressed_clusters_end = self.raw_file.cluster_address( + compressed_cluster_addr // Start of compressed data + + compressed_cluster_size as u64 // Add size to get end address + + self.raw_file.cluster_size() + - 1, // Catch possibly partially used last cluster + ); + let mut addr = self.raw_file.cluster_address(compressed_cluster_addr); + while addr < compressed_clusters_end { + let refcount = self + .refcounts + .get_cluster_refcount(&mut self.raw_file, addr) + .map_err(|e| std::io::Error::other(Error::GettingRefcount(e)))?; + if refcount > 0 { + let mut newly_unref = self.set_cluster_refcount(addr, refcount - 1)?; + self.unref_clusters.append(&mut newly_unref); + } + addr += self.raw_file.cluster_size(); + } + cluster_addr } else if l2_entry_is_empty(l2_entry) { let initial_data = if let Some(backing) = self.backing_file.as_mut() { diff --git a/block/src/qcow/qcow_raw_file.rs b/block/src/qcow/qcow_raw_file.rs index d3a9ad7ab..1d9dbc0b5 100644 --- a/block/src/qcow/qcow_raw_file.rs +++ b/block/src/qcow/qcow_raw_file.rs @@ -136,6 +136,11 @@ impl QcowRawFile { address & self.cluster_mask } + /// Returns the base address of the cluster containing `address`. + pub fn cluster_address(&self, address: u64) -> u64 { + address & !self.cluster_mask + } + /// Zeros out a cluster in the file. pub fn zero_cluster(&mut self, address: u64) -> io::Result<()> { let cluster_size = self.cluster_size as usize;