|
@@ -2273,7 +2273,6 @@ static TransactionActionDrv bdrv_replace_child_drv = {
|
|
|
*
|
|
|
* Note: real unref of old_bs is done only on commit.
|
|
|
*/
|
|
|
-__attribute__((unused))
|
|
|
static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
|
|
|
Transaction *tran)
|
|
|
{
|
|
@@ -4877,8 +4876,9 @@ static int bdrv_replace_node_common(BlockDriverState *from,
|
|
|
bool auto_skip, Error **errp)
|
|
|
{
|
|
|
BdrvChild *c, *next;
|
|
|
- GSList *list = NULL, *p;
|
|
|
- uint64_t perm = 0, shared = BLK_PERM_ALL;
|
|
|
+ Transaction *tran = tran_new();
|
|
|
+ g_autoptr(GHashTable) found = NULL;
|
|
|
+ g_autoptr(GSList) refresh_list = NULL;
|
|
|
int ret;
|
|
|
|
|
|
/* Make sure that @from doesn't go away until we have successfully attached
|
|
@@ -4889,7 +4889,12 @@ static int bdrv_replace_node_common(BlockDriverState *from,
|
|
|
assert(bdrv_get_aio_context(from) == bdrv_get_aio_context(to));
|
|
|
bdrv_drained_begin(from);
|
|
|
|
|
|
- /* Put all parents into @list and calculate their cumulative permissions */
|
|
|
+ /*
|
|
|
+ * Do the replacement without permission update.
|
|
|
+ * Replacement may influence the permissions, we should calculate new
|
|
|
+ * permissions based on new graph. If we fail, we'll roll-back the
|
|
|
+ * replacement.
|
|
|
+ */
|
|
|
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
|
|
|
assert(c->bs == from);
|
|
|
if (!should_update_child(c, to)) {
|
|
@@ -4907,36 +4912,24 @@ static int bdrv_replace_node_common(BlockDriverState *from,
|
|
|
c->name, from->node_name);
|
|
|
goto out;
|
|
|
}
|
|
|
- list = g_slist_prepend(list, c);
|
|
|
- perm |= c->perm;
|
|
|
- shared &= c->shared_perm;
|
|
|
+ bdrv_replace_child_safe(c, to, tran);
|
|
|
}
|
|
|
|
|
|
- /* Check whether the required permissions can be granted on @to, ignoring
|
|
|
- * all BdrvChild in @list so that they can't block themselves. */
|
|
|
- ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp);
|
|
|
- if (ret < 0) {
|
|
|
- bdrv_abort_perm_update(to);
|
|
|
- goto out;
|
|
|
- }
|
|
|
+ found = g_hash_table_new(NULL, NULL);
|
|
|
|
|
|
- /* Now actually perform the change. We performed the permission check for
|
|
|
- * all elements of @list at once, so set the permissions all at once at the
|
|
|
- * very end. */
|
|
|
- for (p = list; p != NULL; p = p->next) {
|
|
|
- c = p->data;
|
|
|
+ refresh_list = bdrv_topological_dfs(refresh_list, found, to);
|
|
|
+ refresh_list = bdrv_topological_dfs(refresh_list, found, from);
|
|
|
|
|
|
- bdrv_ref(to);
|
|
|
- bdrv_replace_child_noperm(c, to);
|
|
|
- bdrv_unref(from);
|
|
|
+ ret = bdrv_list_refresh_perms(refresh_list, NULL, tran, errp);
|
|
|
+ if (ret < 0) {
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- bdrv_set_perm(to);
|
|
|
-
|
|
|
ret = 0;
|
|
|
|
|
|
out:
|
|
|
- g_slist_free(list);
|
|
|
+ tran_finalize(tran, ret);
|
|
|
+
|
|
|
bdrv_drained_end(from);
|
|
|
bdrv_unref(from);
|
|
|
|