Adds qcow2_read_dirty_bitmaps, reading Dirty Bitmap Directory as specified in docs/specs/qcow2.txt
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- block/qcow2-dirty-bitmap.c | 155 +++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.h | 10 +++ 2 files changed, 165 insertions(+) diff --git a/block/qcow2-dirty-bitmap.c b/block/qcow2-dirty-bitmap.c index fd4e0ef..1260d1d 100644 --- a/block/qcow2-dirty-bitmap.c +++ b/block/qcow2-dirty-bitmap.c @@ -25,6 +25,9 @@ * THE SOFTWARE. */ +#include "block/block_int.h" +#include "block/qcow2.h" + /* NOTICE: DBM here means Dirty Bitmap and used as a namespace for _internal_ * constants. Please do not use this _internal_ abbreviation for other needs * and/or outside of this file. */ @@ -40,3 +43,155 @@ /* bits [0, 8] U [56, 63] are reserved */ #define DBM_TABLE_ENTRY_RESERVED_MASK 0xff000000000001ff + +void qcow2_free_dirty_bitmaps(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + int i; + + for (i = 0; i < s->nb_dirty_bitmaps; i++) { + g_free(s->dirty_bitmaps[i].name); + } + g_free(s->dirty_bitmaps); + s->dirty_bitmaps = NULL; + s->nb_dirty_bitmaps = 0; + + g_free(s->dirty_bitmap_directory); + s->dirty_bitmap_directory = NULL; +} + +static void bitmap_header_to_cpu(QCowDirtyBitmapHeader *h) +{ + be64_to_cpus(&h->dirty_bitmap_table_offset); + be64_to_cpus(&h->nb_virtual_bits); + be32_to_cpus(&h->dirty_bitmap_table_size); + be32_to_cpus(&h->granularity_bits); + be32_to_cpus(&h->flags); + be16_to_cpus(&h->name_size); +} + +static int calc_dir_entry_size(size_t name_size) +{ + return align_offset(sizeof(QCowDirtyBitmapHeader) + name_size, 8); +} + +static int dir_entry_size(QCowDirtyBitmapHeader *h) +{ + return calc_dir_entry_size(h->name_size); +} + +static int check_constraints(int cluster_size, + QCowDirtyBitmapHeader *h) +{ + uint64_t phys_bitmap_bytes = + (uint64_t)h->dirty_bitmap_table_size * cluster_size; + uint64_t max_virtual_bits = (phys_bitmap_bytes * 8) << h->granularity_bits; + + int fail = + (h->dirty_bitmap_table_offset % cluster_size) || + (h->dirty_bitmap_table_size > DBM_MAX_TABLE_SIZE) || + (phys_bitmap_bytes > DBM_MAX_PHYS_SIZE) || + (h->nb_virtual_bits > max_virtual_bits) || + (h->granularity_bits > DBM_MAX_GRANULARITY_BITS) || + (h->flags & DBM_RESERVED_FLAGS) || + (h->name_size > DBM_MAX_NAME_SIZE); + + return fail ? -EINVAL : 0; +} + +static int directory_read(BlockDriverState *bs) +{ + int ret; + BDRVQcowState *s = bs->opaque; + uint8_t *entry, *end; + + if (s->dirty_bitmap_directory != NULL) { + /* already read */ + return -EEXIST; + } + + s->dirty_bitmap_directory = g_try_malloc0(s->dirty_bitmap_directory_size); + if (s->dirty_bitmap_directory == NULL) { + return -ENOMEM; + } + + ret = bdrv_pread(bs->file, + s->dirty_bitmap_directory_offset, + s->dirty_bitmap_directory, + s->dirty_bitmap_directory_size); + if (ret < 0) { + goto fail; + } + + entry = s->dirty_bitmap_directory; + end = s->dirty_bitmap_directory + s->dirty_bitmap_directory_size; + while (entry < end) { + QCowDirtyBitmapHeader *h = (QCowDirtyBitmapHeader *)entry; + bitmap_header_to_cpu(h); + + ret = check_constraints(s->cluster_size, h); + if (ret < 0) { + goto fail; + } + + entry += dir_entry_size(h); + } + + return 0; + +fail: + g_free(s->dirty_bitmap_directory); + s->dirty_bitmap_directory = NULL; + + return ret; +} + +int qcow2_read_dirty_bitmaps(BlockDriverState *bs) +{ + int ret; + BDRVQcowState *s = bs->opaque; + size_t offset; + QCowDirtyBitmap *bm, *end; + + if (s->dirty_bitmap_directory != NULL || s->dirty_bitmaps != NULL) { + /* already read */ + return -EEXIST; + } + + if (s->nb_dirty_bitmaps == 0) { + /* No bitmaps - nothing to do */ + return 0; + } + + ret = directory_read(bs); + if (ret < 0) { + return ret; + } + + s->dirty_bitmaps = g_try_new0(QCowDirtyBitmap, s->nb_dirty_bitmaps); + if (s->dirty_bitmaps == NULL) { + ret = -ENOMEM; + goto out; + } + + offset = 0; + end = s->dirty_bitmaps + s->nb_dirty_bitmaps; + for (bm = s->dirty_bitmaps; bm < end; ++bm) { + QCowDirtyBitmapHeader *h = + (QCowDirtyBitmapHeader *)(s->dirty_bitmap_directory + offset); + + bm->offset = offset; + bm->name = g_malloc(h->name_size + 1); + memcpy(bm->name, h + 1, h->name_size); + bm->name[h->name_size] = '\0'; + + offset += dir_entry_size(h); + } + ret = 0; + +out: + if (ret < 0) { + qcow2_free_dirty_bitmaps(bs); + } + return ret; +} diff --git a/block/qcow2.h b/block/qcow2.h index a2a5d4a..5016fa1 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -288,6 +288,12 @@ typedef struct BDRVQcowState { unsigned int nb_snapshots; QCowSnapshot *snapshots; + uint64_t dirty_bitmap_directory_offset; + size_t dirty_bitmap_directory_size; + uint8_t *dirty_bitmap_directory; + unsigned int nb_dirty_bitmaps; + QCowDirtyBitmap *dirty_bitmaps; + int flags; int qcow_version; bool use_lazy_refcounts; @@ -598,6 +604,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, void qcow2_free_snapshots(BlockDriverState *bs); int qcow2_read_snapshots(BlockDriverState *bs); +/* qcow2-dirty-bitmap.c functions */ +void qcow2_free_dirty_bitmaps(BlockDriverState *bs); +int qcow2_read_dirty_bitmaps(BlockDriverState *bs); + /* qcow2-cache.c functions */ Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables); int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c); -- 2.1.4