From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([208.118.235.92]:35798) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SV10T-0007LU-6C for qemu-devel@nongnu.org; Thu, 17 May 2012 09:44:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SV10K-0007PQ-DZ for qemu-devel@nongnu.org; Thu, 17 May 2012 09:44:40 -0400 Received: from mx1.redhat.com ([209.132.183.28]:32295) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SV10K-0007Oz-5k for qemu-devel@nongnu.org; Thu, 17 May 2012 09:44:32 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q4HDiUrq012398 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 17 May 2012 09:44:30 -0400 Received: from trick.home.annexia.org (vpn1-6-35.ams2.redhat.com [10.36.6.35]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q4HDiS1D000832 for ; Thu, 17 May 2012 09:44:29 -0400 From: "Richard W.M. Jones" Date: Thu, 17 May 2012 14:44:26 +0100 Message-Id: <1337262266-32227-1-git-send-email-rjones@redhat.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Subject: [Qemu-devel] [PATCH] qemu-img: Implement 'diff' operation. List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org From: "Richard W.M. Jones" 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 Cc: Matthew Booth Cc: Pablo Iranzo G=C3=B3mez Cc: Tomas Von Veschler --- 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 =20 +DEF("diff", img_diff, + "diff [-f fmt] [-F backing_fmt] [-O output_fmt] -b backing_file file= name 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; } =20 +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 =3D 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 =3D NULL; + fmt_modified =3D NULL; + fmt_out =3D NULL; + original =3D NULL; + for(;;) { + c =3D getopt(argc, argv, "hf:F:b:O:"); + if (c =3D=3D -1) { + break; + } + switch(c) { + case '?': + case 'h': + help(); + return 0; + case 'f': + fmt_modified =3D optarg; + break; + case 'F': + fmt_original =3D optarg; + break; + case 'b': + original =3D optarg; + break; + case 'O': + fmt_out =3D optarg; + break; + } + } + + if (original =3D=3D NULL) { + error_report("The -b (backing filename) option must be supplied"= ); + return 1; + } + + if (argc - optind !=3D 2) { + error_report("The input and output filenames must be supplied"); + return 1; + } + modified =3D argv[optind++]; + out =3D argv[optind++]; + + /* Open the input images. */ + bs_original =3D bdrv_new_open(original, fmt_original, BDRV_O_FLAGS); + if (!bs_original) { + return 1; + } + + bs_modified =3D 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 !=3D modified_num_sectors) { + error_report("Number of sectors in backing and source must be th= e same"); + goto out2; + } + + /* Output image. */ + if (fmt_out =3D=3D NULL || fmt_out[0] =3D=3D '\0') { + fmt_out =3D "qcow2"; + } + ret =3D bdrv_img_create(out, fmt_out, + /* original file becomes the new backing file = */ + original, fmt_original, + NULL, num_sectors * BDRV_SECTOR_SIZE, BDRV_O_F= LAGS); + if (ret !=3D 0) { + goto out2; + } + bs_out =3D bdrv_new_open(out, fmt_out, BDRV_O_RDWR); + + buf_original =3D qemu_blockalign(bs_original, IO_BUF_SIZE); + buf_modified =3D qemu_blockalign(bs_modified, IO_BUF_SIZE); + + for (sector =3D 0; sector < num_sectors; sector +=3D n) { + /* How many sectors can we handle with the next read? */ + if (sector + (IO_BUF_SIZE / BDRV_SECTOR_SIZE) <=3D num_sectors) = { + n =3D IO_BUF_SIZE / BDRV_SECTOR_SIZE; + } else { + n =3D num_sectors - sector; + } + + /* Read input files and compare. */ + ret =3D bdrv_read(bs_original, sector, buf_original, n); + if (ret < 0) { + error_report("error while reading from backing file"); + goto out; + } + + ret =3D 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 =3D 0; + + while (written < n) { + int pnum; + + if (compare_sectors(buf_original + written * BDRV_SECTOR_SIZ= E, + buf_modified + written * BDRV_SECTOR_SIZ= E, + n - written, &pnum)) { + ret =3D bdrv_write(bs_out, sector + written, + buf_modified + written * BDRV_SECTOR_SI= ZE, + pnum); + if (ret < 0) { + error_report("Error while writing to output file: %s= ", + strerror(-ret)); + goto out; + } + } + + written +=3D 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 ba= se image; the @var{backing_file} should have the same content as the input's base imag= e, however the path, image format, etc may differ. =20 +@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} =20 Give information about the disk image @var{filename}. Use it in --=20 1.7.10