block: qcow: Add support for zero bit in standard L2 clusters

Implement read support for bit 0 in QCOW2 L2 table entries.
When this flag is set, the cluster reads as zeros without accessing
disk. This improves compatibility with QCOW2 images that use this
optimization.

According to the QCOW2 specification, bit 0 of the standard cluster
descriptor indicates that the cluster reads as zeros. Unlike
l2_entry == 0 indicating a completely unallocated entry, bit 0 can
be set on an allocated cluster.

Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
Anatol Belski 2026-01-21 19:43:57 +01:00 committed by Bo Chen
parent d58e421eea
commit 13198777dd

View file

@ -193,6 +193,7 @@ const V3_BARE_HEADER_SIZE: u32 = 104;
const L1_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
const L2_TABLE_OFFSET_MASK: u64 = 0x00ff_ffff_ffff_fe00;
// Flags
const ZERO_FLAG: u64 = 1 << 0;
const COMPRESSED_FLAG: u64 = 1 << 62;
const COMPRESSED_SECTOR_SIZE: u64 = 512;
const CLUSTER_USED_FLAG: u64 = 1 << 63;
@ -217,6 +218,11 @@ fn l2_entry_is_empty(l2_entry: u64) -> bool {
l2_entry == 0
}
// Check bit 0 - only valid for standard clusters.
fn l2_entry_is_zero(l2_entry: u64) -> bool {
l2_entry & ZERO_FLAG != 0
}
fn l2_entry_is_compressed(l2_entry: u64) -> bool {
l2_entry & COMPRESSED_FLAG != 0
}
@ -1333,6 +1339,9 @@ impl QcowFile {
return Err(err_inval);
}
buf[..count].copy_from_slice(&decompressed_cluster[start..end.unwrap()]);
} else if l2_entry_is_zero(l2_entry) {
// Cluster with zero flag reads as zeros without accessing disk.
return Ok(None);
} else {
let start = l2_entry_std_cluster_addr(l2_entry) + self.raw_file.cluster_offset(address);
let raw_file = self.raw_file.file_mut();