Re: [PATCH 2/2] qcow2: repair a dirty image when it becomes writable

Hanna Czenczek <[email protected]> Wed, 5 Aug 2026 19:06:30 +0200
Newsgroups gmane.comp.emulators.qemu.block,gmane.comp.emulators.qemu.stable,gmane.comp.emulators.qemu
Message-ID <[email protected]>
On 01.08.26 00:00, Denis V. Lunev wrote:
> A dirty image must be repaired before anything allocates a cluster in
> it. qcow2_do_open() does that, but only for a node that is writable
> from the start. A node opened read-only skips it, and nothing revisits
> the question once that node becomes writable, which block-commit does
> routinely: commit_active_start() and commit_start() reopen the base
> read-write for the duration of the job.
>
> With lazy refcounts the on-disk refcount block then still accounts for
> the metadata clusters only, so the allocator restarts at the front of
> the image and hands out clusters that L2 entries point at. Two guest
> offsets end up sharing one host cluster. Nothing fails, the corrupt bit
> stays clear, and a clean close clears the dirty bit, so no later open
> repairs the image either.
>
> Do the repair in qcow2_reopen_commit_post(). bdrv_reopen_prepare() runs
> before bdrv_list_refresh_perms(), so it holds no BLK_PERM_WRITE and,
> with auto-read-only, bs->file may still have an O_RDONLY descriptor.
> commit_post cannot reject the reopen, so signal corruption if the
> repair fails rather than let writes alias live clusters. An inactive
> node is skipped: bdrv_activate() calls qcow2_do_open() again through
> qcow2_co_invalidate_cache().
>
> Signed-off-by: Denis V. Lunev <[email protected]>
> CC: Kevin Wolf <[email protected]>
> CC: Hanna Reitz <[email protected]>
> ---
>   block/qcow2.c              | 14 ++++++++++++++
>   tests/qemu-iotests/039     | 24 ++++++++++++++++++++++++
>   tests/qemu-iotests/039.out | 17 +++++++++++++++++
>   3 files changed, 55 insertions(+)
>
> diff --git a/block/qcow2.c b/block/qcow2.c
> index 1543255eba..e660655a0d 100644
> --- a/block/qcow2.c
> +++ b/block/qcow2.c
> @@ -2147,8 +2147,22 @@ static void qcow2_reopen_commit(BDRVReopenState *state)
>   
>   static void qcow2_reopen_commit_post(BDRVReopenState *state)
>   {
> +    BDRVQcow2State *s = state->bs->opaque;
> +
>       GRAPH_RDLOCK_GUARD_MAINLOOP();
>   
> +    if (bdrv_is_writable(state->bs) &&
> +        (s->incompatible_features & QCOW2_INCOMPAT_DIRTY)) {
> +        BdrvCheckResult result = {0};
> +        int ret;
> +
> +        ret = bdrv_check(state->bs, &result, BDRV_FIX_ERRORS | BDRV_FIX_LEAKS);
> +        if (ret < 0 || result.check_errors) {
> +            qcow2_signal_corruption(state->bs, true, -1, -1,
> +                                    "Could not repair dirty image");
> +        }

Should we return here? I don’t think we should go on into the 
`qcow2_reopen_bitmaps_rw()` path.

Hanna

> +    }
> +
>       if (state->flags & BDRV_O_RDWR) {
>           Error *local_err = NULL;
>