All of lore.kernel.org
 help / color / mirror / Atom feed
From: Miroslav Rezanina <mrezanin@redhat.com>
To: qemu-devel@nongnu.org
Cc: Paolo Bonzini <pbonzini@redhat.com>
Subject: [Qemu-devel] [PATCH][RFC] Add compare subcommand for qemu-img
Date: Wed, 1 Aug 2012 06:03:43 -0400 (EDT)	[thread overview]
Message-ID: <490157254.6767622.1343815423950.JavaMail.root@redhat.com> (raw)
In-Reply-To: <1736118218.6697488.1343814369561.JavaMail.root@redhat.com>

This patch adds compare subcommand that compares two images. Compare has following criteria:
- only data part is compared
- unallocated sectors are not read
- in case of different image size, exceeding part of bigger disk has to be zeroed/unallocated to compare rest
- qemu-img returns:
   - 0 if images are identical
   - 1 if images differ
   - 2 on error

Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>

Patch:
--
diff --git a/block.c b/block.c
index b38940b..919f8e9 100644
--- a/block.c
+++ b/block.c
@@ -2284,6 +2284,7 @@ int bdrv_has_zero_init(BlockDriverState *bs)
 
 typedef struct BdrvCoIsAllocatedData {
     BlockDriverState *bs;
+    BlockDriverState *base;
     int64_t sector_num;
     int nb_sectors;
     int *pnum;
@@ -2414,6 +2415,44 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
     return 0;
 }
 
+/* Coroutine wrapper for bdrv_is_allocated_above() */
+static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque)
+{
+    BdrvCoIsAllocatedData *data = opaque;
+    BlockDriverState *top = data->bs;
+    BlockDriverState *base = data->base;
+
+    data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num,
+                                           data->nb_sectors,data->pnum);
+    data->done = true;
+}
+
+/*
+ * Synchronous wrapper around bdrv_co_is_allocated_above().
+ *
+ * See bdrv_co_is_allocated_above() for details.
+ */
+int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
+                      int64_t sector_num, int nb_sectors, int *pnum)
+{
+    Coroutine *co;
+    BdrvCoIsAllocatedData data = {
+        .bs = top,
+        .base = base,
+        .sector_num = sector_num,
+        .nb_sectors = nb_sectors,
+        .pnum = pnum,
+        .done = false,
+    };
+
+    co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry);
+    qemu_coroutine_enter(co, &data);
+    while (!data.done) {
+        qemu_aio_wait();
+    }
+    return data.ret;
+}
+
 BlockInfoList *qmp_query_block(Error **errp)
 {
     BlockInfoList *head = NULL, *cur_item = NULL;
diff --git a/block.h b/block.h
index c89590d..6f39da9 100644
--- a/block.h
+++ b/block.h
@@ -256,7 +256,8 @@ int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
 int bdrv_has_zero_init(BlockDriverState *bs);
 int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
                       int *pnum);
-
+int bdrv_is_allocated_above(BlockDriverState* top, BlockDriverState *base,
+                            int64_t sector_num, int nb_sectors, int *pnum);
 void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
                        BlockErrorAction on_write_error);
 BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read);
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 39419a0..5b34896 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -27,6 +27,12 @@ STEXI
 @item commit [-f @var{fmt}] [-t @var{cache}] @var{filename}
 ETEXI
 
+DEF("compare", img_compare,
+    "compare [-f fmt] [-g fmt] [-p] filename1 filename2")
+STEXI
+@item compare [-f fmt] [-g fmt] [-p] filename1 filename2
+ETEXI
+
 DEF("convert", img_convert,
     "convert [-c] [-p] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
 STEXI
diff --git a/qemu-img.c b/qemu-img.c
index 80cfb9b..99a2f16 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -96,7 +96,9 @@ static void help(void)
            "  '-a' applies a snapshot (revert disk to saved state)\n"
            "  '-c' creates a snapshot\n"
            "  '-d' deletes a snapshot\n"
-           "  '-l' lists all snapshots in the given image\n";
+           "  '-l' lists all snapshots in the given image\n"
+           "Parameters to compare subcommand:\n"
+           "  '-g' Second image format (in case it differs from first image)\n";
 
     printf("%s\nSupported formats:", help_msg);
     bdrv_iterate_format(format_print, NULL);
@@ -652,6 +654,223 @@ static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n,
 
 #define IO_BUF_SIZE (2 * 1024 * 1024)
 
+/*
+ * Get number of sectors that can be stored in IO buffer.
+ */
+
+static int64_t sectors_to_process(int64_t total, int64_t from)
+{
+  int64_t rv = total - from;
+
+  if (rv > (IO_BUF_SIZE >> BDRV_SECTOR_BITS))
+     return IO_BUF_SIZE >> BDRV_SECTOR_BITS;
+
+  return rv;
+}
+
+/*
+ * Compares two images. Exit codes:
+ *
+ * 0 - Images are identical
+ * 1 - Images differ
+ * 2 - Error occured
+ */
+
+static int img_compare(int argc, char **argv)
+{
+    const char *fmt1 = NULL, *fmt2 = NULL, *filename1, *filename2;
+    BlockDriverState *bs1, *bs2;
+    int64_t total_sectors1, total_sectors2;
+    uint8_t *buf1 = NULL, *buf2 = NULL;
+    int pnum1, pnum2;
+    int allocated1,allocated2;
+    int flags = BDRV_O_FLAGS;
+    int progress=0,ret = 0; /* return value - 0 Ident, 1 Different, 2 Error */
+    int64_t total_sectors;
+    int64_t sector_num = 0;
+    int64_t nb_sectors;
+    int c, rv, pnum;
+    uint64_t bs_sectors;
+    uint64_t progress_base;
+
+
+    for(;;) {
+        c = getopt(argc, argv, "pf:g:");
+        if (c == -1) {
+            break;
+        }
+        switch(c) {
+        case 'f':
+            fmt1 = optarg;
+            if (fmt2 == NULL)
+                fmt2 = optarg;
+            break;
+        case 'g':
+            fmt2 = optarg;
+            break;
+        case 'p':
+            progress = 1;
+            break;
+        }
+    }
+    if (optind >= argc) {
+        help();
+        goto out3;
+    }
+    filename1 = argv[optind++];
+    filename2 = argv[optind++];
+
+    /* Initialize before goto out */
+    qemu_progress_init(progress, 2.0);
+
+    bs1 = bdrv_new_open(filename1, fmt1, flags);
+    if (!bs1) {
+      error_report("Can't open file %s", filename1);
+      ret = 2;
+      goto out3;
+    }
+
+    bs2 = bdrv_new_open(filename2, fmt2, flags);
+    if (!bs2) {
+      error_report("Can't open file %s:",filename2);
+      ret = 2;
+      goto out2;
+    }
+
+    qemu_progress_print(0, 100);
+
+    buf1 = qemu_blockalign(bs1,IO_BUF_SIZE);
+    buf2 = qemu_blockalign(bs2,IO_BUF_SIZE);
+    bdrv_get_geometry(bs1,&bs_sectors);
+    total_sectors1 = bs_sectors;
+    bdrv_get_geometry(bs2,&bs_sectors);
+    total_sectors2 = bs_sectors;
+    total_sectors = total_sectors1;
+    progress_base = total_sectors;
+
+    if (total_sectors1 != total_sectors2) {
+        BlockDriverState *bsover;
+        int64_t over_sectors, over_num;
+        const char *filename_over;
+
+        if (total_sectors1 > total_sectors2) {
+            total_sectors = total_sectors2;
+            over_sectors = total_sectors1;
+            over_num = total_sectors2;
+            bsover = bs1;
+            filename_over = filename1;
+        } else {
+            total_sectors = total_sectors1;
+            over_sectors = total_sectors2;
+            over_num = total_sectors1;
+            bsover = bs1;
+            filename_over = filename2;
+        }
+
+        progress_base = over_sectors;
+
+        for (;;) {
+            if ((nb_sectors = sectors_to_process(over_sectors,over_num)) <= 0)
+              break;
+
+            rv = bdrv_is_allocated(bsover,over_num,nb_sectors,&pnum);
+            nb_sectors = pnum;
+            if (rv) {
+               rv = bdrv_read(bsover, over_num, buf1, nb_sectors);
+               if (rv < 0) {
+                   error_report("error while reading sector %" PRId64 " of %s:"
+                                " %s",over_num, filename_over, strerror(-rv));
+                   ret = 2;
+                   goto out;
+               }
+               rv = is_allocated_sectors(buf1, nb_sectors, &pnum);
+               if (rv || pnum != nb_sectors) {
+                   ret = 1;
+                   printf("Images are different - image size mismatch!\n");
+                   goto out;
+               }
+            }
+            over_num += nb_sectors;
+            qemu_progress_print(((float) nb_sectors / progress_base)*100, 100);
+        }
+    }
+
+    for (;;) {
+        if ((nb_sectors = sectors_to_process(total_sectors, sector_num)) <= 0)
+            break;
+        allocated1 = bdrv_is_allocated_above(bs1, NULL, sector_num, nb_sectors,
+                                             &pnum1);
+        allocated2 = bdrv_is_allocated_above(bs2, NULL, sector_num, nb_sectors,
+                                             &pnum2);
+        if (pnum1 < pnum2) {
+            nb_sectors = pnum1;
+        } else {
+            nb_sectors = pnum2;
+        }
+
+        if (allocated1 == allocated2) {
+            if (allocated1) {
+              rv = bdrv_read(bs1, sector_num, buf1, nb_sectors);
+              if (rv < 0) {
+                  ret = 2;
+                  error_report("error while reading sector %" PRId64 " of %s:"
+                               " %s", sector_num, filename1, strerror(-rv));
+                  goto out;
+              }
+              rv = bdrv_read(bs2,sector_num,buf2, nb_sectors);
+              if (rv < 0) {
+                  ret = 2;
+                  error_report("error while reading sector %" PRId64 " of %s:"
+                               " %s", sector_num, filename2, strerror(-rv));
+                  goto out;
+              }
+              rv = compare_sectors(buf1,buf2,nb_sectors,&pnum);
+              if (rv || pnum != nb_sectors) {
+                  ret = 1;
+                  printf("Images are different - content mismatch!\n");
+                  goto out;
+              }
+            }
+        } else {
+           BlockDriverState *bstmp;
+           const char* filenametmp;
+           if (allocated1) {
+               bstmp = bs1;
+               filenametmp = filename1;
+           } else {
+               bstmp = bs2;
+               filenametmp = filename2;
+           }
+           rv = bdrv_read(bstmp, sector_num, buf1, nb_sectors);
+           if (rv < 0) {
+               ret = 2;
+               error_report("error while reading sector %" PRId64 " of %s: %s",
+                               sector_num, filenametmp, strerror(-rv));
+               goto out;
+           }
+           rv = is_allocated_sectors(buf1, nb_sectors, &pnum);
+           if (rv || pnum != nb_sectors) {
+               ret = 1;
+               printf("Images are different - content mismatch!\n");
+               goto out;
+           }
+        }
+        sector_num += nb_sectors;
+        qemu_progress_print(((float) nb_sectors / progress_base)*100, 100);
+    }
+    printf("Images are identical.\n");
+
+out:
+    bdrv_delete(bs2);
+    qemu_vfree(buf1);
+    qemu_vfree(buf2);
+out2:
+    bdrv_delete(bs1);
+out3:
+    qemu_progress_end();
+    return ret;
+}
+
 static int img_convert(int argc, char **argv)
 {
     int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors;
-- 
Miroslav Rezanina
Software Engineer - Virtualization Team - XEN kernel

       reply	other threads:[~2012-08-01 10:03 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <1736118218.6697488.1343814369561.JavaMail.root@redhat.com>
2012-08-01 10:03 ` Miroslav Rezanina [this message]
2012-08-01 10:22   ` [Qemu-devel] [PATCH][RFC] Add compare subcommand for qemu-img Paolo Bonzini
2012-08-01 10:44     ` Miroslav Rezanina
2012-08-01 10:52       ` Paolo Bonzini
2012-08-02 10:06     ` Miroslav Rezanina
2012-08-02 11:11       ` Paolo Bonzini
2012-08-01 12:22   ` Stefan Hajnoczi
2012-08-01 13:21   ` Eric Blake
2012-08-01 13:23     ` Paolo Bonzini
2012-08-02  5:19     ` Miroslav Rezanina
2012-08-03  6:45   ` [Qemu-devel] [PATCH v2][RFC] " Miroslav Rezanina
2012-08-03 15:23     ` Eric Blake
2012-11-20 12:36     ` Kevin Wolf
2012-11-20 13:04       ` Miroslav Rezanina
2012-11-21  9:50     ` [Qemu-devel] [PATCH v3] " Miroslav Rezanina
2012-11-22  9:18       ` Stefan Hajnoczi
2012-11-27  8:03       ` [Qemu-devel] [PATCH v4] " Miroslav Rezanina
2012-11-30 14:22         ` Stefan Hajnoczi
2012-12-04 11:06         ` [Qemu-devel] [PATCH v5] " Miroslav Rezanina
2012-12-04 15:22           ` Stefan Hajnoczi
2012-12-06 12:24           ` [Qemu-devel] [PATCH v6] " Miroslav Rezanina
2012-12-11  9:16             ` Stefan Hajnoczi
2012-12-11 12:27             ` Kevin Wolf
2012-12-11 13:09               ` Miroslav Rezanina

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=490157254.6767622.1343815423950.JavaMail.root@redhat.com \
    --to=mrezanin@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.