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