mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
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:
commit
d7941f4eed
15 changed files with 996 additions and 494 deletions
11
hmp.c
11
hmp.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
62
qemu-img.c
62
qemu-img.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
34
qtest.c
|
@ -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",
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
239
util/cutils.c
239
util/cutils.c
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue