9ae3a8
From 7f0500dff4d7da11bda309de3866851f382f6fe8 Mon Sep 17 00:00:00 2001
9ae3a8
From: Max Reitz <mreitz@redhat.com>
9ae3a8
Date: Mon, 4 Nov 2013 22:32:03 +0100
9ae3a8
Subject: [PATCH 10/87] qcow2-refcount: Repair OFLAG_COPIED errors
9ae3a8
9ae3a8
RH-Author: Max Reitz <mreitz@redhat.com>
9ae3a8
Message-id: <1383604354-12743-13-git-send-email-mreitz@redhat.com>
9ae3a8
Patchwork-id: 55312
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH 12/43] qcow2-refcount: Repair OFLAG_COPIED errors
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
Since the OFLAG_COPIED checks are now executed after the refcounts have
9ae3a8
been repaired (if repairing), it is safe to assume that they are correct
9ae3a8
but the OFLAG_COPIED flag may be not. Therefore, if its value differs
9ae3a8
from what it should be (considering the according refcount), that
9ae3a8
discrepancy can be repaired by correctly setting (or clearing that flag.
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9ae3a8
(cherry picked from commit e23e400ec62a03dea58ddb38479b4f1ef86f556d)
9ae3a8
9ae3a8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9ae3a8
---
9ae3a8
 block/qcow2-cluster.c  |  4 ++--
9ae3a8
 block/qcow2-refcount.c | 58 ++++++++++++++++++++++++++++++++++++++++++++------
9ae3a8
 block/qcow2.h          |  1 +
9ae3a8
 3 files changed, 55 insertions(+), 8 deletions(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 block/qcow2-cluster.c  |    4 +-
9ae3a8
 block/qcow2-refcount.c |   58 +++++++++++++++++++++++++++++++++++++++++++-----
9ae3a8
 block/qcow2.h          |    1 +
9ae3a8
 3 files changed, 55 insertions(+), 8 deletions(-)
9ae3a8
9ae3a8
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
9ae3a8
index 7c248aa..2d5aa92 100644
9ae3a8
--- a/block/qcow2-cluster.c
9ae3a8
+++ b/block/qcow2-cluster.c
9ae3a8
@@ -145,7 +145,7 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
9ae3a8
  * and we really don't want bdrv_pread to perform a read-modify-write)
9ae3a8
  */
9ae3a8
 #define L1_ENTRIES_PER_SECTOR (512 / 8)
9ae3a8
-static int write_l1_entry(BlockDriverState *bs, int l1_index)
9ae3a8
+int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index)
9ae3a8
 {
9ae3a8
     BDRVQcowState *s = bs->opaque;
9ae3a8
     uint64_t buf[L1_ENTRIES_PER_SECTOR];
9ae3a8
@@ -254,7 +254,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
9ae3a8
     /* update the L1 entry */
9ae3a8
     trace_qcow2_l2_allocate_write_l1(bs, l1_index);
9ae3a8
     s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
9ae3a8
-    ret = write_l1_entry(bs, l1_index);
9ae3a8
+    ret = qcow2_write_l1_entry(bs, l1_index);
9ae3a8
     if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
9ae3a8
index ddc3029..92ecc64 100644
9ae3a8
--- a/block/qcow2-refcount.c
9ae3a8
+++ b/block/qcow2-refcount.c
9ae3a8
@@ -1207,7 +1207,8 @@ fail:
9ae3a8
  * been already detected and sufficiently signaled by the calling function
9ae3a8
  * (qcow2_check_refcounts) by the time this function is called).
9ae3a8
  */
9ae3a8
-static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res)
9ae3a8
+static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
9ae3a8
+                              BdrvCheckMode fix)
9ae3a8
 {
9ae3a8
     BDRVQcowState *s = bs->opaque;
9ae3a8
     uint64_t *l2_table = qemu_blockalign(bs, s->cluster_size);
9ae3a8
@@ -1218,6 +1219,7 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res)
9ae3a8
     for (i = 0; i < s->l1_size; i++) {
9ae3a8
         uint64_t l1_entry = s->l1_table[i];
9ae3a8
         uint64_t l2_offset = l1_entry & L1E_OFFSET_MASK;
9ae3a8
+        bool l2_dirty = false;
9ae3a8
 
9ae3a8
         if (!l2_offset) {
9ae3a8
             continue;
9ae3a8
@@ -1229,10 +1231,24 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res)
9ae3a8
             continue;
9ae3a8
         }
9ae3a8
         if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
9ae3a8
-            fprintf(stderr, "ERROR OFLAG_COPIED L2 cluster: l1_index=%d "
9ae3a8
+            fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
9ae3a8
                     "l1_entry=%" PRIx64 " refcount=%d\n",
9ae3a8
+                    fix & BDRV_FIX_ERRORS ? "Repairing" :
9ae3a8
+                                            "ERROR",
9ae3a8
                     i, l1_entry, refcount);
9ae3a8
-            res->corruptions++;
9ae3a8
+            if (fix & BDRV_FIX_ERRORS) {
9ae3a8
+                s->l1_table[i] = refcount == 1
9ae3a8
+                               ? l1_entry |  QCOW_OFLAG_COPIED
9ae3a8
+                               : l1_entry & ~QCOW_OFLAG_COPIED;
9ae3a8
+                ret = qcow2_write_l1_entry(bs, i);
9ae3a8
+                if (ret < 0) {
9ae3a8
+                    res->check_errors++;
9ae3a8
+                    goto fail;
9ae3a8
+                }
9ae3a8
+                res->corruptions_fixed++;
9ae3a8
+            } else {
9ae3a8
+                res->corruptions++;
9ae3a8
+            }
9ae3a8
         }
9ae3a8
 
9ae3a8
         ret = bdrv_pread(bs->file, l2_offset, l2_table,
9ae3a8
@@ -1257,13 +1273,43 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res)
9ae3a8
                     continue;
9ae3a8
                 }
9ae3a8
                 if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
9ae3a8
-                    fprintf(stderr, "ERROR OFLAG_COPIED data cluster: "
9ae3a8
+                    fprintf(stderr, "%s OFLAG_COPIED data cluster: "
9ae3a8
                             "l2_entry=%" PRIx64 " refcount=%d\n",
9ae3a8
+                            fix & BDRV_FIX_ERRORS ? "Repairing" :
9ae3a8
+                                                    "ERROR",
9ae3a8
                             l2_entry, refcount);
9ae3a8
-                    res->corruptions++;
9ae3a8
+                    if (fix & BDRV_FIX_ERRORS) {
9ae3a8
+                        l2_table[j] = cpu_to_be64(refcount == 1
9ae3a8
+                                    ? l2_entry |  QCOW_OFLAG_COPIED
9ae3a8
+                                    : l2_entry & ~QCOW_OFLAG_COPIED);
9ae3a8
+                        l2_dirty = true;
9ae3a8
+                        res->corruptions_fixed++;
9ae3a8
+                    } else {
9ae3a8
+                        res->corruptions++;
9ae3a8
+                    }
9ae3a8
                 }
9ae3a8
             }
9ae3a8
         }
9ae3a8
+
9ae3a8
+        if (l2_dirty) {
9ae3a8
+            ret = qcow2_pre_write_overlap_check(bs,
9ae3a8
+                    QCOW2_OL_DEFAULT & ~QCOW2_OL_ACTIVE_L2, l2_offset,
9ae3a8
+                    s->cluster_size);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                fprintf(stderr, "ERROR: Could not write L2 table; metadata "
9ae3a8
+                        "overlap check failed: %s\n", strerror(-ret));
9ae3a8
+                res->check_errors++;
9ae3a8
+                goto fail;
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            ret = bdrv_pwrite(bs->file, l2_offset, l2_table, s->cluster_size);
9ae3a8
+            if (ret < 0) {
9ae3a8
+                fprintf(stderr, "ERROR: Could not write L2 table: %s\n",
9ae3a8
+                        strerror(-ret));
9ae3a8
+                res->check_errors++;
9ae3a8
+                goto fail;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 
9ae3a8
     ret = 0;
9ae3a8
@@ -1409,7 +1455,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
9ae3a8
     }
9ae3a8
 
9ae3a8
     /* check OFLAG_COPIED */
9ae3a8
-    ret = check_oflag_copied(bs, res);
9ae3a8
+    ret = check_oflag_copied(bs, res, fix);
9ae3a8
     if (ret < 0) {
9ae3a8
         goto fail;
9ae3a8
     }
9ae3a8
diff --git a/block/qcow2.h b/block/qcow2.h
9ae3a8
index 86ddb30..10b7bf4 100644
9ae3a8
--- a/block/qcow2.h
9ae3a8
+++ b/block/qcow2.h
9ae3a8
@@ -432,6 +432,7 @@ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int chk, int64_t offset,
9ae3a8
 /* qcow2-cluster.c functions */
9ae3a8
 int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
9ae3a8
                         bool exact_size);
9ae3a8
+int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
9ae3a8
 void qcow2_l2_cache_reset(BlockDriverState *bs);
9ae3a8
 int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
9ae3a8
 void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8