X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/e4e9986b1caebebdbe53d6f9ad5b03d5ba83f4c3..ecf2e5a46d7559f258a2c914131ba25d3c5326bf:/qemu-img.c diff --git a/qemu-img.c b/qemu-img.c index 9c77dcc927..9dddfbefce 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -29,11 +29,13 @@ #include "qemu/error-report.h" #include "qemu/osdep.h" #include "sysemu/sysemu.h" +#include "sysemu/block-backend.h" #include "block/block_int.h" +#include "block/blockjob.h" #include "block/qapi.h" #include -#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION \ +#define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ ", Copyright (c) 2004-2008 Fabrice Bellard\n" typedef struct img_cmd_t { @@ -259,6 +261,7 @@ static int print_block_option_help(const char *filename, const char *fmt) { BlockDriver *drv, *proto_drv; QemuOptsList *create_opts = NULL; + Error *local_err = NULL; /* Find driver and parse its options */ drv = bdrv_find_format(fmt); @@ -269,9 +272,9 @@ static int print_block_option_help(const char *filename, const char *fmt) create_opts = qemu_opts_append(create_opts, drv->create_opts); if (filename) { - proto_drv = bdrv_find_protocol(filename, true); + proto_drv = bdrv_find_protocol(filename, true, &local_err); if (!proto_drv) { - error_report("Unknown protocol '%s'", filename); + error_report_err(local_err); qemu_opts_free(create_opts); return 1; } @@ -283,39 +286,30 @@ static int print_block_option_help(const char *filename, const char *fmt) return 0; } -static BlockDriverState *bdrv_new_open(const char *id, - const char *filename, - const char *fmt, - int flags, - bool require_io, - bool quiet) +static BlockBackend *img_open(const char *id, const char *filename, + const char *fmt, int flags, + bool require_io, bool quiet) { + BlockBackend *blk; BlockDriverState *bs; - BlockDriver *drv; char password[256]; Error *local_err = NULL; - int ret; - - bs = bdrv_new_root(id, &error_abort); + QDict *options = NULL; if (fmt) { - drv = bdrv_find_format(fmt); - if (!drv) { - error_report("Unknown file format '%s'", fmt); - goto fail; - } - } else { - drv = NULL; + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(fmt)); } - ret = bdrv_open(&bs, filename, NULL, NULL, flags, drv, &local_err); - if (ret < 0) { + blk = blk_new_open(id, filename, NULL, options, flags, &local_err); + if (!blk) { error_report("Could not open '%s': %s", filename, error_get_pretty(local_err)); error_free(local_err); goto fail; } + bs = blk_bs(blk); if (bdrv_is_encrypted(bs) && require_io) { qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); if (read_password(password, sizeof(password)) < 0) { @@ -327,9 +321,9 @@ static BlockDriverState *bdrv_new_open(const char *id, goto fail; } } - return bs; + return blk; fail: - bdrv_unref(bs); + blk_unref(blk); return NULL; } @@ -337,17 +331,23 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts, const char *base_filename, const char *base_fmt) { + Error *err = NULL; + if (base_filename) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename, &err); + if (err) { error_report("Backing file not supported for file format '%s'", fmt); + error_free(err); return -1; } } if (base_fmt) { - if (qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt)) { + qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, &err); + if (err) { error_report("Backing file format not supported for file " "format '%s'", fmt); + error_free(err); return -1; } } @@ -575,6 +575,7 @@ static int img_check(int argc, char **argv) int c, ret; OutputFormat output_format = OFORMAT_HUMAN; const char *filename, *fmt, *output, *cache; + BlockBackend *blk; BlockDriverState *bs; int fix = 0; int flags = BDRV_O_FLAGS | BDRV_O_CHECK; @@ -649,10 +650,11 @@ static int img_check(int argc, char **argv) return 1; } - bs = bdrv_new_open("image", filename, fmt, flags, true, quiet); - if (!bs) { + blk = img_open("image", filename, fmt, flags, true, quiet); + if (!blk) { return 1; } + bs = blk_bs(blk); check = g_new0(ImageCheck, 1); ret = collect_image_check(bs, check, filename, fmt, fix); @@ -685,16 +687,23 @@ static int img_check(int argc, char **argv) check->corruptions_fixed = corruptions_fixed; } - switch (output_format) { - case OFORMAT_HUMAN: - dump_human_image_check(check, quiet); - break; - case OFORMAT_JSON: - dump_json_image_check(check, quiet); - break; + if (!ret) { + switch (output_format) { + case OFORMAT_HUMAN: + dump_human_image_check(check, quiet); + break; + case OFORMAT_JSON: + dump_json_image_check(check, quiet); + break; + } } if (ret || check->check_errors) { + if (ret) { + error_report("Check failed: %s", strerror(-ret)); + } else { + error_report("Check failed"); + } ret = 1; goto fail; } @@ -709,22 +718,58 @@ static int img_check(int argc, char **argv) fail: qapi_free_ImageCheck(check); - bdrv_unref(bs); - + blk_unref(blk); return ret; } +typedef struct CommonBlockJobCBInfo { + BlockDriverState *bs; + Error **errp; +} CommonBlockJobCBInfo; + +static void common_block_job_cb(void *opaque, int ret) +{ + CommonBlockJobCBInfo *cbi = opaque; + + if (ret < 0) { + error_setg_errno(cbi->errp, -ret, "Block job failed"); + } + + /* Drop this block job's reference */ + bdrv_unref(cbi->bs); +} + +static void run_block_job(BlockJob *job, Error **errp) +{ + AioContext *aio_context = bdrv_get_aio_context(job->bs); + + do { + aio_poll(aio_context, true); + qemu_progress_print((float)job->offset / job->len * 100.f, 0); + } while (!job->ready); + + block_job_complete_sync(job, errp); + + /* A block job may finish instantaneously without publishing any progress, + * so just signal completion here */ + qemu_progress_print(100.f, 0); +} + static int img_commit(int argc, char **argv) { int c, ret, flags; - const char *filename, *fmt, *cache; - BlockDriverState *bs; - bool quiet = false; + const char *filename, *fmt, *cache, *base; + BlockBackend *blk; + BlockDriverState *bs, *base_bs; + bool progress = false, quiet = false, drop = false; + Error *local_err = NULL; + CommonBlockJobCBInfo cbi; fmt = NULL; cache = BDRV_DEFAULT_CACHE; + base = NULL; for(;;) { - c = getopt(argc, argv, "f:ht:q"); + c = getopt(argc, argv, "f:ht:b:dpq"); if (c == -1) { break; } @@ -739,50 +784,115 @@ static int img_commit(int argc, char **argv) case 't': cache = optarg; break; + case 'b': + base = optarg; + /* -b implies -d */ + drop = true; + break; + case 'd': + drop = true; + break; + case 'p': + progress = true; + break; case 'q': quiet = true; break; } } + + /* Progress is not shown in Quiet mode */ + if (quiet) { + progress = false; + } + if (optind != argc - 1) { error_exit("Expecting one image file name"); } filename = argv[optind++]; - flags = BDRV_O_RDWR; + flags = BDRV_O_RDWR | BDRV_O_UNMAP; ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); return 1; } - bs = bdrv_new_open("image", filename, fmt, flags, true, quiet); - if (!bs) { + blk = img_open("image", filename, fmt, flags, true, quiet); + if (!blk) { return 1; } - ret = bdrv_commit(bs); - switch(ret) { - case 0: - qprintf(quiet, "Image committed.\n"); - break; - case -ENOENT: - error_report("No disk inserted"); - break; - case -EACCES: - error_report("Image is read-only"); - break; - case -ENOTSUP: - error_report("Image is already committed"); - break; - default: - error_report("Error while committing image"); - break; + bs = blk_bs(blk); + + qemu_progress_init(progress, 1.f); + qemu_progress_print(0.f, 100); + + if (base) { + base_bs = bdrv_find_backing_image(bs, base); + if (!base_bs) { + error_set(&local_err, QERR_BASE_NOT_FOUND, base); + goto done; + } + } else { + /* This is different from QMP, which by default uses the deepest file in + * the backing chain (i.e., the very base); however, the traditional + * behavior of qemu-img commit is using the immediate backing file. */ + base_bs = bs->backing_hd; + if (!base_bs) { + error_setg(&local_err, "Image does not have a backing file"); + goto done; + } } - bdrv_unref(bs); - if (ret) { + cbi = (CommonBlockJobCBInfo){ + .errp = &local_err, + .bs = bs, + }; + + commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT, + common_block_job_cb, &cbi, &local_err); + if (local_err) { + goto done; + } + + /* The block job will swap base_bs and bs (which is not what we really want + * here, but okay) and unref base_bs (after the swap, i.e., the old top + * image). In order to still be able to empty that top image afterwards, + * increment the reference counter here preemptively. */ + if (!drop) { + bdrv_ref(base_bs); + } + + run_block_job(bs->job, &local_err); + if (local_err) { + goto unref_backing; + } + + if (!drop && base_bs->drv->bdrv_make_empty) { + ret = base_bs->drv->bdrv_make_empty(base_bs); + if (ret) { + error_setg_errno(&local_err, -ret, "Could not empty %s", + filename); + goto unref_backing; + } + } + +unref_backing: + if (!drop) { + bdrv_unref(base_bs); + } + +done: + qemu_progress_end(); + + blk_unref(blk); + + if (local_err) { + error_report_err(local_err); return 1; } + + qprintf(quiet, "Image committed.\n"); return 0; } @@ -865,7 +975,8 @@ static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, int *pnum) { - int res, i; + bool res; + int i; if (n <= 0) { *pnum = 0; @@ -904,19 +1015,19 @@ static int64_t sectors_to_process(int64_t total, int64_t from) * 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 blk: BlockBackend for the image * @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, +static int check_empty_sectors(BlockBackend *blk, 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); + ret = blk_read(blk, 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)); @@ -942,6 +1053,7 @@ static int check_empty_sectors(BlockDriverState *bs, int64_t sect_num, static int img_compare(int argc, char **argv) { const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2; + BlockBackend *blk1, *blk2; BlockDriverState *bs1, *bs2; int64_t total_sectors1, total_sectors2; uint8_t *buf1 = NULL, *buf2 = NULL; @@ -1011,30 +1123,30 @@ static int img_compare(int argc, char **argv) goto out3; } - bs1 = bdrv_new_open("image_1", filename1, fmt1, flags, true, quiet); - if (!bs1) { - error_report("Can't open file %s", filename1); + blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet); + if (!blk1) { ret = 2; goto out3; } + bs1 = blk_bs(blk1); - bs2 = bdrv_new_open("image_2", filename2, fmt2, flags, true, quiet); - if (!bs2) { - error_report("Can't open file %s", filename2); + blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet); + if (!blk2) { ret = 2; goto out2; } + bs2 = blk_bs(blk2); - buf1 = qemu_blockalign(bs1, IO_BUF_SIZE); - buf2 = qemu_blockalign(bs2, IO_BUF_SIZE); - total_sectors1 = bdrv_nb_sectors(bs1); + buf1 = blk_blockalign(blk1, IO_BUF_SIZE); + buf2 = blk_blockalign(blk2, IO_BUF_SIZE); + total_sectors1 = blk_nb_sectors(blk1); if (total_sectors1 < 0) { error_report("Can't get size of %s: %s", filename1, strerror(-total_sectors1)); ret = 4; goto out; } - total_sectors2 = bdrv_nb_sectors(bs2); + total_sectors2 = blk_nb_sectors(blk2); if (total_sectors2 < 0) { error_report("Can't get size of %s: %s", filename2, strerror(-total_sectors2)); @@ -1076,7 +1188,7 @@ static int img_compare(int argc, char **argv) if (allocated1 == allocated2) { if (allocated1) { - ret = bdrv_read(bs1, sector_num, buf1, nb_sectors); + ret = blk_read(blk1, sector_num, buf1, nb_sectors); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s:" " %s", sectors_to_bytes(sector_num), filename1, @@ -1084,7 +1196,7 @@ static int img_compare(int argc, char **argv) ret = 4; goto out; } - ret = bdrv_read(bs2, sector_num, buf2, nb_sectors); + ret = blk_read(blk2, sector_num, buf2, nb_sectors); if (ret < 0) { error_report("Error while reading offset %" PRId64 " of %s: %s", sectors_to_bytes(sector_num), @@ -1111,10 +1223,10 @@ static int img_compare(int argc, char **argv) } if (allocated1) { - ret = check_empty_sectors(bs1, sector_num, nb_sectors, + ret = check_empty_sectors(blk1, sector_num, nb_sectors, filename1, buf1, quiet); } else { - ret = check_empty_sectors(bs2, sector_num, nb_sectors, + ret = check_empty_sectors(blk2, sector_num, nb_sectors, filename2, buf1, quiet); } if (ret) { @@ -1131,18 +1243,18 @@ static int img_compare(int argc, char **argv) } if (total_sectors1 != total_sectors2) { - BlockDriverState *bs_over; + BlockBackend *blk_over; int64_t total_sectors_over; const char *filename_over; qprintf(quiet, "Warning: Image size mismatch!\n"); if (total_sectors1 > total_sectors2) { total_sectors_over = total_sectors1; - bs_over = bs1; + blk_over = blk1; filename_over = filename1; } else { total_sectors_over = total_sectors2; - bs_over = bs2; + blk_over = blk2; filename_over = filename2; } @@ -1151,7 +1263,7 @@ static int img_compare(int argc, char **argv) if (nb_sectors <= 0) { break; } - ret = bdrv_is_allocated_above(bs_over, NULL, sector_num, + ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL, sector_num, nb_sectors, &pnum); if (ret < 0) { ret = 3; @@ -1162,7 +1274,7 @@ static int img_compare(int argc, char **argv) } nb_sectors = pnum; if (ret) { - ret = check_empty_sectors(bs_over, sector_num, nb_sectors, + ret = check_empty_sectors(blk_over, sector_num, nb_sectors, filename_over, buf1, quiet); if (ret) { if (ret < 0) { @@ -1183,11 +1295,11 @@ static int img_compare(int argc, char **argv) ret = 0; out: - bdrv_unref(bs2); qemu_vfree(buf1); qemu_vfree(buf2); + blk_unref(blk2); out2: - bdrv_unref(bs1); + blk_unref(blk1); out3: qemu_progress_end(); return ret; @@ -1200,6 +1312,7 @@ static int img_convert(int argc, char **argv) int progress = 0, flags, src_flags; const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; + BlockBackend **blk = NULL, *out_blk = NULL; BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; int64_t *bs_sectors = NULL; @@ -1354,6 +1467,7 @@ static int img_convert(int argc, char **argv) qemu_progress_print(0, 100); + blk = g_new0(BlockBackend *, bs_n); bs = g_new0(BlockDriverState *, bs_n); bs_sectors = g_new(int64_t, bs_n); @@ -1361,15 +1475,15 @@ static int img_convert(int argc, char **argv) for (bs_i = 0; bs_i < bs_n; bs_i++) { char *id = bs_n > 1 ? g_strdup_printf("source_%d", bs_i) : g_strdup("source"); - bs[bs_i] = bdrv_new_open(id, argv[optind + bs_i], fmt, src_flags, - true, quiet); + blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags, + true, quiet); g_free(id); - if (!bs[bs_i]) { - error_report("Could not open '%s'", argv[optind + bs_i]); + if (!blk[bs_i]) { ret = -1; goto out; } - bs_sectors[bs_i] = bdrv_nb_sectors(bs[bs_i]); + bs[bs_i] = blk_bs(blk[bs_i]); + bs_sectors[bs_i] = blk_nb_sectors(blk[bs_i]); if (bs_sectors[bs_i] < 0) { error_report("Could not get size of %s: %s", argv[optind + bs_i], strerror(-bs_sectors[bs_i])); @@ -1409,27 +1523,47 @@ static int img_convert(int argc, char **argv) goto out; } - proto_drv = bdrv_find_protocol(out_filename, true); + proto_drv = bdrv_find_protocol(out_filename, true, &local_err); if (!proto_drv) { - error_report("Unknown protocol '%s'", out_filename); + error_report_err(local_err); ret = -1; goto out; } - create_opts = qemu_opts_append(create_opts, drv->create_opts); - create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + if (!skip_create) { + if (!drv->create_opts) { + error_report("Format driver '%s' does not support image creation", + drv->format_name); + ret = -1; + goto out; + } - opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - if (options && qemu_opts_do_parse(opts, options, NULL)) { - error_report("Invalid options for file format '%s'", out_fmt); - ret = -1; - goto out; - } + if (!proto_drv->create_opts) { + error_report("Protocol driver '%s' does not support image creation", + proto_drv->format_name); + ret = -1; + goto out; + } - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512); - ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); - if (ret < 0) { - goto out; + create_opts = qemu_opts_append(create_opts, drv->create_opts); + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (options) { + qemu_opts_do_parse(opts, options, NULL, &local_err); + if (local_err) { + error_report_err(local_err); + ret = -1; + goto out; + } + } + + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, total_sectors * 512, + &error_abort); + ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); + if (ret < 0) { + goto out; + } } /* Get backing file name if -o backing_file was used */ @@ -1486,11 +1620,12 @@ static int img_convert(int argc, char **argv) goto out; } - out_bs = bdrv_new_open("target", out_filename, out_fmt, flags, true, quiet); - if (!out_bs) { + out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet); + if (!out_blk) { ret = -1; goto out; } + out_bs = blk_bs(out_blk); bs_i = 0; bs_offset = 0; @@ -1503,12 +1638,12 @@ static int img_convert(int argc, char **argv) out_bs->bl.discard_alignment)) ); - buf = qemu_blockalign(out_bs, bufsectors * BDRV_SECTOR_SIZE); + buf = blk_blockalign(out_blk, bufsectors * BDRV_SECTOR_SIZE); if (skip_create) { - int64_t output_sectors = bdrv_nb_sectors(out_bs); + int64_t output_sectors = blk_nb_sectors(out_blk); if (output_sectors < 0) { - error_report("unable to get output image length: %s\n", + error_report("unable to get output image length: %s", strerror(-output_sectors)); ret = -1; goto out; @@ -1574,7 +1709,7 @@ static int img_convert(int argc, char **argv) nlow = remainder > bs_sectors[bs_i] - bs_num ? bs_sectors[bs_i] - bs_num : remainder; - ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow); + ret = blk_read(blk[bs_i], bs_num, buf2, nlow); if (ret < 0) { error_report("error while reading sector %" PRId64 ": %s", bs_num, strerror(-ret)); @@ -1589,7 +1724,7 @@ static int img_convert(int argc, char **argv) assert (remainder == 0); if (!buffer_is_zero(buf, n * BDRV_SECTOR_SIZE)) { - ret = bdrv_write_compressed(out_bs, sector_num, buf, n); + ret = blk_write_compressed(out_blk, sector_num, buf, n); if (ret != 0) { error_report("error while compressing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1600,7 +1735,7 @@ static int img_convert(int argc, char **argv) qemu_progress_print(100.0 * sector_num / total_sectors, 0); } /* signal EOF to align */ - bdrv_write_compressed(out_bs, 0, NULL, 0); + blk_write_compressed(out_blk, 0, NULL, 0); } else { int64_t sectors_to_read, sectors_read, sector_num_next_status; bool count_allocated_sectors; @@ -1701,7 +1836,7 @@ restart: } n1 = n; - ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n); + ret = blk_read(blk[bs_i], sector_num - bs_offset, buf, n); if (ret < 0) { error_report("error while reading sector %" PRId64 ": %s", sector_num - bs_offset, strerror(-ret)); @@ -1714,7 +1849,7 @@ restart: while (n > 0) { if (!has_zero_init || is_allocated_sectors_min(buf1, n, &n1, min_sparse)) { - ret = bdrv_write(out_bs, sector_num, buf1, n1); + ret = blk_write(out_blk, sector_num, buf1, n1); if (ret < 0) { error_report("error while writing sector %" PRId64 ": %s", sector_num, strerror(-ret)); @@ -1737,16 +1872,13 @@ out: qemu_opts_free(create_opts); qemu_vfree(buf); qemu_opts_del(sn_opts); - if (out_bs) { - bdrv_unref(out_bs); - } - if (bs) { + blk_unref(out_blk); + g_free(bs); + if (blk) { for (bs_i = 0; bs_i < bs_n; bs_i++) { - if (bs[bs_i]) { - bdrv_unref(bs[bs_i]); - } + blk_unref(blk[bs_i]); } - g_free(bs); + g_free(blk); } g_free(bs_sectors); fail_getopt: @@ -1856,6 +1988,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, filenames = g_hash_table_new_full(g_str_hash, str_equal_func, NULL, NULL); while (filename) { + BlockBackend *blk; BlockDriverState *bs; ImageInfo *info; ImageInfoList *elem; @@ -1867,17 +2000,17 @@ static ImageInfoList *collect_image_info_list(const char *filename, } g_hash_table_insert(filenames, (gpointer)filename, NULL); - bs = bdrv_new_open("image", filename, fmt, - BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false); - if (!bs) { + blk = img_open("image", filename, fmt, + BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false); + if (!blk) { goto err; } + bs = blk_bs(blk); bdrv_query_image_info(bs, &info, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); - bdrv_unref(bs); + error_report_err(err); + blk_unref(blk); goto err; } @@ -1886,7 +2019,7 @@ static ImageInfoList *collect_image_info_list(const char *filename, *last = elem; last = &elem->next; - bdrv_unref(bs); + blk_unref(blk); filename = fmt = NULL; if (chain) { @@ -2080,6 +2213,7 @@ static int img_map(int argc, char **argv) { int c; OutputFormat output_format = OFORMAT_HUMAN; + BlockBackend *blk; BlockDriverState *bs; const char *filename, *fmt, *output; int64_t length; @@ -2128,16 +2262,17 @@ static int img_map(int argc, char **argv) return 1; } - bs = bdrv_new_open("image", filename, fmt, BDRV_O_FLAGS, true, false); - if (!bs) { + blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false); + if (!blk) { return 1; } + bs = blk_bs(blk); if (output_format == OFORMAT_HUMAN) { printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File"); } - length = bdrv_getlength(bs); + length = blk_getlength(blk); while (curr.start + curr.length < length) { int64_t nsectors_left; int64_t sector_num; @@ -2172,7 +2307,7 @@ static int img_map(int argc, char **argv) dump_map_entry(output_format, &curr, NULL); out: - bdrv_unref(bs); + blk_unref(blk); return ret < 0; } @@ -2183,6 +2318,7 @@ out: static int img_snapshot(int argc, char **argv) { + BlockBackend *blk; BlockDriverState *bs; QEMUSnapshotInfo sn; char *filename, *snapshot_name = NULL; @@ -2248,10 +2384,11 @@ static int img_snapshot(int argc, char **argv) filename = argv[optind++]; /* Open the image */ - bs = bdrv_new_open("image", filename, NULL, bdrv_oflags, true, quiet); - if (!bs) { + blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet); + if (!blk) { return 1; } + bs = blk_bs(blk); /* Perform the requested action */ switch(action) { @@ -2294,7 +2431,7 @@ static int img_snapshot(int argc, char **argv) } /* Cleanup */ - bdrv_unref(bs); + blk_unref(blk); if (ret) { return 1; } @@ -2303,8 +2440,8 @@ static int img_snapshot(int argc, char **argv) static int img_rebase(int argc, char **argv) { - BlockDriverState *bs = NULL, *bs_old_backing = NULL, *bs_new_backing = NULL; - BlockDriver *old_backing_drv, *new_backing_drv; + BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; + BlockDriverState *bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; @@ -2391,28 +2528,15 @@ static int img_rebase(int argc, char **argv) * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - bs = bdrv_new_open("image", filename, fmt, flags, true, quiet); - if (!bs) { + blk = img_open("image", filename, fmt, flags, true, quiet); + if (!blk) { ret = -1; goto out; } - - /* 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; - } - } + bs = blk_bs(blk); if (out_basefmt != NULL) { - new_backing_drv = bdrv_find_format(out_basefmt); - if (new_backing_drv == NULL) { + if (bdrv_find_format(out_basefmt) == NULL) { error_report("Invalid format name: '%s'", out_basefmt); ret = -1; goto out; @@ -2421,23 +2545,35 @@ static int img_rebase(int argc, char **argv) /* For safe rebasing we need to compare old and new backing file */ if (!unsafe) { - char backing_name[1024]; + char backing_name[PATH_MAX]; + QDict *options = NULL; + + if (bs->backing_format[0] != '\0') { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(bs->backing_format)); + } - bs_old_backing = bdrv_new_root("old_backing", &error_abort); bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - ret = bdrv_open(&bs_old_backing, backing_name, NULL, NULL, src_flags, - old_backing_drv, &local_err); - if (ret) { + blk_old_backing = blk_new_open("old_backing", backing_name, NULL, + options, src_flags, &local_err); + if (!blk_old_backing) { error_report("Could not open old backing file '%s': %s", backing_name, error_get_pretty(local_err)); error_free(local_err); goto out; } + if (out_baseimg[0]) { - bs_new_backing = bdrv_new_root("new_backing", &error_abort); - ret = bdrv_open(&bs_new_backing, out_baseimg, NULL, NULL, src_flags, - new_backing_drv, &local_err); - if (ret) { + if (out_basefmt) { + options = qdict_new(); + qdict_put(options, "driver", qstring_from_str(out_basefmt)); + } else { + options = NULL; + } + + blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL, + options, src_flags, &local_err); + if (!blk_new_backing) { error_report("Could not open new backing file '%s': %s", out_baseimg, error_get_pretty(local_err)); error_free(local_err); @@ -2465,19 +2601,19 @@ static int img_rebase(int argc, char **argv) uint8_t * buf_new; float local_progress = 0; - buf_old = qemu_blockalign(bs, IO_BUF_SIZE); - buf_new = qemu_blockalign(bs, IO_BUF_SIZE); + buf_old = blk_blockalign(blk, IO_BUF_SIZE); + buf_new = blk_blockalign(blk, IO_BUF_SIZE); - num_sectors = bdrv_nb_sectors(bs); + num_sectors = blk_nb_sectors(blk); if (num_sectors < 0) { error_report("Could not get size of '%s': %s", filename, strerror(-num_sectors)); ret = -1; goto out; } - old_backing_num_sectors = bdrv_nb_sectors(bs_old_backing); + old_backing_num_sectors = blk_nb_sectors(blk_old_backing); if (old_backing_num_sectors < 0) { - char backing_name[1024]; + char backing_name[PATH_MAX]; bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); error_report("Could not get size of '%s': %s", @@ -2485,8 +2621,8 @@ static int img_rebase(int argc, char **argv) ret = -1; goto out; } - if (bs_new_backing) { - new_backing_num_sectors = bdrv_nb_sectors(bs_new_backing); + if (blk_new_backing) { + new_backing_num_sectors = blk_nb_sectors(blk_new_backing); if (new_backing_num_sectors < 0) { error_report("Could not get size of '%s': %s", out_baseimg, strerror(-new_backing_num_sectors)); @@ -2531,21 +2667,21 @@ static int img_rebase(int argc, char **argv) n = old_backing_num_sectors - sector; } - ret = bdrv_read(bs_old_backing, sector, buf_old, n); + ret = blk_read(blk_old_backing, sector, buf_old, n); if (ret < 0) { error_report("error while reading from old backing file"); goto out; } } - if (sector >= new_backing_num_sectors || !bs_new_backing) { + if (sector >= new_backing_num_sectors || !blk_new_backing) { memset(buf_new, 0, n * BDRV_SECTOR_SIZE); } else { if (sector + n > new_backing_num_sectors) { n = new_backing_num_sectors - sector; } - ret = bdrv_read(bs_new_backing, sector, buf_new, n); + ret = blk_read(blk_new_backing, sector, buf_new, n); if (ret < 0) { error_report("error while reading from new backing file"); goto out; @@ -2561,8 +2697,8 @@ static int img_rebase(int argc, char **argv) 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); + ret = blk_write(blk, sector + written, + buf_old + written * 512, pnum); if (ret < 0) { error_report("Error while writing to COW image: %s", strerror(-ret)); @@ -2609,15 +2745,11 @@ out: qemu_progress_end(); /* Cleanup */ if (!unsafe) { - if (bs_old_backing != NULL) { - bdrv_unref(bs_old_backing); - } - if (bs_new_backing != NULL) { - bdrv_unref(bs_new_backing); - } + blk_unref(blk_old_backing); + blk_unref(blk_new_backing); } - bdrv_unref(bs); + blk_unref(blk); if (ret) { return 1; } @@ -2626,11 +2758,12 @@ out: static int img_resize(int argc, char **argv) { + Error *err = NULL; int c, ret, relative; const char *filename, *fmt, *size; int64_t n, total_size; bool quiet = false; - BlockDriverState *bs = NULL; + BlockBackend *blk = NULL; QemuOpts *param; static QemuOptsList resize_options = { .name = "resize_options", @@ -2697,8 +2830,9 @@ static int img_resize(int argc, char **argv) /* Parse size */ param = qemu_opts_create(&resize_options, NULL, 0, &error_abort); - if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) { - /* Error message already printed when size parsing fails */ + qemu_opt_set(param, BLOCK_OPT_SIZE, size, &err); + if (err) { + error_report_err(err); ret = -1; qemu_opts_del(param); goto out; @@ -2706,15 +2840,15 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - bs = bdrv_new_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, - true, quiet); - if (!bs) { + blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, + true, quiet); + if (!blk) { ret = -1; goto out; } if (relative) { - total_size = bdrv_getlength(bs) + n * relative; + total_size = blk_getlength(blk) + n * relative; } else { total_size = n; } @@ -2724,7 +2858,7 @@ static int img_resize(int argc, char **argv) goto out; } - ret = bdrv_truncate(bs, total_size); + ret = blk_truncate(blk, total_size); switch (ret) { case 0: qprintf(quiet, "Image resized.\n"); @@ -2740,29 +2874,35 @@ static int img_resize(int argc, char **argv) break; } out: - if (bs) { - bdrv_unref(bs); - } + blk_unref(blk); if (ret) { return 1; } return 0; } +static void amend_status_cb(BlockDriverState *bs, + int64_t offset, int64_t total_work_size) +{ + qemu_progress_print(100.f * offset / total_work_size, 0); +} + static int img_amend(int argc, char **argv) { + Error *err = NULL; int c, ret = 0; char *options = NULL; QemuOptsList *create_opts = NULL; QemuOpts *opts = NULL; const char *fmt = NULL, *filename, *cache; int flags; - bool quiet = false; + bool quiet = false, progress = false; + BlockBackend *blk = NULL; BlockDriverState *bs = NULL; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "ho:f:t:q"); + c = getopt(argc, argv, "ho:f:t:pq"); if (c == -1) { break; } @@ -2792,6 +2932,9 @@ static int img_amend(int argc, char **argv) case 't': cache = optarg; break; + case 'p': + progress = true; + break; case 'q': quiet = true; break; @@ -2802,6 +2945,11 @@ static int img_amend(int argc, char **argv) error_exit("Must specify options (-o)"); } + if (quiet) { + progress = false; + } + qemu_progress_init(progress, 1.0); + filename = (optind == argc - 1) ? argv[argc - 1] : NULL; if (fmt && has_help_option(options)) { /* If a format is explicitly specified (and possibly no filename is @@ -2811,7 +2959,9 @@ static int img_amend(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_report("Expecting one image file name"); + ret = -1; + goto out; } flags = BDRV_O_FLAGS | BDRV_O_RDWR; @@ -2821,12 +2971,12 @@ static int img_amend(int argc, char **argv) goto out; } - bs = bdrv_new_open("image", filename, fmt, flags, true, quiet); - if (!bs) { - error_report("Could not open image '%s'", filename); + blk = img_open("image", filename, fmt, flags, true, quiet); + if (!blk) { ret = -1; goto out; } + bs = blk_bs(blk); fmt = bs->drv->format_name; @@ -2836,24 +2986,37 @@ static int img_amend(int argc, char **argv) goto out; } - create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); - opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); - if (options && qemu_opts_do_parse(opts, options, NULL)) { - error_report("Invalid options for file format '%s'", fmt); + if (!bs->drv->create_opts) { + error_report("Format driver '%s' does not support any options to amend", + fmt); ret = -1; goto out; } - ret = bdrv_amend_options(bs, opts); + create_opts = qemu_opts_append(create_opts, bs->drv->create_opts); + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (options) { + qemu_opts_do_parse(opts, options, NULL, &err); + if (err) { + error_report_err(err); + ret = -1; + goto out; + } + } + + /* In case the driver does not call amend_status_cb() */ + qemu_progress_print(0.f, 0); + ret = bdrv_amend_options(bs, opts, &amend_status_cb); + qemu_progress_print(100.f, 0); if (ret < 0) { error_report("Error while amending options: %s", strerror(-ret)); goto out; } out: - if (bs) { - bdrv_unref(bs); - } + qemu_progress_end(); + + blk_unref(blk); qemu_opts_del(opts); qemu_opts_free(create_opts); g_free(options); @@ -2893,8 +3056,7 @@ int main(int argc, char **argv) qemu_init_exec_dir(argv[0]); if (qemu_init_main_loop(&local_error)) { - error_report("%s", error_get_pretty(local_error)); - error_free(local_error); + error_report_err(local_error); exit(EXIT_FAILURE); }