Block layer patches for 2.11.0-rc2

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJaDyNMAAoJEH8JsnLIjy/WP+oP/1G0r1b9PBtrEc+B9QG0sMh1
 qivbMRmNUFhuzYW8VBzb2Cpsl/lpe/VlAumf75RUACOh4ZfxkOhaYTMB5x9FHvoa
 5w35UC382zuXTMfeF4JufF5rVJdB+bB9THJOJ7cuaDQSHZNPo8uMmHrk4J5p5uuU
 IrOZLfqgQaWGdobGe6FPWy67z2PRWrzEI/Ogr8zwpa95GJhX3fY1CmGvt2MUBG+j
 cwdYcuX9pawFfjasCkfwYMzY7Uc9wvjIJYSe+WvrDrpjInISgxMOvXylzdWkpsGS
 rHnsLgRXnUE4BmOavzq+uvlo14hlrmdVn5UYlUkbJcx+Bkp7MZGuNjX6doimCdWg
 PpbQB3TIi7ce4v9cYWZSrzDDo6E/d1Evi3xMG0ZouU0ff1DPFIilm8lxb3xYia+b
 ItTLZ7yXWLijGhr4jTeGVhn0+zENiczyiQsL1sDdj83VnpAOGPNZTwtnNGug8ATJ
 VELwA7jlVEL47HYovQoUIFs0NA5xCbOQm5avS5gk44PB/dNkYGKdHhwgQUV/R36A
 lRNj7Vh7hwHSyHO/gKi3sqXTbNG/fTDSy1Nu1xc0pAROIKpc6AanP0mFn2DlZS35
 kyZbxMbuGUsDQ2j0pcGuP60O7+2CY4jlXRCXo6vaHzP3xDfOTx2L9UYVfk/dwNJu
 uYbnbXWv06igjm3Y/2Vv
 =ClZj
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches for 2.11.0-rc2

# gpg: Signature made Fri 17 Nov 2017 17:58:36 GMT
# gpg:                using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (25 commits)
  iotests: Make 087 pass without AIO enabled
  block: Make bdrv_next() keep strong references
  qcow2: Fix overly broad madvise()
  qcow2: Refuse to get unaligned offsets from cache
  qcow2: Add bounds check to get_refblock_offset()
  block: Guard against NULL bs->drv
  qcow2: Unaligned zero cluster in handle_alloc()
  qcow2: check_errors are fatal
  qcow2: reject unaligned offsets in write compressed
  iotests: Add test for failing qemu-img commit
  tests: Add check-qobject for equality tests
  iotests: Add test for non-string option reopening
  block: qobject_is_equal() in bdrv_reopen_prepare()
  qapi: Add qobject_is_equal()
  qapi/qlist: Add qlist_append_null() macro
  qapi/qnull: Add own header
  qcow2: fix image corruption on commit with persistent bitmap
  iotests: test clearing unknown autoclear_features by qcow2
  block: Fix permissions in image activation
  qcow2: fix image corruption after committing qcow2 image into base
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-11-17 19:08:07 +00:00
commit 2e02083438
57 changed files with 1752 additions and 94 deletions

92
block.c
View File

@ -261,6 +261,11 @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
return 0; return 0;
} }
/* TODO Remove (deprecated since 2.11)
* Block drivers are not supposed to automatically change bs->read_only.
* Instead, they should just check whether they can provide what the user
* explicitly requested and error out if read-write is requested, but they can
* only provide read-only access. */
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
{ {
int ret = 0; int ret = 0;
@ -715,6 +720,10 @@ static int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
/* Do not attempt drv->bdrv_getlength() on scsi-generic devices */ /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
if (bdrv_is_sg(bs)) if (bdrv_is_sg(bs))
return 0; return 0;
@ -998,7 +1007,7 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
ret = bdrv_change_backing_file(parent, filename, ret = bdrv_change_backing_file(parent, filename,
base->drv ? base->drv->format_name : ""); base->drv ? base->drv->format_name : "");
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, ret, "Could not update backing file link"); error_setg_errno(errp, -ret, "Could not update backing file link");
} }
if (!(orig_flags & BDRV_O_RDWR)) { if (!(orig_flags & BDRV_O_RDWR)) {
@ -3069,19 +3078,26 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
const QDictEntry *entry = qdict_first(reopen_state->options); const QDictEntry *entry = qdict_first(reopen_state->options);
do { do {
QString *new_obj = qobject_to_qstring(entry->value); QObject *new = entry->value;
const char *new = qstring_get_str(new_obj); QObject *old = qdict_get(reopen_state->bs->options, entry->key);
/*
* Caution: while qdict_get_try_str() is fine, getting
* non-string types would require more care. When
* bs->options come from -blockdev or blockdev_add, its
* members are typed according to the QAPI schema, but
* when they come from -drive, they're all QString.
*/
const char *old = qdict_get_try_str(reopen_state->bs->options,
entry->key);
if (!old || strcmp(new, old)) { /*
* TODO: When using -drive to specify blockdev options, all values
* will be strings; however, when using -blockdev, blockdev-add or
* filenames using the json:{} pseudo-protocol, they will be
* correctly typed.
* In contrast, reopening options are (currently) always strings
* (because you can only specify them through qemu-io; all other
* callers do not specify any options).
* Therefore, when using anything other than -drive to create a BDS,
* this cannot detect non-string options as unchanged, because
* qobject_is_equal() always returns false for objects of different
* type. In the future, this should be remedied by correctly typing
* all options. For now, this is not too big of an issue because
* the user can simply omit options which cannot be changed anyway,
* so they will stay unchanged.
*/
if (!qobject_is_equal(new, old)) {
error_setg(errp, "Cannot change the option '%s'", entry->key); error_setg(errp, "Cannot change the option '%s'", entry->key);
ret = -EINVAL; ret = -EINVAL;
goto error; goto error;
@ -3419,6 +3435,10 @@ int bdrv_change_backing_file(BlockDriverState *bs,
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
int ret; int ret;
if (!drv) {
return -ENOMEDIUM;
}
/* Backing file format doesn't make sense without a backing file */ /* Backing file format doesn't make sense without a backing file */
if (backing_fmt && !backing_file) { if (backing_fmt && !backing_file) {
return -EINVAL; return -EINVAL;
@ -3904,7 +3924,9 @@ int bdrv_has_zero_init_1(BlockDriverState *bs)
int bdrv_has_zero_init(BlockDriverState *bs) int bdrv_has_zero_init(BlockDriverState *bs)
{ {
assert(bs->drv); if (!bs->drv) {
return 0;
}
/* If BS is a copy on write image, it is initialized to /* If BS is a copy on write image, it is initialized to
the contents of the base image, which may not be zeroes. */ the contents of the base image, which may not be zeroes. */
@ -4169,7 +4191,29 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
} }
} }
/*
* Update permissions, they may differ for inactive nodes.
*
* Note that the required permissions of inactive images are always a
* subset of the permissions required after activating the image. This
* allows us to just get the permissions upfront without restricting
* drv->bdrv_invalidate_cache().
*
* It also means that in error cases, we don't have to try and revert to
* the old permissions (which is an operation that could fail, too). We can
* just keep the extended permissions for the next time that an activation
* of the image is tried.
*/
bs->open_flags &= ~BDRV_O_INACTIVE; bs->open_flags &= ~BDRV_O_INACTIVE;
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &local_err);
if (ret < 0) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
return;
}
bdrv_set_perm(bs, perm, shared_perm);
if (bs->drv->bdrv_invalidate_cache) { if (bs->drv->bdrv_invalidate_cache) {
bs->drv->bdrv_invalidate_cache(bs, &local_err); bs->drv->bdrv_invalidate_cache(bs, &local_err);
if (local_err) { if (local_err) {
@ -4186,16 +4230,6 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
return; return;
} }
/* Update permissions, they may differ for inactive nodes */
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
ret = bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &local_err);
if (ret < 0) {
bs->open_flags |= BDRV_O_INACTIVE;
error_propagate(errp, local_err);
return;
}
bdrv_set_perm(bs, perm, shared_perm);
QLIST_FOREACH(parent, &bs->parents, next_parent) { QLIST_FOREACH(parent, &bs->parents, next_parent) {
if (parent->role->activate) { if (parent->role->activate) {
parent->role->activate(parent, &local_err); parent->role->activate(parent, &local_err);
@ -4221,6 +4255,7 @@ void bdrv_invalidate_cache_all(Error **errp)
aio_context_release(aio_context); aio_context_release(aio_context);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
bdrv_next_cleanup(&it);
return; return;
} }
} }
@ -4232,6 +4267,10 @@ static int bdrv_inactivate_recurse(BlockDriverState *bs,
BdrvChild *child, *parent; BdrvChild *child, *parent;
int ret; int ret;
if (!bs->drv) {
return -ENOMEDIUM;
}
if (!setting_flag && bs->drv->bdrv_inactivate) { if (!setting_flag && bs->drv->bdrv_inactivate) {
ret = bs->drv->bdrv_inactivate(bs); ret = bs->drv->bdrv_inactivate(bs);
if (ret < 0) { if (ret < 0) {
@ -4292,6 +4331,7 @@ int bdrv_inactivate_all(void)
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
ret = bdrv_inactivate_recurse(bs, pass); ret = bdrv_inactivate_recurse(bs, pass);
if (ret < 0) { if (ret < 0) {
bdrv_next_cleanup(&it);
goto out; goto out;
} }
} }
@ -4766,6 +4806,9 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque) BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
{ {
if (!bs->drv) {
return -ENOMEDIUM;
}
if (!bs->drv->bdrv_amend_options) { if (!bs->drv->bdrv_amend_options) {
return -ENOTSUP; return -ENOTSUP;
} }
@ -4823,6 +4866,7 @@ bool bdrv_is_first_non_filter(BlockDriverState *candidate)
/* candidate is the first non filter */ /* candidate is the first non filter */
if (perm) { if (perm) {
bdrv_next_cleanup(&it);
return true; return true;
} }
} }

View File

@ -442,21 +442,37 @@ BlockBackend *blk_next(BlockBackend *blk)
* the monitor or attached to a BlockBackend */ * the monitor or attached to a BlockBackend */
BlockDriverState *bdrv_next(BdrvNextIterator *it) BlockDriverState *bdrv_next(BdrvNextIterator *it)
{ {
BlockDriverState *bs; BlockDriverState *bs, *old_bs;
/* Must be called from the main loop */
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
/* First, return all root nodes of BlockBackends. In order to avoid /* First, return all root nodes of BlockBackends. In order to avoid
* returning a BDS twice when multiple BBs refer to it, we only return it * returning a BDS twice when multiple BBs refer to it, we only return it
* if the BB is the first one in the parent list of the BDS. */ * if the BB is the first one in the parent list of the BDS. */
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) { if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
BlockBackend *old_blk = it->blk;
old_bs = old_blk ? blk_bs(old_blk) : NULL;
do { do {
it->blk = blk_all_next(it->blk); it->blk = blk_all_next(it->blk);
bs = it->blk ? blk_bs(it->blk) : NULL; bs = it->blk ? blk_bs(it->blk) : NULL;
} while (it->blk && (bs == NULL || bdrv_first_blk(bs) != it->blk)); } while (it->blk && (bs == NULL || bdrv_first_blk(bs) != it->blk));
if (it->blk) {
blk_ref(it->blk);
}
blk_unref(old_blk);
if (bs) { if (bs) {
bdrv_ref(bs);
bdrv_unref(old_bs);
return bs; return bs;
} }
it->phase = BDRV_NEXT_MONITOR_OWNED; it->phase = BDRV_NEXT_MONITOR_OWNED;
} else {
old_bs = it->bs;
} }
/* Then return the monitor-owned BDSes without a BB attached. Ignore all /* Then return the monitor-owned BDSes without a BB attached. Ignore all
@ -467,18 +483,46 @@ BlockDriverState *bdrv_next(BdrvNextIterator *it)
bs = it->bs; bs = it->bs;
} while (bs && bdrv_has_blk(bs)); } while (bs && bdrv_has_blk(bs));
if (bs) {
bdrv_ref(bs);
}
bdrv_unref(old_bs);
return bs; return bs;
} }
BlockDriverState *bdrv_first(BdrvNextIterator *it) static void bdrv_next_reset(BdrvNextIterator *it)
{ {
*it = (BdrvNextIterator) { *it = (BdrvNextIterator) {
.phase = BDRV_NEXT_BACKEND_ROOTS, .phase = BDRV_NEXT_BACKEND_ROOTS,
}; };
}
BlockDriverState *bdrv_first(BdrvNextIterator *it)
{
bdrv_next_reset(it);
return bdrv_next(it); return bdrv_next(it);
} }
/* Must be called when aborting a bdrv_next() iteration before
* bdrv_next() returns NULL */
void bdrv_next_cleanup(BdrvNextIterator *it)
{
/* Must be called from the main loop */
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
if (it->phase == BDRV_NEXT_BACKEND_ROOTS) {
if (it->blk) {
bdrv_unref(blk_bs(it->blk));
blk_unref(it->blk);
}
} else {
bdrv_unref(it->bs);
}
bdrv_next_reset(it);
}
/* /*
* Add a BlockBackend into the list of backends referenced by the monitor, with * Add a BlockBackend into the list of backends referenced by the monitor, with
* the given @name acting as the handle for the monitor. * the given @name acting as the handle for the monitor.

View File

@ -28,6 +28,7 @@
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/error-report.h"
/**************************************************************/ /**************************************************************/
@ -110,10 +111,16 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
return -EINVAL; return -EINVAL;
} }
if (!bdrv_is_read_only(bs)) {
error_report("Opening bochs images without an explicit read-only=on "
"option is deprecated. Future versions will refuse to "
"open the image instead of automatically marking the "
"image read-only.");
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
}
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
if (ret < 0) { if (ret < 0) {

View File

@ -23,6 +23,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/module.h" #include "qemu/module.h"
@ -72,10 +73,16 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
return -EINVAL; return -EINVAL;
} }
if (!bdrv_is_read_only(bs)) {
error_report("Opening cloop images without an explicit read-only=on "
"option is deprecated. Future versions will refuse to "
"open the image instead of automatically marking the "
"image read-only.");
ret = bdrv_set_read_only(bs, true, errp); ret = bdrv_set_read_only(bs, true, errp);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
}
/* read header */ /* read header */
ret = bdrv_pread(bs->file, 128, &s->block_size, 4); ret = bdrv_pread(bs->file, 128, &s->block_size, 4);

View File

@ -419,10 +419,16 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
return -EINVAL; return -EINVAL;
} }
if (!bdrv_is_read_only(bs)) {
error_report("Opening dmg images without an explicit read-only=on "
"option is deprecated. Future versions will refuse to "
"open the image instead of automatically marking the "
"image read-only.");
ret = bdrv_set_read_only(bs, true, errp); ret = bdrv_set_read_only(bs, true, errp);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
}
block_module_load_one("dmg-bz2"); block_module_load_one("dmg-bz2");

View File

@ -853,6 +853,10 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & ~BDRV_REQ_MASK));
if (!drv) {
return -ENOMEDIUM;
}
if (drv->bdrv_co_preadv) { if (drv->bdrv_co_preadv) {
return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags);
} }
@ -894,6 +898,10 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
assert(!(flags & ~BDRV_REQ_MASK)); assert(!(flags & ~BDRV_REQ_MASK));
if (!drv) {
return -ENOMEDIUM;
}
if (drv->bdrv_co_pwritev) { if (drv->bdrv_co_pwritev) {
ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov,
flags & bs->supported_write_flags); flags & bs->supported_write_flags);
@ -945,6 +953,10 @@ bdrv_driver_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
if (!drv->bdrv_co_pwritev_compressed) { if (!drv->bdrv_co_pwritev_compressed) {
return -ENOTSUP; return -ENOTSUP;
} }
@ -975,6 +987,10 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
BDRV_REQUEST_MAX_BYTES); BDRV_REQUEST_MAX_BYTES);
unsigned int progress = 0; unsigned int progress = 0;
if (!drv) {
return -ENOMEDIUM;
}
/* FIXME We cannot require callers to have write permissions when all they /* FIXME We cannot require callers to have write permissions when all they
* are doing is a read request. If we did things right, write permissions * are doing is a read request. If we did things right, write permissions
* would be obtained anyway, but internally by the copy-on-read code. As * would be obtained anyway, but internally by the copy-on-read code. As
@ -1291,6 +1307,10 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
bs->bl.request_alignment); bs->bl.request_alignment);
int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER); int max_transfer = MIN_NON_ZERO(bs->bl.max_transfer, MAX_BOUNCE_BUFFER);
if (!drv) {
return -ENOMEDIUM;
}
assert(alignment % bs->bl.request_alignment == 0); assert(alignment % bs->bl.request_alignment == 0);
head = offset % alignment; head = offset % alignment;
tail = (offset + bytes) % alignment; tail = (offset + bytes) % alignment;
@ -1397,6 +1417,10 @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
uint64_t bytes_remaining = bytes; uint64_t bytes_remaining = bytes;
int max_transfer; int max_transfer;
if (!drv) {
return -ENOMEDIUM;
}
if (bdrv_has_readonly_bitmaps(bs)) { if (bdrv_has_readonly_bitmaps(bs)) {
return -EPERM; return -EPERM;
} }
@ -1863,6 +1887,8 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs,
bytes = n; bytes = n;
} }
/* Must be non-NULL or bdrv_getlength() would have failed */
assert(bs->drv);
if (!bs->drv->bdrv_co_get_block_status) { if (!bs->drv->bdrv_co_get_block_status) {
*pnum = bytes; *pnum = bytes;
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED; ret = BDRV_BLOCK_DATA | BDRV_BLOCK_ALLOCATED;
@ -2373,6 +2399,12 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
} }
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK); BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK);
if (!bs->drv) {
/* bs->drv->bdrv_co_flush() might have ejected the BDS
* (even in case of apparent success) */
ret = -ENOMEDIUM;
goto out;
}
if (bs->drv->bdrv_co_flush_to_disk) { if (bs->drv->bdrv_co_flush_to_disk) {
ret = bs->drv->bdrv_co_flush_to_disk(bs); ret = bs->drv->bdrv_co_flush_to_disk(bs);
} else if (bs->drv->bdrv_aio_flush) { } else if (bs->drv->bdrv_aio_flush) {
@ -2542,6 +2574,10 @@ int coroutine_fn bdrv_co_pdiscard(BlockDriverState *bs, int64_t offset,
num = max_pdiscard; num = max_pdiscard;
} }
if (!bs->drv) {
ret = -ENOMEDIUM;
goto out;
}
if (bs->drv->bdrv_co_pdiscard) { if (bs->drv->bdrv_co_pdiscard) {
ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); ret = bs->drv->bdrv_co_pdiscard(bs, offset, num);
} else { } else {

View File

@ -39,8 +39,14 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
{ {
ImageInfo **p_image_info; ImageInfo **p_image_info;
BlockDriverState *bs0; BlockDriverState *bs0;
BlockDeviceInfo *info = g_malloc0(sizeof(*info)); BlockDeviceInfo *info;
if (!bs->drv) {
error_setg(errp, "Block device %s is ejected", bs->node_name);
return NULL;
}
info = g_malloc0(sizeof(*info));
info->file = g_strdup(bs->filename); info->file = g_strdup(bs->filename);
info->ro = bs->read_only; info->ro = bs->read_only;
info->drv = g_strdup(bs->drv->format_name); info->drv = g_strdup(bs->drv->format_name);

View File

@ -62,6 +62,18 @@ static inline int qcow2_cache_get_table_idx(BlockDriverState *bs,
return idx; return idx;
} }
static inline const char *qcow2_cache_get_name(BDRVQcow2State *s, Qcow2Cache *c)
{
if (c == s->refcount_block_cache) {
return "refcount block";
} else if (c == s->l2_table_cache) {
return "L2 table";
} else {
/* Do not abort, because this is not critical */
return "unknown";
}
}
static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c, static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c,
int i, int num_tables) int i, int num_tables)
{ {
@ -73,7 +85,7 @@ static void qcow2_cache_table_release(BlockDriverState *bs, Qcow2Cache *c,
size_t mem_size = (size_t) s->cluster_size * num_tables; size_t mem_size = (size_t) s->cluster_size * num_tables;
size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t; size_t offset = QEMU_ALIGN_UP((uintptr_t) t, align) - (uintptr_t) t;
size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align); size_t length = QEMU_ALIGN_DOWN(mem_size - offset, align);
if (length > 0) { if (mem_size > offset && length > 0) {
madvise((uint8_t *) t + offset, length, MADV_DONTNEED); madvise((uint8_t *) t + offset, length, MADV_DONTNEED);
} }
#endif #endif
@ -314,9 +326,18 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
uint64_t min_lru_counter = UINT64_MAX; uint64_t min_lru_counter = UINT64_MAX;
int min_lru_index = -1; int min_lru_index = -1;
assert(offset != 0);
trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache, trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
offset, read_from_disk); offset, read_from_disk);
if (offset_into_cluster(s, offset)) {
qcow2_signal_corruption(bs, true, -1, -1, "Cannot get entry from %s "
"cache: Offset %#" PRIx64 " is unaligned",
qcow2_cache_get_name(s, c), offset);
return -EIO;
}
/* Check if the table is already cached */ /* Check if the table is already cached */
i = lookup_index = (offset / s->cluster_size * 4) % c->size; i = lookup_index = (offset / s->cluster_size * 4) % c->size;
do { do {

View File

@ -1308,10 +1308,21 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
(!*host_offset || (!*host_offset ||
start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK))) start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK)))
{ {
int preallocated_nb_clusters;
if (offset_into_cluster(s, entry & L2E_OFFSET_MASK)) {
qcow2_signal_corruption(bs, true, -1, -1, "Preallocated zero "
"cluster offset %#llx unaligned (guest "
"offset: %#" PRIx64 ")",
entry & L2E_OFFSET_MASK, guest_offset);
ret = -EIO;
goto fail;
}
/* Try to reuse preallocated zero clusters; contiguous normal clusters /* Try to reuse preallocated zero clusters; contiguous normal clusters
* would be fine, too, but count_cow_clusters() above has limited * would be fine, too, but count_cow_clusters() above has limited
* nb_clusters already to a range of COW clusters */ * nb_clusters already to a range of COW clusters */
int preallocated_nb_clusters = preallocated_nb_clusters =
count_contiguous_clusters(nb_clusters, s->cluster_size, count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], QCOW_OFLAG_COPIED); &l2_table[l2_index], QCOW_OFLAG_COPIED);
assert(preallocated_nb_clusters > 0); assert(preallocated_nb_clusters > 0);

View File

@ -3077,16 +3077,40 @@ done:
return ret; return ret;
} }
static int64_t get_refblock_offset(BlockDriverState *bs, uint64_t offset)
{
BDRVQcow2State *s = bs->opaque;
uint32_t index = offset_to_reftable_index(s, offset);
int64_t covering_refblock_offset = 0;
if (index < s->refcount_table_size) {
covering_refblock_offset = s->refcount_table[index] & REFT_OFFSET_MASK;
}
if (!covering_refblock_offset) {
qcow2_signal_corruption(bs, true, -1, -1, "Refblock at %#" PRIx64 " is "
"not covered by the refcount structures",
offset);
return -EIO;
}
return covering_refblock_offset;
}
static int qcow2_discard_refcount_block(BlockDriverState *bs, static int qcow2_discard_refcount_block(BlockDriverState *bs,
uint64_t discard_block_offs) uint64_t discard_block_offs)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
uint64_t refblock_offs = get_refblock_offset(s, discard_block_offs); int64_t refblock_offs;
uint64_t cluster_index = discard_block_offs >> s->cluster_bits; uint64_t cluster_index = discard_block_offs >> s->cluster_bits;
uint32_t block_index = cluster_index & (s->refcount_block_size - 1); uint32_t block_index = cluster_index & (s->refcount_block_size - 1);
void *refblock; void *refblock;
int ret; int ret;
refblock_offs = get_refblock_offset(bs, discard_block_offs);
if (refblock_offs < 0) {
return refblock_offs;
}
assert(discard_block_offs != 0); assert(discard_block_offs != 0);
ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs, ret = qcow2_cache_get(bs, s->refcount_block_cache, refblock_offs,

View File

@ -376,6 +376,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
default: default:
/* unknown magic - save it in case we need to rewrite the header */ /* unknown magic - save it in case we need to rewrite the header */
/* If you add a new feature, make sure to also update the fast
* path of qcow2_make_empty() to deal with it. */
{ {
Qcow2UnknownHeaderExtension *uext; Qcow2UnknownHeaderExtension *uext;
@ -1475,7 +1477,10 @@ static int qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
BdrvCheckResult result = {0}; BdrvCheckResult result = {0};
ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS); ret = qcow2_check(bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
if (ret < 0) { if (ret < 0 || result.check_errors) {
if (ret >= 0) {
ret = -EIO;
}
error_setg_errno(errp, -ret, "Could not repair dirty image"); error_setg_errno(errp, -ret, "Could not repair dirty image");
goto fail; goto fail;
} }
@ -3356,6 +3361,10 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL); return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL);
} }
if (offset_into_cluster(s, offset)) {
return -EINVAL;
}
buf = qemu_blockalign(bs, s->cluster_size); buf = qemu_blockalign(bs, s->cluster_size);
if (bytes != s->cluster_size) { if (bytes != s->cluster_size) {
if (bytes > s->cluster_size || if (bytes > s->cluster_size ||
@ -3600,13 +3609,16 @@ static int qcow2_make_empty(BlockDriverState *bs)
l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t)); l1_clusters = DIV_ROUND_UP(s->l1_size, s->cluster_size / sizeof(uint64_t));
if (s->qcow_version >= 3 && !s->snapshots && if (s->qcow_version >= 3 && !s->snapshots && !s->nb_bitmaps &&
3 + l1_clusters <= s->refcount_block_size) { 3 + l1_clusters <= s->refcount_block_size &&
/* The following function only works for qcow2 v3 images (it requires s->crypt_method_header != QCOW_CRYPT_LUKS) {
* the dirty flag) and only as long as there are no snapshots (because /* The following function only works for qcow2 v3 images (it
* it completely empties the image). Furthermore, the L1 table and three * requires the dirty flag) and only as long as there are no
* additional clusters (image header, refcount table, one refcount * features that reserve extra clusters (such as snapshots,
* block) have to fit inside one refcount block. */ * LUKS header, or persistent bitmaps), because it completely
* empties the image. Furthermore, the L1 table and three
* additional clusters (image header, refcount table, one
* refcount block) have to fit inside one refcount block. */
return make_completely_empty(bs); return make_completely_empty(bs);
} }
@ -4069,6 +4081,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
error_report("Changing the encryption format is not supported"); error_report("Changing the encryption format is not supported");
return -ENOTSUP; return -ENOTSUP;
} }
} else if (g_str_has_prefix(desc->name, "encrypt.")) {
error_report("Changing the encryption parameters is not supported");
return -ENOTSUP;
} else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) { } else if (!strcmp(desc->name, BLOCK_OPT_CLUSTER_SIZE)) {
cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE, cluster_size = qemu_opt_get_size(opts, BLOCK_OPT_CLUSTER_SIZE,
cluster_size); cluster_size);

View File

@ -527,12 +527,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
return offset >> (s->refcount_block_bits + s->cluster_bits); return offset >> (s->refcount_block_bits + s->cluster_bits);
} }
static inline uint64_t get_refblock_offset(BDRVQcow2State *s, uint64_t offset)
{
uint32_t index = offset_to_reftable_index(s, offset);
return s->refcount_table[index] & REFT_OFFSET_MASK;
}
/* qcow2.c functions */ /* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors); int64_t sector_num, int nb_sectors);

View File

@ -665,12 +665,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
/* If we are using an rbd snapshot, we must be r/o, otherwise /* If we are using an rbd snapshot, we must be r/o, otherwise
* leave as-is */ * leave as-is */
if (s->snap != NULL) { if (s->snap != NULL) {
if (!bdrv_is_read_only(bs)) {
error_report("Opening rbd snapshots without an explicit "
"read-only=on option is deprecated. Future versions "
"will refuse to open the image instead of "
"automatically marking the image read-only.");
r = bdrv_set_read_only(bs, true, &local_err); r = bdrv_set_read_only(bs, true, &local_err);
if (r < 0) { if (r < 0) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto failed_open; goto failed_open;
} }
} }
}
qemu_opts_del(opts); qemu_opts_del(opts);
return 0; return 0;

View File

@ -161,10 +161,13 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
uint64_t perm, uint64_t shared, uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared) uint64_t *nperm, uint64_t *nshared)
{ {
*nperm = *nshared = BLK_PERM_CONSISTENT_READ \ *nperm = BLK_PERM_CONSISTENT_READ;
if ((bs->open_flags & (BDRV_O_INACTIVE | BDRV_O_RDWR)) == BDRV_O_RDWR) {
*nperm |= BLK_PERM_WRITE;
}
*nshared = BLK_PERM_CONSISTENT_READ \
| BLK_PERM_WRITE \ | BLK_PERM_WRITE \
| BLK_PERM_WRITE_UNCHANGED; | BLK_PERM_WRITE_UNCHANGED;
return; return;
} }
@ -339,12 +342,24 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp)
return; return;
} }
if (!s->active_disk->bs->drv) {
error_setg(errp, "Active disk %s is ejected",
s->active_disk->bs->node_name);
return;
}
ret = s->active_disk->bs->drv->bdrv_make_empty(s->active_disk->bs); ret = s->active_disk->bs->drv->bdrv_make_empty(s->active_disk->bs);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Cannot make active disk empty"); error_setg(errp, "Cannot make active disk empty");
return; return;
} }
if (!s->hidden_disk->bs->drv) {
error_setg(errp, "Hidden disk %s is ejected",
s->hidden_disk->bs->node_name);
return;
}
ret = s->hidden_disk->bs->drv->bdrv_make_empty(s->hidden_disk->bs); ret = s->hidden_disk->bs->drv->bdrv_make_empty(s->hidden_disk->bs);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "Cannot make hidden disk empty"); error_setg(errp, "Cannot make hidden disk empty");
@ -508,6 +523,9 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
return; return;
} }
/* Must be true, or the bdrv_getlength() calls would have failed */
assert(s->active_disk->bs->drv && s->hidden_disk->bs->drv);
if (!s->active_disk->bs->drv->bdrv_make_empty || if (!s->active_disk->bs->drv->bdrv_make_empty ||
!s->hidden_disk->bs->drv->bdrv_make_empty) { !s->hidden_disk->bs->drv->bdrv_make_empty) {
error_setg(errp, error_setg(errp,

View File

@ -417,6 +417,7 @@ bool bdrv_all_can_snapshot(BlockDriverState **first_bad_bs)
} }
aio_context_release(ctx); aio_context_release(ctx);
if (!ok) { if (!ok) {
bdrv_next_cleanup(&it);
goto fail; goto fail;
} }
} }
@ -444,6 +445,7 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_bs,
} }
aio_context_release(ctx); aio_context_release(ctx);
if (ret < 0) { if (ret < 0) {
bdrv_next_cleanup(&it);
goto fail; goto fail;
} }
} }
@ -469,6 +471,7 @@ int bdrv_all_goto_snapshot(const char *name, BlockDriverState **first_bad_bs)
} }
aio_context_release(ctx); aio_context_release(ctx);
if (err < 0) { if (err < 0) {
bdrv_next_cleanup(&it);
goto fail; goto fail;
} }
} }
@ -494,6 +497,7 @@ int bdrv_all_find_snapshot(const char *name, BlockDriverState **first_bad_bs)
} }
aio_context_release(ctx); aio_context_release(ctx);
if (err < 0) { if (err < 0) {
bdrv_next_cleanup(&it);
goto fail; goto fail;
} }
} }
@ -525,6 +529,7 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
} }
aio_context_release(ctx); aio_context_release(ctx);
if (err < 0) { if (err < 0) {
bdrv_next_cleanup(&it);
goto fail; goto fail;
} }
} }
@ -548,6 +553,7 @@ BlockDriverState *bdrv_all_find_vmstate_bs(void)
aio_context_release(ctx); aio_context_release(ctx);
if (found) { if (found) {
bdrv_next_cleanup(&it);
break; break;
} }
} }

View File

@ -1259,7 +1259,11 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
"Unable to set VVFAT to 'rw' when drive is read-only"); "Unable to set VVFAT to 'rw' when drive is read-only");
goto fail; goto fail;
} }
} else { } else if (!bdrv_is_read_only(bs)) {
error_report("Opening non-rw vvfat images without an explicit "
"read-only=on option is deprecated. Future versions "
"will refuse to open the image instead of "
"automatically marking the image read-only.");
/* read only is the default for safety */ /* read only is the default for safety */
ret = bdrv_set_read_only(bs, true, &local_err); ret = bdrv_set_read_only(bs, true, &local_err);
if (ret < 0) { if (ret < 0) {
@ -2943,7 +2947,7 @@ static int do_commit(BDRVVVFATState* s)
return ret; return ret;
} }
if (s->qcow->bs->drv->bdrv_make_empty) { if (s->qcow->bs->drv && s->qcow->bs->drv->bdrv_make_empty) {
s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs); s->qcow->bs->drv->bdrv_make_empty(s->qcow->bs);
} }

View File

@ -461,6 +461,7 @@ typedef struct BdrvNextIterator {
BlockDriverState *bdrv_first(BdrvNextIterator *it); BlockDriverState *bdrv_first(BdrvNextIterator *it);
BlockDriverState *bdrv_next(BdrvNextIterator *it); BlockDriverState *bdrv_next(BdrvNextIterator *it);
void bdrv_next_cleanup(BdrvNextIterator *it);
BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs);
bool bdrv_is_encrypted(BlockDriverState *bs); bool bdrv_is_encrypted(BlockDriverState *bs);

View File

@ -24,6 +24,7 @@ typedef struct QBool {
QBool *qbool_from_bool(bool value); QBool *qbool_from_bool(bool value);
bool qbool_get_bool(const QBool *qb); bool qbool_get_bool(const QBool *qb);
QBool *qobject_to_qbool(const QObject *obj); QBool *qobject_to_qbool(const QObject *obj);
bool qbool_is_equal(const QObject *x, const QObject *y);
void qbool_destroy_obj(QObject *obj); void qbool_destroy_obj(QObject *obj);
#endif /* QBOOL_H */ #endif /* QBOOL_H */

View File

@ -15,6 +15,7 @@
#include "qapi/qmp/qobject.h" #include "qapi/qmp/qobject.h"
#include "qapi/qmp/qlist.h" #include "qapi/qmp/qlist.h"
#include "qapi/qmp/qnull.h"
#include "qapi/qmp/qnum.h" #include "qapi/qmp/qnum.h"
#include "qemu/queue.h" #include "qemu/queue.h"
@ -42,6 +43,7 @@ void qdict_del(QDict *qdict, const char *key);
int qdict_haskey(const QDict *qdict, const char *key); int qdict_haskey(const QDict *qdict, const char *key);
QObject *qdict_get(const QDict *qdict, const char *key); QObject *qdict_get(const QDict *qdict, const char *key);
QDict *qobject_to_qdict(const QObject *obj); QDict *qobject_to_qdict(const QObject *obj);
bool qdict_is_equal(const QObject *x, const QObject *y);
void qdict_iter(const QDict *qdict, void qdict_iter(const QDict *qdict,
void (*iter)(const char *key, QObject *obj, void *opaque), void (*iter)(const char *key, QObject *obj, void *opaque),
void *opaque); void *opaque);

View File

@ -15,6 +15,7 @@
#include "qapi/qmp/qobject.h" #include "qapi/qmp/qobject.h"
#include "qapi/qmp/qnum.h" #include "qapi/qmp/qnum.h"
#include "qapi/qmp/qnull.h"
#include "qemu/queue.h" #include "qemu/queue.h"
typedef struct QListEntry { typedef struct QListEntry {
@ -37,6 +38,8 @@ typedef struct QList {
qlist_append(qlist, qbool_from_bool(value)) qlist_append(qlist, qbool_from_bool(value))
#define qlist_append_str(qlist, value) \ #define qlist_append_str(qlist, value) \
qlist_append(qlist, qstring_from_str(value)) qlist_append(qlist, qstring_from_str(value))
#define qlist_append_null(qlist) \
qlist_append(qlist, qnull())
#define QLIST_FOREACH_ENTRY(qlist, var) \ #define QLIST_FOREACH_ENTRY(qlist, var) \
for ((var) = ((qlist)->head.tqh_first); \ for ((var) = ((qlist)->head.tqh_first); \
@ -58,6 +61,7 @@ QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist); int qlist_empty(const QList *qlist);
size_t qlist_size(const QList *qlist); size_t qlist_size(const QList *qlist);
QList *qobject_to_qlist(const QObject *obj); QList *qobject_to_qlist(const QObject *obj);
bool qlist_is_equal(const QObject *x, const QObject *y);
void qlist_destroy_obj(QObject *obj); void qlist_destroy_obj(QObject *obj);
static inline const QListEntry *qlist_first(const QList *qlist) static inline const QListEntry *qlist_first(const QList *qlist)

32
include/qapi/qmp/qnull.h Normal file
View File

@ -0,0 +1,32 @@
/*
* QNull
*
* Copyright (C) 2015 Red Hat, Inc.
*
* Authors:
* Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1
* or later. See the COPYING.LIB file in the top-level directory.
*/
#ifndef QNULL_H
#define QNULL_H
#include "qapi/qmp/qobject.h"
struct QNull {
QObject base;
};
extern QNull qnull_;
static inline QNull *qnull(void)
{
QINCREF(&qnull_);
return &qnull_;
}
bool qnull_is_equal(const QObject *x, const QObject *y);
#endif /* QNULL_H */

View File

@ -69,6 +69,7 @@ double qnum_get_double(QNum *qn);
char *qnum_to_string(QNum *qn); char *qnum_to_string(QNum *qn);
QNum *qobject_to_qnum(const QObject *obj); QNum *qobject_to_qnum(const QObject *obj);
bool qnum_is_equal(const QObject *x, const QObject *y);
void qnum_destroy_obj(QObject *obj); void qnum_destroy_obj(QObject *obj);
#endif /* QNUM_H */ #endif /* QNUM_H */

View File

@ -67,6 +67,15 @@ static inline void qobject_incref(QObject *obj)
obj->refcnt++; obj->refcnt++;
} }
/**
* qobject_is_equal(): Return whether the two objects are equal.
*
* Any of the pointers may be NULL; return true if both are. Always
* return false if only one is (therefore a QNull object is not
* considered equal to a NULL pointer).
*/
bool qobject_is_equal(const QObject *x, const QObject *y);
/** /**
* qobject_destroy(): Free resources used by the object * qobject_destroy(): Free resources used by the object
*/ */
@ -93,16 +102,4 @@ static inline QType qobject_type(const QObject *obj)
return obj->type; return obj->type;
} }
struct QNull {
QObject base;
};
extern QNull qnull_;
static inline QNull *qnull(void)
{
QINCREF(&qnull_);
return &qnull_;
}
#endif /* QOBJECT_H */ #endif /* QOBJECT_H */

View File

@ -31,6 +31,7 @@ void qstring_append_int(QString *qstring, int64_t value);
void qstring_append(QString *qstring, const char *str); void qstring_append(QString *qstring, const char *str);
void qstring_append_chr(QString *qstring, int c); void qstring_append_chr(QString *qstring, int c);
QString *qobject_to_qstring(const QObject *obj); QString *qobject_to_qstring(const QObject *obj);
bool qstring_is_equal(const QObject *x, const QObject *y);
void qstring_destroy_obj(QObject *obj); void qstring_destroy_obj(QObject *obj);
#endif /* QSTRING_H */ #endif /* QSTRING_H */

View File

@ -19,5 +19,6 @@
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h" #include "qapi/qmp/qlist.h"
#include "qapi/qmp/qnull.h"
#endif /* QAPI_QMP_TYPES_H */ #endif /* QAPI_QMP_TYPES_H */

View File

@ -415,6 +415,7 @@ static int init_blk_migration(QEMUFile *f)
sectors = bdrv_nb_sectors(bs); sectors = bdrv_nb_sectors(bs);
if (sectors <= 0) { if (sectors <= 0) {
ret = sectors; ret = sectors;
bdrv_next_cleanup(&it);
goto out; goto out;
} }

View File

@ -3134,8 +3134,11 @@
# This option is required on the top level of blockdev-add. # This option is required on the top level of blockdev-add.
# @discard: discard-related options (default: ignore) # @discard: discard-related options (default: ignore)
# @cache: cache-related options # @cache: cache-related options
# @read-only: whether the block device should be read-only # @read-only: whether the block device should be read-only (default: false).
# (default: false) # Note that some block drivers support only read-only access,
# either generally or in certain configurations. In this case,
# the default value does not work and the option must be
# specified explicitly.
# @detect-zeroes: detect and optimize zero writes (Since 2.1) # @detect-zeroes: detect and optimize zero writes (Since 2.1)
# (default: off) # (default: off)
# @force-share: force share all permission on added nodes. # @force-share: force share all permission on added nodes.

View File

@ -12,6 +12,7 @@
#include "qapi/clone-visitor.h" #include "qapi/clone-visitor.h"
#include "qapi/visitor-impl.h" #include "qapi/visitor-impl.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qnull.h"
struct QapiCloneVisitor { struct QapiCloneVisitor {
Visitor visitor; Visitor visitor;

View File

@ -16,6 +16,7 @@
#include "qapi/string-input-visitor.h" #include "qapi/string-input-visitor.h"
#include "qapi/visitor-impl.h" #include "qapi/visitor-impl.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/qmp/qnull.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/queue.h" #include "qemu/queue.h"
#include "qemu/range.h" #include "qemu/range.h"

View File

@ -51,6 +51,14 @@ QBool *qobject_to_qbool(const QObject *obj)
return container_of(obj, QBool, base); return container_of(obj, QBool, base);
} }
/**
* qbool_is_equal(): Test whether the two QBools are equal
*/
bool qbool_is_equal(const QObject *x, const QObject *y)
{
return qobject_to_qbool(x)->value == qobject_to_qbool(y)->value;
}
/** /**
* qbool_destroy_obj(): Free all memory allocated by a * qbool_destroy_obj(): Free all memory allocated by a
* QBool object * QBool object

View File

@ -402,6 +402,35 @@ void qdict_del(QDict *qdict, const char *key)
} }
} }
/**
* qdict_is_equal(): Test whether the two QDicts are equal
*
* Here, equality means whether they contain the same keys and whether
* the respective values are in turn equal (i.e. invoking
* qobject_is_equal() on them yields true).
*/
bool qdict_is_equal(const QObject *x, const QObject *y)
{
const QDict *dict_x = qobject_to_qdict(x);
const QDict *dict_y = qobject_to_qdict(y);
const QDictEntry *e;
if (qdict_size(dict_x) != qdict_size(dict_y)) {
return false;
}
for (e = qdict_first(dict_x); e; e = qdict_next(dict_x, e)) {
const QObject *obj_x = qdict_entry_value(e);
const QObject *obj_y = qdict_get(dict_y, qdict_entry_key(e));
if (!qobject_is_equal(obj_x, obj_y)) {
return false;
}
}
return true;
}
/** /**
* qdict_destroy_obj(): Free all the memory allocated by a QDict * qdict_destroy_obj(): Free all the memory allocated by a QDict
*/ */

View File

@ -139,6 +139,38 @@ QList *qobject_to_qlist(const QObject *obj)
return container_of(obj, QList, base); return container_of(obj, QList, base);
} }
/**
* qlist_is_equal(): Test whether the two QLists are equal
*
* In order to be considered equal, the respective two objects at each
* index of the two lists have to compare equal (regarding
* qobject_is_equal()), and both lists have to have the same number of
* elements.
* That means both lists have to contain equal objects in equal order.
*/
bool qlist_is_equal(const QObject *x, const QObject *y)
{
const QList *list_x = qobject_to_qlist(x);
const QList *list_y = qobject_to_qlist(y);
const QListEntry *entry_x, *entry_y;
entry_x = qlist_first(list_x);
entry_y = qlist_first(list_y);
while (entry_x && entry_y) {
if (!qobject_is_equal(qlist_entry_obj(entry_x),
qlist_entry_obj(entry_y)))
{
return false;
}
entry_x = qlist_next(entry_x);
entry_y = qlist_next(entry_y);
}
return !entry_x && !entry_y;
}
/** /**
* qlist_destroy_obj(): Free all the memory allocated by a QList * qlist_destroy_obj(): Free all the memory allocated by a QList
*/ */

View File

@ -12,7 +12,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "qapi/qmp/qobject.h" #include "qapi/qmp/qnull.h"
QNull qnull_ = { QNull qnull_ = {
.base = { .base = {
@ -20,3 +20,12 @@ QNull qnull_ = {
.refcnt = 1, .refcnt = 1,
}, },
}; };
/**
* qnull_is_equal(): Always return true because any two QNull objects
* are equal.
*/
bool qnull_is_equal(const QObject *x, const QObject *y)
{
return true;
}

View File

@ -212,6 +212,60 @@ QNum *qobject_to_qnum(const QObject *obj)
return container_of(obj, QNum, base); return container_of(obj, QNum, base);
} }
/**
* qnum_is_equal(): Test whether the two QNums are equal
*
* Negative integers are never considered equal to unsigned integers,
* but positive integers in the range [0, INT64_MAX] are considered
* equal independently of whether the QNum's kind is i64 or u64.
*
* Doubles are never considered equal to integers.
*/
bool qnum_is_equal(const QObject *x, const QObject *y)
{
QNum *num_x = qobject_to_qnum(x);
QNum *num_y = qobject_to_qnum(y);
switch (num_x->kind) {
case QNUM_I64:
switch (num_y->kind) {
case QNUM_I64:
/* Comparison in native int64_t type */
return num_x->u.i64 == num_y->u.i64;
case QNUM_U64:
/* Implicit conversion of x to uin64_t, so we have to
* check its sign before */
return num_x->u.i64 >= 0 && num_x->u.i64 == num_y->u.u64;
case QNUM_DOUBLE:
return false;
}
abort();
case QNUM_U64:
switch (num_y->kind) {
case QNUM_I64:
return qnum_is_equal(y, x);
case QNUM_U64:
/* Comparison in native uint64_t type */
return num_x->u.u64 == num_y->u.u64;
case QNUM_DOUBLE:
return false;
}
abort();
case QNUM_DOUBLE:
switch (num_y->kind) {
case QNUM_I64:
case QNUM_U64:
return false;
case QNUM_DOUBLE:
/* Comparison in native double type */
return num_x->u.dbl == num_y->u.dbl;
}
abort();
}
abort();
}
/** /**
* qnum_destroy_obj(): Free all memory allocated by a * qnum_destroy_obj(): Free all memory allocated by a
* QNum object * QNum object

View File

@ -27,3 +27,32 @@ void qobject_destroy(QObject *obj)
assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX); assert(QTYPE_QNULL < obj->type && obj->type < QTYPE__MAX);
qdestroy[obj->type](obj); qdestroy[obj->type](obj);
} }
static bool (*qis_equal[QTYPE__MAX])(const QObject *, const QObject *) = {
[QTYPE_NONE] = NULL, /* No such object exists */
[QTYPE_QNULL] = qnull_is_equal,
[QTYPE_QNUM] = qnum_is_equal,
[QTYPE_QSTRING] = qstring_is_equal,
[QTYPE_QDICT] = qdict_is_equal,
[QTYPE_QLIST] = qlist_is_equal,
[QTYPE_QBOOL] = qbool_is_equal,
};
bool qobject_is_equal(const QObject *x, const QObject *y)
{
/* We cannot test x == y because an object does not need to be
* equal to itself (e.g. NaN floats are not). */
if (!x && !y) {
return true;
}
if (!x || !y || x->type != y->type) {
return false;
}
assert(QTYPE_NONE < x->type && x->type < QTYPE__MAX);
return qis_equal[x->type](x, y);
}

View File

@ -128,6 +128,15 @@ const char *qstring_get_str(const QString *qstring)
return qstring->string; return qstring->string;
} }
/**
* qstring_is_equal(): Test whether the two QStrings are equal
*/
bool qstring_is_equal(const QObject *x, const QObject *y)
{
return !strcmp(qobject_to_qstring(x)->string,
qobject_to_qstring(y)->string);
}
/** /**
* qstring_destroy_obj(): Free all memory allocated by a QString * qstring_destroy_obj(): Free all memory allocated by a QString
* object * object

View File

@ -41,4 +41,7 @@ expression Obj, E;
| |
- qlist_append(Obj, qstring_from_str(E)); - qlist_append(Obj, qstring_from_str(E));
+ qlist_append_str(Obj, E); + qlist_append_str(Obj, E);
|
- qlist_append(Obj, qnull());
+ qlist_append_null(Obj);
) )

1
tests/.gitignore vendored
View File

@ -8,6 +8,7 @@ check-qjson
check-qlist check-qlist
check-qlit check-qlit
check-qnull check-qnull
check-qobject
check-qstring check-qstring
check-qom-interface check-qom-interface
check-qom-proplist check-qom-proplist

View File

@ -41,6 +41,7 @@ check-unit-y += tests/check-qlist$(EXESUF)
gcov-files-check-qlist-y = qobject/qlist.c gcov-files-check-qlist-y = qobject/qlist.c
check-unit-y += tests/check-qnull$(EXESUF) check-unit-y += tests/check-qnull$(EXESUF)
gcov-files-check-qnull-y = qobject/qnull.c gcov-files-check-qnull-y = qobject/qnull.c
check-unit-y += tests/check-qobject$(EXESUF)
check-unit-y += tests/check-qjson$(EXESUF) check-unit-y += tests/check-qjson$(EXESUF)
gcov-files-check-qjson-y = qobject/qjson.c gcov-files-check-qjson-y = qobject/qjson.c
check-unit-y += tests/check-qlit$(EXESUF) check-unit-y += tests/check-qlit$(EXESUF)
@ -546,7 +547,7 @@ GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \
tests/test-qmp-introspect.h tests/test-qmp-introspect.h
test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \
tests/check-qlist.o tests/check-qnull.o \ tests/check-qlist.o tests/check-qnull.o tests/check-qobject.o \
tests/check-qjson.o tests/check-qlit.o \ tests/check-qjson.o tests/check-qlit.o \
tests/test-coroutine.o tests/test-string-output-visitor.o \ tests/test-coroutine.o tests/test-string-output-visitor.o \
tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \ tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \
@ -580,6 +581,7 @@ tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y) tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y) tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y) tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y)
tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y) tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)

View File

@ -8,7 +8,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/qmp/qobject.h" #include "qapi/qmp/qnull.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "qapi/qobject-input-visitor.h" #include "qapi/qobject-input-visitor.h"
#include "qapi/qobject-output-visitor.h" #include "qapi/qobject-output-visitor.h"

328
tests/check-qobject.c Normal file
View File

@ -0,0 +1,328 @@
/*
* Generic QObject unit-tests.
*
* Copyright (C) 2017 Red Hat Inc.
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/qmp/types.h"
#include "qemu-common.h"
#include <math.h>
/* Marks the end of the test_equality() argument list.
* We cannot use NULL there because that is a valid argument. */
static QObject test_equality_end_of_arguments;
/**
* Test whether all variadic QObject *arguments are equal (@expected
* is true) or whether they are all not equal (@expected is false).
* Every QObject is tested to be equal to itself (to test
* reflexivity), all tests are done both ways (to test symmetry), and
* transitivity is not assumed but checked (each object is compared to
* every other one).
*
* Note that qobject_is_equal() is not really an equivalence relation,
* so this function may not be used for all objects (reflexivity is
* not guaranteed, e.g. in the case of a QNum containing NaN).
*
* The @_ argument is required because a boolean may not be the last
* argument before a variadic argument list (C11 7.16.1.4 para. 4).
*/
static void do_test_equality(bool expected, int _, ...)
{
va_list ap_count, ap_extract;
QObject **args;
int arg_count = 0;
int i, j;
va_start(ap_count, _);
va_copy(ap_extract, ap_count);
while (va_arg(ap_count, QObject *) != &test_equality_end_of_arguments) {
arg_count++;
}
va_end(ap_count);
args = g_new(QObject *, arg_count);
for (i = 0; i < arg_count; i++) {
args[i] = va_arg(ap_extract, QObject *);
}
va_end(ap_extract);
for (i = 0; i < arg_count; i++) {
g_assert(qobject_is_equal(args[i], args[i]) == true);
for (j = i + 1; j < arg_count; j++) {
g_assert(qobject_is_equal(args[i], args[j]) == expected);
}
}
}
#define check_equal(...) \
do_test_equality(true, 0, __VA_ARGS__, &test_equality_end_of_arguments)
#define check_unequal(...) \
do_test_equality(false, 0, __VA_ARGS__, &test_equality_end_of_arguments)
static void do_free_all(int _, ...)
{
va_list ap;
QObject *obj;
va_start(ap, _);
while ((obj = va_arg(ap, QObject *)) != NULL) {
qobject_decref(obj);
}
va_end(ap);
}
#define free_all(...) \
do_free_all(0, __VA_ARGS__, NULL)
static void qobject_is_equal_null_test(void)
{
check_unequal(qnull(), NULL);
}
static void qobject_is_equal_num_test(void)
{
QNum *u0, *i0, *d0, *dnan, *um42, *im42, *dm42;
u0 = qnum_from_uint(0u);
i0 = qnum_from_int(0);
d0 = qnum_from_double(0.0);
dnan = qnum_from_double(NAN);
um42 = qnum_from_uint((uint64_t)-42);
im42 = qnum_from_int(-42);
dm42 = qnum_from_double(-42.0);
/* Integers representing a mathematically equal number should
* compare equal */
check_equal(u0, i0);
/* Doubles, however, are always unequal to integers */
check_unequal(u0, d0);
check_unequal(i0, d0);
/* Do not assume any object is equal to itself -- note however
* that NaN cannot occur in a JSON object anyway. */
g_assert(qobject_is_equal(QOBJECT(dnan), QOBJECT(dnan)) == false);
/* No unsigned overflow */
check_unequal(um42, im42);
check_unequal(um42, dm42);
check_unequal(im42, dm42);
free_all(u0, i0, d0, dnan, um42, im42, dm42);
}
static void qobject_is_equal_bool_test(void)
{
QBool *btrue_0, *btrue_1, *bfalse_0, *bfalse_1;
btrue_0 = qbool_from_bool(true);
btrue_1 = qbool_from_bool(true);
bfalse_0 = qbool_from_bool(false);
bfalse_1 = qbool_from_bool(false);
check_equal(btrue_0, btrue_1);
check_equal(bfalse_0, bfalse_1);
check_unequal(btrue_0, bfalse_0);
free_all(btrue_0, btrue_1, bfalse_0, bfalse_1);
}
static void qobject_is_equal_string_test(void)
{
QString *str_base, *str_whitespace_0, *str_whitespace_1, *str_whitespace_2;
QString *str_whitespace_3, *str_case, *str_built;
str_base = qstring_from_str("foo");
str_whitespace_0 = qstring_from_str(" foo");
str_whitespace_1 = qstring_from_str("foo ");
str_whitespace_2 = qstring_from_str("foo\b");
str_whitespace_3 = qstring_from_str("fooo\b");
str_case = qstring_from_str("Foo");
/* Should yield "foo" */
str_built = qstring_from_substr("form", 0, 1);
qstring_append_chr(str_built, 'o');
check_unequal(str_base, str_whitespace_0, str_whitespace_1,
str_whitespace_2, str_whitespace_3, str_case);
check_equal(str_base, str_built);
free_all(str_base, str_whitespace_0, str_whitespace_1, str_whitespace_2,
str_whitespace_3, str_case, str_built);
}
static void qobject_is_equal_list_test(void)
{
QList *list_0, *list_1, *list_cloned;
QList *list_reordered, *list_longer, *list_shorter;
list_0 = qlist_new();
list_1 = qlist_new();
list_reordered = qlist_new();
list_longer = qlist_new();
list_shorter = qlist_new();
qlist_append_int(list_0, 1);
qlist_append_int(list_0, 2);
qlist_append_int(list_0, 3);
qlist_append_int(list_1, 1);
qlist_append_int(list_1, 2);
qlist_append_int(list_1, 3);
qlist_append_int(list_reordered, 1);
qlist_append_int(list_reordered, 3);
qlist_append_int(list_reordered, 2);
qlist_append_int(list_longer, 1);
qlist_append_int(list_longer, 2);
qlist_append_int(list_longer, 3);
qlist_append_null(list_longer);
qlist_append_int(list_shorter, 1);
qlist_append_int(list_shorter, 2);
list_cloned = qlist_copy(list_0);
check_equal(list_0, list_1, list_cloned);
check_unequal(list_0, list_reordered, list_longer, list_shorter);
/* With a NaN in it, the list should no longer compare equal to
* itself */
qlist_append(list_0, qnum_from_double(NAN));
g_assert(qobject_is_equal(QOBJECT(list_0), QOBJECT(list_0)) == false);
free_all(list_0, list_1, list_cloned, list_reordered, list_longer,
list_shorter);
}
static void qobject_is_equal_dict_test(void)
{
Error *local_err = NULL;
QDict *dict_0, *dict_1, *dict_cloned;
QDict *dict_different_key, *dict_different_value, *dict_different_null_key;
QDict *dict_longer, *dict_shorter, *dict_nested;
QDict *dict_crumpled;
dict_0 = qdict_new();
dict_1 = qdict_new();
dict_different_key = qdict_new();
dict_different_value = qdict_new();
dict_different_null_key = qdict_new();
dict_longer = qdict_new();
dict_shorter = qdict_new();
dict_nested = qdict_new();
qdict_put_int(dict_0, "f.o", 1);
qdict_put_int(dict_0, "bar", 2);
qdict_put_int(dict_0, "baz", 3);
qdict_put_null(dict_0, "null");
qdict_put_int(dict_1, "f.o", 1);
qdict_put_int(dict_1, "bar", 2);
qdict_put_int(dict_1, "baz", 3);
qdict_put_null(dict_1, "null");
qdict_put_int(dict_different_key, "F.o", 1);
qdict_put_int(dict_different_key, "bar", 2);
qdict_put_int(dict_different_key, "baz", 3);
qdict_put_null(dict_different_key, "null");
qdict_put_int(dict_different_value, "f.o", 42);
qdict_put_int(dict_different_value, "bar", 2);
qdict_put_int(dict_different_value, "baz", 3);
qdict_put_null(dict_different_value, "null");
qdict_put_int(dict_different_null_key, "f.o", 1);
qdict_put_int(dict_different_null_key, "bar", 2);
qdict_put_int(dict_different_null_key, "baz", 3);
qdict_put_null(dict_different_null_key, "none");
qdict_put_int(dict_longer, "f.o", 1);
qdict_put_int(dict_longer, "bar", 2);
qdict_put_int(dict_longer, "baz", 3);
qdict_put_int(dict_longer, "xyz", 4);
qdict_put_null(dict_longer, "null");
qdict_put_int(dict_shorter, "f.o", 1);
qdict_put_int(dict_shorter, "bar", 2);
qdict_put_int(dict_shorter, "baz", 3);
qdict_put(dict_nested, "f", qdict_new());
qdict_put_int(qdict_get_qdict(dict_nested, "f"), "o", 1);
qdict_put_int(dict_nested, "bar", 2);
qdict_put_int(dict_nested, "baz", 3);
qdict_put_null(dict_nested, "null");
dict_cloned = qdict_clone_shallow(dict_0);
check_equal(dict_0, dict_1, dict_cloned);
check_unequal(dict_0, dict_different_key, dict_different_value,
dict_different_null_key, dict_longer, dict_shorter,
dict_nested);
dict_crumpled = qobject_to_qdict(qdict_crumple(dict_1, &local_err));
g_assert(!local_err);
check_equal(dict_crumpled, dict_nested);
qdict_flatten(dict_nested);
check_equal(dict_0, dict_nested);
/* Containing an NaN value will make this dict compare unequal to
* itself */
qdict_put(dict_0, "NaN", qnum_from_double(NAN));
g_assert(qobject_is_equal(QOBJECT(dict_0), QOBJECT(dict_0)) == false);
free_all(dict_0, dict_1, dict_cloned, dict_different_key,
dict_different_value, dict_different_null_key, dict_longer,
dict_shorter, dict_nested, dict_crumpled);
}
static void qobject_is_equal_conversion_test(void)
{
QNum *u0, *i0, *d0;
QString *s0, *s_empty;
QBool *bfalse;
u0 = qnum_from_uint(0u);
i0 = qnum_from_int(0);
d0 = qnum_from_double(0.0);
s0 = qstring_from_str("0");
s_empty = qstring_new();
bfalse = qbool_from_bool(false);
/* No automatic type conversion */
check_unequal(u0, s0, s_empty, bfalse, qnull(), NULL);
check_unequal(i0, s0, s_empty, bfalse, qnull(), NULL);
check_unequal(d0, s0, s_empty, bfalse, qnull(), NULL);
free_all(u0, i0, d0, s0, s_empty, bfalse);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/public/qobject_is_equal_null",
qobject_is_equal_null_test);
g_test_add_func("/public/qobject_is_equal_num", qobject_is_equal_num_test);
g_test_add_func("/public/qobject_is_equal_bool",
qobject_is_equal_bool_test);
g_test_add_func("/public/qobject_is_equal_string",
qobject_is_equal_string_test);
g_test_add_func("/public/qobject_is_equal_list",
qobject_is_equal_list_test);
g_test_add_func("/public/qobject_is_equal_dict",
qobject_is_equal_dict_test);
g_test_add_func("/public/qobject_is_equal_conversion",
qobject_is_equal_conversion_test);
return g_test_run();
}

View File

@ -110,6 +110,33 @@ for offset in $TEST_OFFSETS; do
io_zero readv $(( offset + 64 * 1024 + 65536 * 4 )) 65536 65536 1 io_zero readv $(( offset + 64 * 1024 + 65536 * 4 )) 65536 65536 1
done done
_check_test_img _check_test_img
_cleanup
TEST_IMG=$TEST_IMG_SAVE
echo
echo 'Testing failing commit'
echo
# Create an image with a null backing file to which committing will fail (with
# ENOSPC so we can distinguish the result from some generic EIO which may be
# generated anywhere in the block layer)
_make_test_img -b "json:{'driver': 'raw',
'file': {
'driver': 'blkdebug',
'inject-error': [{
'event': 'write_aio',
'errno': 28,
'once': true
}],
'image': {
'driver': 'null-co'
}}}"
# Just write anything so committing will not be a no-op
$QEMU_IO -c 'writev 0 64k' "$TEST_IMG" | _filter_qemu_io
$QEMU_IMG commit "$TEST_IMG"
_cleanup_test_img
# success, all done # success, all done
echo "*** done" echo "*** done"

View File

@ -1075,4 +1075,21 @@ read 65536/65536 bytes at offset 4295098368
read 65536/65536 bytes at offset 4295294976 read 65536/65536 bytes at offset 4295294976
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
No errors were found on the image. No errors were found on the image.
Testing failing commit
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 backing_file=json:{'driver': 'raw',,
'file': {
'driver': 'blkdebug',,
'inject-error': [{
'event': 'write_aio',,
'errno': 28,,
'once': true
}],,
'image': {
'driver': 'null-co'
}}}
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qemu-img: Block job failed: No space left on device
*** done *** done

View File

@ -301,6 +301,131 @@ _make_test_img 64M
poke_file "$TEST_IMG" "48" "\x00\x00\x00\x00\x00\x00\x00\x00" poke_file "$TEST_IMG" "48" "\x00\x00\x00\x00\x00\x00\x00\x00"
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io $QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
echo
echo "=== Testing dirty corrupt image ==="
echo
_make_test_img 64M
# Let the refblock appear unaligned
poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\xff\xff\x2a\x00"
# Mark the image dirty, thus forcing an automatic check when opening it
poke_file "$TEST_IMG" 72 "\x00\x00\x00\x00\x00\x00\x00\x01"
# Open the image (qemu should refuse to do so)
$QEMU_IO -c close "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
echo '--- Repairing ---'
# The actual repair should have happened (because of the dirty bit),
# but some cleanup may have failed (like freeing the old reftable)
# because the image was already marked corrupt by that point
_check_test_img -r all
echo
echo "=== Writing to an unaligned preallocated zero cluster ==="
echo
_make_test_img 64M
# Allocate the L2 table
$QEMU_IO -c "write 0 64k" -c "discard 0 64k" "$TEST_IMG" | _filter_qemu_io
# Pretend there is a preallocated zero cluster somewhere inside the
# image header
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x00\x2a\x01"
# Let's write to it!
$QEMU_IO -c "write 0 64k" "$TEST_IMG" | _filter_qemu_io
# Can't repair this yet (TODO: We can just deallocate the cluster)
echo
echo '=== Discarding with an unaligned refblock ==='
echo
_make_test_img 64M
$QEMU_IO -c "write 0 128k" "$TEST_IMG" | _filter_qemu_io
# Make our refblock unaligned
poke_file "$TEST_IMG" "$(($rt_offset))" "\x00\x00\x00\x00\x00\x00\x2a\x00"
# Now try to discard something that will be submitted as two requests
# (main part + tail)
$QEMU_IO -c "discard 0 65537" "$TEST_IMG"
echo '--- Repairing ---'
# Fails the first repair because the corruption prevents the check
# function from double-checking
# (Using -q for the first invocation, because otherwise the
# double-check error message appears above the summary for some
# reason -- so let's just hide the summary)
_check_test_img -q -r all
_check_test_img -r all
echo
echo "=== Discarding an out-of-bounds refblock ==="
echo
_make_test_img 64M
# Pretend there's a refblock really up high
poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\xff\xff\xff\x00\x00\x00\x00"
# Let's try to shrink the qcow2 image so that the block driver tries
# to discard that refblock (and see what happens!)
$QEMU_IMG resize --shrink "$TEST_IMG" 32M
echo '--- Checking and retrying ---'
# Image should not be resized
_img_info | grep 'virtual size'
# But it should pass this check, because the "partial" resize has
# already overwritten refblocks past the end
_check_test_img -r all
# So let's try again
$QEMU_IMG resize --shrink "$TEST_IMG" 32M
_img_info | grep 'virtual size'
echo
echo "=== Discarding a non-covered in-bounds refblock ==="
echo
IMGOPTS='refcount_bits=1' _make_test_img 64M
# Pretend there's a refblock somewhere where there is no refblock to
# cover it (but the covering refblock has a valid index in the
# reftable)
# Every refblock covers 65536 * 8 * 65536 = 32 GB, so we have to point
# to 0x10_0000_0000 (64G) to point to the third refblock
poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00"
$QEMU_IMG resize --shrink "$TEST_IMG" 32M
echo '--- Checking and retrying ---'
# Image should not be resized
_img_info | grep 'virtual size'
# But it should pass this check, because the "partial" resize has
# already overwritten refblocks past the end
_check_test_img -r all
# So let's try again
$QEMU_IMG resize --shrink "$TEST_IMG" 32M
_img_info | grep 'virtual size'
echo
echo "=== Discarding a refblock covered by an unaligned refblock ==="
echo
IMGOPTS='refcount_bits=1' _make_test_img 64M
# Same as above
poke_file "$TEST_IMG" "$(($rt_offset+8))" "\x00\x00\x00\x10\x00\x00\x00\x00"
# But now we actually "create" an unaligned third refblock
poke_file "$TEST_IMG" "$(($rt_offset+16))" "\x00\x00\x00\x00\x00\x00\x02\x00"
$QEMU_IMG resize --shrink "$TEST_IMG" 32M
echo '--- Repairing ---'
# Fails the first repair because the corruption prevents the check
# function from double-checking
# (Using -q for the first invocation, because otherwise the
# double-check error message appears above the summary for some
# reason -- so let's just hide the summary)
_check_test_img -q -r all
_check_test_img -r all
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -284,4 +284,119 @@ No errors were found on the image.
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed qcow2: Marking image as corrupt: Preventing invalid allocation of L2 table at offset 0; further corruption events will be suppressed
write failed: Input/output error write failed: Input/output error
=== Testing dirty corrupt image ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
ERROR refcount block 0 is not cluster aligned; refcount table entry corrupted
IMGFMT: Marking image as corrupt: Refblock offset 0xffff2a00 unaligned (reftable index: 0); further corruption events will be suppressed
Can't get refcount for cluster 0: Input/output error
Can't get refcount for cluster 1: Input/output error
Can't get refcount for cluster 2: Input/output error
Can't get refcount for cluster 3: Input/output error
Rebuilding refcount structure
Repairing cluster 1 refcount=1 reference=0
can't open device TEST_DIR/t.IMGFMT: Could not repair dirty image: Input/output error
--- Repairing ---
Leaked cluster 1 refcount=1 reference=0
Repairing cluster 1 refcount=1 reference=0
The following inconsistencies were found and repaired:
1 leaked clusters
0 corruptions
Double checking the fixed image now...
No errors were found on the image.
=== Writing to an unaligned preallocated zero cluster ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
discard 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: Preallocated zero cluster offset 0x2a00 unaligned (guest offset: 0); further corruption events will be suppressed
write failed: Input/output error
=== Discarding with an unaligned refblock ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
wrote 131072/131072 bytes at offset 0
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
qcow2: Marking image as corrupt: Refblock offset 0x2a00 unaligned (reftable index: 0); further corruption events will be suppressed
qcow2_free_clusters failed: Input/output error
discard failed: No medium found
--- Repairing ---
ERROR refcount block 0 is not cluster aligned; refcount table entry corrupted
qcow2: Marking image as corrupt: Refblock offset 0x2a00 unaligned (reftable index: 0); further corruption events will be suppressed
Can't get refcount for cluster 0: Input/output error
Can't get refcount for cluster 1: Input/output error
Can't get refcount for cluster 2: Input/output error
Can't get refcount for cluster 3: Input/output error
Can't get refcount for cluster 4: Input/output error
Can't get refcount for cluster 5: Input/output error
Can't get refcount for cluster 6: Input/output error
Rebuilding refcount structure
Repairing cluster 1 refcount=1 reference=0
qemu-img: Check failed: No medium found
Leaked cluster 1 refcount=1 reference=0
Repairing cluster 1 refcount=1 reference=0
The following inconsistencies were found and repaired:
1 leaked clusters
0 corruptions
Double checking the fixed image now...
No errors were found on the image.
=== Discarding an out-of-bounds refblock ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qcow2: Marking image as corrupt: Refblock at 0xffffff00000000 is not covered by the refcount structures; further corruption events will be suppressed
qemu-img: Failed to discard unused refblocks: Input/output error
--- Checking and retrying ---
virtual size: 64M (67108864 bytes)
No errors were found on the image.
Image resized.
virtual size: 32M (33554432 bytes)
=== Discarding a non-covered in-bounds refblock ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qcow2: Marking image as corrupt: Refblock at 0x1000000000 is not covered by the refcount structures; further corruption events will be suppressed
qemu-img: Failed to discard unused refblocks: Input/output error
--- Checking and retrying ---
virtual size: 64M (67108864 bytes)
No errors were found on the image.
Image resized.
virtual size: 32M (33554432 bytes)
=== Discarding a refblock covered by an unaligned refblock ===
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qcow2: Marking image as corrupt: Cannot get entry from refcount block cache: Offset 0x200 is unaligned; further corruption events will be suppressed
qemu-img: Failed to discard unused refblocks: Input/output error
--- Repairing ---
Repairing refcount block 1 is outside image
ERROR refcount block 2 is not cluster aligned; refcount table entry corrupted
qcow2: Marking image as corrupt: Refblock offset 0x200 unaligned (reftable index: 0x2); further corruption events will be suppressed
Can't get refcount for cluster 1048576: Input/output error
Rebuilding refcount structure
Repairing cluster 1 refcount=1 reference=0
Repairing cluster 2 refcount=1 reference=0
Repairing cluster 1048576 refcount=1 reference=0
qemu-img: Check failed: No medium found
Leaked cluster 1 refcount=1 reference=0
Leaked cluster 2 refcount=1 reference=0
Leaked cluster 1048576 refcount=1 reference=0
Repairing cluster 1 refcount=1 reference=0
Repairing cluster 2 refcount=1 reference=0
Repairing cluster 1048576 refcount=1 reference=0
The following inconsistencies were found and repaired:
3 leaked clusters
0 corruptions
Double checking the fixed image now...
No errors were found on the image.
*** done *** done

View File

@ -102,7 +102,14 @@ echo
echo === aio=native without O_DIRECT === echo === aio=native without O_DIRECT ===
echo echo
run_qemu <<EOF # Skip this test if AIO is not enabled in this build
function run_qemu_filter_aio()
{
run_qemu "$@" | \
sed -e 's/is not supported in this build/it requires cache.direct=on, which was not specified/'
}
run_qemu_filter_aio <<EOF
{ "execute": "qmp_capabilities" } { "execute": "qmp_capabilities" }
{ "execute": "blockdev-add", { "execute": "blockdev-add",
"arguments": { "arguments": {

View File

@ -83,6 +83,15 @@ $QEMU_IO -c 'reopen -o driver=qcow2' $TEST_IMG
$QEMU_IO -c 'reopen -o file.driver=file' $TEST_IMG $QEMU_IO -c 'reopen -o file.driver=file' $TEST_IMG
$QEMU_IO -c 'reopen -o backing.driver=qcow2' $TEST_IMG $QEMU_IO -c 'reopen -o backing.driver=qcow2' $TEST_IMG
echo
echo "=== Check that reopening works with non-string options ==="
echo
# Using the json: pseudo-protocol we can create non-string options
# (Invoke 'info' just so we get some output afterwards)
IMGOPTSSYNTAX=false $QEMU_IO -f null-co -c 'reopen' -c 'info' \
"json:{'driver': 'null-co', 'size': 65536}"
# success, all done # success, all done
echo "*** done" echo "*** done"
rm -f $seq.full rm -f $seq.full

View File

@ -19,4 +19,9 @@ Cannot change the option 'driver'
=== Check that unchanged driver is okay === === Check that unchanged driver is okay ===
=== Check that reopening works with non-string options ===
format name: null-co
format name: null-co
*** done *** done

View File

@ -3,10 +3,11 @@
# Commit changes into backing chains and empty the top image if the # Commit changes into backing chains and empty the top image if the
# backing image is not explicitly specified. # backing image is not explicitly specified.
# #
# Variant of 097, which includes snapshots to test different codepath # Variant of 097, which includes snapshots and persistent bitmaps, to
# in qcow2 # tickle the slow codepath in qcow2. See also 198, for another feature
# that tickles the slow codepath.
# #
# Copyright (C) 2014 Red Hat, Inc. # Copyright (C) 2014, 2017 Red Hat, Inc.
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -43,11 +44,18 @@ trap "_cleanup; exit \$status" 0 1 2 3 15
. ./common.filter . ./common.filter
. ./common.pattern . ./common.pattern
# Any format supporting backing files and bdrv_make_empty # This test is specific to qcow2
_supported_fmt qcow2 _supported_fmt qcow2
_supported_proto file _supported_proto file
_supported_os Linux _supported_os Linux
function run_qemu()
{
$QEMU -nographic -qmp stdio -serial none "$@" 2>&1 \
| _filter_testdir | _filter_qmp | _filter_qemu
}
for reason in snapshot bitmap; do
# Four passes: # Four passes:
# 0: Two-layer backing chain, commit to upper backing file (implicitly) # 0: Two-layer backing chain, commit to upper backing file (implicitly)
@ -66,14 +74,29 @@ _supported_os Linux
for i in 0 1 2 3; do for i in 0 1 2 3; do
echo echo
echo "=== Test pass $i ===" echo "=== Test pass $reason.$i ==="
echo echo
len=$((2100 * 1024 * 1024 + 512)) # larger than 2G, and not cluster aligned len=$((2100 * 1024 * 1024 + 512)) # larger than 2G, and not cluster aligned
TEST_IMG="$TEST_IMG.base" _make_test_img $len TEST_IMG="$TEST_IMG.base" _make_test_img $len
TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" $len TEST_IMG="$TEST_IMG.itmd" _make_test_img -b "$TEST_IMG.base" $len
_make_test_img -b "$TEST_IMG.itmd" $len _make_test_img -b "$TEST_IMG.itmd" $len
$QEMU_IMG snapshot -c snap "$TEST_IMG" # Update the top image to use a feature that is incompatible with fast path
case $reason in
snapshot) $QEMU_IMG snapshot -c snap "$TEST_IMG" ;;
bitmap)
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": { "driver": "qcow2", "node-name": "drive0",
"file": { "driver": "file", "filename": "$TEST_IMG" } } }
{ "execute": "block-dirty-bitmap-add",
"arguments": { "node": "drive0", "name": "bitmap0",
"persistent": true, "autoload": true } }
{ "execute": "quit" }
EOF
;;
esac
$QEMU_IO -c "write -P 1 0x7ffd0000 192k" "$TEST_IMG.base" | _filter_qemu_io $QEMU_IO -c "write -P 1 0x7ffd0000 192k" "$TEST_IMG.base" | _filter_qemu_io
$QEMU_IO -c "write -P 2 0x7ffe0000 128k" "$TEST_IMG.itmd" | _filter_qemu_io $QEMU_IO -c "write -P 2 0x7ffe0000 128k" "$TEST_IMG.itmd" | _filter_qemu_io
@ -122,8 +145,26 @@ $QEMU_IMG map "$TEST_IMG.base" | _filter_qemu_img_map
$QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map $QEMU_IMG map "$TEST_IMG.itmd" | _filter_qemu_img_map
$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map $QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map
done # Check that the reason for slow path is still present, as appropriate
case $reason in
snapshot)
$QEMU_IMG snapshot -l "$TEST_IMG" |
sed 's/^\(.\{20\}\).*/\1/; s/ *$//' ;;
bitmap)
run_qemu <<EOF
{ "execute": "qmp_capabilities" }
{ "execute": "blockdev-add",
"arguments": { "driver": "qcow2", "node-name": "drive0",
"file": { "driver": "file", "filename": "$TEST_IMG" } } }
{ "execute": "x-debug-block-dirty-bitmap-sha256",
"arguments": { "node": "drive0", "name": "bitmap0" } }
{ "execute": "quit" }
EOF
;;
esac
done
done
# success, all done # success, all done
echo "*** done" echo "*** done"

View File

@ -1,6 +1,6 @@
QA output created by 176 QA output created by 176
=== Test pass 0 === === Test pass snapshot.0 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
@ -36,8 +36,11 @@ Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base 0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd 0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd 0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd
Snapshot list:
ID TAG
1 snap
=== Test pass 1 === === Test pass snapshot.1 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
@ -74,8 +77,11 @@ Offset Length File
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT
Snapshot list:
ID TAG
1 snap
=== Test pass 2 === === Test pass snapshot.2 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
@ -112,8 +118,11 @@ Offset Length File
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT
Snapshot list:
ID TAG
1 snap
=== Test pass 3 === === Test pass snapshot.3 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112 Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
@ -147,4 +156,203 @@ Offset Length File
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd 0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT 0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT 0x83400000 0x200 TEST_DIR/t.IMGFMT
Snapshot list:
ID TAG
1 snap
=== Test pass bitmap.0 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image committed.
read 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147287040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147352576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Test pass bitmap.1 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image committed.
read 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147287040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147352576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Test pass bitmap.2 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image committed.
read 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147287040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147352576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.itmd
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
=== Test pass bitmap.3 ===
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=2202010112
Formatting 'TEST_DIR/t.IMGFMT.itmd', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.base
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2202010112 backing_file=TEST_DIR/t.IMGFMT.itmd
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
wrote 196608/196608 bytes at offset 2147287040
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 131072/131072 bytes at offset 2147352576
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Image committed.
read 65536/65536 bytes at offset 2147287040
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147352576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 2147418112
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 512/512 bytes at offset 2202009600
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Offset Length File
0x7ffd0000 0x30000 TEST_DIR/t.IMGFMT.base
0x83400000 0x200 TEST_DIR/t.IMGFMT.base
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x20000 TEST_DIR/t.IMGFMT.itmd
0x83400000 0x200 TEST_DIR/t.IMGFMT.base
Offset Length File
0x7ffd0000 0x10000 TEST_DIR/t.IMGFMT.base
0x7ffe0000 0x10000 TEST_DIR/t.IMGFMT.itmd
0x7fff0000 0x10000 TEST_DIR/t.IMGFMT
0x83400000 0x200 TEST_DIR/t.IMGFMT
QMP_VERSION
{"return": {}}
{"return": {}}
{"return": {"sha256": "e12600978d86b5a453861ae5c17d275204673fef3874b7c3c5433c6153d84706"}}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
*** done *** done

View File

@ -62,7 +62,7 @@ _launch_qemu -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
echo echo
echo "Starting a second QEMU using the same image should fail" echo "Starting a second QEMU using the same image should fail"
echo 'quit' | $QEMU -monitor stdio \ echo 'quit' | $QEMU -nographic -monitor stdio \
-drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \ -drive file=$TEST_IMG,if=none,id=drive0,file.locking=on \
-device $virtioblk,drive=drive0 2>&1 | _filter_testdir 2>&1 | -device $virtioblk,drive=drive0 2>&1 | _filter_testdir 2>&1 |
_filter_qemu | _filter_qemu |

66
tests/qemu-iotests/196 Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env python
#
# Test clearing unknown autoclear_features flag by qcow2 after
# migration. This test mimics migration to older qemu.
#
# Copyright (c) 2017 Parallels International GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import iotests
from iotests import qemu_img
disk = os.path.join(iotests.test_dir, 'disk')
migfile = os.path.join(iotests.test_dir, 'migfile')
class TestInvalidateAutoclear(iotests.QMPTestCase):
def tearDown(self):
self.vm_a.shutdown()
self.vm_b.shutdown()
os.remove(disk)
os.remove(migfile)
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, disk, '1M')
self.vm_a = iotests.VM(path_suffix='a').add_drive(disk)
self.vm_a.launch()
self.vm_b = iotests.VM(path_suffix='b').add_drive(disk)
self.vm_b.add_incoming("exec: cat '" + migfile + "'")
def test_migration(self):
result = self.vm_a.qmp('migrate', uri='exec:cat>' + migfile)
self.assert_qmp(result, 'return', {});
self.assertNotEqual(self.vm_a.event_wait("STOP"), None)
with open(disk, 'r+b') as f:
f.seek(88) # first byte of autoclear_features field
f.write(b'\xff')
self.vm_b.launch()
while True:
result = self.vm_b.qmp('query-status')
if result['return']['status'] == 'running':
break
with open(disk, 'rb') as f:
f.seek(88)
self.assertEqual(f.read(1), b'\x00')
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'])

View File

@ -0,0 +1,5 @@
.
----------------------------------------------------------------------
Ran 1 tests
OK

104
tests/qemu-iotests/198 Executable file
View File

@ -0,0 +1,104 @@
#!/bin/bash
#
# Test commit of encrypted qcow2 files
#
# Copyright (C) 2017 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=berrange@redhat.com
seq=`basename $0`
echo "QA output created by $seq"
here=`pwd`
status=1 # failure is the default!
_cleanup()
{
_cleanup_test_img
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
_supported_fmt qcow2
_supported_proto generic
_supported_os Linux
size=16M
TEST_IMG_BASE=$TEST_IMG.base
SECRET0="secret,id=sec0,data=astrochicken"
SECRET1="secret,id=sec1,data=furby"
TEST_IMG_SAVE=$TEST_IMG
TEST_IMG=$TEST_IMG_BASE
echo "== create base =="
_make_test_img --object $SECRET0 -o "encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10" $size
TEST_IMG=$TEST_IMG_SAVE
IMGSPECBASE="driver=$IMGFMT,file.filename=$TEST_IMG_BASE,encrypt.key-secret=sec0"
IMGSPECLAYER="driver=$IMGFMT,file.filename=$TEST_IMG,encrypt.key-secret=sec1"
IMGSPEC="$IMGSPECLAYER,backing.driver=$IMGFMT,backing.file.filename=$TEST_IMG_BASE,backing.encrypt.key-secret=sec0"
QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT
echo
echo "== writing whole image base =="
$QEMU_IO --object $SECRET0 -c "write -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo "== create overlay =="
_make_test_img --object $SECRET1 -o "encrypt.format=luks,encrypt.key-secret=sec1,encrypt.iter-time=10" -u -b "$TEST_IMG_BASE" $size
echo
echo "== writing whole image layer =="
$QEMU_IO --object $SECRET0 --object $SECRET1 -c "write -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern base =="
$QEMU_IO --object $SECRET0 -c "read -P 0xa 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern layer =="
$QEMU_IO --object $SECRET0 --object $SECRET1 -c "read -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
echo
echo "== committing layer into base =="
$QEMU_IMG commit --object $SECRET0 --object $SECRET1 --image-opts $IMGSPEC | _filter_testdir
echo
echo "== verify pattern base =="
$QEMU_IO --object $SECRET0 -c "read -P 0x9 0 $size" --image-opts $IMGSPECBASE | _filter_qemu_io | _filter_testdir
echo
echo "== verify pattern layer =="
$QEMU_IO --object $SECRET0 --object $SECRET1 -c "read -P 0x9 0 $size" --image-opts $IMGSPEC | _filter_qemu_io | _filter_testdir
echo
echo "== checking image base =="
$QEMU_IMG info --image-opts $IMGSPECBASE | _filter_img_info | _filter_testdir | sed -e "/^disk size:/ D"
echo
echo "== checking image layer =="
$QEMU_IMG info --image-opts $IMGSPECLAYER | _filter_img_info | _filter_testdir | sed -e "/^disk size:/ D"
# success, all done
echo "*** done"
rm -f $seq.full
status=0

126
tests/qemu-iotests/198.out Normal file
View File

@ -0,0 +1,126 @@
QA output created by 198
== create base ==
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=16777216 encrypt.format=luks encrypt.key-secret=sec0 encrypt.iter-time=10
== writing whole image base ==
wrote 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== create overlay ==
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 backing_file=TEST_DIR/t.IMGFMT.base encrypt.format=luks encrypt.key-secret=sec1 encrypt.iter-time=10
== writing whole image layer ==
wrote 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== verify pattern base ==
read 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== verify pattern layer ==
read 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== committing layer into base ==
Image committed.
== verify pattern base ==
read 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== verify pattern layer ==
read 16777216/16777216 bytes at offset 0
16 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
== checking image base ==
image: json:{"encrypt.key-secret": "sec0", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT.base"}}
file format: IMGFMT
virtual size: 16M (16777216 bytes)
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
encrypt:
ivgen alg: plain64
hash alg: sha256
cipher alg: aes-256
uuid: 00000000-0000-0000-0000-000000000000
format: luks
cipher mode: xts
slots:
[0]:
active: true
iters: 1024
key offset: 4096
stripes: 4000
[1]:
active: false
key offset: 262144
[2]:
active: false
key offset: 520192
[3]:
active: false
key offset: 778240
[4]:
active: false
key offset: 1036288
[5]:
active: false
key offset: 1294336
[6]:
active: false
key offset: 1552384
[7]:
active: false
key offset: 1810432
payload offset: 2068480
master key iters: 1024
corrupt: false
== checking image layer ==
image: json:{"encrypt.key-secret": "sec1", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}}
file format: IMGFMT
virtual size: 16M (16777216 bytes)
backing file: TEST_DIR/t.IMGFMT.base
Format specific information:
compat: 1.1
lazy refcounts: false
refcount bits: 16
encrypt:
ivgen alg: plain64
hash alg: sha256
cipher alg: aes-256
uuid: 00000000-0000-0000-0000-000000000000
format: luks
cipher mode: xts
slots:
[0]:
active: true
iters: 1024
key offset: 4096
stripes: 4000
[1]:
active: false
key offset: 262144
[2]:
active: false
key offset: 520192
[3]:
active: false
key offset: 778240
[4]:
active: false
key offset: 1036288
[5]:
active: false
key offset: 1294336
[6]:
active: false
key offset: 1552384
[7]:
active: false
key offset: 1810432
payload offset: 2068480
master key iters: 1024
corrupt: false
*** done

View File

@ -157,7 +157,9 @@ _filter_img_info()
-e "/lazy_refcounts: \\(on\\|off\\)/d" \ -e "/lazy_refcounts: \\(on\\|off\\)/d" \
-e "/block_size: [0-9]\\+/d" \ -e "/block_size: [0-9]\\+/d" \
-e "/block_state_zero: \\(on\\|off\\)/d" \ -e "/block_state_zero: \\(on\\|off\\)/d" \
-e "/log_size: [0-9]\\+/d" -e "/log_size: [0-9]\\+/d" \
-e "s/iters: [0-9]\\+/iters: 1024/" \
-e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/"
} }
# filter out offsets and file names from qemu-img map; good for both # filter out offsets and file names from qemu-img map; good for both

View File

@ -193,4 +193,6 @@
192 rw auto quick 192 rw auto quick
194 rw auto migration quick 194 rw auto migration quick
195 rw auto quick 195 rw auto quick
196 rw auto quick
197 rw auto quick 197 rw auto quick
198 rw auto