Newer
Older
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/help-texts.h"
#include "qemu/qemu-progress.h"
#include "qemu-version.h"
#include "qapi/error.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp/qdict.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/sockets.h"
#include "qemu/units.h"
#include "qemu/memalign.h"
#include "qom/object_interfaces.h"
#include "block/qapi.h"
#include "qemu/throttle.h"
#include "block/throttle-groups.h"
#define QEMU_IMG_VERSION "qemu-img version " QEMU_FULL_VERSION \
const char *name;
int (*handler)(int argc, char **argv);
enum {
OPTION_OUTPUT = 256,
OPTION_BACKING_CHAIN = 257,
OPTION_OBJECT = 258,
OPTION_IMAGE_OPTS = 259,
OPTION_FLUSH_INTERVAL = 261,
OPTION_NO_DRAIN = 262,
OPTION_TARGET_IMAGE_OPTS = 263,
OPTION_ADD = 269,
OPTION_REMOVE = 270,
OPTION_CLEAR = 271,
OPTION_ENABLE = 272,
OPTION_DISABLE = 273,
OPTION_MERGE = 274,
OPTION_SKIP_BROKEN = 277,
};
typedef enum OutputFormat {
OFORMAT_JSON,
OFORMAT_HUMAN,
} OutputFormat;
/* 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)
printf(" %s", name);
static G_NORETURN G_GNUC_PRINTF(1, 2)
void error_exit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
error_vreport(fmt, ap);
error_printf("Try 'qemu-img --help' for more information\n");
static G_NORETURN
void missing_argument(const char *option)
{
error_exit("missing argument for option '%s'", 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 G_NORETURN
void help(void)
const char *help_msg =
"usage: qemu-img [standard options] command [command options]\n"
" '-h', '--help' display this help and exit\n"
" '-V', '--version' output version information and exit\n"
" '-T', '--trace' [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\n"
#define DEF(option, callback, arg_string) \
" " arg_string "\n"
#include "qemu-img-cmds.h"
#undef DEF
"\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"
" 'directsync' and 'unsafe' (default for convert)\n"
" 'src_cache' is the cache mode used to read input disk images, the valid\n"
" options are the same as for the 'cache' option\n"
" 'size' is the disk image size in bytes. Optional suffixes\n"
" 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n"
" 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n"
" supported. 'b' is ignored.\n"
" 'output_filename' is the destination disk image filename\n"
" 'output_fmt' is the destination format\n"
" 'options' is a comma separated list of format specific options in a\n"
" name=value format. Use -o ? for an overview of the options supported by the\n"
" used format\n"
" 'snapshot_param' is param used for internal snapshot, format\n"
" is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n"
" '[ID_OR_NAME]'\n"
" '-c' indicates that target image must be compressed (qcow format only)\n"
" '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n"
" new backing file match exactly. The image doesn't need a working\n"
" backing file before rebasing in this case (useful for renaming the\n"
" backing file). For image creation, allow creating without attempting\n"
" to open the backing file.\n"
" '-h' with or without a command shows this help and lists the supported formats\n"
" '-p' show progress of command (only certain commands)\n"
" '-q' use Quiet mode - do not print any output (except errors)\n"
" '-S' indicates the consecutive number of bytes (defaults to 4k) that must\n"
" contain only zeros for qemu-img to create a sparse image during\n"
" conversion. If the number of bytes is 0, the source will not be scanned for\n"
" unallocated or zero sectors, and the destination image will always be\n"
" fully allocated\n"
" '--output' takes the format in which the output must be done (human or json)\n"
" '-n' skips the target volume creation (useful if the volume is created\n"
" prior to running qemu-img)\n"
"Parameters to bitmap subcommand:\n"
" 'bitmap' is the name of the bitmap to manipulate, through one or more\n"
" actions from '--add', '--remove', '--clear', '--enable', '--disable',\n"
" or '--merge source'\n"
" '-g granularity' sets the granularity for '--add' actions\n"
" '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n"
" bitmaps from an alternative file\n"
"\n"
"Parameters to check subcommand:\n"
" '-r' tries to repair any inconsistencies that are found during the check.\n"
" '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n"
" kinds of errors, with a higher risk of choosing the wrong fix or\n"
" hiding corruption that has already occurred.\n"
" '--bitmaps' copies all top-level persistent bitmaps to destination\n"
" '-m' specifies how many coroutines work in parallel during the convert\n"
" process (defaults to 8)\n"
" '-W' allow to write to the target out of order rather than sequential\n"
"\n"
"Parameters to snapshot subcommand:\n"
" 'snapshot' is the name of the snapshot to create, apply or delete\n"
" '-a' applies a snapshot (revert disk to saved state)\n"
" '-c' creates a snapshot\n"
" '-d' deletes a snapshot\n"
" '-l' lists all snapshots in the given image\n"
"\n"
"Parameters to compare subcommand:\n"
" '-f' first image format\n"
" '-F' second image format\n"
" '-s' run in Strict mode - fail on different image size or sector allocation\n"
"\n"
"Parameters to dd subcommand:\n"
" 'bs=BYTES' read and write up to BYTES bytes at a time "
"(default: 512)\n"
" 'count=N' copy only N input blocks\n"
" 'if=FILE' read from FILE\n"
" 'of=FILE' write to FILE\n"
" 'skip=N' skip N bs-sized blocks at the start of input\n";
printf("%s\nSupported formats:", help_msg);
bdrv_iterate_format(format_print, NULL, false);
printf("\n\n" QEMU_HELP_BOTTOM "\n");
/*
* Is @optarg safe for accumulate_options()?
* It is when multiple of them can be joined together separated by ','.
* To make that work, @optarg must not start with ',' (or else a
* separating ',' preceding it gets escaped), and it must not end with
* an odd number of ',' (or else a separating ',' following it gets
* escaped), or be empty (or else a separating ',' preceding it can
* escape a separating ',' following it).
*
*/
static bool is_valid_option_list(const char *optarg)
{
size_t len = strlen(optarg);
size_t i;
return false;
}
for (i = len; i > 0 && optarg[i - 1] == ','; i--) {
}
if ((len - i) % 2) {
return false;
}
return true;
static int accumulate_options(char **options, char *optarg)
{
char *new_options;
if (!is_valid_option_list(optarg)) {
error_report("Invalid option list: %s", optarg);
return -1;
}
if (!*options) {
*options = g_strdup(optarg);
} else {
new_options = g_strdup_printf("%s,%s", *options, optarg);
g_free(*options);
*options = new_options;
}
return 0;
}
static QemuOptsList qemu_source_opts = {
.name = "source",
.implied_opt_name = "file",
.head = QTAILQ_HEAD_INITIALIZER(qemu_source_opts.head),
.desc = {
{ }
},
};
static int G_GNUC_PRINTF(2, 3) qprintf(bool quiet, const char *fmt, ...)
{
int ret = 0;
if (!quiet) {
va_list args;
va_start(args, fmt);
ret = vprintf(fmt, args);
va_end(args);
}
return ret;
}
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);
if (!drv) {
error_report("Unknown file format '%s'", fmt);
if (!drv->create_opts) {
error_report("Format driver '%s' does not support image creation", fmt);
return 1;
}
create_opts = qemu_opts_append(create_opts, drv->create_opts);
proto_drv = bdrv_find_protocol(filename, true, &local_err);
error_report_err(local_err);
qemu_opts_free(create_opts);
if (!proto_drv->create_opts) {
error_report("Protocol driver '%s' does not support image creation",
proto_drv->format_name);
create_opts = qemu_opts_append(create_opts, proto_drv->create_opts);
if (filename) {
printf("Supported options:\n");
} else {
printf("Supported %s options:\n", fmt);
}
qemu_opts_print_help(create_opts, false);
qemu_opts_free(create_opts);
if (!filename) {
printf("\n"
"The protocol level may support further options.\n"
"Specify the target filename to include those options.\n");
}
static BlockBackend *img_open_opts(const char *optstr,
QemuOpts *opts, int flags, bool writethrough,
bool quiet, bool force_share)
{
QDict *options;
Error *local_err = NULL;
BlockBackend *blk;
options = qemu_opts_to_qdict(opts, NULL);
if (force_share) {
if (qdict_haskey(options, BDRV_OPT_FORCE_SHARE)
&& strcmp(qdict_get_str(options, BDRV_OPT_FORCE_SHARE), "on")) {
error_report("--force-share/-U conflicts with image options");
qobject_unref(options);
qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on");
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);
return blk;
}
static BlockBackend *img_open_file(const char *filename,
QDict *options,
const char *fmt, int flags,
bool writethrough, bool quiet,
bool force_share)
{
BlockBackend *blk;
if (!options) {
options = qdict_new();
}
qdict_put_str(options, "driver", fmt);
qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true);
blk = blk_new_open(filename, NULL, options, flags, &local_err);
error_reportf_err(local_err, "Could not open '%s': ", filename);
return NULL;
blk_set_enable_write_cache(blk, !writethrough);
return blk;
}
static int img_add_key_secrets(void *opaque,
const char *name, const char *value,
Error **errp)
{
QDict *options = opaque;
if (g_str_has_suffix(name, "key-secret")) {
qdict_put_str(options, name, value);
}
return 0;
}
static BlockBackend *img_open(bool image_opts,
const char *filename,
const char *fmt, int flags, bool writethrough,
bool quiet, bool force_share)
{
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,
force_share);
blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet,
static int add_old_style_options(const char *fmt, QemuOpts *opts,
const char *base_filename,
const char *base_fmt)
if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename,
NULL)) {
error_report("Backing file not supported for file format '%s'",
fmt);
return -1;
if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) {
error_report("Backing file format not supported for file "
"format '%s'", fmt);
return -1;
return 0;
static int64_t cvtnum_full(const char *name, const char *value, int64_t min,
int64_t max)
int err;
uint64_t res;
err = qemu_strtosz(value, NULL, &res);
if (err < 0 && err != -ERANGE) {
error_report("Invalid %s specified. You may use "
"k, M, G, T, P or E suffixes for", name);
error_report("kilobytes, megabytes, gigabytes, terabytes, "
"petabytes and exabytes.");
return err;
}
if (err == -ERANGE || res > max || res < min) {
error_report("Invalid %s specified. Must be between %" PRId64
" and %" PRId64 ".", name, min, max);
return -ERANGE;
}
return res;
}
static int64_t cvtnum(const char *name, const char *value)
{
return cvtnum_full(name, value, 0, INT64_MAX);
static int img_create(int argc, char **argv)
{
uint64_t img_size = -1;

Anthony Liguori
committed
const char *base_fmt = NULL;
const char *filename;
const char *base_filename = NULL;
Error *local_err = NULL;

Thiemo Seufer
committed
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:ho:qu",
long_options, NULL);
case ':':
missing_argument(argv[optind - 1]);
break;
unrecognized_option(argv[optind - 1]);
break;

Anthony Liguori
committed
case 'F':
base_fmt = optarg;
break;
case 'b':
base_filename = optarg;
break;
case 'f':
fmt = optarg;
break;
if (accumulate_options(&options, optarg) < 0) {
case 'q':
quiet = true;
break;
case 'u':
flags |= BDRV_O_NO_BACKING;
break;
case OPTION_OBJECT:
user_creatable_process_cmdline(optarg);
break;

Anthony Liguori
committed
filename = (optind < argc) ? argv[optind] : NULL;
if (options && has_help_option(options)) {
g_free(options);
return print_block_option_help(filename, fmt);
}
/* Get image size, if specified */
if (optind < argc) {
sval = cvtnum("image size", argv[optind++]);
}
img_size = (uint64_t)sval;
}
error_exit("Unexpected argument: %s", argv[optind]);
bdrv_img_create(filename, fmt, base_filename, base_fmt,
options, img_size, flags, quiet, &local_err);
error_reportf_err(local_err, "%s: ", filename);
fail:
g_free(options);
return 1;
static void dump_json_image_check(ImageCheck *check, bool quiet)
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, true);
assert(str != NULL);
qprintf(quiet, "%s\n", str->str);
qobject_unref(obj);
g_string_free(str, true);
static void dump_human_image_check(ImageCheck *check, bool quiet)
{
if (!(check->corruptions || check->leaks || check->check_errors)) {
qprintf(quiet, "No errors were found on the image.\n");
} else {
if (check->corruptions) {
qprintf(quiet, "\n%" PRId64 " errors were found on the image.\n"
"Data may be corrupted, or further writes to the image "
"may corrupt it.\n",
check->corruptions);
}
if (check->leaks) {
qprintf(quiet,
"\n%" PRId64 " leaked clusters were found on the image.\n"
"This means waste of disk space, but no harm to data.\n",
check->leaks);
}
if (check->check_errors) {
qprintf(quiet,
"\n%" PRId64
" internal errors have occurred during the check.\n",
check->check_errors);
}
}
if (check->total_clusters != 0 && check->allocated_clusters != 0) {
qprintf(quiet, "%" PRId64 "/%" PRId64 " = %0.2f%% allocated, "
"%0.2f%% fragmented, %0.2f%% compressed clusters\n",
check->allocated_clusters, check->total_clusters,
check->allocated_clusters * 100.0 / check->total_clusters,
check->fragmented_clusters * 100.0 / check->allocated_clusters,
check->compressed_clusters * 100.0 /
check->allocated_clusters);
}
if (check->image_end_offset) {
qprintf(quiet,
"Image end offset: %" PRId64 "\n", check->image_end_offset);
}
}
static int collect_image_check(BlockDriverState *bs,
ImageCheck *check,
const char *filename,
const char *fmt,
int fix)
{
int ret;
BdrvCheckResult result;
ret = bdrv_check(bs, &result, fix);
if (ret < 0) {
return ret;
}
check->filename = g_strdup(filename);
check->format = g_strdup(bdrv_get_format_name(bs));
check->check_errors = result.check_errors;
check->corruptions = result.corruptions;
check->has_corruptions = result.corruptions != 0;
check->leaks = result.leaks;
check->has_leaks = result.leaks != 0;
check->corruptions_fixed = result.corruptions_fixed;
check->has_corruptions_fixed = result.corruptions_fixed != 0;
check->leaks_fixed = result.leaks_fixed;
check->has_leaks_fixed = result.leaks_fixed != 0;
check->image_end_offset = result.image_end_offset;
check->has_image_end_offset = result.image_end_offset != 0;
check->total_clusters = result.bfi.total_clusters;
check->has_total_clusters = result.bfi.total_clusters != 0;
check->allocated_clusters = result.bfi.allocated_clusters;
check->has_allocated_clusters = result.bfi.allocated_clusters != 0;
check->fragmented_clusters = result.bfi.fragmented_clusters;
check->has_fragmented_clusters = result.bfi.fragmented_clusters != 0;
check->compressed_clusters = result.bfi.compressed_clusters;
check->has_compressed_clusters = result.bfi.compressed_clusters != 0;
return 0;
}
/*
* Checks an image for consistency. Exit codes:
*
* 0 - Check completed, image is good
* 1 - Check not completed because of internal errors
* 2 - Check completed, image is corrupted
* 3 - Check completed, image has leaked clusters, but is good otherwise
* 63 - Checks are not supported by the image format
static int img_check(int argc, char **argv)
{
int c, ret;
OutputFormat output_format = OFORMAT_HUMAN;
const char *filename, *fmt, *output, *cache;
BlockDriverState *bs;
int flags = BDRV_O_CHECK;
bool writethrough;
bool image_opts = false;
output = NULL;
cache = BDRV_DEFAULT_CACHE;
int option_index = 0;
static const struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"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},
{"force-share", no_argument, 0, 'U'},
{0, 0, 0, 0}
};
c = getopt_long(argc, argv, ":hf:r:T:qU",
long_options, &option_index);
case ':':
missing_argument(argv[optind - 1]);
break;
unrecognized_option(argv[optind - 1]);
break;
case 'h':
help();
break;
case 'f':
fmt = optarg;
break;
case 'r':
flags |= BDRV_O_RDWR;
if (!strcmp(optarg, "leaks")) {
fix = BDRV_FIX_LEAKS;
} else if (!strcmp(optarg, "all")) {
fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS;
} else {
error_exit("Unknown option value for -r "
"(expecting 'leaks' or 'all'): %s", optarg);
case OPTION_OUTPUT:
output = optarg;
break;
case 'T':
cache = optarg;
break;
case 'q':
quiet = true;
break;
case 'U':
force_share = true;
break;
case OPTION_OBJECT:
user_creatable_process_cmdline(optarg);
break;
case OPTION_IMAGE_OPTS:
image_opts = true;
break;
error_exit("Expecting one image file name");
filename = argv[optind++];
if (output && !strcmp(output, "json")) {
output_format = OFORMAT_JSON;
} else if (output && !strcmp(output, "human")) {
output_format = OFORMAT_HUMAN;
} else if (output) {
error_report("--output must be used with human or json as argument.");
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_opts, filename, fmt, flags, writethrough, quiet,
force_share);
if (!blk) {
return 1;
check = g_new0(ImageCheck, 1);
ret = collect_image_check(bs, check, filename, fmt, fix);
if (ret == -ENOTSUP) {
error_report("This image format does not support checks");
if (check->corruptions_fixed || check->leaks_fixed) {
int corruptions_fixed, leaks_fixed;
bool has_leaks_fixed, has_corruptions_fixed;
leaks_fixed = check->leaks_fixed;
has_leaks_fixed = check->has_leaks_fixed;
corruptions_fixed = check->corruptions_fixed;
has_corruptions_fixed = check->has_corruptions_fixed;
if (output_format == OFORMAT_HUMAN) {
qprintf(quiet,
"The following inconsistencies were found and repaired:\n\n"
" %" PRId64 " leaked clusters\n"
" %" PRId64 " corruptions\n\n"
"Double checking the fixed image now...\n",
check->leaks_fixed,
check->corruptions_fixed);
qapi_free_ImageCheck(check);
check = g_new0(ImageCheck, 1);
ret = collect_image_check(bs, check, filename, fmt, 0);
check->leaks_fixed = leaks_fixed;
check->has_leaks_fixed = has_leaks_fixed;
check->corruptions_fixed = corruptions_fixed;
check->has_corruptions_fixed = has_corruptions_fixed;
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;
if (check->corruptions) {
ret = 2;
} else if (check->leaks) {
ret = 3;
fail:
qapi_free_ImageCheck(check);
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");
}
}
static void run_block_job(BlockJob *job, Error **errp)
{
uint64_t progress_current, progress_total;
AioContext *aio_context = block_job_get_aio_context(job);
int ret = 0;
aio_context_acquire(aio_context);
progress_get_snapshot(&job->job.progress, &progress_current,
&progress_total);
if (progress_total) {
progress = (float)progress_current / progress_total * 100.f;
} while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
if (!job_is_completed(&job->job)) {
ret = job_complete_sync(&job->job, errp);
} else {
}
aio_context_release(aio_context);
/* publish completion progress only when success */
if (!ret) {
qemu_progress_print(100.f, 0);
}
static int img_commit(int argc, char **argv)
{
const char *filename, *fmt, *cache, *base;
BlockJob *job;
bool progress = false, quiet = false, drop = false;
Error *local_err = NULL;
CommonBlockJobCBInfo cbi;
bool image_opts = false;
AioContext *aio_context;
int64_t rate_limit = 0;
cache = BDRV_DEFAULT_CACHE;
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:dpqr:",
long_options, NULL);
case ':':
missing_argument(argv[optind - 1]);
break;
unrecognized_option(argv[optind - 1]);
break;
case 'h':
help();
break;
case 'f':
fmt = optarg;
break;
case 't':
cache = optarg;
break;
case 'b':
base = optarg;
/* -b implies -d */
drop = true;
break;
case 'p':
progress = true;
break;