Signed-off-by: Miroslav Rezanina mreza...@redhat.com
---
qemu-img-cmds.hx |6 +
qemu-img.c | 290 +-
qemu-img.texi| 53 ++
3 files changed, 348 insertions(+), 1 deletions(-)
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
index 9283776..4ca7e95 100644
--- a/qemu-img-cmds.hx
+++ b/qemu-img-cmds.hx
@@ -27,6 +27,12 @@ STEXI
@item commit [-q] [-f @var{fmt}] [-t @var{cache}] @var{filename}
ETEXI
+DEF(compare, img_compare,
+compare [-f fmt] [-F fmt] [-p] [-q] [-s] filename1 filename2)
+STEXI
+@item compare [-f @var{fmt}] [-F @var{fmt}] [-p] [-q] [-s] @var{filename1}
@var{filename2}
+ETEXI
+
DEF(convert, img_convert,
convert [-c] [-p] [-q] [-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 a2a2044..5f0c0c1 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -113,7 +113,12 @@ 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
+ \n
+ Parameters to compare subcommand:\n
+ '-f' first image format\n
+ '-F' second image format\n
+ '-s' run in Strict mode - fail on different image size or sector
allocation\n;
printf(%s\nSupported formats:, help_msg);
bdrv_iterate_format(format_print, NULL);
@@ -817,6 +822,289 @@ static int compare_sectors(const uint8_t *buf1, const
uint8_t *buf2, int n,
#define IO_BUF_SIZE (2 * 1024 * 1024)
+static int64_t sectors_to_bytes(int64_t sectors)
+{
+return sectors BDRV_SECTOR_BITS;
+}
+
+static int64_t sectors_to_process(int64_t total, int64_t from)
+{
+return MIN(total - from, IO_BUF_SIZE BDRV_SECTOR_BITS);
+}
+
+/*
+ * Check if passed sectors are empty (not allocated or contain only 0 bytes)
+ *
+ * Returns 0 in case sectors are filled with 0, 1 if sectors contain non-zero
+ * data and negative value on error.
+ *
+ * @param bs: Driver used for accessing file
+ * @param sect_num: Number of first sector to check
+ * @param sect_count: Number of sectors to check
+ * @param filename: Name of disk file we are checking (logging purpose)
+ * @param buffer: Allocated buffer for storing read data
+ * @param quiet: Flag for quiet mode
+ */
+static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num,
+ int sect_count, const char *filename,
+ uint8_t *buffer, bool quiet)
+{
+int pnum, ret = 0;
+ret = bdrv_read(bs, sect_num, buffer, sect_count);
+if (ret 0) {
+error_report(Error while reading offset % PRId64 of %s: %s,
+ sectors_to_bytes(sect_num), filename, strerror(-ret));
+return ret;
+}
+ret = is_allocated_sectors(buffer, sect_count, pnum);
+if (ret || pnum != sect_count) {
+qprintf(quiet, Content mismatch at offset % PRId64 !\n,
+sectors_to_bytes(ret ? sect_num : sect_num + pnum));
+return 1;
+}
+
+return 0;
+}
+
+/*
+ * Compares two images. Exit codes:
+ *
+ * 0 - Images are identical
+ * 1 - Images differ
+ * 1 - Error occurred
+ */
+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 ret = 0; /* return value - 0 Ident, 1 Different, 1 Error */
+bool progress = false, quiet = false, strict = false;
+int64_t total_sectors;
+int64_t sector_num = 0;
+int64_t nb_sectors;
+int c, pnum;
+uint64_t bs_sectors;
+uint64_t progress_base;
+
+for (;;) {
+c = getopt(argc, argv, hpf:F:sq);
+if (c == -1) {
+break;
+}
+switch (c) {
+case '?':
+case 'h':
+help();
+break;
+case 'f':
+fmt1 = optarg;
+break;
+case 'F':
+fmt2 = optarg;
+break;
+case 'p':
+progress = true;
+break;
+case 'q':
+quiet = true;
+break;
+case 's':
+strict = true;
+break;
+}
+}
+
+/* Progress is not shown in Quiet mode */
+if (quiet) {
+progress = false;
+}
+
+
+if (optind argc - 2) {
+help();
+}
+filename1 = argv[optind++];
+filename2 = argv[optind++];
+
+/* Initialize before goto out */
+qemu_progress_init(progress, 2.0);
+
+bs1 = bdrv_new_open(filename1, fmt1, BDRV_O_FLAGS, true, quiet);
+if (!bs1) {
+error_report(Can't open file %s,