On Fri, May 30, 2025 at 10:44:44AM +0200, Hanna Czenczek wrote:
> Add a command-line-only option to prevent overwriting the file specified
> as external data file.
> 
> This option is only available on the qemu-img create command line, not
> via blockdev-create, as it makes no sense there: That interface
> separates file creation and formatting, so where the external data file
> attached to a newly formatted qcow2 node comes from is completely up to
> the user.
> 
> Implementation detail: Enabling this option will not only not overwrite
> the external data file, but also assume it already exists, for two
> reasons:
> - It is simpler than checking whether the file exists, and only skipping
>   creating it when it does not.  It is therefore also less error-prone,
>   i.e. we can never accidentally overwrite an existing file because we
>   made some mistake in checking whether it exists.
> - I think it makes sense from a user's perspective: You set this option
>   when you want to use an existing data file, and you unset it when you
>   want a new one.  Getting an error when you expect to use an existing
>   data file seems to me a nice warning that something is not right.

And it also helps if you accidentally type something like nbd://... as
the data file and expect it to exist, but qemu interprets that as a
local filename ./nbd:/... which (hopefully) does not exist.

> 
> Signed-off-by: Hanna Czenczek <hre...@redhat.com>
> ---
>  include/block/block_int-common.h |  1 +
>  block/qcow2.c                    | 75 ++++++++++++++++++++++++++++++--
>  tests/qemu-iotests/082.out       | 18 ++++++++
>  3 files changed, 90 insertions(+), 4 deletions(-)
> 
> @@ -3933,6 +3935,22 @@ qcow2_co_create_opts(BlockDriver *drv, const char 
> *filename, QemuOpts *opts,
>          qdict_put_str(qdict, BLOCK_OPT_COMPAT_LEVEL, "v3");
>      }
>  
> +    val = qdict_get_try_str(qdict, BLOCK_OPT_KEEP_DATA_FILE);
> +    if (val) {
> +        if (!strcmp(val, "on")) {
> +            keep_data_file = true;
> +        } else if (!strcmp(val, "off")) {
> +            keep_data_file = false;
> +        } else {
> +            error_setg(errp,
> +                       "Invalid value '%s' for '%s': Must be 'on' or 'off'",
> +                       val, BLOCK_OPT_KEEP_DATA_FILE);

Do we have a function that does this parsing so that we don't have to
open-code copy-paste (with slight differences) this sort of parsing
into similar clients?  A quick grep for "'on' or 'off'" shows at least:

monitor/hmp.c:                    monitor_printf(mon, "Expected 'on' or 
'off'\n");
net/filter.c:                         "should be 'on' or 'off'");
qapi/qapi-util.c:               "'on' or 'off'");
qapi/qobject-input-visitor.c:                   full_name(qiv, name), "'on' or 
'off'");

Not necessarily a show-stopper to this patch, but food for thought in
the technical debt department.

> @@ -4004,6 +4029,40 @@ qcow2_co_create_opts(BlockDriver *drv, const char 
> *filename, QemuOpts *opts,
>          goto finish;
>      }
>  
> +    qcow2_opts = &create_options->u.qcow2;
> +
> +    if (!qcow2_opts->has_preallocation) {
> +        qcow2_opts->preallocation = PREALLOC_MODE_OFF;
> +    }
> +    if (!qcow2_opts->has_data_file_raw) {
> +        qcow2_opts->data_file_raw = false;
> +    }
> +
> +    if (keep_data_file &&
> +        qcow2_opts->preallocation != PREALLOC_MODE_OFF &&
> +        qcow2_opts->preallocation != PREALLOC_MODE_METADATA)
> +    {
> +        error_setg(errp, "Preallocating more than only metadata would "
> +                   "overwrite the external data file's content and is "
> +                   "therefore incompatible with '%s=on'",
> +                   BLOCK_OPT_KEEP_DATA_FILE);

I like this safety check.

Reviewed-by: Eric Blake <ebl...@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.
Virtualization:  qemu.org | libguestfs.org


Reply via email to