int bdrv_open(BlockDriverState *bs, const char *filename, int flags,

              BlockDriver *drv)

{

    int ret;

    char tmp_filename[PATH_MAX];



    if (flags & BDRV_O_SNAPSHOT) {

        BlockDriverState *bs1;

        int64_t total_size;

        int is_protocol = 0;

        BlockDriver *bdrv_qcow2;

        QEMUOptionParameter *options;

        char backing_filename[PATH_MAX];



        /* if snapshot, we create a temporary backing file and open it

           instead of opening 'filename' directly */



        /* if there is a backing file, use it */

        bs1 = bdrv_new("");

        ret = bdrv_open(bs1, filename, 0, drv);

        if (ret < 0) {

            bdrv_delete(bs1);

            return ret;

        }

        total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;



        if (bs1->drv && bs1->drv->protocol_name)

            is_protocol = 1;



        bdrv_delete(bs1);



        get_tmp_filename(tmp_filename, sizeof(tmp_filename));



        /* Real path is meaningless for protocols */

        if (is_protocol)

            snprintf(backing_filename, sizeof(backing_filename),

                     "%s", filename);

        else if (!realpath(filename, backing_filename))

            return -errno;



        bdrv_qcow2 = bdrv_find_format("qcow2");

        options = parse_option_parameters("", bdrv_qcow2->create_options, NULL);



        set_option_parameter_int(options, BLOCK_OPT_SIZE, total_size);

        set_option_parameter(options, BLOCK_OPT_BACKING_FILE, backing_filename);

        if (drv) {

            set_option_parameter(options, BLOCK_OPT_BACKING_FMT,

                drv->format_name);

        }



        ret = bdrv_create(bdrv_qcow2, tmp_filename, options);

        free_option_parameters(options);

        if (ret < 0) {

            return ret;

        }



        filename = tmp_filename;

        drv = bdrv_qcow2;

        bs->is_temporary = 1;

    }



    /* Find the right image format driver */

    if (!drv) {

        ret = find_image_format(filename, &drv);

    }



    if (!drv) {

        goto unlink_and_fail;

    }



    /* Open the image */

    ret = bdrv_open_common(bs, filename, flags, drv);

    if (ret < 0) {

        goto unlink_and_fail;

    }



    /* If there is a backing file, use it */

    if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {

        char backing_filename[PATH_MAX];

        int back_flags;

        BlockDriver *back_drv = NULL;



        bs->backing_hd = bdrv_new("");

        bdrv_get_full_backing_filename(bs, backing_filename,

                                       sizeof(backing_filename));



        if (bs->backing_format[0] != '\0') {

            back_drv = bdrv_find_format(bs->backing_format);

        }



        /* backing files always opened read-only */

        back_flags =

            flags & ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);



        ret = bdrv_open(bs->backing_hd, backing_filename, back_flags, back_drv);

        if (ret < 0) {

            bdrv_close(bs);

            return ret;

        }

        if (bs->is_temporary) {

            bs->backing_hd->keep_read_only = !(flags & BDRV_O_RDWR);

        } else {

            /* base image inherits from "parent" */

            bs->backing_hd->keep_read_only = bs->keep_read_only;

        }

    }



    if (!bdrv_key_required(bs)) {

        bdrv_dev_change_media_cb(bs, true);

    }



    /* throttling disk I/O limits */

    if (bs->io_limits_enabled) {

        bdrv_io_limits_enable(bs);

    }



    return 0;



unlink_and_fail:

    if (bs->is_temporary) {

        unlink(filename);

    }

    return ret;

}
