block: Include details on permission errors in message
Instead of just telling that there was some conflict, we can be specific
and tell which permissions were in conflict and which way the conflict
is.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Acked-by: Fam Zheng <famz@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
diff --git a/block.c b/block.c
index e03cc5d..f72a67f 100644
--- a/block.c
+++ b/block.c
@@ -1462,6 +1462,43 @@
*shared_perm = cumulative_shared_perms;
}
+static char *bdrv_child_user_desc(BdrvChild *c)
+{
+ if (c->role->get_parent_desc) {
+ return c->role->get_parent_desc(c);
+ }
+
+ return g_strdup("another user");
+}
+
+static char *bdrv_perm_names(uint64_t perm)
+{
+ struct perm_name {
+ uint64_t perm;
+ const char *name;
+ } permissions[] = {
+ { BLK_PERM_CONSISTENT_READ, "consistent read" },
+ { BLK_PERM_WRITE, "write" },
+ { BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
+ { BLK_PERM_RESIZE, "resize" },
+ { BLK_PERM_GRAPH_MOD, "change children" },
+ { 0, NULL }
+ };
+
+ char *result = g_strdup("");
+ struct perm_name *p;
+
+ for (p = permissions; p->name; p++) {
+ if (perm & p->perm) {
+ char *old = result;
+ result = g_strdup_printf("%s%s%s", old, *old ? ", " : "", p->name);
+ g_free(old);
+ }
+ }
+
+ return result;
+}
+
/*
* Checks whether a new reference to @bs can be added if the new user requires
* @new_used_perm/@new_shared_perm as its permissions. If @ignore_child is set,
@@ -1486,17 +1523,25 @@
continue;
}
- if ((new_used_perm & c->shared_perm) != new_used_perm ||
- (c->perm & new_shared_perm) != c->perm)
- {
- const char *user = NULL;
- if (c->role->get_name) {
- user = c->role->get_name(c);
- if (user && !*user) {
- user = NULL;
- }
- }
- error_setg(errp, "Conflicts with %s", user ?: "another operation");
+ if ((new_used_perm & c->shared_perm) != new_used_perm) {
+ char *user = bdrv_child_user_desc(c);
+ char *perm_names = bdrv_perm_names(new_used_perm & ~c->shared_perm);
+ error_setg(errp, "Conflicts with use by %s as '%s', which does not "
+ "allow '%s' on %s",
+ user, c->name, perm_names, bdrv_get_node_name(c->bs));
+ g_free(user);
+ g_free(perm_names);
+ return -EPERM;
+ }
+
+ if ((c->perm & new_shared_perm) != c->perm) {
+ char *user = bdrv_child_user_desc(c);
+ char *perm_names = bdrv_perm_names(c->perm & ~new_shared_perm);
+ error_setg(errp, "Conflicts with use by %s as '%s', which uses "
+ "'%s' on %s",
+ user, c->name, perm_names, bdrv_get_node_name(c->bs));
+ g_free(user);
+ g_free(perm_names);
return -EPERM;
}