From: "Richard W.M. Jones" <rjo...@redhat.com> This produces a qcow2 file which is the different between two disk images. ie, if:
original.img - is a disk image (in any format) modified.img - is a modified version of original.img then: qemu-img diff -b original.img modified.img diff.qcow2 creates 'diff.qcow2' which contains just the differences. Note that 'diff.qcow2' has 'original.img' set as the backing file. Signed-off-by: Richard W.M. Jones <rjo...@redhat.com> Cc: Matthew Booth <mbo...@redhat.com> Cc: Pablo Iranzo Gómez <pablo.ira...@redhat.com> Cc: Tomas Von Veschler <tvv...@redhat.com> --- qemu-img-cmds.hx | 6 +++ qemu-img.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu-img.texi | 17 +++++++ 3 files changed, 173 insertions(+) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 49dce7c..01a9246 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -33,6 +33,12 @@ STEXI @item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI +DEF("diff", img_diff, + "diff [-f fmt] [-F backing_fmt] [-O output_fmt] -b backing_file filename output_filename") +STEXI +@item rebase [-f @var{fmt}] [-F @var{backing_fmt}] [-O @var{output_fmt}] -b @var{backing_file} @var{filename} @var{output_filename} +ETEXI + DEF("info", img_info, "info [-f fmt] filename") STEXI diff --git a/qemu-img.c b/qemu-img.c index c8a70ff..6e3fe2a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1533,6 +1533,156 @@ out: return 0; } +static int img_diff(int argc, char **argv) +{ + /* qemu-img diff -b original modified out */ + BlockDriverState *bs_original, *bs_modified, *bs_out; + const char *fmt_original, *original, + *fmt_modified, *modified, + *fmt_out, *out; + int c, ret = 0; + uint64_t num_sectors, modified_num_sectors; + uint64_t sector; + int n; + uint8_t *buf_original; + uint8_t *buf_modified; + + /* Parse commandline parameters */ + fmt_original = NULL; + fmt_modified = NULL; + fmt_out = NULL; + original = NULL; + for(;;) { + c = getopt(argc, argv, "hf:F:b:O:"); + if (c == -1) { + break; + } + switch(c) { + case '?': + case 'h': + help(); + return 0; + case 'f': + fmt_modified = optarg; + break; + case 'F': + fmt_original = optarg; + break; + case 'b': + original = optarg; + break; + case 'O': + fmt_out = optarg; + break; + } + } + + if (original == NULL) { + error_report("The -b (backing filename) option must be supplied"); + return 1; + } + + if (argc - optind != 2) { + error_report("The input and output filenames must be supplied"); + return 1; + } + modified = argv[optind++]; + out = argv[optind++]; + + /* Open the input images. */ + bs_original = bdrv_new_open(original, fmt_original, BDRV_O_FLAGS); + if (!bs_original) { + return 1; + } + + bs_modified = bdrv_new_open(modified, fmt_modified, BDRV_O_FLAGS); + if (!bs_modified) { + return 1; + } + + bdrv_get_geometry(bs_original, &num_sectors); + bdrv_get_geometry(bs_modified, &modified_num_sectors); + if (num_sectors != modified_num_sectors) { + error_report("Number of sectors in backing and source must be the same"); + goto out2; + } + + /* Output image. */ + if (fmt_out == NULL || fmt_out[0] == '\0') { + fmt_out = "qcow2"; + } + ret = bdrv_img_create(out, fmt_out, + /* original file becomes the new backing file */ + original, fmt_original, + NULL, num_sectors * BDRV_SECTOR_SIZE, BDRV_O_FLAGS); + if (ret != 0) { + goto out2; + } + bs_out = bdrv_new_open(out, fmt_out, BDRV_O_RDWR); + + buf_original = qemu_blockalign(bs_original, IO_BUF_SIZE); + buf_modified = qemu_blockalign(bs_modified, IO_BUF_SIZE); + + for (sector = 0; sector < num_sectors; sector += n) { + /* How many sectors can we handle with the next read? */ + if (sector + (IO_BUF_SIZE / BDRV_SECTOR_SIZE) <= num_sectors) { + n = IO_BUF_SIZE / BDRV_SECTOR_SIZE; + } else { + n = num_sectors - sector; + } + + /* Read input files and compare. */ + ret = bdrv_read(bs_original, sector, buf_original, n); + if (ret < 0) { + error_report("error while reading from backing file"); + goto out; + } + + ret = bdrv_read(bs_modified, sector, buf_modified, n); + if (ret < 0) { + error_report("error while reading from input file"); + goto out; + } + + /* If they differ, we need to write to the differences file. */ + uint64_t written = 0; + + while (written < n) { + int pnum; + + if (compare_sectors(buf_original + written * BDRV_SECTOR_SIZE, + buf_modified + written * BDRV_SECTOR_SIZE, + n - written, &pnum)) { + ret = bdrv_write(bs_out, sector + written, + buf_modified + written * BDRV_SECTOR_SIZE, + pnum); + if (ret < 0) { + error_report("Error while writing to output file: %s", + strerror(-ret)); + goto out; + } + } + + written += pnum; + } + } + + qemu_vfree(buf_original); + qemu_vfree(buf_modified); + + out: + /* Cleanup */ + bdrv_delete(bs_out); + out2: + bdrv_delete(bs_original); + bdrv_delete(bs_modified); + + if (ret) { + return 1; + } + return 0; +} + static int img_resize(int argc, char **argv) { int c, ret, relative; diff --git a/qemu-img.texi b/qemu-img.texi index b2ca3a5..e1a123b 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -114,6 +114,23 @@ created as a copy on write image of the specified base image; the @var{backing_file} should have the same content as the input's base image, however the path, image format, etc may differ. +@item diff [-f @var{fmt}] [-F @var{backing_fmt}] [-O @var{output_fmt}] -b @var{backing_file} @var{filename} @var{output_filename} + +Create a new file (@var{output_filename}) which contains the +differences between @var{backing_file} and @var{filename}. + +The @var{backing_file} and @var{filename} must have the same +virtual disk size, but may be in different formats. + +@var{output_file} will have @var{backing_file} set as its backing +file. The format of @var{output_file} must be one that supports +backing files (currently @code{qcow2} is the default and only +permitted output format). + +Typical usage is: + +@code{qemu-img diff -b original.img modified.img diff.qcow2} + @item info [-f @var{fmt}] @var{filename} Give information about the disk image @var{filename}. Use it in -- 1.7.10