diff mbox

[27/29] qcow2-bitmap: delete in_use bitmaps on image load

Message ID 1470668720-211300-28-git-send-email-vsementsov@virtuozzo.com (mailing list archive)
State New, archived
Headers show

Commit Message

Vladimir Sementsov-Ogievskiy Aug. 8, 2016, 3:05 p.m. UTC
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
---
 block/qcow2-bitmap.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
diff mbox

Patch

diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 49066bd..e94019c 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -28,6 +28,7 @@ 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
 #include "qemu/cutils.h"
+#include "qemu/log.h"
 
 #include "block/block_int.h"
 #include "block/qcow2.h"
@@ -132,6 +133,17 @@  static inline QCow2BitmapHeader *next_dir_entry(QCow2BitmapHeader *entry)
     return (QCow2BitmapHeader *)((uint8_t *)entry + dir_entry_size(entry));
 }
 
+static inline void bitmap_directory_to_cpu(uint8_t *dir, size_t size)
+{
+    QCow2BitmapHeader *h;
+
+    /* loop is safe because next entry offset is calculated after conversion to
+     * cpu format */
+    for_each_bitmap_header_in_dir(h, dir, size) {
+        bitmap_header_to_cpu(h);
+    }
+}
+
 static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
 {
     uint8_t *end = dir + size;
@@ -241,6 +253,7 @@  static int load_autoload_bitmaps(BlockDriverState *bs, Error **errp)
     return 0;
 }
 
+static int handle_in_use_bitmaps(BlockDriverState *bs);
 int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp)
 {
     int ret;
@@ -262,6 +275,19 @@  int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp)
         return -EINVAL;
     }
 
+    if (can_write(bs)) {
+        ret = handle_in_use_bitmaps(bs);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Can't delete in_use bitmaps.");
+            goto fail;
+        }
+
+        if (s->nb_bitmaps == 0) {
+            /* No bitmaps - nothing to do */
+            return 0;
+        }
+    }
+
     ret = load_autoload_bitmaps(bs, errp);
     if (ret < 0) {
         goto fail;
@@ -685,6 +711,75 @@  int qcow2_delete_bitmaps(BlockDriverState *bs)
     return directory_update(bs, NULL, 0, 0);
 }
 
+static int handle_in_use_bitmaps(BlockDriverState *bs)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    uint64_t new_size = 0;
+    uint32_t new_nb_bitmaps = 0;
+    uint8_t *new_dir = NULL, *p;
+    QCow2BitmapHeader *h;
+
+    /* free in_use bitmaps clusters */
+    for_each_bitmap_header(h, s) {
+        if (h->flags & BME_FLAG_IN_USE) {
+            free_bitmap_clusters(bs, h);
+            h->bitmap_table_offset = 0;
+            h->bitmap_table_size = 0;
+        } else {
+            new_size += dir_entry_size(h);
+            new_nb_bitmaps++;
+        }
+    }
+
+    /* no in_use bitmaps */
+    if (new_nb_bitmaps == s->nb_bitmaps) {
+        return 0;
+    }
+
+    /* just update bitmap directory with freed out in_use bitmaps */
+    if (!(bdrv_get_flags(bs) & BDRV_O_CHECK)) {
+        bitmap_directory_to_be(s->bitmap_directory, s->bitmap_directory_size);
+        ret = bdrv_pwrite(bs->file,
+                          s->bitmap_directory_offset,
+                          s->bitmap_directory,
+                          s->bitmap_directory_size);
+        bitmap_directory_to_cpu(s->bitmap_directory, s->bitmap_directory_size);
+
+        return ret;
+    }
+
+    if (new_nb_bitmaps == 0) {
+        goto update;
+    }
+
+    p = new_dir = g_try_malloc(new_size);
+    if (new_dir == NULL) {
+        return -ENOMEM;
+    }
+
+    for_each_bitmap_header(h, s) {
+        if (h->flags & BME_FLAG_IN_USE) {
+            char *bitmap_name = g_strndup((char *)(h + 1), h->name_size);
+            qemu_log("Delete corrupted (in_use) bitmap '%s' from device '%s'\n",
+                     bitmap_name, bdrv_get_device_or_node_name(bs));
+            g_free(bitmap_name);
+        } else {
+            memcpy(p, h, dir_entry_size(h));
+            p += dir_entry_size(h);
+        }
+    }
+
+update:
+    ret = directory_update(bs, new_dir, new_size, new_nb_bitmaps);
+    if (ret < 0) {
+        g_free(new_dir);
+        return ret;
+    }
+
+    return 0;
+}
+
 /* store_bitmap_data()
  * Store bitmap to image, filling bitamp table accordingly.
  */