mirror of
https://github.com/qemu/qemu.git
synced 2024-11-27 05:43:47 +08:00
nbd patches for 2021-03-09
- Add Vladimir as NBD co-maintainer - Fix reporting of holes in NBD_CMD_BLOCK_STATUS - Improve command-line parsing accuracy of large numbers (anything going through qemu_strtosz), including the deprecation of hex+suffix - Improve some error reporting in the block layer -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEccLMIrHEYCkn0vOqp6FrSiUnQ2oFAmBHlmIACgkQp6FrSiUn Q2q2cQgAqJWNb4J/ShjvzocDDPzJ0iBitFbg0huFPfbt4DScubEZo5wBJG7vOhOW hIHrWCRzGvRgsn0tcSfrgFaegmHKrLgjkibM7ou8ni9NC1kUBd3R/3FBNIMxhYf7 Q8Kfspl0LRfMJDKF9jdCnQ4Gxcd6h2OIYZqiWVg8V4Tc8WdCpIVOah7e7wjuW8bT vgZvfboUWm5AmIF9j/MxuMn+HFZ4ArSuFVL80ZaXlD00vRra7u3HZ8pUfcOlOujg 7HeouM1E5j3NNE6aZSN++x/EQ3sg0zmirbWUCcgAyRfdRkAmB15uh2PUzPxEIJKH UHUIW5LvNtz2+yzOAz2yK29OE523Yg== =blE1 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/ericb/tags/pull-nbd-2021-03-09' into staging nbd patches for 2021-03-09 - Add Vladimir as NBD co-maintainer - Fix reporting of holes in NBD_CMD_BLOCK_STATUS - Improve command-line parsing accuracy of large numbers (anything going through qemu_strtosz), including the deprecation of hex+suffix - Improve some error reporting in the block layer # gpg: Signature made Tue 09 Mar 2021 15:38:10 GMT # gpg: using RSA key 71C2CC22B1C4602927D2F3AAA7A16B4A2527436A # gpg: Good signature from "Eric Blake <eblake@redhat.com>" [full] # gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>" [full] # gpg: aka "[jpeg image of size 6874]" [full] # Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A * remotes/ericb/tags/pull-nbd-2021-03-09: block/qcow2: refactor qcow2_update_options_prepare error paths block/qed: bdrv_qed_do_open: deal with errp block/qcow2: simplify qcow2_co_invalidate_cache() block/qcow2: read_cache_sizes: return status value block/qcow2-bitmap: return status from qcow2_store_persistent_dirty_bitmaps block/qcow2-bitmap: improve qcow2_load_dirty_bitmaps() interface block/qcow2: qcow2_get_specific_info(): drop error propagation blockjob: return status from block_job_set_speed() block/mirror: drop extra error propagation in commit_active_start() block: drop extra error propagation for bdrv_set_backing_hd blockdev: fix drive_backup_prepare() missed error block: check return value of bdrv_open_child and drop error propagation utils: Deprecate hex-with-suffix sizes utils: Improve qemu_strtosz() to have 64 bits of precision utils: Enhance testsuite for do_strtosz() nbd: server: Report holes for raw images MAINTAINERS: add Vladimir as co-maintainer of NBD Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
9abda42bf2
@ -3050,6 +3050,7 @@ F: block/iscsi-opts.c
|
||||
|
||||
Network Block Device (NBD)
|
||||
M: Eric Blake <eblake@redhat.com>
|
||||
M: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Maintained
|
||||
F: block/nbd*
|
||||
@ -3060,6 +3061,7 @@ F: blockdev-nbd.c
|
||||
F: docs/interop/nbd.txt
|
||||
F: docs/interop/qemu-nbd.rst
|
||||
T: git https://repo.or.cz/qemu/ericb.git nbd
|
||||
T: git https://src.openvz.org/scm/~vsementsov/qemu.git nbd
|
||||
|
||||
NFS
|
||||
M: Peter Lieven <pl@kamp.de>
|
||||
|
6
block.c
6
block.c
@ -2995,11 +2995,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
||||
|
||||
/* Hook up the backing file link; drop our reference, bs owns the
|
||||
* backing_hd reference now */
|
||||
bdrv_set_backing_hd(bs, backing_hd, &local_err);
|
||||
ret = bdrv_set_backing_hd(bs, backing_hd, errp);
|
||||
bdrv_unref(backing_hd);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
if (ret < 0) {
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
|
@ -464,7 +464,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
uint64_t align;
|
||||
|
||||
@ -494,10 +493,9 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
|
||||
bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -157,19 +157,17 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Open the file */
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
errp);
|
||||
if (!bs->file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the log file */
|
||||
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_of_bds,
|
||||
BDRV_CHILD_METADATA, false, &local_err);
|
||||
if (local_err) {
|
||||
BDRV_CHILD_METADATA, false, errp);
|
||||
if (!s->log_file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -23,16 +23,14 @@ typedef struct Request {
|
||||
static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/* Open the image file */
|
||||
bs->file = bdrv_open_child(NULL, options, "image", bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,6 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
{
|
||||
BDRVBlkverifyState *s = bs->opaque;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
@ -125,20 +124,18 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-raw"), options, "raw",
|
||||
bs, &child_of_bds,
|
||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Open the test file */
|
||||
s->test_file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options,
|
||||
"test", bs, &child_of_bds, BDRV_CHILD_DATA,
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
false, errp);
|
||||
if (!s->test_file) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -1860,8 +1860,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
bool auto_complete, Error **errp)
|
||||
{
|
||||
bool base_read_only;
|
||||
Error *local_err = NULL;
|
||||
BlockJob *ret;
|
||||
BlockJob *job;
|
||||
|
||||
base_read_only = bdrv_is_read_only(base);
|
||||
|
||||
@ -1871,19 +1870,18 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
ret = mirror_start_job(
|
||||
job = mirror_start_job(
|
||||
job_id, bs, creation_flags, base, NULL, speed, 0, 0,
|
||||
MIRROR_LEAVE_BACKING_CHAIN, false,
|
||||
on_error, on_error, true, cb, opaque,
|
||||
&commit_active_job_driver, false, base, auto_complete,
|
||||
filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
errp);
|
||||
if (!job) {
|
||||
goto error_restore_flags;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return job;
|
||||
|
||||
error_restore_flags:
|
||||
/* ignore error and errp for bdrv_reopen, because we want to propagate
|
||||
|
@ -950,25 +950,27 @@ static void set_readonly_helper(gpointer bitmap, gpointer value)
|
||||
bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value);
|
||||
}
|
||||
|
||||
/* qcow2_load_dirty_bitmaps()
|
||||
* Return value is a hint for caller: true means that the Qcow2 header was
|
||||
* updated. (false doesn't mean that the header should be updated by the
|
||||
* caller, it just means that updating was not needed or the image cannot be
|
||||
* written to).
|
||||
* On failure the function returns false.
|
||||
/*
|
||||
* Return true on success, false on failure.
|
||||
* If header_updated is not NULL then it is set appropriately regardless of
|
||||
* the return value.
|
||||
*/
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2BitmapList *bm_list;
|
||||
Qcow2Bitmap *bm;
|
||||
GSList *created_dirty_bitmaps = NULL;
|
||||
bool header_updated = false;
|
||||
bool needs_update = false;
|
||||
|
||||
if (header_updated) {
|
||||
*header_updated = false;
|
||||
}
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
/* No bitmaps - nothing to do */
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
@ -1024,7 +1026,9 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
error_setg_errno(errp, -ret, "Can't update bitmap directory");
|
||||
goto fail;
|
||||
}
|
||||
header_updated = true;
|
||||
if (header_updated) {
|
||||
*header_updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_write(bs)) {
|
||||
@ -1035,7 +1039,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
g_slist_free(created_dirty_bitmaps);
|
||||
bitmap_list_free(bm_list);
|
||||
|
||||
return header_updated;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
g_slist_foreach(created_dirty_bitmaps, release_dirty_bitmap_helper, bs);
|
||||
@ -1077,30 +1081,32 @@ static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
|
||||
/*
|
||||
* qcow2_get_bitmap_info_list()
|
||||
* Returns a list of QCOW2 bitmap details.
|
||||
* In case of no bitmaps, the function returns NULL and
|
||||
* the @errp parameter is not set.
|
||||
* When bitmap information can not be obtained, the function returns
|
||||
* NULL and the @errp parameter is set.
|
||||
* On success return true with info_list set (note, that if there are no
|
||||
* bitmaps, info_list is set to NULL).
|
||||
* On failure return false with errp set.
|
||||
*/
|
||||
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
bool qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
Qcow2BitmapInfoList **info_list, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
Qcow2BitmapList *bm_list;
|
||||
Qcow2Bitmap *bm;
|
||||
Qcow2BitmapInfoList *list = NULL;
|
||||
Qcow2BitmapInfoList **tail = &list;
|
||||
Qcow2BitmapInfoList **tail;
|
||||
|
||||
if (s->nb_bitmaps == 0) {
|
||||
return NULL;
|
||||
*info_list = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
if (bm_list == NULL) {
|
||||
return NULL;
|
||||
if (!bm_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*info_list = NULL;
|
||||
tail = info_list;
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1);
|
||||
info->granularity = 1U << bm->granularity_bits;
|
||||
@ -1111,7 +1117,7 @@ Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
|
||||
bitmap_list_free(bm_list);
|
||||
|
||||
return list;
|
||||
return true;
|
||||
}
|
||||
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
|
||||
@ -1513,9 +1519,10 @@ out:
|
||||
* readonly to begin with, and whether we opened directly or reopened to that
|
||||
* state shouldn't matter for the state we get afterward.
|
||||
*/
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool release_stored, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint32_t new_nb_bitmaps = s->nb_bitmaps;
|
||||
@ -1535,7 +1542,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
|
||||
s->bitmap_directory_size, errp);
|
||||
if (bm_list == NULL) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1650,7 +1657,7 @@ success:
|
||||
}
|
||||
|
||||
bitmap_list_free(bm_list);
|
||||
return;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
@ -1668,16 +1675,14 @@ fail:
|
||||
}
|
||||
|
||||
bitmap_list_free(bm_list);
|
||||
return false;
|
||||
}
|
||||
|
||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, false, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!qcow2_store_persistent_dirty_bitmaps(bs, false, errp)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -868,7 +868,7 @@ static void qcow2_attach_aio_context(BlockDriverState *bs,
|
||||
cache_clean_timer_init(bs, new_context);
|
||||
}
|
||||
|
||||
static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
static bool read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
uint64_t *l2_cache_size,
|
||||
uint64_t *l2_cache_entry_size,
|
||||
uint64_t *refcount_cache_size, Error **errp)
|
||||
@ -906,16 +906,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE
|
||||
" and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set "
|
||||
"at the same time");
|
||||
return;
|
||||
return false;
|
||||
} else if (l2_cache_size_set &&
|
||||
(l2_cache_max_setting > combined_cache_size)) {
|
||||
error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed "
|
||||
QCOW2_OPT_CACHE_SIZE);
|
||||
return;
|
||||
return false;
|
||||
} else if (*refcount_cache_size > combined_cache_size) {
|
||||
error_setg(errp, QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not exceed "
|
||||
QCOW2_OPT_CACHE_SIZE);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (l2_cache_size_set) {
|
||||
@ -954,8 +954,10 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
error_setg(errp, "L2 cache entry size must be a power of two "
|
||||
"between %d and the cluster size (%d)",
|
||||
1 << MIN_CLUSTER_BITS, s->cluster_size);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct Qcow2ReopenState {
|
||||
@ -982,7 +984,6 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
int i;
|
||||
const char *encryptfmt;
|
||||
QDict *encryptopts = NULL;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
qdict_extract_subqdict(options, &encryptopts, "encrypt.");
|
||||
@ -995,10 +996,8 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* get L2 table/refcount block cache size from command line options */
|
||||
read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size,
|
||||
&refcount_cache_size, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!read_cache_sizes(bs, opts, &l2_cache_size, &l2_cache_entry_size,
|
||||
&refcount_cache_size, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@ -1159,6 +1158,10 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
}
|
||||
qdict_put_str(encryptopts, "format", "qcow");
|
||||
r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
|
||||
if (!r->crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
case QCOW_CRYPT_LUKS:
|
||||
@ -1171,14 +1174,15 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
}
|
||||
qdict_put_str(encryptopts, "format", "luks");
|
||||
r->crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
|
||||
if (!r->crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
error_setg(errp, "Unsupported encryption method %d",
|
||||
s->crypt_method_header);
|
||||
break;
|
||||
}
|
||||
if (s->crypt_method_header != QCOW_CRYPT_NONE && !r->crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@ -1292,11 +1296,11 @@ static int validate_compression_type(BDRVQcow2State *s, Error **errp)
|
||||
static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
int flags, Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
unsigned int len, i;
|
||||
int ret = 0;
|
||||
QCowHeader header;
|
||||
Error *local_err = NULL;
|
||||
uint64_t ext_end;
|
||||
uint64_t l1_vm_state_index;
|
||||
bool update_header = false;
|
||||
@ -1611,9 +1615,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
/* Open external data file */
|
||||
s->data_file = bdrv_open_child(NULL, options, "data-file", bs,
|
||||
&child_of_bds, BDRV_CHILD_DATA,
|
||||
true, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
true, errp);
|
||||
if (*errp) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@ -1785,9 +1788,8 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) {
|
||||
/* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */
|
||||
bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
bool header_updated;
|
||||
if (!qcow2_load_dirty_bitmaps(bs, &header_updated, errp)) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@ -2719,11 +2721,11 @@ static void qcow2_close(BlockDriverState *bs)
|
||||
static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
ERRP_GUARD();
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int flags = s->flags;
|
||||
QCryptoBlock *crypto = NULL;
|
||||
QDict *options;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
@ -2741,16 +2743,11 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
|
||||
flags &= ~BDRV_O_INACTIVE;
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = qcow2_do_open(bs, options, flags, &local_err);
|
||||
ret = qcow2_do_open(bs, options, flags, errp);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qobject_unref(options);
|
||||
if (local_err) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"Could not reopen qcow2 layer: ");
|
||||
bs->drv = NULL;
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qcow2 layer");
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Could not reopen qcow2 layer: ");
|
||||
bs->drv = NULL;
|
||||
return;
|
||||
}
|
||||
@ -5066,12 +5063,10 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
ImageInfoSpecific *spec_info;
|
||||
QCryptoBlockInfo *encrypt_info = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (s->crypto != NULL) {
|
||||
encrypt_info = qcrypto_block_get_info(s->crypto, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
encrypt_info = qcrypto_block_get_info(s->crypto, errp);
|
||||
if (!encrypt_info) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -5088,9 +5083,7 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
|
||||
};
|
||||
} else if (s->qcow_version == 3) {
|
||||
Qcow2BitmapInfoList *bitmaps;
|
||||
bitmaps = qcow2_get_bitmap_info_list(bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!qcow2_get_bitmap_info_list(bs, &bitmaps, errp)) {
|
||||
qapi_free_ImageInfoSpecific(spec_info);
|
||||
qapi_free_QCryptoBlockInfo(encrypt_info);
|
||||
return NULL;
|
||||
|
@ -978,12 +978,13 @@ void qcow2_cache_discard(Qcow2Cache *c, void *table);
|
||||
int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||
void **refcount_table,
|
||||
int64_t *refcount_table_size);
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp);
|
||||
Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
Error **errp);
|
||||
bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, bool *header_updated,
|
||||
Error **errp);
|
||||
bool qcow2_get_bitmap_info_list(BlockDriverState *bs,
|
||||
Qcow2BitmapInfoList **info_list, Error **errp);
|
||||
int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp);
|
||||
int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp);
|
||||
void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs,
|
||||
bool release_stored, Error **errp);
|
||||
int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp);
|
||||
bool qcow2_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
|
||||
|
24
block/qed.c
24
block/qed.c
@ -393,6 +393,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &le_header, sizeof(le_header));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to read QED header");
|
||||
return ret;
|
||||
}
|
||||
qed_header_le_to_cpu(&le_header, &s->header);
|
||||
@ -408,25 +409,30 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
return -ENOTSUP;
|
||||
}
|
||||
if (!qed_is_cluster_size_valid(s->header.cluster_size)) {
|
||||
error_setg(errp, "QED cluster size is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Round down file size to the last cluster */
|
||||
file_size = bdrv_getlength(bs->file->bs);
|
||||
if (file_size < 0) {
|
||||
error_setg(errp, "Failed to get file length");
|
||||
return file_size;
|
||||
}
|
||||
s->file_size = qed_start_of_cluster(s, file_size);
|
||||
|
||||
if (!qed_is_table_size_valid(s->header.table_size)) {
|
||||
error_setg(errp, "QED table size is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_is_image_size_valid(s->header.image_size,
|
||||
s->header.cluster_size,
|
||||
s->header.table_size)) {
|
||||
error_setg(errp, "QED image size is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!qed_check_table_offset(s, s->header.l1_table_offset)) {
|
||||
error_setg(errp, "QED table offset is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -438,6 +444,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
/* Header size calculation must not overflow uint32_t */
|
||||
if (s->header.header_size > UINT32_MAX / s->header.cluster_size) {
|
||||
error_setg(errp, "QED header size is too large");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -445,6 +452,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
if ((uint64_t)s->header.backing_filename_offset +
|
||||
s->header.backing_filename_size >
|
||||
s->header.cluster_size * s->header.header_size) {
|
||||
error_setg(errp, "QED backing filename offset is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -453,6 +461,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
bs->auto_backing_file,
|
||||
sizeof(bs->auto_backing_file));
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Failed to read backing filename");
|
||||
return ret;
|
||||
}
|
||||
pstrcpy(bs->backing_file, sizeof(bs->backing_file),
|
||||
@ -475,6 +484,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
ret = qed_write_header_sync(s);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to update header");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -487,6 +497,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
ret = qed_read_l1_table_sync(s);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to read L1 table");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -503,6 +514,7 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
|
||||
|
||||
ret = qed_check(s, &result, true);
|
||||
if (ret) {
|
||||
error_setg(errp, "Image corrupted");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -1537,22 +1549,16 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
bdrv_qed_close(bs);
|
||||
|
||||
bdrv_qed_init_state(bs);
|
||||
qemu_co_mutex_lock(&s->table_lock);
|
||||
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
|
||||
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, errp);
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
if (local_err) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"Could not reopen qed layer: ");
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||
return;
|
||||
if (ret < 0) {
|
||||
error_prepend(errp, "Could not reopen qed layer: ");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -929,7 +929,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
const char *pattern_str;
|
||||
bool *opened;
|
||||
@ -1007,9 +1006,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
s->children[i] = bdrv_open_child(NULL, options, indexstr, bs,
|
||||
&child_of_bds, BDRV_CHILD_DATA, false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
errp);
|
||||
if (!s->children[i]) {
|
||||
ret = -EINVAL;
|
||||
goto close_exit;
|
||||
}
|
||||
|
@ -1824,9 +1824,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (set_backing_hd) {
|
||||
bdrv_set_backing_hd(target_bs, source, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (bdrv_set_backing_hd(target_bs, source, errp) < 0) {
|
||||
goto unref;
|
||||
}
|
||||
}
|
||||
|
18
blockjob.c
18
blockjob.c
@ -258,18 +258,18 @@ static bool job_timer_pending(Job *job)
|
||||
return timer_pending(&job->sleep_timer);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
const BlockJobDriver *drv = block_job_driver(job);
|
||||
int64_t old_speed = job->speed;
|
||||
|
||||
if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
|
||||
return;
|
||||
if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp) < 0) {
|
||||
return false;
|
||||
}
|
||||
if (speed < 0) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "speed",
|
||||
"a non-negative value");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
ratelimit_set_speed(&job->limit, speed, BLOCK_JOB_SLICE_TIME);
|
||||
@ -281,11 +281,13 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
}
|
||||
|
||||
if (speed && speed <= old_speed) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* kick only if a timer is pending */
|
||||
job_enter_cond(&job->job, job_timer_pending);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
|
||||
@ -462,12 +464,8 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
|
||||
/* Only set speed when necessary to avoid NotSupported error */
|
||||
if (speed != 0) {
|
||||
Error *local_err = NULL;
|
||||
|
||||
block_job_set_speed(job, speed, &local_err);
|
||||
if (local_err) {
|
||||
if (!block_job_set_speed(job, speed, errp)) {
|
||||
job_early_fail(&job->job);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -166,6 +166,14 @@ Using ``-M kernel-irqchip=off`` with x86 machine types that include a local
|
||||
APIC is deprecated. The ``split`` setting is supported, as is using
|
||||
``-M kernel-irqchip=off`` with the ISA PC machine type.
|
||||
|
||||
hexadecimal sizes with scaling multipliers (since 6.0)
|
||||
''''''''''''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
Input parameters that take a size value should only use a size suffix
|
||||
(such as 'k' or 'M') when the base is written in decimal, and not when
|
||||
the value is hexadecimal. That is, '0x20M' is deprecated, and should
|
||||
be written either as '32M' or as '0x2000000'.
|
||||
|
||||
QEMU Machine Protocol (QMP) commands
|
||||
------------------------------------
|
||||
|
||||
|
@ -139,7 +139,7 @@ bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs);
|
||||
* Set a rate-limiting parameter for the job; the actual meaning may
|
||||
* vary depending on the job type.
|
||||
*/
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
|
||||
bool block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
|
||||
|
||||
/**
|
||||
* block_job_query:
|
||||
|
@ -2087,8 +2087,8 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
||||
return ret;
|
||||
}
|
||||
|
||||
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
||||
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
||||
flags = (ret & BDRV_BLOCK_DATA ? 0 : NBD_STATE_HOLE) |
|
||||
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
||||
|
||||
if (nbd_extent_array_add(ea, num, flags) < 0) {
|
||||
return 0;
|
||||
|
@ -92,16 +92,22 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off comp
|
||||
== 3. Invalid sizes ==
|
||||
|
||||
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1024
|
||||
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
|
||||
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
|
||||
qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2
|
||||
qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size'
|
||||
qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
|
||||
Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
|
||||
and exabytes, respectively.
|
||||
|
||||
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
|
||||
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
|
||||
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
|
||||
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
|
||||
qemu-img: TEST_DIR/t.qcow2: Value '-1k' is out of range for parameter 'size'
|
||||
qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
|
||||
Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
|
||||
and exabytes, respectively.
|
||||
|
||||
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte
|
||||
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
|
||||
|
@ -13,7 +13,8 @@ qemu-img: Invalid option list: ,
|
||||
qemu-img: Invalid parameter 'snapshot.foo'
|
||||
qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
|
||||
qemu-img: --output must be used with human or json as argument.
|
||||
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
|
||||
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
qemu-img: Unknown file format 'foo'
|
||||
|
||||
== Size calculation for a new file (human) ==
|
||||
|
@ -13,7 +13,8 @@ qemu-img: Invalid option list: ,
|
||||
qemu-img: Invalid parameter 'snapshot.foo'
|
||||
qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar'
|
||||
qemu-img: --output must be used with human or json as argument.
|
||||
qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807.
|
||||
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for
|
||||
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
|
||||
qemu-img: Unknown file format 'foo'
|
||||
|
||||
== Size calculation for a new file (human) ==
|
||||
|
@ -5,7 +5,7 @@ QA output created by 241
|
||||
size: 1024
|
||||
min block: 1
|
||||
[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}]
|
||||
{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
|
||||
|
||||
=== Exporting unaligned raw image, forced server sector alignment ===
|
||||
@ -23,6 +23,6 @@ WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed
|
||||
size: 1024
|
||||
min block: 1
|
||||
[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET},
|
||||
{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}]
|
||||
{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": false, "offset": OFFSET}]
|
||||
1 KiB (0x400) bytes allocated at offset 0 bytes (0x0)
|
||||
*** done
|
||||
|
@ -1960,18 +1960,24 @@ static void test_qemu_strtosz_simple(void)
|
||||
g_assert_cmpint(res, ==, 0);
|
||||
g_assert(endptr == str + 1);
|
||||
|
||||
str = "12345";
|
||||
/* Leading 0 gives decimal results, not octal */
|
||||
str = "08";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 8);
|
||||
g_assert(endptr == str + 2);
|
||||
|
||||
/* Leading space is ignored */
|
||||
str = " 12345";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12345);
|
||||
g_assert(endptr == str + 5);
|
||||
g_assert(endptr == str + 6);
|
||||
|
||||
err = qemu_strtosz(str, NULL, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12345);
|
||||
|
||||
/* Note: precision is 53 bits since we're parsing with strtod() */
|
||||
|
||||
str = "9007199254740991"; /* 2^53-1 */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
@ -1987,7 +1993,7 @@ static void test_qemu_strtosz_simple(void)
|
||||
str = "9007199254740993"; /* 2^53+1 */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */
|
||||
g_assert_cmpint(res, ==, 0x20000000000001);
|
||||
g_assert(endptr == str + 16);
|
||||
|
||||
str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */
|
||||
@ -1999,11 +2005,40 @@ static void test_qemu_strtosz_simple(void)
|
||||
str = "18446744073709550591"; /* 0xfffffffffffffbff */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */
|
||||
g_assert_cmpint(res, ==, 0xfffffffffffffbff);
|
||||
g_assert(endptr == str + 20);
|
||||
|
||||
/* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to
|
||||
* 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */
|
||||
str = "18446744073709551615"; /* 0xffffffffffffffff */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0xffffffffffffffff);
|
||||
g_assert(endptr == str + 20);
|
||||
}
|
||||
|
||||
static void test_qemu_strtosz_hex(void)
|
||||
{
|
||||
const char *str;
|
||||
const char *endptr;
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
str = "0x0";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 0);
|
||||
g_assert(endptr == str + 3);
|
||||
|
||||
str = "0xab";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 171);
|
||||
g_assert(endptr == str + 4);
|
||||
|
||||
str = "0xae";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 174);
|
||||
g_assert(endptr == str + 4);
|
||||
}
|
||||
|
||||
static void test_qemu_strtosz_units(void)
|
||||
@ -2064,14 +2099,36 @@ static void test_qemu_strtosz_units(void)
|
||||
|
||||
static void test_qemu_strtosz_float(void)
|
||||
{
|
||||
const char *str = "12.345M";
|
||||
const char *str;
|
||||
int err;
|
||||
const char *endptr;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
str = "0.5E";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12.345 * MiB);
|
||||
g_assert_cmpint(res, ==, EiB / 2);
|
||||
g_assert(endptr == str + 4);
|
||||
|
||||
/* For convenience, a fraction of 0 is tolerated even on bytes */
|
||||
str = "1.0B";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 1);
|
||||
g_assert(endptr == str + 4);
|
||||
|
||||
/* An empty fraction is tolerated */
|
||||
str = "1.k";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 1024);
|
||||
g_assert(endptr == str + 3);
|
||||
|
||||
/* For convenience, we permit values that are not byte-exact */
|
||||
str = "12.345M";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, (uint64_t) (12.345 * MiB));
|
||||
g_assert(endptr == str + 7);
|
||||
}
|
||||
|
||||
@ -2106,6 +2163,45 @@ static void test_qemu_strtosz_invalid(void)
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
/* Fractional values require scale larger than bytes */
|
||||
str = "1.1B";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
str = "1.1";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
/* No floating point exponents */
|
||||
str = "1.5e1k";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
str = "1.5E+0k";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
/* No hex fractions */
|
||||
str = "0x1.8k";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
/* No negative values */
|
||||
str = "-0";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
|
||||
str = "-1";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
g_assert(endptr == str);
|
||||
}
|
||||
|
||||
static void test_qemu_strtosz_trailing(void)
|
||||
@ -2131,6 +2227,30 @@ static void test_qemu_strtosz_trailing(void)
|
||||
|
||||
err = qemu_strtosz(str, NULL, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
|
||||
str = "0x";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(res, ==, 0);
|
||||
g_assert(endptr == str + 1);
|
||||
|
||||
err = qemu_strtosz(str, NULL, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
|
||||
str = "0.NaN";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert(endptr == str + 2);
|
||||
|
||||
err = qemu_strtosz(str, NULL, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
|
||||
str = "123-45";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(res, ==, 123);
|
||||
g_assert(endptr == str + 3);
|
||||
|
||||
err = qemu_strtosz(str, NULL, &res);
|
||||
g_assert_cmpint(err, ==, -EINVAL);
|
||||
}
|
||||
|
||||
static void test_qemu_strtosz_erange(void)
|
||||
@ -2140,22 +2260,7 @@ static void test_qemu_strtosz_erange(void)
|
||||
int err;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
str = "-1";
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert(endptr == str + 2);
|
||||
|
||||
str = "18446744073709550592"; /* 0xfffffffffffffc00 */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert(endptr == str + 20);
|
||||
|
||||
str = "18446744073709551615"; /* 2^64-1 */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert(endptr == str + 20);
|
||||
|
||||
str = "18446744073709551616"; /* 2^64 */
|
||||
str = "18446744073709551616"; /* 2^64; see strtosz_simple for 2^64-1 */
|
||||
err = qemu_strtosz(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, -ERANGE);
|
||||
g_assert(endptr == str + 20);
|
||||
@ -2168,15 +2273,22 @@ static void test_qemu_strtosz_erange(void)
|
||||
|
||||
static void test_qemu_strtosz_metric(void)
|
||||
{
|
||||
const char *str = "12345k";
|
||||
const char *str;
|
||||
int err;
|
||||
const char *endptr;
|
||||
uint64_t res = 0xbaadf00d;
|
||||
|
||||
str = "12345k";
|
||||
err = qemu_strtosz_metric(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12345000);
|
||||
g_assert(endptr == str + 6);
|
||||
|
||||
str = "12.345M";
|
||||
err = qemu_strtosz_metric(str, &endptr, &res);
|
||||
g_assert_cmpint(err, ==, 0);
|
||||
g_assert_cmpint(res, ==, 12345000);
|
||||
g_assert(endptr == str + 7);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
@ -2443,6 +2555,8 @@ int main(int argc, char **argv)
|
||||
|
||||
g_test_add_func("/cutils/strtosz/simple",
|
||||
test_qemu_strtosz_simple);
|
||||
g_test_add_func("/cutils/strtosz/hex",
|
||||
test_qemu_strtosz_hex);
|
||||
g_test_add_func("/cutils/strtosz/units",
|
||||
test_qemu_strtosz_units);
|
||||
g_test_add_func("/cutils/strtosz/float",
|
||||
|
@ -445,9 +445,9 @@ static void test_keyval_visit_size(void)
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
/* Note: precision is 53 bits since we're parsing with strtod() */
|
||||
/* Note: full 64 bits of precision */
|
||||
|
||||
/* Around limit of precision: 2^53-1, 2^53, 2^53+1 */
|
||||
/* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */
|
||||
qdict = keyval_parse("sz1=9007199254740991,"
|
||||
"sz2=9007199254740992,"
|
||||
"sz3=9007199254740993",
|
||||
@ -460,22 +460,25 @@ static void test_keyval_visit_size(void)
|
||||
visit_type_size(v, "sz2", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0x20000000000000);
|
||||
visit_type_size(v, "sz3", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0x20000000000000);
|
||||
g_assert_cmphex(sz, ==, 0x20000000000001);
|
||||
visit_check_struct(v, &error_abort);
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
/* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
|
||||
qdict = keyval_parse("sz1=9223372036854774784," /* 7ffffffffffffc00 */
|
||||
"sz2=9223372036854775295", /* 7ffffffffffffdff */
|
||||
/* Close to signed integer limit 2^63 */
|
||||
qdict = keyval_parse("sz1=9223372036854775807," /* 7fffffffffffffff */
|
||||
"sz2=9223372036854775808," /* 8000000000000000 */
|
||||
"sz3=9223372036854775809", /* 8000000000000001 */
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_size(v, "sz1", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
|
||||
g_assert_cmphex(sz, ==, 0x7fffffffffffffff);
|
||||
visit_type_size(v, "sz2", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0x7ffffffffffffc00);
|
||||
g_assert_cmphex(sz, ==, 0x8000000000000000);
|
||||
visit_type_size(v, "sz3", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0x8000000000000001);
|
||||
visit_check_struct(v, &error_abort);
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
@ -490,14 +493,26 @@ static void test_keyval_visit_size(void)
|
||||
visit_type_size(v, "sz1", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0xfffffffffffff800);
|
||||
visit_type_size(v, "sz2", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0xfffffffffffff800);
|
||||
g_assert_cmphex(sz, ==, 0xfffffffffffffbff);
|
||||
visit_check_struct(v, &error_abort);
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
/* Actual limit 2^64-1*/
|
||||
qdict = keyval_parse("sz1=18446744073709551615", /* ffffffffffffffff */
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||
visit_type_size(v, "sz1", &sz, &error_abort);
|
||||
g_assert_cmphex(sz, ==, 0xffffffffffffffff);
|
||||
visit_check_struct(v, &error_abort);
|
||||
visit_end_struct(v, NULL);
|
||||
visit_free(v);
|
||||
|
||||
/* Beyond limits */
|
||||
qdict = keyval_parse("sz1=-1,"
|
||||
"sz2=18446744073709550592", /* fffffffffffffc00 */
|
||||
"sz2=18446744073709551616", /* 2^64 */
|
||||
NULL, NULL, &error_abort);
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
qobject_unref(qdict);
|
||||
|
@ -654,9 +654,9 @@ static void test_opts_parse_size(void)
|
||||
g_assert_cmpuint(opts_count(opts), ==, 1);
|
||||
g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0);
|
||||
|
||||
/* Note: precision is 53 bits since we're parsing with strtod() */
|
||||
/* Note: full 64 bits of precision */
|
||||
|
||||
/* Around limit of precision: 2^53-1, 2^53, 2^54 */
|
||||
/* Around double limit of precision: 2^53-1, 2^53, 2^53+1 */
|
||||
opts = qemu_opts_parse(&opts_list_02,
|
||||
"size1=9007199254740991,"
|
||||
"size2=9007199254740992,"
|
||||
@ -668,18 +668,21 @@ static void test_opts_parse_size(void)
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
|
||||
==, 0x20000000000000);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
|
||||
==, 0x20000000000000);
|
||||
==, 0x20000000000001);
|
||||
|
||||
/* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
|
||||
/* Close to signed int limit: 2^63-1, 2^63, 2^63+1 */
|
||||
opts = qemu_opts_parse(&opts_list_02,
|
||||
"size1=9223372036854774784," /* 7ffffffffffffc00 */
|
||||
"size2=9223372036854775295", /* 7ffffffffffffdff */
|
||||
"size1=9223372036854775807," /* 7fffffffffffffff */
|
||||
"size2=9223372036854775808," /* 8000000000000000 */
|
||||
"size3=9223372036854775809", /* 8000000000000001 */
|
||||
false, &error_abort);
|
||||
g_assert_cmpuint(opts_count(opts), ==, 2);
|
||||
g_assert_cmpuint(opts_count(opts), ==, 3);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
|
||||
==, 0x7ffffffffffffc00);
|
||||
==, 0x7fffffffffffffff);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
|
||||
==, 0x7ffffffffffffc00);
|
||||
==, 0x8000000000000000);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
|
||||
==, 0x8000000000000001);
|
||||
|
||||
/* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
|
||||
opts = qemu_opts_parse(&opts_list_02,
|
||||
@ -690,14 +693,22 @@ static void test_opts_parse_size(void)
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
|
||||
==, 0xfffffffffffff800);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
|
||||
==, 0xfffffffffffff800);
|
||||
==, 0xfffffffffffffbff);
|
||||
|
||||
/* Actual limit, 2^64-1 */
|
||||
opts = qemu_opts_parse(&opts_list_02,
|
||||
"size1=18446744073709551615", /* ffffffffffffffff */
|
||||
false, &error_abort);
|
||||
g_assert_cmpuint(opts_count(opts), ==, 1);
|
||||
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
|
||||
==, 0xffffffffffffffff);
|
||||
|
||||
/* Beyond limits */
|
||||
opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!opts);
|
||||
opts = qemu_opts_parse(&opts_list_02,
|
||||
"size1=18446744073709550592", /* fffffffffffffc00 */
|
||||
"size1=18446744073709551616", /* 2^64 */
|
||||
false, &err);
|
||||
error_free_or_abort(&err);
|
||||
g_assert(!opts);
|
||||
|
114
util/cutils.c
114
util/cutils.c
@ -241,52 +241,108 @@ static int64_t suffix_mul(char suffix, int64_t unit)
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert string to bytes, allowing either B/b for bytes, K/k for KB,
|
||||
* M/m for MB, G/g for GB or T/t for TB. End pointer will be returned
|
||||
* in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on
|
||||
* other error.
|
||||
* Convert size string to bytes.
|
||||
*
|
||||
* The size parsing supports the following syntaxes
|
||||
* - 12345 - decimal, scale determined by @default_suffix and @unit
|
||||
* - 12345{bBkKmMgGtTpPeE} - decimal, scale determined by suffix and @unit
|
||||
* - 12345.678{kKmMgGtTpPeE} - decimal, scale determined by suffix, and
|
||||
* fractional portion is truncated to byte
|
||||
* - 0x7fEE - hexadecimal, unit determined by @default_suffix
|
||||
*
|
||||
* The following cause a deprecation warning, and may be removed in the future
|
||||
* - 0xabc{kKmMgGtTpP} - hex with scaling suffix
|
||||
*
|
||||
* The following are intentionally not supported
|
||||
* - octal, such as 08
|
||||
* - fractional hex, such as 0x1.8
|
||||
* - floating point exponents, such as 1e3
|
||||
*
|
||||
* The end pointer will be returned in *end, if not NULL. If there is
|
||||
* no fraction, the input can be decimal or hexadecimal; if there is a
|
||||
* fraction, then the input must be decimal and there must be a suffix
|
||||
* (possibly by @default_suffix) larger than Byte, and the fractional
|
||||
* portion may suffer from precision loss or rounding. The input must
|
||||
* be positive.
|
||||
*
|
||||
* Return -ERANGE on overflow (with *@end advanced), and -EINVAL on
|
||||
* other error (with *@end left unchanged).
|
||||
*/
|
||||
static int do_strtosz(const char *nptr, const char **end,
|
||||
const char default_suffix, int64_t unit,
|
||||
uint64_t *result)
|
||||
{
|
||||
int retval;
|
||||
const char *endptr;
|
||||
const char *endptr, *f;
|
||||
unsigned char c;
|
||||
int mul_required = 0;
|
||||
double val, mul, integral, fraction;
|
||||
bool mul_required = false, hex = false;
|
||||
uint64_t val;
|
||||
int64_t mul;
|
||||
double fraction = 0.0;
|
||||
|
||||
retval = qemu_strtod_finite(nptr, &endptr, &val);
|
||||
/* Parse integral portion as decimal. */
|
||||
retval = qemu_strtou64(nptr, &endptr, 10, &val);
|
||||
if (retval) {
|
||||
goto out;
|
||||
}
|
||||
fraction = modf(val, &integral);
|
||||
if (fraction != 0) {
|
||||
mul_required = 1;
|
||||
}
|
||||
c = *endptr;
|
||||
mul = suffix_mul(c, unit);
|
||||
if (mul >= 0) {
|
||||
endptr++;
|
||||
} else {
|
||||
mul = suffix_mul(default_suffix, unit);
|
||||
assert(mul >= 0);
|
||||
}
|
||||
if (mul == 1 && mul_required) {
|
||||
if (memchr(nptr, '-', endptr - nptr) != NULL) {
|
||||
endptr = nptr;
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Values near UINT64_MAX overflow to 2**64 when converting to double
|
||||
* precision. Compare against the maximum representable double precision
|
||||
* value below 2**64, computed as "the next value after 2**64 (0x1p64) in
|
||||
* the direction of 0".
|
||||
*/
|
||||
if ((val * mul > nextafter(0x1p64, 0)) || val < 0) {
|
||||
if (val == 0 && (*endptr == 'x' || *endptr == 'X')) {
|
||||
/* Input looks like hex, reparse, and insist on no fraction. */
|
||||
retval = qemu_strtou64(nptr, &endptr, 16, &val);
|
||||
if (retval) {
|
||||
goto out;
|
||||
}
|
||||
if (*endptr == '.') {
|
||||
endptr = nptr;
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
hex = true;
|
||||
} else if (*endptr == '.') {
|
||||
/*
|
||||
* Input looks like a fraction. Make sure even 1.k works
|
||||
* without fractional digits. If we see an exponent, treat
|
||||
* the entire input as invalid instead.
|
||||
*/
|
||||
f = endptr;
|
||||
retval = qemu_strtod_finite(f, &endptr, &fraction);
|
||||
if (retval) {
|
||||
fraction = 0.0;
|
||||
endptr++;
|
||||
} else if (memchr(f, 'e', endptr - f) || memchr(f, 'E', endptr - f)) {
|
||||
endptr = nptr;
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
} else if (fraction != 0) {
|
||||
mul_required = true;
|
||||
}
|
||||
}
|
||||
c = *endptr;
|
||||
mul = suffix_mul(c, unit);
|
||||
if (mul > 0) {
|
||||
if (hex) {
|
||||
warn_report("Using a multiplier suffix on hex numbers "
|
||||
"is deprecated: %s", nptr);
|
||||
}
|
||||
endptr++;
|
||||
} else {
|
||||
mul = suffix_mul(default_suffix, unit);
|
||||
assert(mul > 0);
|
||||
}
|
||||
if (mul == 1 && mul_required) {
|
||||
endptr = nptr;
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (val > (UINT64_MAX - ((uint64_t) (fraction * mul))) / mul) {
|
||||
retval = -ERANGE;
|
||||
goto out;
|
||||
}
|
||||
*result = val * mul;
|
||||
*result = val * mul + (uint64_t) (fraction * mul);
|
||||
retval = 0;
|
||||
|
||||
out:
|
||||
|
Loading…
Reference in New Issue
Block a user