#include "block/trace.h"
#include "block/block_int.h"
#include "block/blockjob.h"
+#include "block/fuse.h"
#include "block/nbd.h"
#include "block/qdict.h"
#include "qemu/error-report.h"
-#include "module_block.h"
+#include "block/module_block.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "qemu/timer.h"
#include "qemu/cutils.h"
#include "qemu/id.h"
+#include "block/coroutines.h"
#ifdef CONFIG_BSD
#include <sys/ioctl.h>
/* Stripping the explicit protocol prefix may result in a protocol
* prefix being (wrongly) detected (if the filename contains a colon) */
if (path_has_protocol(filename)) {
- QString *fat_filename;
+ GString *fat_filename;
/* This means there is some colon before the first slash; therefore,
* this cannot be an absolute path */
/* And we can thus fix the protocol detection issue by prefixing it
* by "./" */
- fat_filename = qstring_from_str("./");
- qstring_append(fat_filename, filename);
+ fat_filename = g_string_new("./");
+ g_string_append(fat_filename, filename);
- assert(!path_has_protocol(qstring_get_str(fat_filename)));
+ assert(!path_has_protocol(fat_filename->str));
- qdict_put(options, "filename", fat_filename);
+ qdict_put(options, "filename",
+ qstring_from_gstring(fat_filename));
} else {
/* If no protocol prefix was detected, we can use the shortened
* filename as-is */
{
static const char *whitelist_rw[] = {
CONFIG_BDRV_RW_WHITELIST
+ NULL
};
static const char *whitelist_ro[] = {
CONFIG_BDRV_RO_WHITELIST
+ NULL
};
const char **p;
int bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
{
BlockDriver *drv = bs->drv;
+ BlockDriverState *filtered = bdrv_filter_bs(bs);
if (drv && drv->bdrv_probe_blocksizes) {
return drv->bdrv_probe_blocksizes(bs, bsz);
- } else if (drv && drv->is_filter && bs->file) {
- return bdrv_probe_blocksizes(bs->file->bs, bsz);
+ } else if (filtered) {
+ return bdrv_probe_blocksizes(filtered, bsz);
}
return -ENOTSUP;
int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
{
BlockDriver *drv = bs->drv;
+ BlockDriverState *filtered = bdrv_filter_bs(bs);
if (drv && drv->bdrv_probe_geometry) {
return drv->bdrv_probe_geometry(bs, geo);
- } else if (drv && drv->is_filter && bs->file) {
- return bdrv_probe_geometry(bs->file->bs, geo);
+ } else if (filtered) {
+ return bdrv_probe_geometry(filtered, geo);
}
return -ENOTSUP;
}
bs->total_sectors = hint;
+
+ if (bs->total_sectors * BDRV_SECTOR_SIZE > BDRV_MAX_LENGTH) {
+ return -EFBIG;
+ }
+
return 0;
}
bdrv_refresh_filename(backing_hd);
parent->open_flags &= ~BDRV_O_NO_BACKING;
- pstrcpy(parent->backing_file, sizeof(parent->backing_file),
- backing_hd->filename);
- pstrcpy(parent->backing_format, sizeof(parent->backing_format),
- backing_hd->drv ? backing_hd->drv->format_name : "");
bdrv_op_block_all(backing_hd, parent->backing_blocker);
/* Otherwise we won't be able to commit or stream */
}
/* bdrv_new() and bdrv_close() make it so */
- assert(atomic_read(&bs->copy_on_read) == 0);
+ assert(qatomic_read(&bs->copy_on_read) == 0);
if (bs->open_flags & BDRV_O_COPY_ON_READ) {
if (!bs->read_only) {
static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q,
uint64_t perm, uint64_t shared,
- GSList *ignore_children,
- bool *tighten_restrictions, Error **errp);
+ 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);
+static void bdrv_child_set_perm(BdrvChild *c);
typedef struct BlockReopenQueueEntry {
bool prepared;
* 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,
- bool *tighten_restrictions, Error **errp)
+ GSList *ignore_children, 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))
}
if (drv->bdrv_check_perm) {
- return drv->bdrv_check_perm(bs, cumulative_perms,
- cumulative_shared_perms, errp);
+ ret = drv->bdrv_check_perm(bs, cumulative_perms,
+ cumulative_shared_perms, errp);
+ if (ret < 0) {
+ return ret;
+ }
}
/* Drivers that never have children can omit .bdrv_child_perm() */
/* 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,
- tighten_restrictions ? &child_tighten_restr
- : NULL,
errp);
- if (tighten_restrictions) {
- *tighten_restrictions |= child_tighten_restr;
- }
if (ret < 0) {
return ret;
}
}
}
-static void bdrv_set_perm(BlockDriverState *bs, uint64_t cumulative_perms,
- uint64_t cumulative_shared_perms)
+static void bdrv_set_perm(BlockDriverState *bs)
{
+ uint64_t cumulative_perms, cumulative_shared_perms;
BlockDriver *drv = bs->drv;
BdrvChild *c;
return;
}
+ bdrv_get_cumulative_perm(bs, &cumulative_perms, &cumulative_shared_perms);
+
/* Update this node */
if (drv->bdrv_set_perm) {
drv->bdrv_set_perm(bs, cumulative_perms, cumulative_shared_perms);
/* Update all children */
QLIST_FOREACH(c, &bs->children, next) {
- uint64_t cur_perm, cur_shared;
- bdrv_child_perm(bs, c->bs, c, c->role, NULL,
- cumulative_perms, cumulative_shared_perms,
- &cur_perm, &cur_shared);
- bdrv_child_set_perm(c, cur_perm, cur_shared);
+ bdrv_child_set_perm(c);
}
}
* 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,
- 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);
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));
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));
}
return bdrv_check_perm(bs, q, cumulative_perms, cumulative_shared_perms,
- ignore_children, tighten_restrictions, errp);
+ ignore_children, 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,
- bool *tighten_restrictions, Error **errp)
+ GSList *ignore_children, 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,
- tighten_restrictions, errp);
+ ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp);
g_slist_free(ignore_children);
if (ret < 0) {
return 0;
}
-static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared)
+static void bdrv_child_set_perm(BdrvChild *c)
{
- uint64_t cumulative_perms, cumulative_shared_perms;
-
c->has_backup_perm = false;
- c->perm = perm;
- c->shared_perm = shared;
-
- bdrv_get_cumulative_perm(c->bs, &cumulative_perms,
- &cumulative_shared_perms);
- bdrv_set_perm(c->bs, cumulative_perms, cumulative_shared_perms);
+ bdrv_set_perm(c->bs);
}
static void bdrv_child_abort_perm_update(BdrvChild *c)
bdrv_abort_perm_update(c->bs);
}
+static int bdrv_refresh_perms(BlockDriverState *bs, Error **errp)
+{
+ int ret;
+ uint64_t perm, shared_perm;
+
+ bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
+ ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, errp);
+ if (ret < 0) {
+ bdrv_abort_perm_update(bs);
+ return ret;
+ }
+ bdrv_set_perm(bs);
+
+ return 0;
+}
+
int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
Error **errp)
{
Error *local_err = NULL;
int ret;
- bool tighten_restrictions;
- ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL,
- &tighten_restrictions, &local_err);
+ ret = bdrv_child_check_perm(c, NULL, perm, shared, NULL, &local_err);
if (ret < 0) {
bdrv_child_abort_perm_update(c);
- if (tighten_restrictions) {
+ if ((perm & ~c->perm) || (c->shared_perm & ~shared)) {
+ /* tighten permissions */
error_propagate(errp, local_err);
} else {
/*
return ret;
}
- bdrv_child_set_perm(c, perm, shared);
+ bdrv_child_set_perm(c);
return 0;
}
/*
* Updates @child to change its reference to point to @new_bs, including
- * checking and applying the necessary permisson updates both to the old node
+ * 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;
- uint64_t perm, shared_perm;
+ /* Asserts that child->frozen == false */
bdrv_replace_child_noperm(child, new_bs);
/*
* restrictions.
*/
if (new_bs) {
- bdrv_get_cumulative_perm(new_bs, &perm, &shared_perm);
- bdrv_set_perm(new_bs, perm, shared_perm);
+ bdrv_set_perm(new_bs);
}
if (old_bs) {
- /* Update permissions for old node. This is guaranteed to succeed
- * because we're just taking a parent away, so we're loosening
- * restrictions. */
- bool tighten_restrictions;
- int ret;
-
- bdrv_get_cumulative_perm(old_bs, &perm, &shared_perm);
- ret = bdrv_check_perm(old_bs, NULL, perm, shared_perm, NULL,
- &tighten_restrictions, NULL);
- assert(tighten_restrictions == false);
- if (ret < 0) {
- /* We only tried to loosen restrictions, so errors are not fatal */
- bdrv_abort_perm_update(old_bs);
- } else {
- bdrv_set_perm(old_bs, perm, shared_perm);
- }
+ /*
+ * 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 */
Error *local_err = NULL;
int ret;
- ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, NULL,
- errp);
+ ret = bdrv_check_update_perm(child_bs, NULL, perm, shared_perm, NULL, errp);
if (ret < 0) {
bdrv_abort_perm_update(child_bs);
bdrv_unref(child_bs);
g_free(child);
}
+/* Callers must ensure that child->frozen is false. */
void bdrv_root_unref_child(BdrvChild *child)
{
BlockDriverState *child_bs;
}
}
+/* Callers must ensure that child->frozen is false. */
void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child)
{
if (child == NULL) {
}
/*
- * Sets the backing file link of a BDS. A new reference is created; callers
+ * Sets the bs->backing link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
*/
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
bdrv_inherits_from_recursive(backing_hd, bs);
- if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) {
+ if (bdrv_is_backing_chain_frozen(bs, child_bs(bs->backing), errp)) {
return;
}
}
if (bs->backing) {
+ /* Cannot be frozen, we checked that above */
bdrv_unref_child(bs, bs->backing);
bs->backing = NULL;
}
QTAILQ_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, NULL, errp);
+ state->shared_perm, NULL, errp);
if (ret < 0) {
goto cleanup_perm;
}
bs_queue, state->perm, state->shared_perm,
&nperm, &nshared);
ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
- nperm, nshared, NULL, NULL, errp);
+ nperm, nshared, NULL, errp);
if (ret < 0) {
goto cleanup_perm;
}
}
if (ret == 0) {
- bdrv_set_perm(state->bs, state->perm, state->shared_perm);
+ uint64_t perm, shared;
+
+ bdrv_get_cumulative_perm(state->bs, &perm, &shared);
+ assert(perm == state->perm);
+ assert(shared == state->shared_perm);
+
+ bdrv_set_perm(state->bs);
} else {
bdrv_abort_perm_update(state->bs);
if (state->replace_backing_bs && state->new_backing_bs) {
Error **errp)
{
BlockDriverState *bs = reopen_state->bs;
- BlockDriverState *overlay_bs, *new_backing_bs;
+ BlockDriverState *overlay_bs, *below_bs, *new_backing_bs;
QObject *value;
const char *str;
new_backing_bs = NULL;
break;
case QTYPE_QSTRING:
- str = qobject_get_try_str(value);
+ str = qstring_get_str(qobject_to(QString, value));
new_backing_bs = bdrv_lookup_bs(NULL, str, errp);
if (new_backing_bs == NULL) {
return -EINVAL;
}
}
+ /*
+ * Ensure that @bs can really handle backing files, because we are
+ * about to give it one (or swap the existing one)
+ */
+ if (bs->drv->is_filter) {
+ /* Filters always have a file or a backing child */
+ if (!bs->backing) {
+ error_setg(errp, "'%s' is a %s filter node that does not support a "
+ "backing child", bs->node_name, bs->drv->format_name);
+ return -EINVAL;
+ }
+ } else if (!bs->drv->supports_backing) {
+ error_setg(errp, "Driver '%s' of node '%s' does not support backing "
+ "files", bs->drv->format_name, bs->node_name);
+ return -EINVAL;
+ }
+
/*
* Find the "actual" backing file by skipping all links that point
* to an implicit node, if any (e.g. a commit filter node).
+ * We cannot use any of the bdrv_skip_*() functions here because
+ * those return the first explicit node, while we are looking for
+ * its overlay here.
*/
overlay_bs = bs;
- while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) {
- overlay_bs = backing_bs(overlay_bs);
+ for (below_bs = bdrv_filter_or_cow_bs(overlay_bs);
+ below_bs && below_bs->implicit;
+ below_bs = bdrv_filter_or_cow_bs(overlay_bs))
+ {
+ overlay_bs = below_bs;
}
/* If we want to replace the backing file we need some extra checks */
- if (new_backing_bs != backing_bs(overlay_bs)) {
+ if (new_backing_bs != bdrv_filter_or_cow_bs(overlay_bs)) {
/* Check for implicit nodes between bs and its backing file */
if (bs != overlay_bs) {
error_setg(errp, "Cannot change backing link if '%s' has "
"an implicit backing file", bs->node_name);
return -EPERM;
}
- /* Check if the backing link that we want to replace is frozen */
- if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs),
- errp)) {
+ /*
+ * Check if the backing link that we want to replace is frozen.
+ * Note that
+ * bdrv_filter_or_cow_child(overlay_bs) == overlay_bs->backing,
+ * because we know that overlay_bs == bs, and that @bs
+ * either is a filter that uses ->backing or a COW format BDS
+ * with bs->drv->supports_backing == true.
+ */
+ if (bdrv_is_backing_chain_frozen(overlay_bs,
+ child_bs(overlay_bs->backing), errp))
+ {
return -EPERM;
}
reopen_state->replace_backing_bs = true;
* its metadata. Otherwise the 'backing' option can be omitted.
*/
if (drv->supports_backing && reopen_state->backing_missing &&
- (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) {
+ (reopen_state->bs->backing || reopen_state->bs->backing_file[0])) {
error_setg(errp, "backing is missing for '%s'",
reopen_state->bs->node_name);
ret = -EINVAL;
}
if (child) {
- const char *str = qobject_get_try_str(new);
- if (!strcmp(child->bs->node_name, str)) {
+ if (!strcmp(child->bs->node_name,
+ qstring_get_str(qobject_to(QString, new)))) {
continue; /* Found child with this name, skip option */
}
}
* from bdrv_set_backing_hd()) has the new values.
*/
if (reopen_state->replace_backing_bs) {
- BlockDriverState *old_backing_bs = backing_bs(bs);
+ BlockDriverState *old_backing_bs = child_bs(bs->backing);
assert(!old_backing_bs || !old_backing_bs->implicit);
/* Abort the permission update on the backing bs we're detaching */
if (old_backing_bs) {
if (bs->drv) {
if (bs->drv->bdrv_close) {
+ /* Must unfreeze all children, so bdrv_unref_child() works */
bs->drv->bdrv_close(bs);
}
bs->drv = NULL;
bs->file = NULL;
g_free(bs->opaque);
bs->opaque = NULL;
- atomic_set(&bs->copy_on_read, 0);
+ qatomic_set(&bs->copy_on_read, 0);
bs->backing_file[0] = '\0';
bs->backing_format[0] = '\0';
bs->total_sectors = 0;
}
QLIST_INIT(&bs->aio_notifiers);
bdrv_drained_end(bs);
+
+ /*
+ * If we're still inside some bdrv_drain_all_begin()/end() sections, end
+ * them now since this BDS won't exist anymore when bdrv_drain_all_end()
+ * gets called.
+ */
+ if (bs->quiesce_counter) {
+ bdrv_drain_all_end_quiesce(bs);
+ }
}
void bdrv_close_all(void)
{
assert(job_next(NULL) == NULL);
- nbd_export_close_all();
+ blk_exp_close_all();
/* Drop references from requests still in flight, such as canceled block
* jobs whose AIO context has not been polled yet */
return ret;
}
-void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
- Error **errp)
+/*
+ * With auto_skip=true bdrv_replace_node_common skips updating from parents
+ * if it creates a parent-child relation loop or if parent is block-job.
+ *
+ * With auto_skip=false the error is returned if from has a parent which should
+ * not be updated.
+ */
+static void bdrv_replace_node_common(BlockDriverState *from,
+ BlockDriverState *to,
+ bool auto_skip, Error **errp)
{
BdrvChild *c, *next;
GSList *list = NULL, *p;
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
assert(c->bs == from);
if (!should_update_child(c, to)) {
- continue;
+ if (auto_skip) {
+ continue;
+ }
+ error_setg(errp, "Should not change '%s' link to '%s'",
+ c->name, from->node_name);
+ goto out;
}
if (c->frozen) {
error_setg(errp, "Cannot change '%s' link to '%s'",
/* 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, NULL, errp);
+ ret = bdrv_check_update_perm(to, NULL, perm, shared, list, errp);
if (ret < 0) {
bdrv_abort_perm_update(to);
goto out;
bdrv_unref(from);
}
- bdrv_get_cumulative_perm(to, &perm, &shared);
- bdrv_set_perm(to, perm, shared);
+ bdrv_set_perm(to);
out:
g_slist_free(list);
bdrv_unref(from);
}
+void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
+ Error **errp)
+{
+ return bdrv_replace_node_common(from, to, true, errp);
+}
+
/*
* Add new bs contents at the top of an image chain while the chain is
* live, while keeping required fields on the top layer.
* free of errors) or -errno when an internal error occurred. The results of the
* check are stored in res.
*/
-static int coroutine_fn bdrv_co_check(BlockDriverState *bs,
- BdrvCheckResult *res, BdrvCheckMode fix)
+int coroutine_fn bdrv_co_check(BlockDriverState *bs,
+ BdrvCheckResult *res, BdrvCheckMode fix)
{
if (bs->drv == NULL) {
return -ENOMEDIUM;
return bs->drv->bdrv_co_check(bs, res, fix);
}
-typedef struct CheckCo {
- BlockDriverState *bs;
- BdrvCheckResult *res;
- BdrvCheckMode fix;
- int ret;
-} CheckCo;
-
-static void coroutine_fn bdrv_check_co_entry(void *opaque)
-{
- CheckCo *cco = opaque;
- cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
- aio_wait_kick();
-}
-
-int bdrv_check(BlockDriverState *bs,
- BdrvCheckResult *res, BdrvCheckMode fix)
-{
- Coroutine *co;
- CheckCo cco = {
- .bs = bs,
- .res = res,
- .ret = -EINPROGRESS,
- .fix = fix,
- };
-
- if (qemu_in_coroutine()) {
- /* Fast-path if already in coroutine context */
- bdrv_check_co_entry(&cco);
- } else {
- co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
- bdrv_coroutine_enter(bs, co);
- BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
- }
-
- return cco.ret;
-}
-
/*
* Return values:
* 0 - success
}
/*
- * Finds the image layer in the chain that has 'bs' as its backing file.
- *
- * active is the current topmost image.
+ * Finds the first non-filter node above bs in the chain between
+ * active and bs. The returned node is either an immediate parent of
+ * bs, or there are only filter nodes between the two.
*
* Returns NULL if bs is not found in active's image chain,
* or if active == bs.
BlockDriverState *bdrv_find_overlay(BlockDriverState *active,
BlockDriverState *bs)
{
- while (active && bs != backing_bs(active)) {
- active = backing_bs(active);
+ bs = bdrv_skip_filters(bs);
+ active = bdrv_skip_filters(active);
+
+ while (active) {
+ BlockDriverState *next = bdrv_backing_chain_next(active);
+ if (bs == next) {
+ return active;
+ }
+ active = next;
}
- return active;
+ return NULL;
}
/* Given a BDS, searches for the base layer. */
}
/*
- * Return true if at least one of the backing links between @bs and
- * @base is frozen. @errp is set if that's the case.
+ * Return true if at least one of the COW (backing) and filter links
+ * between @bs and @base is frozen. @errp is set if that's the case.
* @base must be reachable from @bs, or NULL.
*/
bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base,
Error **errp)
{
BlockDriverState *i;
+ BdrvChild *child;
+
+ for (i = bs; i != base; i = child_bs(child)) {
+ child = bdrv_filter_or_cow_child(i);
- for (i = bs; i != base; i = backing_bs(i)) {
- if (i->backing && i->backing->frozen) {
+ if (child && child->frozen) {
error_setg(errp, "Cannot change '%s' link from '%s' to '%s'",
- i->backing->name, i->node_name,
- backing_bs(i)->node_name);
+ child->name, i->node_name, child->bs->node_name);
return true;
}
}
}
/*
- * Freeze all backing links between @bs and @base.
+ * Freeze all COW (backing) and filter links between @bs and @base.
* If any of the links is already frozen the operation is aborted and
* none of the links are modified.
* @base must be reachable from @bs, or NULL.
Error **errp)
{
BlockDriverState *i;
+ BdrvChild *child;
if (bdrv_is_backing_chain_frozen(bs, base, errp)) {
return -EPERM;
}
- for (i = bs; i != base; i = backing_bs(i)) {
- if (i->backing && backing_bs(i)->never_freeze) {
+ for (i = bs; i != base; i = child_bs(child)) {
+ child = bdrv_filter_or_cow_child(i);
+ if (child && child->bs->never_freeze) {
error_setg(errp, "Cannot freeze '%s' link to '%s'",
- i->backing->name, backing_bs(i)->node_name);
+ child->name, child->bs->node_name);
return -EPERM;
}
}
- for (i = bs; i != base; i = backing_bs(i)) {
- if (i->backing) {
- i->backing->frozen = true;
+ for (i = bs; i != base; i = child_bs(child)) {
+ child = bdrv_filter_or_cow_child(i);
+ if (child) {
+ child->frozen = true;
}
}
}
/*
- * Unfreeze all backing links between @bs and @base. The caller must
- * ensure that all links are frozen before using this function.
+ * Unfreeze all COW (backing) and filter links between @bs and @base.
+ * The caller must ensure that all links are frozen before using this
+ * function.
* @base must be reachable from @bs, or NULL.
*/
void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base)
{
BlockDriverState *i;
+ BdrvChild *child;
- for (i = bs; i != base; i = backing_bs(i)) {
- if (i->backing) {
- assert(i->backing->frozen);
- i->backing->frozen = false;
+ for (i = bs; i != base; i = child_bs(child)) {
+ child = bdrv_filter_or_cow_child(i);
+ if (child) {
+ assert(child->frozen);
+ child->frozen = false;
}
}
}
{
BlockDriverState *explicit_top = top;
bool update_inherits_from;
- BdrvChild *c, *next;
+ BdrvChild *c;
Error *local_err = NULL;
int ret = -EIO;
+ g_autoptr(GSList) updated_children = NULL;
+ GSList *p;
bdrv_ref(top);
bdrv_subtree_drained_begin(top);
goto exit;
}
- /* This function changes all links that point to top and makes
- * them point to base. Check that none of them is frozen. */
- QLIST_FOREACH(c, &top->parents, next_parent) {
- if (c->frozen) {
- goto exit;
- }
- }
-
/* If 'base' recursively inherits from 'top' then we should set
* base->inherits_from to top->inherits_from after 'top' and all
* other intermediate nodes have been dropped.
* If 'top' is an implicit node (e.g. "commit_top") we should skip
* it because no one inherits from it. We use explicit_top for that. */
- while (explicit_top && explicit_top->implicit) {
- explicit_top = backing_bs(explicit_top);
- }
+ explicit_top = bdrv_skip_implicit_filters(explicit_top);
update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
/* success - we can delete the intermediate states, and link top->base */
backing_file_str = base->filename;
}
- QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) {
- /* 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, NULL, &local_err);
- g_slist_free(ignore_children);
- if (ret < 0) {
- error_report_err(local_err);
- goto exit;
- }
+ QLIST_FOREACH(c, &top->parents, next_parent) {
+ updated_children = g_slist_prepend(updated_children, c);
+ }
+
+ bdrv_replace_node_common(top, base, false, &local_err);
+ if (local_err) {
+ error_report_err(local_err);
+ goto exit;
+ }
+
+ for (p = updated_children; p; p = p->next) {
+ c = p->data;
- /* If so, update the backing file path in the image file */
if (c->klass->update_filename) {
ret = c->klass->update_filename(c, base, backing_file_str,
&local_err);
if (ret < 0) {
- bdrv_abort_perm_update(base);
+ /*
+ * TODO: Actually, we want to rollback all previous iterations
+ * of this loop, and (which is almost impossible) previous
+ * bdrv_replace_node()...
+ *
+ * Note, that c->klass->update_filename may lead to permission
+ * update, so it's a bad idea to call it inside permission
+ * update transaction of bdrv_replace_node.
+ */
error_report_err(local_err);
goto exit;
}
}
-
- /* Do the actual switch in the in-memory graph.
- * Completes bdrv_check_update_perm() transaction internally. */
- bdrv_ref(base);
- bdrv_replace_child(c, base);
- bdrv_unref(top);
}
if (update_inherits_from) {
return ret;
}
+/**
+ * Implementation of BlockDriver.bdrv_get_allocated_file_size() that
+ * sums the size of all data-bearing children. (This excludes backing
+ * children.)
+ */
+static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs)
+{
+ BdrvChild *child;
+ int64_t child_size, sum = 0;
+
+ QLIST_FOREACH(child, &bs->children, next) {
+ if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA |
+ BDRV_CHILD_FILTERED))
+ {
+ child_size = bdrv_get_allocated_file_size(child->bs);
+ if (child_size < 0) {
+ return child_size;
+ }
+ sum += child_size;
+ }
+ }
+
+ return sum;
+}
+
/**
* Length of a allocated file in bytes. Sparse files are counted by actual
* allocated space. Return < 0 if error or unknown.
if (drv->bdrv_get_allocated_file_size) {
return drv->bdrv_get_allocated_file_size(bs);
}
- if (bs->file) {
- return bdrv_get_allocated_file_size(bs->file->bs);
+
+ if (drv->bdrv_file_open) {
+ /*
+ * Protocol drivers default to -ENOTSUP (most of their data is
+ * not stored in any of their children (if they even have any),
+ * so there is no generic way to figure it out).
+ */
+ return -ENOTSUP;
+ } else if (drv->is_filter) {
+ /* Filter drivers default to the size of their filtered child */
+ return bdrv_get_allocated_file_size(bdrv_filter_bs(bs));
+ } else {
+ /* Other drivers default to summing their children's sizes */
+ return bdrv_sum_allocated_file_size(bs);
}
- return -ENOTSUP;
}
/*
{
int64_t ret = bdrv_nb_sectors(bs);
- ret = ret > INT64_MAX / BDRV_SECTOR_SIZE ? -EFBIG : ret;
- return ret < 0 ? ret : ret * BDRV_SECTOR_SIZE;
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret > INT64_MAX / BDRV_SECTOR_SIZE) {
+ return -EFBIG;
+ }
+ return ret * BDRV_SECTOR_SIZE;
}
/* return 0 as number of sectors if no device present or error */
return bs->sg;
}
-bool bdrv_is_encrypted(BlockDriverState *bs)
+/**
+ * Return whether the given node supports compressed writes.
+ */
+bool bdrv_supports_compressed_writes(BlockDriverState *bs)
{
- if (bs->backing && bs->backing->bs->encrypted) {
- return true;
+ BlockDriverState *filtered;
+
+ if (!bs->drv || !block_driver_can_compress(bs->drv)) {
+ return false;
+ }
+
+ filtered = bdrv_filter_bs(bs);
+ if (filtered) {
+ /*
+ * Filters can only forward compressed writes, so we have to
+ * check the child.
+ */
+ return bdrv_supports_compressed_writes(filtered);
}
- return bs->encrypted;
+
+ return true;
}
const char *bdrv_get_format_name(BlockDriverState *bs)
BlockDeviceInfoList *bdrv_named_nodes_list(bool flat,
Error **errp)
{
- BlockDeviceInfoList *list, *entry;
+ BlockDeviceInfoList *list;
BlockDriverState *bs;
list = NULL;
qapi_free_BlockDeviceInfoList(list);
return NULL;
}
- entry = g_malloc0(sizeof(*entry));
- entry->value = info;
- entry->next = list;
- list = entry;
+ QAPI_LIST_PREPEND(list, info);
}
return list;
}
-#define QAPI_LIST_ADD(list, element) do { \
- typeof(list) _tmp = g_new(typeof(*(list)), 1); \
- _tmp->value = (element); \
- _tmp->next = (list); \
- (list) = _tmp; \
-} while (0)
-
typedef struct XDbgBlockGraphConstructor {
XDbgBlockGraph *graph;
GHashTable *graph_nodes;
n->type = type;
n->name = g_strdup(name);
- QAPI_LIST_ADD(gr->graph->nodes, n);
+ QAPI_LIST_PREPEND(gr->graph->nodes, n);
}
static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent,
uint64_t flag = bdrv_qapi_perm_to_blk_perm(qapi_perm);
if (flag & child->perm) {
- QAPI_LIST_ADD(edge->perm, qapi_perm);
+ QAPI_LIST_PREPEND(edge->perm, qapi_perm);
}
if (flag & child->shared_perm) {
- QAPI_LIST_ADD(edge->shared_perm, qapi_perm);
+ QAPI_LIST_PREPEND(edge->shared_perm, qapi_perm);
}
}
- QAPI_LIST_ADD(gr->graph->edges, edge);
+ QAPI_LIST_PREPEND(gr->graph->edges, edge);
}
bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base)
{
while (top && top != base) {
- top = backing_bs(top);
+ top = bdrv_filter_or_cow_bs(top);
}
return top != NULL;
int bdrv_has_zero_init(BlockDriverState *bs)
{
+ BlockDriverState *filtered;
+
if (!bs->drv) {
return 0;
}
/* If BS is a copy on write image, it is initialized to
the contents of the base image, which may not be zeroes. */
- if (bs->backing) {
+ if (bdrv_cow_child(bs)) {
return 0;
}
if (bs->drv->bdrv_has_zero_init) {
return bs->drv->bdrv_has_zero_init(bs);
}
- if (bs->file && bs->drv->is_filter) {
- return bdrv_has_zero_init(bs->file->bs);
+
+ filtered = bdrv_filter_bs(bs);
+ if (filtered) {
+ return bdrv_has_zero_init(filtered);
}
/* safe default */
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
+ int ret;
BlockDriver *drv = bs->drv;
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) {
return -ENOMEDIUM;
}
if (!drv->bdrv_get_info) {
- if (bs->file && drv->is_filter) {
- return bdrv_get_info(bs->file->bs, bdi);
+ BlockDriverState *filtered = bdrv_filter_bs(bs);
+ if (filtered) {
+ return bdrv_get_info(filtered, bdi);
}
return -ENOTSUP;
}
memset(bdi, 0, sizeof(*bdi));
- return drv->bdrv_get_info(bs, bdi);
+ ret = drv->bdrv_get_info(bs, bdi);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (bdi->cluster_size > BDRV_MAX_ALIGNMENT) {
+ return -EINVAL;
+ }
+
+ return 0;
}
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
{
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
- if (bs->file) {
- bs = bs->file->bs;
- continue;
- }
-
- if (bs->drv->is_filter && bs->backing) {
- bs = bs->backing->bs;
- continue;
- }
-
- break;
+ bs = bdrv_primary_bs(bs);
}
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
int bdrv_debug_resume(BlockDriverState *bs, const char *tag)
{
while (bs && (!bs->drv || !bs->drv->bdrv_debug_resume)) {
- bs = bs->file ? bs->file->bs : NULL;
+ bs = bdrv_primary_bs(bs);
}
if (bs && bs->drv && bs->drv->bdrv_debug_resume) {
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag)
{
while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) {
- bs = bs->file ? bs->file->bs : NULL;
+ bs = bdrv_primary_bs(bs);
}
if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) {
char *backing_file_full = NULL;
char *filename_tmp = NULL;
int is_protocol = 0;
+ bool filenames_refreshed = false;
BlockDriverState *curr_bs = NULL;
BlockDriverState *retval = NULL;
+ BlockDriverState *bs_below;
if (!bs || !bs->drv || !backing_file) {
return NULL;
is_protocol = path_has_protocol(backing_file);
- for (curr_bs = bs; curr_bs->backing; curr_bs = curr_bs->backing->bs) {
+ /*
+ * Being largely a legacy function, skip any filters here
+ * (because filters do not have normal filenames, so they cannot
+ * match anyway; and allowing json:{} filenames is a bit out of
+ * scope).
+ */
+ for (curr_bs = bdrv_skip_filters(bs);
+ bdrv_cow_child(curr_bs) != NULL;
+ curr_bs = bs_below)
+ {
+ bs_below = bdrv_backing_chain_next(curr_bs);
+
+ if (bdrv_backing_overridden(curr_bs)) {
+ /*
+ * If the backing file was overridden, we can only compare
+ * directly against the backing node's filename.
+ */
- /* If either of the filename paths is actually a protocol, then
- * compare unmodified paths; otherwise make paths relative */
- if (is_protocol || path_has_protocol(curr_bs->backing_file)) {
+ if (!filenames_refreshed) {
+ /*
+ * This will automatically refresh all of the
+ * filenames in the rest of the backing chain, so we
+ * only need to do this once.
+ */
+ bdrv_refresh_filename(bs_below);
+ filenames_refreshed = true;
+ }
+
+ if (strcmp(backing_file, bs_below->filename) == 0) {
+ retval = bs_below;
+ break;
+ }
+ } else if (is_protocol || path_has_protocol(curr_bs->backing_file)) {
+ /*
+ * If either of the filename paths is actually a protocol, then
+ * compare unmodified paths; otherwise make paths relative.
+ */
char *backing_file_full_ret;
if (strcmp(backing_file, curr_bs->backing_file) == 0) {
- retval = curr_bs->backing->bs;
+ retval = bs_below;
break;
}
/* Also check against the full backing filename for the image */
bool equal = strcmp(backing_file, backing_file_full_ret) == 0;
g_free(backing_file_full_ret);
if (equal) {
- retval = curr_bs->backing->bs;
+ retval = bs_below;
break;
}
}
g_free(filename_tmp);
if (strcmp(backing_file_full, filename_full) == 0) {
- retval = curr_bs->backing->bs;
+ retval = bs_below;
break;
}
}
bdrv_init();
}
-static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
- Error **errp)
+int coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp)
{
BdrvChild *child, *parent;
- uint64_t perm, shared_perm;
Error *local_err = NULL;
int ret;
BdrvDirtyBitmap *bm;
if (!bs->drv) {
- return;
+ return -ENOMEDIUM;
}
QLIST_FOREACH(child, &bs->children, next) {
bdrv_co_invalidate_cache(child->bs, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- return;
+ return -EINVAL;
}
}
*/
if (bs->open_flags & BDRV_O_INACTIVE) {
bs->open_flags &= ~BDRV_O_INACTIVE;
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, NULL, errp);
+ ret = bdrv_refresh_perms(bs, errp);
if (ret < 0) {
bs->open_flags |= BDRV_O_INACTIVE;
- return;
+ return ret;
}
- bdrv_set_perm(bs, perm, shared_perm);
if (bs->drv->bdrv_co_invalidate_cache) {
bs->drv->bdrv_co_invalidate_cache(bs, &local_err);
if (local_err) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
- return;
+ return -EINVAL;
}
}
if (ret < 0) {
bs->open_flags |= BDRV_O_INACTIVE;
error_setg_errno(errp, -ret, "Could not refresh total sector count");
- return;
+ return ret;
}
}
if (local_err) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
- return;
+ return -EINVAL;
}
}
}
-}
-
-typedef struct InvalidateCacheCo {
- BlockDriverState *bs;
- Error **errp;
- bool done;
-} InvalidateCacheCo;
-
-static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
-{
- InvalidateCacheCo *ico = opaque;
- bdrv_co_invalidate_cache(ico->bs, ico->errp);
- ico->done = true;
- aio_wait_kick();
-}
-
-void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
-{
- Coroutine *co;
- InvalidateCacheCo ico = {
- .bs = bs,
- .done = false,
- .errp = errp
- };
- if (qemu_in_coroutine()) {
- /* Fast-path if already in coroutine context */
- bdrv_invalidate_cache_co_entry(&ico);
- } else {
- co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
- bdrv_coroutine_enter(bs, co);
- BDRV_POLL_WHILE(bs, !ico.done);
- }
+ return 0;
}
void bdrv_invalidate_cache_all(Error **errp)
{
BlockDriverState *bs;
- Error *local_err = NULL;
BdrvNextIterator it;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs);
+ int ret;
aio_context_acquire(aio_context);
- bdrv_invalidate_cache(bs, &local_err);
+ ret = bdrv_invalidate_cache(bs, errp);
aio_context_release(aio_context);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (ret < 0) {
bdrv_next_cleanup(&it);
return;
}
static int bdrv_inactivate_recurse(BlockDriverState *bs)
{
BdrvChild *child, *parent;
- bool tighten_restrictions;
- uint64_t perm, shared_perm;
int ret;
if (!bs->drv) {
bs->open_flags |= BDRV_O_INACTIVE;
- /* Update permissions, they may differ for inactive nodes */
- bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
- ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL,
- &tighten_restrictions, NULL);
- assert(tighten_restrictions == false);
- if (ret < 0) {
- /* We only tried to loosen restrictions, so errors are not fatal */
- bdrv_abort_perm_update(bs);
- } else {
- bdrv_set_perm(bs, perm, shared_perm);
- }
-
+ /*
+ * Update permissions, they may differ for inactive nodes.
+ * We only tried to loosen restrictions, so errors are not fatal, ignore
+ * them.
+ */
+ bdrv_refresh_perms(bs, NULL);
/* Recursively inactivate children */
QLIST_FOREACH(child, &bs->children, next) {
"same filename as the backing file");
goto out;
}
+ if (backing_file[0] == '\0') {
+ error_setg(errp, "Expected backing file name, got empty string");
+ goto out;
+ }
}
backing_fmt = qemu_opt_get(opts, BLOCK_OPT_BACKING_FMT);
return bs ? bs->aio_context : qemu_get_aio_context();
}
+AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs)
+{
+ Coroutine *self = qemu_coroutine_self();
+ AioContext *old_ctx = qemu_coroutine_get_aio_context(self);
+ AioContext *new_ctx;
+
+ /*
+ * Increase bs->in_flight to ensure that this operation is completed before
+ * moving the node to a different AioContext. Read new_ctx only afterwards.
+ */
+ bdrv_inc_in_flight(bs);
+
+ new_ctx = bdrv_get_aio_context(bs);
+ aio_co_reschedule_self(new_ctx);
+ return old_ctx;
+}
+
+void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx)
+{
+ aio_co_reschedule_self(old_ctx);
+ bdrv_dec_in_flight(bs);
+}
+
+void coroutine_fn bdrv_co_lock(BlockDriverState *bs)
+{
+ AioContext *ctx = bdrv_get_aio_context(bs);
+
+ /* In the main thread, bs->aio_context won't change concurrently */
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
+
+ /*
+ * We're in coroutine context, so we already hold the lock of the main
+ * loop AioContext. Don't lock it twice to avoid deadlocks.
+ */
+ assert(qemu_in_coroutine());
+ if (ctx != qemu_get_aio_context()) {
+ aio_context_acquire(ctx);
+ }
+}
+
+void coroutine_fn bdrv_co_unlock(BlockDriverState *bs)
+{
+ AioContext *ctx = bdrv_get_aio_context(bs);
+
+ assert(qemu_in_coroutine());
+ if (ctx != qemu_get_aio_context()) {
+ aio_context_release(ctx);
+ }
+}
+
void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
{
aio_co_enter(bdrv_get_aio_context(bs), co);
bool bdrv_recurse_can_replace(BlockDriverState *bs,
BlockDriverState *to_replace)
{
+ BlockDriverState *filtered;
+
if (!bs || !bs->drv) {
return false;
}
}
/* For filters without an own implementation, we can recurse on our own */
- if (bs->drv->is_filter) {
- BdrvChild *child = bs->file ?: bs->backing;
- return bdrv_recurse_can_replace(child->bs, to_replace);
+ filtered = bdrv_filter_bs(bs);
+ if (filtered) {
+ return bdrv_recurse_can_replace(filtered, to_replace);
}
/* Safe default */
/* Note: This function may return false positives; it may return true
* even if opening the backing file specified by bs's image header
* would result in exactly bs->backing. */
-static bool bdrv_backing_overridden(BlockDriverState *bs)
+bool bdrv_backing_overridden(BlockDriverState *bs)
{
if (bs->backing) {
return strcmp(bs->auto_backing_file,
{
BlockDriver *drv = bs->drv;
BdrvChild *child;
+ BlockDriverState *primary_child_bs;
QDict *opts;
bool backing_overridden;
bool generate_json_filename; /* Whether our default implementation should
qobject_unref(bs->full_open_options);
bs->full_open_options = opts;
+ primary_child_bs = bdrv_primary_bs(bs);
+
if (drv->bdrv_refresh_filename) {
/* Obsolete information is of no use here, so drop the old file name
* information before refreshing it */
bs->exact_filename[0] = '\0';
drv->bdrv_refresh_filename(bs);
- } else if (bs->file) {
- /* Try to reconstruct valid information from the underlying file */
+ } else if (primary_child_bs) {
+ /*
+ * Try to reconstruct valid information from the underlying
+ * file -- this only works for format nodes (filter nodes
+ * cannot be probed and as such must be selected by the user
+ * either through an options dict, or through a special
+ * filename which the filter driver must construct in its
+ * .bdrv_refresh_filename() implementation).
+ */
bs->exact_filename[0] = '\0';
/*
* We can use the underlying file's filename if:
* - it has a filename,
+ * - the current BDS is not a filter,
* - the file is a protocol BDS, and
* - opening that file (as this BDS's format) will automatically create
* the BDS tree we have right now, that is:
* - no non-file child of this BDS has been overridden by the user
* Both of these conditions are represented by generate_json_filename.
*/
- if (bs->file->bs->exact_filename[0] &&
- bs->file->bs->drv->bdrv_file_open &&
- !generate_json_filename)
+ if (primary_child_bs->exact_filename[0] &&
+ primary_child_bs->drv->bdrv_file_open &&
+ !drv->is_filter && !generate_json_filename)
{
- strcpy(bs->exact_filename, bs->file->bs->exact_filename);
+ strcpy(bs->exact_filename, primary_child_bs->exact_filename);
}
}
if (bs->exact_filename[0]) {
pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
} else {
- QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
+ GString *json = qobject_to_json(QOBJECT(bs->full_open_options));
if (snprintf(bs->filename, sizeof(bs->filename), "json:%s",
- qstring_get_str(json)) >= sizeof(bs->filename)) {
+ json->str) >= sizeof(bs->filename)) {
/* Give user a hint if we truncated things. */
strcpy(bs->filename + sizeof(bs->filename) - 4, "...");
}
- qobject_unref(json);
+ g_string_free(json, true);
}
}
char *bdrv_dirname(BlockDriverState *bs, Error **errp)
{
BlockDriver *drv = bs->drv;
+ BlockDriverState *child_bs;
if (!drv) {
error_setg(errp, "Node '%s' is ejected", bs->node_name);
return drv->bdrv_dirname(bs, errp);
}
- if (bs->file) {
- return bdrv_dirname(bs->file->bs, errp);
+ child_bs = bdrv_primary_bs(bs);
+ if (child_bs) {
+ return bdrv_dirname(child_bs, errp);
}
bdrv_refresh_filename(bs);
return 0;
}
+
+/*
+ * Return the child that @bs acts as an overlay for, and from which data may be
+ * copied in COW or COR operations. Usually this is the backing file.
+ */
+BdrvChild *bdrv_cow_child(BlockDriverState *bs)
+{
+ if (!bs || !bs->drv) {
+ return NULL;
+ }
+
+ if (bs->drv->is_filter) {
+ return NULL;
+ }
+
+ if (!bs->backing) {
+ return NULL;
+ }
+
+ assert(bs->backing->role & BDRV_CHILD_COW);
+ return bs->backing;
+}
+
+/*
+ * If @bs acts as a filter for exactly one of its children, return
+ * that child.
+ */
+BdrvChild *bdrv_filter_child(BlockDriverState *bs)
+{
+ BdrvChild *c;
+
+ if (!bs || !bs->drv) {
+ return NULL;
+ }
+
+ if (!bs->drv->is_filter) {
+ return NULL;
+ }
+
+ /* Only one of @backing or @file may be used */
+ assert(!(bs->backing && bs->file));
+
+ c = bs->backing ?: bs->file;
+ if (!c) {
+ return NULL;
+ }
+
+ assert(c->role & BDRV_CHILD_FILTERED);
+ return c;
+}
+
+/*
+ * Return either the result of bdrv_cow_child() or bdrv_filter_child(),
+ * whichever is non-NULL.
+ *
+ * Return NULL if both are NULL.
+ */
+BdrvChild *bdrv_filter_or_cow_child(BlockDriverState *bs)
+{
+ BdrvChild *cow_child = bdrv_cow_child(bs);
+ BdrvChild *filter_child = bdrv_filter_child(bs);
+
+ /* Filter nodes cannot have COW backing files */
+ assert(!(cow_child && filter_child));
+
+ return cow_child ?: filter_child;
+}
+
+/*
+ * Return the primary child of this node: For filters, that is the
+ * filtered child. For other nodes, that is usually the child storing
+ * metadata.
+ * (A generally more helpful description is that this is (usually) the
+ * child that has the same filename as @bs.)
+ *
+ * Drivers do not necessarily have a primary child; for example quorum
+ * does not.
+ */
+BdrvChild *bdrv_primary_child(BlockDriverState *bs)
+{
+ BdrvChild *c, *found = NULL;
+
+ QLIST_FOREACH(c, &bs->children, next) {
+ if (c->role & BDRV_CHILD_PRIMARY) {
+ assert(!found);
+ found = c;
+ }
+ }
+
+ return found;
+}
+
+static BlockDriverState *bdrv_do_skip_filters(BlockDriverState *bs,
+ bool stop_on_explicit_filter)
+{
+ BdrvChild *c;
+
+ if (!bs) {
+ return NULL;
+ }
+
+ while (!(stop_on_explicit_filter && !bs->implicit)) {
+ c = bdrv_filter_child(bs);
+ if (!c) {
+ /*
+ * A filter that is embedded in a working block graph must
+ * have a child. Assert this here so this function does
+ * not return a filter node that is not expected by the
+ * caller.
+ */
+ assert(!bs->drv || !bs->drv->is_filter);
+ break;
+ }
+ bs = c->bs;
+ }
+ /*
+ * Note that this treats nodes with bs->drv == NULL as not being
+ * filters (bs->drv == NULL should be replaced by something else
+ * anyway).
+ * The advantage of this behavior is that this function will thus
+ * always return a non-NULL value (given a non-NULL @bs).
+ */
+
+ return bs;
+}
+
+/*
+ * Return the first BDS that has not been added implicitly or that
+ * does not have a filtered child down the chain starting from @bs
+ * (including @bs itself).
+ */
+BlockDriverState *bdrv_skip_implicit_filters(BlockDriverState *bs)
+{
+ return bdrv_do_skip_filters(bs, true);
+}
+
+/*
+ * Return the first BDS that does not have a filtered child down the
+ * chain starting from @bs (including @bs itself).
+ */
+BlockDriverState *bdrv_skip_filters(BlockDriverState *bs)
+{
+ return bdrv_do_skip_filters(bs, false);
+}
+
+/*
+ * For a backing chain, return the first non-filter backing image of
+ * the first non-filter image.
+ */
+BlockDriverState *bdrv_backing_chain_next(BlockDriverState *bs)
+{
+ return bdrv_skip_filters(bdrv_cow_bs(bdrv_skip_filters(bs)));
+}