Browse Source

qemu-img: extend cvtnum() and use it in more places

cvtnum() expects input string to specify some sort of size
(optionally with KMG... suffix).  However, there are a lot
of other number conversions in there (using qemu_strtol &Co),
also, not all conversions which use cvtnum, actually expects
size, - like dd count=nn.

Add bool is_size argument to cvtnum() to specify if it should
treat the argument as a size or something else, - this changes
conversion routine in use and error text.

Use the new cvtnum() in more places (like where strtol were used),
since it never return negative number in successful conversion.
When it makes sense, also specify upper or lower bounds at the
same time.  This simplifies option processing in multiple places,
removing the need of local temporary variables and longer error
reporting code.

While at it, fix errors, like depth in measure must be >= 1,
while the previous code allowed it to be 0.

In a few places, change unsigned variables (like of type size_t)
to be signed instead, - to avoid the need of temporary conversion
variable.  All these variables are okay to be signed, we never
assign <0 value to them except of the cases of conversion error,
where we return immediately.

While at it, remove allowed size suffixes from the error message
as it makes no sense most of the time (should be in help instead).

Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20250531171609.197078-28-mjt@tls.msk.ru>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
pull/294/head
Michael Tokarev 10 months ago
committed by Kevin Wolf
parent
commit
d7bd47bf84
  1. 111
      qemu-img.c
  2. 9
      tests/qemu-iotests/049.out

111
qemu-img.c

@ -398,18 +398,16 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts,
return 0; return 0;
} }
static int64_t cvtnum_full(const char *name, const char *value, int64_t min, static int64_t cvtnum_full(const char *name, const char *value,
int64_t max) bool is_size, int64_t min, int64_t max)
{ {
int err; int err;
uint64_t res; uint64_t res;
err = qemu_strtosz(value, NULL, &res); err = is_size ? qemu_strtosz(value, NULL, &res) :
qemu_strtou64(value, NULL, 0, &res);
if (err < 0 && err != -ERANGE) { if (err < 0 && err != -ERANGE) {
error_report("Invalid %s specified. You may use " error_report("Invalid %s specified: '%s'", name, value);
"k, M, G, T, P or E suffixes for", name);
error_report("kilobytes, megabytes, gigabytes, terabytes, "
"petabytes and exabytes.");
return err; return err;
} }
if (err == -ERANGE || res > max || res < min) { if (err == -ERANGE || res > max || res < min) {
@ -420,9 +418,9 @@ static int64_t cvtnum_full(const char *name, const char *value, int64_t min,
return res; return res;
} }
static int64_t cvtnum(const char *name, const char *value) static int64_t cvtnum(const char *name, const char *value, bool is_size)
{ {
return cvtnum_full(name, value, 0, INT64_MAX); return cvtnum_full(name, value, is_size, 0, INT64_MAX);
} }
static int img_create(const img_cmd_t *ccmd, int argc, char **argv) static int img_create(const img_cmd_t *ccmd, int argc, char **argv)
@ -525,7 +523,7 @@ static int img_create(const img_cmd_t *ccmd, int argc, char **argv)
/* Get image size, if specified */ /* Get image size, if specified */
if (optind < argc) { if (optind < argc) {
img_size = cvtnum("image size", argv[optind++]); img_size = cvtnum("image size", argv[optind++], true);
if (img_size < 0) { if (img_size < 0) {
goto fail; goto fail;
} }
@ -984,7 +982,7 @@ static int img_commit(const img_cmd_t *ccmd, int argc, char **argv)
drop = true; drop = true;
break; break;
case 'r': case 'r':
rate_limit = cvtnum("rate limit", optarg); rate_limit = cvtnum("rate limit", optarg, true);
if (rate_limit < 0) { if (rate_limit < 0) {
return 1; return 1;
} }
@ -2428,7 +2426,7 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
{ {
int64_t sval; int64_t sval;
sval = cvtnum("buffer size for sparse output", optarg); sval = cvtnum("buffer size for sparse output", optarg, true);
if (sval < 0) { if (sval < 0) {
goto fail_getopt; goto fail_getopt;
} else if (!QEMU_IS_ALIGNED(sval, BDRV_SECTOR_SIZE) || } else if (!QEMU_IS_ALIGNED(sval, BDRV_SECTOR_SIZE) ||
@ -2462,16 +2460,15 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
force_share = true; force_share = true;
break; break;
case 'r': case 'r':
rate_limit = cvtnum("rate limit", optarg); rate_limit = cvtnum("rate limit", optarg, true);
if (rate_limit < 0) { if (rate_limit < 0) {
goto fail_getopt; goto fail_getopt;
} }
break; break;
case 'm': case 'm':
if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || s.num_coroutines = cvtnum_full("number of coroutines", optarg,
s.num_coroutines < 1 || s.num_coroutines > MAX_COROUTINES) { false, 1, MAX_COROUTINES);
error_report("Invalid number of coroutines. Allowed number of" if (s.num_coroutines < 0) {
" coroutines is between 1 and %d", MAX_COROUTINES);
goto fail_getopt; goto fail_getopt;
} }
break; break;
@ -3376,13 +3373,13 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv)
image_opts = true; image_opts = true;
break; break;
case 's': case 's':
start_offset = cvtnum("start offset", optarg); start_offset = cvtnum("start offset", optarg, true);
if (start_offset < 0) { if (start_offset < 0) {
return 1; return 1;
} }
break; break;
case 'l': case 'l':
max_length = cvtnum("max length", optarg); max_length = cvtnum("max length", optarg, true);
if (max_length < 0) { if (max_length < 0) {
return 1; return 1;
} }
@ -4720,9 +4717,9 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv)
int count = 75000; int count = 75000;
int depth = 64; int depth = 64;
int64_t offset = 0; int64_t offset = 0;
size_t bufsize = 4096; ssize_t bufsize = 4096;
int pattern = 0; int pattern = 0;
size_t step = 0; ssize_t step = 0;
int flush_interval = 0; int flush_interval = 0;
bool drain_on_flush = true; bool drain_on_flush = true;
int64_t image_size; int64_t image_size;
@ -4827,27 +4824,17 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv)
} }
break; break;
case 'c': case 'c':
{ count = cvtnum_full("request count", optarg, false, 1, INT_MAX);
unsigned long res; if (count < 0) {
if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) {
error_report("Invalid request count specified");
return 1; return 1;
} }
count = res;
break; break;
}
case 'd': case 'd':
{ depth = cvtnum_full("queue depth", optarg, false, 1, INT_MAX);
unsigned long res; if (depth < 0) {
if (qemu_strtoul(optarg, NULL, 0, &res) <= 0 || res > INT_MAX) {
error_report("Invalid queue depth specified");
return 1; return 1;
} }
depth = res;
break; break;
}
case 'n': case 'n':
flags |= BDRV_O_NATIVE_AIO; flags |= BDRV_O_NATIVE_AIO;
break; break;
@ -4860,64 +4847,40 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv)
} }
break; break;
case 'o': case 'o':
{ offset = cvtnum("offset", optarg, true);
offset = cvtnum("offset", optarg);
if (offset < 0) { if (offset < 0) {
return 1; return 1;
} }
break; break;
}
break;
case 's': case 's':
{ bufsize = cvtnum_full("buffer size", optarg, true, 1, INT_MAX);
int64_t sval; if (bufsize < 0) {
sval = cvtnum_full("buffer size", optarg, 0, INT_MAX);
if (sval < 0) {
return 1; return 1;
} }
bufsize = sval;
break; break;
}
case 'S': case 'S':
{ step = cvtnum_full("step size", optarg, true, 0, INT_MAX);
int64_t sval; if (step < 0) {
sval = cvtnum_full("step_size", optarg, 0, INT_MAX);
if (sval < 0) {
return 1; return 1;
} }
step = sval;
break; break;
}
case 'w': case 'w':
flags |= BDRV_O_RDWR; flags |= BDRV_O_RDWR;
is_write = true; is_write = true;
break; break;
case OPTION_PATTERN: case OPTION_PATTERN:
{ pattern = cvtnum_full("pattern byte", optarg, false, 0, 0xff);
unsigned long res; if (pattern < 0) {
if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > 0xff) {
error_report("Invalid pattern byte specified");
return 1; return 1;
} }
pattern = res;
break; break;
}
case OPTION_FLUSH_INTERVAL: case OPTION_FLUSH_INTERVAL:
{ flush_interval = cvtnum_full("flush interval", optarg,
unsigned long res; false, 0, INT_MAX);
if (flush_interval < 0) {
if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) {
error_report("Invalid flush interval specified");
return 1; return 1;
} }
flush_interval = res;
break; break;
}
case OPTION_NO_DRAIN: case OPTION_NO_DRAIN:
drain_on_flush = false; drain_on_flush = false;
break; break;
@ -5129,7 +5092,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv)
add = true; add = true;
break; break;
case 'g': case 'g':
granularity = cvtnum("granularity", optarg); granularity = cvtnum("granularity", optarg, true);
if (granularity < 0) { if (granularity < 0) {
return 1; return 1;
} }
@ -5314,7 +5277,7 @@ static int img_dd_bs(const char *arg,
{ {
int64_t res; int64_t res;
res = cvtnum_full("bs", arg, 1, INT_MAX); res = cvtnum_full("bs", arg, true, 1, INT_MAX);
if (res < 0) { if (res < 0) {
return 1; return 1;
@ -5328,7 +5291,7 @@ static int img_dd_count(const char *arg,
struct DdIo *in, struct DdIo *out, struct DdIo *in, struct DdIo *out,
struct DdInfo *dd) struct DdInfo *dd)
{ {
dd->count = cvtnum("count", arg); dd->count = cvtnum("count", arg, true);
if (dd->count < 0) { if (dd->count < 0) {
return 1; return 1;
@ -5359,7 +5322,7 @@ static int img_dd_skip(const char *arg,
struct DdIo *in, struct DdIo *out, struct DdIo *in, struct DdIo *out,
struct DdInfo *dd) struct DdInfo *dd)
{ {
in->offset = cvtnum("skip", arg); in->offset = cvtnum("skip", arg, true);
if (in->offset < 0) { if (in->offset < 0) {
return 1; return 1;
@ -5767,7 +5730,7 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv)
user_creatable_process_cmdline(optarg); user_creatable_process_cmdline(optarg);
break; break;
case 's': case 's':
img_size = cvtnum("image size", optarg); img_size = cvtnum("image size", optarg, true);
if (img_size < 0) { if (img_size < 0) {
goto out; goto out;
} }

9
tests/qemu-iotests/049.out

@ -98,8 +98,7 @@ 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: Value '-1024' is out of range for parameter 'size'
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for qemu-img: Invalid image size specified: '-1k'
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2
qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
@ -107,8 +106,7 @@ Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
and exabytes, respectively. and exabytes, respectively.
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte 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 qemu-img: Invalid image size specified: '1kilobyte'
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2
qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64
@ -116,8 +114,7 @@ Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
and exabytes, respectively. and exabytes, respectively.
qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar
qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for qemu-img: Invalid image size specified: 'foobar'
qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes.
qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2
qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64

Loading…
Cancel
Save