BdrvChildRole child_role,
Error **errp);
-/* TODO: Remove when no longer needed */
-static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format,
- int *child_flags, QDict *child_options,
- int parent_flags, QDict *parent_options);
-static void bdrv_child_cb_attach(BdrvChild *child);
-static void bdrv_child_cb_detach(BdrvChild *child);
-
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
*child_flags &= ~BDRV_O_NATIVE_AIO;
}
-/*
- * Returns the options and flags that bs->file should get if a protocol driver
- * is expected, based on the given options and flags for the parent BDS
- */
-static void bdrv_protocol_options(BdrvChildRole role, bool parent_is_format,
- int *child_flags, QDict *child_options,
- int parent_flags, QDict *parent_options)
-{
- bdrv_inherited_options(BDRV_CHILD_IMAGE, true,
- child_flags, child_options,
- parent_flags, parent_options);
-}
-
-const BdrvChildClass child_file = {
- .parent_is_bds = true,
- .get_parent_desc = bdrv_child_get_parent_desc,
- .inherit_options = bdrv_protocol_options,
- .drained_begin = bdrv_child_cb_drained_begin,
- .drained_poll = bdrv_child_cb_drained_poll,
- .drained_end = bdrv_child_cb_drained_end,
- .attach = bdrv_child_cb_attach,
- .detach = bdrv_child_cb_detach,
- .inactivate = bdrv_child_cb_inactivate,
- .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
- .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
-};
-
-/*
- * Returns the options and flags that bs->file should get if the use of formats
- * (and not only protocols) is permitted for it, based on the given options and
- * flags for the parent BDS
- */
-static void bdrv_inherited_fmt_options(BdrvChildRole role,
- bool parent_is_format,
- int *child_flags, QDict *child_options,
- int parent_flags, QDict *parent_options)
-{
- bdrv_inherited_options(BDRV_CHILD_DATA, false,
- child_flags, child_options,
- parent_flags, parent_options);
-}
-
-const BdrvChildClass child_format = {
- .parent_is_bds = true,
- .get_parent_desc = bdrv_child_get_parent_desc,
- .inherit_options = bdrv_inherited_fmt_options,
- .drained_begin = bdrv_child_cb_drained_begin,
- .drained_poll = bdrv_child_cb_drained_poll,
- .drained_end = bdrv_child_cb_drained_end,
- .attach = bdrv_child_cb_attach,
- .detach = bdrv_child_cb_detach,
- .inactivate = bdrv_child_cb_inactivate,
- .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
- .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
-};
-
static void bdrv_backing_attach(BdrvChild *c)
{
BlockDriverState *parent = c->opaque;
parent->backing_blocker);
}
-/* XXX: Will be removed along with child_backing */
-static void bdrv_child_cb_attach_backing(BdrvChild *c)
-{
- if (!(c->role & BDRV_CHILD_COW)) {
- bdrv_backing_attach(c);
- }
- bdrv_child_cb_attach(c);
-}
-
static void bdrv_backing_detach(BdrvChild *c)
{
BlockDriverState *parent = c->opaque;
parent->backing_blocker = NULL;
}
-/* XXX: Will be removed along with child_backing */
-static void bdrv_child_cb_detach_backing(BdrvChild *c)
-{
- if (!(c->role & BDRV_CHILD_COW)) {
- bdrv_backing_detach(c);
- }
- bdrv_child_cb_detach(c);
-}
-
-/*
- * Returns the options and flags that bs->backing should get, based on the
- * given options and flags for the parent BDS
- */
-static void bdrv_backing_options(BdrvChildRole role, bool parent_is_format,
- int *child_flags, QDict *child_options,
- int parent_flags, QDict *parent_options)
-{
- bdrv_inherited_options(BDRV_CHILD_COW, true,
- child_flags, child_options,
- parent_flags, parent_options);
-}
-
static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
const char *filename, Error **errp)
{
return ret;
}
-const BdrvChildClass child_backing = {
- .parent_is_bds = true,
- .get_parent_desc = bdrv_child_get_parent_desc,
- .attach = bdrv_child_cb_attach_backing,
- .detach = bdrv_child_cb_detach_backing,
- .inherit_options = bdrv_backing_options,
- .drained_begin = bdrv_child_cb_drained_begin,
- .drained_poll = bdrv_child_cb_drained_poll,
- .drained_end = bdrv_child_cb_drained_end,
- .inactivate = bdrv_child_cb_inactivate,
- .update_filename = bdrv_backing_update_filename,
- .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
- .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
-};
-
/*
* Returns the options and flags that a generic child of a BDS should
* get, based on the given options and flags for the parent BDS.
bdrv_unapply_subtree_drain(child, bs);
}
+static int bdrv_child_cb_update_filename(BdrvChild *c, BlockDriverState *base,
+ const char *filename, Error **errp)
+{
+ if (c->role & BDRV_CHILD_COW) {
+ return bdrv_backing_update_filename(c, base, filename, errp);
+ }
+ return 0;
+}
+
+const BdrvChildClass child_of_bds = {
+ .parent_is_bds = true,
+ .get_parent_desc = bdrv_child_get_parent_desc,
+ .inherit_options = bdrv_inherited_options,
+ .drained_begin = bdrv_child_cb_drained_begin,
+ .drained_poll = bdrv_child_cb_drained_poll,
+ .drained_end = bdrv_child_cb_drained_end,
+ .attach = bdrv_child_cb_attach,
+ .detach = bdrv_child_cb_detach,
+ .inactivate = bdrv_child_cb_inactivate,
+ .can_set_aio_ctx = bdrv_child_cb_can_set_aio_ctx,
+ .set_aio_ctx = bdrv_child_cb_set_aio_ctx,
+ .update_filename = bdrv_child_cb_update_filename,
+};
+
static int bdrv_open_flags(BlockDriverState *bs, int flags)
{
int open_flags = flags;
}
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
- BdrvChild *c, const BdrvChildClass *child_class,
- BdrvChildRole role, BlockReopenQueue *reopen_queue,
+ BdrvChild *c, BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
uint64_t parent_perm, uint64_t parent_shared,
uint64_t *nperm, uint64_t *nshared)
{
assert(bs->drv && bs->drv->bdrv_child_perm);
- bs->drv->bdrv_child_perm(bs, c, child_class, role, reopen_queue,
+ bs->drv->bdrv_child_perm(bs, c, role, reopen_queue,
parent_perm, parent_shared,
nperm, nshared);
/* TODO Take force_share from reopen_queue */
uint64_t cur_perm, cur_shared;
bool child_tighten_restr;
- bdrv_child_perm(bs, c->bs, c, c->klass, c->role, q,
+ 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,
/* Update all children */
QLIST_FOREACH(c, &bs->children, next) {
uint64_t cur_perm, cur_shared;
- bdrv_child_perm(bs, c->bs, c, c->klass, c->role, NULL,
+ 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);
uint64_t perms, shared;
bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared);
- bdrv_child_perm(bs, c->bs, c, c->klass, c->role, NULL,
+ bdrv_child_perm(bs, c->bs, c, c->role, NULL,
parent_perms, parent_shared, &perms, &shared);
return bdrv_child_try_set_perm(c, perms, shared, errp);
}
-void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
- const BdrvChildClass *child_class,
- BdrvChildRole role,
- BlockReopenQueue *reopen_queue,
- uint64_t perm, uint64_t shared,
- uint64_t *nperm, uint64_t *nshared)
+/*
+ * Default implementation for .bdrv_child_perm() for block filters:
+ * Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED, and RESIZE to the
+ * filtered child.
+ */
+static void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
{
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
*nshared = (shared & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
}
-void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
- const BdrvChildClass *child_class,
- BdrvChildRole role,
- BlockReopenQueue *reopen_queue,
- uint64_t perm, uint64_t shared,
- uint64_t *nperm, uint64_t *nshared)
+static void bdrv_default_perms_for_cow(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ assert(role & BDRV_CHILD_COW);
+
+ /*
+ * We want consistent read from backing files if the parent needs it.
+ * No other operations are performed on backing files.
+ */
+ perm &= BLK_PERM_CONSISTENT_READ;
+
+ /*
+ * If the parent can deal with changing data, we're okay with a
+ * writable and resizable backing file.
+ * TODO Require !(perm & BLK_PERM_CONSISTENT_READ), too?
+ */
+ if (shared & BLK_PERM_WRITE) {
+ shared = BLK_PERM_WRITE | BLK_PERM_RESIZE;
+ } else {
+ shared = 0;
+ }
+
+ shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
+ BLK_PERM_WRITE_UNCHANGED;
+
+ if (bs->open_flags & BDRV_O_INACTIVE) {
+ shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
+ }
+
+ *nperm = perm;
+ *nshared = shared;
+}
+
+static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role,
+ BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
{
- bool backing = (child_class == &child_backing);
- assert(child_class == &child_backing || child_class == &child_file);
+ int flags;
- if (!backing) {
- int flags = bdrv_reopen_get_flags(reopen_queue, bs);
+ assert(role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA));
- /* Apart from the modifications below, the same permissions are
- * forwarded and left alone as for filters */
- bdrv_filter_default_perms(bs, c, child_class, role, reopen_queue,
- perm, shared, &perm, &shared);
+ flags = bdrv_reopen_get_flags(reopen_queue, bs);
+ /*
+ * Apart from the modifications below, the same permissions are
+ * forwarded and left alone as for filters
+ */
+ bdrv_filter_default_perms(bs, c, role, reopen_queue,
+ perm, shared, &perm, &shared);
+
+ if (role & BDRV_CHILD_METADATA) {
/* Format drivers may touch metadata even if the guest doesn't write */
if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
}
- /* bs->file always needs to be consistent because of the metadata. We
- * can never allow other users to resize or write to it. */
+ /*
+ * bs->file always needs to be consistent because of the
+ * metadata. We can never allow other users to resize or write
+ * to it.
+ */
if (!(flags & BDRV_O_NO_IO)) {
perm |= BLK_PERM_CONSISTENT_READ;
}
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
- } else {
- /* We want consistent read from backing files if the parent needs it.
- * No other operations are performed on backing files. */
- perm &= BLK_PERM_CONSISTENT_READ;
-
- /* If the parent can deal with changing data, we're okay with a
- * writable and resizable backing file. */
- /* TODO Require !(perm & BLK_PERM_CONSISTENT_READ), too? */
- if (shared & BLK_PERM_WRITE) {
- shared = BLK_PERM_WRITE | BLK_PERM_RESIZE;
- } else {
- shared = 0;
+ }
+
+ if (role & BDRV_CHILD_DATA) {
+ /*
+ * Technically, everything in this block is a subset of the
+ * BDRV_CHILD_METADATA path taken above, and so this could
+ * be an "else if" branch. However, that is not obvious, and
+ * this function is not performance critical, therefore we let
+ * this be an independent "if".
+ */
+
+ /*
+ * We cannot allow other users to resize the file because the
+ * format driver might have some assumptions about the size
+ * (e.g. because it is stored in metadata, or because the file
+ * is split into fixed-size data files).
+ */
+ shared &= ~BLK_PERM_RESIZE;
+
+ /*
+ * WRITE_UNCHANGED often cannot be performed as such on the
+ * data file. For example, the qcow2 driver may still need to
+ * write copied clusters on copy-on-read.
+ */
+ if (perm & BLK_PERM_WRITE_UNCHANGED) {
+ perm |= BLK_PERM_WRITE;
}
- shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
- BLK_PERM_WRITE_UNCHANGED;
+ /*
+ * If the data file is written to, the format driver may
+ * expect to be able to resize it by writing beyond the EOF.
+ */
+ if (perm & BLK_PERM_WRITE) {
+ perm |= BLK_PERM_RESIZE;
+ }
}
if (bs->open_flags & BDRV_O_INACTIVE) {
*nshared = shared;
}
+void bdrv_default_perms(BlockDriverState *bs, BdrvChild *c,
+ BdrvChildRole role, BlockReopenQueue *reopen_queue,
+ uint64_t perm, uint64_t shared,
+ uint64_t *nperm, uint64_t *nshared)
+{
+ if (role & BDRV_CHILD_FILTERED) {
+ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA |
+ BDRV_CHILD_COW)));
+ bdrv_filter_default_perms(bs, c, role, reopen_queue,
+ perm, shared, nperm, nshared);
+ } else if (role & BDRV_CHILD_COW) {
+ assert(!(role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA)));
+ bdrv_default_perms_for_cow(bs, c, role, reopen_queue,
+ perm, shared, nperm, nshared);
+ } else if (role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)) {
+ bdrv_default_perms_for_storage(bs, c, role, reopen_queue,
+ perm, shared, nperm, nshared);
+ } else {
+ g_assert_not_reached();
+ }
+}
+
uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
{
static const uint64_t permissions[] = {
bdrv_get_cumulative_perm(parent_bs, &perm, &shared_perm);
assert(parent_bs->drv);
- bdrv_child_perm(parent_bs, child_bs, NULL, child_class, child_role, NULL,
+ bdrv_child_perm(parent_bs, child_bs, NULL, child_role, NULL,
perm, shared_perm, &perm, &shared_perm);
child = bdrv_root_attach_child(child_bs, child_name, child_class,
return child != NULL;
}
+/*
+ * Return the BdrvChildRole for @bs's backing child. bs->backing is
+ * mostly used for COW backing children (role = COW), but also for
+ * filtered children (role = FILTERED | PRIMARY).
+ */
+static BdrvChildRole bdrv_backing_role(BlockDriverState *bs)
+{
+ if (bs->drv && bs->drv->is_filter) {
+ return BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY;
+ } else {
+ return BDRV_CHILD_COW;
+ }
+}
+
/*
* Sets the backing file link of a BDS. A new reference is created; callers
* which don't need their own reference any more must call bdrv_unref().
goto out;
}
- bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
- 0, errp);
+ bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_of_bds,
+ bdrv_backing_role(bs), errp);
/* If backing_hd was already part of bs's backing chain, and
* inherits_from pointed recursively to bs then let's update it to
* point directly to bs (else it will become NULL). */
}
backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
- &child_backing, 0, errp);
+ &child_of_bds, bdrv_backing_role(bs), errp);
if (!backing_hd) {
bs->open_flags |= BDRV_O_NO_BACKING;
error_prepend(errp, "Could not open backing file: ");
BlockDriverState *file_bs;
file_bs = bdrv_open_child_bs(filename, options, "file", bs,
- &child_file, 0, true, &local_err);
+ &child_of_bds, BDRV_CHILD_IMAGE,
+ true, &local_err);
if (local_err) {
goto fail;
}
if (state->replace_backing_bs && state->new_backing_bs) {
uint64_t nperm, nshared;
bdrv_child_perm(state->bs, state->new_backing_bs,
- NULL, &child_backing, 0, bs_queue,
- state->perm, state->shared_perm,
+ NULL, bdrv_backing_role(state->bs),
+ bs_queue, state->perm, state->shared_perm,
&nperm, &nshared);
ret = bdrv_check_update_perm(state->new_backing_bs, NULL,
nperm, nshared, NULL, NULL, errp);
} else {
uint64_t nperm, nshared;
- bdrv_child_perm(parent->state.bs, bs, c, c->klass, c->role, q,
+ bdrv_child_perm(parent->state.bs, bs, c, c->role, q,
parent->state.perm, parent->state.shared_perm,
&nperm, &nshared);
drv->bdrv_gather_child_options(bs, opts, backing_overridden);
} else {
QLIST_FOREACH(child, &bs->children, next) {
- if (child->klass == &child_backing && !backing_overridden) {
+ if (child == bs->backing && !backing_overridden) {
/* We can skip the backing BDS if it has not been overridden */
continue;
}
pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename);
} else {
QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
- snprintf(bs->filename, sizeof(bs->filename), "json:%s",
- qstring_get_str(json));
+ if (snprintf(bs->filename, sizeof(bs->filename), "json:%s",
+ qstring_get_str(json)) >= sizeof(bs->filename)) {
+ /* Give user a hint if we truncated things. */
+ strcpy(bs->filename + sizeof(bs->filename) - 4, "...");
+ }
qobject_unref(json);
}
}