From 459ab7806a3abbb002d6462b99a574f20d6cba31 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 1 Jun 2010 11:03:03 -0300 Subject: [PATCH 01/13] qcow2: Fix corruption after refblock allocation RH-Author: Kevin Wolf Message-id: <1275390183-14081-1-git-send-email-kwolf@redhat.com> Patchwork-id: 9643 O-Subject: [RHEL-6 qemu-kvm PATCH] qcow2: Fix corruption after refblock allocation Bugzilla: 598407 RH-Acked-by: Eduardo Habkost RH-Acked-by: Jes Sorensen RH-Acked-by: Juan Quintela Bugzilla: 598407 Upstream status: Submitted 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 (cherry picked from commit 25136a82998850811c55ecfc5d6118a0e89f7fbd) --- block/qcow2-refcount.c | 11 +++++++++-- 1 files changed, 9 insertions(+), 2 deletions(-) Signed-off-by: Eduardo Habkost --- block/qcow2-refcount.c | 11 +++++++++-- 1 files changed, 9 insertions(+), 2 deletions(-) diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index a7aa4e6..1fc0aa1 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -221,8 +221,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 @@ -231,6 +229,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); @@ -242,6 +244,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 */ -- 1.7.0.3