@@ -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.
*/