mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
block: Add *tighten_restrictions to *check*_perm()
This patch makes three functions report whether the necessary permission change tightens restrictions or not. These functions are: - bdrv_check_perm() - bdrv_check_update_perm() - bdrv_child_check_perm() Callers can use this result to decide whether a failure is fatal or not (see the next patch). Signed-off-by: Max Reitz <mreitz@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
87ace5f8b6
commit
9eab154415
1 changed files with 70 additions and 17 deletions
87
block.c
87
block.c
|
@ -1706,7 +1706,8 @@ static int bdrv_fill_options(QDict **options, const char *filename,
|
|||
|
||||
static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
|
||||
uint64_t perm, uint64_t shared,
|
||||
GSList *ignore_children, Error **errp);
|
||||
GSList *ignore_children,
|
||||
bool *tighten_restrictions, Error **errp);
|
||||
static void bdrv_child_abort_perm_update(BdrvChild *c);
|
||||
static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared);
|
||||
static void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
|
||||
|
@ -1781,18 +1782,43 @@ static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
|
|||
* permissions of all its parents. This involves checking whether all necessary
|
||||
* permission changes to child nodes can be performed.
|
||||
*
|
||||
* Will set *tighten_restrictions to true if and only if new permissions have to
|
||||
* be taken or currently shared permissions are to be unshared. Otherwise,
|
||||
* errors are not fatal as long as the caller accepts that the restrictions
|
||||
* remain tighter than they need to be. The caller still has to abort the
|
||||
* transaction.
|
||||
* @tighten_restrictions cannot be used together with @q: When reopening, we may
|
||||
* encounter fatal errors even though no restrictions are to be tightened. For
|
||||
* example, changing a node from RW to RO will fail if the WRITE permission is
|
||||
* to be kept.
|
||||
*
|
||||
* 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)
|
||||
GSList *ignore_children,
|
||||
bool *tighten_restrictions, Error **errp)
|
||||
{
|
||||
BlockDriver *drv = bs->drv;
|
||||
BdrvChild *c;
|
||||
int ret;
|
||||
|
||||
assert(!q || !tighten_restrictions);
|
||||
|
||||
if (tighten_restrictions) {
|
||||
uint64_t current_perms, current_shared;
|
||||
uint64_t added_perms, removed_shared_perms;
|
||||
|
||||
bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared);
|
||||
|
||||
added_perms = cumulative_perms & ~current_perms;
|
||||
removed_shared_perms = current_shared & ~cumulative_shared_perms;
|
||||
|
||||
*tighten_restrictions = added_perms || removed_shared_perms;
|
||||
}
|
||||
|
||||
/* Write permissions never work with read-only images */
|
||||
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
|
||||
!bdrv_is_writable_after_reopen(bs, q))
|
||||
|
@ -1833,11 +1859,18 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
/* Check all children */
|
||||
QLIST_FOREACH(c, &bs->children, next) {
|
||||
uint64_t cur_perm, cur_shared;
|
||||
bool child_tighten_restr;
|
||||
|
||||
bdrv_child_perm(bs, c->bs, c, c->role, q,
|
||||
cumulative_perms, cumulative_shared_perms,
|
||||
&cur_perm, &cur_shared);
|
||||
ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared,
|
||||
ignore_children, errp);
|
||||
ret = bdrv_child_check_perm(c, q, cur_perm, cur_shared, ignore_children,
|
||||
tighten_restrictions ? &child_tighten_restr
|
||||
: NULL,
|
||||
errp);
|
||||
if (tighten_restrictions) {
|
||||
*tighten_restrictions |= child_tighten_restr;
|
||||
}
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -1961,17 +1994,23 @@ char *bdrv_perm_names(uint64_t perm)
|
|||
* set, the BdrvChild objects in this list are ignored in the calculations;
|
||||
* this allows checking permission updates for an existing reference.
|
||||
*
|
||||
* See bdrv_check_perm() for the semantics of @tighten_restrictions.
|
||||
*
|
||||
* Needs to be followed by a call to either bdrv_set_perm() or
|
||||
* bdrv_abort_perm_update(). */
|
||||
static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
||||
uint64_t new_used_perm,
|
||||
uint64_t new_shared_perm,
|
||||
GSList *ignore_children, Error **errp)
|
||||
GSList *ignore_children,
|
||||
bool *tighten_restrictions,
|
||||
Error **errp)
|
||||
{
|
||||
BdrvChild *c;
|
||||
uint64_t cumulative_perms = new_used_perm;
|
||||
uint64_t cumulative_shared_perms = new_shared_perm;
|
||||
|
||||
assert(!q || !tighten_restrictions);
|
||||
|
||||
/* There is no reason why anyone couldn't tolerate write_unchanged */
|
||||
assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
|
||||
|
||||
|
@ -1983,6 +2022,11 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
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);
|
||||
|
||||
if (tighten_restrictions) {
|
||||
*tighten_restrictions = true;
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -1994,6 +2038,11 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
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);
|
||||
|
||||
if (tighten_restrictions) {
|
||||
*tighten_restrictions = true;
|
||||
}
|
||||
|
||||
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));
|
||||
|
@ -2007,19 +2056,21 @@ static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
}
|
||||
|
||||
return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
|
||||
ignore_children, errp);
|
||||
ignore_children, tighten_restrictions, errp);
|
||||
}
|
||||
|
||||
/* Needs to be followed by a call to either bdrv_child_set_perm() or
|
||||
* bdrv_child_abort_perm_update(). */
|
||||
static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
|
||||
uint64_t perm, uint64_t shared,
|
||||
GSList *ignore_children, Error **errp)
|
||||
GSList *ignore_children,
|
||||
bool *tighten_restrictions, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ignore_children = g_slist_prepend(g_slist_copy(ignore_children), c);
|
||||
ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
|
||||
ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children,
|
||||
tighten_restrictions, errp);
|
||||
g_slist_free(ignore_children);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -2072,7 +2123,7 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, errp);
|
||||
ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, NULL, errp);
|
||||
if (ret < 0) {
|
||||
bdrv_child_abort_perm_update(c);
|
||||
return ret;
|
||||
|
@ -2258,7 +2309,8 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
|
|||
* because we're just taking a parent away, so we're loosening
|
||||
* restrictions. */
|
||||
bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL,
|
||||
NULL, &error_abort);
|
||||
bdrv_set_perm(old_bs, perm, shared_perm);
|
||||
|
||||
/* When the parent requiring a non-default AioContext is removed, the
|
||||
|
@ -2288,7 +2340,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs,
|
|||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
|
||||
ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, NULL,
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
bdrv_abort_perm_update(child_bs);
|
||||
bdrv_unref(child_bs);
|
||||
|
@ -3369,7 +3422,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
|||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
BDRVReopenState *state = &bs_entry->state;
|
||||
ret = bdrv_check_perm(state->bs, bs_queue, state->perm,
|
||||
state->shared_perm, NULL, errp);
|
||||
state->shared_perm, NULL, NULL, errp);
|
||||
if (ret < 0) {
|
||||
goto cleanup_perm;
|
||||
}
|
||||
|
@ -3381,7 +3434,7 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp)
|
|||
state->perm, state->shared_perm,
|
||||
&nperm, &nshared);
|
||||
ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
|
||||
nperm, nshared, NULL, errp);
|
||||
nperm, nshared, NULL, NULL, errp);
|
||||
if (ret < 0) {
|
||||
goto cleanup_perm;
|
||||
}
|
||||
|
@ -4097,7 +4150,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
|||
|
||||
/* 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);
|
||||
ret = bdrv_check_update_perm(to, NULL, perm, shared, list, NULL, errp);
|
||||
if (ret < 0) {
|
||||
bdrv_abort_perm_update(to);
|
||||
goto out;
|
||||
|
@ -4444,7 +4497,7 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
|||
/* Check whether we are allowed to switch c from top to base */
|
||||
GSList *ignore_children = g_slist_prepend(NULL, c);
|
||||
ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm,
|
||||
ignore_children, &local_err);
|
||||
ignore_children, NULL, &local_err);
|
||||
g_slist_free(ignore_children);
|
||||
if (ret < 0) {
|
||||
error_report_err(local_err);
|
||||
|
@ -5219,7 +5272,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
|||
*/
|
||||
bs->open_flags &= ~BDRV_O_INACTIVE;
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &local_err);
|
||||
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &local_err);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -5369,7 +5422,7 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs)
|
|||
|
||||
/* Update permissions, they may differ for inactive nodes */
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, &error_abort);
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue