aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--block.c63
1 files changed, 54 insertions, 9 deletions
diff --git a/block.c b/block.c
index 34c728d..fd621f0 100644
--- a/block.c
+++ b/block.c
@@ -2026,6 +2026,57 @@ bool bdrv_is_writable(BlockDriverState *bs)
return bdrv_is_writable_after_reopen(bs, NULL);
}
+static char *bdrv_child_user_desc(BdrvChild *c)
+{
+ if (c->klass->get_parent_desc) {
+ return c->klass->get_parent_desc(c);
+ }
+
+ return g_strdup("another user");
+}
+
+static bool bdrv_a_allow_b(BdrvChild *a, BdrvChild *b, Error **errp)
+{
+ g_autofree char *user = NULL;
+ g_autofree char *perm_names = NULL;
+
+ if ((b->perm & a->shared_perm) == b->perm) {
+ return true;
+ }
+
+ perm_names = bdrv_perm_names(b->perm & ~a->shared_perm);
+ user = bdrv_child_user_desc(a);
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
+ "allow '%s' on %s",
+ user, a->name, perm_names, bdrv_get_node_name(b->bs));
+
+ return false;
+}
+
+static bool bdrv_parent_perms_conflict(BlockDriverState *bs, Error **errp)
+{
+ BdrvChild *a, *b;
+
+ /*
+ * During the loop we'll look at each pair twice. That's correct because
+ * bdrv_a_allow_b() is asymmetric and we should check each pair in both
+ * directions.
+ */
+ QLIST_FOREACH(a, &bs->parents, next_parent) {
+ QLIST_FOREACH(b, &bs->parents, next_parent) {
+ if (a == b) {
+ continue;
+ }
+
+ if (!bdrv_a_allow_b(a, b, errp)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, BdrvChildRole role,
BlockReopenQueue *reopen_queue,
@@ -2203,15 +2254,6 @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
*shared_perm = cumulative_shared_perms;
}
-static char *bdrv_child_user_desc(BdrvChild *c)
-{
- if (c->klass->get_parent_desc) {
- return c->klass->get_parent_desc(c);
- }
-
- return g_strdup("another user");
-}
-
char *bdrv_perm_names(uint64_t perm)
{
struct perm_name {
@@ -2355,6 +2397,9 @@ static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
int ret;
uint64_t perm, shared_perm;
+ if (bdrv_parent_perms_conflict(bs, errp)) {
+ return -EPERM;
+ }
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
if (ret < 0) {