#include "qemu/cutils.h"
#include "block/block_int.h"
-#include "block/qcow2.h"
+#include "qcow2.h"
/* NOTICE: BME here means Bitmaps Extension and used as a namespace for
* _internal_ constants. Please do not use this _internal_ abbreviation for
uint32_t size; /* number of 64bit entries */
QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry;
} Qcow2BitmapTable;
-typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable)
- Qcow2BitmapTableList;
typedef struct Qcow2Bitmap {
Qcow2BitmapTable table;
return ret;
}
- return bdrv_flush(bs);
+ return bdrv_flush(bs->file->bs);
}
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]);
+ bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
}
}
}
for (i = 0; i < tb->size; ++i) {
- be64_to_cpus(&table[i]);
+ table[i] = be64_to_cpu(table[i]);
ret = check_table_entry(table[i], s->cluster_size);
if (ret < 0) {
goto fail;
ret = bitmap_table_load(bs, tb, &bitmap_table);
if (ret < 0) {
- assert(bitmap_table == NULL);
return ret;
}
return 0;
}
-/* This function returns the number of disk sectors covered by a single qcow2
- * cluster of bitmap data. */
-static uint64_t sectors_covered_by_bitmap_cluster(const BDRVQcow2State *s,
- const BdrvDirtyBitmap *bitmap)
+/* Return the disk size covered by a single qcow2 cluster of bitmap data. */
+static uint64_t bytes_covered_by_bitmap_cluster(const BDRVQcow2State *s,
+ const BdrvDirtyBitmap *bitmap)
{
- uint32_t sector_granularity =
- bdrv_dirty_bitmap_granularity(bitmap) >> BDRV_SECTOR_BITS;
+ uint64_t granularity = bdrv_dirty_bitmap_granularity(bitmap);
+ uint64_t limit = granularity * (s->cluster_size << 3);
- return (uint64_t)sector_granularity * (s->cluster_size << 3);
+ assert(QEMU_IS_ALIGNED(limit,
+ bdrv_dirty_bitmap_serialization_align(bitmap)));
+ return limit;
}
/* load_bitmap_data
{
int ret = 0;
BDRVQcow2State *s = bs->opaque;
- uint64_t sector, sbc;
+ uint64_t offset, limit;
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
uint8_t *buf = NULL;
uint64_t i, tab_size =
}
buf = g_malloc(s->cluster_size);
- sbc = sectors_covered_by_bitmap_cluster(s, bitmap);
- for (i = 0, sector = 0; i < tab_size; ++i, sector += sbc) {
- uint64_t count = MIN(bm_size - sector, sbc);
+ limit = bytes_covered_by_bitmap_cluster(s, bitmap);
+ for (i = 0, offset = 0; i < tab_size; ++i, offset += limit) {
+ uint64_t count = MIN(bm_size - offset, limit);
uint64_t entry = bitmap_table[i];
- uint64_t offset = entry & BME_TABLE_ENTRY_OFFSET_MASK;
+ uint64_t data_offset = entry & BME_TABLE_ENTRY_OFFSET_MASK;
assert(check_table_entry(entry, s->cluster_size) == 0);
- if (offset == 0) {
+ if (data_offset == 0) {
if (entry & BME_TABLE_ENTRY_FLAG_ALL_ONES) {
- bdrv_dirty_bitmap_deserialize_ones(bitmap, sector, count,
+ bdrv_dirty_bitmap_deserialize_ones(bitmap, offset, count,
false);
} else {
/* No need to deserialize zeros because the dirty bitmap is
* already cleared */
}
} else {
- ret = bdrv_pread(bs->file, offset, buf, s->cluster_size);
+ ret = bdrv_pread(bs->file, data_offset, buf, s->cluster_size);
if (ret < 0) {
goto finish;
}
- bdrv_dirty_bitmap_deserialize_part(bitmap, buf, sector, count,
+ bdrv_dirty_bitmap_deserialize_part(bitmap, buf, offset, count,
false);
}
}
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
{
- be64_to_cpus(&entry->bitmap_table_offset);
- be32_to_cpus(&entry->bitmap_table_size);
- be32_to_cpus(&entry->flags);
- be16_to_cpus(&entry->name_size);
- be32_to_cpus(&entry->extra_data_size);
+ entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
+ entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
+ entry->flags = be32_to_cpu(entry->flags);
+ entry->name_size = be16_to_cpu(entry->name_size);
+ entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
}
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
{
- cpu_to_be64s(&entry->bitmap_table_offset);
- cpu_to_be32s(&entry->bitmap_table_size);
- cpu_to_be32s(&entry->flags);
- cpu_to_be16s(&entry->name_size);
- cpu_to_be32s(&entry->extra_data_size);
+ entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
+ entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
+ entry->flags = cpu_to_be32(entry->flags);
+ entry->name_size = cpu_to_be16(entry->name_size);
+ entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
}
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
{
- return align_offset(sizeof(Qcow2BitmapDirEntry) +
- name_size + extra_data_size, 8);
+ int size = sizeof(Qcow2BitmapDirEntry) + name_size + extra_data_size;
+ return ROUND_UP(size, 8);
}
static inline int dir_entry_size(Qcow2BitmapDirEntry *entry)
}
}
- ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size);
+ /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
+ * necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
+ * directory in-place (actually, turn-off the extension), which is checked
+ * in qcow2_check_metadata_overlap() */
+ ret = qcow2_pre_write_overlap_check(
+ bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size,
+ false);
if (ret < 0) {
goto fail;
}
return ret;
}
- ret = bdrv_flush(bs->file->bs);
+ ret = qcow2_flush_caches(bs);
if (ret < 0) {
goto fail;
}
bdrv_dirty_bitmap_set_readonly(bitmap, (bool)value);
}
-/* qcow2_load_autoloading_dirty_bitmaps()
+/* qcow2_load_dirty_bitmaps()
* Return value is a hint for caller: true means that the Qcow2 header was
* updated. (false doesn't mean that the header should be updated by the
* caller, it just means that updating was not needed or the image cannot be
* written to).
* On failure the function returns false.
*/
-bool qcow2_load_autoloading_dirty_bitmaps(BlockDriverState *bs, Error **errp)
+bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp)
{
BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list;
}
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
- if ((bm->flags & BME_FLAG_AUTO) && !(bm->flags & BME_FLAG_IN_USE)) {
+ if (!(bm->flags & BME_FLAG_IN_USE)) {
BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp);
if (bitmap == NULL) {
goto fail;
}
+ if (!(bm->flags & BME_FLAG_AUTO)) {
+ bdrv_disable_dirty_bitmap(bitmap);
+ }
bdrv_dirty_bitmap_set_persistance(bitmap, true);
- bdrv_dirty_bitmap_set_autoload(bitmap, true);
bm->flags |= BME_FLAG_IN_USE;
created_dirty_bitmaps =
g_slist_append(created_dirty_bitmaps, bitmap);
return false;
}
-int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
+
+static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags)
+{
+ Qcow2BitmapInfoFlagsList *list = NULL;
+ Qcow2BitmapInfoFlagsList **plist = &list;
+ int i;
+
+ static const struct {
+ int bme; /* Bitmap directory entry flags */
+ int info; /* The flags to report to the user */
+ } map[] = {
+ { BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE },
+ { BME_FLAG_AUTO, QCOW2_BITMAP_INFO_FLAGS_AUTO },
+ };
+
+ int map_size = ARRAY_SIZE(map);
+
+ for (i = 0; i < map_size; ++i) {
+ if (flags & map[i].bme) {
+ Qcow2BitmapInfoFlagsList *entry =
+ g_new0(Qcow2BitmapInfoFlagsList, 1);
+ entry->value = map[i].info;
+ *plist = entry;
+ plist = &entry->next;
+ flags &= ~map[i].bme;
+ }
+ }
+ /* Check if the BME_* mapping above is complete */
+ assert(!flags);
+
+ return list;
+}
+
+/*
+ * qcow2_get_bitmap_info_list()
+ * Returns a list of QCOW2 bitmap details.
+ * In case of no bitmaps, the function returns NULL and
+ * the @errp parameter is not set.
+ * When bitmap information can not be obtained, the function returns
+ * NULL and the @errp parameter is set.
+ */
+Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs,
+ Error **errp)
+{
+ BDRVQcow2State *s = bs->opaque;
+ Qcow2BitmapList *bm_list;
+ Qcow2Bitmap *bm;
+ Qcow2BitmapInfoList *list = NULL;
+ Qcow2BitmapInfoList **plist = &list;
+
+ if (s->nb_bitmaps == 0) {
+ return NULL;
+ }
+
+ bm_list = bitmap_list_load(bs, s->bitmap_directory_offset,
+ s->bitmap_directory_size, errp);
+ if (bm_list == NULL) {
+ return NULL;
+ }
+
+ QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+ Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1);
+ Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1);
+ info->granularity = 1U << bm->granularity_bits;
+ info->name = g_strdup(bm->name);
+ info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS);
+ obj->value = info;
+ *plist = obj;
+ plist = &obj->next;
+ }
+
+ bitmap_list_free(bm_list);
+
+ return list;
+}
+
+int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated,
+ Error **errp)
{
BDRVQcow2State *s = bs->opaque;
Qcow2BitmapList *bm_list;
GSList *ro_dirty_bitmaps = NULL;
int ret = 0;
+ if (header_updated != NULL) {
+ *header_updated = false;
+ }
+
if (s->nb_bitmaps == 0) {
/* No bitmaps - nothing to do */
return 0;
error_setg_errno(errp, -ret, "Can't update bitmap directory");
goto out;
}
+ if (header_updated != NULL) {
+ *header_updated = true;
+ }
g_slist_foreach(ro_dirty_bitmaps, set_readonly_helper, false);
}
return ret;
}
+int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp)
+{
+ return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp);
+}
+
/* store_bitmap_data()
* Store bitmap to image, filling bitmap table accordingly.
*/
{
int ret;
BDRVQcow2State *s = bs->opaque;
- int64_t sector;
- uint64_t sbc;
+ int64_t offset;
+ uint64_t limit;
uint64_t bm_size = bdrv_dirty_bitmap_size(bitmap);
const char *bm_name = bdrv_dirty_bitmap_name(bitmap);
uint8_t *buf = NULL;
return NULL;
}
- dbi = bdrv_dirty_iter_new(bitmap, 0);
+ dbi = bdrv_dirty_iter_new(bitmap);
buf = g_malloc(s->cluster_size);
- sbc = sectors_covered_by_bitmap_cluster(s, bitmap);
- assert(DIV_ROUND_UP(bm_size, sbc) == tb_size);
+ limit = bytes_covered_by_bitmap_cluster(s, bitmap);
+ assert(DIV_ROUND_UP(bm_size, limit) == tb_size);
- while ((sector = bdrv_dirty_iter_next(dbi)) != -1) {
- uint64_t cluster = sector / sbc;
+ while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
+ uint64_t cluster = offset / limit;
uint64_t end, write_size;
int64_t off;
- sector = cluster * sbc;
- end = MIN(bm_size, sector + sbc);
- write_size =
- bdrv_dirty_bitmap_serialization_size(bitmap, sector, end - sector);
+ /*
+ * We found the first dirty offset, but want to write out the
+ * entire cluster of the bitmap that includes that offset,
+ * including any leading zero bits.
+ */
+ offset = QEMU_ALIGN_DOWN(offset, limit);
+ end = MIN(bm_size, offset + limit);
+ write_size = bdrv_dirty_bitmap_serialization_size(bitmap, offset,
+ end - offset);
assert(write_size <= s->cluster_size);
off = qcow2_alloc_clusters(bs, s->cluster_size);
}
tb[cluster] = off;
- bdrv_dirty_bitmap_serialize_part(bitmap, buf, sector, end - sector);
+ bdrv_dirty_bitmap_serialize_part(bitmap, buf, offset, end - offset);
if (write_size < s->cluster_size) {
memset(buf + write_size, 0, s->cluster_size - write_size);
}
- ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size);
+ ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
goto fail;
}
ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset,
- tb_size * sizeof(tb[0]));
+ tb_size * sizeof(tb[0]), false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Qcow2 overlap check failed");
goto fail;
int ret;
Qcow2BitmapList *bm_list;
Qcow2Bitmap *bm;
- Qcow2BitmapTableList drop_tables;
+ QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables;
Qcow2BitmapTable *tb, *tb_next;
if (!bdrv_has_changed_persistent_bitmaps(bs)) {
bm->table.size = 0;
QSIMPLEQ_INSERT_TAIL(&drop_tables, tb, entry);
}
- bm->flags = bdrv_dirty_bitmap_get_autoload(bitmap) ? BME_FLAG_AUTO : 0;
+ bm->flags = bdrv_dirty_bitmap_enabled(bitmap) ? BME_FLAG_AUTO : 0;
bm->granularity_bits = ctz32(bdrv_dirty_bitmap_granularity(bitmap));
bm->dirty_bitmap = bitmap;
}
g_free(tb);
}
+ QSIMPLEQ_FOREACH(bm, bm_list, entry) {
+ /* For safety, we remove bitmap after storing.
+ * We may be here in two cases:
+ * 1. bdrv_close. It's ok to drop bitmap.
+ * 2. inactivation. It means migration without 'dirty-bitmaps'
+ * capability, so bitmaps are not marked with
+ * BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
+ * and reload on invalidation.
+ */
+ if (bm->dirty_bitmap == NULL) {
+ continue;
+ }
+
+ bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
+ }
+
bitmap_list_free(bm_list);
return;
bool found;
Qcow2BitmapList *bm_list;
+ if (s->qcow_version < 3) {
+ /* Without autoclear_features, we would always have to assume
+ * that a program without persistent dirty bitmap support has
+ * accessed this qcow2 file when opening it, and would thus
+ * have to drop all dirty bitmaps (defeating their purpose).
+ */
+ error_setg(errp, "Cannot store dirty bitmaps in qcow2 v2 files");
+ goto fail;
+ }
+
if (check_constraints_on_bitmap(bs, name, granularity, errp) != 0) {
goto fail;
}