|
@@ -2054,7 +2054,9 @@ static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
|
|
|
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs,
|
|
|
+ GSList *ignore_children,
|
|
|
+ Error **errp)
|
|
|
{
|
|
|
BdrvChild *a, *b;
|
|
|
|
|
@@ -2064,8 +2066,12 @@ static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
|
|
|
* directions.
|
|
|
*/
|
|
|
QLIST_FOREACH(a, &bs->parents, next_parent) {
|
|
|
+ if (g_slist_find(ignore_children, a)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
QLIST_FOREACH(b, &bs->parents, next_parent) {
|
|
|
- if (a == b) {
|
|
|
+ if (a == b || g_slist_find(ignore_children, b)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -2094,6 +2100,40 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Adds the whole subtree of @bs (including @bs itself) to the @list (except for
|
|
|
+ * nodes that are already in the @list, of course) so that final list is
|
|
|
+ * topologically sorted. Return the result (GSList @list object is updated, so
|
|
|
+ * don't use old reference after function call).
|
|
|
+ *
|
|
|
+ * On function start @list must be already topologically sorted and for any node
|
|
|
+ * in the @list the whole subtree of the node must be in the @list as well. The
|
|
|
+ * simplest way to satisfy this criteria: use only result of
|
|
|
+ * bdrv_topological_dfs() or NULL as @list parameter.
|
|
|
+ */
|
|
|
+static GSList *bdrv_topological_dfs(GSList *list, GHashTable *found,
|
|
|
+ BlockDriverState *bs)
|
|
|
+{
|
|
|
+ BdrvChild *child;
|
|
|
+ g_autoptr(GHashTable) local_found = NULL;
|
|
|
+
|
|
|
+ if (!found) {
|
|
|
+ assert(!list);
|
|
|
+ found = local_found = g_hash_table_new(NULL, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (g_hash_table_contains(found, bs)) {
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ g_hash_table_add(found, bs);
|
|
|
+
|
|
|
+ QLIST_FOREACH(child, &bs->children, next) {
|
|
|
+ list = bdrv_topological_dfs(list, found, child->bs);
|
|
|
+ }
|
|
|
+
|
|
|
+ return g_slist_prepend(list, bs);
|
|
|
+}
|
|
|
+
|
|
|
static void bdrv_child_set_perm_commit(void *opaque)
|
|
|
{
|
|
|
BdrvChild *c = opaque;
|
|
@@ -2158,10 +2198,10 @@ static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
|
|
|
* A call to this function must always be followed by a call to bdrv_set_perm()
|
|
|
* or bdrv_abort_perm_update().
|
|
|
*/
|
|
|
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|
|
- uint64_t cumulative_perms,
|
|
|
- uint64_t cumulative_shared_perms,
|
|
|
- GSList *ignore_children, Error **errp)
|
|
|
+static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|
|
+ uint64_t cumulative_perms,
|
|
|
+ uint64_t cumulative_shared_perms,
|
|
|
+ GSList *ignore_children, Error **errp)
|
|
|
{
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
BdrvChild *c;
|
|
@@ -2226,21 +2266,43 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|
|
/* Check all children */
|
|
|
QLIST_FOREACH(c, &bs->children, next) {
|
|
|
uint64_t cur_perm, cur_shared;
|
|
|
- GSList *cur_ignore_children;
|
|
|
|
|
|
bdrv_child_perm(bs, c->bs, c, c->role, q,
|
|
|
cumulative_perms, cumulative_shared_perms,
|
|
|
&cur_perm, &cur_shared);
|
|
|
+ bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|
|
+ uint64_t cumulative_perms,
|
|
|
+ uint64_t cumulative_shared_perms,
|
|
|
+ GSList *ignore_children, Error **errp)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ BlockDriverState *root = bs;
|
|
|
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, root);
|
|
|
|
|
|
- cur_ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
|
|
|
- ret = bdrv_check_update_perm(c->bs, q, cur_perm, cur_shared,
|
|
|
- cur_ignore_children, errp);
|
|
|
- g_slist_free(cur_ignore_children);
|
|
|
+ for ( ; list; list = list->next) {
|
|
|
+ bs = list->data;
|
|
|
+
|
|
|
+ if (bs != root) {
|
|
|
+ if (bdrv_parent_perms_conflict(bs, ignore_children, errp)) {
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ bdrv_get_cumulative_perm(bs, &cumulative_perms,
|
|
|
+ &cumulative_shared_perms);
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = bdrv_node_check_perm(bs, q, cumulative_perms,
|
|
|
+ cumulative_shared_perms,
|
|
|
+ ignore_children, errp);
|
|
|
if (ret < 0) {
|
|
|
return ret;
|
|
|
}
|
|
|
-
|
|
|
- bdrv_child_set_perm_safe(c, cur_perm, cur_shared, NULL);
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -2250,10 +2312,8 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|
|
* Notifies drivers that after a previous bdrv_check_perm() call, the
|
|
|
* permission update is not performed and any preparations made for it (e.g.
|
|
|
* taken file locks) need to be undone.
|
|
|
- *
|
|
|
- * This function recursively notifies all child nodes.
|
|
|
*/
|
|
|
-static void bdrv_abort_perm_update(BlockDriverState *bs)
|
|
|
+static void bdrv_node_abort_perm_update(BlockDriverState *bs)
|
|
|
{
|
|
|
BlockDriver *drv = bs->drv;
|
|
|
BdrvChild *c;
|
|
@@ -2268,11 +2328,19 @@ static void bdrv_abort_perm_update(BlockDriverState *bs)
|
|
|
|
|
|
QLIST_FOREACH(c, &bs->children, next) {
|
|
|
bdrv_child_set_perm_abort(c);
|
|
|
- bdrv_abort_perm_update(c->bs);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void bdrv_set_perm(BlockDriverState *bs)
|
|
|
+static void bdrv_abort_perm_update(BlockDriverState *bs)
|
|
|
+{
|
|
|
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
|
|
|
+
|
|
|
+ for ( ; list; list = list->next) {
|
|
|
+ bdrv_node_abort_perm_update((BlockDriverState *)list->data);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void bdrv_node_set_perm(BlockDriverState *bs)
|
|
|
{
|
|
|
uint64_t cumulative_perms, cumulative_shared_perms;
|
|
|
BlockDriver *drv = bs->drv;
|
|
@@ -2298,7 +2366,15 @@ static void bdrv_set_perm(BlockDriverState *bs)
|
|
|
/* Update all children */
|
|
|
QLIST_FOREACH(c, &bs->children, next) {
|
|
|
bdrv_child_set_perm_commit(c);
|
|
|
- bdrv_set_perm(c->bs);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void bdrv_set_perm(BlockDriverState *bs)
|
|
|
+{
|
|
|
+ g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
|
|
|
+
|
|
|
+ for ( ; list; list = list->next) {
|
|
|
+ bdrv_node_set_perm((BlockDriverState *)list->data);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2411,7 +2487,7 @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
|
|
|
int ret;
|
|
|
uint64_t perm, shared_perm;
|
|
|
|
|
|
- if (bdrv_parent_perms_conflict(bs, errp)) {
|
|
|
+ if (bdrv_parent_perms_conflict(bs, NULL, errp)) {
|
|
|
return -EPERM;
|
|
|
}
|
|
|
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|