9ae3a8
From b4dbb84eeca1c793c08b39fe26f3772f200eff36 Mon Sep 17 00:00:00 2001
9ae3a8
From: Max Reitz <mreitz@redhat.com>
9ae3a8
Date: Mon, 4 Nov 2013 22:32:01 +0100
9ae3a8
Subject: [PATCH 08/87] qcow2: Employ metadata overlap checks
9ae3a8
9ae3a8
RH-Author: Max Reitz <mreitz@redhat.com>
9ae3a8
Message-id: <1383604354-12743-11-git-send-email-mreitz@redhat.com>
9ae3a8
Patchwork-id: 55310
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH 10/43] qcow2: Employ metadata overlap checks
9ae3a8
Bugzilla: 1004347
9ae3a8
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Fam Zheng <famz@redhat.com>
9ae3a8
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
9ae3a8
9ae3a8
BZ: 1004347
9ae3a8
9ae3a8
The pre-write overlap check function is now called before most of the
9ae3a8
qcow2 writes (aborting it on collision or other error).
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
(cherry picked from commit cf93980e775b709ec8f33f55846e6dcf1c7a612c)
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
---
9ae3a8
 block/qcow2-cache.c    | 17 +++++++++++++++++
9ae3a8
 block/qcow2-cluster.c  | 21 +++++++++++++++++++++
9ae3a8
 block/qcow2-snapshot.c | 22 ++++++++++++++++++++++
9ae3a8
 block/qcow2.c          | 26 ++++++++++++++++++++++++++
9ae3a8
 4 files changed, 86 insertions(+)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/qcow2-cache.c    |   17 +++++++++++++++++
9ae3a8
 block/qcow2-cluster.c  |   21 +++++++++++++++++++++
9ae3a8
 block/qcow2-snapshot.c |   22 ++++++++++++++++++++++
9ae3a8
 block/qcow2.c          |   26 ++++++++++++++++++++++++++
9ae3a8
 4 files changed, 86 insertions(+), 0 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c
9ae3a8
index 2f3114e..7bcae09 100644
9ae3a8
--- a/block/qcow2-cache.c
9ae3a8
+++ b/block/qcow2-cache.c
9ae3a8
@@ -115,6 +115,23 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
9ae3a8
     }
9ae3a8
 
9ae3a8
     if (c == s->refcount_block_cache) {
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+                QCOW2_OL_DEFAULT & ~QCOW2_OL_REFCOUNT_BLOCK,
9ae3a8
+                c->entries[i].offset, s->cluster_size);
9ae3a8
+    } else if (c == s->l2_table_cache) {
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+                QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2,
9ae3a8
+                c->entries[i].offset, s->cluster_size);
9ae3a8
+    } else {
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+                c->entries[i].offset, s->cluster_size);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return ret;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (c == s->refcount_block_cache) {
9ae3a8
         BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
9ae3a8
     } else if (c == s->l2_table_cache) {
9ae3a8
         BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
9ae3a8
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
9ae3a8
index cca76d4..7c248aa 100644
9ae3a8
--- a/block/qcow2-cluster.c
9ae3a8
+++ b/block/qcow2-cluster.c
9ae3a8
@@ -80,6 +80,14 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    /* the L1 position has not yet been updated, so these clusters must
9ae3a8
+     * indeed be completely free */
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+                                        new_l1_table_offset, new_l1_size2);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
9ae3a8
     for(i = 0; i < s->l1_size; i++)
9ae3a8
         new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
9ae3a8
@@ -149,6 +157,13 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index)
9ae3a8
         buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]);
9ae3a8
     }
9ae3a8
 
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+            QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
9ae3a8
+            s->l1_table_offset + 8 * l1_start_index, sizeof(buf));
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return ret;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE);
9ae3a8
     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index,
9ae3a8
         buf, sizeof(buf));
9ae3a8
@@ -368,6 +383,12 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
9ae3a8
                         &s->aes_encrypt_key);
9ae3a8
     }
9ae3a8
 
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+            cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto out;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
9ae3a8
     ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
9ae3a8
     if (ret < 0) {
9ae3a8
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
9ae3a8
index 0caac90..e7e6013 100644
9ae3a8
--- a/block/qcow2-snapshot.c
9ae3a8
+++ b/block/qcow2-snapshot.c
9ae3a8
@@ -189,6 +189,15 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
9ae3a8
         return ret;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    /* The snapshot list position has not yet been updated, so these clusters
9ae3a8
+     * must indeed be completely free */
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT, offset,
9ae3a8
+                                        s->snapshots_size);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        return ret;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+
9ae3a8
     /* Write all snapshots to the new list */
9ae3a8
     for(i = 0; i < s->nb_snapshots; i++) {
9ae3a8
         sn = s->snapshots + i;
9ae3a8
@@ -363,6 +372,12 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
9ae3a8
         l1_table[i] = cpu_to_be64(s->l1_table[i]);
9ae3a8
     }
9ae3a8
 
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+            sn->l1_table_offset, s->l1_size * sizeof(uint64_t));
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
9ae3a8
                       s->l1_size * sizeof(uint64_t));
9ae3a8
     if (ret < 0) {
9ae3a8
@@ -475,6 +490,13 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+            QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L1,
9ae3a8
+            s->l1_table_offset, cur_l1_bytes);
9ae3a8
+    if (ret < 0) {
9ae3a8
+        goto fail;
9ae3a8
+    }
9ae3a8
+
9ae3a8
     ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
9ae3a8
                            cur_l1_bytes);
9ae3a8
     if (ret < 0) {
9ae3a8
diff --git a/block/qcow2.c b/block/qcow2.c
9ae3a8
index aa9dd23..90950cb 100644
9ae3a8
--- a/block/qcow2.c
9ae3a8
+++ b/block/qcow2.c
9ae3a8
@@ -624,6 +624,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
9ae3a8
     qcow2_free_snapshots(bs);
9ae3a8
     qcow2_refcount_close(bs);
9ae3a8
     g_free(s->l1_table);
9ae3a8
+    /* else pre-write overlap checks in cache_destroy may crash */
9ae3a8
+    s->l1_table = NULL;
9ae3a8
     if (s->l2_table_cache) {
9ae3a8
         qcow2_cache_destroy(bs, s->l2_table_cache);
9ae3a8
     }
9ae3a8
@@ -933,6 +935,13 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
9ae3a8
                 cur_nr_sectors * 512);
9ae3a8
         }
9ae3a8
 
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+                cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE,
9ae3a8
+                cur_nr_sectors * BDRV_SECTOR_SIZE);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
         qemu_co_mutex_unlock(&s->lock);
9ae3a8
         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
9ae3a8
         trace_qcow2_writev_data(qemu_coroutine_self(),
9ae3a8
@@ -999,6 +1008,8 @@ static void qcow2_close(BlockDriverState *bs)
9ae3a8
 {
9ae3a8
     BDRVQcowState *s = bs->opaque;
9ae3a8
     g_free(s->l1_table);
9ae3a8
+    /* else pre-write overlap checks in cache_destroy may crash */
9ae3a8
+    s->l1_table = NULL;
9ae3a8
 
9ae3a8
     qcow2_cache_flush(bs, s->l2_table_cache);
9ae3a8
     qcow2_cache_flush(bs, s->refcount_block_cache);
9ae3a8
@@ -1676,6 +1687,14 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
9ae3a8
 
9ae3a8
     if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
9ae3a8
         /* could not compress: write normal cluster */
9ae3a8
+
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+                sector_num * BDRV_SECTOR_SIZE,
9ae3a8
+                s->cluster_sectors * BDRV_SECTOR_SIZE);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
         ret = bdrv_write(bs, sector_num, buf, s->cluster_sectors);
9ae3a8
         if (ret < 0) {
9ae3a8
             goto fail;
9ae3a8
@@ -1688,6 +1707,13 @@ static int qcow2_write_compressed(BlockDriverState *bs, int64_t sector_num,
9ae3a8
             goto fail;
9ae3a8
         }
9ae3a8
         cluster_offset &= s->cluster_offset_mask;
9ae3a8
+
9ae3a8
+        ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_DEFAULT,
9ae3a8
+                cluster_offset, out_len);
9ae3a8
+        if (ret < 0) {
9ae3a8
+            goto fail;
9ae3a8
+        }
9ae3a8
+
9ae3a8
         BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED);
9ae3a8
         ret = bdrv_pwrite(bs->file, cluster_offset, out_buf, out_len);
9ae3a8
         if (ret < 0) {
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8