Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > aadbe78a25743146bb784eee19f007c5 > files > 62

kvm-83-164.el5_5.9.src.rpm

From 581594deef4c48e6b155adb128018df270c02d9c Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 17 Jun 2009 16:44:52 +0200
Subject: [PATCH 2/2] Add qemu-img check subcommand

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=506017

This patch consists of four commits in upstream qemu:

    qcow2: Fix warnings in check_refcount() (Kevin Wolf)

    This code is currently only compiled when DEBUG_ALLOC is defined, so you
    usually don't see compiler warnings on it. This patch series wants to enable
    the code, so fix the format string warnings first.

    While we're at it, let's print error messages to stderr.

    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7213 c046a42c-6fe2-441c-8c8c-71466251a162

    ---

    Introduce bdrv_check (Kevin Wolf)

    Introduce a new bdrv_check function pointer for block drivers. Modify qcow2 to
    return an error status in check_refcounts(), so it can implement bdrv_check.

    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7214 c046a42c-6fe2-441c-8c8c-71466251a162

    ---

    Introduce qemu-img check subcommand (Kevin Wolf)

    Now that block drivers can provide check functions, expose them through
    qemu-img.

    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7215 c046a42c-6fe2-441c-8c8c-71466251a162

    ---

    qcow2: Add plausibility check for L1/L2 entries (Kevin Wolf)

    All L1 and L2 entries must point at the start of a cluster. If there is some
    offset into the cluster, the entry is corrupted.

    Signed-off-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>

    git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7217 c046a42c-6fe2-441c-8c8c-71466251a162

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <1245249892-28638-1-git-send-email-kwolf@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Bugzilla: 506017
RH-Upstream-status: applied(qemu/master)
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Acked-by: Juan Quintela <quintela@redhat.com>
Acked-by: Gleb Natapov <gleb@redhat.com>
---
 qemu/block-qcow2.c |  109 ++++++++++++++++++++++++++++++++++++++--------------
 qemu/block.c       |   14 +++++++
 qemu/block.h       |    1 +
 qemu/block_int.h   |    3 +
 qemu/qemu-img.c    |   62 +++++++++++++++++++++++++++++
 5 files changed, 160 insertions(+), 29 deletions(-)

diff --git a/qemu/block-qcow2.c b/qemu/block-qcow2.c
index 2f377f3..3e111ad 100644
--- a/qemu/block-qcow2.c
+++ b/qemu/block-qcow2.c
@@ -181,9 +181,7 @@ static int64_t alloc_clusters(BlockDriverState *bs, int64_t size);
 static int64_t alloc_bytes(BlockDriverState *bs, int size);
 static void free_clusters(BlockDriverState *bs,
                           int64_t offset, int64_t size);
-#ifdef DEBUG_ALLOC
-static void check_refcounts(BlockDriverState *bs);
-#endif
+static int check_refcounts(BlockDriverState *bs);
 
 static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
 {
@@ -2609,8 +2607,14 @@ static void update_refcount(BlockDriverState *bs,
     }
 }
 
-#ifdef DEBUG_ALLOC
-static void inc_refcounts(BlockDriverState *bs,
+/*
+ * Increases the refcount for a range of clusters in a given refcount table.
+ * This is used to construct a temporary refcount table out of L1 and L2 tables
+ * which can be compared the the refcount table saved in the image.
+ *
+ * Returns the number of errors in the image that were found
+ */
+static int inc_refcounts(BlockDriverState *bs,
                           uint16_t *refcount_table,
                           int refcount_table_size,
                           int64_t offset, int64_t size)
@@ -2618,9 +2622,10 @@ static void inc_refcounts(BlockDriverState *bs,
     BDRVQcowState *s = bs->opaque;
     int64_t start, last, cluster_offset;
     int k;
+    int errors = 0;
 
     if (size <= 0)
-        return;
+        return 0;
 
     start = offset & ~(s->cluster_size - 1);
     last = (offset + size - 1) & ~(s->cluster_size - 1);
@@ -2628,13 +2633,19 @@ static void inc_refcounts(BlockDriverState *bs,
         cluster_offset += s->cluster_size) {
         k = cluster_offset >> s->cluster_bits;
         if (k < 0 || k >= refcount_table_size) {
-            printf("ERROR: invalid cluster offset=0x%llx\n", cluster_offset);
+            fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n",
+                cluster_offset);
+            errors++;
         } else {
             if (++refcount_table[k] == 0) {
-                printf("ERROR: overflow cluster offset=0x%llx\n", cluster_offset);
+                fprintf(stderr, "ERROR: overflow cluster offset=0x%" PRIx64
+                    "\n", cluster_offset);
+                errors++;
             }
         }
     }
+
+    return errors;
 }
 
 static int check_refcounts_l1(BlockDriverState *bs,
@@ -2646,11 +2657,12 @@ static int check_refcounts_l1(BlockDriverState *bs,
     BDRVQcowState *s = bs->opaque;
     uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2;
     int l2_size, i, j, nb_csectors, refcount;
+    int errors = 0;
 
     l2_table = NULL;
     l1_size2 = l1_size * sizeof(uint64_t);
 
-    inc_refcounts(bs, refcount_table, refcount_table_size,
+    errors += inc_refcounts(bs, refcount_table, refcount_table_size,
                   l1_table_offset, l1_size2);
 
     l1_table = qemu_malloc(l1_size2);
@@ -2672,8 +2684,9 @@ static int check_refcounts_l1(BlockDriverState *bs,
             if (check_copied) {
                 refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits);
                 if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) {
-                    printf("ERROR OFLAG_COPIED: l2_offset=%llx refcount=%d\n",
-                           l2_offset, refcount);
+                    fprintf(stderr, "ERROR OFLAG_COPIED: l2_offset=%" PRIx64
+                        " refcount=%d\n", l2_offset, refcount);
+                    errors++;
                 }
             }
             l2_offset &= ~QCOW_OFLAG_COPIED;
@@ -2684,65 +2697,94 @@ static int check_refcounts_l1(BlockDriverState *bs,
                 if (offset != 0) {
                     if (offset & QCOW_OFLAG_COMPRESSED) {
                         if (offset & QCOW_OFLAG_COPIED) {
-                            printf("ERROR: cluster %lld: copied flag must never be set for compressed clusters\n",
-                                   offset >> s->cluster_bits);
+                            fprintf(stderr, "ERROR: cluster %" PRId64 ": "
+                                "copied flag must never be set for compressed "
+                                "clusters\n", offset >> s->cluster_bits);
                             offset &= ~QCOW_OFLAG_COPIED;
+                            errors++;
                         }
                         nb_csectors = ((offset >> s->csize_shift) &
                                        s->csize_mask) + 1;
                         offset &= s->cluster_offset_mask;
-                        inc_refcounts(bs, refcount_table,
+                        errors += inc_refcounts(bs, refcount_table,
                                       refcount_table_size,
                                       offset & ~511, nb_csectors * 512);
                     } else {
                         if (check_copied) {
                             refcount = get_refcount(bs, (offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits);
                             if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) {
-                                printf("ERROR OFLAG_COPIED: offset=%llx refcount=%d\n",
-                                       offset, refcount);
+                                fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
+                                    PRIx64 " refcount=%d\n", offset, refcount);
+                                errors++;
                             }
                         }
                         offset &= ~QCOW_OFLAG_COPIED;
-                        inc_refcounts(bs, refcount_table,
+                        errors += inc_refcounts(bs, refcount_table,
                                       refcount_table_size,
                                       offset, s->cluster_size);
+
+                        /* Correct offsets are cluster aligned */
+                        if (offset & (s->cluster_size - 1)) {
+                            fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
+                                "properly aligned; L2 entry corrupted.\n", offset);
+                            errors++;
+                        }
                     }
                 }
             }
-            inc_refcounts(bs, refcount_table,
+            errors += inc_refcounts(bs, refcount_table,
                           refcount_table_size,
                           l2_offset,
                           s->cluster_size);
+
+            /* L2 tables are cluster aligned */
+            if (l2_offset & (s->cluster_size - 1)) {
+                fprintf(stderr, "ERROR l2_offset=%" PRIx64 ": Table is not "
+                    "cluster aligned; L1 entry corrupted\n", l2_offset);
+                errors++;
+            }
+
         }
     }
     qemu_free(l1_table);
     qemu_free(l2_table);
-    return 0;
+    return errors;
  fail:
-    printf("ERROR: I/O error in check_refcounts_l1\n");
+    fprintf(stderr, "ERROR: I/O error in check_refcounts_l1\n");
     qemu_free(l1_table);
     qemu_free(l2_table);
     return -EIO;
 }
 
-static void check_refcounts(BlockDriverState *bs)
+/*
+ * Checks an image for refcount consistency.
+ *
+ * Returns 0 if no errors are found, the number of errors in case the image is
+ * detected as corrupted, and -errno when an internal error occured.
+ */
+static int check_refcounts(BlockDriverState *bs)
 {
     BDRVQcowState *s = bs->opaque;
     int64_t size;
     int nb_clusters, refcount1, refcount2, i;
     QCowSnapshot *sn;
     uint16_t *refcount_table;
+    int ret, errors = 0;
 
     size = bdrv_getlength(s->hd);
     nb_clusters = size_to_clusters(s, size);
     refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t));
 
     /* header */
-    inc_refcounts(bs, refcount_table, nb_clusters,
+    errors += inc_refcounts(bs, refcount_table, nb_clusters,
                   0, s->cluster_size);
 
-    check_refcounts_l1(bs, refcount_table, nb_clusters,
+    ret = check_refcounts_l1(bs, refcount_table, nb_clusters,
                        s->l1_table_offset, s->l1_size, 1);
+    if (ret < 0) {
+        return ret;
+    }
+    errors += ret;
 
     /* snapshots */
     for(i = 0; i < s->nb_snapshots; i++) {
@@ -2750,18 +2792,18 @@ static void check_refcounts(BlockDriverState *bs)
         check_refcounts_l1(bs, refcount_table, nb_clusters,
                            sn->l1_table_offset, sn->l1_size, 0);
     }
-    inc_refcounts(bs, refcount_table, nb_clusters,
+    errors += inc_refcounts(bs, refcount_table, nb_clusters,
                   s->snapshots_offset, s->snapshots_size);
 
     /* refcount data */
-    inc_refcounts(bs, refcount_table, nb_clusters,
+    errors += inc_refcounts(bs, refcount_table, nb_clusters,
                   s->refcount_table_offset,
                   s->refcount_table_size * sizeof(uint64_t));
     for(i = 0; i < s->refcount_table_size; i++) {
         int64_t offset;
         offset = s->refcount_table[i];
         if (offset != 0) {
-            inc_refcounts(bs, refcount_table, nb_clusters,
+            errors += inc_refcounts(bs, refcount_table, nb_clusters,
                           offset, s->cluster_size);
         }
     }
@@ -2770,12 +2812,21 @@ static void check_refcounts(BlockDriverState *bs)
     for(i = 0; i < nb_clusters; i++) {
         refcount1 = get_refcount(bs, i);
         refcount2 = refcount_table[i];
-        if (refcount1 != refcount2)
-            printf("ERROR cluster %d refcount=%d reference=%d\n",
+        if (refcount1 != refcount2) {
+            fprintf(stderr, "ERROR cluster %d refcount=%d reference=%d\n",
                    i, refcount1, refcount2);
+            errors++;
+        }
     }
 
     qemu_free(refcount_table);
+
+    return errors;
+}
+
+static int qcow_check(BlockDriverState *bs)
+{
+    return check_refcounts(bs);
 }
 
 #if 0
@@ -2797,7 +2848,6 @@ static void dump_refcounts(BlockDriverState *bs)
     }
 }
 #endif
-#endif
 
 static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf,
                            int64_t pos, int size)
@@ -2854,4 +2904,5 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_get_buffer    = qcow_get_buffer,
 
     .bdrv_create2 = qcow_create2,
+    .bdrv_check = qcow_check,
 };
diff --git a/qemu/block.c b/qemu/block.c
index 5fbd797..d506b83 100644
--- a/qemu/block.c
+++ b/qemu/block.c
@@ -513,6 +513,20 @@ void bdrv_delete(BlockDriverState *bs)
     qemu_free(bs);
 }
 
+/*
+ * Run consistency checks on an image
+ *
+ * Returns the number of errors or -errno when an internal error occurs
+ */
+int bdrv_check(BlockDriverState *bs)
+{
+    if (bs->drv->bdrv_check == NULL) {
+        return -ENOTSUP;
+    }
+
+    return bs->drv->bdrv_check(bs);
+}
+
 /* commit COW file into the raw image */
 int bdrv_commit(BlockDriverState *bs)
 {
diff --git a/qemu/block.h b/qemu/block.h
index 867c463..d93c1c6 100644
--- a/qemu/block.h
+++ b/qemu/block.h
@@ -69,6 +69,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags);
 int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
                BlockDriver *drv);
 void bdrv_close(BlockDriverState *bs);
+int bdrv_check(BlockDriverState *bs);
 int bdrv_read(BlockDriverState *bs, int64_t sector_num,
               uint8_t *buf, int nb_sectors);
 int bdrv_write(BlockDriverState *bs, int64_t sector_num,
diff --git a/qemu/block_int.h b/qemu/block_int.h
index cfbcfaf..1f77cf0 100644
--- a/qemu/block_int.h
+++ b/qemu/block_int.h
@@ -97,6 +97,9 @@ struct BlockDriver {
                         const char *backing_file, const char *backing_format,
                         int flags);
 
+    /* Returns number of errors in image, -errno for internal errors */
+    int (*bdrv_check)(BlockDriverState* bs);
+
     struct BlockDriver *next;
 };
 
diff --git a/qemu/qemu-img.c b/qemu/qemu-img.c
index e0bc14e..21f3a62 100644
--- a/qemu/qemu-img.c
+++ b/qemu/qemu-img.c
@@ -58,6 +58,7 @@ static void help(void)
            "QEMU disk image utility\n"
            "\n"
            "Command syntax:\n"
+           "  check [-f fmt] filename\n"
            "  create [-e] [-6] [-F fmt] [-b base_image] [-f fmt] filename [size]\n"
            "  commit [-f fmt] filename\n"
            "  convert [-c] [-e] [-6] [-f fmt] [-O output_fmt] [-B output_base_image] filename [filename2 [...]] output_filename\n"
@@ -310,6 +311,65 @@ static int img_create(int argc, char **argv)
     return 0;
 }
 
+static int img_check(int argc, char **argv)
+{
+    int c, ret;
+    const char *filename, *fmt;
+    BlockDriver *drv;
+    BlockDriverState *bs;
+
+    fmt = NULL;
+    for(;;) {
+        c = getopt(argc, argv, "f:h");
+        if (c == -1)
+            break;
+        switch(c) {
+        case 'h':
+            help();
+            break;
+        case 'f':
+            fmt = optarg;
+            break;
+        }
+    }
+    if (optind >= argc)
+        help();
+    filename = argv[optind++];
+
+    bs = bdrv_new("");
+    if (!bs)
+        error("Not enough memory");
+    if (fmt) {
+        drv = bdrv_find_format(fmt);
+        if (!drv)
+            error("Unknown file format '%s'", fmt);
+    } else {
+        drv = NULL;
+    }
+    if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
+        error("Could not open '%s'", filename);
+    }
+    ret = bdrv_check(bs);
+    switch(ret) {
+    case 0:
+        printf("No errors were found on the image.\n");
+        break;
+    case -ENOTSUP:
+        error("This image format does not support checks");
+        break;
+    default:
+        if (ret < 0) {
+            error("An error occurred during the check");
+        } else {
+            printf("%d errors were found on the image.\n", ret);
+        }
+        break;
+    }
+
+    bdrv_delete(bs);
+    return 0;
+}
+
 static int img_commit(int argc, char **argv)
 {
     int c, ret;
@@ -878,6 +938,8 @@ int main(int argc, char **argv)
     optind++;
     if (!strcmp(cmd, "create")) {
         img_create(argc, argv);
+    } else if (!strcmp(cmd, "check")) {
+        img_check(argc, argv);
     } else if (!strcmp(cmd, "commit")) {
         img_commit(argc, argv);
     } else if (!strcmp(cmd, "convert")) {
-- 
1.6.3.rc4.29.g8146