diff options
Diffstat (limited to 'qemu/qemu-img.c')
-rw-r--r-- | qemu/qemu-img.c | 770 |
1 files changed, 587 insertions, 183 deletions
diff --git a/qemu/qemu-img.c b/qemu/qemu-img.c index 75f4ee442..46f2a6def 100644 --- a/qemu/qemu-img.c +++ b/qemu/qemu-img.c @@ -21,19 +21,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi-visit.h" #include "qapi/qmp-output-visitor.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qjson.h" -#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/error-report.h" -#include "qemu/osdep.h" +#include "qom/object_interfaces.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" #include "block/block_int.h" #include "block/blockjob.h" #include "block/qapi.h" +#include "crypto/init.h" #include <getopt.h> #define QEMU_IMG_VERSION "qemu-img version " QEMU_VERSION QEMU_PKGVERSION \ @@ -47,6 +51,8 @@ typedef struct img_cmd_t { enum { OPTION_OUTPUT = 256, OPTION_BACKING_CHAIN = 257, + OPTION_OBJECT = 258, + OPTION_IMAGE_OPTS = 259, }; typedef enum OutputFormat { @@ -54,8 +60,7 @@ typedef enum OutputFormat { OFORMAT_HUMAN, } OutputFormat; -/* Default to cache=writeback as data integrity is not important for qemu-tcg. */ -#define BDRV_O_FLAGS BDRV_O_CACHE_WB +/* Default to cache=writeback as data integrity is not important for qemu-img */ #define BDRV_DEFAULT_CACHE "writeback" static void format_print(void *opaque, const char *name) @@ -94,6 +99,10 @@ static void QEMU_NORETURN help(void) "\n" "Command parameters:\n" " 'filename' is a disk image filename\n" + " 'objectdef' is a QEMU user creatable object definition. See the qemu(1)\n" + " manual page for a description of the object properties. The most common\n" + " object type is a 'secret', which is used to supply passwords and/or\n" + " encryption keys.\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" " 'cache' is the cache mode used to write the output disk image, the valid\n" " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" @@ -154,6 +163,24 @@ 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 QemuOptsList qemu_source_opts = { + .name = "source", + .implied_opt_name = "file", + .head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head), + .desc = { + { } + }, +}; + static int GCC_FMT_ATTR(2, 3) qprintf(bool quiet, const char *fmt, ...) { int ret = 0; @@ -196,13 +223,57 @@ static int print_block_option_help(const char *filename, const char *fmt) return 0; } -static BlockBackend *img_open(const char *id, const char *filename, - const char *fmt, int flags, - bool require_io, bool quiet) + +static int img_open_password(BlockBackend *blk, const char *filename, + int flags, bool quiet) { - BlockBackend *blk; BlockDriverState *bs; char password[256]; + + bs = blk_bs(blk); + if (bdrv_is_encrypted(bs) && bdrv_key_required(bs) && + !(flags & BDRV_O_NO_IO)) { + qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); + if (qemu_read_password(password, sizeof(password)) < 0) { + error_report("No password given"); + return -1; + } + if (bdrv_set_key(bs, password) < 0) { + error_report("invalid password"); + return -1; + } + } + return 0; +} + + +static BlockBackend *img_open_opts(const char *optstr, + QemuOpts *opts, int flags, bool writethrough, + bool quiet) +{ + QDict *options; + Error *local_err = NULL; + BlockBackend *blk; + options = qemu_opts_to_qdict(opts, NULL); + blk = blk_new_open(NULL, NULL, options, flags, &local_err); + if (!blk) { + error_reportf_err(local_err, "Could not open '%s': ", optstr); + return NULL; + } + blk_set_enable_write_cache(blk, !writethrough); + + if (img_open_password(blk, optstr, flags, quiet) < 0) { + blk_unref(blk); + return NULL; + } + return blk; +} + +static BlockBackend *img_open_file(const char *filename, + const char *fmt, int flags, + bool writethrough, bool quiet) +{ + BlockBackend *blk; Error *local_err = NULL; QDict *options = NULL; @@ -211,32 +282,46 @@ static BlockBackend *img_open(const char *id, const char *filename, qdict_put(options, "driver", qstring_from_str(fmt)); } - blk = blk_new_open(id, filename, NULL, options, flags, &local_err); + blk = blk_new_open(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; + error_reportf_err(local_err, "Could not open '%s': ", filename); + return NULL; } + blk_set_enable_write_cache(blk, !writethrough); - bs = blk_bs(blk); - if (bdrv_is_encrypted(bs) && require_io) { - qprintf(quiet, "Disk image '%s' is encrypted.\n", filename); - if (qemu_read_password(password, sizeof(password)) < 0) { - error_report("No password given"); - goto fail; - } - if (bdrv_set_key(bs, password) < 0) { - error_report("invalid password"); - goto fail; - } + if (img_open_password(blk, filename, flags, quiet) < 0) { + blk_unref(blk); + return NULL; } return blk; -fail: - blk_unref(blk); - return NULL; } + +static BlockBackend *img_open(bool image_opts, + const char *filename, + const char *fmt, int flags, bool writethrough, + bool quiet) +{ + BlockBackend *blk; + if (image_opts) { + QemuOpts *opts; + if (fmt) { + error_report("--image-opts and --format are mutually exclusive"); + return NULL; + } + opts = qemu_opts_parse_noisily(qemu_find_opts("source"), + filename, true); + if (!opts) { + return NULL; + } + blk = img_open_opts(filename, opts, flags, writethrough, quiet); + } else { + blk = img_open_file(filename, fmt, flags, writethrough, quiet); + } + return blk; +} + + static int add_old_style_options(const char *fmt, QemuOpts *opts, const char *base_filename, const char *base_fmt) @@ -277,7 +362,13 @@ static int img_create(int argc, char **argv) bool quiet = false; for(;;) { - c = getopt(argc, argv, "F:b:f:he6o:q"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "F:b:f:he6o:q", + long_options, NULL); if (c == -1) { break; } @@ -319,6 +410,14 @@ static int img_create(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) { + goto fail; + } + } break; } } @@ -334,11 +433,18 @@ static int img_create(int argc, char **argv) } optind++; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + goto fail; + } + /* Get image size, if specified */ if (optind < argc) { int64_t sval; char *end; - sval = strtosz_suffix(argv[optind++], &end, STRTOSZ_DEFSUFFIX_B); + sval = qemu_strtosz_suffix(argv[optind++], &end, + QEMU_STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { if (sval == -ERANGE) { error_report("Image size must be less than 8 EiB!"); @@ -357,10 +463,9 @@ static int img_create(int argc, char **argv) } bdrv_img_create(filename, fmt, base_filename, base_fmt, - options, img_size, BDRV_O_FLAGS, &local_err, quiet); + options, img_size, 0, &local_err, quiet); if (local_err) { - error_report("%s: %s", filename, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "%s: ", filename); goto fail; } @@ -378,8 +483,8 @@ static void dump_json_image_check(ImageCheck *check, bool quiet) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageCheck(qmp_output_get_visitor(ov), - &check, NULL, &local_err); + visit_type_ImageCheck(qmp_output_get_visitor(ov), NULL, &check, + &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -488,13 +593,16 @@ static int img_check(int argc, char **argv) BlockBackend *blk; BlockDriverState *bs; int fix = 0; - int flags = BDRV_O_FLAGS | BDRV_O_CHECK; + int flags = BDRV_O_CHECK; + bool writethrough; ImageCheck *check; bool quiet = false; + bool image_opts = false; fmt = NULL; output = NULL; cache = BDRV_DEFAULT_CACHE; + for(;;) { int option_index = 0; static const struct option long_options[] = { @@ -502,6 +610,8 @@ static int img_check(int argc, char **argv) {"format", required_argument, 0, 'f'}, {"repair", required_argument, 0, 'r'}, {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "hf:r:T:q", @@ -538,6 +648,17 @@ static int img_check(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_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -554,13 +675,19 @@ static int img_check(int argc, char **argv) return 1; } - ret = bdrv_parse_cache_flags(cache, &flags); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); return 1; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { return 1; } @@ -644,9 +771,6 @@ static void common_block_job_cb(void *opaque, int ret) 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) @@ -655,7 +779,8 @@ static void run_block_job(BlockJob *job, Error **errp) do { aio_poll(aio_context, true); - qemu_progress_print((float)job->offset / job->len * 100.f, 0); + qemu_progress_print(job->len ? + ((float)job->offset / job->len * 100.f) : 0.0f, 0); } while (!job->ready); block_job_complete_sync(job, errp); @@ -672,14 +797,23 @@ static int img_commit(int argc, char **argv) BlockBackend *blk; BlockDriverState *bs, *base_bs; bool progress = false, quiet = false, drop = false; + bool writethrough; Error *local_err = NULL; CommonBlockJobCBInfo cbi; + bool image_opts = false; fmt = NULL; cache = BDRV_DEFAULT_CACHE; base = NULL; for(;;) { - c = getopt(argc, argv, "f:ht:b:dpq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:ht:b:dpq", + long_options, NULL); if (c == -1) { break; } @@ -708,6 +842,17 @@ 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) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -721,14 +866,20 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + flags = BDRV_O_RDWR | BDRV_O_UNMAP; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); return 1; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { return 1; } @@ -747,7 +898,7 @@ static int img_commit(int argc, char **argv) /* 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; + base_bs = backing_bs(bs); if (!base_bs) { error_setg(&local_err, "Image does not have a backing file"); goto done; @@ -765,12 +916,12 @@ static int img_commit(int argc, char **argv) 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. */ + /* When the block job completes, the BlockBackend reference will point to + * the old backing file. In order to avoid that the top image is already + * deleted, so we can still empty it afterwards, increment the reference + * counter here preemptively. */ if (!drop) { - bdrv_ref(base_bs); + bdrv_ref(bs); } run_block_job(bs->job, &local_err); @@ -778,8 +929,8 @@ static int img_commit(int argc, char **argv) goto unref_backing; } - if (!drop && base_bs->drv->bdrv_make_empty) { - ret = base_bs->drv->bdrv_make_empty(base_bs); + if (!drop && bs->drv->bdrv_make_empty) { + ret = bs->drv->bdrv_make_empty(bs); if (ret) { error_setg_errno(&local_err, -ret, "Could not empty %s", filename); @@ -789,7 +940,7 @@ static int img_commit(int argc, char **argv) unref_backing: if (!drop) { - bdrv_unref(base_bs); + bdrv_unref(bs); } done: @@ -972,15 +1123,24 @@ static int img_compare(int argc, char **argv) int ret = 0; /* return value - 0 Ident, 1 Different, >1 Error */ bool progress = false, quiet = false, strict = false; int flags; + bool writethrough; int64_t total_sectors; int64_t sector_num = 0; int64_t nb_sectors; int c, pnum; uint64_t progress_base; + bool image_opts = false; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "hf:F:T:pqs"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:F:T:pqs", + long_options, NULL); if (c == -1) { break; } @@ -1007,6 +1167,18 @@ static int img_compare(int argc, char **argv) case 's': strict = true; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + ret = 2; + goto out4; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -1022,29 +1194,36 @@ 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, + NULL, NULL)) { + ret = 2; + goto out4; + } + /* Initialize before goto out */ qemu_progress_init(progress, 2.0); - flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(cache, &flags); + flags = 0; + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); ret = 2; goto out3; } - blk1 = img_open("image_1", filename1, fmt1, flags, true, quiet); + blk1 = img_open(image_opts, filename1, fmt1, flags, writethrough, quiet); if (!blk1) { ret = 2; goto out3; } - bs1 = blk_bs(blk1); - blk2 = img_open("image_2", filename2, fmt2, flags, true, quiet); + blk2 = img_open(image_opts, filename2, fmt2, flags, writethrough, quiet); if (!blk2) { ret = 2; goto out2; } + bs1 = blk_bs(blk1); bs2 = blk_bs(blk2); buf1 = blk_blockalign(blk1, IO_BUF_SIZE); @@ -1075,28 +1254,52 @@ static int img_compare(int argc, char **argv) } for (;;) { + int64_t status1, status2; + BlockDriverState *file; + nb_sectors = sectors_to_process(total_sectors, sector_num); if (nb_sectors <= 0) { break; } - allocated1 = bdrv_is_allocated_above(bs1, NULL, sector_num, nb_sectors, - &pnum1); - if (allocated1 < 0) { + status1 = bdrv_get_block_status_above(bs1, NULL, sector_num, + total_sectors1 - sector_num, + &pnum1, &file); + if (status1 < 0) { ret = 3; error_report("Sector allocation test failed for %s", filename1); goto out; } + allocated1 = status1 & BDRV_BLOCK_ALLOCATED; - allocated2 = bdrv_is_allocated_above(bs2, NULL, sector_num, nb_sectors, - &pnum2); - if (allocated2 < 0) { + status2 = bdrv_get_block_status_above(bs2, NULL, sector_num, + total_sectors2 - sector_num, + &pnum2, &file); + if (status2 < 0) { ret = 3; error_report("Sector allocation test failed for %s", filename2); goto out; } - nb_sectors = MIN(pnum1, pnum2); + allocated2 = status2 & BDRV_BLOCK_ALLOCATED; + if (pnum1) { + nb_sectors = MIN(nb_sectors, pnum1); + } + if (pnum2) { + nb_sectors = MIN(nb_sectors, pnum2); + } - if (allocated1 == allocated2) { + if (strict) { + if ((status1 & ~BDRV_BLOCK_OFFSET_MASK) != + (status2 & ~BDRV_BLOCK_OFFSET_MASK)) { + ret = 1; + qprintf(quiet, "Strict mode: Offset %" PRId64 + " block status mismatch!\n", + sectors_to_bytes(sector_num)); + goto out; + } + } + if ((status1 & BDRV_BLOCK_ZERO) && (status2 & BDRV_BLOCK_ZERO)) { + nb_sectors = MIN(pnum1, pnum2); + } else if (allocated1 == allocated2) { if (allocated1) { ret = blk_read(blk1, sector_num, buf1, nb_sectors); if (ret < 0) { @@ -1124,13 +1327,6 @@ static int img_compare(int argc, char **argv) } } } else { - if (strict) { - ret = 1; - qprintf(quiet, "Strict mode: Offset %" PRId64 - " allocation mismatch!\n", - sectors_to_bytes(sector_num)); - goto out; - } if (allocated1) { ret = check_empty_sectors(blk1, sector_num, nb_sectors, @@ -1212,6 +1408,7 @@ out2: blk_unref(blk1); out3: qemu_progress_end(); +out4: return ret; } @@ -1260,9 +1457,10 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) n = MIN(s->total_sectors - sector_num, BDRV_REQUEST_MAX_SECTORS); if (s->sector_next_status <= sector_num) { + BlockDriverState *file; ret = bdrv_get_block_status(blk_bs(s->src[s->src_cur]), sector_num - s->src_cur_offset, - n, &n); + n, &n, &file); if (ret < 0) { return ret; } @@ -1311,10 +1509,6 @@ static int convert_read(ImgConvertState *s, int64_t sector_num, int nb_sectors, int n; int ret; - if (s->status == BLK_ZERO || s->status == BLK_BACKING_FILE) { - return 0; - } - assert(nb_sectors <= s->buf_sectors); while (nb_sectors > 0) { BlockBackend *blk; @@ -1452,7 +1646,8 @@ static int convert_do_copy(ImgConvertState *s) ret = n; goto fail; } - if (s->status == BLK_DATA) { + if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) + { s->allocated_sectors += n; } sector_num += n; @@ -1472,17 +1667,24 @@ static int convert_do_copy(ImgConvertState *s) ret = n; goto fail; } - if (s->status == BLK_DATA) { + if (s->status == BLK_DATA || (!s->min_sparse && s->status == BLK_ZERO)) + { allocated_done += n; qemu_progress_print(100.0 * allocated_done / s->allocated_sectors, 0); } - ret = convert_read(s, sector_num, n, buf); - if (ret < 0) { - error_report("error while reading sector %" PRId64 - ": %s", sector_num, strerror(-ret)); - goto fail; + if (s->status == BLK_DATA) { + ret = convert_read(s, sector_num, n, buf); + if (ret < 0) { + error_report("error while reading sector %" PRId64 + ": %s", sector_num, strerror(-ret)); + goto fail; + } + } else if (!s->min_sparse && s->status == BLK_ZERO) { + n = MIN(n, s->buf_sectors); + memset(buf, 0, n * BDRV_SECTOR_SIZE); + s->status = BLK_DATA; } ret = convert_write(s, sector_num, n, buf); @@ -1514,6 +1716,7 @@ static int img_convert(int argc, char **argv) int c, bs_n, bs_i, compress, cluster_sectors, skip_create; int64_t ret = 0; int progress = 0, flags, src_flags; + bool writethrough, src_writethrough; const char *fmt, *out_fmt, *cache, *src_cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockBackend **blk = NULL, *out_blk = NULL; @@ -1532,6 +1735,7 @@ static int img_convert(int argc, char **argv) Error *local_err = NULL; QemuOpts *sn_opts = NULL; ImgConvertState state; + bool image_opts = false; fmt = NULL; out_fmt = "raw"; @@ -1541,7 +1745,14 @@ static int img_convert(int argc, char **argv) compress = 0; skip_create = 0; for(;;) { - c = getopt(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:O:B:ce6o:s:l:S:pt:T:qn", + long_options, NULL); if (c == -1) { break; } @@ -1607,7 +1818,7 @@ static int img_convert(int argc, char **argv) { int64_t sval; char *end; - sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B); + sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); if (sval < 0 || *end) { error_report("Invalid minimum zero buffer size for sparse output specified"); ret = -1; @@ -1632,16 +1843,31 @@ static int img_convert(int argc, char **argv) case 'n': skip_create = 1; break; + case OPTION_OBJECT: + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + goto fail_getopt; + } + break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + goto fail_getopt; + } + /* Initialize before goto out */ if (quiet) { progress = 0; } qemu_progress_init(progress, 1.0); - bs_n = argc - optind - 1; out_filename = bs_n >= 1 ? argv[argc - 1] : NULL; @@ -1662,8 +1888,8 @@ static int img_convert(int argc, char **argv) goto out; } - src_flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(src_cache, &src_flags); + src_flags = 0; + ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", src_cache); goto out; @@ -1677,11 +1903,8 @@ static int img_convert(int argc, char **argv) total_sectors = 0; 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"); - blk[bs_i] = img_open(id, argv[optind + bs_i], fmt, src_flags, - true, quiet); - g_free(id); + blk[bs_i] = img_open(image_opts, argv[optind + bs_i], + fmt, src_flags, src_writethrough, quiet); if (!blk[bs_i]) { ret = -1; goto out; @@ -1712,9 +1935,7 @@ static int img_convert(int argc, char **argv) bdrv_snapshot_load_tmp_by_id_or_name(bs[0], snapshot_name, &local_err); } if (local_err) { - error_report("Failed to load snapshot: %s", - error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "Failed to load snapshot: "); ret = -1; goto out; } @@ -1810,21 +2031,25 @@ static int img_convert(int argc, char **argv) /* Create the new image */ ret = bdrv_create(drv, out_filename, opts, &local_err); if (ret < 0) { - error_report("%s: error while converting %s: %s", - out_filename, out_fmt, error_get_pretty(local_err)); - error_free(local_err); + error_reportf_err(local_err, "%s: error while converting %s: ", + out_filename, out_fmt); goto out; } } flags = min_sparse ? (BDRV_O_RDWR | BDRV_O_UNMAP) : BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - out_blk = img_open("target", out_filename, out_fmt, flags, true, quiet); + /* XXX we should allow --image-opts to trigger use of + * img_open() here, but then we have trouble with + * the bdrv_create() call which takes different params. + * Not critical right now, so fix can wait... + */ + out_blk = img_open_file(out_filename, out_fmt, flags, writethrough, quiet); if (!out_blk) { ret = -1; goto out; @@ -1931,8 +2156,8 @@ static void dump_json_image_info_list(ImageInfoList *list) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfoList(qmp_output_get_visitor(ov), - &list, NULL, &local_err); + visit_type_ImageInfoList(qmp_output_get_visitor(ov), NULL, &list, + &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -1948,8 +2173,7 @@ static void dump_json_image_info(ImageInfo *info) QString *str; QmpOutputVisitor *ov = qmp_output_visitor_new(); QObject *obj; - visit_type_ImageInfo(qmp_output_get_visitor(ov), - &info, NULL, &local_err); + visit_type_ImageInfo(qmp_output_get_visitor(ov), NULL, &info, &local_err); obj = qmp_output_get_qobject(ov); str = qobject_to_json_pretty(obj); assert(str != NULL); @@ -1991,7 +2215,8 @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b) * image file. If there was an error a message will have been printed to * stderr. */ -static ImageInfoList *collect_image_info_list(const char *filename, +static ImageInfoList *collect_image_info_list(bool image_opts, + const char *filename, const char *fmt, bool chain) { @@ -2015,8 +2240,8 @@ static ImageInfoList *collect_image_info_list(const char *filename, } g_hash_table_insert(filenames, (gpointer)filename, NULL); - blk = img_open("image", filename, fmt, - BDRV_O_FLAGS | BDRV_O_NO_BACKING, false, false); + blk = img_open(image_opts, filename, fmt, + BDRV_O_NO_BACKING | BDRV_O_NO_IO, false, false); if (!blk) { goto err; } @@ -2041,7 +2266,10 @@ static ImageInfoList *collect_image_info_list(const char *filename, if (info->has_full_backing_filename) { filename = info->full_backing_filename; } else if (info->has_backing_filename) { - filename = info->backing_filename; + error_report("Could not determine absolute backing filename," + " but backing filename '%s' present", + info->backing_filename); + goto err; } if (info->has_backing_filename_format) { fmt = info->backing_filename_format; @@ -2064,6 +2292,7 @@ static int img_info(int argc, char **argv) bool chain = false; const char *filename, *fmt, *output; ImageInfoList *list; + bool image_opts = false; fmt = NULL; output = NULL; @@ -2074,6 +2303,8 @@ static int img_info(int argc, char **argv) {"format", required_argument, 0, 'f'}, {"output", required_argument, 0, OPTION_OUTPUT}, {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "f:h", @@ -2095,6 +2326,17 @@ 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_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2111,7 +2353,13 @@ static int img_info(int argc, char **argv) return 1; } - list = collect_image_info_list(filename, fmt, chain); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + list = collect_image_info_list(image_opts, filename, fmt, chain); if (!list) { return 1; } @@ -2133,47 +2381,37 @@ static int img_info(int argc, char **argv) return 0; } - -typedef struct MapEntry { - int flags; - int depth; - int64_t start; - int64_t length; - int64_t offset; - BlockDriverState *bs; -} MapEntry; - static void dump_map_entry(OutputFormat output_format, MapEntry *e, MapEntry *next) { switch (output_format) { case OFORMAT_HUMAN: - if ((e->flags & BDRV_BLOCK_DATA) && - !(e->flags & BDRV_BLOCK_OFFSET_VALID)) { + if (e->data && !e->has_offset) { error_report("File contains external, encrypted or compressed clusters."); exit(1); } - if ((e->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) == BDRV_BLOCK_DATA) { + if (e->data && !e->zero) { printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", - e->start, e->length, e->offset, e->bs->filename); + e->start, e->length, + e->has_offset ? e->offset : 0, + e->has_filename ? e->filename : ""); } /* This format ignores the distinction between 0, ZERO and ZERO|DATA. * Modify the flags here to allow more coalescing. */ - if (next && - (next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) { - next->flags &= ~BDRV_BLOCK_DATA; - next->flags |= BDRV_BLOCK_ZERO; + if (next && (!next->data || next->zero)) { + next->data = false; + next->zero = true; } break; case OFORMAT_JSON: - printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d," - " \"zero\": %s, \"data\": %s", + printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64"," + " \"depth\": %"PRId64", \"zero\": %s, \"data\": %s", (e->start == 0 ? "[" : ",\n"), e->start, e->length, e->depth, - (e->flags & BDRV_BLOCK_ZERO) ? "true" : "false", - (e->flags & BDRV_BLOCK_DATA) ? "true" : "false"); - if (e->flags & BDRV_BLOCK_OFFSET_VALID) { + e->zero ? "true" : "false", + e->data ? "true" : "false"); + if (e->has_offset) { printf(", \"offset\": %"PRId64"", e->offset); } putchar('}'); @@ -2190,6 +2428,8 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, { int64_t ret; int depth; + BlockDriverState *file; + bool has_offset; /* As an optimization, we could cache the current range of unallocated * clusters in each file of the chain, and avoid querying the same @@ -2198,7 +2438,8 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, depth = 0; for (;;) { - ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors); + ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors, + &file); if (ret < 0) { return ret; } @@ -2206,7 +2447,7 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) { break; } - bs = bs->backing_hd; + bs = backing_bs(bs); if (bs == NULL) { ret = 0; break; @@ -2215,15 +2456,44 @@ static int get_block_status(BlockDriverState *bs, int64_t sector_num, depth++; } - e->start = sector_num * BDRV_SECTOR_SIZE; - e->length = nb_sectors * BDRV_SECTOR_SIZE; - e->flags = ret & ~BDRV_BLOCK_OFFSET_MASK; - e->offset = ret & BDRV_BLOCK_OFFSET_MASK; - e->depth = depth; - e->bs = bs; + has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); + + *e = (MapEntry) { + .start = sector_num * BDRV_SECTOR_SIZE, + .length = nb_sectors * BDRV_SECTOR_SIZE, + .data = !!(ret & BDRV_BLOCK_DATA), + .zero = !!(ret & BDRV_BLOCK_ZERO), + .offset = ret & BDRV_BLOCK_OFFSET_MASK, + .has_offset = has_offset, + .depth = depth, + .has_filename = file && has_offset, + .filename = file && has_offset ? file->filename : NULL, + }; + return 0; } +static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) +{ + if (curr->length == 0) { + return false; + } + if (curr->zero != next->zero || + curr->data != next->data || + curr->depth != next->depth || + curr->has_filename != next->has_filename || + curr->has_offset != next->has_offset) { + return false; + } + if (curr->has_filename && strcmp(curr->filename, next->filename)) { + return false; + } + if (curr->has_offset && curr->offset + curr->length != next->offset) { + return false; + } + return true; +} + static int img_map(int argc, char **argv) { int c; @@ -2234,6 +2504,7 @@ static int img_map(int argc, char **argv) int64_t length; MapEntry curr = { .length = 0 }, next; int ret = 0; + bool image_opts = false; fmt = NULL; output = NULL; @@ -2243,6 +2514,8 @@ static int img_map(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"format", required_argument, 0, 'f'}, {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "f:h", @@ -2261,6 +2534,17 @@ static int img_map(int argc, char **argv) case OPTION_OUTPUT: output = optarg; break; + case OPTION_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + return 1; + } + } break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2277,7 +2561,13 @@ static int img_map(int argc, char **argv) return 1; } - blk = img_open("image", filename, fmt, BDRV_O_FLAGS, true, false); + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + + blk = img_open(image_opts, filename, fmt, 0, false, false); if (!blk) { return 1; } @@ -2305,10 +2595,7 @@ static int img_map(int argc, char **argv) goto out; } - if (curr.length != 0 && curr.flags == next.flags && - curr.depth == next.depth && - ((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 || - curr.offset + curr.length == next.offset)) { + if (entry_mergeable(&curr, &next)) { curr.length += next.length; continue; } @@ -2342,11 +2629,19 @@ static int img_snapshot(int argc, char **argv) qemu_timeval tv; bool quiet = false; Error *err = NULL; + bool image_opts = false; - bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; + bdrv_oflags = BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { - c = getopt(argc, argv, "la:c:d:hq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "la:c:d:hq", + long_options, NULL); if (c == -1) { break; } @@ -2390,6 +2685,17 @@ static int img_snapshot(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_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2398,8 +2704,14 @@ static int img_snapshot(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + /* Open the image */ - blk = img_open("image", filename, NULL, bdrv_oflags, true, quiet); + blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet); if (!blk) { return 1; } @@ -2437,9 +2749,8 @@ static int img_snapshot(int argc, char **argv) case SNAPSHOT_DELETE: bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err); if (err) { - error_report("Could not delete snapshot '%s': (%s)", - snapshot_name, error_get_pretty(err)); - error_free(err); + error_reportf_err(err, "Could not delete snapshot '%s': ", + snapshot_name); ret = 1; } break; @@ -2456,14 +2767,18 @@ static int img_snapshot(int argc, char **argv) static int img_rebase(int argc, char **argv) { BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; + uint8_t *buf_old = NULL; + uint8_t *buf_new = NULL; BlockDriverState *bs = NULL; char *filename; const char *fmt, *cache, *src_cache, *out_basefmt, *out_baseimg; int c, flags, src_flags, ret; + bool writethrough, src_writethrough; int unsafe = 0; int progress = 0; bool quiet = false; Error *local_err = NULL; + bool image_opts = false; /* Parse commandline parameters */ fmt = NULL; @@ -2472,7 +2787,14 @@ static int img_rebase(int argc, char **argv) out_baseimg = NULL; out_basefmt = NULL; for(;;) { - c = getopt(argc, argv, "hf:F:b:upt:T:q"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "hf:F:b:upt:T:q", + long_options, NULL); if (c == -1) { break; } @@ -2505,6 +2827,17 @@ 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_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2520,30 +2853,40 @@ static int img_rebase(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + qemu_progress_init(progress, 2.0); qemu_progress_print(0, 100); flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); - ret = bdrv_parse_cache_flags(cache, &flags); + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - src_flags = BDRV_O_FLAGS; - ret = bdrv_parse_cache_flags(src_cache, &src_flags); + src_flags = 0; + ret = bdrv_parse_cache_mode(src_cache, &src_flags, &src_writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", src_cache); goto out; } + /* The source files are opened read-only, don't care about WCE */ + assert((src_flags & BDRV_O_RDWR) == 0); + (void) src_writethrough; + /* * 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. */ - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { ret = -1; goto out; @@ -2569,12 +2912,12 @@ static int img_rebase(int argc, char **argv) } bdrv_get_backing_filename(bs, backing_name, sizeof(backing_name)); - blk_old_backing = blk_new_open("old_backing", backing_name, NULL, + blk_old_backing = blk_new_open(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); + error_reportf_err(local_err, + "Could not open old backing file '%s': ", + backing_name); goto out; } @@ -2586,12 +2929,12 @@ static int img_rebase(int argc, char **argv) options = NULL; } - blk_new_backing = blk_new_open("new_backing", out_baseimg, NULL, + blk_new_backing = blk_new_open(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); + error_reportf_err(local_err, + "Could not open new backing file '%s': ", + out_baseimg); goto out; } } @@ -2612,8 +2955,6 @@ static int img_rebase(int argc, char **argv) int64_t new_backing_num_sectors = 0; uint64_t sector; int n; - uint8_t * buf_old; - uint8_t * buf_new; float local_progress = 0; buf_old = blk_blockalign(blk, IO_BUF_SIZE); @@ -2725,9 +3066,6 @@ static int img_rebase(int argc, char **argv) } qemu_progress_print(local_progress, 100); } - - qemu_vfree(buf_old); - qemu_vfree(buf_new); } /* @@ -2763,6 +3101,8 @@ out: blk_unref(blk_old_backing); blk_unref(blk_new_backing); } + qemu_vfree(buf_old); + qemu_vfree(buf_new); blk_unref(blk); if (ret) { @@ -2780,6 +3120,7 @@ static int img_resize(int argc, char **argv) bool quiet = false; BlockBackend *blk = NULL; QemuOpts *param; + static QemuOptsList resize_options = { .name = "resize_options", .head = QTAILQ_HEAD_INITIALIZER(resize_options.head), @@ -2793,6 +3134,7 @@ static int img_resize(int argc, char **argv) } }, }; + bool image_opts = false; /* Remove size from argv manually so that negative numbers are not treated * as options by getopt. */ @@ -2806,7 +3148,14 @@ static int img_resize(int argc, char **argv) /* Parse getopt arguments */ fmt = NULL; for(;;) { - c = getopt(argc, argv, "f:hq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "f:hq", + long_options, NULL); if (c == -1) { break; } @@ -2821,6 +3170,17 @@ 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_IMAGE_OPTS: + image_opts = true; + break; } } if (optind != argc - 1) { @@ -2828,6 +3188,12 @@ static int img_resize(int argc, char **argv) } filename = argv[optind++]; + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, NULL)) { + return 1; + } + /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) { case '+': @@ -2855,8 +3221,8 @@ static int img_resize(int argc, char **argv) n = qemu_opt_get_size(param, BLOCK_OPT_SIZE, 0); qemu_opts_del(param); - blk = img_open("image", filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR, - true, quiet); + blk = img_open(image_opts, filename, fmt, + BDRV_O_RDWR, false, quiet); if (!blk) { ret = -1; goto out; @@ -2897,7 +3263,8 @@ out: } static void amend_status_cb(BlockDriverState *bs, - int64_t offset, int64_t total_work_size) + int64_t offset, int64_t total_work_size, + void *opaque) { qemu_progress_print(100.f * offset / total_work_size, 0); } @@ -2911,13 +3278,22 @@ static int img_amend(int argc, char **argv) QemuOpts *opts = NULL; const char *fmt = NULL, *filename, *cache; int flags; + bool writethrough; bool quiet = false, progress = false; BlockBackend *blk = NULL; BlockDriverState *bs = NULL; + bool image_opts = false; cache = BDRV_DEFAULT_CACHE; for (;;) { - c = getopt(argc, argv, "ho:f:t:pq"); + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {0, 0, 0, 0} + }; + c = getopt_long(argc, argv, "ho:f:t:pq", + long_options, NULL); if (c == -1) { break; } @@ -2931,7 +3307,7 @@ static int img_amend(int argc, char **argv) if (!is_valid_option_list(optarg)) { error_report("Invalid option list: %s", optarg); ret = -1; - goto out; + goto out_no_progress; } if (!options) { options = g_strdup(optarg); @@ -2953,6 +3329,17 @@ static int img_amend(int argc, char **argv) case 'q': quiet = true; break; + case OPTION_OBJECT: + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + ret = -1; + goto out_no_progress; + } + break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; } } @@ -2960,6 +3347,13 @@ 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, + NULL, NULL)) { + ret = -1; + goto out_no_progress; + } + if (quiet) { progress = false; } @@ -2979,14 +3373,14 @@ static int img_amend(int argc, char **argv) goto out; } - flags = BDRV_O_FLAGS | BDRV_O_RDWR; - ret = bdrv_parse_cache_flags(cache, &flags); + flags = BDRV_O_RDWR; + ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid cache option: %s", cache); goto out; } - blk = img_open("image", filename, fmt, flags, true, quiet); + blk = img_open(image_opts, filename, fmt, flags, writethrough, quiet); if (!blk) { ret = -1; goto out; @@ -3021,7 +3415,7 @@ static int img_amend(int argc, char **argv) /* 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); + ret = bdrv_amend_options(bs, opts, &amend_status_cb, NULL); qemu_progress_print(100.f, 0); if (ret < 0) { error_report("Error while amending options: %s", strerror(-ret)); @@ -3031,6 +3425,7 @@ static int img_amend(int argc, char **argv) out: qemu_progress_end(); +out_no_progress: blk_unref(blk); qemu_opts_del(opts); qemu_opts_free(create_opts); @@ -3075,12 +3470,21 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if (qcrypto_init(&local_error) < 0) { + error_reportf_err(local_error, "cannot initialize crypto: "); + exit(1); + } + + module_call_init(MODULE_INIT_QOM); bdrv_init(); if (argc < 2) { error_exit("Not enough arguments"); } cmdname = argv[1]; + qemu_add_opts(&qemu_object_opts); + qemu_add_opts(&qemu_source_opts); + /* find the command */ for (cmd = img_cmds; cmd->name != NULL; cmd++) { if (!strcmp(cmdname, cmd->name)) { |