From 6d2b5cbafb8fb4bb3563cbf698b3a0903a993d7a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Apr 2020 09:49:25 +0200 Subject: qemu-img: Factor out accumulate_options() helper Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Reviewed-by: Kevin Wolf Message-Id: <20200415074927.19897-8-armbru@redhat.com> --- qemu-img.c | 59 +++++++++++++++++++++++------------------------------------ 1 file changed, 23 insertions(+), 36 deletions(-) (limited to 'qemu-img.c') diff --git a/qemu-img.c b/qemu-img.c index 821cbf610e..d36b21b758 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -223,6 +223,25 @@ static bool qemu_img_object_print_help(const char *type, QemuOpts *opts) return true; } +static int accumulate_options(char **options, char *optarg) +{ + char *new_options; + + if (!is_valid_option_list(optarg)) { + error_report("Invalid option list: %s", optarg); + return -1; + } + + if (!*options) { + *options = g_strdup(optarg); + } else { + new_options = g_strdup_printf("%s,%s", *options, optarg); + g_free(*options); + *options = new_options; + } + return 0; +} + static QemuOptsList qemu_source_opts = { .name = "source", .implied_opt_name = "file", @@ -482,17 +501,9 @@ static int img_create(int argc, char **argv) fmt = optarg; break; case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); + if (accumulate_options(&options, optarg) < 0) { goto fail; } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } break; case 'q': quiet = true; @@ -2127,17 +2138,9 @@ static int img_convert(int argc, char **argv) s.compressed = true; break; case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); + if (accumulate_options(&options, optarg) < 0) { goto fail_getopt; } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { @@ -3953,18 +3956,10 @@ static int img_amend(int argc, char **argv) help(); break; case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); + if (accumulate_options(&options, optarg) < 0) { ret = -1; goto out_no_progress; } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } break; case 'f': fmt = optarg; @@ -4855,17 +4850,9 @@ static int img_measure(int argc, char **argv) out_fmt = optarg; break; case 'o': - if (!is_valid_option_list(optarg)) { - error_report("Invalid option list: %s", optarg); + if (accumulate_options(&options, optarg) < 0) { goto out; } - if (!options) { - options = g_strdup(optarg); - } else { - char *old_options = options; - options = g_strdup_printf("%s,%s", options, optarg); - g_free(old_options); - } break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { -- cgit 1.4.1 From 80c710cb06ff40b45de033e4352528b3adcd2de9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Apr 2020 09:49:26 +0200 Subject: qemu-img: Move is_valid_option_list() to qemu-img.c and rewrite is_valid_option_list()'s purpose is ensuring qemu-img.c's can safely join multiple parameter strings separated by ',' like this: g_strdup_printf("%s,%s", params1, params2); How it does that is anything but obvious. A close reading of the code reveals that it fails exactly when its argument starts with ',' or ends with an odd number of ','. Makes sense, actually, because when the argument starts with ',', a separating ',' preceding it would get escaped, and when it ends with an odd number of ',', a separating ',' following it would get escaped. Move it to qemu-img.c and rewrite it the obvious way. Signed-off-by: Markus Armbruster Reviewed-by: Eric Blake Message-Id: <20200415074927.19897-9-armbru@redhat.com> --- include/qemu/option.h | 1 - qemu-img.c | 26 ++++++++++++++++++++++++++ util/qemu-option.c | 22 ---------------------- 3 files changed, 26 insertions(+), 23 deletions(-) (limited to 'qemu-img.c') diff --git a/include/qemu/option.h b/include/qemu/option.h index 844587cab3..eb4097889d 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -33,7 +33,6 @@ const char *get_opt_value(const char *p, char **value); void parse_option_size(const char *name, const char *value, uint64_t *ret, Error **errp); bool has_help_option(const char *param); -bool is_valid_option_list(const char *param); enum QemuOptType { QEMU_OPT_STRING = 0, /* no parsing (use string as-is) */ diff --git a/qemu-img.c b/qemu-img.c index d36b21b758..cc51db7ed4 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -223,6 +223,32 @@ static bool qemu_img_object_print_help(const char *type, QemuOpts *opts) return true; } +/* + * Is @optarg safe for accumulate_options()? + * It is when multiple of them can be joined together separated by ','. + * To make that work, @optarg must not start with ',' (or else a + * separating ',' preceding it gets escaped), and it must not end with + * an odd number of ',' (or else a separating ',' following it gets + * escaped). + */ +static bool is_valid_option_list(const char *optarg) +{ + size_t len = strlen(optarg); + size_t i; + + if (optarg[0] == ',') { + return false; + } + + for (i = len; i > 0 && optarg[i - 1] == ','; i--) { + } + if ((len - i) % 2) { + return false; + } + + return true; +} + static int accumulate_options(char **options, char *optarg) { char *new_options; diff --git a/util/qemu-option.c b/util/qemu-option.c index 2d0d24ee27..9542988183 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -165,28 +165,6 @@ void parse_option_size(const char *name, const char *value, *ret = size; } -bool is_valid_option_list(const char *p) -{ - char *value = NULL; - bool result = false; - - while (*p) { - p = get_opt_value(p, &value); - if ((*p && !*++p) || - (!*value || *value == ',')) { - goto out; - } - - g_free(value); - value = NULL; - } - - result = true; -out: - g_free(value); - return result; -} - static const char *opt_type_to_string(enum QemuOptType type) { switch (type) { -- cgit 1.4.1 From f62514b3def5fb2acbef64d0e053c0c31fa45aff Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 15 Apr 2020 09:49:27 +0200 Subject: qemu-img: Reject broken -o "" qemu-img create, convert, amend, and measure use accumulate_options() to merge multiple -o options. This is broken for -o "": $ qemu-img create -f qcow2 -o backing_file=a -o "" -o backing_fmt=raw,size=1M new.qcow2 qemu-img: warning: Could not verify backing image. This may become an error in future versions. Could not open 'a,backing_fmt=raw': No such file or directory Formatting 'new.qcow2', fmt=qcow2 size=1048576 backing_file=a,,backing_fmt=raw cluster_size=65536 lazy_refcounts=off refcount_bits=16 $ qemu-img info new.qcow2 image: new.qcow2 file format: qcow2 virtual size: 1 MiB (1048576 bytes) disk size: 196 KiB cluster_size: 65536 --> backing file: a,backing_fmt=raw Format specific information: compat: 1.1 lazy refcounts: false refcount bits: 16 corrupt: false Merging these three -o the obvious way is wrong, because it results in an unwanted ',' escape: backing_file=a,,backing_fmt=raw,size=1M ~~ We could silently drop -o "", but Kevin asked me to reject it instead. Signed-off-by: Markus Armbruster Message-Id: <20200415074927.19897-10-armbru@redhat.com> Reviewed-by: Eric Blake --- qemu-img.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'qemu-img.c') diff --git a/qemu-img.c b/qemu-img.c index cc51db7ed4..a2369766f0 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -229,14 +229,16 @@ static bool qemu_img_object_print_help(const char *type, QemuOpts *opts) * To make that work, @optarg must not start with ',' (or else a * separating ',' preceding it gets escaped), and it must not end with * an odd number of ',' (or else a separating ',' following it gets - * escaped). + * escaped), or be empty (or else a separating ',' preceding it can + * escape a separating ',' following it). + * */ static bool is_valid_option_list(const char *optarg) { size_t len = strlen(optarg); size_t i; - if (optarg[0] == ',') { + if (!optarg[0] || optarg[0] == ',') { return false; } -- cgit 1.4.1