option cutils: Fix and clean up number conversions

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJYrzrdAAoJEDhwtADrkYZTjJIP/0pjdtvdCdIq855DSgHO6jdm
 xM3W1DngCHt78LPXKutqRX8le2tFuY7uXQG2PqTXtirni5WKB9MB4wdOzeucrXvW
 NIpb8AC1GM0ToOwQvrc8T840QfdGFFE8X2DIAPPYcBivQWAlRi6tqbGY8KwvWFYC
 vSjUIJbIu8mOUt49Q5LfhaH2UPJlcNlED/oUmDLorz9Rz6E75EHP5pKPrr7Y0JkX
 4wjxSE18yM4z0wXwIfRiW5zKDs7JuvHwLSVX75ZwpS5GpWgGsyc4EeyAQb5Xi97s
 /S3a4SFj/kOvDeuw8uy7BkuPknYF2hHD6XUkoIWr2hiyjatjeAM9USO18N2HwoIg
 rO3Icw1HADC8Z/RrXjKecaLAA+gKNFGAMgmrYH0GImg6QfC24VQdHNXm3v+i1i27
 1p6AnSdtzG26DPk8pJODn2UbxngoeHUy0PPjra7ZEMDK7Igkw8x0oFBL887jPxkd
 oyBA5S4dOUCYXo86hYjjrhRXrQ7j5oIx45myX8jX6RoQLEq1XrUiVN59t1WXW5Vv
 EiZc7YB/QSPMZe/q0DiWvif4DH+YLAqD9k7OeQ2HjINBf5sKqIn3lGO0zloL2W+8
 mQ9+HLryP4j+PRbtpQavnAYg6Mo7Qzg9y5ZkABAj0fdyPJFZ8ZZ7gH0+atF1CjlP
 Vvv+VdyWpzKfFam752Hk
 =o8eA
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-util-2017-02-23' into staging

option cutils: Fix and clean up number conversions

# gpg: Signature made Thu 23 Feb 2017 19:41:17 GMT
# gpg:                using RSA key 0x3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-util-2017-02-23: (24 commits)
  option: Fix checking of sizes for overflow and trailing crap
  util/cutils: Change qemu_strtosz*() from int64_t to uint64_t
  util/cutils: Return qemu_strtosz*() error and value separately
  util/cutils: Let qemu_strtosz*() optionally reject trailing crap
  qemu-img: Wrap cvtnum() around qemu_strtosz()
  test-cutils: Drop suffix from test_qemu_strtosz_simple()
  test-cutils: Use qemu_strtosz() more often
  util/cutils: Drop QEMU_STRTOSZ_DEFSUFFIX_* macros
  util/cutils: New qemu_strtosz()
  util/cutils: Rename qemu_strtosz() to qemu_strtosz_MiB()
  util/cutils: New qemu_strtosz_metric()
  test-cutils: Cover qemu_strtosz() around range limits
  test-cutils: Cover qemu_strtosz() with trailing crap
  test-cutils: Cover qemu_strtosz() invalid input
  test-cutils: Add missing qemu_strtosz()... endptr checks
  option: Fix to reject invalid and overflowing numbers
  util/cutils: Clean up control flow around qemu_strtol() a bit
  util/cutils: Clean up variable names around qemu_strtol()
  util/cutils: Rename qemu_strtoll(), qemu_strtoull()
  util/cutils: Rewrite documentation of qemu_strtol() & friends
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-02-24 18:34:27 +00:00
commit d7941f4eed
15 changed files with 996 additions and 494 deletions

11
hmp.c
View file

@ -1344,12 +1344,11 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
{
const char *param = qdict_get_str(qdict, "parameter");
const char *valuestr = qdict_get_str(qdict, "value");
int64_t valuebw = 0;
uint64_t valuebw = 0;
long valueint = 0;
char *endp;
Error *err = NULL;
bool use_int_value = false;
int i;
int i, ret;
for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) {
if (strcmp(param, MigrationParameter_lookup[i]) == 0) {
@ -1385,9 +1384,9 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
break;
case MIGRATION_PARAMETER_MAX_BANDWIDTH:
p.has_max_bandwidth = true;
valuebw = qemu_strtosz(valuestr, &endp);
if (valuebw < 0 || (size_t)valuebw != valuebw
|| *endp != '\0') {
ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw);
if (ret < 0 || valuebw > INT64_MAX
|| (size_t)valuebw != valuebw) {
error_setg(&err, "Invalid size %s", valuestr);
goto cleanup;
}

View file

@ -1267,10 +1267,11 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp)
if (s->sizearg == NULL) {
s->legacy_size = 4 << 20; /* 4 MB default */
} else {
char *end;
int64_t size = qemu_strtosz(s->sizearg, &end);
if (size < 0 || (size_t)size != size || *end != '\0'
|| !is_power_of_2(size)) {
int ret;
uint64_t size;
ret = qemu_strtosz_MiB(s->sizearg, NULL, &size);
if (ret < 0 || (size_t)size != size || !is_power_of_2(size)) {
error_setg(errp, "Invalid size %s", s->sizearg);
return;
}

View file

@ -130,34 +130,19 @@ int qemu_strtol(const char *nptr, const char **endptr, int base,
long *result);
int qemu_strtoul(const char *nptr, const char **endptr, int base,
unsigned long *result);
int qemu_strtoll(const char *nptr, const char **endptr, int base,
int64_t *result);
int qemu_strtoull(const char *nptr, const char **endptr, int base,
int qemu_strtoi64(const char *nptr, const char **endptr, int base,
int64_t *result);
int qemu_strtou64(const char *nptr, const char **endptr, int base,
uint64_t *result);
int parse_uint(const char *s, unsigned long long *value, char **endptr,
int base);
int parse_uint_full(const char *s, unsigned long long *value, int base);
/*
* qemu_strtosz() suffixes used to specify the default treatment of an
* argument passed to qemu_strtosz() without an explicit suffix.
* These should be defined using upper case characters in the range
* A-Z, as qemu_strtosz() will use qemu_toupper() on the given argument
* prior to comparison.
*/
#define QEMU_STRTOSZ_DEFSUFFIX_EB 'E'
#define QEMU_STRTOSZ_DEFSUFFIX_PB 'P'
#define QEMU_STRTOSZ_DEFSUFFIX_TB 'T'
#define QEMU_STRTOSZ_DEFSUFFIX_GB 'G'
#define QEMU_STRTOSZ_DEFSUFFIX_MB 'M'
#define QEMU_STRTOSZ_DEFSUFFIX_KB 'K'
#define QEMU_STRTOSZ_DEFSUFFIX_B 'B'
int64_t qemu_strtosz(const char *nptr, char **end);
int64_t qemu_strtosz_suffix(const char *nptr, char **end,
const char default_suffix);
int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end,
const char default_suffix, int64_t unit);
int qemu_strtosz(const char *nptr, char **end, uint64_t *result);
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result);
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result);
#define K_BYTE (1ULL << 10)
#define M_BYTE (1ULL << 20)
#define G_BYTE (1ULL << 30)

View file

@ -2799,7 +2799,8 @@ static QDict *monitor_parse_arguments(Monitor *mon,
break;
case 'o':
{
int64_t val;
int ret;
uint64_t val;
char *end;
while (qemu_isspace(*p)) {
@ -2811,8 +2812,8 @@ static QDict *monitor_parse_arguments(Monitor *mon,
break;
}
}
val = qemu_strtosz(p, &end);
if (val < 0) {
ret = qemu_strtosz_MiB(p, &end, &val);
if (ret < 0 || val > INT64_MAX) {
monitor_printf(mon, "invalid size\n");
goto fail;
}

View file

@ -481,23 +481,20 @@ opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp)
{
OptsVisitor *ov = to_ov(v);
const QemuOpt *opt;
int64_t val;
char *endptr;
int err;
opt = lookup_scalar(ov, name, errp);
if (!opt) {
return;
}
val = qemu_strtosz_suffix(opt->str ? opt->str : "", &endptr,
QEMU_STRTOSZ_DEFSUFFIX_B);
if (val < 0 || *endptr) {
err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj);
if (err < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
"a size value representible as a non-negative int64");
"a size value");
return;
}
*obj = val;
processed(ov, name);
}

View file

@ -368,6 +368,21 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts,
return 0;
}
static int64_t cvtnum(const char *s)
{
int err;
uint64_t value;
err = qemu_strtosz(s, NULL, &value);
if (err < 0) {
return err;
}
if (value > INT64_MAX) {
return -ERANGE;
}
return value;
}
static int img_create(int argc, char **argv)
{
int c;
@ -461,10 +476,9 @@ static int img_create(int argc, char **argv)
/* Get image size, if specified */
if (optind < argc) {
int64_t sval;
char *end;
sval = qemu_strtosz_suffix(argv[optind++], &end,
QEMU_STRTOSZ_DEFSUFFIX_B);
if (sval < 0 || *end) {
sval = cvtnum(argv[optind++]);
if (sval < 0) {
if (sval == -ERANGE) {
error_report("Image size must be less than 8 EiB!");
} else {
@ -1864,9 +1878,9 @@ static int img_convert(int argc, char **argv)
case 'S':
{
int64_t sval;
char *end;
sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (sval < 0 || *end) {
sval = cvtnum(optarg);
if (sval < 0) {
error_report("Invalid minimum zero buffer size for sparse output specified");
ret = -1;
goto fail_getopt;
@ -3651,11 +3665,8 @@ static int img_bench(int argc, char **argv)
break;
case 'o':
{
char *end;
errno = 0;
offset = qemu_strtosz_suffix(optarg, &end,
QEMU_STRTOSZ_DEFSUFFIX_B);
if (offset < 0|| *end) {
offset = cvtnum(optarg);
if (offset < 0) {
error_report("Invalid offset specified");
return 1;
}
@ -3668,10 +3679,9 @@ static int img_bench(int argc, char **argv)
case 's':
{
int64_t sval;
char *end;
sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (sval < 0 || sval > INT_MAX || *end) {
sval = cvtnum(optarg);
if (sval < 0 || sval > INT_MAX) {
error_report("Invalid buffer size specified");
return 1;
}
@ -3682,10 +3692,9 @@ static int img_bench(int argc, char **argv)
case 'S':
{
int64_t sval;
char *end;
sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (sval < 0 || sval > INT_MAX || *end) {
sval = cvtnum(optarg);
if (sval < 0 || sval > INT_MAX) {
error_report("Invalid step size specified");
return 1;
}
@ -3844,12 +3853,11 @@ static int img_dd_bs(const char *arg,
struct DdIo *in, struct DdIo *out,
struct DdInfo *dd)
{
char *end;
int64_t res;
res = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
res = cvtnum(arg);
if (res <= 0 || res > INT_MAX || *end) {
if (res <= 0 || res > INT_MAX) {
error_report("invalid number: '%s'", arg);
return 1;
}
@ -3862,11 +3870,9 @@ static int img_dd_count(const char *arg,
struct DdIo *in, struct DdIo *out,
struct DdInfo *dd)
{
char *end;
dd->count = cvtnum(arg);
dd->count = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (dd->count < 0 || *end) {
if (dd->count < 0) {
error_report("invalid number: '%s'", arg);
return 1;
}
@ -3896,11 +3902,9 @@ static int img_dd_skip(const char *arg,
struct DdIo *in, struct DdIo *out,
struct DdInfo *dd)
{
char *end;
in->offset = cvtnum(arg);
in->offset = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (in->offset < 0 || *end) {
if (in->offset < 0) {
error_report("invalid number: '%s'", arg);
return 1;
}

View file

@ -137,15 +137,17 @@ static char **breakline(char *input, int *count)
static int64_t cvtnum(const char *s)
{
char *end;
int64_t ret;
int err;
uint64_t value;
ret = qemu_strtosz_suffix(s, &end, QEMU_STRTOSZ_DEFSUFFIX_B);
if (*end != '\0') {
/* Detritus at the end of the string */
return -EINVAL;
err = qemu_strtosz(s, NULL, &value);
if (err < 0) {
return err;
}
return ret;
if (value > INT64_MAX) {
return -ERANGE;
}
return value;
}
static void print_cvtnum_err(int64_t rc, const char *arg)

View file

@ -743,7 +743,7 @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
for (ent = qdict_first(maybe_list); ent != NULL;
ent = qdict_next(maybe_list, ent)) {
if (qemu_strtoll(ent->key, NULL, 10, &val) == 0) {
if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) {
if (is_list == -1) {
is_list = 1;
} else if (!is_list) {

34
qtest.c
View file

@ -373,8 +373,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
uint64_t value;
g_assert(words[1] && words[2]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &value) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &value) == 0);
if (words[0][5] == 'b') {
uint8_t data = value;
@ -402,7 +402,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
uint64_t value = UINT64_C(-1);
g_assert(words[1]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
if (words[0][4] == 'b') {
uint8_t data;
@ -428,8 +428,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
char *enc;
g_assert(words[1] && words[2]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0);
/* We'd send garbage to libqtest if len is 0 */
g_assert(len);
@ -452,8 +452,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
gchar *b64_data;
g_assert(words[1] && words[2]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0);
data = g_malloc(len);
cpu_physical_memory_read(addr, data, len);
@ -469,8 +469,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
size_t data_len;
g_assert(words[1] && words[2] && words[3]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0);
data_len = strlen(words[3]);
if (data_len < 3) {
@ -498,8 +498,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
unsigned long pattern;
g_assert(words[1] && words[2] && words[3]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtoul(words[3], NULL, 0, &pattern) == 0);
if (len) {
@ -518,8 +518,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
gsize out_len;
g_assert(words[1] && words[2] && words[3]);
g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0);
g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0);
g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0);
data_len = strlen(words[3]);
if (data_len < 3) {
@ -552,9 +552,9 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
unsigned long nargs, nret;
g_assert(qemu_strtoul(words[2], NULL, 0, &nargs) == 0);
g_assert(qemu_strtoull(words[3], NULL, 0, &args) == 0);
g_assert(qemu_strtou64(words[3], NULL, 0, &args) == 0);
g_assert(qemu_strtoul(words[4], NULL, 0, &nret) == 0);
g_assert(qemu_strtoull(words[5], NULL, 0, &ret) == 0);
g_assert(qemu_strtou64(words[5], NULL, 0, &ret) == 0);
res = qtest_rtas_call(words[1], nargs, args, nret, ret);
qtest_send_prefix(chr);
@ -564,7 +564,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
int64_t ns;
if (words[1]) {
g_assert(qemu_strtoll(words[1], NULL, 0, &ns) == 0);
g_assert(qemu_strtoi64(words[1], NULL, 0, &ns) == 0);
} else {
ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
}
@ -576,7 +576,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words)
int64_t ns;
g_assert(words[1]);
g_assert(qemu_strtoll(words[1], NULL, 0, &ns) == 0);
g_assert(qemu_strtoi64(words[1], NULL, 0, &ns) == 0);
qtest_clock_warp(ns);
qtest_send_prefix(chr);
qtest_sendf(chr, "OK %"PRIi64"\n",

View file

@ -2033,12 +2033,11 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features,
/* Special case: */
if (!strcmp(name, "tsc-freq")) {
int64_t tsc_freq;
char *err;
int ret;
uint64_t tsc_freq;
tsc_freq = qemu_strtosz_suffix_unit(val, &err,
QEMU_STRTOSZ_DEFSUFFIX_B, 1000);
if (tsc_freq < 0 || *err) {
ret = qemu_strtosz_metric(val, NULL, &tsc_freq);
if (ret < 0 || tsc_freq > INT64_MAX) {
error_setg(errp, "bad numerical value %s", val);
return;
}

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "qapi/qmp/qstring.h"
#include "qemu/config-file.h"
@ -29,6 +30,9 @@ static QemuOptsList opts_list_01 = {
},{
.name = "number1",
.type = QEMU_OPT_NUMBER,
},{
.name = "number2",
.type = QEMU_OPT_NUMBER,
},
{ /* end of list */ }
},
@ -41,15 +45,24 @@ static QemuOptsList opts_list_02 = {
{
.name = "str1",
.type = QEMU_OPT_STRING,
},{
.name = "bool1",
.type = QEMU_OPT_BOOL,
},{
.name = "str2",
.type = QEMU_OPT_STRING,
},{
.name = "bool1",
.type = QEMU_OPT_BOOL,
},{
.name = "bool2",
.type = QEMU_OPT_BOOL,
},{
.name = "size1",
.type = QEMU_OPT_SIZE,
},{
.name = "size2",
.type = QEMU_OPT_SIZE,
},{
.name = "size3",
.type = QEMU_OPT_SIZE,
},
{ /* end of list */ }
},
@ -57,6 +70,7 @@ static QemuOptsList opts_list_02 = {
static QemuOptsList opts_list_03 = {
.name = "opts_list_03",
.implied_opt_name = "implied",
.head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head),
.desc = {
/* no elements => accept any params */
@ -421,6 +435,308 @@ static void test_qemu_opts_set(void)
g_assert(opts == NULL);
}
static int opts_count_iter(void *opaque, const char *name, const char *value,
Error **errp)
{
(*(size_t *)opaque)++;
return 0;
}
static size_t opts_count(QemuOpts *opts)
{
size_t n = 0;
qemu_opt_foreach(opts, opts_count_iter, &n, NULL);
return n;
}
static void test_opts_parse(void)
{
Error *err = NULL;
QemuOpts *opts;
char long_key[129];
char *params;
/* Nothing */
opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 0);
/* Empty key */
opts = qemu_opts_parse(&opts_list_03, "=val", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val");
/* Long key */
memset(long_key, 'a', 127);
long_key[127] = 'z';
long_key[128] = 0;
params = g_strdup_printf("%s=v", long_key);
opts = qemu_opts_parse(&opts_list_03, params + 1, NULL, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v");
/* Overlong key gets truncated */
opts = qemu_opts_parse(&opts_list_03, params, NULL, &error_abort);
g_assert(opts_count(opts) == 1);
long_key[127] = 0;
g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v");
g_free(params);
/* Multiple keys, last one wins */
opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "3");
g_assert_cmpstr(qemu_opt_get(opts, "b"), ==, "2,x");
/* Except when it doesn't */
opts = qemu_opts_parse(&opts_list_03, "id=foo,id=bar",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 0);
g_assert_cmpstr(qemu_opts_id(opts), ==, "foo");
/* TODO Cover low-level access to repeated keys */
/* Trailing comma is ignored */
opts = qemu_opts_parse(&opts_list_03, "x=y,", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, "y");
/* Except when it isn't */
opts = qemu_opts_parse(&opts_list_03, ",", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "on");
/* Duplicate ID */
opts = qemu_opts_parse(&opts_list_03, "x=y,id=foo", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* TODO Cover .merge_lists = true */
/* Buggy ID recognition */
opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opts_id(opts), ==, "bar"); /* BUG */
g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, ",id=bar");
/* Anti-social ID */
opts = qemu_opts_parse(&opts_list_01, "id=666", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Implied value */
opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmpstr(qemu_opt_get(opts, "an"), ==, "on");
g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off");
g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, "");
/* Implied key */
opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", true,
&error_abort);
g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "an");
g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off");
g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, "");
/* Implied key with empty value */
opts = qemu_opts_parse(&opts_list_03, ",", true, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "");
/* Implied key with comma value */
opts = qemu_opts_parse(&opts_list_03, ",,,a=1", true, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ",");
g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "1");
/* Empty key is not an implied key */
opts = qemu_opts_parse(&opts_list_03, "=val", true, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val");
/* Unknown key */
opts = qemu_opts_parse(&opts_list_01, "nonexistent=", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
qemu_opts_reset(&opts_list_01);
qemu_opts_reset(&opts_list_03);
}
static void test_opts_parse_bool(void)
{
Error *err = NULL;
QemuOpts *opts;
opts = qemu_opts_parse(&opts_list_02, "bool1=on,bool2=off",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert(qemu_opt_get_bool(opts, "bool1", false));
g_assert(!qemu_opt_get_bool(opts, "bool2", true));
opts = qemu_opts_parse(&opts_list_02, "bool1=offer", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
qemu_opts_reset(&opts_list_02);
}
static void test_opts_parse_number(void)
{
Error *err = NULL;
QemuOpts *opts;
/* Lower limit zero */
opts = qemu_opts_parse(&opts_list_01, "number1=0", false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 0);
/* Upper limit 2^64-1 */
opts = qemu_opts_parse(&opts_list_01,
"number1=18446744073709551615,number2=-1",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmphex(qemu_opt_get_number(opts, "number1", 1), ==, UINT64_MAX);
g_assert_cmphex(qemu_opt_get_number(opts, "number2", 0), ==, UINT64_MAX);
/* Above upper limit */
opts = qemu_opts_parse(&opts_list_01, "number1=18446744073709551616",
false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Below lower limit */
opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616",
false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Hex and octal */
opts = qemu_opts_parse(&opts_list_01, "number1=0x2a,number2=052",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42);
g_assert_cmpuint(qemu_opt_get_number(opts, "number2", 0), ==, 42);
/* Invalid */
opts = qemu_opts_parse(&opts_list_01, "number1=", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_01, "number1=eins", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Leading whitespace */
opts = qemu_opts_parse(&opts_list_01, "number1= \t42",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 1);
g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42);
/* Trailing crap */
opts = qemu_opts_parse(&opts_list_01, "number1=3.14", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_01, "number1=08", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_01, "number1=0 ", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
qemu_opts_reset(&opts_list_01);
}
static void test_opts_parse_size(void)
{
Error *err = NULL;
QemuOpts *opts;
/* Lower limit zero */
opts = qemu_opts_parse(&opts_list_02, "size1=0", false, &error_abort);
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() */
/* Around limit of precision: 2^53-1, 2^53, 2^54 */
opts = qemu_opts_parse(&opts_list_02,
"size1=9007199254740991,"
"size2=9007199254740992,"
"size3=9007199254740993",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
==, 0x1fffffffffffff);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
==, 0x20000000000000);
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1),
==, 0x20000000000000);
/* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */
opts = qemu_opts_parse(&opts_list_02,
"size1=9223372036854774784," /* 7ffffffffffffc00 */
"size2=9223372036854775295", /* 7ffffffffffffdff */
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
==, 0x7ffffffffffffc00);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
==, 0x7ffffffffffffc00);
/* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */
opts = qemu_opts_parse(&opts_list_02,
"size1=18446744073709549568," /* fffffffffffff800 */
"size2=18446744073709550591", /* fffffffffffffbff */
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1),
==, 0xfffffffffffff800);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1),
==, 0xfffffffffffff800);
/* 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 */
false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Suffixes */
opts = qemu_opts_parse(&opts_list_02, "size1=8b,size2=1.5k,size3=2M",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 3);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536);
g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * M_BYTE);
opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T",
false, &error_abort);
g_assert_cmpuint(opts_count(opts), ==, 2);
g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, G_BYTE / 10);
g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0),
==, 16777215 * T_BYTE);
/* Beyond limit with suffix */
opts = qemu_opts_parse(&opts_list_02, "size1=16777216T",
false, &err);
error_free_or_abort(&err);
g_assert(!opts);
/* Trailing crap */
opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err);
error_free_or_abort(&err);
g_assert(!opts);
qemu_opts_reset(&opts_list_02);
}
int main(int argc, char *argv[])
{
register_opts();
@ -435,6 +751,10 @@ int main(int argc, char *argv[])
g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset);
g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset);
g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set);
g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse);
g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool);
g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number);
g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size);
g_test_run();
return 0;
}

View file

@ -181,19 +181,19 @@ int fcntl_setfl(int fd, int flag)
static int64_t suffix_mul(char suffix, int64_t unit)
{
switch (qemu_toupper(suffix)) {
case QEMU_STRTOSZ_DEFSUFFIX_B:
case 'B':
return 1;
case QEMU_STRTOSZ_DEFSUFFIX_KB:
case 'K':
return unit;
case QEMU_STRTOSZ_DEFSUFFIX_MB:
case 'M':
return unit * unit;
case QEMU_STRTOSZ_DEFSUFFIX_GB:
case 'G':
return unit * unit * unit;
case QEMU_STRTOSZ_DEFSUFFIX_TB:
case 'T':
return unit * unit * unit * unit;
case QEMU_STRTOSZ_DEFSUFFIX_PB:
case 'P':
return unit * unit * unit * unit * unit;
case QEMU_STRTOSZ_DEFSUFFIX_EB:
case 'E':
return unit * unit * unit * unit * unit * unit;
}
return -1;
@ -205,10 +205,11 @@ static int64_t suffix_mul(char suffix, int64_t unit)
* in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on
* other error.
*/
int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end,
const char default_suffix, int64_t unit)
static int do_strtosz(const char *nptr, char **end,
const char default_suffix, int64_t unit,
uint64_t *result)
{
int64_t retval = -EINVAL;
int retval;
char *endptr;
unsigned char c;
int mul_required = 0;
@ -217,7 +218,8 @@ int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end,
errno = 0;
val = strtod(nptr, &endptr);
if (isnan(val) || endptr == nptr || errno != 0) {
goto fail;
retval = -EINVAL;
goto out;
}
fraction = modf(val, &integral);
if (fraction != 0) {
@ -232,181 +234,204 @@ int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end,
assert(mul >= 0);
}
if (mul == 1 && mul_required) {
goto fail;
retval = -EINVAL;
goto out;
}
if ((val * mul >= INT64_MAX) || val < 0) {
/*
* Values >= 0xfffffffffffffc00 overflow uint64_t after their trip
* through double (53 bits of precision).
*/
if ((val * mul >= 0xfffffffffffffc00) || val < 0) {
retval = -ERANGE;
goto fail;
goto out;
}
retval = val * mul;
*result = val * mul;
retval = 0;
fail:
out:
if (end) {
*end = endptr;
} else if (*endptr) {
retval = -EINVAL;
}
return retval;
}
int64_t qemu_strtosz_suffix(const char *nptr, char **end,
const char default_suffix)
int qemu_strtosz(const char *nptr, char **end, uint64_t *result)
{
return qemu_strtosz_suffix_unit(nptr, end, default_suffix, 1024);
return do_strtosz(nptr, end, 'B', 1024, result);
}
int64_t qemu_strtosz(const char *nptr, char **end)
int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result)
{
return qemu_strtosz_suffix(nptr, end, QEMU_STRTOSZ_DEFSUFFIX_MB);
return do_strtosz(nptr, end, 'M', 1024, result);
}
int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result)
{
return do_strtosz(nptr, end, 'B', 1000, result);
}
/**
* Helper function for qemu_strto*l() functions.
* Helper function for error checking after strtol() and the like
*/
static int check_strtox_error(const char *p, char *endptr, const char **next,
int err)
static int check_strtox_error(const char *nptr, char *ep,
const char **endptr, int libc_errno)
{
/* If no conversion was performed, prefer BSD behavior over glibc
* behavior.
*/
if (err == 0 && endptr == p) {
err = EINVAL;
if (endptr) {
*endptr = ep;
}
if (!next && *endptr) {
/* Turn "no conversion" into an error */
if (libc_errno == 0 && ep == nptr) {
return -EINVAL;
}
if (next) {
*next = endptr;
/* Fail when we're expected to consume the string, but didn't */
if (!endptr && *ep) {
return -EINVAL;
}
return -err;
return -libc_errno;
}
/**
* QEMU wrappers for strtol(), strtoll(), strtoul(), strotull() C functions.
* Convert string @nptr to a long integer, and store it in @result.
*
* Convert ASCII string @nptr to a long integer value
* from the given @base. Parameters @nptr, @endptr, @base
* follows same semantics as strtol() C function.
* This is a wrapper around strtol() that is harder to misuse.
* Semantics of @nptr, @endptr, @base match strtol() with differences
* noted below.
*
* Unlike from strtol() function, if @endptr is not NULL, this
* function will return -EINVAL whenever it cannot fully convert
* the string in @nptr with given @base to a long. This function returns
* the result of the conversion only through the @result parameter.
* @nptr may be null, and no conversion is performed then.
*
* If NULL is passed in @endptr, then the whole string in @ntpr
* is a number otherwise it returns -EINVAL.
* If no conversion is performed, store @nptr in *@endptr and return
* -EINVAL.
*
* RETURN VALUE
* Unlike from strtol() function, this wrapper returns either
* -EINVAL or the errno set by strtol() function (e.g -ERANGE).
* If the conversion overflows, -ERANGE is returned, and @result
* is set to the max value of the desired type
* (e.g. LONG_MAX, LLONG_MAX, ULONG_MAX, ULLONG_MAX). If the case
* of underflow, -ERANGE is returned, and @result is set to the min
* value of the desired type. For strtol(), strtoll(), @result is set to
* LONG_MIN, LLONG_MIN, respectively, and for strtoul(), strtoull() it
* is set to 0.
* If @endptr is null, and the string isn't fully converted, return
* -EINVAL. This is the case when the pointer that would be stored in
* a non-null @endptr points to a character other than '\0'.
*
* If the conversion overflows @result, store LONG_MAX in @result,
* and return -ERANGE.
*
* If the conversion underflows @result, store LONG_MIN in @result,
* and return -ERANGE.
*
* Else store the converted value in @result, and return zero.
*/
int qemu_strtol(const char *nptr, const char **endptr, int base,
long *result)
{
char *p;
int err = 0;
char *ep;
if (!nptr) {
if (endptr) {
*endptr = nptr;
}
err = -EINVAL;
} else {
errno = 0;
*result = strtol(nptr, &p, base);
err = check_strtox_error(nptr, p, endptr, errno);
return -EINVAL;
}
return err;
errno = 0;
*result = strtol(nptr, &ep, base);
return check_strtox_error(nptr, ep, endptr, errno);
}
/**
* Converts ASCII string to an unsigned long integer.
* Convert string @nptr to an unsigned long, and store it in @result.
*
* If string contains a negative number, value will be converted to
* the unsigned representation of the signed value, unless the original
* (nonnegated) value would overflow, in this case, it will set @result
* to ULONG_MAX, and return ERANGE.
* This is a wrapper around strtoul() that is harder to misuse.
* Semantics of @nptr, @endptr, @base match strtoul() with differences
* noted below.
*
* The same behavior holds, for qemu_strtoull() but sets @result to
* ULLONG_MAX instead of ULONG_MAX.
* @nptr may be null, and no conversion is performed then.
*
* See qemu_strtol() documentation for more info.
* If no conversion is performed, store @nptr in *@endptr and return
* -EINVAL.
*
* If @endptr is null, and the string isn't fully converted, return
* -EINVAL. This is the case when the pointer that would be stored in
* a non-null @endptr points to a character other than '\0'.
*
* If the conversion overflows @result, store ULONG_MAX in @result,
* and return -ERANGE.
*
* Else store the converted value in @result, and return zero.
*
* Note that a number with a leading minus sign gets converted without
* the minus sign, checked for overflow (see above), then negated (in
* @result's type). This is exactly how strtoul() works.
*/
int qemu_strtoul(const char *nptr, const char **endptr, int base,
unsigned long *result)
{
char *p;
int err = 0;
char *ep;
if (!nptr) {
if (endptr) {
*endptr = nptr;
}
err = -EINVAL;
} else {
errno = 0;
*result = strtoul(nptr, &p, base);
/* Windows returns 1 for negative out-of-range values. */
if (errno == ERANGE) {
*result = -1;
}
err = check_strtox_error(nptr, p, endptr, errno);
return -EINVAL;
}
return err;
errno = 0;
*result = strtoul(nptr, &ep, base);
/* Windows returns 1 for negative out-of-range values. */
if (errno == ERANGE) {
*result = -1;
}
return check_strtox_error(nptr, ep, endptr, errno);
}
/**
* Converts ASCII string to a long long integer.
* Convert string @nptr to an int64_t.
*
* See qemu_strtol() documentation for more info.
* Works like qemu_strtol(), except it stores INT64_MAX on overflow,
* and INT_MIN on underflow.
*/
int qemu_strtoll(const char *nptr, const char **endptr, int base,
int qemu_strtoi64(const char *nptr, const char **endptr, int base,
int64_t *result)
{
char *p;
int err = 0;
char *ep;
if (!nptr) {
if (endptr) {
*endptr = nptr;
}
err = -EINVAL;
} else {
errno = 0;
*result = strtoll(nptr, &p, base);
err = check_strtox_error(nptr, p, endptr, errno);
return -EINVAL;
}
return err;
errno = 0;
/* FIXME This assumes int64_t is long long */
*result = strtoll(nptr, &ep, base);
return check_strtox_error(nptr, ep, endptr, errno);
}
/**
* Converts ASCII string to an unsigned long long integer.
* Convert string @nptr to an uint64_t.
*
* See qemu_strtol() documentation for more info.
* Works like qemu_strtoul(), except it stores UINT64_MAX on overflow.
*/
int qemu_strtoull(const char *nptr, const char **endptr, int base,
int qemu_strtou64(const char *nptr, const char **endptr, int base,
uint64_t *result)
{
char *p;
int err = 0;
char *ep;
if (!nptr) {
if (endptr) {
*endptr = nptr;
}
err = -EINVAL;
} else {
errno = 0;
*result = strtoull(nptr, &p, base);
/* Windows returns 1 for negative out-of-range values. */
if (errno == ERANGE) {
*result = -1;
}
err = check_strtox_error(nptr, p, endptr, errno);
return -EINVAL;
}
return err;
errno = 0;
/* FIXME This assumes uint64_t is unsigned long long */
*result = strtoull(nptr, &ep, base);
/* Windows returns 1 for negative out-of-range values. */
if (errno == ERANGE) {
*result = -1;
}
return check_strtox_error(nptr, ep, endptr, errno);
}
/**

View file

@ -183,13 +183,13 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp)
goto out;
}
if (qemu_strtoull(r, &e, 0, &r1val)
if (qemu_strtou64(r, &e, 0, &r1val)
|| e != range_op) {
error_setg(errp, "Invalid number to the left of %.*s",
(int)(r2 - range_op), range_op);
goto out;
}
if (qemu_strtoull(r2, NULL, 0, &r2val)) {
if (qemu_strtou64(r2, NULL, 0, &r2val)) {
error_setg(errp, "Invalid number to the right of %.*s",
(int)(r2 - range_op), range_op);
goto out;

View file

@ -128,36 +128,33 @@ int get_param_value(char *buf, int buf_size,
static void parse_option_bool(const char *name, const char *value, bool *ret,
Error **errp)
{
if (value != NULL) {
if (!strcmp(value, "on")) {
*ret = 1;
} else if (!strcmp(value, "off")) {
*ret = 0;
} else {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
name, "'on' or 'off'");
}
} else {
if (!strcmp(value, "on")) {
*ret = 1;
} else if (!strcmp(value, "off")) {
*ret = 0;
} else {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
name, "'on' or 'off'");
}
}
static void parse_option_number(const char *name, const char *value,
uint64_t *ret, Error **errp)
{
char *postfix;
uint64_t number;
int err;
if (value != NULL) {
number = strtoull(value, &postfix, 0);
if (*postfix != '\0') {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
return;
}
*ret = number;
} else {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
err = qemu_strtou64(value, NULL, 0, &number);
if (err == -ERANGE) {
error_setg(errp, "Value '%s' is too large for parameter '%s'",
value, name);
return;
}
if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
return;
}
*ret = number;
}
static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc,
@ -177,43 +174,24 @@ static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc,
void parse_option_size(const char *name, const char *value,
uint64_t *ret, Error **errp)
{
char *postfix;
double sizef;
uint64_t size;
int err;
if (value != NULL) {
sizef = strtod(value, &postfix);
if (sizef < 0 || sizef > UINT64_MAX) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
"a non-negative number below 2^64");
return;
}
switch (*postfix) {
case 'T':
sizef *= 1024;
/* fall through */
case 'G':
sizef *= 1024;
/* fall through */
case 'M':
sizef *= 1024;
/* fall through */
case 'K':
case 'k':
sizef *= 1024;
/* fall through */
case 'b':
case '\0':
*ret = (uint64_t) sizef;
break;
default:
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
error_append_hint(errp, "You may use k, M, G or T suffixes for "
"kilobytes, megabytes, gigabytes and terabytes.\n");
return;
}
} else {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size");
err = qemu_strtosz(value, NULL, &size);
if (err == -ERANGE) {
error_setg(errp, "Value '%s' is too large for parameter '%s'",
value, name);
return;
}
if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
"a non-negative number below 2^64");
error_append_hint(errp, "Optional suffix k, M, G, T, P or E means"
" kilo-, mega-, giga-, tera-, peta-\n"
"and exabytes, respectively.\n");
return;
}
*ret = size;
}
bool has_help_option(const char *param)
@ -566,6 +544,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value,
}
opt->desc = desc;
opt->str = g_strdup(value);
assert(opt->str);
qemu_opt_parse(opt, &local_err);
if (local_err) {
error_propagate(errp, local_err);