On 11/09/2016 01:17 PM, Vladimir Sementsov-Ogievskiy wrote:
Add dirty bitmap extension as specified in docs/specs/qcow2.txt.
For now, just mirror extension header into Qcow2 state and check
constraints.
For now, disable image resize if it has bitmaps. It will be fixed later.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com>
---
block/qcow2.c | 101
++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
block/qcow2.h | 24 ++++++++++++++
2 files changed, 123 insertions(+), 2 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index 6d5689a..c6050de 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -63,6 +63,7 @@ typedef struct {
#define QCOW2_EXT_MAGIC_END 0
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
+#define QCOW2_EXT_MAGIC_DIRTY_BITMAPS 0x23852875
static int qcow2_probe(const uint8_t *buf, int buf_size, const char
*filename)
{
@@ -92,6 +93,7 @@ static int qcow2_read_extensions(BlockDriverState
*bs, uint64_t start_offset,
QCowExtension ext;
uint64_t offset;
int ret;
+ Qcow2BitmapHeaderExt bitmaps_ext;
#ifdef DEBUG_EXT
printf("qcow2_read_extensions: start=%ld end=%ld\n",
start_offset, end_offset);
Unrelated, the comment at the top of qcow2_read_extensions is sorely
lacking.
@@ -162,6 +164,75 @@ static int
qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
}
break;
+ case QCOW2_EXT_MAGIC_DIRTY_BITMAPS:
+ if (ext.len != sizeof(bitmaps_ext)) {
+ error_setg_errno(errp, -ret, "ERROR: bitmaps_ext: "
+ "Invalid extension length");
+ return -EINVAL;
+ }
+
+ if (!(s->autoclear_features &
QCOW2_AUTOCLEAR_DIRTY_BITMAPS)) {
+ fprintf(stderr,
+ "WARNING: bitmaps_ext: autoclear flag is not "
+ "set, all bitmaps will be considered as
inconsistent");
+ break;
+ }
+
I might drop the "as" and just say "WARNING: bitmaps_ext: [the]
autoclear flag is not set. All bitmaps will be considered inconsistent."
This may be a good place for Eric to check our English.
+ ret = bdrv_pread(bs->file, offset, &bitmaps_ext, ext.len);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret, "ERROR: bitmaps_ext: "
+ "Could not read ext header");
+ return ret;
+ }
+
+ if (bitmaps_ext.reserved32 != 0) {
+ error_setg_errno(errp, -ret, "ERROR: bitmaps_ext: "
+ "Reserved field is not zero");
+ return -EINVAL;
+ }
+
+ be32_to_cpus(&bitmaps_ext.nb_bitmaps);
+ be64_to_cpus(&bitmaps_ext.bitmap_directory_size);
+ be64_to_cpus(&bitmaps_ext.bitmap_directory_offset);
+
+ if (bitmaps_ext.nb_bitmaps > QCOW_MAX_DIRTY_BITMAPS) {
+ error_setg(errp, "ERROR: bitmaps_ext: "
+ "too many dirty bitmaps");
I might opt for something more like "File %s has %d bitmaps, exceeding
the QEMU supported maximum of %d" to be a little more informative than
"too many." (How many is too many? How many do we have?)
+ return -EINVAL;
+ }
+
+ if (bitmaps_ext.nb_bitmaps == 0) {
+ error_setg(errp, "ERROR: bitmaps_ext: "
+ "found bitmaps extension with zero
bitmaps");
+ return -EINVAL;
+ }
+
+ if (bitmaps_ext.bitmap_directory_offset &
(s->cluster_size - 1)) {
+ error_setg(errp, "ERROR: bitmaps_ext: "
+ "invalid dirty bitmap directory
offset");
+ return -EINVAL;
+ }
+
Don't we have a helper for checking alignment? We've got
"validate_table_offset." Can we use that?
+ if (bitmaps_ext.bitmap_directory_size >
+ QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE) {
+ error_setg(errp, "ERROR: bitmaps_ext: "
+ "too large dirty bitmap directory");
+ return -EINVAL;
+ }
+
"Too large dirty bitmap" is an awkward phrasing, because it turns the
entire message into a large compound noun.
I suggest working in a verb into the message, like "is" or "exceeds,"
here are some suggestions:
"[the] dirty bitmap directory size is too large" or "[the] dirty
bitmap directory size (%zu) exceeds [the] maximum supported size (%zu)"
I can't decide if it's appropriate to include or exclude the article.
Luckily nobody else knows how English works either.
+ s->nb_bitmaps = bitmaps_ext.nb_bitmaps;
+ s->bitmap_directory_offset =
+ bitmaps_ext.bitmap_directory_offset;
+ s->bitmap_directory_size =
+ bitmaps_ext.bitmap_directory_size;
+
+#ifdef DEBUG_EXT
+ printf("Qcow2: Got dirty bitmaps extension:"
+ " offset=%" PRIu64 " nb_bitmaps=%" PRIu32 "\n",
+ s->bitmap_directory_offset, s->nb_bitmaps);
+#endif
+ break;
+
default:
/* unknown magic - save it in case we need to rewrite
the header */
{
@@ -1144,8 +1215,9 @@ static int qcow2_open(BlockDriverState *bs,
QDict *options, int flags,
}
/* Clear unknown autoclear feature bits */
- if (!bs->read_only && !(flags & BDRV_O_INACTIVE) &&
s->autoclear_features) {
- s->autoclear_features = 0;
+ if (!bs->read_only && !(flags & BDRV_O_INACTIVE) &&
+ (s->autoclear_features & ~QCOW2_AUTOCLEAR_MASK)) {
+ s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
ret = qcow2_update_header(bs);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not update qcow2
header");
@@ -1944,6 +2016,24 @@ int qcow2_update_header(BlockDriverState *bs)
buflen -= ret;
}
+ if (s->nb_bitmaps > 0) {
+ Qcow2BitmapHeaderExt bitmaps_header = {
+ .nb_bitmaps = cpu_to_be32(s->nb_bitmaps),
+ .bitmap_directory_size =
+ cpu_to_be64(s->bitmap_directory_size),
+ .bitmap_directory_offset =
+ cpu_to_be64(s->bitmap_directory_offset)
+ };
+ ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DIRTY_BITMAPS,
+ &bitmaps_header, sizeof(bitmaps_header),
+ buflen);
+ if (ret < 0) {
+ goto fail;
+ }
+ buf += ret;
+ buflen -= ret;
+ }
+
/* Keep unknown header extensions */
QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
ret = header_ext_add(buf, uext->magic, uext->data,
uext->len, buflen);
@@ -2514,6 +2604,13 @@ static int qcow2_truncate(BlockDriverState
*bs, int64_t offset)
return -ENOTSUP;
}
+ /* cannot proceed if image has bitmaps */
+ if (s->nb_bitmaps) {
+ /* TODO: resize bitmaps in the image */
+ error_report("Can't resize an image which has bitmaps");
+ return -ENOTSUP;
+ }
+
I guess this is an open problem and not one we address in this series.
/* shrinking is currently not supported */
if (offset < bs->total_sectors * 512) {
error_report("qcow2 doesn't support shrinking images yet");
diff --git a/block/qcow2.h b/block/qcow2.h
index 92203a8..be9ba32 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -52,6 +52,10 @@
* space for snapshot names and IDs */
#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
+/* Bitmap header extension constraints */
+#define QCOW_MAX_DIRTY_BITMAPS 65535
+#define QCOW_MAX_DIRTY_BITMAP_DIRECTORY_SIZE (1024 *
QCOW_MAX_DIRTY_BITMAPS)
+
64MiB for just the headers seems like a really generous limit that we
will never, ever, ever exhaust.
(I look forward to future versions of QEMU where this feedback item is
laughed at playfully.)
/* indicate that the refcount of the referenced cluster is exactly
one. */
#define QCOW_OFLAG_COPIED (1ULL << 63)
/* indicate that the cluster is compressed (they never have the
copied flag) */
@@ -195,6 +199,15 @@ enum {
QCOW2_COMPAT_FEAT_MASK = QCOW2_COMPAT_LAZY_REFCOUNTS,
};
+/* Autoclear feature bits */
+enum {
+ QCOW2_AUTOCLEAR_DIRTY_BITMAPS_BITNR = 0,
+ QCOW2_AUTOCLEAR_DIRTY_BITMAPS =
+ 1 << QCOW2_AUTOCLEAR_DIRTY_BITMAPS_BITNR,
+
+ QCOW2_AUTOCLEAR_MASK =
QCOW2_AUTOCLEAR_DIRTY_BITMAPS,
+};
+
This seems like a strange construct to me -- the ranges for _BITNR and
mask variants will begin colliding as soon as we add just one more
constant to the list.
Enumerations do allow this, but I generally don't like using a single
enumeration to describe more than one thing.
Do we really need the BITNR variants at all? Why not the following:
enum {
QCOW2_AUTOCLEAR_DIRTY_BITMAPS = 1 << 0,
};
#define QCOW2_AUTOCLEAR_MASK QCOW2_AUTOCLEAR_DIRTY_BITMAPS
enum qcow2_discard_type {
QCOW2_DISCARD_NEVER = 0,
QCOW2_DISCARD_ALWAYS,
@@ -222,6 +235,13 @@ typedef uint64_t Qcow2GetRefcountFunc(const void
*refcount_array,
typedef void Qcow2SetRefcountFunc(void *refcount_array,
uint64_t index, uint64_t value);
+typedef struct Qcow2BitmapHeaderExt {
+ uint32_t nb_bitmaps;
+ uint32_t reserved32;
+ uint64_t bitmap_directory_size;
+ uint64_t bitmap_directory_offset;
+} QEMU_PACKED Qcow2BitmapHeaderExt;
+
typedef struct BDRVQcow2State {
int cluster_bits;
int cluster_size;
@@ -263,6 +283,10 @@ typedef struct BDRVQcow2State {
unsigned int nb_snapshots;
QCowSnapshot *snapshots;
+ uint32_t nb_bitmaps;
+ uint64_t bitmap_directory_size;
+ uint64_t bitmap_directory_offset;
+
int flags;
int qcow_version;
bool use_lazy_refcounts;
Only style issues from my POV...
Reviewed-by: John Snow <js...@redhat.com>