QCOWv3 introduced feature bits in the header, which get stored in virStorageFileFeatures. --- src/util/virstoragefile.c | 108 ++++++++++++++++++++++++++++++++++++++++++-- src/util/virstoragefile.h | 26 +++++++++++ 2 files changed, 129 insertions(+), 5 deletions(-) diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c index c7941c3..e9ecff1 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,17 @@ 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") +VIR_ENUM_IMPL(virStorageFileQcow3Incomp, + VIR_STORAGE_FILE_QCOW3_INCOMP_LAST, + "dirty_refcounts") + +VIR_ENUM_IMPL(virStorageFileQcow3Comp, + VIR_STORAGE_FILE_QCOW3_COMP_LAST, + "lazy_refcounts") + enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ LV_BIG_ENDIAN /* 4321 */ @@ -89,12 +98,15 @@ struct FileTypeInfo { const unsigned char *buf, size_t buf_size); }; +static unsigned long qcow3GetHeaderSize(const unsigned char *, size_t); static int cowGetBackingStore(char **, int *, const unsigned char *, size_t); 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 @@ -110,6 +122,8 @@ 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 (QCOW2_HDR_TOTAL_SIZE) +#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 @@ -172,6 +186,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] = { + "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 */ "QED", NULL, @@ -284,15 +303,53 @@ done: } +static unsigned long +qcow3GetHeaderSize(const unsigned char *buf, + size_t buf_size) +{ + unsigned long ret; + if (buf_size < QCOW3_HDR_SIZE+4) + return 0; + ret = (((unsigned long)buf[QCOW3_HDR_SIZE] << 24) + | ((unsigned long)buf[QCOW3_HDR_SIZE+1] << 16) + | ((unsigned long)buf[QCOW3_HDR_SIZE+2] << 8) + | ((unsigned long)buf[QCOW3_HDR_SIZE+3])); + return ret; +} + +static int +qcow3GetFeatures(virStorageFileFeaturesPtr features, + const unsigned char *buf, + size_t buf_size) +{ + if (buf_size < QCOW3_HDR_SIZE) + return -1; + /* FIXME */ + /* incompatible features */ + if (buf[QCOW3_HDR_FEATURES+7] & (1 << 0)) { + if (virBitmapSetBit(features->incompatible, + VIR_STORAGE_FILE_QCOW3_INCOMP_DIRTY) < 0) + return -1; + } + /* compatible features */ + if (buf[QCOW3_HDR_FEATURES+8+7] & (1 << 0)) { + if (virBitmapSetBit(features->compatible, + VIR_STORAGE_FILE_QCOW3_COMP_LAZY_REFCOUNT) < 0) + return -1; + } + return 0; +} + static int qcowXGetBackingStore(char **res, int *format, const unsigned char *buf, size_t buf_size, - bool isQCow2) + unsigned version) { unsigned long long offset; unsigned int size; + unsigned long header_size; *res = NULL; if (format) @@ -354,10 +411,21 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format && + if (version == 2 && format && qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, offset) < 0) return BACKING_STORE_INVALID; + /* + * QCow3 files can have a variable header length, it's stored at + * QCOW3_HDR_SIZE. + */ + if (version == 3 && format) { + header_size = qcow3GetHeaderSize(buf, buf_size); + if (!header_size || + qcow2GetBackingStoreFormat(format, buf, buf_size, header_size, + offset) < 0) + return BACKING_STORE_INVALID; + } return BACKING_STORE_OK; } @@ -374,7 +442,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; @@ -386,7 +454,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); } @@ -754,7 +831,26 @@ virStorageFileGetMetadataFromBuf(int format, } } + if (format == VIR_STORAGE_FILE_QCOW3) { + meta->features.compatible = + virBitmapNew(VIR_STORAGE_FILE_QCOW3_COMP_LAST); + meta->features.incompatible = + virBitmapNew(VIR_STORAGE_FILE_QCOW3_INCOMP_LAST); + if (!meta->features.compatible || !meta->features.incompatible) { + virReportOOMError(); + goto error; + } + if (qcow3GetFeatures(&(meta->features), buf, buflen) < 0) + goto error; + } + return 0; + +error: + VIR_FREE(meta->backingStore); + virBitmapFree(meta->features.compatible); + virBitmapFree(meta->features.incompatible); + return -1; } @@ -1069,6 +1165,8 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) return; virStorageFileFreeMetadata(meta->backingMeta); + virBitmapFree(meta->features.compatible); + virBitmapFree(meta->features.incompatible); VIR_FREE(meta->backingStore); VIR_FREE(meta->backingStoreRaw); VIR_FREE(meta); diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h index 618b028..3249e8f 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, @@ -50,6 +52,29 @@ enum virStorageFileFormat { VIR_ENUM_DECL(virStorageFileFormat); +enum virStorageFileQcow3Incomp { + VIR_STORAGE_FILE_QCOW3_INCOMP_NONE = -1, + VIR_STORAGE_FILE_QCOW3_INCOMP_DIRTY = 0, + + VIR_STORAGE_FILE_QCOW3_INCOMP_LAST, +}; +VIR_ENUM_DECL(virStorageFileQcow3Incomp); + +enum virStorageFileQcow3Comp { + VIR_STORAGE_FILE_QCOW3_COMP_NONE = -1, + VIR_STORAGE_FILE_QCOW3_COMP_LAZY_REFCOUNT = 0, + + VIR_STORAGE_FILE_QCOW3_COMP_LAST, +}; +VIR_ENUM_DECL(virStorageFileQcow3Comp); + +typedef struct _virStorageFileFeatures virStorageFileFeatures; +typedef virStorageFileFeatures *virStorageFileFeaturesPtr; +struct _virStorageFileFeatures { + virBitmapPtr compatible; + virBitmapPtr incompatible; +}; + typedef struct _virStorageFileMetadata virStorageFileMetadata; typedef virStorageFileMetadata *virStorageFileMetadataPtr; struct _virStorageFileMetadata { @@ -60,6 +85,7 @@ struct _virStorageFileMetadata { virStorageFileMetadataPtr backingMeta; unsigned long long capacity; bool encrypted; + virStorageFileFeatures features; }; # ifndef DEV_BSIZE -- 1.7.8.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list