ret = strstart(filename, "json:", &filename);
assert(ret);
- options_obj = qobject_from_json(filename);
+ options_obj = qobject_from_json(filename, errp);
if (!options_obj) {
- error_setg(errp, "Could not parse the JSON options");
+ /* Work around qobject_from_json() lossage TODO fix that */
+ if (errp && !*errp) {
+ error_setg(errp, "Could not parse the JSON options");
+ return NULL;
+ }
+ error_prepend(errp, "Could not parse the JSON options: ");
return NULL;
}
return 0;
}
+static int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+ GSList *ignore_children, 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);
+
/*
* Check whether permissions on this node can be changed in a way that
* @cumulative_perms and @cumulative_shared_perms are the new cumulative
/* Needs to be followed by a call to either bdrv_child_set_perm() or
* bdrv_child_abort_perm_update(). */
-int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
- GSList *ignore_children, Error **errp)
+static int bdrv_child_check_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
+ GSList *ignore_children, Error **errp)
{
int ret;
return ret;
}
-void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
+static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
{
uint64_t cumulative_perms, cumulative_shared_perms;
bdrv_set_perm(c->bs, cumulative_perms, cumulative_shared_perms);
}
-void bdrv_child_abort_perm_update(BdrvChild *c)
+static void bdrv_child_abort_perm_update(BdrvChild *c)
{
bdrv_abort_perm_update(c->bs);
}
}
}
-static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs,
- bool check_new_perm)
+/*
+ * Updates @child to change its reference to point to @new_bs, including
+ * checking and applying the necessary permisson 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.
+ */
+static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs)
{
BlockDriverState *old_bs = child->bs;
uint64_t perm, shared_perm;
if (new_bs) {
bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm);
- if (check_new_perm) {
- bdrv_check_perm(new_bs, perm, shared_perm, NULL, &error_abort);
- }
bdrv_set_perm(new_bs, perm, shared_perm);
}
}
};
/* This performs the matching bdrv_set_perm() for the above check. */
- bdrv_replace_child(child, child_bs, false);
+ bdrv_replace_child(child, child_bs);
return child;
}
child->next.le_prev = NULL;
}
- bdrv_replace_child(child, NULL, false);
+ bdrv_replace_child(child, NULL);
g_free(child->name);
g_free(child);
bdrv_unref(backing_hd);
}
+ bdrv_refresh_filename(bs);
+
out:
bdrv_refresh_limits(bs, NULL);
}
bdrv_set_backing_hd(bs, backing_hd, &local_err);
bdrv_unref(backing_hd);
if (local_err) {
+ error_propagate(errp, local_err);
ret = -EINVAL;
goto free_exit;
}
return true;
}
-static void change_parent_backing_link(BlockDriverState *from,
- BlockDriverState *to)
+void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
+ Error **errp)
{
BdrvChild *c, *next;
+ GSList *list = NULL, *p;
+ uint64_t old_perm, old_shared;
+ uint64_t perm = 0, shared = BLK_PERM_ALL;
+ int ret;
+
+ assert(!atomic_read(&from->in_flight));
+ assert(!atomic_read(&to->in_flight));
+ /* Make sure that @from doesn't go away until we have successfully attached
+ * all of its parents to @to. */
+ bdrv_ref(from);
+
+ /* Put all parents into @list and calculate their cumulative permissions */
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
if (!should_update_child(c, to)) {
continue;
}
+ list = g_slist_prepend(list, c);
+ perm |= c->perm;
+ shared &= c->shared_perm;
+ }
+
+ /* 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, perm, shared, list, errp);
+ if (ret < 0) {
+ bdrv_abort_perm_update(to);
+ goto out;
+ }
+
+ /* Now actually perform the change. We performed the permission check for
+ * all elements of @list at once, so set the permissions all at once at the
+ * very end. */
+ for (p = list; p != NULL; p = p->next) {
+ c = p->data;
bdrv_ref(to);
- /* FIXME Are we sure that bdrv_replace_child() can't run into
- * &error_abort because of permissions? */
- bdrv_replace_child(c, to, true);
+ bdrv_replace_child_noperm(c, to);
bdrv_unref(from);
}
+
+ bdrv_get_cumulative_perm(to, &old_perm, &old_shared);
+ bdrv_set_perm(to, old_perm | perm, old_shared | shared);
+
+out:
+ g_slist_free(list);
+ bdrv_unref(from);
}
/*
{
Error *local_err = NULL;
- assert(!atomic_read(&bs_top->in_flight));
- assert(!atomic_read(&bs_new->in_flight));
-
bdrv_set_backing_hd(bs_new, bs_top, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
- change_parent_backing_link(bs_top, bs_new);
+ bdrv_replace_node(bs_top, bs_new, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ bdrv_set_backing_hd(bs_new, NULL, &error_abort);
+ goto out;
+ }
/* bs_new is now referenced by its new parents, we don't need the
* additional reference any more. */
bdrv_unref(bs_new);
}
-void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
-{
- assert(!bdrv_requests_pending(old));
- assert(!bdrv_requests_pending(new));
-
- bdrv_ref(old);
-
- change_parent_backing_link(old, new);
-
- bdrv_unref(old);
-}
-
static void bdrv_delete(BlockDriverState *bs)
{
assert(!bs->job);
void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
{
+ AioContext *ctx;
+
bdrv_drain(bs); /* ensure there are no in-flight requests */
+ ctx = bdrv_get_aio_context(bs);
+ while (aio_poll(ctx, false)) {
+ /* wait for all bottom halves to execute */
+ }
+
bdrv_detach_aio_context(bs);
/* This function executes in the old AioContext so acquire the new one in