qemu/block.c
/*
 * Prepares a BlockDriverState for reopen. All changes are staged in the
 * 'opaque' field of the BDRVReopenState, which is used and allocated by
 * the block driver layer .bdrv_reopen_prepare()
 *
 * bs is the BlockDriverState to reopen
 * flags are the new open flags
 * queue is the reopen queue
 *
 * Returns 0 on success, non-zero on error.  On error errp will be set
 * as well.
 *
 * On failure, bdrv_reopen_abort() will be called to clean up any data.
 * It is the responsibility of the caller to then call the abort() or
 * commit() for any other BDS that have been left in a prepare() state
 *
 */
Prepares a BlockDriverState for reopen. All changes are staged in the
'opaque' field of the BDRVReopenState, which is used and allocated by
the block driver layer .bdrv_reopen_prepare()

bs is the BlockDriverState to reopen
flags are the new open flags
queue is the reopen queue

Returns 0 on success, non-zero on error.  On error errp will be set
as well.

On failure, bdrv_reopen_abort() will be called to clean up any data.
It is the responsibility of the caller to then call the abort() or
commit() for any other BDS that have been left in a prepare() state
Prepares a BlockDriverState for reopen . All changes are staged in the ' opaque ' field of the BDRVReopenState which is used and allocated by the block driver layer . bdrv_reopen_prepare () bs is the BlockDriverState to reopen flags are the new open flags queue is the reopen queue Returns 0 on success non - zero on error . On error errp will be set as well . On failure bdrv_reopen_abort () will be called to clean up any data . It is the responsibility of the caller to then call the abort () or commit () for any other BDS that have been left in a prepare () state
int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
                        Error **errp)
{
    int ret = -1;
    Error *local_err = NULL;
    BlockDriver *drv;
    QemuOpts *opts;
    QDict *orig_reopen_opts;
    char *discard = NULL;
    bool read_only;
    bool drv_prepared = false;
    assert(reopen_state != NULL);
    assert(reopen_state->bs->drv != NULL);
    drv = reopen_state->bs->drv;
    orig_reopen_opts = qdict_clone_shallow(reopen_state->options);
    opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
    qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto error;
    }
    update_flags_from_options(&reopen_state->flags, opts);
    discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD);
    if (discard != NULL) {
        if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) {
            error_setg(errp, "Invalid discard option");
            ret = -EINVAL;
            goto error;
        }
    }
    reopen_state->detect_zeroes =
        bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        ret = -EINVAL;
        goto error;
    }
    qemu_opts_to_qdict(opts, reopen_state->options);
    read_only = !(reopen_state->flags & BDRV_O_RDWR);
    ret = bdrv_can_set_read_only(reopen_state->bs, read_only, true, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        goto error;
    }
    bdrv_reopen_perm(queue, reopen_state->bs,
                     &reopen_state->perm, &reopen_state->shared_perm);
    ret = bdrv_flush(reopen_state->bs);
    if (ret) {
        error_setg_errno(errp, -ret, "Error flushing drive");
        goto error;
    }
    if (drv->bdrv_reopen_prepare) {
        ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err);
        if (ret) {
            if (local_err != NULL) {
                error_propagate(errp, local_err);
            } else {
                error_setg(errp, "failed while preparing to reopen image '%s'",
                           reopen_state->bs->filename);
            }
            goto error;
        }
    } else {
        error_setg(errp, "Block format '%s' used by node '%s' "
                   "does not support reopening files", drv->format_name,
                   bdrv_get_device_or_node_name(reopen_state->bs));
        ret = -1;
        goto error;
    }
    drv_prepared = true;
    if (qdict_size(reopen_state->options)) {
        const QDictEntry *entry = qdict_first(reopen_state->options);
        do {
            QObject *new = entry->value;
            QObject *old = qdict_get(reopen_state->bs->options, entry->key);
            if (qobject_type(new) == QTYPE_QSTRING) {
                BdrvChild *child;
                QLIST_FOREACH(child, &reopen_state->bs->children, next) {
                    if (!strcmp(child->name, entry->key)) {
                        break;
                    }
                }
                if (child) {
                    const char *str = qobject_get_try_str(new);
                    if (!strcmp(child->bs->node_name, str)) {
                        continue; 
                    }
                }
            }
            if (!qobject_is_equal(new, old)) {
                error_setg(errp, "Cannot change the option '%s'", entry->key);
                ret = -EINVAL;
                goto error;
            }
        } while ((entry = qdict_next(reopen_state->options, entry)));
    }
    ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm,
                          reopen_state->shared_perm, NULL, errp);
    if (ret < 0) {
        goto error;
    }
    ret = 0;
    qobject_unref(reopen_state->options);
    reopen_state->options = qobject_ref(orig_reopen_opts);
error:
    if (ret < 0 && drv_prepared) {
        if (drv->bdrv_reopen_abort) {
            drv->bdrv_reopen_abort(reopen_state);
        }
    }
    qemu_opts_del(opts);
    qobject_unref(orig_reopen_opts);
    g_free(discard);
    return ret;
}
int bdrv_reopen_prepare ( BDRVReopenState * reopen_state , BlockReopenQueue * queue , Error * * errp ) { int ret = -1 ; Error * local_err = NULL ; BlockDriver * drv ; QemuOpts * opts ; QDict * orig_reopen_opts ; char * discard = NULL ; bool read_only ; bool drv_prepared = false ; assert ( reopen_state != NULL ) ; assert ( reopen_state -> bs -> drv != NULL ) ; drv = reopen_state -> bs -> drv ; orig_reopen_opts = qdict_clone_shallow ( reopen_state -> options ) ; opts = qemu_opts_create ( & bdrv_runtime_opts , NULL , 0 , & error_abort ) ; qemu_opts_absorb_qdict ( opts , reopen_state -> options , & local_err ) ; if ( local_err ) { error_propagate ( errp , local_err ) ; ret = - EINVAL ; goto error ; } update_flags_from_options ( & reopen_state -> flags , opts ) ; discard = qemu_opt_get_del ( opts , BDRV_OPT_DISCARD ) ; if ( discard != NULL ) { if ( bdrv_parse_discard_flags ( discard , & reopen_state -> flags ) != 0 ) { error_setg ( errp , " " ) ; ret = - EINVAL ; goto error ; } } reopen_state -> detect_zeroes = bdrv_parse_detect_zeroes ( opts , reopen_state -> flags , & local_err ) ; if ( local_err ) { error_propagate ( errp , local_err ) ; ret = - EINVAL ; goto error ; } qemu_opts_to_qdict ( opts , reopen_state -> options ) ; read_only = ! ( reopen_state -> flags & BDRV_O_RDWR ) ; ret = bdrv_can_set_read_only ( reopen_state -> bs , read_only , true , & local_err ) ; if ( local_err ) { error_propagate ( errp , local_err ) ; goto error ; } bdrv_reopen_perm ( queue , reopen_state -> bs , & reopen_state -> perm , & reopen_state -> shared_perm ) ; ret = bdrv_flush ( reopen_state -> bs ) ; if ( ret ) { error_setg_errno ( errp , - ret , " " ) ; goto error ; } if ( drv -> bdrv_reopen_prepare ) { ret = drv -> bdrv_reopen_prepare ( reopen_state , queue , & local_err ) ; if ( ret ) { if ( local_err != NULL ) { error_propagate ( errp , local_err ) ; } else { error_setg ( errp , " " , reopen_state -> bs -> filename ) ; } goto error ; } } else { error_setg ( errp , " " " " , drv -> format_name , bdrv_get_device_or_node_name ( reopen_state -> bs ) ) ; ret = -1 ; goto error ; } drv_prepared = true ; if ( qdict_size ( reopen_state -> options ) ) { const QDictEntry * entry = qdict_first ( reopen_state -> options ) ; do { QObject * new = entry -> value ; QObject * old = qdict_get ( reopen_state -> bs -> options , entry -> key ) ; if ( qobject_type ( new ) == QTYPE_QSTRING ) { BdrvChild * child ; QLIST_FOREACH ( child , & reopen_state -> bs -> children , next ) { if ( ! strcmp ( child -> name , entry -> key ) ) { break ; } } if ( child ) { const char * str = qobject_get_try_str ( new ) ; if ( ! strcmp ( child -> bs -> node_name , str ) ) { continue ; } } } if ( ! qobject_is_equal ( new , old ) ) { error_setg ( errp , " " , entry -> key ) ; ret = - EINVAL ; goto error ; } } while ( ( entry = qdict_next ( reopen_state -> options , entry ) ) ) ; } ret = bdrv_check_perm ( reopen_state -> bs , queue , reopen_state -> perm , reopen_state -> shared_perm , NULL , errp ) ; if ( ret < 0 ) { goto error ; } ret = 0 ; qobject_unref ( reopen_state -> options ) ; reopen_state -> options = qobject_ref ( orig_reopen_opts ) ; error : if ( ret < 0 && drv_prepared ) { if ( drv -> bdrv_reopen_abort ) { drv -> bdrv_reopen_abort ( reopen_state ) ; } } qemu_opts_del ( opts ) ; qobject_unref ( orig_reopen_opts ) ; g_free ( discard ) ; return ret ; }
Prepares a BlockDriverState for reopen.
Prepares a BlockDriverState for reopen .
/* This function and each driver's bdrv_reopen_prepare() remove
     * entries from reopen_state->options as they are processed, so
     * we need to make a copy of the original QDict. */ /* Process generic block layer options */ /* All other options (including node-name and driver) must be unchanged.
     * Put them back into the QDict, so that they are checked at the end
     * of this function. */ /* If we are to stay read-only, do not allow permission change
     * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is
     * not set, or if the BDS still has copy_on_read enabled */ /* Calculate required permissions after reopening */ /* It is currently mandatory to have a bdrv_reopen_prepare()
         * handler for each supported drv. */ /* Options that are not handled are only okay if they are unchanged
     * compared to the old state. It is expected that some options are only
     * used for the initial open, but not reopen (e.g. filename) */ /* Allow child references (child_name=node_name) as long as they
             * point to the current child (i.e. everything stays the same). */ /* Found child with this name, skip option */ /*
             * TODO: When using -drive to specify blockdev options, all values
             * will be strings; however, when using -blockdev, blockdev-add or
             * filenames using the json:{} pseudo-protocol, they will be
             * correctly typed.
             * In contrast, reopening options are (currently) always strings
             * (because you can only specify them through qemu-io; all other
             * callers do not specify any options).
             * Therefore, when using anything other than -drive to create a BDS,
             * this cannot detect non-string options as unchanged, because
             * qobject_is_equal() always returns false for objects of different
             * type.  In the future, this should be remedied by correctly typing
             * all options.  For now, this is not too big of an issue because
             * the user can simply omit options which cannot be changed anyway,
             * so they will stay unchanged.
             */ /* Restore the original reopen_state->options QDict */ /* drv->bdrv_reopen_prepare() has succeeded, so we need to
         * call drv->bdrv_reopen_abort() before signaling an error
         * (bdrv_reopen_multiple() will not call bdrv_reopen_abort()
         * when the respective bdrv_reopen_prepare() has failed) */