Skip to content
Snippets Groups Projects
qemu-img.c 160 KiB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
/*
Fabrice Bellard's avatar
Fabrice Bellard committed
 * QEMU disk image utility
Fabrice Bellard's avatar
Fabrice Bellard committed
 * Copyright (c) 2003-2008 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
 * 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.
 */
Peter Maydell's avatar
Peter Maydell committed
#include "qemu/osdep.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"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/qmp/qjson.h"
#include "qapi/qmp/qdict.h"
#include "qemu/cutils.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "qemu/sockets.h"
#include "qemu/memalign.h"
#include "qom/object_interfaces.h"
#include "sysemu/block-backend.h"
#include "block/block_int.h"
#include "block/blockjob.h"
#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"
typedef struct img_cmd_t {
    const char *name;
    int (*handler)(int argc, char **argv);
} img_cmd_t;
enum {
    OPTION_OUTPUT = 256,
    OPTION_BACKING_CHAIN = 257,
    OPTION_PATTERN = 260,
    OPTION_FLUSH_INTERVAL = 261,
    OPTION_NO_DRAIN = 262,
    OPTION_SIZE = 264,
    OPTION_PREALLOCATION = 265,
    OPTION_SHRINK = 266,
    OPTION_SALVAGE = 267,
    OPTION_TARGET_IS_ZERO = 268,
    OPTION_ADD = 269,
    OPTION_REMOVE = 270,
    OPTION_CLEAR = 271,
    OPTION_ENABLE = 272,
    OPTION_DISABLE = 273,
    OPTION_MERGE = 274,
    OPTION_BITMAPS = 275,
    OPTION_FORCE = 276,
    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)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
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);
    va_end(ap);

    error_printf("Try 'qemu-img --help' for more information\n");
    exit(EXIT_FAILURE);
}

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)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
           QEMU_IMG_VERSION
           "usage: qemu-img [standard options] command [command options]\n"
           "QEMU disk image utility\n"
           "\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"
           "Command syntax:\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"
           "Parameters to convert subcommand:\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");
    exit(EXIT_SUCCESS);
/*
 * 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;

    if (!optarg[0] || optarg[0] == ',') {
        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;
    }

Chunyan Liu's avatar
Chunyan Liu committed
    create_opts = qemu_opts_append(create_opts, drv->create_opts);
        proto_drv = bdrv_find_protocol(filename, true, &local_err);
        if (!proto_drv->create_opts) {
Hanna Reitz's avatar
Hanna Reitz committed
            error_report("Protocol driver '%s' does not support image creation",
                         proto_drv->format_name);
Hanna Reitz's avatar
Hanna Reitz committed
            qemu_opts_free(create_opts);
Chunyan Liu's avatar
Chunyan Liu committed
        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);

    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");
        qdict_put_str(options, BDRV_OPT_FORCE_SHARE, "on");
    blk = blk_new_open(NULL, NULL, options, flags, &local_err);
        error_reportf_err(local_err, "Could not open '%s': ", optstr);
    blk_set_enable_write_cache(blk, !writethrough);
static BlockBackend *img_open_file(const char *filename,
                                   bool writethrough, bool quiet,
                                   bool force_share)
    Error *local_err = NULL;
        qdict_put_str(options, "driver", fmt);
    if (force_share) {
        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);
    blk_set_enable_write_cache(blk, !writethrough);
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);
static BlockBackend *img_open(bool image_opts,
                              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 (base_filename) {
        if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, base_filename,
            error_report("Backing file not supported for file format '%s'",
                         fmt);
        if (!qemu_opt_set(opts, BLOCK_OPT_BACKING_FMT, base_fmt, NULL)) {
            error_report("Backing file format not supported for file "
                         "format '%s'", fmt);
static int64_t cvtnum_full(const char *name, const char *value, int64_t min,
                           int64_t max)
    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.");
    if (err == -ERANGE || res > max || res < min) {
        error_report("Invalid %s specified. Must be between %" PRId64
                     " and %" PRId64 ".", name, min, max);
    return res;
}

static int64_t cvtnum(const char *name, const char *value)
{
    return cvtnum_full(name, value, 0, INT64_MAX);
Fabrice Bellard's avatar
Fabrice Bellard committed
static int img_create(int argc, char **argv)
{
Fabrice Bellard's avatar
Fabrice Bellard committed
    const char *fmt = "raw";
Fabrice Bellard's avatar
Fabrice Bellard committed
    const char *filename;
    const char *base_filename = NULL;
    char *options = NULL;
    bool quiet = false;
Fabrice Bellard's avatar
Fabrice Bellard committed
    for(;;) {
        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",
Fabrice Bellard's avatar
Fabrice Bellard committed
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        switch(c) {
        case ':':
            missing_argument(argv[optind - 1]);
            break;
        case '?':
            unrecognized_option(argv[optind - 1]);
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        case 'h':
            help();
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        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;
    /* Get the filename */
    filename = (optind < argc) ? argv[optind] : NULL;
    if (options && has_help_option(options)) {
        g_free(options);
        return print_block_option_help(filename, fmt);
    }

    if (optind >= argc) {
        error_exit("Expecting image file name");
    /* Get image size, if specified */
    if (optind < argc) {
        sval = cvtnum("image size", argv[optind++]);
    if (optind != argc) {
        error_exit("Unexpected argument: %s", argv[optind]);
    bdrv_img_create(filename, fmt, base_filename, base_fmt,
                    options, img_size, flags, quiet, &local_err);
    if (local_err) {
        error_reportf_err(local_err, "%s: ", filename);
    g_free(options);
Fabrice Bellard's avatar
Fabrice Bellard committed
    return 0;

fail:
    g_free(options);
    return 1;
static void dump_json_image_check(ImageCheck *check, bool quiet)
    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);
    qprintf(quiet, "%s\n", str->str);
    visit_free(v);
    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);
            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);
            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);
        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;
/*
 * 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;
    BlockBackend *blk;
    BlockDriverState *bs;
    int fix = 0;
    int flags = BDRV_O_CHECK;
    bool writethrough;
    bool quiet = false;
    bool force_share = false;
    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'},
        c = getopt_long(argc, argv, ":hf:r:T:qU",
                        long_options, &option_index);
        case ':':
            missing_argument(argv[optind - 1]);
            break;
        case '?':
            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;
    if (optind != argc - 1) {
        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);

    check = g_new0(ImageCheck, 1);
    ret = collect_image_check(bs, check, filename, fmt, fix);
        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");
        }
    if (check->corruptions) {
        ret = 2;
    } else if (check->leaks) {
        ret = 3;
    blk_unref(blk);
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);
    aio_context_acquire(aio_context);
Kevin Wolf's avatar
Kevin Wolf committed
    job_ref(&job->job);
        float progress = 0.0f;
        aio_poll(aio_context, true);

        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);
Kevin Wolf's avatar
Kevin Wolf committed
    } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
    if (!job_is_completed(&job->job)) {
        ret = job_complete_sync(&job->job, errp);
        ret = job->job.ret;
Kevin Wolf's avatar
Kevin Wolf committed
    job_unref(&job->job);
    aio_context_release(aio_context);
    /* publish completion progress only when success */
    if (!ret) {
        qemu_progress_print(100.f, 0);
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
static int img_commit(int argc, char **argv)
{
    int c, ret, flags;
    const char *filename, *fmt, *cache, *base;
    BlockBackend *blk;
    BlockDriverState *bs, *base_bs;
    bool progress = false, quiet = false, drop = false;
    Error *local_err = NULL;
    CommonBlockJobCBInfo cbi;
    int64_t rate_limit = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed

    fmt = NULL;
    cache = BDRV_DEFAULT_CACHE;
Fabrice Bellard's avatar
Fabrice Bellard committed
    for(;;) {
        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},
        c = getopt_long(argc, argv, ":f:ht:b:dpqr:",
Fabrice Bellard's avatar
Fabrice Bellard committed
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        switch(c) {
        case ':':
            missing_argument(argv[optind - 1]);
            break;
        case '?':
            unrecognized_option(argv[optind - 1]);
            break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        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 'd':
            drop = true;
            break;
        case 'p':
            progress = true;
            break;
        case 'q':
            quiet = true;