Add new 'qcow3' format, and support for detecting the backing file and qcow3 features in the header (just one so far). --- docs/schemas/storagevol.rng | 1 + src/storage/storage_backend_fs.c | 1 + src/util/virstoragefile.c | 101 +++++++++++++++++++++++++++++++++++---- src/util/virstoragefile.h | 11 +++++ 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/docs/schemas/storagevol.rng b/docs/schemas/storagevol.rng index 10b7847..653491d 100644 --- a/docs/schemas/storagevol.rng +++ b/docs/schemas/storagevol.rng @@ -190,6 +190,7 @@ <value>iso</value> <value>qcow</value> <value>qcow2</value> + <value>qcow3</value> <value>qed</value> <value>vmdk</value> <value>vpc</value> diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index a582804..f356173 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -136,6 +136,7 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, switch (target->format) { case VIR_STORAGE_FILE_QCOW: case VIR_STORAGE_FILE_QCOW2: + case VIR_STORAGE_FILE_QCOW3: (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; break; default: diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index 9e50ccb..c74a8fd 100644 --- a/src/util/virstoragefile.c +++ b/src/util/virstoragefile.c @@ -36,6 +36,7 @@ #endif #include "dirname.h" #include "viralloc.h" +#include "virbitmap.h" #include "virerror.h" #include "virlog.h" #include "virfile.h" @@ -50,9 +51,13 @@ VIR_ENUM_IMPL(virStorageFileFormat, "none", "raw", "dir", "bochs", "cloop", "cow", "dmg", "iso", - "qcow", "qcow2", "qed", "vmdk", "vpc", + "qcow", "qcow2", "qcow3", "qed", "vmdk", "vpc", "fat", "vhd", "vdi") +VIR_ENUM_IMPL(virStorageFileFeaturesQcow3, + VIR_STORAGE_FILE_FEAT_QCOW3_LAST, + "lazy_refcounts") + enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ LV_BIG_ENDIAN /* 4321 */ @@ -96,6 +101,8 @@ static int qcow1GetBackingStore(char **, int *, const unsigned char *, size_t); static int qcow2GetBackingStore(char **, int *, const unsigned char *, size_t); +static int qcow3GetBackingStore(char **, int *, + const unsigned char *, size_t); static int vmdk4GetBackingStore(char **, int *, const unsigned char *, size_t); static int @@ -111,6 +118,12 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t); #define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8) #define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8) +#define QCOW3_HDR_FEATURES_INCOMPATIBLE (QCOW2_HDR_TOTAL_SIZE) +#define QCOW3_HDR_FEATURES_COMPATIBLE (QCOW3_HDR_FEATURES_INCOMPATIBLE+8) +#define QCOW3_HDR_FEATURES_AUTOCLEAR (QCOW3_HDR_FEATURES_COMPATIBLE+8) + +/* The location of the header size [4 bytes] */ +#define QCOW3_HDR_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8+8+8+8+4) #define QCOW2_HDR_EXTENSION_END 0 #define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA @@ -178,6 +191,11 @@ static struct FileTypeInfo const fileTypeInfo[] = { LV_BIG_ENDIAN, 4, 2, QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow2GetBackingStore, }, + [VIR_STORAGE_FILE_QCOW3] = { + 0, "QFI", NULL, + LV_BIG_ENDIAN, 4, 3, + QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow3GetBackingStore, + }, [VIR_STORAGE_FILE_QED] = { /* http://wiki.qemu.org/Features/QED */ 0, "QED", NULL, @@ -209,6 +227,19 @@ static struct FileTypeInfo const fileTypeInfo[] = { verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST); +enum Qcow3CompatibleFeatures { + QCOW3_COMPATIBLE_LAZY_REFCOUNTS, + + QCOW3_COMPATIBLE_LAST, +}; + +/* Translation from qcow3 feature bits to enum VirStorageFileFeaturesQcow3 */ +int Qcow3CompatibleFeaturesArray [] = { + VIR_STORAGE_FILE_FEAT_QCOW3_LAZY_REFCOUNTS, /* QCOW3_COMPATIBLE_LAZY_REFCOUNTS */ +}; +verify(ARRAY_CARDINALITY(Qcow3CompatibleFeaturesArray) == QCOW3_COMPATIBLE_LAST); + + static uint64_t getBEHeader64(const unsigned char *buf) { @@ -334,14 +365,37 @@ done: static int +qcow3GetFeatures(virBitmapPtr features, + const unsigned char *buf, + size_t buf_size) +{ + uint64_t bits; + int i; + + if (buf_size < QCOW3_HDR_SIZE) + return -1; + + bits = getBEHeader64(buf + QCOW3_HDR_FEATURES_COMPATIBLE); + for (i = 0; i < QCOW3_COMPATIBLE_LAST; i++) { + if (Qcow3CompatibleFeaturesArray[i] >= 0) + if (bits & ((uint64_t) 1 << i)) + if (virBitmapSetBit(features, + Qcow3CompatibleFeaturesArray[i]) < 0) + return -1; + } + return 0; +} + +static int qcowXGetBackingStore(char **res, int *format, const unsigned char *buf, size_t buf_size, - bool isQCow2) + unsigned int version) { unsigned long long offset; unsigned int size; + unsigned long hdr_size; *res = NULL; if (format) @@ -394,11 +448,17 @@ qcowXGetBackingStore(char **res, * Thus the file region to search for extensions is * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) + * + * QCow3 files can have a variable header length, it's stored at + * QCOW3_HDR_SIZE. */ - if (isQCow2 && format && - qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, - offset) < 0) - return BACKING_STORE_INVALID; + if (format && (version == 2 || version == 3)) { + hdr_size = (version == 2) ? QCOW2_HDR_TOTAL_SIZE + : getBEHeader32(buf + QCOW3_HDR_SIZE); + if (qcow2GetBackingStoreFormat(format, buf, buf_size, hdr_size, + offset) < 0) + return BACKING_STORE_INVALID; + } return BACKING_STORE_OK; } @@ -415,7 +475,7 @@ qcow1GetBackingStore(char **res, /* QCow1 doesn't have the extensions capability * used to store backing format */ *format = VIR_STORAGE_FILE_AUTO; - ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false); + ret = qcowXGetBackingStore(res, NULL, buf, buf_size, 1); if (ret == 0 && *buf == '\0') *format = VIR_STORAGE_FILE_NONE; return ret; @@ -427,7 +487,16 @@ qcow2GetBackingStore(char **res, const unsigned char *buf, size_t buf_size) { - return qcowXGetBackingStore(res, format, buf, buf_size, true); + return qcowXGetBackingStore(res, format, buf, buf_size, 2); +} + +static int +qcow3GetBackingStore(char **res, + int *format, + const unsigned char *buf, + size_t buf_size) +{ + return qcowXGetBackingStore(res, format, buf, buf_size, 3); } @@ -748,7 +817,22 @@ virStorageFileGetMetadataFromBuf(int format, } } + if (format == VIR_STORAGE_FILE_QCOW3) { + if (!(meta->features = virBitmapNew(VIR_STORAGE_FILE_FEAT_QCOW3_LAST))) + goto no_memory; + + if (qcow3GetFeatures(meta->features, buf, buflen) < 0) + goto error; + } + return 0; + +no_memory: + virReportOOMError(); +error: + VIR_FREE(meta->backingStore); + virBitmapFree(meta->features); + return -1; } @@ -1063,6 +1147,7 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) return; virStorageFileFreeMetadata(meta->backingMeta); + virBitmapFree(meta->features); VIR_FREE(meta->backingStore); VIR_FREE(meta->backingStoreRaw); VIR_FREE(meta); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 821d07e..88b3b6f 100644 --- a/src/util/virstoragefile.h +++ b/src/util/virstoragefile.h @@ -24,6 +24,7 @@ #ifndef __VIR_STORAGE_FILE_H__ # define __VIR_STORAGE_FILE_H__ +# include "virbitmap.h" # include "virutil.h" enum virStorageFileFormat { @@ -39,6 +40,7 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_ISO, VIR_STORAGE_FILE_QCOW, VIR_STORAGE_FILE_QCOW2, + VIR_STORAGE_FILE_QCOW3, VIR_STORAGE_FILE_QED, VIR_STORAGE_FILE_VMDK, VIR_STORAGE_FILE_VPC, @@ -51,6 +53,14 @@ enum virStorageFileFormat { VIR_ENUM_DECL(virStorageFileFormat); +enum virStorageFileFeaturesQcow3 { + VIR_STORAGE_FILE_FEAT_QCOW3_NONE = -1, + VIR_STORAGE_FILE_FEAT_QCOW3_LAZY_REFCOUNTS = 0, + + VIR_STORAGE_FILE_FEAT_QCOW3_LAST, +}; +VIR_ENUM_DECL(virStorageFileFeaturesQcow3); + typedef struct _virStorageFileMetadata virStorageFileMetadata; typedef virStorageFileMetadata *virStorageFileMetadataPtr; struct _virStorageFileMetadata { @@ -61,6 +71,7 @@ struct _virStorageFileMetadata { virStorageFileMetadataPtr backingMeta; unsigned long long capacity; bool encrypted; + virBitmapPtr features; }; # ifndef DEV_BSIZE -- 1.7.12.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list