[libvirt] [PATCHv1 3/5] util: add qcow3 format probing

Ján Tomko jtomko at redhat.com
Thu Jan 10 12:28:57 UTC 2013


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




More information about the libvir-list mailing list