bdrv_append is not very good for inserting filters: it does extra permission update as part of bdrv_set_backing_hd(). During this update filter may conflict with other parents of top_bs.
Instead, let's first do all graph modifications and after it update permissions. Note: bdrv_append() is still only works for backing-child based filters. It's something to improve later. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- block.c | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/block.c b/block.c index f2e714a81d..cf7b859a81 100644 --- a/block.c +++ b/block.c @@ -4953,22 +4953,50 @@ int bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, Error **errp) { - Error *local_err = NULL; + int ret; + GSList *tran = NULL; + AioContext *bs_new_ctx = bdrv_get_aio_context(bs_new); + AioContext *bs_top_ctx = bdrv_get_aio_context(bs_top); - bdrv_set_backing_hd(bs_new, bs_top, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return -EPERM; + assert(!bs_new->backing); + + if (bs_new_ctx != bs_top_ctx) { + ret = bdrv_try_set_aio_context(bs_new, bs_top_ctx, NULL); + if (ret < 0) { + ret = bdrv_try_set_aio_context(bs_top, bs_new_ctx, errp); + } + if (ret < 0) { + return ret; + } } - bdrv_replace_node(bs_top, bs_new, &local_err); - if (local_err) { - error_propagate(errp, local_err); - bdrv_set_backing_hd(bs_new, NULL, &error_abort); - return -EPERM; + bs_new->backing = bdrv_attach_child_noperm(bs_new, bs_top, "backing", + bdrv_backing_role(bs_new), + &tran, errp); + if (!bs_new->backing) { + ret = -EINVAL; + goto out; } - return 0; + ret = bdrv_replace_node_noperm(bs_top, bs_new, true, &tran, errp); + if (ret < 0) { + goto out; + } + + ret = bdrv_refresh_perms(bs_new, errp); +out: + tran_finalize(tran, ret); + if (ret < 0) { + bs_new->backing = NULL; + if (bs_new_ctx != bdrv_get_aio_context(bs_new)) { + bdrv_try_set_aio_context(bs_new, bs_new_ctx, &error_abort); + } + if (bs_top_ctx != bdrv_get_aio_context(bs_top)) { + bdrv_try_set_aio_context(bs_top, bs_top_ctx, &error_abort); + } + } + + return ret; } static void bdrv_delete(BlockDriverState *bs) -- 2.21.3