From: Kevin Wolf <kwolf@redhat.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, qemu-devel@nongnu.org
Subject: [PULL 09/12] qcow2: Add keep_data_file command-line option
Date: Fri, 6 Mar 2026 19:37:02 +0100 [thread overview]
Message-ID: <20260306183705.410357-10-kwolf@redhat.com> (raw)
In-Reply-To: <20260306183705.410357-1-kwolf@redhat.com>
From: Hanna Czenczek <hreitz@redhat.com>
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.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Message-ID: <20250530084448.192369-2-hreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[kwolf: Removed redundant has_data_file_raw check]
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
include/block/block_int-common.h | 1 +
block/qcow2.c | 72 ++++++++++++++++++++++++++++++--
tests/qemu-iotests/082.out | 18 ++++++++
3 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
index 9324af903d0..147c08155f7 100644
--- a/include/block/block_int-common.h
+++ b/include/block/block_int-common.h
@@ -56,6 +56,7 @@
#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw"
#define BLOCK_OPT_COMPRESSION_TYPE "compression_type"
#define BLOCK_OPT_EXTL2 "extended_l2"
+#define BLOCK_OPT_KEEP_DATA_FILE "keep_data_file"
#define BLOCK_PROBE_BUF_SIZE 512
diff --git a/block/qcow2.c b/block/qcow2.c
index e29810d86a3..e3728806203 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3991,6 +3991,8 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
BlockDriverState *bs = NULL;
BlockDriverState *data_bs = NULL;
const char *val;
+ bool keep_data_file = false;
+ BlockdevCreateOptionsQcow2 *qcow2_opts;
int ret;
/* Only the keyval visitor supports the dotted syntax needed for
@@ -4022,6 +4024,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);
+ ret = -EINVAL;
+ goto finish;
+ }
+ qdict_del(qdict, BLOCK_OPT_KEEP_DATA_FILE);
+ }
+
/* Change legacy command line options into QMP ones */
static const QDictRenames opt_renames[] = {
{ BLOCK_OPT_BACKING_FILE, "backing-file" },
@@ -4058,9 +4076,11 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
/* Create and open an external data file (protocol layer) */
val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE);
if (val) {
- ret = bdrv_co_create_file(val, opts, false, errp);
- if (ret < 0) {
- goto finish;
+ if (!keep_data_file) {
+ ret = bdrv_co_create_file(val, opts, false, errp);
+ if (ret < 0) {
+ goto finish;
+ }
}
data_bs = bdrv_co_open(val, NULL, NULL,
@@ -4073,6 +4093,11 @@ qcow2_co_create_opts(BlockDriver *drv, const char *filename, QemuOpts *opts,
qdict_del(qdict, BLOCK_OPT_DATA_FILE);
qdict_put_str(qdict, "data-file", data_bs->node_name);
+ } else if (keep_data_file) {
+ error_setg(errp, "Must not use '%s=on' without '%s'",
+ BLOCK_OPT_KEEP_DATA_FILE, BLOCK_OPT_DATA_FILE);
+ ret = -EINVAL;
+ goto finish;
}
/* Set 'driver' and 'node' options */
@@ -4093,6 +4118,37 @@ 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 (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);
+ ret = -EINVAL;
+ goto finish;
+ }
+
+ if (keep_data_file &&
+ qcow2_opts->preallocation == PREALLOC_MODE_OFF &&
+ !qcow2_opts->data_file_raw)
+ {
+ error_setg(errp, "'%s=on' requires '%s=metadata' or '%s=on', or the "
+ "file contents will not be visible",
+ BLOCK_OPT_KEEP_DATA_FILE,
+ BLOCK_OPT_PREALLOC,
+ BLOCK_OPT_DATA_FILE_RAW);
+ ret = -EINVAL;
+ goto finish;
+ }
+
/* Silently round up size */
create_options->u.qcow2.size = ROUND_UP(create_options->u.qcow2.size,
BDRV_SECTOR_SIZE);
@@ -4103,7 +4159,9 @@ finish:
if (ret < 0) {
bdrv_graph_co_rdlock();
bdrv_co_delete_file_noerr(bs);
- bdrv_co_delete_file_noerr(data_bs);
+ if (!keep_data_file) {
+ bdrv_co_delete_file_noerr(data_bs);
+ }
bdrv_graph_co_rdunlock();
} else {
ret = 0;
@@ -6202,6 +6260,12 @@ static QemuOptsList qcow2_create_opts = {
.help = "Compression method used for image cluster " \
"compression", \
.def_value_str = "zlib" \
+ }, \
+ { \
+ .name = BLOCK_OPT_KEEP_DATA_FILE, \
+ .type = QEMU_OPT_BOOL, \
+ .help = "Assume the external data file already exists and " \
+ "do not overwrite it" \
},
QCOW_COMMON_OPTIONS,
{ /* end of list */ }
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
index d0dd333117a..e0463815c65 100644
--- a/tests/qemu-iotests/082.out
+++ b/tests/qemu-iotests/082.out
@@ -66,6 +66,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -92,6 +93,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -118,6 +120,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -144,6 +147,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -170,6 +174,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -196,6 +201,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -222,6 +228,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -248,6 +255,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -288,6 +296,7 @@ Supported qcow2 options:
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
refcount_bits=<num> - Width of a reference count entry in bits
@@ -376,6 +385,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -402,6 +412,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -428,6 +439,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -454,6 +466,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -480,6 +493,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -506,6 +520,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -532,6 +547,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -558,6 +574,7 @@ Supported options:
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
extent_size_hint=<size> - Extent size hint for the image file, 0 to disable
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
@@ -598,6 +615,7 @@ Supported qcow2 options:
encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
extended_l2=<bool (on/off)> - Extended L2 tables
+ keep_data_file=<bool (on/off)> - Assume the external data file already exists and do not overwrite it
lazy_refcounts=<bool (on/off)> - Postpone refcount updates
preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
refcount_bits=<num> - Width of a reference count entry in bits
--
2.53.0
next prev parent reply other threads:[~2026-03-06 18:40 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-06 18:36 [PULL 00/12] Block layer patches Kevin Wolf
2026-03-06 18:36 ` [PULL 01/12] block/vmdk: fix OOB read in vmdk_read_extent() Kevin Wolf
2026-03-06 18:36 ` [PULL 02/12] block: Wire up 'flat' mode also for 'query-block' Kevin Wolf
2026-03-06 18:36 ` [PULL 03/12] hmp_nbd_server_start: Don't ask for backing image data Kevin Wolf
2026-03-06 18:36 ` [PULL 04/12] block/curl: fix concurrent completion handling Kevin Wolf
2026-03-06 18:36 ` [PULL 05/12] mirror: Fix missed dirty bitmap writes during startup Kevin Wolf
2026-03-06 18:36 ` [PULL 06/12] block/throttle-groups: fix deadlock with iolimits and muliple iothreads Kevin Wolf
2026-03-06 18:37 ` [PULL 07/12] block: Never drop BLOCK_IO_ERROR with action=stop for rate limiting Kevin Wolf
2026-03-06 18:37 ` [PULL 08/12] block/nfs: Do not enter coroutine from CB Kevin Wolf
2026-03-06 18:37 ` Kevin Wolf [this message]
2026-03-06 18:37 ` [PULL 10/12] qcow2: Simplify size round-up in co_create_opts Kevin Wolf
2026-03-06 18:37 ` [PULL 11/12] iotests/common.filter: Sort keep_data_file Kevin Wolf
2026-03-06 18:37 ` [PULL 12/12] iotests/244: Add test cases for keep_data_file Kevin Wolf
2026-03-07 11:22 ` [PULL 00/12] Block layer patches Peter Maydell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260306183705.410357-10-kwolf@redhat.com \
--to=kwolf@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.