static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,

                                   Error **errp)

{

    const char *buf;

    int bdrv_flags = 0;

    int on_read_error, on_write_error;

    bool account_invalid, account_failed;

    const char *stats_intervals;

    BlockBackend *blk;

    BlockDriverState *bs;

    ThrottleConfig cfg;

    int snapshot = 0;

    Error *error = NULL;

    QemuOpts *opts;

    const char *id;

    bool has_driver_specific_opts;

    BlockdevDetectZeroesOptions detect_zeroes =

        BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF;

    const char *throttling_group = NULL;



    /* Check common options by copying from bs_opts to opts, all other options

     * stay in bs_opts for processing by bdrv_open(). */

    id = qdict_get_try_str(bs_opts, "id");

    opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error);

    if (error) {

        error_propagate(errp, error);

        goto err_no_opts;

    }



    qemu_opts_absorb_qdict(opts, bs_opts, &error);

    if (error) {

        error_propagate(errp, error);

        goto early_err;

    }



    if (id) {

        qdict_del(bs_opts, "id");

    }



    has_driver_specific_opts = !!qdict_size(bs_opts);



    /* extract parameters */

    snapshot = qemu_opt_get_bool(opts, "snapshot", 0);



    account_invalid = qemu_opt_get_bool(opts, "stats-account-invalid", true);

    account_failed = qemu_opt_get_bool(opts, "stats-account-failed", true);



    stats_intervals = qemu_opt_get(opts, "stats-intervals");



    extract_common_blockdev_options(opts, &bdrv_flags, &throttling_group, &cfg,

                                    &detect_zeroes, &error);

    if (error) {

        error_propagate(errp, error);

        goto early_err;

    }



    if ((buf = qemu_opt_get(opts, "format")) != NULL) {

        if (is_help_option(buf)) {

            error_printf("Supported formats:");

            bdrv_iterate_format(bdrv_format_print, NULL);

            error_printf("\n");

            goto early_err;

        }



        if (qdict_haskey(bs_opts, "driver")) {

            error_setg(errp, "Cannot specify both 'driver' and 'format'");

            goto early_err;

        }

        qdict_put(bs_opts, "driver", qstring_from_str(buf));

    }



    on_write_error = BLOCKDEV_ON_ERROR_ENOSPC;

    if ((buf = qemu_opt_get(opts, "werror")) != NULL) {

        on_write_error = parse_block_error_action(buf, 0, &error);

        if (error) {

            error_propagate(errp, error);

            goto early_err;

        }

    }



    on_read_error = BLOCKDEV_ON_ERROR_REPORT;

    if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {

        on_read_error = parse_block_error_action(buf, 1, &error);

        if (error) {

            error_propagate(errp, error);

            goto early_err;

        }

    }



    if (snapshot) {

        /* always use cache=unsafe with snapshot */

        bdrv_flags &= ~BDRV_O_CACHE_MASK;

        bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);

    }



    /* init */

    if ((!file || !*file) && !has_driver_specific_opts) {

        BlockBackendRootState *blk_rs;



        blk = blk_new(qemu_opts_id(opts), errp);

        if (!blk) {

            goto early_err;

        }



        blk_rs = blk_get_root_state(blk);

        blk_rs->open_flags    = bdrv_flags;

        blk_rs->read_only     = !(bdrv_flags & BDRV_O_RDWR);

        blk_rs->detect_zeroes = detect_zeroes;



        if (throttle_enabled(&cfg)) {

            if (!throttling_group) {

                throttling_group = blk_name(blk);

            }

            blk_rs->throttle_group = g_strdup(throttling_group);

            blk_rs->throttle_state = throttle_group_incref(throttling_group);

            blk_rs->throttle_state->cfg = cfg;

        }



        QDECREF(bs_opts);

    } else {

        if (file && !*file) {

            file = NULL;

        }



        blk = blk_new_open(qemu_opts_id(opts), file, NULL, bs_opts, bdrv_flags,

                           errp);

        if (!blk) {

            goto err_no_bs_opts;

        }

        bs = blk_bs(blk);



        bs->detect_zeroes = detect_zeroes;



        /* disk I/O throttling */

        if (throttle_enabled(&cfg)) {

            if (!throttling_group) {

                throttling_group = blk_name(blk);

            }

            bdrv_io_limits_enable(bs, throttling_group);

            bdrv_set_io_limits(bs, &cfg);

        }



        if (bdrv_key_required(bs)) {

            autostart = 0;

        }



        block_acct_init(blk_get_stats(blk), account_invalid, account_failed);



        if (stats_intervals) {

            char **intervals = g_strsplit(stats_intervals, ":", 0);

            unsigned i;



            if (*stats_intervals == '\0') {

                error_setg(&error, "stats-intervals can't have an empty value");

            }



            for (i = 0; !error && intervals[i] != NULL; i++) {

                unsigned long long val;

                if (parse_uint_full(intervals[i], &val, 10) == 0 &&

                    val > 0 && val <= UINT_MAX) {

                    block_acct_add_interval(blk_get_stats(blk), val);

                } else {

                    error_setg(&error, "Invalid interval length: '%s'",

                               intervals[i]);

                }

            }



            g_strfreev(intervals);



            if (error) {

                error_propagate(errp, error);

                blk_unref(blk);

                blk = NULL;

                goto err_no_bs_opts;

            }

        }

    }



    blk_set_on_error(blk, on_read_error, on_write_error);



err_no_bs_opts:

    qemu_opts_del(opts);

    return blk;



early_err:

    qemu_opts_del(opts);

err_no_opts:

    QDECREF(bs_opts);

    return NULL;

}
