+static int img_rebase(int argc, char **argv)
+{
+ BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL;
+ BlockDriver *old_backing_drv, *new_backing_drv;
+ char *filename;
+ const char *fmt, *out_basefmt, *out_baseimg;
+ int c, flags, ret;
+ int unsafe = 0;
+ int progress = 0;
+
+ /* Parse commandline parameters */
+ fmt = NULL;
+ out_baseimg = NULL;
+ out_basefmt = NULL;
+
+ for(;;) {
+ c = getopt(argc, argv, "uhf:F:b:p");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ return 0;
+ case 'f':
+ fmt = optarg;
+ break;
+ case 'F':
+ out_basefmt = optarg;
+ break;
+ case 'b':
+ out_baseimg = optarg;
+ break;
+ case 'u':
+ unsafe = 1;
+ break;
+ case 'p':
+ progress = 1;
+ break;
+ }
+ }
+
+ if ((optind >= argc) || !out_baseimg) {
+ help();
+ }
+ filename = argv[optind++];
+
+ qemu_progress_init(progress, 2.0);
+ qemu_progress_print(0, 100);
+
+ /*
+ * Open the images.
+ *
+ * Ignore the old backing file for unsafe rebase in case we want to correct
+ * the reference to a renamed or moved backing file.
+ */
+ flags = BDRV_O_FLAGS | BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0);
+ bs = bdrv_new_open(filename, fmt, flags);
+ if (!bs) {
+ return 1;
+ }
+
+ /* Find the right drivers for the backing files */
+ old_backing_drv = NULL;
+ new_backing_drv = NULL;
+
+ if (!unsafe && bs->backing_format[0] != '\0') {
+ old_backing_drv = bdrv_find_format(bs->backing_format);
+ if (old_backing_drv == NULL) {
+ error_report("Invalid format name: '%s'", bs->backing_format);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ if (out_basefmt != NULL) {
+ new_backing_drv = bdrv_find_format(out_basefmt);
+ if (new_backing_drv == NULL) {
+ error_report("Invalid format name: '%s'", out_basefmt);
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* For safe rebasing we need to compare old and new backing file */
+ if (unsafe) {
+ /* Make the compiler happy */
+ bs_old_backing = NULL;
+ bs_new_backing = NULL;
+ } else {
+ char backing_name[1024];
+
+ bs_old_backing = bdrv_new("old_backing");
+ bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name));
+ ret = bdrv_open(bs_old_backing, backing_name, BDRV_O_FLAGS,
+ old_backing_drv);
+ if (ret) {
+ error_report("Could not open old backing file '%s'", backing_name);
+ goto out;
+ }
+
+ bs_new_backing = bdrv_new("new_backing");
+ ret = bdrv_open(bs_new_backing, out_baseimg, BDRV_O_FLAGS,
+ new_backing_drv);
+ if (ret) {
+ error_report("Could not open new backing file '%s'", out_baseimg);
+ goto out;
+ }
+ }
+
+ /*
+ * Check each unallocated cluster in the COW file. If it is unallocated,
+ * accesses go to the backing file. We must therefore compare this cluster
+ * in the old and new backing file, and if they differ we need to copy it
+ * from the old backing file into the COW file.
+ *
+ * If qemu-img crashes during this step, no harm is done. The content of
+ * the image is the same as the original one at any time.
+ */
+ if (!unsafe) {
+ uint64_t num_sectors;
+ uint64_t sector;
+ int n;
+ uint8_t * buf_old;
+ uint8_t * buf_new;
+ float local_progress;
+
+ buf_old = qemu_malloc(IO_BUF_SIZE);
+ buf_new = qemu_malloc(IO_BUF_SIZE);
+
+ bdrv_get_geometry(bs, &num_sectors);
+
+ local_progress = (float)100 /
+ (num_sectors / MIN(num_sectors, (IO_BUF_SIZE / 512)));
+ for (sector = 0; sector < num_sectors; sector += n) {
+
+ /* How many sectors can we handle with the next read? */
+ if (sector + (IO_BUF_SIZE / 512) <= num_sectors) {
+ n = (IO_BUF_SIZE / 512);
+ } else {
+ n = num_sectors - sector;
+ }
+
+ /* If the cluster is allocated, we don't need to take action */
+ ret = bdrv_is_allocated(bs, sector, n, &n);
+ if (ret) {
+ continue;
+ }
+
+ /* Read old and new backing file */
+ ret = bdrv_read(bs_old_backing, sector, buf_old, n);
+ if (ret < 0) {
+ error_report("error while reading from old backing file");
+ goto out;
+ }
+ ret = bdrv_read(bs_new_backing, sector, buf_new, n);
+ if (ret < 0) {
+ error_report("error while reading from new backing file");
+ goto out;
+ }
+
+ /* If they differ, we need to write to the COW file */
+ uint64_t written = 0;
+
+ while (written < n) {
+ int pnum;
+
+ if (compare_sectors(buf_old + written * 512,
+ buf_new + written * 512, n - written, &pnum))
+ {
+ ret = bdrv_write(bs, sector + written,
+ buf_old + written * 512, pnum);
+ if (ret < 0) {
+ error_report("Error while writing to COW image: %s",
+ strerror(-ret));
+ goto out;
+ }
+ }
+
+ written += pnum;
+ }
+ qemu_progress_print(local_progress, 100);
+ }
+
+ qemu_free(buf_old);
+ qemu_free(buf_new);
+ }
+
+ /*
+ * Change the backing file. All clusters that are different from the old
+ * backing file are overwritten in the COW file now, so the visible content
+ * doesn't change when we switch the backing file.
+ */
+ ret = bdrv_change_backing_file(bs, out_baseimg, out_basefmt);
+ if (ret == -ENOSPC) {
+ error_report("Could not change the backing file to '%s': No "
+ "space left in the file header", out_baseimg);
+ } else if (ret < 0) {
+ error_report("Could not change the backing file to '%s': %s",
+ out_baseimg, strerror(-ret));
+ }
+
+ qemu_progress_print(100, 0);
+ /*
+ * TODO At this point it is possible to check if any clusters that are
+ * allocated in the COW file are the same in the backing file. If so, they
+ * could be dropped from the COW file. Don't do this before switching the
+ * backing file, in case of a crash this would lead to corruption.
+ */
+out:
+ qemu_progress_end();
+ /* Cleanup */
+ if (!unsafe) {
+ if (bs_old_backing != NULL) {
+ bdrv_delete(bs_old_backing);
+ }
+ if (bs_new_backing != NULL) {
+ bdrv_delete(bs_new_backing);
+ }
+ }
+
+ bdrv_delete(bs);
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static int img_resize(int argc, char **argv)
+{
+ int c, ret, relative;
+ const char *filename, *fmt, *size;
+ int64_t n, total_size;
+ BlockDriverState *bs = NULL;
+ QEMUOptionParameter *param;
+ QEMUOptionParameter resize_options[] = {
+ {
+ .name = BLOCK_OPT_SIZE,
+ .type = OPT_SIZE,
+ .help = "Virtual disk size"
+ },
+ { NULL }
+ };
+
+ fmt = NULL;
+ for(;;) {
+ c = getopt(argc, argv, "f:h");
+ if (c == -1) {
+ break;
+ }
+ switch(c) {
+ case '?':
+ case 'h':
+ help();
+ break;
+ case 'f':
+ fmt = optarg;
+ break;
+ }
+ }
+ if (optind + 1 >= argc) {
+ help();
+ }
+ filename = argv[optind++];
+ size = argv[optind++];
+
+ /* Choose grow, shrink, or absolute resize mode */
+ switch (size[0]) {
+ case '+':
+ relative = 1;
+ size++;
+ break;
+ case '-':
+ relative = -1;
+ size++;
+ break;
+ default:
+ relative = 0;
+ break;
+ }
+
+ /* Parse size */
+ param = parse_option_parameters("", resize_options, NULL);
+ if (set_option_parameter(param, BLOCK_OPT_SIZE, size)) {
+ /* Error message already printed when size parsing fails */
+ ret = -1;
+ goto out;
+ }
+ n = get_option_parameter(param, BLOCK_OPT_SIZE)->value.n;
+ free_option_parameters(param);
+
+ bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR);
+ if (!bs) {
+ ret = -1;
+ goto out;
+ }
+
+ if (relative) {
+ total_size = bdrv_getlength(bs) + n * relative;
+ } else {
+ total_size = n;
+ }
+ if (total_size <= 0) {
+ error_report("New image size must be positive");
+ ret = -1;
+ goto out;
+ }
+
+ ret = bdrv_truncate(bs, total_size);
+ switch (ret) {
+ case 0:
+ printf("Image resized.\n");
+ break;
+ case -ENOTSUP:
+ error_report("This image format does not support resize");
+ break;
+ case -EACCES:
+ error_report("Image is read-only");
+ break;
+ default:
+ error_report("Error resizing image (%d)", -ret);
+ break;
+ }
+out:
+ if (bs) {
+ bdrv_delete(bs);
+ }
+ if (ret) {
+ return 1;
+ }
+ return 0;
+}
+
+static const img_cmd_t img_cmds[] = {
+#define DEF(option, callback, arg_string) \
+ { option, callback },
+#include "qemu-img-cmds.h"
+#undef DEF
+#undef GEN_DOCS
+ { NULL, NULL, },
+};
+