diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
index e677c31..43a9bb9 100644
--- a/block/qcow2-bitmap.c
+++ b/block/qcow2-bitmap.c
@@ -27,6 +27,7 @@
 
 #include "qemu/osdep.h"
 #include "qapi/error.h"
+#include "qemu/cutils.h"
 
 #include "block/block_int.h"
 #include "block/qcow2.h"
@@ -93,6 +94,15 @@ static inline void bitmap_table_to_cpu(uint64_t *bitmap_table, size_t size)
     }
 }
 
+static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
+{
+    size_t i;
+
+    for (i = 0; i < size; ++i) {
+        cpu_to_be64s(&bitmap_table[i]);
+    }
+}
+
 static inline int calc_dir_entry_size(size_t name_size)
 {
     return align_offset(sizeof(QCow2BitmapHeader) + name_size, 8);
@@ -537,3 +547,290 @@ BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name,
 
     return load_bitmap(bs, h, errp);
 }
+
+/* store_bitmap_data()
+ * Store bitmap to image, filling bitamp table accordingly.
+ */
+static int store_bitmap_data(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
+                             uint64_t *bitmap_table, uint32_t bitmap_table_size)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    uint64_t sector, dsc;
+    uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
+    int cl_size = s->cluster_size;
+    uint8_t *buf = NULL;
+    uint32_t tb_size =
+            size_to_clusters(s,
+                bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size));
+
+    BdrvDirtyBitmapIter *dbi;
+
+    if (tb_size != bitmap_table_size) {
+        return -EINVAL;
+    }
+
+    memset(bitmap_table, 0, bitmap_table_size * sizeof(bitmap_table[0]));
+
+    dbi = bdrv_dirty_iter_new(bitmap, 0);
+    buf = g_malloc(cl_size);
+    dsc = dirty_sectors_in_cluster(s, bitmap);
+
+    while ((sector = bdrv_dirty_iter_next(dbi)) != -1) {
+        uint64_t cluster = sector / dsc;
+        sector = cluster * dsc;
+        uint64_t end = MIN(bm_size, sector + dsc);
+        uint64_t write_size =
+            bdrv_dirty_bitmap_serialization_size(bitmap, sector, end - sector);
+
+        int64_t off = qcow2_alloc_clusters(bs, cl_size);
+        if (off < 0) {
+            ret = off;
+            goto finish;
+        }
+        bitmap_table[cluster] = off;
+
+        bdrv_dirty_bitmap_serialize_part(bitmap, buf, sector, end);
+        if (write_size < cl_size) {
+            memset(buf + write_size, 0, cl_size - write_size);
+        }
+
+        ret = bdrv_pwrite(bs->file, off, buf, cl_size);
+        if (ret < 0) {
+            goto finish;
+        }
+
+        if (end >= bm_size) {
+            break;
+        }
+
+        bdrv_set_dirty_iter(dbi, end);
+    }
+    ret = 0; /* writes */
+
+finish:
+    if (ret < 0) {
+        clear_bitmap_table(bs, bitmap_table, bitmap_table_size);
+    }
+    g_free(buf);
+    bdrv_dirty_iter_free(dbi);
+
+    return ret;
+}
+
+/* store_bitmap()
+ * Store bitmap to qcow2 and set bitmap_table. bitmap_table itself is not
+ * stored to qcow2.
+ */
+static int store_bitmap(BlockDriverState *bs,
+                        BdrvDirtyBitmap *bitmap,
+                        uint64_t **bitmap_table,
+                        uint64_t *bitmap_table_offset,
+                        uint32_t *bitmap_table_size)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
+
+    uint64_t *tb;
+    int64_t tb_offset;
+    uint32_t tb_size =
+            size_to_clusters(s,
+                bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size));
+
+    tb = g_try_new(uint64_t, tb_size);
+    if (tb == NULL) {
+        return -ENOMEM;
+    }
+
+    ret = store_bitmap_data(bs, bitmap, tb, tb_size);
+    if (ret < 0) {
+        g_free(tb);
+        return ret;
+    }
+
+    tb_offset = qcow2_alloc_clusters(bs, tb_size * sizeof(tb[0]));
+    if (tb_offset < 0) {
+        ret = tb_offset;
+        goto fail;
+    }
+
+    bitmap_table_to_be(tb, tb_size);
+    ret = bdrv_pwrite(bs->file, tb_offset, tb, tb_size * sizeof(tb[0]));
+    if (ret < 0) {
+        goto fail;
+    }
+
+    if (bitmap_table != NULL) {
+        bitmap_table_to_cpu(tb, tb_size);
+        *bitmap_table = tb;
+    } else {
+        g_free(tb);
+    }
+    if (bitmap_table_offset != NULL) {
+        *bitmap_table_offset = tb_offset;
+    }
+    if (bitmap_table_size != NULL) {
+        *bitmap_table_size = tb_size;
+    }
+
+    return 0;
+
+fail:
+    clear_bitmap_table(bs, tb, tb_size);
+
+    if (tb_offset > 0) {
+        qcow2_free_clusters(bs, tb_offset, tb_size, QCOW2_DISCARD_ALWAYS);
+    }
+
+    g_free(tb);
+
+    return ret;
+}
+
+static int directory_push(BlockDriverState *bs, const char *name,
+        uint32_t granularity, uint64_t table_offset, uint32_t table_size,
+        uint32_t flags)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    size_t name_size = strlen(name);
+    size_t entry_size = calc_dir_entry_size(name_size);
+    QCow2BitmapHeader *bmh = NULL;
+    uint64_t new_size = s->bitmap_directory_size + entry_size;
+    uint8_t *new_dir;
+
+    if (s->nb_bitmaps >= QCOW_MAX_DIRTY_BITMAPS) {
+        return -EFBIG;
+    }
+
+    if (new_size > QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) {
+        return -EINVAL;
+    }
+
+    new_dir = g_try_malloc(new_size);
+    if (new_dir == NULL) {
+        return -ENOMEM;
+    }
+    memcpy(new_dir, s->bitmap_directory, s->bitmap_directory_size);
+
+    bmh = (QCow2BitmapHeader *)(new_dir + s->bitmap_directory_size);
+    bmh->bitmap_table_offset = table_offset;
+    bmh->bitmap_table_size = table_size;
+    bmh->flags = flags;
+    bmh->type = BT_DIRTY_TRACKING_BITMAP;
+    bmh->granularity_bits = ctz32(granularity);
+    bmh->name_size = name_size;
+    bmh->extra_data_size = 0;
+    memcpy(bmh + 1, name, name_size);
+
+    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 int directory_set(BlockDriverState *bs, QCow2BitmapHeader *bmh,
+        uint32_t granularity, uint64_t table_offset, uint32_t table_size,
+        uint32_t flags)
+{
+    int ret;
+    BDRVQcow2State *s = bs->opaque;
+    uint8_t *new_dir;
+
+    assert((uint8_t *)bmh >= s->bitmap_directory &&
+           (uint8_t *)bmh < s->bitmap_directory + s->bitmap_directory_size);
+
+    new_dir = g_memdup(s->bitmap_directory, s->bitmap_directory_size);
+    if (new_dir == NULL) {
+        return -ENOMEM;
+    }
+
+    bmh = (QCow2BitmapHeader *)
+            (new_dir + ((uint8_t *)bmh - s->bitmap_directory));
+    bmh->bitmap_table_offset = table_offset;
+    bmh->bitmap_table_size = table_size;
+    bmh->flags = flags;
+    bmh->granularity_bits = ctz32(granularity);
+
+    ret = directory_update(bs, new_dir, s->bitmap_directory_size,
+                           s->nb_bitmaps);
+    if (ret < 0) {
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    g_free(new_dir);
+
+    return ret;
+}
+
+void qcow2_bitmap_store(BlockDriverState *bs,
+                        BdrvDirtyBitmap *bitmap, Error **errp)
+{
+    int ret = 0;
+    QCow2BitmapHeader *bmh;
+    const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
+    uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
+    uint64_t table_offset;
+    uint32_t table_size;
+    uint64_t *bitmap_table;
+
+    /* find/create dirty bitmap */
+    bmh = find_bitmap_by_name(bs, bm_name);
+    if (bmh != NULL) {
+        if (granularity != (1U << bmh->granularity_bits)) {
+            error_setg(errp,
+                       "The bitmap with same name (but other granularity) "
+                       "already exists.");
+            return;
+        }
+
+        if (bmh->bitmap_table_offset) {
+            error_setg(errp,
+                       "The bitmap with same name already exists, but was"
+                       "not loaded.");
+            return;
+        }
+
+        assert(bmh->bitmap_table_size == 0);
+    }
+
+    ret = store_bitmap(bs, bitmap, &bitmap_table, &table_offset, &table_size);
+    if (ret < 0) {
+        error_setg_errno(errp, ret, "Can't store bitmap table.");
+        return;
+    }
+
+    if (bmh == NULL) {
+        ret = directory_push(bs, bm_name, granularity, table_offset,
+                             table_size, 0);
+        if (ret < 0) {
+            error_setg_errno(errp, ret, "Can't create dirty bitmap in qcow2.");
+            goto fail;
+        }
+    } else {
+        ret = directory_set(bs, bmh, granularity, table_offset, table_size,
+                            bmh->flags);
+        if (ret < 0) {
+            error_setg_errno(errp, ret, "Can't update dirty bitmap in qcow2.");
+            goto fail;
+        }
+    }
+
+    g_free(bitmap_table);
+    return;
+
+fail:
+    do_free_bitmap_clusters(bs, table_offset, table_size, bitmap_table);
+    g_free(bitmap_table);
+}
diff --git a/block/qcow2.c b/block/qcow2.c
index 74dab08..bad7b83 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -3424,6 +3424,7 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_get_specific_info = qcow2_get_specific_info,
 
     .bdrv_dirty_bitmap_load = qcow2_bitmap_load,
+    .bdrv_dirty_bitmap_store = qcow2_bitmap_store,
 
     .bdrv_save_vmstate    = qcow2_save_vmstate,
     .bdrv_load_vmstate    = qcow2_load_vmstate,
diff --git a/block/qcow2.h b/block/qcow2.h
index 573fc36..87b0a32 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -610,6 +610,8 @@ int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp);
 
 BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name,
                                    Error **errp);
+void qcow2_bitmap_store(BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
+                        Error **errp);
 
 /* qcow2-cache.c functions */
 Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables);
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 2c11ad7..ba002f3 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -227,6 +227,9 @@ struct BlockDriver {
     BdrvDirtyBitmap *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs,
                                                const char *name,
                                                Error **errp);
+    void (*bdrv_dirty_bitmap_store)(BlockDriverState *bs,
+                                    BdrvDirtyBitmap *bitmap,
+                                    Error **errp);
 
     int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
                                           QEMUIOVector *qiov,
