The code partly duplicates bdrv_root_attach_child() and bdrv_attach_child(). Still refactoring these two functions by renaming them to *_common with new noperm argument is more complicating. When all operations moved to new graph update paradigm (update permissions only on updated graph) all duplications should leave.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- block.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 08501350b7..5f6ad1d016 100644 --- a/block.c +++ b/block.c @@ -2974,16 +2974,102 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, return child; } -static void bdrv_detach_child(BdrvChild *child) +static void bdrv_remove_empty_child(BdrvChild *child) { + assert(!child->bs); QLIST_SAFE_REMOVE(child, next); - - bdrv_replace_child(child, NULL); - g_free(child->name); g_free(child); } +typedef struct BdrvAttachChildNopermState { + BdrvChild *child; + AioContext *old_aio_context; /* NULL if not changed */ +} BdrvAttachChildNopermState; + +static void bdrv_attach_child_noperm_abort(void *opaque) +{ + BdrvAttachChildNopermState *s = opaque; + BlockDriverState *bs = s->child->bs; + + bdrv_replace_child_noperm(s->child, NULL); + bdrv_remove_empty_child(s->child); + + /* + * refcnt was positive prior to bdrv_ref() in bdrv_attach_child_noperm(), + * so bs should not be deleted now. + */ + assert(bs->refcnt > 1); + bdrv_unref(bs); + if (s->old_aio_context) { + bdrv_try_set_aio_context(bs, s->old_aio_context, NULL); + } +} + +static BdrvActionDrv bdrv_attach_child_noperm_drv = { + .abort = bdrv_attach_child_noperm_abort, + .clean = g_free, +}; + +__attribute__((unused)) +static BdrvChild *bdrv_attach_child_noperm(BlockDriverState *parent_bs, + BlockDriverState *child_bs, + const char *child_name, + BdrvChildRole child_role, + GSList **tran, + Error **errp) +{ + int ret; + BdrvChild *child; + uint64_t perm, shared_perm; + AioContext *parent_ctx = bdrv_get_aio_context(parent_bs); + AioContext *child_ctx = bdrv_get_aio_context(child_bs); + BdrvAttachChildNopermState *s; + + if (child_ctx != parent_ctx) { + ret = bdrv_try_set_aio_context(child_bs, parent_ctx, errp); + if (ret < 0) { + return NULL; + } + } + + bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm); + + assert(parent_bs->drv); + bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL, + perm, shared_perm, &perm, &shared_perm); + + child = g_new(BdrvChild, 1); + *child = (BdrvChild) { + .bs = NULL, + .name = g_strdup(child_name), + .klass = &child_of_bds, + .role = child_role, + .perm = perm, + .shared_perm = shared_perm, + .opaque = parent_bs, + }; + bdrv_ref(child_bs); + bdrv_replace_child_noperm(child, child_bs); + + QLIST_INSERT_HEAD(&parent_bs->children, child, next); + + s = g_new(BdrvAttachChildNopermState, 1); + *s = (BdrvAttachChildNopermState) { + .child = child, + .old_aio_context = child_ctx == parent_ctx ? NULL : child_ctx, + }; + tran_prepend(tran, &bdrv_attach_child_noperm_drv, s); + + return child; +} + +static void bdrv_detach_child(BdrvChild *child) +{ + bdrv_replace_child(child, NULL); + bdrv_remove_empty_child(child); +} + /* Callers must ensure that child->frozen is false. */ void bdrv_root_unref_child(BdrvChild *child) { -- 2.21.3