* [PATCH 0/5] qcow2: Suppress data-file WRITE during creation
@ 2026-02-05 14:47 Hanna Czenczek
2026-02-05 14:47 ` [PATCH 1/5] qcow2: Skip data-file resize if possible Hanna Czenczek
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
Hi,
Some people want to create a qcow2 image for an existing raw image while
that raw image is in use by a VM. That is, the raw image is attached to
a guest device, and the new qcow2 image is supposed to use it as its raw
external data file.
(See https://issues.redhat.com/browse/RHEL-128573 -- specifically, the
idea is to allow incremental backups on a previously raw image.)
One reason why that currently does not work is that formatting the qcow2
image requires write access, which will automatically result in taking
the WRITE permission on the data-file child, i.e. the raw image. If
that image is currently in use and thus the WRITE permission is not
shared, the qcow2 image cannot be created. However, without data
preallocation, we don't really need to write to the data file, so this
series allows qcow2 to relax that requirement to make formatting work
while the data-file child is in use.
NOTE: One exception for needing the WRITE permission without data
preallocation is when creating a qcow2 image with a guest disk size that
is not aligned to the cluster size. Then, we will implicitly COW the
cluster tail past the end of the guest disk. I think that is
unnecessary, so commit 3 removes it. This will cause images whose guest
disk size is not aligned to full clusters have a partial data cluster at
the end, after creation. I think that’s fine, but maybe not...?
---
Side note: Another reason why it's difficult to replace a raw image with
a qcow2 image using that raw image as a raw data file is because we
don't have a comprehensive QMP command to fully replace one node by
another (there is no QMP command for bdrv_replace_node()). You can kind
of get around that via qom-set for guest devices and blockdev-reopen for
in-graph changes, but (1) there is no command for changing a block
export's node yet, (2) this is not fully transactionable, and (3) it's
cumbersome. blockdev-replace is supposed to solve (1) and can probably
also be made transactionable later to solve (2), but maybe we still want
a simple command that just calls bdrv_replace_node() for the case where
a user just wants to fully replace one node by another.
But this series does not concern itself with that problem.
Hanna Czenczek (5):
qcow2: Skip data-file resize if possible
block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
qcow2: Preallocation: Do not COW after disk end
qcow2: Suppress data-file WRITE/RESIZE if possible
iotests: Add qcow2-live-data-file test
include/block/block-common.h | 11 +
block.c | 15 +
block/qcow2.c | 96 +-
tests/qemu-iotests/125 | 17 +-
tests/qemu-iotests/tests/qcow2-live-data-file | 274 ++
.../tests/qcow2-live-data-file.out | 2904 +++++++++++++++++
6 files changed, 3302 insertions(+), 15 deletions(-)
create mode 100755 tests/qemu-iotests/tests/qcow2-live-data-file
create mode 100644 tests/qemu-iotests/tests/qcow2-live-data-file.out
--
2.52.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/5] qcow2: Skip data-file resize if possible
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
@ 2026-02-05 14:47 ` Hanna Czenczek
2026-02-05 14:47 ` [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE} Hanna Czenczek
` (3 subsequent siblings)
4 siblings, 0 replies; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
With PREALLOC_MODE_OFF, we currently always resize the data file to the
right length. This is definitely not necessary if it already has the
correct length, and if @exact is false, we also don't need to shrink it.
This is what other preallocation modes already do: preallocate_co() only
increases the data file size if it is too small, but never shrinks it.
(And note that for raw data files, PREALLOC_MODE_OFF is silently turned
into PREALLOC_MODE_METADATA, so this commit only changes behavior for
non-raw external data files.)
For the next commits, having all preallocation modes behave the same
will make it easier to decide when we can skip taking the RESIZE
permission on the data-file child.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
---
block/qcow2.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index e29810d86a..69d621e9bf 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -4515,15 +4515,22 @@ qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
switch (prealloc) {
case PREALLOC_MODE_OFF:
if (has_data_file(bs)) {
+ int64_t data_file_length = bdrv_co_getlength(s->data_file->bs);
+
/*
* If the caller wants an exact resize, the external data
* file should be resized to the exact target size, too,
* so we pass @exact here.
+ * Without @exact, we leave it as-is if it is already big enough.
+ * Implicitly handle bdrv_co_getlength() errors by resizing.
*/
- ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0,
- errp);
- if (ret < 0) {
- goto fail;
+ if (data_file_length < offset ||
+ (exact && data_file_length != offset)) {
+ ret = bdrv_co_truncate(s->data_file, offset, exact, prealloc, 0,
+ errp);
+ if (ret < 0) {
+ goto fail;
+ }
}
}
break;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
2026-02-05 14:47 ` [PATCH 1/5] qcow2: Skip data-file resize if possible Hanna Czenczek
@ 2026-02-05 14:47 ` Hanna Czenczek
2026-03-02 14:30 ` Kevin Wolf
2026-02-05 14:47 ` [PATCH 3/5] qcow2: Preallocation: Do not COW after disk end Hanna Czenczek
` (2 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
pure data (no metadata) children. These are going to be used by qcow2
during formatting, when we need write access to format the metadata
file, but no write access to an external data file. This will allow
creating a qcow2 image for a raw image while the latter is currently in
use by the VM.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
---
include/block/block-common.h | 11 +++++++++++
block.c | 15 +++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/include/block/block-common.h b/include/block/block-common.h
index c8c626daea..504f6aa113 100644
--- a/include/block/block-common.h
+++ b/include/block/block-common.h
@@ -245,6 +245,17 @@ typedef enum {
#define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
+/*
+ * Promise not to write any data to pure (non-metadata-bearing) data storage
+ * children, so we don't need the WRITE permission for them.
+ * For image creation, formatting requires write access to the image, but not
+ * necessarily to its pure storage children. This allows creating an image on
+ * top of an existing raw storage image that is already attached to the VM.
+ */
+#define BDRV_O_NO_DATA_WRITE 0x100000
+/* Same as O_NO_DATA_WRITE, but for resizing */
+#define BDRV_O_NO_DATA_RESIZE 0x200000
+
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH)
diff --git a/block.c b/block.c
index 48a17f393c..cdfed7a4d8 100644
--- a/block.c
+++ b/block.c
@@ -2887,6 +2887,21 @@ static void bdrv_default_perms_for_storage(BlockDriverState *bs, BdrvChild *c,
if (perm & BLK_PERM_WRITE) {
perm |= BLK_PERM_RESIZE;
}
+
+ if (!(role & BDRV_CHILD_METADATA)) {
+ /*
+ * For a pure data storage child (no metadata), these flags
+ * respectively promise that
+ * - nothing will be written, and/or
+ * - it will not be resized.
+ */
+ if (flags & BDRV_O_NO_DATA_WRITE) {
+ perm &= ~BLK_PERM_WRITE;
+ }
+ if (flags & BDRV_O_NO_DATA_RESIZE) {
+ perm &= ~BLK_PERM_RESIZE;
+ }
+ }
}
if (bs->open_flags & BDRV_O_INACTIVE) {
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/5] qcow2: Preallocation: Do not COW after disk end
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
2026-02-05 14:47 ` [PATCH 1/5] qcow2: Skip data-file resize if possible Hanna Czenczek
2026-02-05 14:47 ` [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE} Hanna Czenczek
@ 2026-02-05 14:47 ` Hanna Czenczek
2026-02-05 14:47 ` [PATCH 4/5] qcow2: Suppress data-file WRITE/RESIZE if possible Hanna Czenczek
2026-02-05 14:47 ` [PATCH 5/5] iotests: Add qcow2-live-data-file test Hanna Czenczek
4 siblings, 0 replies; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
When creating/resizing an image with/to a non-cluster-aligned length, we
currently implicitly COW the cluster tail after the guest disk end.
That is unnecessary, but more importantly, when creating a qcow2 image
for a pre-existing external raw data file, we should not write to it
unless data preallocation was requested.
So far, writing to the data file past the guest disk end during qcow2
image creation may just be a peculiarity. But the next commit in this
series will make qcow2 use the BDRV_O_NO_DATA_WRITE flag to suppress
taking the WRITE flag on the data file during image creation, to allow
users to create a qcow2 image with an existing raw image as its data
file that is currently in use by the VM (i.e. the WRITE flag is
unshared). To make this work, we really must not write to the data file
at all during creation, unless data preallocation (falloc/full) has been
requested.
(The comment added in this commit therefore primarily reasons with the
use of that flag, and how we must not break it. Admittedly, that only
makes sense after the next patch in this series.)
Note that this means that creating/resizing images with/to a guest disk
size that is not aligned to clusters will create a partial data cluster
at the image end when preallocating (except for falloc/full
preallocation without a data file). I think that is fine, similarly to
how creating a non-preallocated image will generally leave a partial
cluster at the end, too (the L1 table).
It does mean iotest 125 needs to be adjusted, though.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
---
block/qcow2.c | 18 ++++++++++++++++++
tests/qemu-iotests/125 | 17 +++++++++++------
2 files changed, 29 insertions(+), 6 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 69d621e9bf..b601bd540d 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3368,7 +3368,25 @@ preallocate_co(BlockDriverState *bs, uint64_t offset, uint64_t new_length,
}
for (m = meta; m != NULL; m = m->next) {
+ int64_t disk_end_ofs;
+
m->prealloc = true;
+
+ /*
+ * Do not COW beyond the supposed image end: There is no point, and
+ * it could break BDRV_O_NO_DATA_WRITE from qcow2_co_create():
+ * qcow2_alloc_host_offset() does not COW anything for the range we
+ * pass, but everything outside. If growing to a non-cluster
+ * aligned size, it will thus request COW beyond the image end,
+ * breaking the BDRV_O_NO_DATA_WRITE promise.
+ * (As long as the @offset passed to this function was aligned to
+ * full clusters, that is the only possible instance of COW. With
+ * qcow2_co_create(), it's always 0, so always aligned.)
+ */
+ disk_end_ofs = new_length - (int64_t)m->offset;
+ if (m->cow_end.offset + m->cow_end.nb_bytes > disk_end_ofs) {
+ m->cow_end.nb_bytes = MAX(disk_end_ofs - m->cow_end.offset, 0);
+ }
}
ret = qcow2_handle_l2meta(bs, &meta, true);
diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125
index 708e7c5ba2..8213de012e 100755
--- a/tests/qemu-iotests/125
+++ b/tests/qemu-iotests/125
@@ -172,10 +172,10 @@ done
# Test image resizing using preallocation and unaligned offsets
$QEMU_IMG create -f raw "$TEST_IMG.base" 128k | _filter_img_create
$QEMU_IO -c 'write -q -P 1 0 128k' -f raw "$TEST_IMG.base"
-for orig_size in 31k 33k; do
- for dst_size in 96k 128k; do
+for orig_size in $((31 * 1024)) $((33 * 1024)); do
+ for dst_size in $((96 * 1024)) $((128 * 1024)); do
for prealloc in metadata full; do
- echo "--- Resizing image from $orig_size to $dst_size (preallocation=$prealloc) ---"
+ echo "--- Resizing image from $((orig_size / 1024))k to $((dst_size / 1024))k (preallocation=$prealloc) ---"
_make_test_img -F raw -b "$TEST_IMG.base" -o cluster_size=64k "$orig_size"
$QEMU_IMG resize -f "$IMGFMT" --preallocation="$prealloc" "$TEST_IMG" "$dst_size"
# The first part of the image should contain data from the backing file
@@ -183,10 +183,15 @@ for orig_size in 31k 33k; do
# The resized part of the image should contain zeroes
$QEMU_IO -c "read -q -P 0 ${orig_size} 63k" "$TEST_IMG"
# If the image does not have an external data file we can also verify its
- # actual size. The resized image should have 7 clusters:
- # header, L1 table, L2 table, refcount table, refcount block, 2 data clusters
+ # actual size. The resized image should have 5 metadata clusters
+ # (header, L1 table, L2 table, refcount table, refcount block)
+ # plus the data. We round up that data to full clusters for full
+ # preallocation, but not for metadata preallocation.
if ! _get_data_file "$TEST_IMG" > /dev/null; then
- expected_file_length=$((65536 * 7))
+ expected_file_length=$((65536 * 5 + dst_size))
+ if [ "$prealloc" = full ]; then
+ expected_file_length=$(((expected_file_length + 65535) / 65536 * 65536))
+ fi
file_length=$(stat -c '%s' "$TEST_IMG_FILE")
if [ "$file_length" != "$expected_file_length" ]; then
echo "ERROR: file length $file_length (expected $expected_file_length)"
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/5] qcow2: Suppress data-file WRITE/RESIZE if possible
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
` (2 preceding siblings ...)
2026-02-05 14:47 ` [PATCH 3/5] qcow2: Preallocation: Do not COW after disk end Hanna Czenczek
@ 2026-02-05 14:47 ` Hanna Czenczek
2026-02-05 14:47 ` [PATCH 5/5] iotests: Add qcow2-live-data-file test Hanna Czenczek
4 siblings, 0 replies; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
When formatting a qcow2 image, we only need the WRITE permission on the
data-file child to preallocate data, so for metadata-only preallocation
(or none at all), we can suppress that permission via the
BDRV_O_NO_DATA_WRITE flag.
Similarly, we will only resize the data-file if it is currently smaller
than the supposed virtual disk size; so it is already big enough, we can
suppress the RESIZE permission via the BDRV_O_NO_DATA_RESIZE flag.
This commit allows creating a qcow2 image with an existing raw image as
its external data file while that raw image is in use by the VM.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
---
block/qcow2.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 58 insertions(+), 5 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index b601bd540d..3adc06a9ee 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3627,6 +3627,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
size_t cluster_size;
int version;
int refcount_order;
+ int blk_flags;
uint64_t *refcount_table;
int ret;
uint8_t compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
@@ -3887,20 +3888,42 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
* table)
*/
options = qdict_new();
+ blk_flags = BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH;
qdict_put_str(options, "driver", "qcow2");
qdict_put_str(options, "file", bs->node_name);
if (data_bs) {
qdict_put_str(options, "data-file", data_bs->node_name);
+
+ /*
+ * If possible, suppress the WRITE permission for the data-file child.
+ * We can only do so as long as none of the operations on `blk` will
+ * write to the data file. Such writes can only happen during resize
+ * (which grows the image from length 0 to qcow2_opts->size) with data
+ * preallocation. As long as no data preallocation has been requested,
+ * we can suppress taking the WRITE permission on data-file.
+ */
+ if (qcow2_opts->preallocation == PREALLOC_MODE_METADATA ||
+ qcow2_opts->preallocation == PREALLOC_MODE_OFF) {
+ blk_flags |= BDRV_O_NO_DATA_WRITE;
+ }
+
+ /*
+ * The data-file child is only grown if too small, never shrunk. So if
+ * it already is big enough, we can suppress taking the RESIZE
+ * permission on it.
+ */
+ if (bdrv_co_getlength(data_bs) >= qcow2_opts->size) {
+ blk_flags |= BDRV_O_NO_DATA_RESIZE;
+ }
}
- blk = blk_co_new_open(NULL, NULL, options,
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH,
- errp);
+ blk = blk_co_new_open(NULL, NULL, options, blk_flags, errp);
if (blk == NULL) {
ret = -EIO;
goto out;
}
bdrv_graph_co_rdlock();
+ /* O_NO_DATA_WRITE note: This will not write to data-file */
ret = qcow2_alloc_clusters(blk_bs(blk), 3 * cluster_size);
if (ret < 0) {
bdrv_graph_co_rdunlock();
@@ -3919,7 +3942,10 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
s->image_data_file = g_strdup(data_bs->filename);
}
- /* Create a full header (including things like feature table) */
+ /*
+ * Create a full header (including things like feature table).
+ * O_NO_DATA_WRITE note: This will not write to data-file.
+ */
ret = qcow2_update_header(blk_bs(blk));
bdrv_graph_co_rdunlock();
@@ -3928,7 +3954,13 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
goto out;
}
- /* Okay, now that we have a valid image, let's give it the right size */
+ /*
+ * Okay, now that we have a valid image, let's give it the right size.
+ * O_NO_DATA_WRITE note: This will only write to data-file if data
+ * preallocation has been requested.
+ * O_NO_DATA_RESIZE note: We pass @exact = false, so the data-file is only
+ * resized if it is smaller than qcow2_opts->size.
+ */
ret = blk_co_truncate(blk, qcow2_opts->size, false,
qcow2_opts->preallocation, 0, errp);
if (ret < 0) {
@@ -3945,6 +3977,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
}
bdrv_graph_co_rdlock();
+ /* O_NO_DATA_WRITE note: This will not write to data-file */
ret = bdrv_co_change_backing_file(blk_bs(blk), qcow2_opts->backing_file,
backing_format, false);
bdrv_graph_co_rdunlock();
@@ -3960,6 +3993,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
/* Want encryption? There you go. */
if (qcow2_opts->encrypt) {
bdrv_graph_co_rdlock();
+ /* O_NO_DATA_WRITE note: This will not write to data-file */
ret = qcow2_set_up_encryption(blk_bs(blk), qcow2_opts->encrypt, errp);
bdrv_graph_co_rdunlock();
@@ -4401,6 +4435,15 @@ fail:
return ret;
}
+/**
+ * Resize the qcow2 image.
+ * To support BDRV_O_NO_DATA_WRITE and BDRV_O_NO_DATA_RESIZE from
+ * qcow2_co_create(), this function must:
+ * - If @exact is false, resize an external data file only if its size is less
+ * than @offset
+ * - Only write to an external data file if @prealloc prescribes data
+ * preallocation (FALLOC/FULL).
+ */
static int coroutine_fn GRAPH_RDLOCK
qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
PreallocMode prealloc, BdrvRequestFlags flags, Error **errp)
@@ -4554,6 +4597,11 @@ qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
break;
case PREALLOC_MODE_METADATA:
+ /*
+ * Note for BDRV_O_NO_DATA_RESIZE and BDRV_O_NO_DATA_WRITE: This will
+ * not write data to an external data file, and will only resize it if
+ * its current length is less than `offset`.
+ */
ret = preallocate_co(bs, old_length, offset, prealloc, errp);
if (ret < 0) {
goto fail;
@@ -4572,6 +4620,11 @@ qcow2_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
/* With a data file, preallocation means just allocating the metadata
* and forwarding the truncate request to the data file */
if (has_data_file(bs)) {
+ /*
+ * Note for BDRV_O_NO_DATA_RESIZE and BDRV_O_NO_DATA_WRITE: This
+ * *will* write data to an external data file, but only resize it
+ * if its current length is less than `offset`.
+ */
ret = preallocate_co(bs, old_length, offset, prealloc, errp);
if (ret < 0) {
goto fail;
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 5/5] iotests: Add qcow2-live-data-file test
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
` (3 preceding siblings ...)
2026-02-05 14:47 ` [PATCH 4/5] qcow2: Suppress data-file WRITE/RESIZE if possible Hanna Czenczek
@ 2026-02-05 14:47 ` Hanna Czenczek
4 siblings, 0 replies; 13+ messages in thread
From: Hanna Czenczek @ 2026-02-05 14:47 UTC (permalink / raw)
To: qemu-block; +Cc: qemu-devel, Hanna Czenczek, Kevin Wolf
While a raw image is attached to a VM, create a pure-metadata qcow2
image with that raw image as its data file, and then replace the raw
image by the qcow2 image.
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
---
tests/qemu-iotests/tests/qcow2-live-data-file | 274 ++
.../tests/qcow2-live-data-file.out | 2904 +++++++++++++++++
2 files changed, 3178 insertions(+)
create mode 100755 tests/qemu-iotests/tests/qcow2-live-data-file
create mode 100644 tests/qemu-iotests/tests/qcow2-live-data-file.out
diff --git a/tests/qemu-iotests/tests/qcow2-live-data-file b/tests/qemu-iotests/tests/qcow2-live-data-file
new file mode 100755
index 0000000000..cdff1ada3d
--- /dev/null
+++ b/tests/qemu-iotests/tests/qcow2-live-data-file
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+# group: rw
+#
+# Given a raw image file already attached to a VM, create a qcow2 file with
+# that image as its external (raw) data file, and replace the raw image by
+# the qcow2 image.
+#
+# Copyright (C) 2026 Red Hat, Inc.
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Creator/Owner: Hanna Czenczek <hreitz@redhat.com>
+
+from enum import Enum, auto
+import re
+import iotests
+from iotests import log, qemu_img, qemu_img_log, qemu_io_log
+
+iotests.script_initialize(
+ supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat'],
+)
+
+DISK_SIZE = 64 * 1024 * 1024
+
+class CreateException(Exception):
+ pass
+
+class GuestDiskType(Enum):
+ VIRTIO_BLK = auto()
+ SCSI_HD = auto()
+ SCSI_CD = auto()
+
+def do_test(
+ vm: iotests.VM,
+ with_raw_node: bool,
+ guest_disk_type: GuestDiskType,
+ preallocation: str,
+ qcow2_size: int,
+ raw_img_path: str,
+ qcow2_img_path: str,
+) -> None:
+ log('=== do_test() ===')
+ log(f' - with_raw_node={with_raw_node}')
+ log(f' - guest_disk_type={guest_disk_type}')
+ log(f' - preallocation={preallocation}')
+ log(f' - qcow2_size={qcow2_size}')
+
+ log('')
+ log('--- Setting up raw image ---')
+ qemu_img('create', '-f', 'raw', raw_img_path, str(DISK_SIZE))
+
+ log('')
+ log('--- Launching VM ---')
+
+ vm.add_blockdev(vm.qmp_to_opts({
+ 'driver': 'file',
+ 'node-name': 'protocol',
+ 'filename': raw_img_path,
+ }))
+
+ if with_raw_node:
+ vm.add_blockdev(vm.qmp_to_opts({
+ 'driver': 'raw',
+ 'node-name': 'raw',
+ 'file': 'protocol',
+ }))
+ raw_node = 'raw'
+ else:
+ raw_node = 'protocol'
+
+ dev_id = 'sda'
+ qom_path = f'/machine/peripheral/{dev_id}'
+ if guest_disk_type == GuestDiskType.VIRTIO_BLK:
+ vm.add_device(f'virtio-blk,id={dev_id},drive={raw_node}')
+ dev_id = f'{qom_path}/virtio-backend'
+ elif guest_disk_type == GuestDiskType.SCSI_HD:
+ vm.add_device('virtio-scsi')
+ vm.add_device(f'scsi-hd,id={dev_id},drive={raw_node}')
+ elif guest_disk_type == GuestDiskType.SCSI_CD:
+ vm.add_device('virtio-scsi')
+ vm.add_device(f'scsi-cd,id={dev_id},drive={raw_node}')
+
+ vm.launch()
+
+ log('')
+ log('--- Creating qcow2 image ---')
+
+ create_and_open(vm, {
+ 'driver': 'file',
+ 'filename': qcow2_img_path,
+ }, {
+ 'size': 0,
+ }, {
+ 'node-name': 'qcow2-protocol',
+ })
+
+ create_and_open(vm, {
+ 'driver': 'qcow2',
+ 'file': 'qcow2-protocol',
+ 'data-file': 'protocol',
+ }, {
+ 'data-file-raw': True,
+ 'size': qcow2_size,
+ 'preallocation': preallocation,
+ }, {
+ 'node-name': 'qcow2',
+ })
+
+ cmd = 'aio_write -P 42 0 4M'
+ log(f'[HMP qemu-io: {cmd}]')
+ log(vm.qmp(
+ 'human-monitor-command',
+ command_line=f'qemu-io -d {dev_id} "{cmd}"',
+ ))
+
+ if with_raw_node:
+ log('[blockdev-reopen]')
+ log(vm.qmp('blockdev-reopen', {
+ 'options': [{
+ 'driver': 'raw',
+ 'node-name': 'raw',
+ 'file': 'qcow2',
+ }],
+ }))
+ else:
+ log('[qom-set]')
+ log(vm.qmp('qom-set', {
+ 'path': qom_path,
+ 'property': 'drive',
+ 'value': 'qcow2',
+ }))
+
+ cmd = 'aio_flush'
+ log(f'[HMP qemu-io: {cmd}]')
+ log(vm.qmp(
+ 'human-monitor-command',
+ command_line=f'qemu-io -d {dev_id} {cmd}',
+ ))
+
+ vm.shutdown()
+
+ qlog = vm.get_log()
+ if qlog is not None:
+ log('[qemu log]')
+ qlog = iotests.filter_qemu_io(iotests.filter_qtest(qlog))
+ log(qlog)
+
+ log('[qemu-img check]')
+ qemu_img_log('check', '-f', 'qcow2', qcow2_img_path)
+
+ cmd = 'read -P 42 0 4M'
+ log(f'[qemu-io: {cmd}]')
+ qemu_io_log(qcow2_img_path, '-c', cmd)
+
+def create_and_open(
+ vm: iotests.VM,
+ base_options: dict[str, object],
+ create_options: dict[str, object],
+ open_options: dict[str, object],
+) -> None:
+ log(f'[blockdev-create: {base_options['driver']}]')
+ log(vm.qmp('blockdev-create', {
+ 'job-id': 'create',
+ 'options': base_options | create_options,
+ }))
+
+ while True:
+ event = vm.event_wait(
+ name='JOB_STATUS_CHANGE',
+ match={'data': {'id': 'create'}},
+ )
+ assert event is not None
+ status = event['data']['status']
+ log(f' -> {status}')
+ if status == 'aborting':
+ jobs = vm.qmp('query-jobs')['return']
+ job_info = next(job for job in jobs if job['id'] == 'create')
+ error = re.sub(r'#block\d+', '#blockXXX', job_info['error'])
+ log(f'blockdev-create failed: {error}')
+ raise CreateException(error)
+ if status == 'concluded':
+ break
+
+ log('[job-dismiss]')
+ log(vm.qmp('job-dismiss', id='create'))
+ log('[blockdev-add]')
+ log(vm.qmp('blockdev-add', base_options | open_options))
+
+def verify_exception(
+ exception: CreateException,
+ conflicting_permissions: list[str],
+ with_raw_node: bool,
+ guest_disk_type: GuestDiskType,
+) -> None:
+ if with_raw_node:
+ user = "node 'raw'"
+ user_child = 'file'
+ elif guest_disk_type == GuestDiskType.VIRTIO_BLK:
+ user = "block device '/machine/peripheral/sda/virtio-backend'"
+ user_child = 'root'
+ else:
+ user = "block device 'sda'"
+ user_child = 'root'
+
+ refstr = (
+ "Permission conflict on node 'protocol': permissions "
+ f"'{', '.join(conflicting_permissions)}' are both required by node "
+ "'#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared "
+ f"by {user} (uses node 'protocol' as '{user_child}' child)."
+ )
+
+ if str(exception) != refstr:
+ log("Exception differs from reference:")
+ log(f'Is: {exception}')
+ log(f'Expected: {refstr}')
+ assert str(exception) == refstr
+
+def run_test(
+ with_raw_node: bool,
+ guest_disk_type: GuestDiskType,
+ preallocation: str,
+ qcow2_size: int,
+) -> None:
+ with iotests.FilePath('raw.img') as raw_img_path, \
+ iotests.FilePath('metadata.qcow2') as qcow2_img_path, \
+ iotests.VM() as vm:
+ try:
+ do_test(
+ vm,
+ with_raw_node,
+ guest_disk_type,
+ preallocation,
+ qcow2_size,
+ raw_img_path,
+ qcow2_img_path,
+ )
+ except CreateException as e:
+ expected_conflicting_perms = []
+
+ if preallocation in ('falloc', 'full'):
+ # Data preallocation must fail because it must write to the
+ # data file, requiring the WRITE permission.
+ expected_conflicting_perms += ['write']
+
+ if guest_disk_type == GuestDiskType.SCSI_CD and \
+ qcow2_size > DISK_SIZE:
+ # CD does not allow resizing, but creating a bigger qcow2 image
+ # would require growing the raw image, necessitating the RESIZE
+ # permission. (Creating a smaller qcow2 however will not
+ # shrink it.)
+ expected_conflicting_perms += ['resize']
+
+ if not expected_conflicting_perms:
+ raise e
+
+ verify_exception(e, expected_conflicting_perms,
+ with_raw_node, guest_disk_type)
+ log('(Handled expected exception)')
+ log('')
+
+
+def run_all_test_combinations():
+ for with_raw_node in (False, True):
+ for guest_disk_type in GuestDiskType:
+ for preallocation in ('off', 'metadata', 'falloc', 'full'):
+ # Sprinkle in -512 and +512 to verify that non-cluster-aligned
+ # sizes work, too
+ for qcow2_size in (DISK_SIZE, DISK_SIZE // 2 - 512,
+ DISK_SIZE * 2 + 512):
+ run_test(with_raw_node, guest_disk_type, preallocation,
+ qcow2_size)
+
+run_all_test_combinations()
diff --git a/tests/qemu-iotests/tests/qcow2-live-data-file.out b/tests/qemu-iotests/tests/qcow2-live-data-file.out
new file mode 100644
index 0000000000..0adb307756
--- /dev/null
+++ b/tests/qemu-iotests/tests/qcow2-live-data-file.out
@@ -0,0 +1,2904 @@
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device '/machine/peripheral/sda/virtio-backend' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[qom-set]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write, resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=False
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write, resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by block device 'sda' (uses node 'protocol' as 'root' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.VIRTIO_BLK
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+2049/2049 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_HD
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=off
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+1024/1024 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[HMP qemu-io: aio_write -P 42 0 4M]
+{"return": ""}
+[blockdev-reopen]
+{"return": {}}
+[HMP qemu-io: aio_flush]
+{"return": ""}
+[qemu log]
+wrote 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+[qemu-img check]
+No errors were found on the image.
+512/512 = 100.00% allocated, 0.00% fragmented, 0.00% compressed clusters
+Image end offset: 327680
+
+[qemu-io: read -P 42 0 4M]
+read 4194304/4194304 bytes at offset 0
+4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=metadata
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=falloc
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write, resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=67108864
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=33553920
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
+=== do_test() ===
+ - with_raw_node=True
+ - guest_disk_type=GuestDiskType.SCSI_CD
+ - preallocation=full
+ - qcow2_size=134218240
+
+--- Setting up raw image ---
+
+--- Launching VM ---
+
+--- Creating qcow2 image ---
+[blockdev-create: file]
+{"return": {}}
+ -> created
+ -> running
+ -> waiting
+ -> pending
+ -> concluded
+[job-dismiss]
+{"return": {}}
+[blockdev-add]
+{"return": {}}
+[blockdev-create: qcow2]
+{"return": {}}
+ -> null
+ -> created
+ -> running
+ -> aborting
+blockdev-create failed: Permission conflict on node 'protocol': permissions 'write, resize' are both required by node '#blockXXX' (uses node 'protocol' as 'data-file' child) and unshared by node 'raw' (uses node 'protocol' as 'file' child).
+(Handled expected exception)
+
--
2.52.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-02-05 14:47 ` [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE} Hanna Czenczek
@ 2026-03-02 14:30 ` Kevin Wolf
2026-03-04 14:20 ` Hanna Czenczek
0 siblings, 1 reply; 13+ messages in thread
From: Kevin Wolf @ 2026-03-02 14:30 UTC (permalink / raw)
To: Hanna Czenczek; +Cc: qemu-block, qemu-devel
Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
> Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
> pure data (no metadata) children. These are going to be used by qcow2
> during formatting, when we need write access to format the metadata
> file, but no write access to an external data file. This will allow
> creating a qcow2 image for a raw image while the latter is currently in
> use by the VM.
>
> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> ---
> include/block/block-common.h | 11 +++++++++++
> block.c | 15 +++++++++++++++
> 2 files changed, 26 insertions(+)
>
> diff --git a/include/block/block-common.h b/include/block/block-common.h
> index c8c626daea..504f6aa113 100644
> --- a/include/block/block-common.h
> +++ b/include/block/block-common.h
> @@ -245,6 +245,17 @@ typedef enum {
>
> #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
>
> +/*
> + * Promise not to write any data to pure (non-metadata-bearing) data storage
> + * children, so we don't need the WRITE permission for them.
> + * For image creation, formatting requires write access to the image, but not
> + * necessarily to its pure storage children. This allows creating an image on
> + * top of an existing raw storage image that is already attached to the VM.
> + */
> +#define BDRV_O_NO_DATA_WRITE 0x100000
Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
doesn't allow reading either, but I don't think image creation ever
requires reading from the image?
> +/* Same as O_NO_DATA_WRITE, but for resizing */
> +#define BDRV_O_NO_DATA_RESIZE 0x200000
How does this differ from BDRV_O_RESIZE (apart from being negative)?
Doesn't resizing always refer to the data part?
Kevin
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-02 14:30 ` Kevin Wolf
@ 2026-03-04 14:20 ` Hanna Czenczek
2026-03-04 16:13 ` Kevin Wolf
0 siblings, 1 reply; 13+ messages in thread
From: Hanna Czenczek @ 2026-03-04 14:20 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-block, qemu-devel
On 02.03.26 15:30, Kevin Wolf wrote:
> Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
>> Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
>> pure data (no metadata) children. These are going to be used by qcow2
>> during formatting, when we need write access to format the metadata
>> file, but no write access to an external data file. This will allow
>> creating a qcow2 image for a raw image while the latter is currently in
>> use by the VM.
>>
>> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
>> ---
>> include/block/block-common.h | 11 +++++++++++
>> block.c | 15 +++++++++++++++
>> 2 files changed, 26 insertions(+)
>>
>> diff --git a/include/block/block-common.h b/include/block/block-common.h
>> index c8c626daea..504f6aa113 100644
>> --- a/include/block/block-common.h
>> +++ b/include/block/block-common.h
>> @@ -245,6 +245,17 @@ typedef enum {
>>
>> #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
>>
>> +/*
>> + * Promise not to write any data to pure (non-metadata-bearing) data storage
>> + * children, so we don't need the WRITE permission for them.
>> + * For image creation, formatting requires write access to the image, but not
>> + * necessarily to its pure storage children. This allows creating an image on
>> + * top of an existing raw storage image that is already attached to the VM.
>> + */
>> +#define BDRV_O_NO_DATA_WRITE 0x100000
> Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
> doesn't allow reading either, but I don't think image creation ever
> requires reading from the image?
How would qcow2 set it? It opens the qcow2 image, so it can only set
the flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs
to go on the data-file child. Maybe we can construct the graph
manually…? It would be quite painful, I imagine, but I haven’t tried yet.
Hanna
>
>> +/* Same as O_NO_DATA_WRITE, but for resizing */
>> +#define BDRV_O_NO_DATA_RESIZE 0x200000
> How does this differ from BDRV_O_RESIZE (apart from being negative)?
> Doesn't resizing always refer to the data part?
>
> Kevin
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-04 14:20 ` Hanna Czenczek
@ 2026-03-04 16:13 ` Kevin Wolf
2026-03-06 10:20 ` Hanna Czenczek
0 siblings, 1 reply; 13+ messages in thread
From: Kevin Wolf @ 2026-03-04 16:13 UTC (permalink / raw)
To: Hanna Czenczek; +Cc: qemu-block, qemu-devel
Am 04.03.2026 um 15:20 hat Hanna Czenczek geschrieben:
> On 02.03.26 15:30, Kevin Wolf wrote:
> > Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
> > > Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
> > > pure data (no metadata) children. These are going to be used by qcow2
> > > during formatting, when we need write access to format the metadata
> > > file, but no write access to an external data file. This will allow
> > > creating a qcow2 image for a raw image while the latter is currently in
> > > use by the VM.
> > >
> > > Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> > > ---
> > > include/block/block-common.h | 11 +++++++++++
> > > block.c | 15 +++++++++++++++
> > > 2 files changed, 26 insertions(+)
> > >
> > > diff --git a/include/block/block-common.h b/include/block/block-common.h
> > > index c8c626daea..504f6aa113 100644
> > > --- a/include/block/block-common.h
> > > +++ b/include/block/block-common.h
> > > @@ -245,6 +245,17 @@ typedef enum {
> > > #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
> > > +/*
> > > + * Promise not to write any data to pure (non-metadata-bearing) data storage
> > > + * children, so we don't need the WRITE permission for them.
> > > + * For image creation, formatting requires write access to the image, but not
> > > + * necessarily to its pure storage children. This allows creating an image on
> > > + * top of an existing raw storage image that is already attached to the VM.
> > > + */
> > > +#define BDRV_O_NO_DATA_WRITE 0x100000
> > Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
> > doesn't allow reading either, but I don't think image creation ever
> > requires reading from the image?
>
> How would qcow2 set it? It opens the qcow2 image, so it can only set the
> flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs to go on
> the data-file child. Maybe we can construct the graph manually…? It would
> be quite painful, I imagine, but I haven’t tried yet.
Why should it go on the data-file child? The child permissions are
defined by the qcow2 node. If the caller promises not to do any I/O (and
I'm fairly sure that apart from preallocation, creating the image
doesn't involve any I/O on the qcow2 node, just on the primary child),
then qcow2 doesn't need any permissions on data-file.
Or am I missing a reason why BDRV_O_NO_IO can't be set for the qcow2
node?
Kevin
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-04 16:13 ` Kevin Wolf
@ 2026-03-06 10:20 ` Hanna Czenczek
2026-03-06 11:12 ` Kevin Wolf
0 siblings, 1 reply; 13+ messages in thread
From: Hanna Czenczek @ 2026-03-06 10:20 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-block, qemu-devel
On 04.03.26 17:13, Kevin Wolf wrote:
> Am 04.03.2026 um 15:20 hat Hanna Czenczek geschrieben:
>> On 02.03.26 15:30, Kevin Wolf wrote:
>>> Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
>>>> Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
>>>> pure data (no metadata) children. These are going to be used by qcow2
>>>> during formatting, when we need write access to format the metadata
>>>> file, but no write access to an external data file. This will allow
>>>> creating a qcow2 image for a raw image while the latter is currently in
>>>> use by the VM.
>>>>
>>>> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
>>>> ---
>>>> include/block/block-common.h | 11 +++++++++++
>>>> block.c | 15 +++++++++++++++
>>>> 2 files changed, 26 insertions(+)
>>>>
>>>> diff --git a/include/block/block-common.h b/include/block/block-common.h
>>>> index c8c626daea..504f6aa113 100644
>>>> --- a/include/block/block-common.h
>>>> +++ b/include/block/block-common.h
>>>> @@ -245,6 +245,17 @@ typedef enum {
>>>> #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
>>>> +/*
>>>> + * Promise not to write any data to pure (non-metadata-bearing) data storage
>>>> + * children, so we don't need the WRITE permission for them.
>>>> + * For image creation, formatting requires write access to the image, but not
>>>> + * necessarily to its pure storage children. This allows creating an image on
>>>> + * top of an existing raw storage image that is already attached to the VM.
>>>> + */
>>>> +#define BDRV_O_NO_DATA_WRITE 0x100000
>>> Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
>>> doesn't allow reading either, but I don't think image creation ever
>>> requires reading from the image?
>> How would qcow2 set it? It opens the qcow2 image, so it can only set the
>> flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs to go on
>> the data-file child. Maybe we can construct the graph manually…? It would
>> be quite painful, I imagine, but I haven’t tried yet.
> Why should it go on the data-file child? The child permissions are
> defined by the qcow2 node. If the caller promises not to do any I/O (and
> I'm fairly sure that apart from preallocation, creating the image
> doesn't involve any I/O on the qcow2 node, just on the primary child),
> then qcow2 doesn't need any permissions on data-file.
>
> Or am I missing a reason why BDRV_O_NO_IO can't be set for the qcow2
> node?
First, bdrv_co_write_req_prepare() requires !BDRV_O_NO_IO and the RESIZE
permission. We could bypass this by calling qcow2_co_truncate() instead
of blk_co_truncate(). Feels wrong to me to pass BDRV_O_NO_IO and
!BDRV_O_RESIZE to blk_co_new_open() when we actually kind of want those
things and just bypass the BB to get them, but only morally wrong. Not
technically wrong.
Bigger problem: BDRV_O_NO_IO makes qcow2 skip opening the data-file
altogether. So we would need to distinguish between qemu-img info and
this case somehow.
(I also thought maybe the bdrv_co_truncate() in qcow2_co_truncate()
might be a problem with data-file inheriting BDRV_O_NO_IO, but because
qcow2_co_create() puts in a pre-opened BDS (without the flag set), that
part works.)
Hanna
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-06 10:20 ` Hanna Czenczek
@ 2026-03-06 11:12 ` Kevin Wolf
2026-03-06 11:28 ` Hanna Czenczek
0 siblings, 1 reply; 13+ messages in thread
From: Kevin Wolf @ 2026-03-06 11:12 UTC (permalink / raw)
To: Hanna Czenczek; +Cc: qemu-block, qemu-devel
Am 06.03.2026 um 11:20 hat Hanna Czenczek geschrieben:
> On 04.03.26 17:13, Kevin Wolf wrote:
> > Am 04.03.2026 um 15:20 hat Hanna Czenczek geschrieben:
> > > On 02.03.26 15:30, Kevin Wolf wrote:
> > > > Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
> > > > > Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
> > > > > pure data (no metadata) children. These are going to be used by qcow2
> > > > > during formatting, when we need write access to format the metadata
> > > > > file, but no write access to an external data file. This will allow
> > > > > creating a qcow2 image for a raw image while the latter is currently in
> > > > > use by the VM.
> > > > >
> > > > > Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> > > > > ---
> > > > > include/block/block-common.h | 11 +++++++++++
> > > > > block.c | 15 +++++++++++++++
> > > > > 2 files changed, 26 insertions(+)
> > > > >
> > > > > diff --git a/include/block/block-common.h b/include/block/block-common.h
> > > > > index c8c626daea..504f6aa113 100644
> > > > > --- a/include/block/block-common.h
> > > > > +++ b/include/block/block-common.h
> > > > > @@ -245,6 +245,17 @@ typedef enum {
> > > > > #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
> > > > > +/*
> > > > > + * Promise not to write any data to pure (non-metadata-bearing) data storage
> > > > > + * children, so we don't need the WRITE permission for them.
> > > > > + * For image creation, formatting requires write access to the image, but not
> > > > > + * necessarily to its pure storage children. This allows creating an image on
> > > > > + * top of an existing raw storage image that is already attached to the VM.
> > > > > + */
> > > > > +#define BDRV_O_NO_DATA_WRITE 0x100000
> > > > Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
> > > > doesn't allow reading either, but I don't think image creation ever
> > > > requires reading from the image?
> > > How would qcow2 set it? It opens the qcow2 image, so it can only set the
> > > flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs to go on
> > > the data-file child. Maybe we can construct the graph manually…? It would
> > > be quite painful, I imagine, but I haven’t tried yet.
> > Why should it go on the data-file child? The child permissions are
> > defined by the qcow2 node. If the caller promises not to do any I/O (and
> > I'm fairly sure that apart from preallocation, creating the image
> > doesn't involve any I/O on the qcow2 node, just on the primary child),
> > then qcow2 doesn't need any permissions on data-file.
> >
> > Or am I missing a reason why BDRV_O_NO_IO can't be set for the qcow2
> > node?
>
> First, bdrv_co_write_req_prepare() requires !BDRV_O_NO_IO and the RESIZE
> permission.
Ah, I wasn't aware that truncate requires !BDRV_O_NO_IO. It's not what I
would intuitively call I/O, but it's also justifiable because it changes
the result of reading some blocks.
The part where things start to feel questionable is with the way
qcow2_co_create() uses truncate. It doesn't actually want to truncate
the image (especially with an existing data file), but just allocate the
metadata for the full image size.
If the problem is just bdrv_co_write_req_prepare(), would a request flag
for the truncate solve it without having to introduce two global BDS
flags that can never be set by the user? BDRV_REQ_NO_DATA_IO or
something?
> We could bypass this by calling qcow2_co_truncate() instead of
> blk_co_truncate(). Feels wrong to me to pass BDRV_O_NO_IO and
> !BDRV_O_RESIZE to blk_co_new_open() when we actually kind of want those
> things and just bypass the BB to get them, but only morally wrong. Not
> technically wrong.
>
> Bigger problem: BDRV_O_NO_IO makes qcow2 skip opening the data-file
> altogether. So we would need to distinguish between qemu-img info and
> this case somehow.
Do we need to have it opened, except for preallocation, which can't set
BDRV_O_NO_IO anyway?
> (I also thought maybe the bdrv_co_truncate() in qcow2_co_truncate() might be
> a problem with data-file inheriting BDRV_O_NO_IO, but because
> qcow2_co_create() puts in a pre-opened BDS (without the flag set), that part
> works.)
We should probably skip truncating the data file for BDRV_REQ_NO_DATA_IO
and we want to make the image the same size as the data file anyway, no?
I don't think we have the intention to actually resize the data file, do
we?
Kevin
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-06 11:12 ` Kevin Wolf
@ 2026-03-06 11:28 ` Hanna Czenczek
2026-03-06 13:04 ` Kevin Wolf
0 siblings, 1 reply; 13+ messages in thread
From: Hanna Czenczek @ 2026-03-06 11:28 UTC (permalink / raw)
To: Kevin Wolf; +Cc: qemu-block, qemu-devel
On 06.03.26 12:12, Kevin Wolf wrote:
> Am 06.03.2026 um 11:20 hat Hanna Czenczek geschrieben:
>> On 04.03.26 17:13, Kevin Wolf wrote:
>>> Am 04.03.2026 um 15:20 hat Hanna Czenczek geschrieben:
>>>> On 02.03.26 15:30, Kevin Wolf wrote:
>>>>> Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
>>>>>> Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
>>>>>> pure data (no metadata) children. These are going to be used by qcow2
>>>>>> during formatting, when we need write access to format the metadata
>>>>>> file, but no write access to an external data file. This will allow
>>>>>> creating a qcow2 image for a raw image while the latter is currently in
>>>>>> use by the VM.
>>>>>>
>>>>>> Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
>>>>>> ---
>>>>>> include/block/block-common.h | 11 +++++++++++
>>>>>> block.c | 15 +++++++++++++++
>>>>>> 2 files changed, 26 insertions(+)
>>>>>>
>>>>>> diff --git a/include/block/block-common.h b/include/block/block-common.h
>>>>>> index c8c626daea..504f6aa113 100644
>>>>>> --- a/include/block/block-common.h
>>>>>> +++ b/include/block/block-common.h
>>>>>> @@ -245,6 +245,17 @@ typedef enum {
>>>>>> #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
>>>>>> +/*
>>>>>> + * Promise not to write any data to pure (non-metadata-bearing) data storage
>>>>>> + * children, so we don't need the WRITE permission for them.
>>>>>> + * For image creation, formatting requires write access to the image, but not
>>>>>> + * necessarily to its pure storage children. This allows creating an image on
>>>>>> + * top of an existing raw storage image that is already attached to the VM.
>>>>>> + */
>>>>>> +#define BDRV_O_NO_DATA_WRITE 0x100000
>>>>> Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
>>>>> doesn't allow reading either, but I don't think image creation ever
>>>>> requires reading from the image?
>>>> How would qcow2 set it? It opens the qcow2 image, so it can only set the
>>>> flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs to go on
>>>> the data-file child. Maybe we can construct the graph manually…? It would
>>>> be quite painful, I imagine, but I haven’t tried yet.
>>> Why should it go on the data-file child? The child permissions are
>>> defined by the qcow2 node. If the caller promises not to do any I/O (and
>>> I'm fairly sure that apart from preallocation, creating the image
>>> doesn't involve any I/O on the qcow2 node, just on the primary child),
>>> then qcow2 doesn't need any permissions on data-file.
>>>
>>> Or am I missing a reason why BDRV_O_NO_IO can't be set for the qcow2
>>> node?
>> First, bdrv_co_write_req_prepare() requires !BDRV_O_NO_IO and the RESIZE
>> permission.
> Ah, I wasn't aware that truncate requires !BDRV_O_NO_IO. It's not what I
> would intuitively call I/O, but it's also justifiable because it changes
> the result of reading some blocks.
>
> The part where things start to feel questionable is with the way
> qcow2_co_create() uses truncate. It doesn't actually want to truncate
> the image (especially with an existing data file), but just allocate the
> metadata for the full image size.
>
> If the problem is just bdrv_co_write_req_prepare(), would a request flag
> for the truncate solve it without having to introduce two global BDS
> flags that can never be set by the user? BDRV_REQ_NO_DATA_IO or
> something?
Oh, only one new flag. We can drop the RESIZE flag, as you said, and
make it !BDRV_O_RESIZE. (If we call qcow2_co_truncate() directly, that is.)
Dropping that flag changes the error messages sometimes (because without
this flag, WRITE will always imply RESIZE, so with a guest device that
prevents concurrent resize, you will then always get RESIZE conflicts
alongside WRITE), but that’s OK (because it’s only when you get a WRITE
conflict anyway).
>> We could bypass this by calling qcow2_co_truncate() instead of
>> blk_co_truncate(). Feels wrong to me to pass BDRV_O_NO_IO and
>> !BDRV_O_RESIZE to blk_co_new_open() when we actually kind of want those
>> things and just bypass the BB to get them, but only morally wrong. Not
>> technically wrong.
>>
>> Bigger problem: BDRV_O_NO_IO makes qcow2 skip opening the data-file
>> altogether. So we would need to distinguish between qemu-img info and
>> this case somehow.
> Do we need to have it opened, except for preallocation, which can't set
> BDRV_O_NO_IO anyway?
Yes, for resizing it without preallocation (growing to fit).
We could have qcow2_do_open() distinguish by checking whether the
"data-file" option in the QDict is set and a string (a node-name),
because if it is, it’s safe to take that existing BDS despite BDRV_O_NO_IO.
I’m still not sure how I morally feel about passing BDRV_O_NO_IO when we
do want to do I/O. Yes, only metadata. I know. But I understand
BDRV_O_NO_IO was introduced specifically for nothing at all, just
querying image information. And it works, yes – you just have to make
sure you keep BDRV_O_RDWR in there, too, because otherwise the whole
thing will be read-only and not work. Feels really wrong to me, but I
guess we already do the same thing at the end of qcow2_co_create() to
flush the image, so… Too late to protest now, I suppose.
Hanna
>> (I also thought maybe the bdrv_co_truncate() in qcow2_co_truncate() might be
>> a problem with data-file inheriting BDRV_O_NO_IO, but because
>> qcow2_co_create() puts in a pre-opened BDS (without the flag set), that part
>> works.)
> We should probably skip truncating the data file for BDRV_REQ_NO_DATA_IO
> and we want to make the image the same size as the data file anyway, no?
> I don't think we have the intention to actually resize the data file, do
> we?
>
> Kevin
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE}
2026-03-06 11:28 ` Hanna Czenczek
@ 2026-03-06 13:04 ` Kevin Wolf
0 siblings, 0 replies; 13+ messages in thread
From: Kevin Wolf @ 2026-03-06 13:04 UTC (permalink / raw)
To: Hanna Czenczek; +Cc: qemu-block, qemu-devel
Am 06.03.2026 um 12:28 hat Hanna Czenczek geschrieben:
> On 06.03.26 12:12, Kevin Wolf wrote:
> > Am 06.03.2026 um 11:20 hat Hanna Czenczek geschrieben:
> > > On 04.03.26 17:13, Kevin Wolf wrote:
> > > > Am 04.03.2026 um 15:20 hat Hanna Czenczek geschrieben:
> > > > > On 02.03.26 15:30, Kevin Wolf wrote:
> > > > > > Am 05.02.2026 um 15:47 hat Hanna Czenczek geschrieben:
> > > > > > > Add BDS flags that prevent taking WRITE and/or RESIZE permissions on
> > > > > > > pure data (no metadata) children. These are going to be used by qcow2
> > > > > > > during formatting, when we need write access to format the metadata
> > > > > > > file, but no write access to an external data file. This will allow
> > > > > > > creating a qcow2 image for a raw image while the latter is currently in
> > > > > > > use by the VM.
> > > > > > >
> > > > > > > Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
> > > > > > > ---
> > > > > > > include/block/block-common.h | 11 +++++++++++
> > > > > > > block.c | 15 +++++++++++++++
> > > > > > > 2 files changed, 26 insertions(+)
> > > > > > >
> > > > > > > diff --git a/include/block/block-common.h b/include/block/block-common.h
> > > > > > > index c8c626daea..504f6aa113 100644
> > > > > > > --- a/include/block/block-common.h
> > > > > > > +++ b/include/block/block-common.h
> > > > > > > @@ -245,6 +245,17 @@ typedef enum {
> > > > > > > #define BDRV_O_CBW_DISCARD_SOURCE 0x80000 /* for copy-before-write filter */
> > > > > > > +/*
> > > > > > > + * Promise not to write any data to pure (non-metadata-bearing) data storage
> > > > > > > + * children, so we don't need the WRITE permission for them.
> > > > > > > + * For image creation, formatting requires write access to the image, but not
> > > > > > > + * necessarily to its pure storage children. This allows creating an image on
> > > > > > > + * top of an existing raw storage image that is already attached to the VM.
> > > > > > > + */
> > > > > > > +#define BDRV_O_NO_DATA_WRITE 0x100000
> > > > > > Can't we just use BDRV_O_NO_IO for this one? It is stricter because it
> > > > > > doesn't allow reading either, but I don't think image creation ever
> > > > > > requires reading from the image?
> > > > > How would qcow2 set it? It opens the qcow2 image, so it can only set the
> > > > > flag on the qcow2 BDS (via a BlockBackend), but BDRV_O_NO_IO needs to go on
> > > > > the data-file child. Maybe we can construct the graph manually…? It would
> > > > > be quite painful, I imagine, but I haven’t tried yet.
> > > > Why should it go on the data-file child? The child permissions are
> > > > defined by the qcow2 node. If the caller promises not to do any I/O (and
> > > > I'm fairly sure that apart from preallocation, creating the image
> > > > doesn't involve any I/O on the qcow2 node, just on the primary child),
> > > > then qcow2 doesn't need any permissions on data-file.
> > > >
> > > > Or am I missing a reason why BDRV_O_NO_IO can't be set for the qcow2
> > > > node?
> > > First, bdrv_co_write_req_prepare() requires !BDRV_O_NO_IO and the RESIZE
> > > permission.
> > Ah, I wasn't aware that truncate requires !BDRV_O_NO_IO. It's not what I
> > would intuitively call I/O, but it's also justifiable because it changes
> > the result of reading some blocks.
> >
> > The part where things start to feel questionable is with the way
> > qcow2_co_create() uses truncate. It doesn't actually want to truncate
> > the image (especially with an existing data file), but just allocate the
> > metadata for the full image size.
> >
> > If the problem is just bdrv_co_write_req_prepare(), would a request flag
> > for the truncate solve it without having to introduce two global BDS
> > flags that can never be set by the user? BDRV_REQ_NO_DATA_IO or
> > something?
>
> Oh, only one new flag. We can drop the RESIZE flag, as you said, and make
> it !BDRV_O_RESIZE. (If we call qcow2_co_truncate() directly, that is.)
The per-request part feels almost more important to me than having only
one flag. But yes, two separate flags for a single purpose isn't great
either, so moving to one would already be some improvement.
> Dropping that flag changes the error messages sometimes (because without
> this flag, WRITE will always imply RESIZE, so with a guest device that
> prevents concurrent resize, you will then always get RESIZE conflicts
> alongside WRITE), but that’s OK (because it’s only when you get a WRITE
> conflict anyway).
>
> > > We could bypass this by calling qcow2_co_truncate() instead of
> > > blk_co_truncate(). Feels wrong to me to pass BDRV_O_NO_IO and
> > > !BDRV_O_RESIZE to blk_co_new_open() when we actually kind of want those
> > > things and just bypass the BB to get them, but only morally wrong. Not
> > > technically wrong.
> > >
> > > Bigger problem: BDRV_O_NO_IO makes qcow2 skip opening the data-file
> > > altogether. So we would need to distinguish between qemu-img info and
> > > this case somehow.
> > Do we need to have it opened, except for preallocation, which can't set
> > BDRV_O_NO_IO anyway?
>
> Yes, for resizing it without preallocation (growing to fit).
Right. In this case, this series wouldn't set BDRV_O_NO_DATA_RESIZE
either, but it would set BDRV_O_NO_DATA_WRITE. This makes me wonder if
it would make sense...
> We could have qcow2_do_open() distinguish by checking whether the
> "data-file" option in the QDict is set and a string (a node-name), because
> if it is, it’s safe to take that existing BDS despite BDRV_O_NO_IO.
...to let qcow2_do_open() still open the data file with BDRV_O_NO_IO if
BDRV_O_RESIZE is given at the same time.
> I’m still not sure how I morally feel about passing BDRV_O_NO_IO when we do
> want to do I/O. Yes, only metadata. I know. But I understand BDRV_O_NO_IO
> was introduced specifically for nothing at all, just querying image
> information.
"Just querying image information" is reading metadata. So if you include
metadata in your definition, you're already inconsistent.
For me I/O means reads, writes, discards etc. on the node. That is,
operations that actually access data. Metadata was never part of this
for me and obviously it is always accessed read-only at least because
otherwise you can't open the image. What's different here is that it's
also written to, but that's what BDRV_O_RDWR means. If you don't want to
write either data or metadata, you should just open the image read-only.
> And it works, yes – you just have to make sure you keep
> BDRV_O_RDWR in there, too, because otherwise the whole thing will be
> read-only and not work. Feels really wrong to me, but I guess we already do
> the same thing at the end of qcow2_co_create() to flush the image, so… Too
> late to protest now, I suppose.
Yes, that seems like the same case to me. Nothing is doing I/O on the
qcow2 node, but some metadata writes may be involved.
Kevin
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-03-06 13:05 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-05 14:47 [PATCH 0/5] qcow2: Suppress data-file WRITE during creation Hanna Czenczek
2026-02-05 14:47 ` [PATCH 1/5] qcow2: Skip data-file resize if possible Hanna Czenczek
2026-02-05 14:47 ` [PATCH 2/5] block: Introduce BDRV_O_NO_DATA_{WRITE,RESIZE} Hanna Czenczek
2026-03-02 14:30 ` Kevin Wolf
2026-03-04 14:20 ` Hanna Czenczek
2026-03-04 16:13 ` Kevin Wolf
2026-03-06 10:20 ` Hanna Czenczek
2026-03-06 11:12 ` Kevin Wolf
2026-03-06 11:28 ` Hanna Czenczek
2026-03-06 13:04 ` Kevin Wolf
2026-02-05 14:47 ` [PATCH 3/5] qcow2: Preallocation: Do not COW after disk end Hanna Czenczek
2026-02-05 14:47 ` [PATCH 4/5] qcow2: Suppress data-file WRITE/RESIZE if possible Hanna Czenczek
2026-02-05 14:47 ` [PATCH 5/5] iotests: Add qcow2-live-data-file test Hanna Czenczek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox