From c2c9a46609164a36b477f6cff1d10ed27a6b53fc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 16 Nov 2011 11:35:54 +0100 Subject: [PATCH] qcow2: Allow >4 GB VM state This is a compatible extension to the snapshot header format that allows saving a 64 bit VM state size. Signed-off-by: Kevin Wolf --- block.h | 2 +- block/qcow2-snapshot.c | 34 ++++++++++++++++++++++++++++++++-- block/qcow2.h | 2 +- docs/specs/qcow2.txt | 8 +++++++- savevm.c | 2 +- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/block.h b/block.h index 0e3ff9f23..3bd439860 100644 --- a/block.h +++ b/block.h @@ -22,7 +22,7 @@ typedef struct QEMUSnapshotInfo { /* the following fields are informative. They are not needed for the consistency of the snapshot */ char name[256]; /* user chosen name */ - uint32_t vm_state_size; /* VM state info size */ + uint64_t vm_state_size; /* VM state info size */ uint32_t date_sec; /* UTC date of the snapshot */ uint32_t date_nsec; uint64_t vm_clock_nsec; /* VM clock relative to boot */ diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index c3112bf71..7d3fde5a8 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -46,6 +46,10 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { /* name follows */ } QCowSnapshotHeader; +typedef struct QEMU_PACKED QCowSnapshotExtraData { + uint64_t vm_state_size_large; +} QCowSnapshotExtraData; + void qcow2_free_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -64,6 +68,7 @@ int qcow2_read_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; QCowSnapshotHeader h; + QCowSnapshotExtraData extra; QCowSnapshot *sn; int i, id_str_size, name_size; int64_t offset; @@ -100,9 +105,18 @@ int qcow2_read_snapshots(BlockDriverState *bs) id_str_size = be16_to_cpu(h.id_str_size); name_size = be16_to_cpu(h.name_size); - /* Skip extra data */ + /* Read extra data */ + ret = bdrv_pread(bs->file, offset, &extra, + MIN(sizeof(extra), extra_data_size)); + if (ret < 0) { + goto fail; + } offset += extra_data_size; + if (extra_data_size >= 8) { + sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); + } + /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); @@ -136,6 +150,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; QCowSnapshotHeader h; + QCowSnapshotExtraData extra; int i, name_size, id_str_size, snapshots_size; struct { uint32_t nb_snapshots; @@ -150,6 +165,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) sn = s->snapshots + i; offset = align_offset(offset, 8); offset += sizeof(h); + offset += sizeof(extra); offset += strlen(sn->id_str); offset += strlen(sn->name); } @@ -169,10 +185,18 @@ static int qcow2_write_snapshots(BlockDriverState *bs) memset(&h, 0, sizeof(h)); h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); h.l1_size = cpu_to_be32(sn->l1_size); - h.vm_state_size = cpu_to_be32(sn->vm_state_size); + /* If it doesn't fit in 32 bit, older implementations should treat it + * as a disk-only snapshot rather than truncate the VM state */ + if (sn->vm_state_size <= 0xffffffff) { + h.vm_state_size = cpu_to_be32(sn->vm_state_size); + } h.date_sec = cpu_to_be32(sn->date_sec); h.date_nsec = cpu_to_be32(sn->date_nsec); h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); + h.extra_data_size = cpu_to_be32(sizeof(extra)); + + memset(&extra, 0, sizeof(extra)); + extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); id_str_size = strlen(sn->id_str); name_size = strlen(sn->name); @@ -186,6 +210,12 @@ static int qcow2_write_snapshots(BlockDriverState *bs) } offset += sizeof(h); + ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + if (ret < 0) { + goto fail; + } + offset += sizeof(extra); + ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); if (ret < 0) { goto fail; diff --git a/block/qcow2.h b/block/qcow2.h index 4e44eea5e..99e45361f 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -78,7 +78,7 @@ typedef struct QCowSnapshot { uint32_t l1_size; char *id_str; char *name; - uint32_t vm_state_size; + uint64_t vm_state_size; uint32_t date_sec; uint32_t date_nsec; uint64_t vm_clock_nsec; diff --git a/docs/specs/qcow2.txt b/docs/specs/qcow2.txt index e792953c8..b6adcaddb 100644 --- a/docs/specs/qcow2.txt +++ b/docs/specs/qcow2.txt @@ -253,7 +253,13 @@ Snapshot table entry: 36 - 39: Size of extra data in the table entry (used for future extensions of the format) - variable: Extra data for future extensions. Must be ignored. + variable: Extra data for future extensions. Unknown fields must be + ignored. Currently defined are (offset relative to snapshot + table entry): + + Byte 40 - 47: Size of the VM state in bytes. 0 if no VM + state is saved. If this field is present, + the 32-bit value in bytes 32-35 is ignored. variable: Unique ID string for the snapshot (not null terminated) diff --git a/savevm.c b/savevm.c index b72f6c0d1..f153c2541 100644 --- a/savevm.c +++ b/savevm.c @@ -2002,7 +2002,7 @@ void do_savevm(Monitor *mon, const QDict *qdict) int ret; QEMUFile *f; int saved_vm_running; - uint32_t vm_state_size; + uint64_t vm_state_size; #ifdef _WIN32 struct _timeb tb; struct tm *ptm;