Commit 901ac73874 for qemu.org

commit 901ac73874c2129a17b6e54482aee800a0f9b2d2
Author: Fiona Ebner <f.ebner@proxmox.com>
Date:   Wed Sep 30 15:19:25 2026 +0200

    qemu-img: convert: flush target image after copying

    With 'qemu-img convert', when using a cache mode with 'cache.no-flush
    = off', the expectation is that the data is flushed to the disk, which
    happens since commit 80ccf93b88 ("qemu-img: let 'qemu-img convert'
    flush data"). This expectation also encompasses that failure to do so
    is properly reported, so ensure that 'qemu-img convert' exits with a
    non-zero status code when the target image cannot be flushed.

    In particular, the reproducer below [0], based on a report by
    Ing. Alfonso Kuen Arroyo [1], would result in
    > rc=0
    > Content mismatch at offset XYZ!
    before this change, and in
    > rc=1
    > Content mismatch at offset XYZ!
    after this change.

    Note that this change does not affect the (default) 'unsafe' cache
    mode, because bdrv_co_flush() still skips flush to disk for that cache
    mode.

    [0]:
    > #!/bin/bash -x
    > dd if=/dev/urandom of=src.raw bs=1M count=500
    > truncate -s 500M dst.img
    > LOOP=$(losetup --find --show dst.img)
    > SIZE=$(blockdev --getsz $LOOP)
    > dd if=/dev/zero of=$LOOP bs=1M count=500 conv=fsync status=none
    > sync
    > echo 3 > /proc/sys/vm/drop_caches
    > dmsetup create flk --table "0 $SIZE flakey $LOOP 0 5 5"
    > qemu-img convert -t writeback -p -r 100M -f raw -O raw src.raw /dev/mapper/flk
    > echo rc=$?
    > sleep 5
    > dmsetup remove flk
    > qemu-img compare src.raw $LOOP
    > losetup -d $LOOP
    > rm src.raw dst.img

    [1]: https://lore.proxmox.com/pve-devel/178961792132.43804.17173233935297903311@idkmanager.com/

    Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
    Message-ID: <20260930132140.2342574-5-f.ebner@proxmox.com>
    Reviewed-by: Eric Blake <eblake@redhat.com>
    Signed-off-by: Eric Blake <eblake@redhat.com>

diff --git a/qemu-img.c b/qemu-img.c
index 2f63d31141..edf75e43ab 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -2245,7 +2245,7 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
     Error *local_err = NULL;
     bool writethrough, src_writethrough, image_opts = false,
          skip_create = false, progress = false, tgt_image_opts = false;
-    int64_t ret = -EINVAL;
+    int64_t ret = -EINVAL, ret2;
     bool force_share = false;
     bool explict_min_sparse = false;
     bool bitmaps = false;
@@ -2872,6 +2872,12 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv)
         ret = convert_copy_bitmaps(blk_bs(s.src[0]), out_bs, skip_broken);
     }

+    ret2 = blk_flush(s.target);
+    if (ret2 < 0) {
+        error_report("Failed to flush target image: %s", strerror(-ret2));
+        ret = ret != 0 ? ret : ret2;
+    }
+
 out:
     if (!ret) {
         qemu_progress_print(100, 0);
diff --git a/tests/qemu-iotests/122.out b/tests/qemu-iotests/122.out
index 84283f2434..38794f96ac 100644
--- a/tests/qemu-iotests/122.out
+++ b/tests/qemu-iotests/122.out
@@ -267,9 +267,10 @@ qemu-img convert exited with status 0

 === failing flush after convert with 'writeback' cache mode ===

+qemu-img: Failed to flush target image: Input/output error
 qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
 qemu-img: Failed to flush the L2 table cache: Input/output error
 qemu-img: Failed to flush the refcount block cache: Input/output error
 qemu-img: Failed flush for 'json:{ /* filtered */ }' during close: Input/output error
-qemu-img convert exited with status 0
+qemu-img convert exited with status 1
 *** done