diff --git a/block/src/qcow/mod.rs b/block/src/qcow/mod.rs
index aebeeae64..77824039b 100644
--- a/block/src/qcow/mod.rs
+++ b/block/src/qcow/mod.rs
@@ -186,7 +186,7 @@ const MAX_CLUSTER_BITS: u32 = 21;
// This easily covers 1 TB files. When support for bigger files is needed the assumptions made to
// keep these tables in RAM needs to be thrown out.
const MAX_RAM_POINTER_TABLE_SIZE: u64 = 35_000_000;
-// Only support 2 byte refcounts, 2^refcount_order bits.
+// 16-bit refcounts.
const DEFAULT_REFCOUNT_ORDER: u32 = 4;
const V2_BARE_HEADER_SIZE: u32 = 72;
@@ -682,12 +682,10 @@ impl QcowHeader {
fn max_refcount_clusters(refcount_order: u32, cluster_size: u32, num_clusters: u32) -> u64 {
// Use u64 as the product of the u32 inputs can overflow.
- let refcount_bytes = (0x01 << u64::from(refcount_order)) / 8;
- let for_data = div_round_up_u64(
- u64::from(num_clusters) * refcount_bytes,
- u64::from(cluster_size),
- );
- let for_refcounts = div_round_up_u64(for_data * refcount_bytes, u64::from(cluster_size));
+ let refcount_bits = 0x01u64 << u64::from(refcount_order);
+ let cluster_bits = u64::from(cluster_size) * 8;
+ let for_data = div_round_up_u64(u64::from(num_clusters) * refcount_bits, cluster_bits);
+ let for_refcounts = div_round_up_u64(for_data * refcount_bits, cluster_bits);
for_data + for_refcounts
}
@@ -849,14 +847,13 @@ impl QcowFile {
let backing_file =
BackingFile::new(header.backing_file.as_ref(), direct_io, max_nesting_depth)?;
- // Only support two byte refcounts.
+ // Validate refcount order to be 0..6
let refcount_bits: u64 = 0x01u64
.checked_shl(header.refcount_order)
.ok_or(Error::UnsupportedRefcountOrder)?;
- if refcount_bits != 16 {
+ if refcount_bits > 64 {
return Err(Error::UnsupportedRefcountOrder);
}
- let refcount_bytes = refcount_bits.div_ceil(8);
// Need at least one refcount cluster
if header.refcount_table_clusters == 0 {
@@ -891,8 +888,8 @@ impl QcowFile {
refcount_rebuild_required = true;
}
- let mut raw_file =
- QcowRawFile::from(file, cluster_size).ok_or(Error::InvalidClusterSize)?;
+ let mut raw_file = QcowRawFile::from(file, cluster_size, refcount_bits)
+ .ok_or(Error::InvalidClusterSize)?;
if refcount_rebuild_required {
QcowFile::rebuild_refcounts(&mut raw_file, header.clone())?;
}
@@ -928,7 +925,7 @@ impl QcowFile {
if l1_clusters + refcount_clusters > MAX_RAM_POINTER_TABLE_SIZE {
return Err(Error::TooManyRefcounts(refcount_clusters));
}
- let refcount_block_entries = cluster_size / refcount_bytes;
+ let refcount_block_entries = cluster_size * 8 / refcount_bits;
let refcounts = RefCount::new(
&mut raw_file,
header.refcount_table_offset,
@@ -1067,7 +1064,7 @@ impl QcowFile {
}
/// Returns the `index`th refcount block from the file.
- pub fn refcount_block(&mut self, index: usize) -> Result