Paolo Bonzini <pbonz...@redhat.com> writes:

> Forbid ids if the option is intended to be a singleton, as indicated by
> list->merge_lists.

Well, ->merge_lists need not imply singleton.  Perhaps we only ever use
it that way.  Careful review is called for.

>                     This avoids that "./qemu-system-x86_64 -M q35,id=ff"
> uses a "pc" machine type.

Just like any other QemuOptsList, "machine" may have any number of
QemuOpts.  The ones with non-null ID happen to be ignored silently.
Known[*] trap for the unwary.

>                            Instead it errors out.  The affected options
> are "qemu-img reopen -o",

reopen_opts in qemu-io-cmds.c

>                           "qemu-io open -o",

empty_opts in qemu-io.c

>                                              -rtc, -M, -boot, -name,
> -m, -icount, -smp,

qemu_rtc_opts, qemu_machine_opts, qemu_boot_opts, qemu_name_opts,
qemu_mem_opts, qemu_icount_opts, qemu_smp_opts in vl.c

>                    -spice.

qemu_spice_opts in spice-core.c.

Are these all singletons?

There's also machine_opts in qemu-config.c, but that's only to make
query-command-line-options lie backward-compatibly.

>
> qemu_opts_create's fail_if_exists parameter is now unnecessary:
>
> - it is unused if id is NULL
>
> - opts_parse only passes false if reached from qemu_opts_set_defaults,
> in which case this patch enforces that id must be NULL
>
> - other callers that can pass a non-NULL id always set it to true
>
> Assert that it is true in the only case where "fail_if_exists" matters,
> i.e. "id && !lists->merge_lists".  This means that if an id is present,
> duplicates are always forbidden, which was already the status quo.

Sounds like you're specializing the code (which might be good).

> Signed-off-by: Paolo Bonzini <pbonz...@redhat.com>
> ---
>  util/qemu-option.c | 27 ++++++++++++++-------------
>  1 file changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/util/qemu-option.c b/util/qemu-option.c
> index c88e159f18..91f4120ce1 100644
> --- a/util/qemu-option.c
> +++ b/util/qemu-option.c
> @@ -619,7 +619,17 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const 
> char *id,
>  {
>      QemuOpts *opts = NULL;
>  
> -    if (id) {
> +    if (list->merge_lists) {
> +        if (id) {
> +            error_setg(errp, QERR_INVALID_PARAMETER, "id");
> +            return NULL;
> +        }
> +        opts = qemu_opts_find(list, NULL);
> +        if (opts) {
> +            return opts;
> +        }

If lists>merge_lists, you no longer check id_wellformed().  Easy enough
to fix: lift the check before this conditional.

> +    } else if (id) {
> +        assert(fail_if_exists);
>          if (!id_wellformed(id)) {
>              error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id",
>                         "an identifier");
> @@ -629,17 +639,8 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const 
> char *id,
>          }
>          opts = qemu_opts_find(list, id);
>          if (opts != NULL) {
> -            if (fail_if_exists && !list->merge_lists) {
> -                error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
> -                return NULL;
> -            } else {
> -                return opts;
> -            }
> -        }
> -    } else if (list->merge_lists) {
> -        opts = qemu_opts_find(list, NULL);
> -        if (opts) {
> -            return opts;
> +            error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
> +            return NULL;
>          }
>      }
>      opts = g_malloc0(sizeof(*opts));

Paths through the function before the patch:

    id        fail_if_exists  merge_lists  |  return
    null      don't care      false        |  new opts
    null      don't care      true         |  existing or else new opts
    non-null  false           don't care   |  existing or else new opts
    non-null  true            true         |  existing or else new opts
    non-null  true            false        |  new opts / fail if exist

Too many cases.

After the patch:

    id        fail_if_exists  merge_lists  |  return
    non-null  don't care      true         |  fail
    null      don't care      true         |  existing or else new opts
    non-null  false           false        |  abort
    non-null  true            false        |  new opts / fail if exist
    null      don't care      false        |  new opts

Still too many :)

I'm running out of time for today, and I still need to convince myself
that the changes in behavior are all okay.

> @@ -893,7 +894,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const 
> char *params,
>       * (if unlikely) future misuse:
>       */
>      assert(!defaults || list->merge_lists);
> -    opts = qemu_opts_create(list, id, !defaults, errp);
> +    opts = qemu_opts_create(list, id, !list->merge_lists, errp);
>      g_free(id);
>      if (opts == NULL) {
>          return NULL;



[*] Known to the QemuOpts cognoscenti, whose number could be
embarrasingly close to one.


Reply via email to