diff options
-rw-r--r-- | block.c | 68 | ||||
-rw-r--r-- | block/commit.c | 2 |
2 files changed, 41 insertions, 29 deletions
@@ -985,14 +985,26 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, const char *filename, Error **errp) { BlockDriverState *parent = c->opaque; + int orig_flags = bdrv_get_flags(parent); int ret; + if (!(orig_flags & BDRV_O_RDWR)) { + ret = bdrv_reopen(parent, orig_flags | BDRV_O_RDWR, errp); + if (ret < 0) { + return ret; + } + } + ret = bdrv_change_backing_file(parent, filename, base->drv ? base->drv->format_name : ""); if (ret < 0) { error_setg_errno(errp, ret, "Could not update backing file link"); } + if (!(orig_flags & BDRV_O_RDWR)) { + bdrv_reopen(parent, orig_flags, NULL); + } + return ret; } @@ -3482,7 +3494,7 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, BlockDriverState *base, const char *backing_file_str) { - BlockDriverState *new_top_bs = NULL; + BdrvChild *c, *next; Error *local_err = NULL; int ret = -EIO; @@ -3492,42 +3504,42 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top, goto exit; } - new_top_bs = bdrv_find_overlay(active, top); - - if (new_top_bs == NULL) { - /* we could not find the image above 'top', this is an error */ - goto exit; - } - - /* special case of new_top_bs->backing->bs already pointing to base - nothing - * to do, no intermediate images */ - if (backing_bs(new_top_bs) == base) { - ret = 0; - goto exit; - } - /* Make sure that base is in the backing chain of top */ if (!bdrv_chain_contains(top, base)) { goto exit; } /* success - we can delete the intermediate states, and link top->base */ - if (new_top_bs->backing->role->update_filename) { - backing_file_str = backing_file_str ? backing_file_str : base->filename; - ret = new_top_bs->backing->role->update_filename(new_top_bs->backing, - base, backing_file_str, - &local_err); - if (ret < 0) { - bdrv_set_backing_hd(new_top_bs, top, &error_abort); + backing_file_str = backing_file_str ? backing_file_str : base->filename; + + QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { + /* Check whether we are allowed to switch c from top to base */ + GSList *ignore_children = g_slist_prepend(NULL, c); + bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, + ignore_children, &local_err); + if (local_err) { + ret = -EPERM; + error_report_err(local_err); goto exit; } - } + g_slist_free(ignore_children); - bdrv_set_backing_hd(new_top_bs, base, &local_err); - if (local_err) { - ret = -EPERM; - error_report_err(local_err); - goto exit; + /* If so, update the backing file path in the image file */ + if (c->role->update_filename) { + ret = c->role->update_filename(c, base, backing_file_str, + &local_err); + if (ret < 0) { + bdrv_abort_perm_update(base); + error_report_err(local_err); + goto exit; + } + } + + /* Do the actual switch in the in-memory graph. + * Completes bdrv_check_update_perm() transaction internally. */ + bdrv_ref(base); + bdrv_replace_child(c, base); + bdrv_unref(top); } ret = 0; diff --git a/block/commit.c b/block/commit.c index 8f0e83578a..610e1cd8f5 100644 --- a/block/commit.c +++ b/block/commit.c @@ -350,7 +350,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, error_propagate(errp, local_err); goto fail; } - bdrv_set_backing_hd(overlay_bs, commit_top_bs, &local_err); + bdrv_replace_node(top, commit_top_bs, &local_err); if (local_err) { bdrv_unref(commit_top_bs); commit_top_bs = NULL; |