[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH v2 3/6] util: add support for probing qcow3 images



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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]