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