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:
Peter Maydell 2021-03-11 13:57:08 +00:00
commit 9abda42bf2
25 changed files with 416 additions and 216 deletions

View File

@ -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>

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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,

View File

@ -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: ");
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
------------------------------------

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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) ==

View File

@ -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) ==

View File

@ -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

View File

@ -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",

View File

@ -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);

View File

@ -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);

View File

@ -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: