diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index 280b7bf..e677c31 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -61,6 +61,11 @@ typedef enum BitmapType {
     BT_DIRTY_TRACKING_BITMAP = 1
 } BitmapType;
 
+static inline bool can_write(BlockDriverState *bs)
+{
+    return !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE);
+}
+
 static inline void bitmap_header_to_cpu(QCow2BitmapHeader *h)
 {
     be64_to_cpus(&h->bitmap_table_offset);
@@ -70,6 +75,15 @@ static inline void bitmap_header_to_cpu(QCow2BitmapHeader *h)
     be32_to_cpus(&h->extra_data_size);
 }
 
+static inline void bitmap_header_to_be(QCow2BitmapHeader *h)
+{
+    cpu_to_be64s(&h->bitmap_table_offset);
+    cpu_to_be32s(&h->bitmap_table_size);
+    cpu_to_be32s(&h->flags);
+    cpu_to_be16s(&h->name_size);
+    cpu_to_be32s(&h->extra_data_size);
+}
+
 static inline void bitmap_table_to_cpu(uint64_t *bitmap_table, size_t size)
 {
     size_t i;
@@ -94,6 +108,17 @@ static inline QCow2BitmapHeader *next_dir_entry(QCow2BitmapHeader *entry)
     return (QCow2BitmapHeader *)((uint8_t *)entry + dir_entry_size(entry));
 }
 
+static inline void bitmap_directory_to_be(uint8_t *dir, size_t size)
+{
+    uint8_t *end = dir + size;
+    while (dir < end) {
+        QCow2BitmapHeader *h = (QCow2BitmapHeader *)dir;
+        dir += dir_entry_size(h);
+
+        bitmap_header_to_be(h);
+    }
+}
+
 /* directory_read
  * Read bitmaps directory from bs by @offset and @size. Convert it to cpu
  * format from BE.
@@ -174,6 +199,35 @@ static QCow2BitmapHeader *find_bitmap_by_name(BlockDriverState *bs,
     return NULL;
 }
 
+static void clear_bitmap_table(BlockDriverState *bs, uint64_t *bitmap_table,
+                               uint32_t bitmap_table_size)
+{
+    BDRVQcow2State *s = bs->opaque;
+    int cl_size = s->cluster_size;
+    int i;
+
+    for (i = 0; i < bitmap_table_size; ++i) {
+        uint64_t addr = bitmap_table[i];
+        if (addr <= 1) {
+            continue;
+        }
+
+        qcow2_free_clusters(bs, addr, cl_size, QCOW2_DISCARD_ALWAYS);
+        bitmap_table[i] = 0;
+    }
+}
+
+static void do_free_bitmap_clusters(BlockDriverState *bs,
+                                    uint64_t table_offset,
+                                    uint32_t table_size,
+                                    uint64_t *bitmap_table)
+{
+    clear_bitmap_table(bs, bitmap_table, table_size);
+
+    qcow2_free_clusters(bs, table_offset, table_size * sizeof(uint64_t),
+                        QCOW2_DISCARD_ALWAYS);
+}
+
 /* dirty sectors in cluster is a number of sectors in the image, corresponding
  * to one cluster of bitmap data */
 static uint64_t dirty_sectors_in_cluster(const BDRVQcow2State *s,
@@ -255,6 +309,165 @@ static int bitmap_table_load(BlockDriverState *bs, QCow2BitmapHeader *bmh,
     return 0;
 }
 
+static int update_header_sync(BlockDriverState *bs)
+{
+    int ret;
+
+    ret = qcow2_update_header(bs);
+    if (ret < 0) {
+        return ret;
+    }
+
+    ret = bdrv_flush(bs);
+    if (ret < 0) {
+        return ret;
+    }
+
+    return 0;
+}
+
+/* write bitmap directory from the state to new allocated clusters */
+static int64_t directory_write(BlockDriverState *bs, const uint8_t *dir,
+                               size_t size)
+{
+    int ret = 0;
+    uint8_t *dir_be = NULL;
+    int64_t dir_offset = 0;
+
+    dir_be = g_try_malloc(size);
+    if (dir_be == NULL) {
+        return -ENOMEM;
+    }
+    memcpy(dir_be, dir, size);
+    bitmap_directory_to_be(dir_be, size);
+
+    /* Allocate space for the new bitmap directory */
+    dir_offset = qcow2_alloc_clusters(bs, size);
+    if (dir_offset < 0) {
+        ret = dir_offset;
+        goto out;
+    }
+
+    /* The bitmap directory position has not yet been updated, so these
+     * clusters must indeed be completely free */
+    ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, size);
+    if (ret < 0) {
+        goto out;
+    }
+
+    ret = bdrv_pwrite(bs->file, dir_offset, dir_be, size);
+    if (ret < 0) {
+        goto out;
+    }
+
+out:
+    g_free(dir_be);
+
+    if (ret < 0) {
+        if (dir_offset > 0) {
+            qcow2_free_clusters(bs, dir_offset, size, QCOW2_DISCARD_ALWAYS);
+        }
+
+        return ret;
+    }
+
+    return dir_offset;
+}
+
+static int directory_update(BlockDriverState *bs, uint8_t *new_dir,
+                            size_t new_size, uint32_t new_nb_bitmaps)
+{
+    BDRVQcow2State *s = bs->opaque;
+    int ret;
+    int64_t new_offset = 0;
+    uint64_t old_offset = s->bitmap_directory_offset;
+    uint64_t old_size = s->bitmap_directory_size;
+    uint32_t old_nb_bitmaps = s->nb_bitmaps;
+    uint64_t old_autocl = s->autoclear_features;
+
+    if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) {
+        return -EINVAL;
+    }
+
+    if ((new_size == 0) != (new_nb_bitmaps == 0)) {
+        return -EINVAL;
+    }
+
+    if (new_nb_bitmaps > 0) {
+        new_offset = directory_write(bs, new_dir, new_size);
+        if (new_offset < 0) {
+            return new_offset;
+        }
+
+        ret = bdrv_flush(bs);
+        if (ret < 0) {
+            goto fail;
+        }
+    }
+    s->bitmap_directory_offset = new_offset;
+    s->bitmap_directory_size = new_size;
+    s->nb_bitmaps = new_nb_bitmaps;
+
+    ret = update_header_sync(bs);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    if (old_size) {
+        qcow2_free_clusters(bs, old_offset, old_size, QCOW2_DISCARD_ALWAYS);
+    }
+
+    g_free(s->bitmap_directory);
+    s->bitmap_directory = new_dir;
+
+    return 0;
+
+fail:
+    if (new_offset > 0) {
+        qcow2_free_clusters(bs, new_offset, new_size, QCOW2_DISCARD_ALWAYS);
+        s->bitmap_directory_offset = old_offset;
+        s->bitmap_directory_size = old_size;
+        s->nb_bitmaps = old_nb_bitmaps;
+        s->autoclear_features = old_autocl;
+    }
+
+    return ret;
+}
+
+static int directory_del(BlockDriverState *bs, QCow2BitmapHeader *bmh)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    uint8_t *new_dir = NULL;
+
+    size_t sz1 = (uint8_t *)bmh - s->bitmap_directory;
+    size_t sz2 = dir_entry_size(bmh);
+    size_t sz3 = s->bitmap_directory_size - sz1 - sz2;
+
+    uint64_t new_size = sz1 + sz3;
+
+    if (new_size > 0) {
+        new_dir = g_try_malloc(new_size);
+        if (new_dir == NULL) {
+            return -ENOMEM;
+        }
+        memcpy(new_dir, s->bitmap_directory, sz1);
+        memcpy(new_dir + sz1, s->bitmap_directory + sz1 + sz2, sz3);
+    }
+
+    ret = directory_update(bs, new_dir, new_size, s->nb_bitmaps - 1);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    g_free(new_dir);
+
+    return ret;
+}
+
 static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
                                     QCow2BitmapHeader *bmh, Error **errp)
 {
@@ -284,6 +497,20 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
         goto fail;
     }
 
+    if (can_write(bs)) {
+        uint64_t off = bmh->bitmap_table_offset;
+        uint32_t sz = bmh->bitmap_table_size;
+
+        ret = directory_del(bs, bmh);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret,
+                             "Could not free bitmap after read.");
+            goto fail;
+        }
+
+        do_free_bitmap_clusters(bs, off, sz, bitmap_table);
+    }
+
     g_free(name);
     g_free(bitmap_table);
     return bitmap;
