05bba0
From 735513ff089e56379c2cfd290c5b3e1f1a13cbcc Mon Sep 17 00:00:00 2001
05bba0
From: Max Reitz <mreitz@redhat.com>
05bba0
Date: Sat, 13 Jun 2015 16:22:12 +0200
05bba0
Subject: [PATCH 18/42] qcow2: Split qcow2_check_refcounts()
05bba0
MIME-Version: 1.0
05bba0
Content-Type: text/plain; charset=UTF-8
05bba0
Content-Transfer-Encoding: 8bit
05bba0
05bba0
Message-id: <1434212556-3927-19-git-send-email-mreitz@redhat.com>
05bba0
Patchwork-id: 66037
05bba0
O-Subject: [RHEL-7.2 qemu-kvm PATCH 18/42] qcow2: Split qcow2_check_refcounts()
05bba0
Bugzilla: 1129893
05bba0
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
05bba0
RH-Acked-by: Fam Zheng <famz@redhat.com>
05bba0
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
05bba0
05bba0
BZ: 1129893
05bba0
05bba0
Put the code for calculating the reference counts and comparing them
05bba0
during qemu-img check into own functions.
05bba0
05bba0
Signed-off-by: Max Reitz <mreitz@redhat.com>
05bba0
Reviewed-by: BenoƮt Canet <benoit.canet@nodalink.com>
05bba0
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
05bba0
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
05bba0
(cherry picked from commit 6ca56bf5e90aa167395727667d17c699950c545c)
05bba0
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
05bba0
05bba0
Conflicts:
05bba0
	block/qcow2-refcount.c
05bba0
05bba0
5839e53bbc0fec56021d758aab7610df421ed8c8 is missing downstream, which
05bba0
replaces g*_malloc*() by g*_new*() and g*_realloc() by g*_renew() all
05bba0
over the code base. Not backporting that commit results in only two
05bba0
conflicts which are trivial to resolve; backporting it would mean
05bba0
touching the whole code base, which I think is worse.
05bba0
05bba0
Signed-off-by: Max Reitz <mreitz@redhat.com>
05bba0
---
05bba0
 block/qcow2-refcount.c | 151 +++++++++++++++++++++++++++++++++----------------
05bba0
 1 file changed, 103 insertions(+), 48 deletions(-)
05bba0
05bba0
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
05bba0
index 0c5a6ad..08b2b62 100644
05bba0
--- a/block/qcow2-refcount.c
05bba0
+++ b/block/qcow2-refcount.c
05bba0
@@ -1501,67 +1501,70 @@ fail:
05bba0
     return new_offset;
05bba0
 }
05bba0
 
05bba0
+static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
+                           BdrvCheckMode fix, uint16_t **refcount_table,
05bba0
+                           int64_t *nb_clusters);
05bba0
+
05bba0
 /*
05bba0
- * Checks an image for refcount consistency.
05bba0
- *
05bba0
- * Returns 0 if no errors are found, the number of errors in case the image is
05bba0
- * detected as corrupted, and -errno when an internal error occurred.
05bba0
+ * Calculates an in-memory refcount table.
05bba0
  */
05bba0
-int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
-                          BdrvCheckMode fix)
05bba0
+static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
+                               BdrvCheckMode fix, uint16_t **refcount_table,
05bba0
+                               int64_t *nb_clusters)
05bba0
 {
05bba0
     BDRVQcowState *s = bs->opaque;
05bba0
-    int64_t size, i, highest_cluster, nb_clusters;
05bba0
-    int refcount1, refcount2;
05bba0
+    int64_t i;
05bba0
     QCowSnapshot *sn;
05bba0
-    uint16_t *refcount_table;
05bba0
     int ret;
05bba0
 
05bba0
-    size = bdrv_getlength(bs->file);
05bba0
-    if (size < 0) {
05bba0
+    *refcount_table = g_try_new0(uint16_t, *nb_clusters);
05bba0
+    if (*nb_clusters && *refcount_table == NULL) {
05bba0
         res->check_errors++;
05bba0
-        return size;
05bba0
+        return -ENOMEM;
05bba0
     }
05bba0
 
05bba0
-    nb_clusters = size_to_clusters(s, size);
05bba0
-    if (nb_clusters > INT_MAX) {
05bba0
-        res->check_errors++;
05bba0
-        return -EFBIG;
05bba0
-    }
05bba0
-
05bba0
-    refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
05bba0
-
05bba0
-    res->bfi.total_clusters =
05bba0
-        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
05bba0
-
05bba0
     /* header */
05bba0
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
05bba0
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
05bba0
         0, s->cluster_size);
05bba0
 
05bba0
     /* current L1 table */
05bba0
-    ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
05bba0
+    ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
05bba0
                              s->l1_table_offset, s->l1_size, CHECK_FRAG_INFO);
05bba0
     if (ret < 0) {
05bba0
-        goto fail;
05bba0
+        return ret;
05bba0
     }
05bba0
 
05bba0
     /* snapshots */
05bba0
-    for(i = 0; i < s->nb_snapshots; i++) {
05bba0
+    for (i = 0; i < s->nb_snapshots; i++) {
05bba0
         sn = s->snapshots + i;
05bba0
-        ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
05bba0
+        ret = check_refcounts_l1(bs, res, *refcount_table, *nb_clusters,
05bba0
             sn->l1_table_offset, sn->l1_size, 0);
05bba0
         if (ret < 0) {
05bba0
-            goto fail;
05bba0
+            return ret;
05bba0
         }
05bba0
     }
05bba0
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
05bba0
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
05bba0
         s->snapshots_offset, s->snapshots_size);
05bba0
 
05bba0
     /* refcount data */
05bba0
-    inc_refcounts(bs, res, refcount_table, nb_clusters,
05bba0
+    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
05bba0
         s->refcount_table_offset,
05bba0
         s->refcount_table_size * sizeof(uint64_t));
05bba0
 
05bba0
+    return check_refblocks(bs, res, fix, refcount_table, nb_clusters);
05bba0
+}
05bba0
+
05bba0
+/*
05bba0
+ * Checks consistency of refblocks and accounts for each refblock in
05bba0
+ * *refcount_table.
05bba0
+ */
05bba0
+static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
+                           BdrvCheckMode fix, uint16_t **refcount_table,
05bba0
+                           int64_t *nb_clusters)
05bba0
+{
05bba0
+    BDRVQcowState *s = bs->opaque;
05bba0
+    int64_t i;
05bba0
+
05bba0
     for(i = 0; i < s->refcount_table_size; i++) {
05bba0
         uint64_t offset, cluster;
05bba0
         offset = s->refcount_table[i];
05bba0
@@ -1575,7 +1578,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
             continue;
05bba0
         }
05bba0
 
05bba0
-        if (cluster >= nb_clusters) {
05bba0
+        if (cluster >= *nb_clusters) {
05bba0
             fprintf(stderr, "ERROR refcount block %" PRId64
05bba0
                     " is outside image\n", i);
05bba0
             res->corruptions++;
05bba0
@@ -1583,14 +1586,14 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
         }
05bba0
 
05bba0
         if (offset != 0) {
05bba0
-            inc_refcounts(bs, res, refcount_table, nb_clusters,
05bba0
+            inc_refcounts(bs, res, *refcount_table, *nb_clusters,
05bba0
                 offset, s->cluster_size);
05bba0
-            if (refcount_table[cluster] != 1) {
05bba0
+            if ((*refcount_table)[cluster] != 1) {
05bba0
                 fprintf(stderr, "%s refcount block %" PRId64
05bba0
                     " refcount=%d\n",
05bba0
                     fix & BDRV_FIX_ERRORS ? "Repairing" :
05bba0
                                             "ERROR",
05bba0
-                    i, refcount_table[cluster]);
05bba0
+                    i, (*refcount_table)[cluster]);
05bba0
 
05bba0
                 if (fix & BDRV_FIX_ERRORS) {
05bba0
                     int64_t new_offset;
05bba0
@@ -1602,17 +1605,18 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
                     }
05bba0
 
05bba0
                     /* update refcounts */
05bba0
-                    if ((new_offset >> s->cluster_bits) >= nb_clusters) {
05bba0
+                    if ((new_offset >> s->cluster_bits) >= *nb_clusters) {
05bba0
                         /* increase refcount_table size if necessary */
05bba0
-                        int old_nb_clusters = nb_clusters;
05bba0
-                        nb_clusters = (new_offset >> s->cluster_bits) + 1;
05bba0
-                        refcount_table = g_realloc(refcount_table,
05bba0
-                                nb_clusters * sizeof(uint16_t));
05bba0
-                        memset(&refcount_table[old_nb_clusters], 0, (nb_clusters
05bba0
-                                - old_nb_clusters) * sizeof(uint16_t));
05bba0
+                        int old_nb_clusters = *nb_clusters;
05bba0
+                        *nb_clusters = (new_offset >> s->cluster_bits) + 1;
05bba0
+                        *refcount_table = g_renew(uint16_t, *refcount_table,
05bba0
+                                                  *nb_clusters);
05bba0
+                        memset(&(*refcount_table)[old_nb_clusters], 0,
05bba0
+                               (*nb_clusters - old_nb_clusters) *
05bba0
+                               sizeof(uint16_t));
05bba0
                     }
05bba0
-                    refcount_table[cluster]--;
05bba0
-                    inc_refcounts(bs, res, refcount_table, nb_clusters,
05bba0
+                    (*refcount_table)[cluster]--;
05bba0
+                    inc_refcounts(bs, res, *refcount_table, *nb_clusters,
05bba0
                             new_offset, s->cluster_size);
05bba0
 
05bba0
                     res->corruptions_fixed++;
05bba0
@@ -1623,8 +1627,22 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
         }
05bba0
     }
05bba0
 
05bba0
-    /* compare ref counts */
05bba0
-    for (i = 0, highest_cluster = 0; i < nb_clusters; i++) {
05bba0
+    return 0;
05bba0
+}
05bba0
+
05bba0
+/*
05bba0
+ * Compares the actual reference count for each cluster in the image against the
05bba0
+ * refcount as reported by the refcount structures on-disk.
05bba0
+ */
05bba0
+static void compare_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
+                              BdrvCheckMode fix, int64_t *highest_cluster,
05bba0
+                              uint16_t *refcount_table, int64_t nb_clusters)
05bba0
+{
05bba0
+    BDRVQcowState *s = bs->opaque;
05bba0
+    int64_t i;
05bba0
+    int refcount1, refcount2, ret;
05bba0
+
05bba0
+    for (i = 0, *highest_cluster = 0; i < nb_clusters; i++) {
05bba0
         refcount1 = get_refcount(bs, i);
05bba0
         if (refcount1 < 0) {
05bba0
             fprintf(stderr, "Can't get refcount for cluster %" PRId64 ": %s\n",
05bba0
@@ -1636,11 +1654,10 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
         refcount2 = refcount_table[i];
05bba0
 
05bba0
         if (refcount1 > 0 || refcount2 > 0) {
05bba0
-            highest_cluster = i;
05bba0
+            *highest_cluster = i;
05bba0
         }
05bba0
 
05bba0
         if (refcount1 != refcount2) {
05bba0
-
05bba0
             /* Check if we're allowed to fix the mismatch */
05bba0
             int *num_fixed = NULL;
05bba0
             if (refcount1 > refcount2 && (fix & BDRV_FIX_LEAKS)) {
05bba0
@@ -1673,6 +1690,44 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
             }
05bba0
         }
05bba0
     }
05bba0
+}
05bba0
+
05bba0
+/*
05bba0
+ * Checks an image for refcount consistency.
05bba0
+ *
05bba0
+ * Returns 0 if no errors are found, the number of errors in case the image is
05bba0
+ * detected as corrupted, and -errno when an internal error occurred.
05bba0
+ */
05bba0
+int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
05bba0
+                          BdrvCheckMode fix)
05bba0
+{
05bba0
+    BDRVQcowState *s = bs->opaque;
05bba0
+    int64_t size, highest_cluster, nb_clusters;
05bba0
+    uint16_t *refcount_table;
05bba0
+    int ret;
05bba0
+
05bba0
+    size = bdrv_getlength(bs->file);
05bba0
+    if (size < 0) {
05bba0
+        res->check_errors++;
05bba0
+        return size;
05bba0
+    }
05bba0
+
05bba0
+    nb_clusters = size_to_clusters(s, size);
05bba0
+    if (nb_clusters > INT_MAX) {
05bba0
+        res->check_errors++;
05bba0
+        return -EFBIG;
05bba0
+    }
05bba0
+
05bba0
+    res->bfi.total_clusters =
05bba0
+        size_to_clusters(s, bs->total_sectors * BDRV_SECTOR_SIZE);
05bba0
+
05bba0
+    ret = calculate_refcounts(bs, res, fix, &refcount_table, &nb_clusters);
05bba0
+    if (ret < 0) {
05bba0
+        goto fail;
05bba0
+    }
05bba0
+
05bba0
+    compare_refcounts(bs, res, fix, &highest_cluster, refcount_table,
05bba0
+                      nb_clusters);
05bba0
 
05bba0
     /* check OFLAG_COPIED */
05bba0
     ret = check_oflag_copied(bs, res, fix);
05bba0
-- 
05bba0
1.8.3.1
05bba0