From d77504bee48dd182609b44935b1314a5ae050a33 Mon Sep 17 00:00:00 2001
From: Max Reitz <mreitz@redhat.com>
Date: Fri, 2 May 2014 16:58:47 -0500
Subject: [PATCH 15/26] qcow2: Switch L1 table in a single sequence

RH-Author: Max Reitz <mreitz@redhat.com>
Message-id: <1399049936-13496-16-git-send-email-mreitz@redhat.com>
Patchwork-id: 58661
O-Subject: [RHEL-6.6 qemu-kvm PATCH v3 15/24] qcow2: Switch L1 table in a single sequence
Bugzilla: 1004420
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>

BZ: 1004420

Switching the L1 table in memory should be an atomic operation, as far
as possible. Calling qcow2_free_clusters on the old L1 table on disk is
not a good idea when the old L1 table is no longer valid and the address
to the new one hasn't yet been written into the corresponding
BDRVQcowState field. To be more specific, this can lead to segfaults due
to qcow2_check_metadata_overlap trying to access the L1 table during the
free operation.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit fda74f826baec78d685e5a87fd8a95bfb7bb2243)

Conflicts:
	block/qcow2-cluster.c

qcow2_free_clusters() does not take a "type" parameter downstream.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2-cluster.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Signed-off-by: Jeff E. Nelson <jen@redhat.com>
---
 block/qcow2-cluster.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 6679c33..391c132 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -33,6 +33,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size)
     BDRVQcowState *s = bs->opaque;
     int new_l1_size2, ret, i;
     uint64_t *new_l1_table;
+    int64_t old_l1_table_offset, old_l1_size;
     int64_t new_l1_table_offset, new_l1_size;
     uint8_t data[12];
 
@@ -98,10 +99,12 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size)
         goto fail;
     }
     g_free(s->l1_table);
-    qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t));
+    old_l1_table_offset = s->l1_table_offset;
     s->l1_table_offset = new_l1_table_offset;
     s->l1_table = new_l1_table;
+    old_l1_size = s->l1_size;
     s->l1_size = new_l1_size;
+    qcow2_free_clusters(bs, old_l1_table_offset, old_l1_size * sizeof(uint64_t));
     return 0;
  fail:
     g_free(new_l1_table);
-- 
1.7.1