In some cases, it might happen that bitmaps become corrupt.
For example when adding/removing bitmaps on a live image.
Of course this should not happen, but in case this happens, the image is
corrupt and cannot even be opened anymore. You get something like the
following for example:
qemu-img: Could not open 'disk.qcow2': Bitmap '' doesn't satisfy the
constraints
So the image becomes useless, and cannot be repaired. This while in fact
only (one) bitmap entry is corrupt, and the rest of the data is just
intact.
This commit adds a way to fix this corruption, by just replacing the
bitmap list in the qcow2 image with the valid bitmaps, and dropping the
bitmaps that are corrupt.
qemu-img: Check failed: Invalid argument
qemu-img: Lost persistent bitmaps during inactivation of node
'#block195': Bitmap '' doesn't satisfy the constraints
qcow2_free_clusters failed: Invalid argument
Leaked cluster 3 refcount=1 reference=0
Leaked cluster 26 refcount=1 reference=0
Repairing cluster 3 refcount=1 reference=0
Repairing cluster 26 refcount=1 reference=0
The following inconsistencies were found and repaired:
2 leaked clusters
1 corruptions
Double checking the fixed image now...
No errors were found on the image.
983056/1048576 = 93.75% allocated, 0.05% fragmented, 0.00% compressed
clusters
Image end offset: 64435847168
And the image is valid again!
Worst case you lose all bitmaps, but at least the image and the data
itself is useable again.
Signed-off-by: Jean-Louis Dupond <[email protected]>
---
block/qcow2-bitmap.c | 78 ++++++++++++++++++++++++++++++++++++++++++++
block/qcow2.c | 22 ++++++++++---
block/qcow2.h | 4 +++
3 files changed, 100 insertions(+), 4 deletions(-)
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 256ec99878..f107de3301 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -1807,3 +1807,81 @@ uint64_t
qcow2_get_persistent_dirty_bitmap_size(BlockDriverState *in_bs,
return bitmaps_size;
}
+
+/*
+ * qcow2_check_bitmaps - Check and optionally fix bitmap directory
and bitmaps
+ * This function is called during image check to detect bitmap
corruption.
+ * It attempts to load the bitmap directory and validates the
structures.
+ * If BDRV_FIX_ERRORS is set, it removes corrupted bitmaps.
+ */
+int coroutine_fn GRAPH_RDLOCK
+qcow2_check_bitmaps(BlockDriverState *bs, BdrvCheckResult *result,
+ BdrvCheckMode fix)
+{
+ BDRVQcow2State *s = bs->opaque;
+ Qcow2BitmapList *bm_list;
+ Qcow2BitmapList *fixed_list = NULL;
+ Qcow2Bitmap *bm;
+ int valid_bitmaps = 0;
+ int ret = 0;
+
+ if (s->nb_bitmaps == 0) {
+ /* No bitmaps - nothing to check */
+ return 0;
+ }
+
+ /* Try to load the bitmap directory */
+ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
+ s->bitmap_directory_size, NULL);
+ if (bm_list == NULL) {
+ /* Bitmap directory is corrupted */
+ result->corruptions++;
+
+ if (fix & BDRV_FIX_ERRORS) {
+ ret = update_ext_header_and_dir(bs, bitmap_list_new());
+ if (ret < 0) {
+ return ret;
+ }
+ result->corruptions_fixed++;
+ }
+
+ return 0;
+ }
+
+ if (fix & BDRV_FIX_ERRORS) {
+ fixed_list = bitmap_list_new();
+ }
+
+ /* Validate each bitmap */
+ QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+ uint64_t *bitmap_table = NULL;
+
+ /* Try to load the bitmap table */
+ ret = bitmap_table_load(bs, &bm->table, &bitmap_table);
+ g_free(bitmap_table);
+ if (ret < 0) {
+ /* Bitmap table is corrupted */
+ result->corruptions++;
+ continue;
+ }
+
+ if (fix & BDRV_FIX_ERRORS) {
+ valid_bitmaps++;
+ QSIMPLEQ_INSERT_TAIL(fixed_list, bm, entry);
+ }
+ }
+
+ bitmap_list_free(bm_list);
+
+ /* If fixing, update the bitmap directory with the repaired list */
+ if ((fix & BDRV_FIX_ERRORS) && s->nb_bitmaps != valid_bitmaps) {
+ ret = update_ext_header_and_dir(bs, fixed_list);
+ bitmap_list_free(fixed_list);
+ if (ret < 0) {
+ return ret;
+ }
+ result->corruptions_fixed += s->nb_bitmaps - valid_bitmaps;
+ }