
On 06/10/2013 02:10 PM, Ján Tomko wrote:
Detect qcow2 images with version 3 in the image header as VIR_STORAGE_FILE_QCOW2.
These images have a feature bitfield, with just one feature supported so far: lazy_refcounts.
The header length changed too, moving the location of the backing format name. --- src/util/virstoragefile.c | 164 +++++++++++++++++++++++++++++++++++----------- src/util/virstoragefile.h | 12 ++++ 2 files changed, 139 insertions(+), 37 deletions(-)
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index a391738..f30324e 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -59,6 +59,11 @@ VIR_ENUM_IMPL(virStorageFileFormat, "qcow", "qcow2", "qed", "vmdk", "vpc", "fat", "vhd", "vdi")
+VIR_ENUM_IMPL(virStorageFileFeature, + VIR_STORAGE_FILE_FEATURE_LAST, + "lazy_refcounts", + ) + enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ LV_BIG_ENDIAN /* 4321 */ @@ -81,7 +86,7 @@ struct FileTypeInfo { * where we find version number, * -1 to always fail the version test, * -2 to always pass the version test */ - int versionNumber; /* Version number to validate */ + int versionNumbers[3]; /* Version numbers to validate, terminated by 0 */
There could be a constant for this, which will be increased in case of need and will appear on some more readable place.
int sizeOffset; /* Byte offset from start of file * where we find capacity info, * -1 to use st_size as capacity */ @@ -95,6 +100,8 @@ struct FileTypeInfo { * -1 if encryption is not used */ int (*getBackingStore)(char **res, int *format, const unsigned char *buf, size_t buf_size); + int (*getFeatures)(virBitmapPtr *features, int format, + unsigned char *buf, ssize_t len); };
static int cowGetBackingStore(char **, int *, @@ -103,6 +110,8 @@ static int qcow1GetBackingStore(char **, int *, const unsigned char *, size_t); static int qcow2GetBackingStore(char **, int *, const unsigned char *, size_t); +static int qcow2GetFeatures(virBitmapPtr *features, int format, + unsigned char *buf, ssize_t len); static int vmdk4GetBackingStore(char **, int *, const unsigned char *, size_t); static int @@ -122,6 +131,13 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t); #define QCOW2_HDR_EXTENSION_END 0 #define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
+#define QCOW2v3_HDR_FEATURES_INCOMPATIBLE (QCOW2_HDR_TOTAL_SIZE) +#define QCOW2v3_HDR_FEATURES_COMPATIBLE (QCOW2v3_HDR_FEATURES_INCOMPATIBLE+8) +#define QCOW2v3_HDR_FEATURES_AUTOCLEAR (QCOW2v3_HDR_FEATURES_COMPATIBLE+8) + +/* The location of the header size [4 bytes] */ +#define QCOW2v3_HDR_SIZE (QCOW2_HDR_TOTAL_SIZE+8+8+8+4) +
I must admit I'm not very fond of the naming (qcow2v3), but since this is an implementation detail and makes sense, let's go with it.
#define QED_HDR_FEATURES_OFFSET (4+4+4+4) #define QED_HDR_IMAGE_SIZE (QED_HDR_FEATURES_OFFSET+8+8+8+8) #define QED_HDR_BACKING_FILE_OFFSET (QED_HDR_IMAGE_SIZE+8) @@ -137,16 +153,16 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t);
static struct FileTypeInfo const fileTypeInfo[] = { [VIR_STORAGE_FILE_NONE] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, 0, 0, 0, 0, 0, NULL }, + -1, {0}, 0, 0, 0, 0, NULL, NULL }, [VIR_STORAGE_FILE_RAW] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, 0, 0, 0, 0, 0, NULL }, + -1, {0}, 0, 0, 0, 0, NULL, NULL }, [VIR_STORAGE_FILE_DIR] = { 0, NULL, NULL, LV_LITTLE_ENDIAN, - -1, 0, 0, 0, 0, 0, NULL }, + -1, {0}, 0, 0, 0, 0, NULL, NULL }, [VIR_STORAGE_FILE_BOCHS] = { /*"Bochs Virtual HD Image", */ /* Untested */ 0, NULL, NULL, - LV_LITTLE_ENDIAN, 64, 0x20000, - 32+16+16+4+4+4+4+4, 8, 1, -1, NULL + LV_LITTLE_ENDIAN, 64, {0x20000, 0},
Good you added the trailing zero, in case some format goes to 3 version numbers, compiler will shout and we won't get to segfauls'n'stuff. Took me a while to appreciate this ;) But... [...]
+/* qcow2 compatible features in the order they appear on-disk */ +enum qcow2CompatibleFeature { + QCOW2_COMPATIBLE_FEATURE_LAZY_REFCOUNTS = 0, + + QCOW2_COMPATIBLE_FEATURE_LAST +}; + +/* conversion to virStorageFeatures */
Did you mean virStorageFileFeature?
+static int qcow2CompatibleFeatureArray[] = {
s/int/const int/ maybe ? [...]
@@ -611,12 +653,14 @@ virStorageFileMatchesVersion(int format, else version = virReadBufInt32BE(buf + fileTypeInfo[format].versionOffset);
- VIR_DEBUG("Compare detected version %d vs expected version %d", - version, fileTypeInfo[format].versionNumber); - if (version != fileTypeInfo[format].versionNumber) - return false; + for (i = 0; fileTypeInfo[format].versionNumbers[i] != 0; i++) { + VIR_DEBUG("Compare detected version %d vs expected version %d",
Changing the 'expected version' to something like 'one of the expected versions' could make users less confused when going through the logs, but not required.
+ version, fileTypeInfo[format].versionNumbers[i]); + if (version == fileTypeInfo[format].versionNumbers[i]) + return true; + }
... the constant mentioned earlier could be checked upon in here as well and that would make the format specification 1) safer 2) shorter (not much, though). [...]
@@ -669,6 +713,42 @@ cleanup: }
+static int +qcow2GetFeatures(virBitmapPtr *features, + int format, + unsigned char *buf, + ssize_t len)
indentation [...] ACK with: - the constant used instead of magic '3' - const int - indentation Martin