diff --git a/block.c b/block.c index 0653ccb913..8416376c9b 100644 --- a/block.c +++ b/block.c @@ -76,7 +76,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, Error **errp); /* If non-zero, use only whitelisted block drivers */ @@ -1093,18 +1094,6 @@ static void bdrv_child_cb_drained_end(BdrvChild *child, bdrv_drained_end_no_poll(bs, drained_end_counter); } -static void bdrv_child_cb_attach(BdrvChild *child) -{ - BlockDriverState *bs = child->opaque; - bdrv_apply_subtree_drain(child, bs); -} - -static void bdrv_child_cb_detach(BdrvChild *child) -{ - BlockDriverState *bs = child->opaque; - bdrv_unapply_subtree_drain(child, bs); -} - static int bdrv_child_cb_inactivate(BdrvChild *child) { BlockDriverState *bs = child->opaque; @@ -1149,82 +1138,6 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, *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_inherited_options(int *child_flags, QDict *child_options, - int parent_flags, QDict *parent_options) -{ - int flags = parent_flags; - - /* Enable protocol handling, disable format probing for bs->file */ - flags |= BDRV_O_PROTOCOL; - - /* If the cache mode isn't explicitly set, inherit direct and no-flush from - * the parent. */ - qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); - qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); - qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); - - /* Inherit the read-only option from the parent if it's not set */ - qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); - qdict_copy_default(child_options, parent_options, BDRV_OPT_AUTO_READ_ONLY); - - /* Our block drivers take care to send flushes and respect unmap policy, - * so we can default to enable both on lower layers regardless of the - * corresponding parent options. */ - qdict_set_default_str(child_options, BDRV_OPT_DISCARD, "unmap"); - - /* Clear flags that only apply to the top layer */ - flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ | - BDRV_O_NO_IO); - - *child_flags = flags; -} - -const BdrvChildRole child_file = { - .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, -}; - -/* - * 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(int *child_flags, QDict *child_options, - int parent_flags, QDict *parent_options) -{ - child_file.inherit_options(child_flags, child_options, - parent_flags, parent_options); - - *child_flags &= ~(BDRV_O_PROTOCOL | BDRV_O_NO_IO); -} - -const BdrvChildRole 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; @@ -1266,8 +1179,6 @@ static void bdrv_backing_attach(BdrvChild *c) parent->backing_blocker); bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, parent->backing_blocker); - - bdrv_child_cb_attach(c); } static void bdrv_backing_detach(BdrvChild *c) @@ -1278,34 +1189,6 @@ static void bdrv_backing_detach(BdrvChild *c) bdrv_op_unblock_all(c->bs, parent->backing_blocker); error_free(parent->backing_blocker); parent->backing_blocker = NULL; - - 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(int *child_flags, QDict *child_options, - int parent_flags, QDict *parent_options) -{ - int flags = parent_flags; - - /* The cache mode is inherited unmodified for backing files; except WCE, - * which is only applied on the top level (BlockBackend) */ - qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); - qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); - qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); - - /* backing files always opened read-only */ - qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); - qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); - flags &= ~BDRV_O_COPY_ON_READ; - - /* snapshot=on is handled on the top layer */ - flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_TEMPORARY); - - *child_flags = flags; } static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, @@ -1335,19 +1218,130 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, return ret; } -const BdrvChildRole child_backing = { +/* + * 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. + */ +static void bdrv_inherited_options(BdrvChildRole role, bool parent_is_format, + int *child_flags, QDict *child_options, + int parent_flags, QDict *parent_options) +{ + int flags = parent_flags; + + /* + * First, decide whether to set, clear, or leave BDRV_O_PROTOCOL. + * Generally, the question to answer is: Should this child be + * format-probed by default? + */ + + /* + * Pure and non-filtered data children of non-format nodes should + * be probed by default (even when the node itself has BDRV_O_PROTOCOL + * set). This only affects a very limited set of drivers (namely + * quorum and blkverify when this comment was written). + * Force-clear BDRV_O_PROTOCOL then. + */ + if (!parent_is_format && + (role & BDRV_CHILD_DATA) && + !(role & (BDRV_CHILD_METADATA | BDRV_CHILD_FILTERED))) + { + flags &= ~BDRV_O_PROTOCOL; + } + + /* + * All children of format nodes (except for COW children) and all + * metadata children in general should never be format-probed. + * Force-set BDRV_O_PROTOCOL then. + */ + if ((parent_is_format && !(role & BDRV_CHILD_COW)) || + (role & BDRV_CHILD_METADATA)) + { + flags |= BDRV_O_PROTOCOL; + } + + /* + * If the cache mode isn't explicitly set, inherit direct and no-flush from + * the parent. + */ + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_DIRECT); + qdict_copy_default(child_options, parent_options, BDRV_OPT_CACHE_NO_FLUSH); + qdict_copy_default(child_options, parent_options, BDRV_OPT_FORCE_SHARE); + + if (role & BDRV_CHILD_COW) { + /* backing files are opened read-only by default */ + qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); + qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); + } else { + /* Inherit the read-only option from the parent if it's not set */ + qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); + qdict_copy_default(child_options, parent_options, + BDRV_OPT_AUTO_READ_ONLY); + } + + /* + * bdrv_co_pdiscard() respects unmap policy for the parent, so we + * can default to enable it on lower layers regardless of the + * parent option. + */ + qdict_set_default_str(child_options, BDRV_OPT_DISCARD, "unmap"); + + /* Clear flags that only apply to the top layer */ + flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_COPY_ON_READ); + + if (role & BDRV_CHILD_METADATA) { + flags &= ~BDRV_O_NO_IO; + } + if (role & BDRV_CHILD_COW) { + flags &= ~BDRV_O_TEMPORARY; + } + + *child_flags = flags; +} + +static void bdrv_child_cb_attach(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + + if (child->role & BDRV_CHILD_COW) { + bdrv_backing_attach(child); + } + + bdrv_apply_subtree_drain(child, bs); +} + +static void bdrv_child_cb_detach(BdrvChild *child) +{ + BlockDriverState *bs = child->opaque; + + if (child->role & BDRV_CHILD_COW) { + bdrv_backing_detach(child); + } + + 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, - .attach = bdrv_backing_attach, - .detach = bdrv_backing_detach, - .inherit_options = bdrv_backing_options, + .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, - .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, + .update_filename = bdrv_child_cb_update_filename, }; static int bdrv_open_flags(BlockDriverState *bs, int flags) @@ -1953,7 +1947,7 @@ bool bdrv_is_writable(BlockDriverState *bs) } static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, - BdrvChild *c, const BdrvChildRole *role, + BdrvChild *c, BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t parent_perm, uint64_t parent_shared, uint64_t *nperm, uint64_t *nshared) @@ -2145,8 +2139,8 @@ void bdrv_get_cumulative_perm(BlockDriverState *bs, uint64_t *perm, static char *bdrv_child_user_desc(BdrvChild *c) { - if (c->role->get_parent_desc) { - return c->role->get_parent_desc(c); + if (c->klass->get_parent_desc) { + return c->klass->get_parent_desc(c); } return g_strdup("another user"); @@ -2348,66 +2342,132 @@ int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp) uint64_t perms, shared; bdrv_get_cumulative_perm(bs, &parent_perms, &parent_shared); - bdrv_child_perm(bs, c->bs, c, c->role, NULL, parent_perms, parent_shared, - &perms, &shared); + 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 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 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) { - bool backing = (role == &child_backing); - assert(role == &child_backing || role == &child_file); + assert(role & BDRV_CHILD_COW); - if (!backing) { - int flags = bdrv_reopen_get_flags(reopen_queue, bs); + /* + * 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; - /* 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 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) +{ + int flags; + + assert(role & (BDRV_CHILD_METADATA | BDRV_CHILD_DATA)); + + 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) { @@ -2418,6 +2478,28 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, *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[] = { @@ -2456,7 +2538,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, * If the new child node is drained but the old one was not, flush * all outstanding requests to the old child node. */ - while (drain_saldo > 0 && child->role->drained_begin) { + while (drain_saldo > 0 && child->klass->drained_begin) { bdrv_parent_drained_begin_single(child, true); drain_saldo--; } @@ -2465,8 +2547,8 @@ static void bdrv_replace_child_noperm(BdrvChild *child, /* Detach first so that the recursive drain sections coming from @child * are already gone and we only end the drain sections that came from * elsewhere. */ - if (child->role->detach) { - child->role->detach(child); + if (child->klass->detach) { + child->klass->detach(child); } QLIST_REMOVE(child, next_parent); } @@ -2488,8 +2570,8 @@ static void bdrv_replace_child_noperm(BdrvChild *child, /* Attach only after starting new drained sections, so that recursive * drain sections coming from @child don't get an extra .drained_begin * callback. */ - if (child->role->attach) { - child->role->attach(child); + if (child->klass->attach) { + child->klass->attach(child); } } @@ -2497,7 +2579,7 @@ static void bdrv_replace_child_noperm(BdrvChild *child, * If the old child node was drained but the new one is not, allow * requests to come in only after the new node has been attached. */ - while (drain_saldo < 0 && child->role->drained_end) { + while (drain_saldo < 0 && child->klass->drained_end) { bdrv_parent_drained_end_single(child); drain_saldo++; } @@ -2570,7 +2652,8 @@ static void bdrv_replace_child(BdrvChild *child, BlockDriverState *new_bs) */ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, AioContext *ctx, uint64_t perm, uint64_t shared_perm, void *opaque, Error **errp) @@ -2591,6 +2674,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, *child = (BdrvChild) { .bs = NULL, .name = g_strdup(child_name), + .klass = child_class, .role = child_role, .perm = perm, .shared_perm = shared_perm, @@ -2602,15 +2686,15 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, * try moving the parent into the AioContext of child_bs instead. */ if (bdrv_get_aio_context(child_bs) != ctx) { ret = bdrv_try_set_aio_context(child_bs, ctx, &local_err); - if (ret < 0 && child_role->can_set_aio_ctx) { + if (ret < 0 && child_class->can_set_aio_ctx) { GSList *ignore = g_slist_prepend(NULL, child); ctx = bdrv_get_aio_context(child_bs); - if (child_role->can_set_aio_ctx(child, ctx, &ignore, NULL)) { + if (child_class->can_set_aio_ctx(child, ctx, &ignore, NULL)) { error_free(local_err); ret = 0; g_slist_free(ignore); ignore = g_slist_prepend(NULL, child); - child_role->set_aio_ctx(child, ctx, &ignore); + child_class->set_aio_ctx(child, ctx, &ignore); } g_slist_free(ignore); } @@ -2643,7 +2727,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, const char *child_name, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, Error **errp) { BdrvChild *child; @@ -2655,8 +2740,8 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, 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_role, - bdrv_get_aio_context(parent_bs), + child = bdrv_root_attach_child(child_bs, child_name, child_class, + child_role, bdrv_get_aio_context(parent_bs), perm, shared_perm, parent_bs, errp); if (child == NULL) { return NULL; @@ -2728,8 +2813,8 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) { BdrvChild *c; QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role->change_media) { - c->role->change_media(c, load); + if (c->klass->change_media) { + c->klass->change_media(c, load); } } } @@ -2746,6 +2831,20 @@ static bool bdrv_inherits_from_recursive(BlockDriverState *child, 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(). @@ -2773,8 +2872,8 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, goto out; } - bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing, - 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). */ @@ -2871,7 +2970,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, - &child_backing, 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: "); @@ -2905,15 +3004,15 @@ free_exit: static BlockDriverState * bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, - BlockDriverState *parent, const BdrvChildRole *child_role, - bool allow_none, Error **errp) + BlockDriverState *parent, const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, Error **errp) { BlockDriverState *bs = NULL; QDict *image_options; char *bdref_key_dot; const char *reference; - assert(child_role != NULL); + assert(child_class != NULL); bdref_key_dot = g_strdup_printf("%s.", bdref_key); qdict_extract_subqdict(options, &image_options, bdref_key_dot); @@ -2937,7 +3036,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, } bs = bdrv_open_inherit(filename, reference, image_options, 0, - parent, child_role, errp); + parent, child_class, child_role, errp); if (!bs) { goto done; } @@ -2964,22 +3063,26 @@ done: BdrvChild *bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, Error **errp) { BlockDriverState *bs; - bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_role, - allow_none, errp); + bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class, + child_role, allow_none, errp); if (bs == NULL) { return NULL; } - return bdrv_attach_child(parent, bs, bdref_key, child_role, errp); + return bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, + errp); } -/* TODO Future callers may need to specify parent/child_role in order for - * option inheritance to work. Existing callers use it for the root node. */ +/* + * TODO Future callers may need to specify parent/child_class in order for + * option inheritance to work. Existing callers use it for the root node. + */ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) { BlockDriverState *bs = NULL; @@ -3011,7 +3114,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) } - bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp); + bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, errp); obj = NULL; qobject_unref(obj); visit_free(v); @@ -3107,7 +3210,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, const char *reference, QDict *options, int flags, BlockDriverState *parent, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, Error **errp) { int ret; @@ -3121,8 +3225,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, QDict *snapshot_options = NULL; int snapshot_flags = 0; - assert(!child_role || !flags); - assert(!child_role == !parent); + assert(!child_class || !flags); + assert(!child_class == !parent); if (reference) { bool options_non_empty = options ? qdict_size(options) : false; @@ -3158,10 +3262,24 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, bs->explicit_options = qdict_clone_shallow(options); - if (child_role) { + if (child_class) { + bool parent_is_format; + + if (parent->drv) { + parent_is_format = parent->drv->is_format; + } else { + /* + * parent->drv is not set yet because this node is opened for + * (potential) format probing. That means that @parent is going + * to be a format node. + */ + parent_is_format = true; + } + bs->inherits_from = parent; - child_role->inherit_options(&flags, options, - parent->open_flags, parent->options); + child_class->inherit_options(child_role, parent_is_format, + &flags, options, + parent->open_flags, parent->options); } ret = bdrv_fill_options(&options, filename, &flags, &local_err); @@ -3189,7 +3307,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, flags, options); /* Let bdrv_backing_options() override "read-only" */ qdict_del(options, BDRV_OPT_READ_ONLY); - bdrv_backing_options(&flags, options, flags, options); + bdrv_inherited_options(BDRV_CHILD_COW, true, + &flags, options, flags, options); } bs->open_flags = flags; @@ -3231,7 +3350,8 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, BlockDriverState *file_bs; file_bs = bdrv_open_child_bs(filename, options, "file", bs, - &child_file, true, &local_err); + &child_of_bds, BDRV_CHILD_IMAGE, + true, &local_err); if (local_err) { goto fail; } @@ -3376,7 +3496,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, QDict *options, int flags, Error **errp) { return bdrv_open_inherit(filename, reference, options, flags, NULL, - NULL, errp); + NULL, 0, errp); } /* Return true if the NULL-terminated @list contains @str */ @@ -3472,7 +3592,9 @@ static bool bdrv_recurse_has_child(BlockDriverState *bs, static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, - const BdrvChildRole *role, + const BdrvChildClass *klass, + BdrvChildRole role, + bool parent_is_format, QDict *parent_options, int parent_flags, bool keep_old_opts) @@ -3528,7 +3650,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, /* Inherit from parent node */ if (parent_options) { flags = 0; - role->inherit_options(&flags, options, parent_flags, parent_options); + klass->inherit_options(role, parent_is_format, &flags, options, + parent_flags, parent_options); } else { flags = bdrv_get_flags(bs); } @@ -3619,7 +3742,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, } bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, - child->role, options, flags, child_keep_old); + child->klass, child->role, bs->drv->is_format, + options, flags, child_keep_old); } return bs_queue; @@ -3629,8 +3753,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, bool keep_old_opts) { - return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0, - keep_old_opts); + return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, + NULL, 0, keep_old_opts); } /* @@ -3676,8 +3800,8 @@ int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) 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, 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); @@ -4305,7 +4429,7 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) GHashTable *found; bool ret; - if (c->role->stay_at_node) { + if (c->klass->stay_at_node) { return false; } @@ -4776,9 +4900,9 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, } /* If so, update the backing file path in the image file */ - if (c->role->update_filename) { - ret = c->role->update_filename(c, base, backing_file_str, - &local_err); + 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); error_report_err(local_err); @@ -5226,8 +5350,8 @@ const char *bdrv_get_parent_name(const BlockDriverState *bs) /* If multiple parents have a name, just pick the first one. */ QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role->get_name) { - name = c->role->get_name(c); + if (c->klass->get_name) { + name = c->klass->get_name(c); if (name && *name) { return name; } @@ -5586,8 +5710,8 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, } QLIST_FOREACH(parent, &bs->parents, next_parent) { - if (parent->role->activate) { - parent->role->activate(parent, &local_err); + if (parent->klass->activate) { + parent->klass->activate(parent, &local_err); if (local_err) { bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); @@ -5655,7 +5779,7 @@ static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) BdrvChild *parent; QLIST_FOREACH(parent, &bs->parents, next_parent) { - if (parent->role->parent_is_bds) { + if (parent->klass->parent_is_bds) { BlockDriverState *parent_bs = parent->opaque; if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) { return true; @@ -5694,8 +5818,8 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs) } QLIST_FOREACH(parent, &bs->parents, next_parent) { - if (parent->role->inactivate) { - ret = parent->role->inactivate(parent); + if (parent->klass->inactivate) { + ret = parent->klass->inactivate(parent); if (ret < 0) { return ret; } @@ -6195,9 +6319,9 @@ void bdrv_set_aio_context_ignore(BlockDriverState *bs, if (g_slist_find(*ignore, child)) { continue; } - assert(child->role->set_aio_ctx); + assert(child->klass->set_aio_ctx); *ignore = g_slist_prepend(*ignore, child); - child->role->set_aio_ctx(child, new_context, ignore); + child->klass->set_aio_ctx(child, new_context, ignore); } bdrv_detach_aio_context(bs); @@ -6237,15 +6361,17 @@ static bool bdrv_parent_can_set_aio_context(BdrvChild *c, AioContext *ctx, } *ignore = g_slist_prepend(*ignore, c); - /* A BdrvChildRole that doesn't handle AioContext changes cannot - * tolerate any AioContext changes */ - if (!c->role->can_set_aio_ctx) { + /* + * A BdrvChildClass that doesn't handle AioContext changes cannot + * tolerate any AioContext changes + */ + if (!c->klass->can_set_aio_ctx) { char *user = bdrv_child_user_desc(c); error_setg(errp, "Changing iothreads is not supported by %s", user); g_free(user); return false; } - if (!c->role->can_set_aio_ctx(c, ctx, ignore, errp)) { + if (!c->klass->can_set_aio_ctx(c, ctx, ignore, errp)) { assert(!errp || *errp); return false; } @@ -6631,7 +6757,7 @@ void bdrv_refresh_filename(BlockDriverState *bs) drv->bdrv_gather_child_options(bs, opts, backing_overridden); } else { QLIST_FOREACH(child, &bs->children, next) { - if (child->role == &child_backing && !backing_overridden) { + if (child == bs->backing && !backing_overridden) { /* We can skip the backing BDS if it has not been overridden */ continue; } @@ -6764,3 +6890,26 @@ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) parent_bs->drv->bdrv_del_child(parent_bs, child, errp); } + +int bdrv_make_empty(BdrvChild *c, Error **errp) +{ + BlockDriver *drv = c->bs->drv; + int ret; + + assert(c->perm & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)); + + if (!drv->bdrv_make_empty) { + error_setg(errp, "%s does not support emptying nodes", + drv->format_name); + return -ENOTSUP; + } + + ret = drv->bdrv_make_empty(c->bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to empty %s", + c->bs->filename); + return ret; + } + + return 0; +} diff --git a/block/backup-top.c b/block/backup-top.c index 79b268e6dc..af2f20f346 100644 --- a/block/backup-top.c +++ b/block/backup-top.c @@ -122,7 +122,7 @@ static void backup_top_refresh_filename(BlockDriverState *bs) } static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) @@ -142,7 +142,7 @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, return; } - if (role == &child_file) { + if (!(role & BDRV_CHILD_FILTERED)) { /* * Target child * @@ -155,8 +155,8 @@ static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c, *nperm = BLK_PERM_WRITE; } else { /* Source child */ - bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, - nperm, nshared); + bdrv_default_perms(bs, c, role, reopen_queue, + perm, shared, nperm, nshared); if (perm & BLK_PERM_WRITE) { *nperm = *nperm | BLK_PERM_CONSISTENT_READ; @@ -214,7 +214,8 @@ BlockDriverState *bdrv_backup_top_append(BlockDriverState *source, source->supported_zero_flags); bdrv_ref(target); - state->target = bdrv_attach_child(top, target, "target", &child_file, errp); + state->target = bdrv_attach_child(top, target, "target", &child_of_bds, + BDRV_CHILD_DATA, errp); if (!state->target) { bdrv_unref(target); bdrv_unref(top); diff --git a/block/blkdebug.c b/block/blkdebug.c index af44aa973f..7194bc7f06 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -497,7 +497,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, /* Open the image file */ bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image", - bs, &child_file, false, &local_err); + bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -993,15 +995,15 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, } static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { BDRVBlkdebugState *s = bs->opaque; - bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, - nperm, nshared); + bdrv_default_perms(bs, c, role, reopen_queue, + perm, shared, nperm, nshared); *nperm |= s->take_child_perms; *nshared &= ~s->unshare_child_perms; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 04d8b33607..6753bd9a3e 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -157,7 +157,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the file */ - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, &local_err); if (local_err) { ret = -EINVAL; @@ -166,8 +167,8 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, } /* Open the log file */ - s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false, - &local_err); + s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_of_bds, + BDRV_CHILD_METADATA, false, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -282,7 +283,7 @@ static int64_t blk_log_writes_getlength(BlockDriverState *bs) } static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *ro_q, uint64_t perm, uint64_t shrd, uint64_t *nperm, uint64_t *nshrd) @@ -293,11 +294,8 @@ static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, return; } - if (!strcmp(c->name, "log")) { - bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); - } else { - bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd); - } + bdrv_default_perms(bs, c, role, ro_q, perm, shrd, + nperm, nshrd); } static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp) diff --git a/block/blkreplay.c b/block/blkreplay.c index c96ac8f4bc..30a0f5d57a 100644 --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -27,8 +27,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags, int ret; /* Open the image file */ - bs->file = bdrv_open_child(NULL, options, "image", - bs, &child_file, false, &local_err); + bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -135,9 +136,10 @@ static int blkreplay_snapshot_goto(BlockDriverState *bs, static BlockDriver bdrv_blkreplay = { .format_name = "blkreplay", .instance_size = 0, + .is_filter = true, .bdrv_open = blkreplay_open, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_getlength = blkreplay_getlength, .bdrv_co_preadv = blkreplay_co_preadv, diff --git a/block/blkverify.c b/block/blkverify.c index ba6b1853ae..2f261de24b 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -125,7 +125,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, /* Open the raw file */ bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw", - bs, &child_file, false, &local_err); + bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -134,8 +136,8 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags, /* Open the test file */ s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, - "test", bs, &child_format, false, - &local_err); + "test", bs, &child_of_bds, BDRV_CHILD_DATA, + false, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -317,7 +319,7 @@ static BlockDriver bdrv_blkverify = { .bdrv_parse_filename = blkverify_parse_filename, .bdrv_file_open = blkverify_open, .bdrv_close = blkverify_close, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_getlength = blkverify_getlength, .bdrv_refresh_filename = blkverify_refresh_filename, .bdrv_dirname = blkverify_dirname, diff --git a/block/block-backend.c b/block/block-backend.c index f4944861fa..6936b25c83 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -120,7 +120,8 @@ static QTAILQ_HEAD(, BlockBackend) block_backends = static QTAILQ_HEAD(, BlockBackend) monitor_block_backends = QTAILQ_HEAD_INITIALIZER(monitor_block_backends); -static void blk_root_inherit_options(int *child_flags, QDict *child_options, +static void blk_root_inherit_options(BdrvChildRole role, bool parent_is_format, + int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options) { /* We're not supposed to call this function for root nodes */ @@ -297,7 +298,7 @@ static void blk_root_detach(BdrvChild *child) } } -static const BdrvChildRole child_root = { +static const BdrvChildClass child_root = { .inherit_options = blk_root_inherit_options, .change_media = blk_root_change_media, @@ -423,8 +424,9 @@ BlockBackend *blk_new_open(const char *filename, const char *reference, return NULL; } - blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx, - perm, BLK_PERM_ALL, blk, errp); + blk->root = bdrv_root_attach_child(bs, "root", &child_root, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + blk->ctx, perm, BLK_PERM_ALL, blk, errp); if (!blk->root) { blk_unref(blk); return NULL; @@ -716,7 +718,7 @@ static BlockBackend *bdrv_first_blk(BlockDriverState *bs) { BdrvChild *child; QLIST_FOREACH(child, &bs->parents, next_parent) { - if (child->role == &child_root) { + if (child->klass == &child_root) { return child->opaque; } } @@ -740,7 +742,7 @@ bool bdrv_is_root_node(BlockDriverState *bs) BdrvChild *c; QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role != &child_root) { + if (c->klass != &child_root) { return false; } } @@ -834,8 +836,10 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) { ThrottleGroupMember *tgm = &blk->public.throttle_group_member; bdrv_ref(bs); - blk->root = bdrv_root_attach_child(bs, "root", &child_root, blk->ctx, - blk->perm, blk->shared_perm, blk, errp); + blk->root = bdrv_root_attach_child(bs, "root", &child_root, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + blk->ctx, blk->perm, blk->shared_perm, + blk, errp); if (blk->root == NULL) { return -EPERM; } @@ -2402,3 +2406,13 @@ const BdrvChild *blk_root(BlockBackend *blk) { return blk->root; } + +int blk_make_empty(BlockBackend *blk, Error **errp) +{ + if (!blk_is_available(blk)) { + error_setg(errp, "No medium inserted"); + return -ENOMEDIUM; + } + + return bdrv_make_empty(blk->root, errp); +} diff --git a/block/block-copy.c b/block/block-copy.c index 4713c8f2a3..bb8d0569f2 100644 --- a/block/block-copy.c +++ b/block/block-copy.c @@ -343,9 +343,7 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, ~BDRV_REQ_WRITE_COMPRESSED); if (ret < 0) { trace_block_copy_write_zeroes_fail(s, offset, ret); - if (error_is_read) { - *error_is_read = false; - } + *error_is_read = false; } return ret; } @@ -393,9 +391,7 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0); if (ret < 0) { trace_block_copy_read_fail(s, offset, ret); - if (error_is_read) { - *error_is_read = true; - } + *error_is_read = true; goto out; } @@ -403,9 +399,7 @@ static int coroutine_fn block_copy_do_copy(BlockCopyState *s, s->write_flags); if (ret < 0) { trace_block_copy_write_fail(s, offset, ret); - if (error_is_read) { - *error_is_read = false; - } + *error_is_read = false; goto out; } @@ -418,7 +412,7 @@ out: static coroutine_fn int block_copy_task_entry(AioTask *task) { BlockCopyTask *t = container_of(task, BlockCopyTask, task); - bool error_is_read; + bool error_is_read = false; int ret; ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes, diff --git a/block/bochs.c b/block/bochs.c index 32bb83b268..2f010ab40a 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -110,8 +110,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -297,10 +297,11 @@ static BlockDriver bdrv_bochs = { .instance_size = sizeof(BDRVBochsState), .bdrv_probe = bochs_probe, .bdrv_open = bochs_open, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_refresh_limits = bochs_refresh_limits, .bdrv_co_preadv = bochs_co_preadv, .bdrv_close = bochs_close, + .is_format = true, }; static void bdrv_bochs_init(void) diff --git a/block/cloop.c b/block/cloop.c index 4de94876d4..c99192a57f 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -71,8 +71,8 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -293,10 +293,11 @@ static BlockDriver bdrv_cloop = { .instance_size = sizeof(BDRVCloopState), .bdrv_probe = cloop_probe, .bdrv_open = cloop_open, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_refresh_limits = cloop_refresh_limits, .bdrv_co_preadv = cloop_co_preadv, .bdrv_close = cloop_close, + .is_format = true, }; static void bdrv_cloop_init(void) diff --git a/block/commit.c b/block/commit.c index 87f6096d90..7732d02dfe 100644 --- a/block/commit.c +++ b/block/commit.c @@ -223,7 +223,7 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs) } static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) @@ -240,6 +240,8 @@ static BlockDriver bdrv_commit_top = { .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, .bdrv_child_perm = bdrv_commit_top_child_perm, + + .is_filter = true, }; void commit_start(const char *job_id, BlockDriverState *bs, @@ -414,7 +416,9 @@ int bdrv_commit(BlockDriverState *bs) } ctx = bdrv_get_aio_context(bs); - src = blk_new(ctx, BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); + /* WRITE_UNCHANGED is required for bdrv_make_empty() */ + src = blk_new(ctx, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED, + BLK_PERM_ALL); backing = blk_new(ctx, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); ret = blk_insert_bs(src, bs, &local_err); @@ -492,14 +496,14 @@ int bdrv_commit(BlockDriverState *bs) } } - if (drv->bdrv_make_empty) { - ret = drv->bdrv_make_empty(bs); - if (ret < 0) { - goto ro_cleanup; - } - blk_flush(src); + ret = blk_make_empty(src, NULL); + /* Ignore -ENOTSUP */ + if (ret < 0 && ret != -ENOTSUP) { + goto ro_cleanup; } + blk_flush(src); + /* * Make sure all data we wrote to the backing device is actually * stable on disk. diff --git a/block/copy-on-read.c b/block/copy-on-read.c index 242d3ff055..a6e3c74a68 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -28,8 +28,9 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, - errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, errp); if (!bs->file) { return -EINVAL; } @@ -51,7 +52,7 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, #define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH) static void cor_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) diff --git a/block/crypto.c b/block/crypto.c index 6b21d6bf6c..b216e12c31 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -218,8 +218,8 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, unsigned int cflags = 0; QDict *cryptoopts = NULL; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -756,7 +756,7 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_close = block_crypto_close, /* This driver doesn't modify LUKS metadata except when creating image. * Allow share-rw=on as a special case. */ - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks, .bdrv_co_truncate = block_crypto_co_truncate, @@ -771,6 +771,8 @@ static BlockDriver bdrv_crypto_luks = { .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, + .is_format = true, + .strong_runtime_opts = block_crypto_strong_runtime_opts, }; diff --git a/block/dmg.c b/block/dmg.c index 4a045f2b3e..0d6c317296 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -439,8 +439,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, return ret; } - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -750,9 +750,10 @@ static BlockDriver bdrv_dmg = { .bdrv_probe = dmg_probe, .bdrv_open = dmg_open, .bdrv_refresh_limits = dmg_refresh_limits, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_preadv = dmg_co_preadv, .bdrv_close = dmg_close, + .is_format = true, }; static void bdrv_dmg_init(void) diff --git a/block/filter-compress.c b/block/filter-compress.c index 82c315b298..8ec1991c1f 100644 --- a/block/filter-compress.c +++ b/block/filter-compress.c @@ -30,8 +30,9 @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, - errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, errp); if (!bs->file) { return -EINVAL; } @@ -132,7 +133,7 @@ static BlockDriver bdrv_compress = { .format_name = "compress", .bdrv_open = compress_open, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_getlength = compress_getlength, diff --git a/block/io.c b/block/io.c index 7d30e61edc..121ce17a49 100644 --- a/block/io.c +++ b/block/io.c @@ -50,7 +50,7 @@ static void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore, BdrvChild *c, *next; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { continue; } bdrv_parent_drained_begin_single(c, false); @@ -62,8 +62,8 @@ static void bdrv_parent_drained_end_single_no_poll(BdrvChild *c, { assert(c->parent_quiesce_counter > 0); c->parent_quiesce_counter--; - if (c->role->drained_end) { - c->role->drained_end(c, drained_end_counter); + if (c->klass->drained_end) { + c->klass->drained_end(c, drained_end_counter); } } @@ -81,7 +81,7 @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, BdrvChild *c; QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { continue; } bdrv_parent_drained_end_single_no_poll(c, drained_end_counter); @@ -90,8 +90,8 @@ static void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore, static bool bdrv_parent_drained_poll_single(BdrvChild *c) { - if (c->role->drained_poll) { - return c->role->drained_poll(c); + if (c->klass->drained_poll) { + return c->klass->drained_poll(c); } return false; } @@ -103,7 +103,7 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, bool busy = false; QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { - if (c == ignore || (ignore_bds_parents && c->role->parent_is_bds)) { + if (c == ignore || (ignore_bds_parents && c->klass->parent_is_bds)) { continue; } busy |= bdrv_parent_drained_poll_single(c); @@ -115,8 +115,8 @@ static bool bdrv_parent_drained_poll(BlockDriverState *bs, BdrvChild *ignore, void bdrv_parent_drained_begin_single(BdrvChild *c, bool poll) { c->parent_quiesce_counter++; - if (c->role->drained_begin) { - c->role->drained_begin(c); + if (c->klass->drained_begin) { + c->klass->drained_begin(c); } if (poll) { BDRV_POLL_WHILE(c->bs, bdrv_parent_drained_poll_single(c)); @@ -3326,8 +3326,8 @@ static void bdrv_parent_cb_resize(BlockDriverState *bs) { BdrvChild *c; QLIST_FOREACH(c, &bs->parents, next_parent) { - if (c->role->resize) { - c->role->resize(c); + if (c->klass->resize) { + c->klass->resize(c); } } } diff --git a/block/mirror.c b/block/mirror.c index aca95c9bc9..e8e8844afc 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -872,6 +872,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; int64_t length; + int64_t target_length; BlockDriverInfo bdi; char backing_filename[2]; /* we only need 2 characters because we are only checking for a NULL string */ @@ -887,24 +888,26 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) goto immediate_exit; } + target_length = blk_getlength(s->target); + if (target_length < 0) { + ret = target_length; + goto immediate_exit; + } + /* Active commit must resize the base image if its size differs from the * active layer. */ if (s->base == blk_bs(s->target)) { - int64_t base_length; - - base_length = blk_getlength(s->target); - if (base_length < 0) { - ret = base_length; - goto immediate_exit; - } - - if (s->bdev_length > base_length) { + if (s->bdev_length > target_length) { ret = blk_truncate(s->target, s->bdev_length, false, PREALLOC_MODE_OFF, 0, NULL); if (ret < 0) { goto immediate_exit; } } + } else if (s->bdev_length != target_length) { + error_setg(errp, "Source and target image have different sizes"); + ret = -EINVAL; + goto immediate_exit; } if (s->bdev_length == 0) { @@ -1489,7 +1492,7 @@ static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) } static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) @@ -1527,6 +1530,8 @@ static BlockDriver bdrv_mirror_top = { .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, .bdrv_child_perm = bdrv_mirror_top_child_perm, + + .is_filter = true, }; static BlockJob *mirror_start_job( diff --git a/block/parallels.c b/block/parallels.c index e7717c508e..63a1cde8af 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -739,8 +739,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; char *buf; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -912,12 +912,13 @@ static BlockDriver bdrv_parallels = { .bdrv_probe = parallels_probe, .bdrv_open = parallels_open, .bdrv_close = parallels_close, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_block_status = parallels_co_block_status, .bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_co_flush_to_os = parallels_co_flush_to_os, .bdrv_co_readv = parallels_co_readv, .bdrv_co_writev = parallels_co_writev, + .is_format = true, .supports_backing = true, .bdrv_co_create = parallels_co_create, .bdrv_co_create_opts = parallels_co_create_opts, diff --git a/block/qcow.c b/block/qcow.c index b0475b73a5..ee5d35fe20 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -130,8 +130,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, qdict_extract_subqdict(options, &encryptopts, "encrypt."); encryptfmt = qdict_get_try_str(encryptopts, "format"); - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { ret = -EINVAL; goto fail; @@ -1180,11 +1180,12 @@ static BlockDriver bdrv_qcow = { .bdrv_probe = qcow_probe, .bdrv_open = qcow_open, .bdrv_close = qcow_close, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_reopen_prepare = qcow_reopen_prepare, .bdrv_co_create = qcow_co_create, .bdrv_co_create_opts = qcow_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, + .is_format = true, .supports_backing = true, .bdrv_refresh_limits = qcow_refresh_limits, diff --git a/block/qcow2.c b/block/qcow2.c index ad9ab4fafa..fe0ce39799 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1590,7 +1590,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, } /* Open external data file */ - s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_file, + s->data_file = bdrv_open_child(NULL, options, "data-file", bs, + &child_of_bds, BDRV_CHILD_DATA, true, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1601,8 +1602,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { if (!s->data_file && s->image_data_file) { s->data_file = bdrv_open_child(s->image_data_file, options, - "data-file", bs, &child_file, - false, errp); + "data-file", bs, &child_of_bds, + BDRV_CHILD_DATA, false, errp); if (!s->data_file) { ret = -EINVAL; goto fail; @@ -1613,6 +1614,12 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, ret = -EINVAL; goto fail; } + + /* No data here */ + bs->file->role &= ~BDRV_CHILD_DATA; + + /* Must succeed because we have given up permissions if anything */ + bdrv_child_refresh_perms(bs, bs->file, &error_abort); } else { if (s->data_file) { error_setg(errp, "'data-file' can only be set for images with an " @@ -1863,8 +1870,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, .ret = -EINPROGRESS }; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -5737,7 +5744,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_reopen_commit_post = qcow2_reopen_commit_post, .bdrv_reopen_abort = qcow2_reopen_abort, .bdrv_join_options = qcow2_join_options, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create_opts = qcow2_co_create_opts, .bdrv_co_create = qcow2_co_create, .bdrv_has_zero_init = qcow2_has_zero_init, @@ -5767,6 +5774,7 @@ BlockDriver bdrv_qcow2 = { .bdrv_save_vmstate = qcow2_save_vmstate, .bdrv_load_vmstate = qcow2_load_vmstate, + .is_format = true, .supports_backing = true, .bdrv_change_backing_file = qcow2_change_backing_file, diff --git a/block/qed.c b/block/qed.c index 5da9726518..c0c65015c7 100644 --- a/block/qed.c +++ b/block/qed.c @@ -547,8 +547,8 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, .ret = -EINPROGRESS }; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -1665,13 +1665,14 @@ static BlockDriver bdrv_qed = { .format_name = "qed", .instance_size = sizeof(BDRVQEDState), .create_opts = &qed_create_opts, + .is_format = true, .supports_backing = true, .bdrv_probe = bdrv_qed_probe, .bdrv_open = bdrv_qed_open, .bdrv_close = bdrv_qed_close, .bdrv_reopen_prepare = bdrv_qed_reopen_prepare, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create = bdrv_qed_co_create, .bdrv_co_create_opts = bdrv_qed_co_create_opts, .bdrv_has_zero_init = bdrv_has_zero_init_1, diff --git a/block/quorum.c b/block/quorum.c index 6d7a56bd93..7cf7ab1546 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -977,7 +977,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, assert(ret < 32); s->children[i] = bdrv_open_child(NULL, options, indexstr, bs, - &child_format, false, &local_err); + &child_of_bds, BDRV_CHILD_DATA, false, + &local_err); if (local_err) { ret = -EINVAL; goto close_exit; @@ -1053,7 +1054,8 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, /* We can safely add the child now */ bdrv_ref(child_bs); - child = bdrv_attach_child(bs, child_bs, indexstr, &child_format, errp); + child = bdrv_attach_child(bs, child_bs, indexstr, &child_of_bds, + BDRV_CHILD_DATA, errp); if (child == NULL) { s->next_child_index--; goto out; @@ -1151,7 +1153,7 @@ static char *quorum_dirname(BlockDriverState *bs, Error **errp) } static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) diff --git a/block/raw-format.c b/block/raw-format.c index 9108e43696..018441bddf 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -71,20 +71,13 @@ static QemuOptsList raw_create_opts = { } }; -static int raw_read_options(QDict *options, BlockDriverState *bs, - BDRVRawState *s, Error **errp) +static int raw_read_options(QDict *options, uint64_t *offset, bool *has_size, + uint64_t *size, Error **errp) { Error *local_err = NULL; QemuOpts *opts = NULL; - int64_t real_size = 0; int ret; - real_size = bdrv_getlength(bs->file->bs); - if (real_size < 0) { - error_setg_errno(errp, -real_size, "Could not get image size"); - return real_size; - } - opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); qemu_opts_absorb_qdict(opts, options, &local_err); if (local_err) { @@ -93,64 +86,84 @@ static int raw_read_options(QDict *options, BlockDriverState *bs, goto end; } - s->offset = qemu_opt_get_size(opts, "offset", 0); - if (s->offset > real_size) { - error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " - "size of the containing file (%" PRId64 ")", - s->offset, real_size); - ret = -EINVAL; - goto end; - } + *offset = qemu_opt_get_size(opts, "offset", 0); + *has_size = qemu_opt_find(opts, "size"); + *size = qemu_opt_get_size(opts, "size", 0); - if (qemu_opt_find(opts, "size") != NULL) { - s->size = qemu_opt_get_size(opts, "size", 0); - s->has_size = true; - } else { - s->has_size = false; - s->size = real_size - s->offset; + ret = 0; +end: + qemu_opts_del(opts); + return ret; +} + +static int raw_apply_options(BlockDriverState *bs, BDRVRawState *s, + uint64_t offset, bool has_size, uint64_t size, + Error **errp) +{ + int64_t real_size = 0; + + real_size = bdrv_getlength(bs->file->bs); + if (real_size < 0) { + error_setg_errno(errp, -real_size, "Could not get image size"); + return real_size; } /* Check size and offset */ - if ((real_size - s->offset) < s->size) { + if (offset > real_size) { + error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " + "size of the containing file (%" PRId64 ")", + s->offset, real_size); + return -EINVAL; + } + + if (has_size && (real_size - offset) < size) { error_setg(errp, "The sum of offset (%" PRIu64 ") and size " - "(%" PRIu64 ") has to be smaller or equal to the " - " actual size of the containing file (%" PRId64 ")", - s->offset, s->size, real_size); - ret = -EINVAL; - goto end; + "(%" PRIu64 ") has to be smaller or equal to the " + " actual size of the containing file (%" PRId64 ")", + s->offset, s->size, real_size); + return -EINVAL; } /* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent rounding * up and leaking out of the specified area. */ - if (s->has_size && !QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) { + if (has_size && !QEMU_IS_ALIGNED(size, BDRV_SECTOR_SIZE)) { error_setg(errp, "Specified size is not multiple of %llu", - BDRV_SECTOR_SIZE); - ret = -EINVAL; - goto end; + BDRV_SECTOR_SIZE); + return -EINVAL; } - ret = 0; + s->offset = offset; + s->has_size = has_size; + s->size = has_size ? size : real_size - offset; -end: - - qemu_opts_del(opts); - - return ret; + return 0; } static int raw_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { + bool has_size; + uint64_t offset, size; + int ret; + assert(reopen_state != NULL); assert(reopen_state->bs != NULL); reopen_state->opaque = g_new0(BDRVRawState, 1); - return raw_read_options( - reopen_state->options, - reopen_state->bs, - reopen_state->opaque, - errp); + ret = raw_read_options(reopen_state->options, &offset, &has_size, &size, + errp); + if (ret < 0) { + return ret; + } + + ret = raw_apply_options(reopen_state->bs, reopen_state->opaque, + offset, has_size, size, errp); + if (ret < 0) { + return ret; + } + + return 0; } static void raw_reopen_commit(BDRVReopenState *state) @@ -426,10 +439,28 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVRawState *s = bs->opaque; + bool has_size; + uint64_t offset, size; + BdrvChildRole file_role; int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + ret = raw_read_options(options, &offset, &has_size, &size, errp); + if (ret < 0) { + return ret; + } + + /* + * Without offset and a size limit, this driver behaves very much + * like a filter. With any such limit, it does not. + */ + if (offset || has_size) { + file_role = BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY; + } else { + file_role = BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY; + } + + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + file_role, false, errp); if (!bs->file) { return -EINVAL; } @@ -455,7 +486,7 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, bs->file->bs->filename); } - ret = raw_read_options(options, bs, s, errp); + ret = raw_apply_options(bs, s, offset, has_size, size, errp); if (ret < 0) { return ret; } @@ -555,7 +586,7 @@ BlockDriver bdrv_raw = { .bdrv_reopen_commit = &raw_reopen_commit, .bdrv_reopen_abort = &raw_reopen_abort, .bdrv_open = &raw_open, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create_opts = &raw_co_create_opts, .bdrv_co_preadv = &raw_co_preadv, .bdrv_co_pwritev = &raw_co_pwritev, @@ -566,6 +597,7 @@ BlockDriver bdrv_raw = { .bdrv_co_copy_range_to = &raw_co_copy_range_to, .bdrv_co_truncate = &raw_co_truncate, .bdrv_getlength = &raw_getlength, + .is_format = true, .has_variable_length = true, .bdrv_measure = &raw_measure, .bdrv_get_info = &raw_get_info, diff --git a/block/replication.c b/block/replication.c index 971f0fe266..ccf7b78160 100644 --- a/block/replication.c +++ b/block/replication.c @@ -90,7 +90,8 @@ static int replication_open(BlockDriverState *bs, QDict *options, const char *mode; const char *top_id; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false, errp); if (!bs->file) { return -EINVAL; @@ -163,7 +164,7 @@ static void replication_close(BlockDriverState *bs) } static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) @@ -331,9 +332,8 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) return; } - ret = s->active_disk->bs->drv->bdrv_make_empty(s->active_disk->bs); + ret = bdrv_make_empty(s->active_disk, errp); if (ret < 0) { - error_setg(errp, "Cannot make active disk empty"); return; } @@ -343,9 +343,18 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) return; } - ret = s->hidden_disk->bs->drv->bdrv_make_empty(s->hidden_disk->bs); + BlockBackend *blk = blk_new(qemu_get_current_aio_context(), + BLK_PERM_WRITE, BLK_PERM_ALL); + blk_insert_bs(blk, s->hidden_disk->bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + blk_unref(blk); + return; + } + + ret = blk_make_empty(blk, errp); + blk_unref(blk); if (ret < 0) { - error_setg(errp, "Cannot make hidden disk empty"); return; } } @@ -398,6 +407,8 @@ static void backup_job_cleanup(BlockDriverState *bs) BDRVReplicationState *s = bs->opaque; BlockDriverState *top_bs; + s->backup_job = NULL; + top_bs = bdrv_lookup_bs(s->top_id, s->top_id, NULL); if (!top_bs) { return; diff --git a/block/throttle.c b/block/throttle.c index 71f4bb0ad1..0ebbad0743 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -81,8 +81,9 @@ static int throttle_open(BlockDriverState *bs, QDict *options, char *group; int ret; - bs->file = bdrv_open_child(NULL, options, "file", bs, - &child_file, false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, + false, errp); if (!bs->file) { return -EINVAL; } @@ -236,7 +237,7 @@ static BlockDriver bdrv_throttle = { .bdrv_close = throttle_close, .bdrv_co_flush = throttle_co_flush, - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_getlength = throttle_getlength, diff --git a/block/vdi.c b/block/vdi.c index 2d28046615..2f506a01ba 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -378,8 +378,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, Error *local_err = NULL; QemuUUID uuid_link, uuid_parent; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -1039,7 +1039,7 @@ static BlockDriver bdrv_vdi = { .bdrv_open = vdi_open, .bdrv_close = vdi_close, .bdrv_reopen_prepare = vdi_reopen_prepare, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create = vdi_co_create, .bdrv_co_create_opts = vdi_co_create_opts, .bdrv_has_zero_init = vdi_has_zero_init, @@ -1053,6 +1053,7 @@ static BlockDriver bdrv_vdi = { .bdrv_get_info = vdi_get_info, + .is_format = true, .create_opts = &vdi_create_opts, .bdrv_co_check = vdi_co_check, }; diff --git a/block/vhdx.c b/block/vhdx.c index 53e756438a..fa9e544a5e 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -996,8 +996,8 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, uint64_t signature; Error *local_err = NULL; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -2245,7 +2245,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_open = vhdx_open, .bdrv_close = vhdx_close, .bdrv_reopen_prepare = vhdx_reopen_prepare, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_readv = vhdx_co_readv, .bdrv_co_writev = vhdx_co_writev, .bdrv_co_create = vhdx_co_create, @@ -2254,6 +2254,7 @@ static BlockDriver bdrv_vhdx = { .bdrv_co_check = vhdx_co_check, .bdrv_has_zero_init = vhdx_has_zero_init, + .is_format = true, .create_opts = &vhdx_create_opts, }; diff --git a/block/vmdk.c b/block/vmdk.c index b18f128816..62da465126 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1089,6 +1089,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, char *desc_file_dir = NULL; char *extent_path; BdrvChild *extent_file; + BdrvChildRole extent_role; BDRVVmdkState *s = bs->opaque; VmdkExtent *extent; char extent_opt_prefix[32]; @@ -1151,8 +1152,15 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents); assert(ret < 32); + extent_role = BDRV_CHILD_DATA; + if (strcmp(type, "FLAT") != 0 && strcmp(type, "VMFS") != 0) { + /* non-flat extents have metadata */ + extent_role |= BDRV_CHILD_METADATA; + } + extent_file = bdrv_open_child(extent_path, options, extent_opt_prefix, - bs, &child_file, false, &local_err); + bs, &child_of_bds, extent_role, false, + &local_err); g_free(extent_path); if (local_err) { error_propagate(errp, local_err); @@ -1257,8 +1265,8 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, uint32_t magic; Error *local_err = NULL; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -1277,6 +1285,12 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, s->desc_offset = 0x200; break; default: + /* No data in the descriptor file */ + bs->file->role &= ~BDRV_CHILD_DATA; + + /* Must succeed because we have given up permissions if anything */ + bdrv_child_refresh_perms(bs, bs->file, &error_abort); + ret = vmdk_open_desc_file(bs, flags, buf, options, errp); break; } @@ -3053,7 +3067,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_open = vmdk_open, .bdrv_co_check = vmdk_co_check, .bdrv_reopen_prepare = vmdk_reopen_prepare, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_preadv = vmdk_co_preadv, .bdrv_co_pwritev = vmdk_co_pwritev, .bdrv_co_pwritev_compressed = vmdk_co_pwritev_compressed, @@ -3070,6 +3084,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_get_info = vmdk_get_info, .bdrv_gather_child_options = vmdk_gather_child_options, + .is_format = true, .supports_backing = true, .create_opts = &vmdk_create_opts, }; diff --git a/block/vpc.c b/block/vpc.c index 5e31dd1e47..c055591641 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -228,8 +228,8 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, int ret; int64_t bs_size; - bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, - false, errp); + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds, + BDRV_CHILD_IMAGE, false, errp); if (!bs->file) { return -EINVAL; } @@ -1240,7 +1240,7 @@ static BlockDriver bdrv_vpc = { .bdrv_open = vpc_open, .bdrv_close = vpc_close, .bdrv_reopen_prepare = vpc_reopen_prepare, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, .bdrv_co_create = vpc_co_create, .bdrv_co_create_opts = vpc_co_create_opts, @@ -1250,6 +1250,7 @@ static BlockDriver bdrv_vpc = { .bdrv_get_info = vpc_get_info, + .is_format = true, .create_opts = &vpc_create_opts, .bdrv_has_zero_init = vpc_has_zero_init, .strong_runtime_opts = vpc_strong_runtime_opts, diff --git a/block/vvfat.c b/block/vvfat.c index 6d5c090dec..c65a98e3ee 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -2960,9 +2960,7 @@ static int do_commit(BDRVVVFATState* s) return ret; } - if (s->qcow->bs->drv && s->qcow->bs->drv->bdrv_make_empty) { - s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs); - } + bdrv_make_empty(s->qcow, NULL); memset(s->used_clusters, 0, sector2cluster(s, s->sector_count)); @@ -3130,7 +3128,8 @@ static BlockDriver vvfat_write_target = { .bdrv_co_pwritev = write_target_commit, }; -static void vvfat_qcow_options(int *child_flags, QDict *child_options, +static void vvfat_qcow_options(BdrvChildRole role, bool parent_is_format, + int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options) { qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off"); @@ -3138,7 +3137,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); } -static const BdrvChildRole child_vvfat_qcow = { +static const BdrvChildClass child_vvfat_qcow = { .parent_is_bds = true, .inherit_options = vvfat_qcow_options, }; @@ -3185,7 +3184,9 @@ static int enable_write_target(BlockDriverState *bs, Error **errp) options = qdict_new(); qdict_put_str(options, "write-target.driver", "qcow"); s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, - &child_vvfat_qcow, false, errp); + &child_vvfat_qcow, + BDRV_CHILD_DATA | BDRV_CHILD_METADATA, + false, errp); qobject_unref(options); if (!s->qcow) { ret = -EINVAL; @@ -3212,14 +3213,14 @@ err: } static void vvfat_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) { BDRVVVFATState *s = bs->opaque; - assert(c == s->qcow || role == &child_backing); + assert(c == s->qcow || (role & BDRV_CHILD_COW)); if (c == s->qcow) { /* This is a private node, nobody should try to attach to it */ diff --git a/blockjob.c b/blockjob.c index 2affa1844d..470facfd47 100644 --- a/blockjob.c +++ b/blockjob.c @@ -163,7 +163,7 @@ static void child_job_set_aio_ctx(BdrvChild *c, AioContext *ctx, job->job.aio_context = ctx; } -static const BdrvChildRole child_job = { +static const BdrvChildClass child_job = { .get_parent_desc = child_job_get_parent_desc, .drained_begin = child_job_drained_begin, .drained_poll = child_job_drained_poll, @@ -217,8 +217,9 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, if (job->job.aio_context != qemu_get_aio_context()) { aio_context_release(job->job.aio_context); } - c = bdrv_root_attach_child(bs, name, &child_job, job->job.aio_context, - perm, shared_perm, job, errp); + c = bdrv_root_attach_child(bs, name, &child_job, 0, + job->job.aio_context, perm, shared_perm, job, + errp); if (job->job.aio_context != qemu_get_aio_context()) { aio_context_acquire(job->job.aio_context); } diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index a6cdb4fb7b..6a0221a681 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -240,7 +240,7 @@ static void sbsa_flash_map1(PFlashCFI01 *flash, { DeviceState *dev = DEVICE(flash); - assert(size % SBSA_FLASH_SECTOR_SIZE == 0); + assert(QEMU_IS_ALIGNED(size, SBSA_FLASH_SECTOR_SIZE)); assert(size / SBSA_FLASH_SECTOR_SIZE <= UINT32_MAX); qdev_prop_set_uint32(dev, "num-blocks", size / SBSA_FLASH_SECTOR_SIZE); qdev_init_nofail(dev); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c41d5f9778..37462a6f78 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -977,7 +977,7 @@ static void virt_flash_map1(PFlashCFI01 *flash, { DeviceState *dev = DEVICE(flash); - assert(size % VIRT_FLASH_SECTOR_SIZE == 0); + assert(QEMU_IS_ALIGNED(size, VIRT_FLASH_SECTOR_SIZE)); assert(size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); qdev_prop_set_uint32(dev, "num-blocks", size / VIRT_FLASH_SECTOR_SIZE); qdev_init_nofail(dev); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index 24f3bce7ef..8e8887253d 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -965,7 +965,7 @@ PFlashCFI01 *pflash_cfi01_register(hwaddr base, if (blk) { qdev_prop_set_drive(dev, "drive", blk, &error_abort); } - assert(size % sector_len == 0); + assert(QEMU_IS_ALIGNED(size, sector_len)); qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); qdev_prop_set_uint64(dev, "sector-length", sector_len); qdev_prop_set_uint8(dev, "width", bank_width); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index f0579ecb17..c277b0309d 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -997,7 +997,7 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, if (blk) { qdev_prop_set_drive(dev, "drive", blk, &error_abort); } - assert(size % sector_len == 0); + assert(QEMU_IS_ALIGNED(size, sector_len)); qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); qdev_prop_set_uint32(dev, "sector-length", sector_len); qdev_prop_set_uint8(dev, "width", width); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 2abab3a27c..b8d8ef59eb 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -167,7 +167,7 @@ static void pc_system_flash_map(PCMachineState *pcms, blk_name(blk), strerror(-size)); exit(1); } - if (size == 0 || size % FLASH_SECTOR_SIZE != 0) { + if (size == 0 || !QEMU_IS_ALIGNED(size, FLASH_SECTOR_SIZE)) { error_report("system firmware block device %s has invalid size " "%" PRId64, blk_name(blk), size); diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 13d91e109a..fc82cbd5f1 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1509,6 +1509,7 @@ static void ahci_cmd_done(IDEDMA *dma) static void ahci_irq_set(void *opaque, int n, int level) { + qemu_log_mask(LOG_UNIMP, "ahci: IRQ#%d level:%d\n", n, level); } static const IDEDMAOps ahci_dma_ops = { diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index c695a44979..7ce28895bc 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -111,7 +111,7 @@ static void virt_flash_map1(PFlashCFI01 *flash, { DeviceState *dev = DEVICE(flash); - assert(size % VIRT_FLASH_SECTOR_SIZE == 0); + assert(QEMU_IS_ALIGNED(size, VIRT_FLASH_SECTOR_SIZE)); assert(size / VIRT_FLASH_SECTOR_SIZE <= UINT32_MAX); qdev_prop_set_uint32(dev, "num-blocks", size / VIRT_FLASH_SECTOR_SIZE); qdev_init_nofail(dev); diff --git a/include/block/block.h b/include/block/block.h index 4de8d8f8a6..25e299605e 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -13,7 +13,7 @@ /* block.c */ typedef struct BlockDriver BlockDriver; typedef struct BdrvChild BdrvChild; -typedef struct BdrvChildRole BdrvChildRole; +typedef struct BdrvChildClass BdrvChildClass; typedef struct BlockDriverInfo { /* in bytes, 0 if irrelevant */ @@ -268,6 +268,62 @@ enum { DEFAULT_PERM_UNCHANGED = BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH, }; +/* + * Flags that parent nodes assign to child nodes to specify what kind of + * role(s) they take. + * + * At least one of DATA, METADATA, FILTERED, or COW must be set for + * every child. + */ +enum BdrvChildRoleBits { + /* + * This child stores data. + * Any node may have an arbitrary number of such children. + */ + BDRV_CHILD_DATA = (1 << 0), + + /* + * This child stores metadata. + * Any node may have an arbitrary number of metadata-storing + * children. + */ + BDRV_CHILD_METADATA = (1 << 1), + + /* + * A child that always presents exactly the same visible data as + * the parent, e.g. by virtue of the parent forwarding all reads + * and writes. + * This flag is mutually exclusive with DATA, METADATA, and COW. + * Any node may have at most one filtered child at a time. + */ + BDRV_CHILD_FILTERED = (1 << 2), + + /* + * Child from which to read all data that isn’t allocated in the + * parent (i.e., the backing child); such data is copied to the + * parent through COW (and optionally COR). + * This field is mutually exclusive with DATA, METADATA, and + * FILTERED. + * Any node may have at most one such backing child at a time. + */ + BDRV_CHILD_COW = (1 << 3), + + /* + * The primary child. For most drivers, this is the child whose + * filename applies best to the parent node. + * Any node may have at most one primary child at a time. + */ + BDRV_CHILD_PRIMARY = (1 << 4), + + /* Useful combination of flags */ + BDRV_CHILD_IMAGE = BDRV_CHILD_DATA + | BDRV_CHILD_METADATA + | BDRV_CHILD_PRIMARY, +}; + +/* Mask of BdrvChildRoleBits values */ +typedef unsigned int BdrvChildRole; + char *bdrv_perm_names(uint64_t perm); uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm); @@ -296,7 +352,8 @@ int bdrv_parse_discard_flags(const char *mode, int *flags); BdrvChild *bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState* parent, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, Error **errp); BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, @@ -352,6 +409,7 @@ BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts, void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); void bdrv_refresh_limits(BlockDriverState *bs, Error **errp); int bdrv_commit(BlockDriverState *bs); +int bdrv_make_empty(BdrvChild *c, Error **errp); int bdrv_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt); void bdrv_register(BlockDriver *bdrv); @@ -540,7 +598,8 @@ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child); BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, const char *child_name, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, Error **errp); bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp); diff --git a/include/block/block_int.h b/include/block/block_int.h index 7ba8c89036..5e4f4c348c 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -95,6 +95,13 @@ struct BlockDriver { * must implement them and return -ENOTSUP. */ bool is_filter; + /* + * Set to true if the BlockDriver is a format driver. Format nodes + * generally do not expect their children to be other format nodes + * (except for backing files), and so format probing is disabled + * on those children. + */ + bool is_format; /* * Return true if @to_replace can be replaced by a BDS with the * same data as @bs without it affecting @bs's behavior (that is, @@ -549,14 +556,14 @@ struct BlockDriver { * the parents in @parent_perm and @parent_shared. * * If @c is NULL, return the permissions for attaching a new child for the - * given @role. + * given @child_class and @role. * * If @reopen_queue is non-NULL, don't return the currently needed * permissions, but those that will be needed after applying the * @reopen_queue. */ void (*bdrv_child_perm)(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t parent_perm, uint64_t parent_shared, uint64_t *nperm, uint64_t *nshared); @@ -658,7 +665,7 @@ typedef struct BdrvAioNotifier { QLIST_ENTRY(BdrvAioNotifier) list; } BdrvAioNotifier; -struct BdrvChildRole { +struct BdrvChildClass { /* If true, bdrv_replace_node() doesn't change the node this BdrvChild * points to. */ bool stay_at_node; @@ -669,7 +676,8 @@ struct BdrvChildRole { * non-BDS parents. */ bool parent_is_bds; - void (*inherit_options)(int *child_flags, QDict *child_options, + void (*inherit_options)(BdrvChildRole role, bool parent_is_format, + int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options); void (*change_media)(BdrvChild *child, bool load); @@ -731,14 +739,13 @@ struct BdrvChildRole { void (*set_aio_ctx)(BdrvChild *child, AioContext *ctx, GSList **ignore); }; -extern const BdrvChildRole child_file; -extern const BdrvChildRole child_format; -extern const BdrvChildRole child_backing; +extern const BdrvChildClass child_of_bds; struct BdrvChild { BlockDriverState *bs; char *name; - const BdrvChildRole *role; + const BdrvChildClass *klass; + BdrvChildRole role; void *opaque; /** @@ -765,7 +772,7 @@ struct BdrvChild { /* * How many times the parent of this child has been drained - * (through role->drained_*). + * (through klass->drained_*). * Usually, this is equal to bs->quiesce_counter (potentially * reduced by bdrv_drain_all_count). It may differ while the * child is entering or leaving a drained section. @@ -1225,7 +1232,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, - const BdrvChildRole *child_role, + const BdrvChildClass *child_class, + BdrvChildRole child_role, AioContext *ctx, uint64_t perm, uint64_t shared_perm, void *opaque, Error **errp); @@ -1252,28 +1260,19 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared, */ int bdrv_child_refresh_perms(BlockDriverState *bs, BdrvChild *c, Error **errp); -/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by - * block filters: Forward CONSISTENT_READ, WRITE, WRITE_UNCHANGED and RESIZE to - * all children */ -void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared); - -/* Default implementation for BlockDriver.bdrv_child_perm() that can be used by - * (non-raw) image formats: Like above for bs->backing, but for bs->file it - * requires WRITE | RESIZE for read-write images, always requires - * CONSISTENT_READ and doesn't share WRITE. */ -void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared); - bool bdrv_recurse_can_replace(BlockDriverState *bs, BlockDriverState *to_replace); +/* + * Default implementation for BlockDriver.bdrv_child_perm() that can + * be used by block filters and image formats, as long as they use the + * child_of_bds child class and set an appropriate BdrvChildRole. + */ +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); + /* * Default implementation for drivers to pass bdrv_co_block_status() to * their file. diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 0917663d89..8203d7f6f9 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -266,4 +266,6 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, const BdrvChild *blk_root(BlockBackend *blk); +int blk_make_empty(BlockBackend *blk, Error **errp); + #endif diff --git a/qemu-img.c b/qemu-img.c index 947bf8b34b..2a9186139d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1108,11 +1108,20 @@ static int img_commit(int argc, char **argv) goto unref_backing; } - if (!drop && bs->drv->bdrv_make_empty) { - ret = bs->drv->bdrv_make_empty(bs); - if (ret) { - error_setg_errno(&local_err, -ret, "Could not empty %s", - filename); + if (!drop) { + BlockBackend *old_backing_blk; + + old_backing_blk = blk_new_with_bs(bs, BLK_PERM_WRITE, BLK_PERM_ALL, + &local_err); + if (!old_backing_blk) { + goto unref_backing; + } + ret = blk_make_empty(old_backing_blk, &local_err); + blk_unref(old_backing_blk); + if (ret == -ENOTSUP) { + error_free(local_err); + local_err = NULL; + } else if (ret < 0) { goto unref_backing; } } diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 104e3cee1b..1cdd7e2999 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -354,14 +354,14 @@ class TestParallelOps(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "Node 'node5' is busy: block device is in use by block job: commit") + result = self.vm.qmp('block-job-set-speed', device='commit-drive0', speed=0) + self.assert_qmp(result, 'return', {}) + event = self.vm.event_wait(name='BLOCK_JOB_READY') self.assert_qmp(event, 'data/device', 'commit-drive0') self.assert_qmp(event, 'data/type', 'commit') self.assert_qmp_absent(event, 'data/error') - result = self.vm.qmp('block-job-set-speed', device='commit-drive0', speed=0) - self.assert_qmp(result, 'return', {}) - result = self.vm.qmp('block-job-complete', device='commit-drive0') self.assert_qmp(result, 'return', {}) diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 1812dd8479..601c756117 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -240,6 +240,49 @@ class TestSingleBlockdev(TestSingleDrive): target=self.qmp_target) self.assert_qmp(result, 'error/class', 'GenericError') + def do_test_resize(self, device, node): + def pre_finalize(): + if device: + result = self.vm.qmp('block_resize', device=device, size=65536) + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp('block_resize', node_name=node, size=65536) + self.assert_qmp(result, 'error/class', 'GenericError') + + result = self.vm.qmp(self.qmp_cmd, job_id='job0', device='drive0', + sync='full', target=self.qmp_target, + auto_finalize=False, auto_dismiss=False) + self.assert_qmp(result, 'return', {}) + + result = self.vm.run_job('job0', auto_finalize=False, + pre_finalize=pre_finalize) + self.assertEqual(result, None) + + def test_source_resize(self): + self.do_test_resize('drive0', 'top') + + def test_target_resize(self): + self.do_test_resize(None, self.qmp_target) + + def do_test_target_size(self, size): + result = self.vm.qmp('block_resize', node_name=self.qmp_target, + size=size) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp(self.qmp_cmd, job_id='job0', + device='drive0', sync='full', auto_dismiss=False, + target=self.qmp_target) + self.assert_qmp(result, 'return', {}) + + result = self.vm.run_job('job0') + self.assertEqual(result, 'Source and target image have different sizes') + + def test_small_target(self): + self.do_test_target_size(self.image_len // 2) + + def test_large_target(self): + self.do_test_target_size(self.image_len * 2) + test_large_cluster = None test_image_not_found = None test_small_buffer2 = None @@ -251,6 +294,8 @@ class TestSingleDriveZeroLength(TestSingleDrive): class TestSingleBlockdevZeroLength(TestSingleBlockdev): image_len = 0 + test_small_target = None + test_large_target = None class TestSingleDriveUnalignedLength(TestSingleDrive): image_len = 1025 * 1024 diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index 877b76fd31..53abe11d73 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -.............................................................................................. +........................................................................................................ ---------------------------------------------------------------------- -Ran 94 tests +Ran 104 tests OK diff --git a/tests/qemu-iotests/098.out b/tests/qemu-iotests/098.out index 7634d0e8b0..23cf371f53 100644 --- a/tests/qemu-iotests/098.out +++ b/tests/qemu-iotests/098.out @@ -6,7 +6,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error No errors were found on the image. === empty_image_prepare === @@ -15,7 +15,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error Leaked cluster 4 refcount=1 reference=0 Leaked cluster 5 refcount=1 reference=0 Repairing cluster 4 refcount=1 reference=0 @@ -28,7 +28,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error ERROR cluster 0 refcount=0 reference=1 ERROR cluster 1 refcount=0 reference=1 ERROR cluster 3 refcount=0 reference=1 @@ -42,7 +42,7 @@ Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base wrote 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -qemu-img: Could not empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error +qemu-img: Failed to empty blkdebug:TEST_DIR/blkdebug.conf:TEST_DIR/t.IMGFMT: Input/output error ERROR cluster 0 refcount=0 reference=1 ERROR cluster 1 refcount=0 reference=1 ERROR cluster 3 refcount=0 reference=1 diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109 index 5bc2e9b001..3ffeaf3c55 100755 --- a/tests/qemu-iotests/109 +++ b/tests/qemu-iotests/109 @@ -77,14 +77,14 @@ for fmt in qcow qcow2 qed vdi vmdk vpc; do echo "=== Writing a $fmt header into raw ===" echo - _make_test_img 64M TEST_IMG="$TEST_IMG.src" IMGFMT=$fmt _make_test_img 64M + _make_test_img $(du -b "$TEST_IMG.src" | cut -f1) | _filter_img_create_size # This first test should fail: The image format was probed, we may not # write an image header at the start of the image run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" | _filter_block_job_len - $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c 'read -P 0 0 512' "$TEST_IMG" | _filter_qemu_io # When raw was explicitly specified, the same must succeed @@ -103,12 +103,12 @@ for sample_img in empty.bochs iotest-dirtylog-10G-4M.vhdx parallels-v1 \ # Can't use _use_sample_img because that isn't designed to be used multiple # times and it overwrites $TEST_IMG (both breaks cleanup) - _make_test_img 64M bzcat "$SAMPLE_IMG_DIR/$sample_img.bz2" > "$TEST_IMG.src" + _make_test_img $(du -b "$TEST_IMG.src" | cut -f1) | _filter_img_create_size run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_ERROR" | _filter_block_job_offset | _filter_block_job_len - $QEMU_IO -c 'read -P 0 0 64k' "$TEST_IMG" | _filter_qemu_io + $QEMU_IO -c 'read -P 0 0 512' "$TEST_IMG" | _filter_qemu_io run_qemu "$TEST_IMG" "$TEST_IMG.src" "'format': 'raw'," "BLOCK_JOB_READY" $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" @@ -119,8 +119,8 @@ echo "=== Write legitimate MBR into raw ===" echo for sample_img in grub_mbr.raw; do - _make_test_img 64M bzcat "$SAMPLE_IMG_DIR/$sample_img.bz2" > "$TEST_IMG.src" + _make_test_img $(du -b "$TEST_IMG.src" | cut -f1) | _filter_img_create_size run_qemu "$TEST_IMG" "$TEST_IMG.src" "" "BLOCK_JOB_READY" $QEMU_IMG compare -f raw -F raw "$TEST_IMG" "$TEST_IMG.src" diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out index 884f65f18d..ad739df46c 100644 --- a/tests/qemu-iotests/109.out +++ b/tests/qemu-iotests/109.out @@ -2,8 +2,8 @@ QA output created by 109 === Writing a qcow header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -23,8 +23,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -43,13 +43,12 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Writing a qcow2 header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -69,8 +68,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -89,13 +88,12 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Writing a qed header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -115,8 +113,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -135,13 +133,12 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Writing a vdi header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -161,8 +158,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -181,13 +178,12 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Writing a vmdk header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -207,8 +203,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -227,13 +223,12 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Writing a vpc header into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -253,8 +248,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -273,12 +268,11 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Copying sample image empty.bochs into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -298,8 +292,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -318,12 +312,11 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Copying sample image iotest-dirtylog-10G-4M.vhdx into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -343,8 +336,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -363,12 +356,11 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Copying sample image parallels-v1 into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -388,8 +380,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -408,12 +400,11 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Copying sample image simple-pattern.cloop into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -433,8 +424,8 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"execute":"quit"} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} -read 65536/65536 bytes at offset 0 -64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'format': 'IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -453,12 +444,11 @@ read 65536/65536 bytes at offset 0 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. === Write legitimate MBR into raw === -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=SIZE { 'execute': 'qmp_capabilities' } {"return": {}} {'execute':'drive-mirror', 'arguments':{ 'device': 'src', 'target': 'TEST_DIR/t.IMGFMT', 'mode': 'existing', 'sync': 'full'}} @@ -480,7 +470,6 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. { 'execute': 'qmp_capabilities' } {"return": {}} @@ -500,6 +489,5 @@ Images are identical. {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} -Warning: Image size mismatch! Images are identical. *** done diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 index 866168b236..99acb55ebb 100755 --- a/tests/qemu-iotests/229 +++ b/tests/qemu-iotests/229 @@ -33,6 +33,7 @@ _cleanup() _cleanup_test_img _rm_test_img "$TEST_IMG" _rm_test_img "$DEST_IMG" + rm -f "$TEST_DIR/blkdebug.conf" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -49,11 +50,10 @@ _supported_os Linux DEST_IMG="$TEST_DIR/d.$IMGFMT" TEST_IMG="$TEST_DIR/b.$IMGFMT" +BLKDEBUG_CONF="$TEST_DIR/blkdebug.conf" _make_test_img 2M - -# destination for mirror will be too small, causing error -TEST_IMG=$DEST_IMG _make_test_img 1M +TEST_IMG=$DEST_IMG _make_test_img 2M $QEMU_IO -c 'write 0 2M' "$TEST_IMG" | _filter_qemu_io @@ -67,11 +67,18 @@ echo echo '=== Starting drive-mirror, causing error & stop ===' echo +cat > "$BLKDEBUG_CONF" <. + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +# get standard environment +. ./common.rc + +if ! type -p "pylint-3" > /dev/null; then + _notrun "pylint-3 not found" +fi +if ! type -p "mypy" > /dev/null; then + _notrun "mypy not found" +fi + +pylint-3 --score=n iotests.py + +MYPYPATH=../../python/ mypy --warn-unused-configs --disallow-subclassing-any \ + --disallow-any-generics --disallow-incomplete-defs \ + --disallow-untyped-decorators --no-implicit-optional \ + --warn-redundant-casts --warn-unused-ignores \ + --no-implicit-reexport iotests.py + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/297.out b/tests/qemu-iotests/297.out new file mode 100644 index 0000000000..6acc843649 --- /dev/null +++ b/tests/qemu-iotests/297.out @@ -0,0 +1,3 @@ +QA output created by 297 +Success: no issues found in 1 source file +*** done diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 279e0bbb0d..03e4f71808 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -156,6 +156,11 @@ _filter_img_create() -e "s# compression_type=[a-zA-Z0-9]\\+##g" } +_filter_img_create_size() +{ + $SED -e "s# size=[0-9]\\+# size=SIZE#g" +} + _filter_img_info() { if [[ "$1" == "--format-specific" ]]; then diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 34175fd437..445c26f8d2 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -300,3 +300,4 @@ 289 rw quick 290 rw auto quick 292 rw auto quick +297 meta diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 6c0e781af7..f20d90f969 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -1040,7 +1040,7 @@ def _verify_cache_mode(supported_cache_modes: Sequence[str] = ()) -> None: if supported_cache_modes and (cachemode not in supported_cache_modes): notrun('not suitable for this cache mode: %s' % cachemode) -def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()): +def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()) -> None: if supported_aio_modes and (aiomode not in supported_aio_modes): notrun('not suitable for this aio mode: %s' % aiomode) @@ -1087,7 +1087,8 @@ def skip_if_unsupported(required_formats=(), read_only=False): '''Skip Test Decorator Runs the test if all the required formats are whitelisted''' def skip_test_decorator(func): - def func_wrapper(test_case: QMPTestCase, *args, **kwargs): + def func_wrapper(test_case: QMPTestCase, *args: List[Any], + **kwargs: Dict[str, Any]) -> None: if callable(required_formats): fmts = required_formats(test_case) else: @@ -1097,9 +1098,8 @@ def func_wrapper(test_case: QMPTestCase, *args, **kwargs): if usf_list: msg = f'{test_case}: formats {usf_list} are not whitelisted' test_case.case_skip(msg) - return None else: - return func(test_case, *args, **kwargs) + func(test_case, *args, **kwargs) return func_wrapper return skip_test_decorator @@ -1168,18 +1168,17 @@ def execute_setup_common(supported_fmts: Sequence[str] = (), sys.stderr.write('Please run this test via the "check" script\n') sys.exit(os.EX_USAGE) + debug = '-d' in sys.argv + if debug: + sys.argv.remove('-d') + logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN)) + _verify_image_format(supported_fmts, unsupported_fmts) _verify_protocol(supported_protocols, unsupported_protocols) _verify_platform(supported=supported_platforms) _verify_cache_mode(supported_cache_modes) _verify_aio_mode(supported_aio_modes) - debug = '-d' in sys.argv - if debug: - sys.argv.remove('-d') - logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN)) - logger.debug("iotests debugging messages active") - return debug def execute_test(*args, test_function=None, **kwargs): diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index fa0e6a648b..1107271840 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -85,22 +85,6 @@ static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs, return 0; } -static void bdrv_test_child_perm(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, - BlockReopenQueue *reopen_queue, - uint64_t perm, uint64_t shared, - uint64_t *nperm, uint64_t *nshared) -{ - /* bdrv_format_default_perms() accepts only these two, so disguise - * detach_by_driver_cb_role as one of them. */ - if (role != &child_file && role != &child_backing) { - role = &child_file; - } - - bdrv_format_default_perms(bs, c, role, reopen_queue, perm, shared, - nperm, nshared); -} - static int bdrv_test_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) @@ -118,7 +102,7 @@ static BlockDriver bdrv_test = { .bdrv_co_drain_begin = bdrv_test_co_drain_begin, .bdrv_co_drain_end = bdrv_test_co_drain_end, - .bdrv_child_perm = bdrv_test_child_perm, + .bdrv_child_perm = bdrv_default_perms, .bdrv_change_backing_file = bdrv_test_change_backing_file, }; @@ -1134,7 +1118,7 @@ static BlockDriver bdrv_test_top_driver = { .bdrv_close = bdrv_test_top_close, .bdrv_co_preadv = bdrv_test_top_co_preadv, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, }; typedef struct TestCoDeleteByDrainData { @@ -1200,7 +1184,8 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); - bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1208,14 +1193,17 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, &error_abort); child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; /* Takes our reference to child_bs */ - tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_file, + tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", + &child_of_bds, + BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, &error_abort); /* This child is just there to be deleted * (for detach_instead_of_delete == true) */ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); - bdrv_attach_child(bs, null_bs, "null-child", &child_file, &error_abort); + bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, + &error_abort); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1312,7 +1300,8 @@ static void detach_indirect_bh(void *opaque) bdrv_ref(data->c); data->child_c = bdrv_attach_child(data->parent_b, data->c, "PB-C", - &child_file, &error_abort); + &child_of_bds, BDRV_CHILD_DATA, + &error_abort); } static void detach_by_parent_aio_cb(void *opaque, int ret) @@ -1329,10 +1318,10 @@ static void detach_by_driver_cb_drained_begin(BdrvChild *child) { aio_bh_schedule_oneshot(qemu_get_current_aio_context(), detach_indirect_bh, &detach_by_parent_data); - child_file.drained_begin(child); + child_of_bds.drained_begin(child); } -static BdrvChildRole detach_by_driver_cb_role; +static BdrvChildClass detach_by_driver_cb_class; /* * Initial graph: @@ -1349,7 +1338,7 @@ static BdrvChildRole detach_by_driver_cb_role; * * by_parent_cb == false: Test that bdrv_drain_invoke() doesn't poll * - * PA's BdrvChildRole has a .drained_begin callback that schedules a BH + * PA's BdrvChildClass has a .drained_begin callback that schedules a BH * that does the same graph change. If bdrv_drain_invoke() calls it, the * state is messed up, but if it is only polled in the single * BDRV_POLL_WHILE() at the end of the drain, this should work fine. @@ -1364,8 +1353,8 @@ static void test_detach_indirect(bool by_parent_cb) QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); if (!by_parent_cb) { - detach_by_driver_cb_role = child_file; - detach_by_driver_cb_role.drained_begin = + detach_by_driver_cb_class = child_of_bds; + detach_by_driver_cb_class.drained_begin = detach_by_driver_cb_drained_begin; } @@ -1394,13 +1383,15 @@ static void test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); - child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_file, &error_abort); - child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_backing, &error_abort); + child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); + child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, + BDRV_CHILD_COW, &error_abort); bdrv_ref(a); bdrv_attach_child(parent_a, a, "PA-A", - by_parent_cb ? &child_file : &detach_by_driver_cb_role, - &error_abort); + by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, + BDRV_CHILD_DATA, &error_abort); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1735,7 +1726,7 @@ static int drop_intermediate_poll_update_filename(BdrvChild *child, /** * Test a poll in the midst of bdrv_drop_intermediate(). * - * bdrv_drop_intermediate() calls BdrvChildRole.update_filename(), + * bdrv_drop_intermediate() calls BdrvChildClass.update_filename(), * which can yield or poll. This may lead to graph changes, unless * the whole subtree in question is drained. * @@ -1772,7 +1763,7 @@ static int drop_intermediate_poll_update_filename(BdrvChild *child, * * The solution is for bdrv_drop_intermediate() to drain top's * subtree. This prevents graph changes from happening just because - * BdrvChildRole.update_filename() yields or polls. Thus, the block + * BdrvChildClass.update_filename() yields or polls. Thus, the block * job is paused during that drained section and must finish before or * after. * @@ -1780,7 +1771,7 @@ static int drop_intermediate_poll_update_filename(BdrvChild *child, */ static void test_drop_intermediate_poll(void) { - static BdrvChildRole chain_child_role; + static BdrvChildClass chain_child_class; BlockDriverState *chain[3]; TestSimpleBlockJob *job; BlockDriverState *job_node; @@ -1788,8 +1779,8 @@ static void test_drop_intermediate_poll(void) int i; int ret; - chain_child_role = child_backing; - chain_child_role.update_filename = drop_intermediate_poll_update_filename; + chain_child_class = child_of_bds; + chain_child_class.update_filename = drop_intermediate_poll_update_filename; for (i = 0; i < 3; i++) { char name[32]; @@ -1810,8 +1801,8 @@ static void test_drop_intermediate_poll(void) if (i) { /* Takes the reference to chain[i - 1] */ chain[i]->backing = bdrv_attach_child(chain[i], chain[i - 1], - "chain", &chain_child_role, - &error_abort); + "chain", &chain_child_class, + BDRV_CHILD_COW, &error_abort); } } @@ -1956,7 +1947,7 @@ static BlockDriver bdrv_replace_test = { .bdrv_co_drain_begin = bdrv_replace_test_co_drain_begin, .bdrv_co_drain_end = bdrv_replace_test_co_drain_end, - .bdrv_child_perm = bdrv_format_default_perms, + .bdrv_child_perm = bdrv_default_perms, }; static void coroutine_fn test_replace_child_mid_drain_read_co(void *opaque) @@ -2029,7 +2020,8 @@ static void do_test_replace_child_mid_drain(int old_drain_count, bdrv_ref(old_child_bs); parent_bs->backing = bdrv_attach_child(parent_bs, old_child_bs, "child", - &child_backing, &error_abort); + &child_of_bds, BDRV_CHILD_COW, + &error_abort); for (i = 0; i < old_drain_count; i++) { bdrv_drained_begin(old_child_bs); diff --git a/tests/test-bdrv-graph-mod.c b/tests/test-bdrv-graph-mod.c index a007754d9e..f93f3168b0 100644 --- a/tests/test-bdrv-graph-mod.c +++ b/tests/test-bdrv-graph-mod.c @@ -26,11 +26,11 @@ static BlockDriver bdrv_pass_through = { .format_name = "pass-through", - .bdrv_child_perm = bdrv_filter_default_perms, + .bdrv_child_perm = bdrv_default_perms, }; static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c, - const BdrvChildRole *role, + BdrvChildRole role, BlockReopenQueue *reopen_queue, uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared) @@ -111,7 +111,8 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); - bdrv_attach_child(filter, bs, "child", &child_file, &error_abort); + bdrv_attach_child(filter, bs, "child", &child_of_bds, + BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_append(filter, bs, &local_err); @@ -177,7 +178,8 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); g_assert(target->backing->bs == bs); - bdrv_attach_child(filter, target, "target", &child_file, &error_abort); + bdrv_attach_child(filter, target, "target", &child_of_bds, + BDRV_CHILD_DATA, &error_abort); bdrv_append(filter, bs, &error_abort); g_assert(target->backing->bs == bs); diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c index 71e9bce3b1..a953794be2 100644 --- a/tests/test-block-iothread.c +++ b/tests/test-block-iothread.c @@ -482,8 +482,13 @@ static void test_propagate_basic(void) BlockDriverState *bs_a, *bs_b, *bs_verify; QDict *options; - /* Create bs_a and its BlockBackend */ - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + /* + * Create bs_a and its BlockBackend. We cannot take the RESIZE + * permission because blkverify will not share it on the test + * image. + */ + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, + BLK_PERM_ALL); bs_a = bdrv_new_open_driver(&bdrv_test, "bs_a", BDRV_O_RDWR, &error_abort); blk_insert_bs(blk, bs_a, &error_abort); @@ -566,7 +571,13 @@ static void test_propagate_diamond(void) qdict_put_str(options, "raw", "bs_c"); bs_verify = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort); - blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); + /* + * Do not take the RESIZE permission: This would require the same + * from bs_c and thus from bs_a; however, blkverify will not share + * it on bs_b, and thus it will not be available for bs_a. + */ + blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL & ~BLK_PERM_RESIZE, + BLK_PERM_ALL); blk_insert_bs(blk, bs_verify, &error_abort); /* Switch the AioContext */