@@ -53,6 +53,10 @@
h < (QCow2BitmapHeader *)((uint8_t *)(dir) + size); \
h = next_dir_entry(h))
+#define for_each_bitmap_header(h, s) \
+ for_each_bitmap_header_in_dir(h, s->bitmap_directory, \
+ s->bitmap_directory_size)
+
typedef enum BitmapType {
BT_DIRTY_TRACKING_BITMAP = 1
} BitmapType;
@@ -66,6 +70,15 @@ static inline void bitmap_header_to_cpu(QCow2BitmapHeader *h)
be32_to_cpus(&h->extra_data_size);
}
+static inline void bitmap_table_to_cpu(uint64_t *bitmap_table, size_t size)
+{
+ size_t i;
+
+ for (i = 0; i < size; ++i) {
+ be64_to_cpus(&bitmap_table[i]);
+ }
+}
+
static inline int calc_dir_entry_size(size_t name_size)
{
return align_offset(sizeof(QCow2BitmapHeader) + name_size, 8);
@@ -145,3 +158,155 @@ int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp)
return 0;
}
+
+static QCow2BitmapHeader *find_bitmap_by_name(BlockDriverState *bs,
+ const char *name)
+{
+ BDRVQcow2State *s = bs->opaque;
+ QCow2BitmapHeader *h;
+
+ for_each_bitmap_header(h, s) {
+ if (strncmp((char *)(h + 1), name, h->name_size) == 0) {
+ return h;
+ }
+ }
+
+ return NULL;
+}
+
+/* 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,
+ const BdrvDirtyBitmap *bitmap)
+{
+ uint32_t sector_granularity =
+ bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS;
+
+ return (uint64_t)sector_granularity * (s->cluster_size << 3);
+}
+
+static int load_bitmap_data(BlockDriverState *bs,
+ const uint64_t *dirty_bitmap_table,
+ uint32_t dirty_bitmap_table_size,
+ BdrvDirtyBitmap *bitmap)
+{
+ int ret = 0;
+ 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 i, tab_size =
+ size_to_clusters(s,
+ bdrv_dirty_bitmap_serialization_size(bitmap, 0, bm_size));
+
+ if (tab_size != dirty_bitmap_table_size) {
+ return -EINVAL;
+ }
+
+ bdrv_clear_dirty_bitmap(bitmap, NULL);
+
+ buf = g_malloc0(cl_size);
+ dsc = dirty_sectors_in_cluster(s, bitmap);
+ for (i = 0, sector = 0; i < tab_size; ++i, sector += dsc) {
+ uint64_t end = MIN(bm_size, sector + dsc);
+ uint64_t offset = dirty_bitmap_table[i];
+
+ if (offset == 1) {
+ bdrv_dirty_bitmap_deserialize_ones(bitmap, sector, end, false);
+ } else if (offset > 1) {
+ ret = bdrv_pread(bs->file, offset, buf, cl_size);
+ if (ret < 0) {
+ goto finish;
+ }
+ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, sector, end, false);
+ }
+ }
+ ret = 0;
+
+ bdrv_dirty_bitmap_deserialize_finish(bitmap);
+
+finish:
+ g_free(buf);
+
+ return ret;
+}
+
+static int bitmap_table_load(BlockDriverState *bs, QCow2BitmapHeader *bmh,
+ uint64_t **table)
+{
+ int ret;
+
+ *table = g_try_new(uint64_t, bmh->bitmap_table_size);
+ if (*table == NULL) {
+ return -ENOMEM;
+ }
+
+ ret = bdrv_pread(bs->file, bmh->bitmap_table_offset,
+ *table, bmh->bitmap_table_size * sizeof(uint64_t));
+ if (ret < 0) {
+ g_free(*table);
+ *table = NULL;
+ return ret;
+ }
+
+ bitmap_table_to_cpu(*table, bmh->bitmap_table_size);
+
+ return 0;
+}
+
+static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs,
+ QCow2BitmapHeader *bmh, Error **errp)
+{
+ int ret;
+ uint64_t *bitmap_table = NULL;
+ uint32_t granularity;
+ BdrvDirtyBitmap *bitmap = NULL;
+ char *name = g_strndup((char *)(bmh + 1), bmh->name_size);
+
+ ret = bitmap_table_load(bs, bmh, &bitmap_table);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Could not read bitmap_table table from image");
+ goto fail;
+ }
+
+ granularity = 1U << bmh->granularity_bits;
+ bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp);
+ if (bitmap == NULL) {
+ goto fail;
+ }
+
+ ret = load_bitmap_data(bs, bitmap_table, bmh->bitmap_table_size,
+ bitmap);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "Could not read bitmap from image");
+ goto fail;
+ }
+
+ g_free(name);
+ g_free(bitmap_table);
+ return bitmap;
+
+fail:
+ g_free(name);
+ g_free(bitmap_table);
+ if (bitmap != NULL) {
+ bdrv_release_dirty_bitmap(bs, bitmap);
+ }
+
+ return NULL;
+}
+
+BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name,
+ Error **errp)
+{
+ QCow2BitmapHeader *h = find_bitmap_by_name(bs, name);
+ if (h == NULL) {
+ error_setg(errp, "Could not find bitmap '%s' in the node '%s'", name,
+ bdrv_get_device_or_node_name(bs));
+ return NULL;
+ }
+
+ return load_bitmap(bs, h, errp);
+}
@@ -3423,6 +3423,8 @@ BlockDriver bdrv_qcow2 = {
.bdrv_get_info = qcow2_get_info,
.bdrv_get_specific_info = qcow2_get_specific_info,
+ .bdrv_dirty_bitmap_load = qcow2_bitmap_load,
+
.bdrv_save_vmstate = qcow2_save_vmstate,
.bdrv_load_vmstate = qcow2_load_vmstate,
@@ -608,6 +608,9 @@ int qcow2_read_snapshots(BlockDriverState *bs);
void qcow2_free_bitmaps(BlockDriverState *bs);
int qcow2_read_bitmaps(BlockDriverState *bs, Error **errp);
+BdrvDirtyBitmap *qcow2_bitmap_load(BlockDriverState *bs, const char *name,
+ Error **errp);
+
/* qcow2-cache.c functions */
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables);
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
@@ -224,6 +224,10 @@ struct BlockDriver {
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
+ BdrvDirtyBitmap *(*bdrv_dirty_bitmap_load)(BlockDriverState *bs,
+ const char *name,
+ Error **errp);
+
int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
QEMUIOVector *qiov,
int64_t pos);
This function loads block dirty bitmap from qcow2. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> --- block/qcow2-bitmap.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 2 + block/qcow2.h | 3 + include/block/block_int.h | 4 ++ 4 files changed, 174 insertions(+)