summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Wolf <kwolf@redhat.com>2010-05-28 12:05:45 +0200
committerAurelien Jarno <aurelien@aurel32.net>2010-06-09 18:36:09 +0200
commitff9e1776172a6533e7501eda67a7411fe6988eb6 (patch)
treed215433d80cde26b3885e165d8d025cb22977ff6
parentblock: Fix multiwrite with overlapping requests (diff)
downloadqemu-kvm-ff9e1776172a6533e7501eda67a7411fe6988eb6.tar.gz
qemu-kvm-ff9e1776172a6533e7501eda67a7411fe6988eb6.tar.bz2
qemu-kvm-ff9e1776172a6533e7501eda67a7411fe6988eb6.zip
qcow2: Fix corruption after refblock allocation
Refblock allocation code needs to take into consideration that update_refcount will load a different refcount block into the cache, so it must initialize the cache for a new refcount block only afterwards. Not doing this means that not only the refcount in the wrong block is updated, but also that the caller will work on the wrong block. Signed-off-by: Kevin Wolf <kwolf@redhat.com> (cherry picked from commit 25408c09502be036e5575754fe54019ed4ed5dfa)
-rw-r--r--block/qcow2-refcount.c11
1 files changed, 9 insertions, 2 deletions
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 5ebbcb63d..fa78e46ee 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -215,8 +215,6 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
/* Allocate the refcount block itself and mark it as used */
uint64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
- memset(s->refcount_block_cache, 0, s->cluster_size);
- s->refcount_block_cache_offset = new_block;
#ifdef DEBUG_ALLOC2
fprintf(stderr, "qcow2: Allocate refcount block %d for %" PRIx64
@@ -225,6 +223,10 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
#endif
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
+ /* Zero the new refcount block before updating it */
+ memset(s->refcount_block_cache, 0, s->cluster_size);
+ s->refcount_block_cache_offset = new_block;
+
/* The block describes itself, need to update the cache */
int block_index = (new_block >> s->cluster_bits) &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
@@ -236,6 +238,11 @@ static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
if (ret < 0) {
goto fail_block;
}
+
+ /* Initialize the new refcount block only after updating its refcount,
+ * update_refcount uses the refcount cache itself */
+ memset(s->refcount_block_cache, 0, s->cluster_size);
+ s->refcount_block_cache_offset = new_block;
}
/* Now the new refcount block needs to be written to disk */