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