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; >