mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
block: qcow: Set OFLAG_COPIED bit in L1 entries for spec compliance
The OFLAG_COPIED bit (bit 63) indicates a cluster's refcount is exactly 1 and doesn't need copy-on-write. This bit must be set in L1 entries when their referenced L2 clusters have refcount=1. Previously, L1 entries were always written as raw addresses without the OFLAG_COPIED bit, violating the QCOW2 specification and causing qemu-img check to report errors like `ERROR OFLAG_COPIED L2 cluster: l1_index=X .... refcount=1` The implementation queries each L2 cluster's refcount in sync_caches() and sets OFLAG_COPIED appropriately when writing the L1 table. This ensures QCOW2 images are specification compliant and maintain correct COW semantics to avoid data corruption. Signed-off-by: Anatol Belski <anbelski@linux.microsoft.com>
This commit is contained in:
committed by
Rob Bradford
parent
85556951a6
commit
6897c2a462
@@ -207,6 +207,11 @@ fn l2_entry_make_std(cluster_addr: u64) -> u64 {
|
||||
(cluster_addr & L2_TABLE_OFFSET_MASK) | CLUSTER_USED_FLAG
|
||||
}
|
||||
|
||||
// Make L1 entry with optional flags
|
||||
fn l1_entry_make(cluster_addr: u64, refcount_is_one: bool) -> u64 {
|
||||
(cluster_addr & L1_TABLE_OFFSET_MASK) | (refcount_is_one as u64 * CLUSTER_USED_FLAG)
|
||||
}
|
||||
|
||||
/// Contains the information from the header of a qcow file.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QcowHeader {
|
||||
@@ -1571,11 +1576,25 @@ impl QcowFile {
|
||||
// Push L1 table and refcount table last as all the clusters they point to are now
|
||||
// guaranteed to be valid.
|
||||
let mut sync_required = if self.l1_table.dirty() {
|
||||
self.raw_file.write_pointer_table(
|
||||
self.header.l1_table_offset,
|
||||
self.l1_table.get_values(),
|
||||
0,
|
||||
)?;
|
||||
// Build L1 table with OFLAG_COPIED bits set correctly based on L2 cluster refcounts
|
||||
let l1_active: Vec<u64> = self
|
||||
.l1_table
|
||||
.get_values()
|
||||
.iter()
|
||||
.map(|&l2_addr| {
|
||||
if l2_addr == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
let refcount = self
|
||||
.refcounts
|
||||
.get_cluster_refcount(&mut self.raw_file, l2_addr)
|
||||
.map_err(|e| std::io::Error::other(Error::GettingRefcount(e)))?;
|
||||
Ok(l1_entry_make(l2_addr, refcount == 1))
|
||||
}
|
||||
})
|
||||
.collect::<io::Result<Vec<u64>>>()?;
|
||||
self.raw_file
|
||||
.write_pointer_table(self.header.l1_table_offset, &l1_active, 0)?;
|
||||
self.l1_table.mark_clean();
|
||||
true
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user