Blame qemu-fix-qcow2-corruption.patch

Mark McLoughlin e1eede
From: Nolan Leake <nolan <at> sigbus.net>
Mark McLoughlin e1eede
Subject: [PATCH] Fix (at least one cause of) qcow2 corruption.
Mark McLoughlin e1eede
Mark McLoughlin e1eede
qcow2's get_cluster_offset() scans forward in the l2 table to find other
Mark McLoughlin e1eede
clusters that have the same allocation status as the first cluster.
Mark McLoughlin e1eede
This is used by (among others) qcow_is_allocated().
Mark McLoughlin e1eede
Mark McLoughlin e1eede
Unfortunately, it was not checking to be sure that it didn't fall off
Mark McLoughlin e1eede
the end of the l2 table.  This patch adds that check.
Mark McLoughlin e1eede
Mark McLoughlin e1eede
The symptom that motivated me to look into this was that
Mark McLoughlin e1eede
bdrv_is_allocated() was returning false when there was in fact data
Mark McLoughlin e1eede
there.  This is one of many ways this bug could lead to data corruption.
Mark McLoughlin e1eede
Mark McLoughlin e1eede
I checked the other place that scans for consecutive unallocated blocks
Mark McLoughlin e1eede
(alloc_cluster_offset()) and it appears to be OK:
Mark McLoughlin e1eede
    nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
Mark McLoughlin e1eede
appears to prevent the same problem from occurring.
Mark McLoughlin e1eede
Mark McLoughlin e1eede
Signed-off-by: Nolan Leake <nolan <at> sigbus.net>
Mark McLoughlin e1eede
Mark McLoughlin e1eede
---
Mark McLoughlin e1eede
Mark McLoughlin 35588f
From: Kevin Wolf <kwolf@redhat.com>
Mark McLoughlin e1eede
Subject: [PATCH] qcow2 corruption: Fix alloc_cluster_link_l2
Mark McLoughlin 35588f
Mark McLoughlin 35588f
This patch fixes a qcow2 corruption bug introduced in SVN Rev 5861. L2 tables
Mark McLoughlin 35588f
are big endian, so entries must be converted before being passed to functions.
Mark McLoughlin 35588f
Mark McLoughlin 35588f
This bug is easy to trigger. The following script will create and destroy a
Mark McLoughlin 35588f
qcow2 image (the header is gone after three loop iterations):
Mark McLoughlin 35588f
Mark McLoughlin 35588f
    #!/bin/bash
Mark McLoughlin 35588f
    qemu-img create -f qcow2 test.qcow 1M
Mark McLoughlin 35588f
    for i in $(seq 1 10); do
Mark McLoughlin 35588f
    qemu-system-x86_64 -hda test.qcow -monitor stdio > /dev/null 2>&1 <
Mark McLoughlin 35588f
    savevm test-$i
Mark McLoughlin 35588f
    quit
Mark McLoughlin 35588f
    EOF
Mark McLoughlin 35588f
    done
Mark McLoughlin 35588f
Mark McLoughlin 35588f
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Mark McLoughlin 35588f
Mark McLoughlin 35588f
diff -up qemu-kvm-0.10/qemu/block-qcow2.c.qcow2-corruption qemu-kvm-0.10/qemu/block-qcow2.c
Mark McLoughlin e1eede
diff -up qemu-kvm-0.10/qemu/block-qcow2.c.qcow2-corruption qemu-kvm-0.10/qemu/block-qcow2.c
Mark McLoughlin e1eede
--- qemu-kvm-0.10/qemu/block-qcow2.c.qcow2-corruption	2009-04-21 09:57:21.000000000 +0100
Mark McLoughlin e1eede
+++ qemu-kvm-0.10/qemu/block-qcow2.c	2009-04-21 09:58:27.000000000 +0100
Mark McLoughlin e1eede
@@ -670,6 +670,10 @@ static uint64_t get_cluster_offset(Block
Mark McLoughlin e1eede
 
Mark McLoughlin e1eede
     nb_available = (nb_available >> 9) + index_in_cluster;
Mark McLoughlin e1eede
 
Mark McLoughlin e1eede
+    if (nb_needed > nb_available) {
Mark McLoughlin e1eede
+        nb_needed = nb_available;
Mark McLoughlin e1eede
+    }
Mark McLoughlin e1eede
+
Mark McLoughlin e1eede
     cluster_offset = 0;
Mark McLoughlin e1eede
 
Mark McLoughlin e1eede
     /* seek the the l2 offset in the l1 table */
Mark McLoughlin e1eede
@@ -912,7 +916,7 @@ static int alloc_cluster_link_l2(BlockDr
Mark McLoughlin 35588f
         goto err;
Mark McLoughlin 35588f
 
Mark McLoughlin 35588f
     for (i = 0; i < j; i++)
Mark McLoughlin 35588f
-        free_any_clusters(bs, old_cluster[i], 1);
Mark McLoughlin 35588f
+        free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1);
Mark McLoughlin 35588f
 
Mark McLoughlin 35588f
     ret = 0;
Mark McLoughlin 35588f
 err: