X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-img.c;h=4cf4d2423df82178526f1731480bad8c11275dbf;hb=f3f0cbaf97c78b1fc1f3855f576e46ea6ad4f34c;hp=d0b1c97562e84fe20fd58b5a003ee08b713c87b5;hpb=c47edb8dda0660180f86df4defae2a1f60e345db;p=mirror_qemu.git diff --git a/qemu-img.c b/qemu-img.c index d0b1c97562..4cf4d2423d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -25,7 +25,8 @@ #include "qemu/osdep.h" #include -#include "qemu-common.h" +#include "qemu/help-texts.h" +#include "qemu/qemu-progress.h" #include "qemu-version.h" #include "qapi/error.h" #include "qapi/qapi-commands-block-core.h" @@ -33,7 +34,6 @@ #include "qapi/qobject-output-visitor.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" #include "qemu/cutils.h" #include "qemu/config-file.h" #include "qemu/option.h" @@ -43,6 +43,7 @@ #include "qemu/module.h" #include "qemu/sockets.h" #include "qemu/units.h" +#include "qemu/memalign.h" #include "qom/object_interfaces.h" #include "sysemu/block-backend.h" #include "block/block_int.h" @@ -50,6 +51,8 @@ #include "block/qapi.h" #include "crypto/init.h" #include "trace/control.h" +#include "qemu/throttle.h" +#include "block/throttle-groups.h" #define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \ "\n" QEMU_COPYRIGHT "\n" @@ -81,6 +84,7 @@ enum { OPTION_MERGE = 274, OPTION_BITMAPS = 275, OPTION_FORCE = 276, + OPTION_SKIP_BROKEN = 277, }; typedef enum OutputFormat { @@ -96,7 +100,8 @@ static void format_print(void *opaque, const char *name) printf(" %s", name); } -static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) +static G_NORETURN G_GNUC_PRINTF(1, 2) +void error_exit(const char *fmt, ...) { va_list ap; @@ -108,18 +113,21 @@ static void QEMU_NORETURN GCC_FMT_ATTR(1, 2) error_exit(const char *fmt, ...) exit(EXIT_FAILURE); } -static void QEMU_NORETURN missing_argument(const char *option) +static G_NORETURN +void missing_argument(const char *option) { error_exit("missing argument for option '%s'", option); } -static void QEMU_NORETURN unrecognized_option(const char *option) +static G_NORETURN +void unrecognized_option(const char *option) { error_exit("unrecognized option '%s'", option); } /* Please keep in synch with docs/tools/qemu-img.rst */ -static void QEMU_NORETURN help(void) +static G_NORETURN +void help(void) { const char *help_msg = QEMU_IMG_VERSION @@ -225,23 +233,6 @@ static void QEMU_NORETURN help(void) exit(EXIT_SUCCESS); } -static QemuOptsList qemu_object_opts = { - .name = "object", - .implied_opt_name = "qom-type", - .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), - .desc = { - { } - }, -}; - -static bool qemu_img_object_print_help(const char *type, QemuOpts *opts) -{ - if (user_creatable_print_help(type, opts)) { - exit(0); - } - return true; -} - /* * Is @optarg safe for accumulate_options()? * It is when multiple of them can be joined together separated by ','. @@ -298,7 +289,7 @@ static QemuOptsList qemu_source_opts = { }, }; -static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...) +static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...) { int ret = 0; if (!quiet) { @@ -565,14 +556,9 @@ static int img_create(int argc, char **argv) case 'u': flags |= BDRV_O_NO_BACKING; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - goto fail; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; } } @@ -588,12 +574,6 @@ static int img_create(int argc, char **argv) } optind++; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - goto fail; - } - /* Get image size, if specified */ if (optind < argc) { int64_t sval; @@ -625,18 +605,18 @@ fail: static void dump_json_image_check(ImageCheck *check, bool quiet) { - QString *str; + GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageCheck(v, NULL, &check, &error_abort); visit_complete(v, &obj); - str = qobject_to_json_pretty(obj); + str = qobject_to_json_pretty(obj, true); assert(str != NULL); - qprintf(quiet, "%s\n", qstring_get_str(str)); + qprintf(quiet, "%s\n", str->str); qobject_unref(obj); visit_free(v); - qobject_unref(str); + g_string_free(str, true); } static void dump_human_image_check(ImageCheck *check, bool quiet) @@ -803,14 +783,9 @@ static int img_check(int argc, char **argv) case 'U': force_share = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -830,12 +805,6 @@ static int img_check(int argc, char **argv) return 1; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); @@ -938,7 +907,8 @@ static void common_block_job_cb(void *opaque, int ret) static void run_block_job(BlockJob *job, Error **errp) { - AioContext *aio_context = blk_get_aio_context(job->blk); + uint64_t progress_current, progress_total; + AioContext *aio_context = block_job_get_aio_context(job); int ret = 0; aio_context_acquire(aio_context); @@ -946,9 +916,11 @@ static void run_block_job(BlockJob *job, Error **errp) do { float progress = 0.0f; aio_poll(aio_context, true); - if (job->job.progress.total) { - progress = (float)job->job.progress.current / - job->job.progress.total * 100.f; + + progress_get_snapshot(&job->job.progress, &progress_current, + &progress_total); + if (progress_total) { + progress = (float)progress_current / progress_total * 100.f; } qemu_progress_print(progress, 0); } while (!job_is_ready(&job->job) && !job_is_completed(&job->job)); @@ -980,6 +952,7 @@ static int img_commit(int argc, char **argv) CommonBlockJobCBInfo cbi; bool image_opts = false; AioContext *aio_context; + int64_t rate_limit = 0; fmt = NULL; cache = BDRV_DEFAULT_CACHE; @@ -991,7 +964,7 @@ static int img_commit(int argc, char **argv) {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:ht:b:dpq", + c = getopt_long(argc, argv, ":f:ht:b:dpqr:", long_options, NULL); if (c == -1) { break; @@ -1026,14 +999,15 @@ static int img_commit(int argc, char **argv) case 'q': quiet = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { return 1; } - } break; + break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -1050,12 +1024,6 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - flags = BDRV_O_RDWR | BDRV_O_UNMAP; ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { @@ -1099,7 +1067,7 @@ static int img_commit(int argc, char **argv) aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0, + commit_active_start("commit", bs, base_bs, JOB_DEFAULT, rate_limit, BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb, &cbi, false, &local_err); aio_context_release(aio_context); @@ -1188,7 +1156,7 @@ static int64_t find_nonzero(const uint8_t *buf, int64_t n) * 'pnum' is set to the number of sectors (including and immediately following * the first one) that are known to be in the same allocated/unallocated state. * The function will try to align the end offset to alignment boundaries so - * that the request will at least end aligned and consequtive requests will + * that the request will at least end aligned and consecutive requests will * also start at an aligned offset. */ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, @@ -1201,27 +1169,42 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum, *pnum = 0; return 0; } - is_zero = buffer_is_zero(buf, 512); + is_zero = buffer_is_zero(buf, BDRV_SECTOR_SIZE); for(i = 1; i < n; i++) { - buf += 512; - if (is_zero != buffer_is_zero(buf, 512)) { + buf += BDRV_SECTOR_SIZE; + if (is_zero != buffer_is_zero(buf, BDRV_SECTOR_SIZE)) { break; } } + if (i == n) { + /* + * The whole buf is the same. + * No reason to split it into chunks, so return now. + */ + *pnum = i; + return !is_zero; + } + tail = (sector_num + i) & (alignment - 1); if (tail) { if (is_zero && i <= tail) { - /* treat unallocated areas which only consist - * of a small tail as allocated. */ + /* + * For sure next sector after i is data, and it will rewrite this + * tail anyway due to RMW. So, let's just write data now. + */ is_zero = false; } if (!is_zero) { - /* align up end offset of allocated areas. */ + /* If possible, align up end offset of allocated areas. */ i += alignment - tail; i = MIN(i, n); } else { - /* align down end offset of zero areas. */ + /* + * For sure next sector after i is data, and it will rewrite this + * tail anyway due to RMW. Better is avoid RMW and write zeroes up + * to aligned bound. + */ i -= tail; } } @@ -1345,7 +1328,7 @@ static int check_empty_sectors(BlockBackend *blk, int64_t offset, /* * Compares two images. Exit codes: * - * 0 - Images are identical + * 0 - Images are identical or the requested help was printed * 1 - Images differ * >1 - Error occurred */ @@ -1415,15 +1398,21 @@ static int img_compare(int argc, char **argv) case 'U': force_share = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - ret = 2; - goto out4; + case OPTION_OBJECT: + { + Error *local_err = NULL; + + if (!user_creatable_add_from_str(optarg, &local_err)) { + if (local_err) { + error_report_err(local_err); + exit(2); + } else { + /* Help was printed */ + exit(EXIT_SUCCESS); + } + } + break; } - } break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -1442,13 +1431,6 @@ static int img_compare(int argc, char **argv) filename1 = argv[optind++]; filename2 = argv[optind++]; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - ret = 2; - goto out4; - } - /* Initialize before goto out */ qemu_progress_init(progress, 2.0); @@ -1633,7 +1615,6 @@ out2: blk_unref(blk1); out3: qemu_progress_end(); -out4: return ret; } @@ -1642,17 +1623,16 @@ static void do_dirty_bitmap_merge(const char *dst_node, const char *dst_name, const char *src_node, const char *src_name, Error **errp) { - BlockDirtyBitmapMergeSource *merge_src; - BlockDirtyBitmapMergeSourceList *list; + BlockDirtyBitmapOrStr *merge_src; + BlockDirtyBitmapOrStrList *list = NULL; - merge_src = g_new0(BlockDirtyBitmapMergeSource, 1); + merge_src = g_new0(BlockDirtyBitmapOrStr, 1); merge_src->type = QTYPE_QDICT; merge_src->u.external.node = g_strdup(src_node); merge_src->u.external.name = g_strdup(src_name); - list = g_new0(BlockDirtyBitmapMergeSourceList, 1); - list->value = merge_src; + QAPI_LIST_PREPEND(list, merge_src); qmp_block_dirty_bitmap_merge(dst_node, dst_name, list, errp); - qapi_free_BlockDirtyBitmapMergeSourceList(list); + qapi_free_BlockDirtyBitmapOrStrList(list); } enum ImgConvertBlockStatus { @@ -1662,10 +1642,12 @@ enum ImgConvertBlockStatus { }; #define MAX_COROUTINES 16 +#define CONVERT_THROTTLE_GROUP "img_convert" typedef struct ImgConvertState { BlockBackend **src; int64_t *src_sectors; + int *src_alignment; int src_num; int64_t total_sectors; int64_t allocated_sectors; @@ -1732,6 +1714,7 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) if (s->sector_next_status <= sector_num) { uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE; int64_t count; + int tail; BlockDriverState *src_bs = blk_bs(s->src[src_cur]); BlockDriverState *base; @@ -1772,6 +1755,16 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); + /* + * Avoid that s->sector_next_status becomes unaligned to the source + * request alignment and/or cluster size to avoid unnecessary read + * cycles. + */ + tail = (sector_num - src_cur_offset + n) % s->src_alignment[src_cur]; + if (n > tail) { + n -= tail; + } + if (ret & BDRV_BLOCK_ZERO) { s->status = post_backing_zero ? BLK_BACKING_FILE : BLK_ZERO; } else if (ret & BDRV_BLOCK_DATA) { @@ -2130,7 +2123,32 @@ static int convert_do_copy(ImgConvertState *s) return s->ret; } -static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) +/* Check that bitmaps can be copied, or output an error */ +static int convert_check_bitmaps(BlockDriverState *src, bool skip_broken) +{ + BdrvDirtyBitmap *bm; + + if (!bdrv_supports_persistent_dirty_bitmap(src)) { + error_report("Source lacks bitmap support"); + return -1; + } + FOR_EACH_DIRTY_BITMAP(src, bm) { + if (!bdrv_dirty_bitmap_get_persistence(bm)) { + continue; + } + if (!skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { + error_report("Cannot copy inconsistent bitmap '%s'", + bdrv_dirty_bitmap_name(bm)); + error_printf("Try --skip-broken-bitmaps, or " + "use 'qemu-img bitmap --remove' to delete it\n"); + return -1; + } + } + return 0; +} + +static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst, + bool skip_broken) { BdrvDirtyBitmap *bm; Error *err = NULL; @@ -2142,6 +2160,10 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) continue; } name = bdrv_dirty_bitmap_name(bm); + if (skip_broken && bdrv_dirty_bitmap_inconsistent(bm)) { + warn_report("Skipping inconsistent bitmap '%s'", name); + continue; + } qmp_block_dirty_bitmap_add(dst->node_name, name, true, bdrv_dirty_bitmap_granularity(bm), true, true, @@ -2156,6 +2178,7 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) &err); if (err) { error_reportf_err(err, "Failed to populate bitmap %s: ", name); + qmp_block_dirty_bitmap_remove(dst->node_name, name, NULL); return -1; } } @@ -2165,12 +2188,24 @@ static int convert_copy_bitmaps(BlockDriverState *src, BlockDriverState *dst) #define MAX_BUF_SECTORS 32768 +static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) +{ + ThrottleConfig cfg; + + throttle_config_init(&cfg); + cfg.buckets[THROTTLE_BPS_WRITE].avg = rate_limit; + + blk_io_limits_enable(blk, CONVERT_THROTTLE_GROUP); + blk_set_io_limits(blk, &cfg); +} + static int img_convert(int argc, char **argv) { - int c, bs_i, flags, src_flags = 0; + int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE; const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", *src_cache = BDRV_DEFAULT_CACHE, *out_baseimg = NULL, - *out_filename, *out_baseimg_param, *snapshot_name = NULL; + *out_filename, *out_baseimg_param, *snapshot_name = NULL, + *backing_fmt = NULL; BlockDriver *drv = NULL, *proto_drv = NULL; BlockDriverInfo bdi; BlockDriverState *out_bs; @@ -2185,6 +2220,8 @@ static int img_convert(int argc, char **argv) bool force_share = false; bool explict_min_sparse = false; bool bitmaps = false; + bool skip_broken = false; + int64_t rate_limit = 0; ImgConvertState s = (ImgConvertState) { /* Need at least 4k of zeros for sparse detection */ @@ -2205,9 +2242,10 @@ static int img_convert(int argc, char **argv) {"salvage", no_argument, 0, OPTION_SALVAGE}, {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, {"bitmaps", no_argument, 0, OPTION_BITMAPS}, + {"skip-broken-bitmaps", no_argument, 0, OPTION_SKIP_BROKEN}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU", + c = getopt_long(argc, argv, ":hf:O:B:CcF:o:l:S:pt:T:qnm:WUr:", long_options, NULL); if (c == -1) { break; @@ -2237,6 +2275,9 @@ static int img_convert(int argc, char **argv) case 'c': s.compressed = true; break; + case 'F': + backing_fmt = optarg; + break; case 'o': if (accumulate_options(&options, optarg) < 0) { goto fail_getopt; @@ -2304,15 +2345,15 @@ static int img_convert(int argc, char **argv) case 'U': force_share = true; break; - case OPTION_OBJECT: { - QemuOpts *object_opts; - object_opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!object_opts) { + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { goto fail_getopt; } break; - } + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -2333,6 +2374,9 @@ static int img_convert(int argc, char **argv) case OPTION_BITMAPS: bitmaps = true; break; + case OPTION_SKIP_BROKEN: + skip_broken = true; + break; } } @@ -2340,9 +2384,8 @@ static int img_convert(int argc, char **argv) out_fmt = "raw"; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { + if (skip_broken && !bitmaps) { + error_report("Use of --skip-broken-bitmaps requires --bitmaps"); goto fail_getopt; } @@ -2410,8 +2453,10 @@ static int img_convert(int argc, char **argv) s.src = g_new0(BlockBackend *, s.src_num); s.src_sectors = g_new(int64_t, s.src_num); + s.src_alignment = g_new(int, s.src_num); for (bs_i = 0; bs_i < s.src_num; bs_i++) { + BlockDriverState *src_bs; s.src[bs_i] = img_open(image_opts, argv[optind + bs_i], fmt, src_flags, src_writethrough, s.quiet, force_share); @@ -2426,6 +2471,13 @@ static int img_convert(int argc, char **argv) ret = -1; goto out; } + src_bs = blk_bs(s.src[bs_i]); + s.src_alignment[bs_i] = DIV_ROUND_UP(src_bs->bl.request_alignment, + BDRV_SECTOR_SIZE); + if (!bdrv_get_info(src_bs, &bdi)) { + s.src_alignment[bs_i] = MAX(s.src_alignment[bs_i], + bdi.cluster_size / BDRV_SECTOR_SIZE); + } s.total_sectors += s.src_sectors[bs_i]; } @@ -2492,9 +2544,9 @@ static int img_convert(int argc, char **argv) } } - qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s.total_sectors * 512, - &error_abort); - ret = add_old_style_options(out_fmt, opts, out_baseimg, NULL); + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, + s.total_sectors * BDRV_SECTOR_SIZE, &error_abort); + ret = add_old_style_options(out_fmt, opts, out_baseimg, backing_fmt); if (ret < 0) { goto out; } @@ -2522,8 +2574,10 @@ static int img_convert(int argc, char **argv) if (out_baseimg_param) { if (!qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT)) { - warn_report("Deprecated use of backing file without explicit " - "backing format"); + error_report("Use of backing file requires explicit " + "backing format"); + ret = -1; + goto out; } } @@ -2566,9 +2620,8 @@ static int img_convert(int argc, char **argv) ret = -1; goto out; } - if (!bdrv_supports_persistent_dirty_bitmap(blk_bs(s.src[0]))) { - error_report("Source lacks bitmap support"); - ret = -1; + ret = convert_check_bitmaps(blk_bs(s.src[0]), skip_broken); + if (ret < 0) { goto out; } } @@ -2600,6 +2653,14 @@ static int img_convert(int argc, char **argv) goto out; } + if (flags & BDRV_O_NOCACHE) { + /* + * If we open the target with O_DIRECT, it may be necessary to + * extend its size to align to the physical sector size. + */ + flags |= BDRV_O_RESIZE; + } + if (skip_create) { s.target = img_open(tgt_image_opts, out_filename, out_fmt, flags, writethrough, s.quiet, false); @@ -2684,11 +2745,15 @@ static int img_convert(int argc, char **argv) s.cluster_sectors = bdi.cluster_size / BDRV_SECTOR_SIZE; } + if (rate_limit) { + set_rate_limit(s.target, rate_limit); + } + ret = convert_do_copy(&s); /* Now copy the bitmaps */ if (bitmaps && ret == 0) { - ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs); + ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken); } out: @@ -2698,7 +2763,6 @@ out: qemu_progress_end(); qemu_opts_del(opts); qemu_opts_free(create_opts); - qemu_opts_del(sn_opts); qobject_unref(open_opts); blk_unref(s.target); if (s.src) { @@ -2708,7 +2772,9 @@ out: g_free(s.src); } g_free(s.src_sectors); + g_free(s.src_alignment); fail_getopt: + qemu_opts_del(sn_opts); g_free(options); return !!ret; @@ -2736,34 +2802,34 @@ static void dump_snapshots(BlockDriverState *bs) static void dump_json_image_info_list(ImageInfoList *list) { - QString *str; + GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageInfoList(v, NULL, &list, &error_abort); visit_complete(v, &obj); - str = qobject_to_json_pretty(obj); + str = qobject_to_json_pretty(obj, true); assert(str != NULL); - printf("%s\n", qstring_get_str(str)); + printf("%s\n", str->str); qobject_unref(obj); visit_free(v); - qobject_unref(str); + g_string_free(str, true); } static void dump_json_image_info(ImageInfo *info) { - QString *str; + GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); visit_type_ImageInfo(v, NULL, &info, &error_abort); visit_complete(v, &obj); - str = qobject_to_json_pretty(obj); + str = qobject_to_json_pretty(obj, true); assert(str != NULL); - printf("%s\n", qstring_get_str(str)); + printf("%s\n", str->str); qobject_unref(obj); visit_free(v); - qobject_unref(str); + g_string_free(str, true); } static void dump_human_image_info_list(ImageInfoList *list) @@ -2804,7 +2870,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, bool chain, bool force_share) { ImageInfoList *head = NULL; - ImageInfoList **last = &head; + ImageInfoList **tail = &head; GHashTable *filenames; Error *err = NULL; @@ -2814,7 +2880,6 @@ static ImageInfoList *collect_image_info_list(bool image_opts, BlockBackend *blk; BlockDriverState *bs; ImageInfo *info; - ImageInfoList *elem; if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) { error_report("Backing file '%s' creates an infinite loop.", @@ -2838,10 +2903,7 @@ static ImageInfoList *collect_image_info_list(bool image_opts, goto err; } - elem = g_new0(ImageInfoList, 1); - elem->value = info; - *last = elem; - last = &elem->next; + QAPI_LIST_APPEND(tail, info); blk_unref(blk); @@ -2923,14 +2985,9 @@ static int img_info(int argc, char **argv) case OPTION_BACKING_CHAIN: chain = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -2950,12 +3007,6 @@ static int img_info(int argc, char **argv) return 1; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - list = collect_image_info_list(image_opts, filename, fmt, chain, force_share); if (!list) { @@ -3004,8 +3055,9 @@ static int dump_map_entry(OutputFormat output_format, MapEntry *e, break; case OFORMAT_JSON: printf("{ \"start\": %"PRId64", \"length\": %"PRId64"," - " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", - e->start, e->length, e->depth, + " \"depth\": %"PRId64", \"present\": %s, \"zero\": %s," + " \"data\": %s", e->start, e->length, e->depth, + e->present ? "true" : "false", e->zero ? "true" : "false", e->data ? "true" : "false"); if (e->has_offset) { @@ -3071,6 +3123,7 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, .offset = map, .has_offset = has_offset, .depth = depth, + .present = !!(ret & BDRV_BLOCK_ALLOCATED), .has_filename = filename, .filename = filename, }; @@ -3086,6 +3139,7 @@ static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) if (curr->zero != next->zero || curr->data != next->data || curr->depth != next->depth || + curr->present != next->present || curr->has_filename != next->has_filename || curr->has_offset != next->has_offset) { return false; @@ -3165,14 +3219,9 @@ static int img_map(int argc, char **argv) return 1; } break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -3192,12 +3241,6 @@ static int img_map(int argc, char **argv) return 1; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - blk = img_open(image_opts, filename, fmt, 0, false, false, force_share); if (!blk) { return 1; @@ -3267,11 +3310,11 @@ static int img_snapshot(int argc, char **argv) char *filename, *snapshot_name = NULL; int c, ret = 0, bdrv_oflags; int action = 0; - qemu_timeval tv; bool quiet = false; Error *err = NULL; bool image_opts = false; bool force_share = false; + int64_t rt; bdrv_oflags = BDRV_O_RDWR; /* Parse commandline parameters */ @@ -3336,14 +3379,9 @@ static int img_snapshot(int argc, char **argv) case 'U': force_share = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -3355,12 +3393,6 @@ static int img_snapshot(int argc, char **argv) } filename = argv[optind++]; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - /* Open the image */ blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet, force_share); @@ -3379,9 +3411,9 @@ static int img_snapshot(int argc, char **argv) memset(&sn, 0, sizeof(sn)); pstrcpy(sn.name, sizeof(sn.name), snapshot_name); - qemu_gettimeofday(&tv); - sn.date_sec = tv.tv_sec; - sn.date_nsec = tv.tv_usec * 1000; + rt = g_get_real_time(); + sn.date_sec = rt / G_USEC_PER_SEC; + sn.date_nsec = (rt % G_USEC_PER_SEC) * 1000; ret = bdrv_snapshot_create(bs, &sn); if (ret) { @@ -3494,14 +3526,9 @@ static int img_rebase(int argc, char **argv) case 'q': quiet = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -3523,12 +3550,6 @@ static int img_rebase(int argc, char **argv) } filename = argv[optind++]; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - qemu_progress_init(progress, 2.0); qemu_progress_print(0, 100); @@ -3822,6 +3843,9 @@ static int img_rebase(int argc, char **argv) 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 == -EINVAL && out_baseimg && !out_basefmt) { + error_report("Could not change the backing file to '%s': backing " + "format must be specified", out_baseimg); } else if (ret < 0) { error_report("Could not change the backing file to '%s': %s", out_baseimg, strerror(-ret)); @@ -3919,14 +3943,9 @@ static int img_resize(int argc, char **argv) case 'q': quiet = true; break; - case OPTION_OBJECT: { - QemuOpts *opts; - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - return 1; - } - } break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -3948,12 +3967,6 @@ static int img_resize(int argc, char **argv) } filename = argv[optind++]; - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - return 1; - } - /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) { case '+': @@ -4133,12 +4146,7 @@ static int img_amend(int argc, char **argv) quiet = true; break; case OPTION_OBJECT: - opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!opts) { - ret = -1; - goto out_no_progress; - } + user_creatable_process_cmdline(optarg); break; case OPTION_IMAGE_OPTS: image_opts = true; @@ -4153,13 +4161,6 @@ static int img_amend(int argc, char **argv) error_exit("Must specify options (-o)"); } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - ret = -1; - goto out_no_progress; - } - if (quiet) { progress = false; } @@ -4712,10 +4713,7 @@ static int img_bitmap(int argc, char **argv) merge = true; break; case OPTION_OBJECT: - opts = qemu_opts_parse_noisily(&qemu_object_opts, optarg, true); - if (!opts) { - goto out; - } + user_creatable_process_cmdline(optarg); break; case OPTION_IMAGE_OPTS: image_opts = true; @@ -4723,12 +4721,6 @@ static int img_bitmap(int argc, char **argv) } } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - goto out; - } - if (QSIMPLEQ_EMPTY(&actions)) { error_report("Need at least one of --add, --remove, --clear, " "--enable, --disable, or --merge"); @@ -4757,14 +4749,19 @@ static int img_bitmap(int argc, char **argv) filename = argv[optind]; bitmap = argv[optind + 1]; - blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR, false, false, - false); + /* + * No need to open backing chains; we will be manipulating bitmaps + * directly in this image without reference to image contents. + */ + blk = img_open(image_opts, filename, fmt, BDRV_O_RDWR | BDRV_O_NO_BACKING, + false, false, false); if (!blk) { goto out; } bs = blk_bs(blk); if (src_filename) { - src = img_open(false, src_filename, src_fmt, 0, false, false, false); + src = img_open(false, src_filename, src_fmt, BDRV_O_NO_BACKING, + false, false, false); if (!src) { goto out; } @@ -4981,10 +4978,7 @@ static int img_dd(int argc, char **argv) force_share = true; break; case OPTION_OBJECT: - if (!qemu_opts_parse_noisily(&qemu_object_opts, optarg, true)) { - ret = -1; - goto out; - } + user_creatable_process_cmdline(optarg); break; case OPTION_IMAGE_OPTS: image_opts = true; @@ -5031,13 +5025,6 @@ static int img_dd(int argc, char **argv) goto out; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - ret = -1; - goto out; - } - blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, force_share); @@ -5178,18 +5165,18 @@ out: static void dump_json_block_measure_info(BlockMeasureInfo *info) { - QString *str; + GString *str; QObject *obj; Visitor *v = qobject_output_visitor_new(&obj); visit_type_BlockMeasureInfo(v, NULL, &info, &error_abort); visit_complete(v, &obj); - str = qobject_to_json_pretty(obj); + str = qobject_to_json_pretty(obj, true); assert(str != NULL); - printf("%s\n", qstring_get_str(str)); + printf("%s\n", str->str); qobject_unref(obj); visit_free(v); - qobject_unref(str); + g_string_free(str, true); } static int img_measure(int argc, char **argv) @@ -5258,11 +5245,7 @@ static int img_measure(int argc, char **argv) force_share = true; break; case OPTION_OBJECT: - object_opts = qemu_opts_parse_noisily(&qemu_object_opts, - optarg, true); - if (!object_opts) { - goto out; - } + user_creatable_process_cmdline(optarg); break; case OPTION_IMAGE_OPTS: image_opts = true; @@ -5292,12 +5275,6 @@ static int img_measure(int argc, char **argv) } } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - qemu_img_object_print_help, &error_fatal)) { - goto out; - } - if (argc - optind > 1) { error_report("At most one filename argument is allowed."); goto out; @@ -5406,8 +5383,6 @@ int main(int argc, char **argv) { const img_cmd_t *cmd; const char *cmdname; - Error *local_error = NULL; - char *trace_file = NULL; int c; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -5425,10 +5400,7 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_TRACE); qemu_init_exec_dir(argv[0]); - if (qemu_init_main_loop(&local_error)) { - error_report_err(local_error); - exit(EXIT_FAILURE); - } + qemu_init_main_loop(&error_fatal); qcrypto_init(&error_fatal); @@ -5438,7 +5410,6 @@ int main(int argc, char **argv) error_exit("Not enough arguments"); } - qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_source_opts); qemu_add_opts(&qemu_trace_opts); @@ -5457,8 +5428,7 @@ int main(int argc, char **argv) printf(QEMU_IMG_VERSION); return 0; case 'T': - g_free(trace_file); - trace_file = trace_opt_parse(optarg); + trace_opt_parse(optarg); break; } } @@ -5476,8 +5446,8 @@ int main(int argc, char **argv) if (!trace_init_backends()) { exit(1); } - trace_init_file(trace_file); - qemu_set_log(LOG_TRACE); + trace_init_file(); + qemu_set_log(LOG_TRACE, &error_fatal); /* find the command */ for (cmd = img_cmds; cmd->name != NULL; cmd++) {