* QEMU System Emulator block driver
*
* Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2020 Virtuozzo International GmbH.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
#include "qapi/qobject-output-visitor.h"
#include "qapi/qapi-visit-block-core.h"
#include "sysemu/block-backend.h"
-#include "sysemu/sysemu.h"
#include "qemu/notify.h"
#include "qemu/option.h"
#include "qemu/coroutine.h"
for (i = 0; i < BLOCK_OP_TYPE_MAX; i++) {
QLIST_INIT(&bs->op_blockers[i]);
}
- notifier_with_return_list_init(&bs->before_write_notifiers);
qemu_co_mutex_init(&bs->reqs_lock);
qemu_mutex_init(&bs->dirty_bitmap_mutex);
bs->refcnt = 1;
return 0;
}
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t new_used_perm,
- uint64_t new_shared_perm,
- Error **errp);
-
typedef struct BlockReopenQueueEntry {
bool prepared;
bool perms_checked;
return g_slist_prepend(list, bs);
}
-static void bdrv_child_set_perm_commit(void *opaque)
-{
- BdrvChild *c = opaque;
-
- c->has_backup_perm = false;
-}
+typedef struct BdrvChildSetPermState {
+ BdrvChild *child;
+ uint64_t old_perm;
+ uint64_t old_shared_perm;
+} BdrvChildSetPermState;
static void bdrv_child_set_perm_abort(void *opaque)
{
- BdrvChild *c = opaque;
- /*
- * We may have child->has_backup_perm unset at this point, as in case of
- * _check_ stage of permission update failure we may _check_ not the whole
- * subtree. Still, _abort_ is called on the whole subtree anyway.
- */
- if (c->has_backup_perm) {
- c->perm = c->backup_perm;
- c->shared_perm = c->backup_shared_perm;
- c->has_backup_perm = false;
- }
+ BdrvChildSetPermState *s = opaque;
+
+ s->child->perm = s->old_perm;
+ s->child->shared_perm = s->old_shared_perm;
}
static TransactionActionDrv bdrv_child_set_pem_drv = {
.abort = bdrv_child_set_perm_abort,
- .commit = bdrv_child_set_perm_commit,
+ .clean = g_free,
};
-/*
- * With tran=NULL needs to be followed by direct call to either
- * bdrv_child_set_perm_commit() or bdrv_child_set_perm_abort().
- *
- * With non-NULL tran needs to be followed by tran_abort() or tran_commit()
- * instead.
- */
-static void bdrv_child_set_perm_safe(BdrvChild *c, uint64_t perm,
- uint64_t shared, Transaction *tran)
+static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm,
+ uint64_t shared, Transaction *tran)
{
- if (!c->has_backup_perm) {
- c->has_backup_perm = true;
- c->backup_perm = c->perm;
- c->backup_shared_perm = c->shared_perm;
- }
- /*
- * Note: it's OK if c->has_backup_perm was already set, as we can find the
- * same c twice during check_perm procedure
- */
+ BdrvChildSetPermState *s = g_new(BdrvChildSetPermState, 1);
+
+ *s = (BdrvChildSetPermState) {
+ .child = c,
+ .old_perm = c->perm,
+ .old_shared_perm = c->shared_perm,
+ };
c->perm = perm;
c->shared_perm = shared;
- if (tran) {
- tran_add(tran, &bdrv_child_set_pem_drv, c);
- }
+ tran_add(tran, &bdrv_child_set_pem_drv, s);
}
static void bdrv_drv_set_perm_commit(void *opaque)
};
/*
- * bdrv_replace_child_safe
+ * bdrv_replace_child
*
* Note: real unref of old_bs is done only on commit.
*/
-static void bdrv_replace_child_safe(BdrvChild *child, BlockDriverState *new_bs,
- Transaction *tran)
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
+ Transaction *tran)
{
BdrvReplaceChildState *s = g_new(BdrvReplaceChildState, 1);
*s = (BdrvReplaceChildState) {
}
/*
- * Check whether permissions on this node can be changed in a way that
- * @cumulative_perms and @cumulative_shared_perms are the new cumulative
- * permissions of all its parents. This involves checking whether all necessary
- * permission changes to child nodes can be performed.
- *
- * A call to this function must always be followed by a call to bdrv_set_perm()
- * or bdrv_abort_perm_update().
+ * Refresh permissions in @bs subtree. The function is intended to be called
+ * after some graph modification that was done without permission update.
*/
-static int bdrv_node_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t cumulative_perms,
- uint64_t cumulative_shared_perms,
- Transaction *tran, Error **errp)
+static int bdrv_node_refresh_perm(BlockDriverState *bs, BlockReopenQueue *q,
+ Transaction *tran, Error **errp)
{
BlockDriver *drv = bs->drv;
BdrvChild *c;
int ret;
+ uint64_t cumulative_perms, cumulative_shared_perms;
+
+ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
/* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
if (!bdrv_is_writable_after_reopen(bs, NULL)) {
error_setg(errp, "Block node is read-only");
} else {
- uint64_t current_perms, current_shared;
- bdrv_get_cumulative_perm(bs, ¤t_perms, ¤t_shared);
- if (current_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) {
- error_setg(errp, "Cannot make block node read-only, there is "
- "a writer on it");
- } else {
- error_setg(errp, "Cannot make block node read-only and create "
- "a writer on it");
- }
+ error_setg(errp, "Read-only block node '%s' cannot support "
+ "read-write users", bdrv_get_node_name(bs));
}
return -EPERM;
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, tran);
+ bdrv_child_set_perm(c, cur_perm, cur_shared, tran);
}
return 0;
}
-/*
- * If use_cumulative_perms is true, use cumulative_perms and
- * cumulative_shared_perms for first element of the list. Otherwise just refresh
- * all permissions.
- */
-static int bdrv_check_perm_common(GSList *list, BlockReopenQueue *q,
- bool use_cumulative_perms,
- uint64_t cumulative_perms,
- uint64_t cumulative_shared_perms,
- Transaction *tran, Error **errp)
+static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
+ Transaction *tran, Error **errp)
{
int ret;
BlockDriverState *bs;
- if (use_cumulative_perms) {
- bs = list->data;
-
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
- cumulative_shared_perms,
- tran, errp);
- if (ret < 0) {
- return ret;
- }
-
- list = list->next;
- }
-
for ( ; list; list = list->next) {
bs = list->data;
return -EINVAL;
}
- bdrv_get_cumulative_perm(bs, &cumulative_perms,
- &cumulative_shared_perms);
-
- ret = bdrv_node_check_perm(bs, q, cumulative_perms,
- cumulative_shared_perms,
- tran, errp);
+ ret = bdrv_node_refresh_perm(bs, q, tran, errp);
if (ret < 0) {
return ret;
}
return 0;
}
-static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t cumulative_perms,
- uint64_t cumulative_shared_perms, Error **errp)
-{
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
- return bdrv_check_perm_common(list, q, true, cumulative_perms,
- cumulative_shared_perms, NULL, errp);
-}
-
-static int bdrv_list_refresh_perms(GSList *list, BlockReopenQueue *q,
- Transaction *tran, Error **errp)
-{
- return bdrv_check_perm_common(list, q, false, 0, 0, tran, errp);
-}
-
-/*
- * 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.
- */
-static void bdrv_node_abort_perm_update(BlockDriverState *bs)
-{
- BlockDriver *drv = bs->drv;
- BdrvChild *c;
-
- if (!drv) {
- return;
- }
-
- bdrv_drv_set_perm_abort(bs);
-
- QLIST_FOREACH(c, &bs->children, next) {
- bdrv_child_set_perm_abort(c);
- }
-}
-
-static void bdrv_list_abort_perm_update(GSList *list)
-{
- for ( ; list; list = list->next) {
- bdrv_node_abort_perm_update((BlockDriverState *)list->data);
- }
-}
-
-__attribute__((unused))
-static void bdrv_abort_perm_update(BlockDriverState *bs)
-{
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
- return bdrv_list_abort_perm_update(list);
-}
-
-static void bdrv_node_set_perm(BlockDriverState *bs)
-{
- BlockDriver *drv = bs->drv;
- BdrvChild *c;
-
- if (!drv) {
- return;
- }
-
- bdrv_drv_set_perm_commit(bs);
-
- /* Drivers that never have children can omit .bdrv_child_perm() */
- if (!drv->bdrv_child_perm) {
- assert(QLIST_EMPTY(&bs->children));
- return;
- }
-
- /* Update all children */
- QLIST_FOREACH(c, &bs->children, next) {
- bdrv_child_set_perm_commit(c);
- }
-}
-
-static void bdrv_list_set_perm(GSList *list)
-{
- for ( ; list; list = list->next) {
- bdrv_node_set_perm((BlockDriverState *)list->data);
- }
-}
-
-static void bdrv_set_perm(BlockDriverState *bs)
-{
- g_autoptr(GSList) list = bdrv_topological_dfs(NULL, NULL, bs);
- return bdrv_list_set_perm(list);
-}
-
void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm,
uint64_t *shared_perm)
{
return g_string_free(result, FALSE);
}
-/*
- * 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_children is
- * set, the BdrvChild objects in this list are ignored in the calculations;
- * this allows checking permission updates for an existing reference.
- *
- * Needs to be followed by a call to either bdrv_set_perm() or
- * bdrv_abort_perm_update(). */
-__attribute__((unused))
-static int bdrv_check_update_perm(BlockDriverState *bs, BlockReopenQueue *q,
- uint64_t new_used_perm,
- uint64_t new_shared_perm,
- Error **errp)
-{
- BdrvChild *c;
- uint64_t cumulative_perms = new_used_perm;
- uint64_t cumulative_shared_perms = new_shared_perm;
-
-
- /* There is no reason why anyone couldn't tolerate write_unchanged */
- assert(new_shared_perm & BLK_PERM_WRITE_UNCHANGED);
-
- QLIST_FOREACH(c, &bs->parents, next_parent) {
- 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;
- }
-
- cumulative_perms |= c->perm;
- cumulative_shared_perms &= c->shared_perm;
- }
-
- return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
- errp);
-}
static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
{
Transaction *tran = tran_new();
int ret;
- bdrv_child_set_perm_safe(c, perm, shared, tran);
+ bdrv_child_set_perm(c, perm, shared, tran);
ret = bdrv_refresh_perms(c->bs, &local_err);
}
}
-/*
- * Updates @child to change its reference to point to @new_bs, including
- * checking and applying the necessary permission updates both to the old node
- * and to @new_bs.
- *
- * NULL is passed as @new_bs for removing the reference before freeing @child.
- *
- * If @new_bs is not NULL, bdrv_check_perm() must be called beforehand, as this
- * function uses bdrv_set_perm() to update the permissions according to the new
- * reference that @new_bs gets.
- *
- * Callers must ensure that child->frozen is false.
- */
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
-{
- BlockDriverState *old_bs = child->bs;
-
- /* Asserts that child->frozen == false */
- bdrv_replace_child_noperm(child, new_bs);
-
- /*
- * Start with the new node's permissions. If @new_bs is a (direct
- * or indirect) child of @old_bs, we must complete the permission
- * update on @new_bs before we loosen the restrictions on @old_bs.
- * Otherwise, bdrv_check_perm() on @old_bs would re-initiate
- * updating the permissions of @new_bs, and thus not purely loosen
- * restrictions.
- */
- if (new_bs) {
- bdrv_set_perm(new_bs);
- }
-
- if (old_bs) {
- /*
- * Update permissions for old node. We're just taking a parent away, so
- * we're loosening restrictions. Errors of permission update are not
- * fatal in this case, ignore them.
- */
- bdrv_refresh_perms(old_bs, NULL);
-
- /* When the parent requiring a non-default AioContext is removed, the
- * node moves back to the main AioContext */
- bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
- }
-}
-
static void bdrv_child_free(void *opaque)
{
BdrvChild *c = opaque;
static void bdrv_detach_child(BdrvChild *child)
{
- bdrv_replace_child(child, NULL);
+ BlockDriverState *old_bs = child->bs;
+
+ bdrv_replace_child_noperm(child, NULL);
bdrv_remove_empty_child(child);
+
+ if (old_bs) {
+ /*
+ * Update permissions for old node. We're just taking a parent away, so
+ * we're loosening restrictions. Errors of permission update are not
+ * fatal in this case, ignore them.
+ */
+ bdrv_refresh_perms(old_bs, NULL);
+
+ /*
+ * When the parent requiring a non-default AioContext is removed, the
+ * node moves back to the main AioContext
+ */
+ bdrv_try_set_aio_context(old_bs, qemu_get_aio_context(), NULL);
+ }
}
/*
child_role, perm, shared_perm, opaque,
&child, tran, errp);
if (ret < 0) {
- bdrv_unref(child_bs);
- return NULL;
+ assert(child == NULL);
+ goto out;
}
ret = bdrv_refresh_perms(child_bs, errp);
- tran_finalize(tran, ret);
+out:
+ tran_finalize(tran, ret);
bdrv_unref(child_bs);
return child;
}
ret = bdrv_flush(bs_entry->state.bs);
if (ret < 0) {
error_setg_errno(errp, -ret, "Error flushing drive");
- goto cleanup;
+ goto abort;
}
}
}
if (child->bs) {
- bdrv_replace_child_safe(child, NULL, tran);
+ bdrv_replace_child(child, NULL, tran);
}
s = g_new(BdrvRemoveFilterOrCowChild, 1);
c->name, from->node_name);
return -EPERM;
}
- bdrv_replace_child_safe(c, to, tran);
+ bdrv_replace_child(c, to, tran);
}
return 0;