[libvirt] [PATCH 6/7] Move virStorageBackendProbeTarget to libvirt_util

Mark McLoughlin markmc at redhat.com
Fri Sep 25 13:27:32 UTC 2009


Finally, we get to the point of all this.

Rename virStorageBackendProbeTarget() to virStorageFileProbeHeader()
and move to src/util/storage_file.[ch]

* src/storage/storage_backend_fs.c: move code from here ...

* src/util/storage_file.[ch]: ... to here

* src/libvirt_private.syms: export virStorageFileProbeHeader()
---
 src/libvirt_private.syms         |    1 +
 src/storage/storage_backend_fs.c |  435 ++------------------------------------
 src/util/storage_file.c          |  409 +++++++++++++++++++++++++++++++++++
 src/util/storage_file.h          |   10 +
 4 files changed, 435 insertions(+), 420 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 9b6f4a7..6a353ee 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -395,6 +395,7 @@ virStorageFileFormatTypeToString;
 virStorageFileFormatTypeFromString;
 virStorageFileGetInfo;
 virStorageFileGetInfoFromFD;
+virStorageFileProbeHeader;
 
 # threads.h
 virMutexInit;
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index 49cfc6f..fdc12c3 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -46,419 +46,10 @@
 #include "memory.h"
 #include "xml.h"
 
-enum lv_endian {
-    LV_LITTLE_ENDIAN = 1, /* 1234 */
-    LV_BIG_ENDIAN         /* 4321 */
-};
-
-enum {
-    BACKING_STORE_OK,
-    BACKING_STORE_INVALID,
-    BACKING_STORE_ERROR,
-};
-
-static int cowGetBackingStore(virConnectPtr, char **,
-                              const unsigned char *, size_t);
-static int qcowXGetBackingStore(virConnectPtr, char **,
-                                const unsigned char *, size_t);
-static int vmdk4GetBackingStore(virConnectPtr, char **,
-                                const unsigned char *, size_t);
-
-/* Either 'magic' or 'extension' *must* be provided */
-struct FileTypeInfo {
-    int type;           /* One of the constants above */
-    const char *magic;  /* Optional string of file magic
-                         * to check at head of file */
-    const char *extension; /* Optional file extension to check */
-    enum lv_endian endian; /* Endianness of file format */
-    int versionOffset;    /* Byte offset from start of file
-                           * where we find version number,
-                           * -1 to skip version test */
-    int versionNumber;    /* Version number to validate */
-    int sizeOffset;       /* Byte offset from start of file
-                           * where we find capacity info,
-                           * -1 to use st_size as capacity */
-    int sizeBytes;        /* Number of bytes for size field */
-    int sizeMultiplier;   /* A scaling factor if size is not in bytes */
-                          /* Store a COW base image path (possibly relative),
-                           * or NULL if there is no COW base image, to RES;
-                           * return BACKING_STORE_* */
-    int qcowCryptOffset;  /* Byte offset from start of file
-                           * where to find encryption mode,
-                           * -1 if encryption is not used */
-    int (*getBackingStore)(virConnectPtr conn, char **res,
-                           const unsigned char *buf, size_t buf_size);
-};
-struct FileTypeInfo const fileTypeInfo[] = {
-    /* Bochs */
-    /* XXX Untested
-    { VIR_STORAGE_FILE_BOCHS, "Bochs Virtual HD Image", NULL,
-      LV_LITTLE_ENDIAN, 64, 0x20000,
-      32+16+16+4+4+4+4+4, 8, 1, -1, NULL },*/
-    /* CLoop */
-    /* XXX Untested
-    { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL,
-      LV_LITTLE_ENDIAN, -1, 0,
-      -1, 0, 0, -1, NULL }, */
-    /* Cow */
-    { VIR_STORAGE_FILE_COW, "OOOM", NULL,
-      LV_BIG_ENDIAN, 4, 2,
-      4+4+1024+4, 8, 1, -1, cowGetBackingStore },
-    /* DMG */
-    /* XXX QEMU says there's no magic for dmg, but we should check... */
-    { VIR_STORAGE_FILE_DMG, NULL, ".dmg",
-      0, -1, 0,
-      -1, 0, 0, -1, NULL },
-    /* XXX there's probably some magic for iso we can validate too... */
-    { VIR_STORAGE_FILE_ISO, NULL, ".iso",
-      0, -1, 0,
-      -1, 0, 0, -1, NULL },
-    /* Parallels */
-    /* XXX Untested
-    { VIR_STORAGE_FILE_PARALLELS, "WithoutFreeSpace", NULL,
-      LV_LITTLE_ENDIAN, 16, 2,
-      16+4+4+4+4, 4, 512, -1, NULL },
-    */
-    /* QCow */
-    { VIR_STORAGE_FILE_QCOW, "QFI", NULL,
-      LV_BIG_ENDIAN, 4, 1,
-      4+4+8+4+4, 8, 1, 4+4+8+4+4+8+1+1+2, qcowXGetBackingStore },
-    /* QCow 2 */
-    { VIR_STORAGE_FILE_QCOW2, "QFI", NULL,
-      LV_BIG_ENDIAN, 4, 2,
-      4+4+8+4+4, 8, 1, 4+4+8+4+4+8, qcowXGetBackingStore },
-    /* VMDK 3 */
-    /* XXX Untested
-    { VIR_STORAGE_FILE_VMDK, "COWD", NULL,
-      LV_LITTLE_ENDIAN, 4, 1,
-      4+4+4, 4, 512, -1, NULL },
-    */
-    /* VMDK 4 */
-    { VIR_STORAGE_FILE_VMDK, "KDMV", NULL,
-      LV_LITTLE_ENDIAN, 4, 1,
-      4+4+4, 8, 512, -1, vmdk4GetBackingStore },
-    /* Connectix / VirtualPC */
-    /* XXX Untested
-    { VIR_STORAGE_FILE_VPC, "conectix", NULL,
-      LV_BIG_ENDIAN, -1, 0,
-      -1, 0, 0, -1, NULL},
-    */
-};
+#if WITH_STORAGE_FS
 
 #define VIR_FROM_THIS VIR_FROM_STORAGE
 
-static int
-cowGetBackingStore(virConnectPtr conn,
-                   char **res,
-                   const unsigned char *buf,
-                   size_t buf_size)
-{
-#define COW_FILENAME_MAXLEN 1024
-    *res = NULL;
-    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
-        return BACKING_STORE_INVALID;
-    if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */
-        return BACKING_STORE_OK;
-
-    *res = strndup ((const char*)buf + 4+4, COW_FILENAME_MAXLEN);
-    if (*res == NULL) {
-        virReportOOMError(conn);
-        return BACKING_STORE_ERROR;
-    }
-    return BACKING_STORE_OK;
-}
-
-static int
-qcowXGetBackingStore(virConnectPtr conn,
-                     char **res,
-                     const unsigned char *buf,
-                     size_t buf_size)
-{
-    unsigned long long offset;
-    unsigned long size;
-
-    *res = NULL;
-    if (buf_size < 4+4+8+4)
-        return BACKING_STORE_INVALID;
-    offset = (((unsigned long long)buf[4+4] << 56)
-              | ((unsigned long long)buf[4+4+1] << 48)
-              | ((unsigned long long)buf[4+4+2] << 40)
-              | ((unsigned long long)buf[4+4+3] << 32)
-              | ((unsigned long long)buf[4+4+4] << 24)
-              | ((unsigned long long)buf[4+4+5] << 16)
-              | ((unsigned long long)buf[4+4+6] << 8)
-              | buf[4+4+7]); /* QCowHeader.backing_file_offset */
-    if (offset > buf_size)
-        return BACKING_STORE_INVALID;
-    size = ((buf[4+4+8] << 24)
-            | (buf[4+4+8+1] << 16)
-            | (buf[4+4+8+2] << 8)
-            | buf[4+4+8+3]); /* QCowHeader.backing_file_size */
-    if (size == 0)
-        return BACKING_STORE_OK;
-    if (offset + size > buf_size || offset + size < offset)
-        return BACKING_STORE_INVALID;
-    if (size + 1 == 0)
-        return BACKING_STORE_INVALID;
-    if (VIR_ALLOC_N(*res, size + 1) < 0) {
-        virReportOOMError(conn);
-        return BACKING_STORE_ERROR;
-    }
-    memcpy(*res, buf + offset, size);
-    (*res)[size] = '\0';
-    return BACKING_STORE_OK;
-}
-
-
-static int
-vmdk4GetBackingStore(virConnectPtr conn,
-                     char **res,
-                     const unsigned char *buf,
-                     size_t buf_size)
-{
-    static const char prefix[] = "parentFileNameHint=\"";
-
-    char desc[20*512 + 1], *start, *end;
-    size_t len;
-
-    *res = NULL;
-
-    if (buf_size <= 0x200)
-        return BACKING_STORE_INVALID;
-    len = buf_size - 0x200;
-    if (len > sizeof(desc) - 1)
-        len = sizeof(desc) - 1;
-    memcpy(desc, buf + 0x200, len);
-    desc[len] = '\0';
-    start = strstr(desc, prefix);
-    if (start == NULL)
-        return BACKING_STORE_OK;
-    start += strlen(prefix);
-    end = strchr(start, '"');
-    if (end == NULL)
-        return BACKING_STORE_INVALID;
-    if (end == start)
-        return BACKING_STORE_OK;
-    *end = '\0';
-    *res = strdup(start);
-    if (*res == NULL) {
-        virReportOOMError(conn);
-        return BACKING_STORE_ERROR;
-    }
-    return BACKING_STORE_OK;
-}
-
-/**
- * Return an absolute path corresponding to PATH, which is absolute or relative
- * to the directory containing BASE_FILE, or NULL on error
- */
-static char *absolutePathFromBaseFile(const char *base_file, const char *path)
-{
-    size_t base_size, path_size;
-    char *res, *p;
-
-    if (*path == '/')
-        return strdup(path);
-
-    base_size = strlen(base_file) + 1;
-    path_size = strlen(path) + 1;
-    if (VIR_ALLOC_N(res, base_size - 1 + path_size) < 0)
-        return NULL;
-    memcpy(res, base_file, base_size);
-    p = strrchr(res, '/');
-    if (p != NULL)
-        p++;
-    else
-        p = res;
-    memcpy(p, path, path_size);
-    if (VIR_REALLOC_N(res, (p + path_size) - res) < 0) {
-        /* Ignore failure */
-    }
-    return res;
-}
-
-
-
-/**
- * Probe the header of a file to determine what type of disk image
- * it is, and info about its capacity if available.
- */
-static int virStorageBackendProbeTarget(virConnectPtr conn,
-                                        virStorageVolTargetPtr target,
-                                        char **backingStore,
-                                        unsigned long long *allocation,
-                                        unsigned long long *capacity,
-                                        virStorageEncryptionPtr *encryption) {
-    int fd;
-    unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
-    int len, i, ret;
-
-    if (backingStore)
-        *backingStore = NULL;
-    if (encryption)
-        *encryption = NULL;
-
-    if ((fd = open(target->path, O_RDONLY)) < 0) {
-        virReportSystemError(conn, errno,
-                             _("cannot open volume '%s'"),
-                             target->path);
-        return -1;
-    }
-
-    if ((ret = virStorageFileGetInfoFromFD(conn, target->path, fd,
-                                           &target->perms,
-                                           allocation, capacity)) < 0) {
-        close(fd);
-        return ret; /* Take care to propagate ret, it is not always -1 */
-    }
-
-    if ((len = read(fd, head, sizeof(head))) < 0) {
-        virReportSystemError(conn, errno,
-                             _("cannot read header '%s'"),
-                             target->path);
-        close(fd);
-        return -1;
-    }
-
-    close(fd);
-
-    /* First check file magic */
-    for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
-        int mlen;
-        bool encrypted_qcow = false;
-
-        if (fileTypeInfo[i].magic == NULL)
-            continue;
-
-        /* Validate magic data */
-        mlen = strlen(fileTypeInfo[i].magic);
-        if (mlen > len)
-            continue;
-        if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0)
-            continue;
-
-        /* Validate version number info */
-        if (fileTypeInfo[i].versionNumber != -1) {
-            int version;
-
-            if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
-                version = (head[fileTypeInfo[i].versionOffset+3] << 24) |
-                    (head[fileTypeInfo[i].versionOffset+2] << 16) |
-                    (head[fileTypeInfo[i].versionOffset+1] << 8) |
-                    head[fileTypeInfo[i].versionOffset];
-            } else {
-                version = (head[fileTypeInfo[i].versionOffset] << 24) |
-                    (head[fileTypeInfo[i].versionOffset+1] << 16) |
-                    (head[fileTypeInfo[i].versionOffset+2] << 8) |
-                    head[fileTypeInfo[i].versionOffset+3];
-            }
-            if (version != fileTypeInfo[i].versionNumber)
-                continue;
-        }
-
-        /* Optionally extract capacity from file */
-        if (fileTypeInfo[i].sizeOffset != -1 && capacity) {
-            if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
-                *capacity =
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
-            } else {
-                *capacity =
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) |
-                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
-            }
-            /* Avoid unlikely, but theoretically possible overflow */
-            if (*capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
-                continue;
-            *capacity *= fileTypeInfo[i].sizeMultiplier;
-        }
-
-        if (fileTypeInfo[i].qcowCryptOffset != -1) {
-            int crypt_format;
-
-            crypt_format = (head[fileTypeInfo[i].qcowCryptOffset] << 24) |
-                (head[fileTypeInfo[i].qcowCryptOffset+1] << 16) |
-                (head[fileTypeInfo[i].qcowCryptOffset+2] << 8) |
-                head[fileTypeInfo[i].qcowCryptOffset+3];
-            encrypted_qcow = crypt_format != 0;
-        }
-
-        /* Validation passed, we know the file format now */
-        target->format = fileTypeInfo[i].type;
-        if (fileTypeInfo[i].getBackingStore != NULL && backingStore) {
-            char *base;
-
-            switch (fileTypeInfo[i].getBackingStore(conn, &base, head, len)) {
-            case BACKING_STORE_OK:
-                break;
-
-            case BACKING_STORE_INVALID:
-                continue;
-
-            case BACKING_STORE_ERROR:
-                return -1;
-            }
-            if (base != NULL) {
-                *backingStore
-                    = absolutePathFromBaseFile(target->path, base);
-                VIR_FREE(base);
-                if (*backingStore == NULL) {
-                    virReportOOMError(conn);
-                    return -1;
-                }
-            }
-        }
-        if (encryption != NULL && encrypted_qcow) {
-            virStorageEncryptionPtr enc;
-
-            if (VIR_ALLOC(enc) < 0) {
-                virReportOOMError(conn);
-                if (backingStore)
-                    VIR_FREE(*backingStore);
-                return -1;
-            }
-            enc->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW;
-            *encryption = enc;
-            /* XXX ideally we'd fill in secret UUID here
-             * but we cannot guarentee 'conn' is non-NULL
-             * at this point in time :-(  So we only fill
-             * in secrets when someone first queries a vol
-             */
-        }
-        return 0;
-    }
-
-    /* No magic, so check file extension */
-    for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
-        if (fileTypeInfo[i].extension == NULL)
-            continue;
-
-        if (!virFileHasSuffix(target->path, fileTypeInfo[i].extension))
-            continue;
-
-        target->format = fileTypeInfo[i].type;
-        return 0;
-    }
-
-    /* All fails, so call it a raw file */
-    target->format = VIR_STORAGE_FILE_RAW;
-    return 0;
-}
-
-#if WITH_STORAGE_FS
-
 #include <mntent.h>
 
 struct _virNetfsDiscoverState {
@@ -901,12 +492,14 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
         if ((vol->key = strdup(vol->target.path)) == NULL)
             goto no_memory;
 
-        if ((ret = virStorageBackendProbeTarget(conn,
-                                                &vol->target,
-                                                &backingStore,
-                                                &vol->allocation,
-                                                &vol->capacity,
-                                                &vol->target.encryption) < 0)) {
+        if ((ret = virStorageFileProbeHeader(conn,
+                                             vol->target.path,
+                                             &vol->target.format,
+                                             &backingStore,
+                                             &vol->target.encryption,
+                                             &vol->target.perms,
+                                             &vol->allocation,
+                                             &vol->capacity) < 0)) {
             if (ret == -1)
                 goto cleanup;
             else {
@@ -942,10 +535,12 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn,
             } else {
                 vol->backingStore.path = backingStore;
 
-                if ((ret = virStorageBackendProbeTarget(conn,
-                                                        &vol->backingStore,
-                                                        NULL, NULL, NULL,
-                                                        NULL)) < 0) {
+                if ((ret = virStorageFileProbeHeader(conn,
+                                                     vol->backingStore.path,
+                                                     &vol->backingStore.format,
+                                                     NULL, NULL,
+                                                     &vol->backingStore.perms,
+                                                     NULL, NULL)) < 0) {
                     if (ret == -1)
                         goto cleanup;
                     else {
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
index c1eec9b..70ffac4 100644
--- a/src/util/storage_file.c
+++ b/src/util/storage_file.c
@@ -51,6 +51,415 @@ VIR_ENUM_IMPL(virStorageFileFormat,
 #define DEV_BSIZE 512
 #endif
 
+enum lv_endian {
+    LV_LITTLE_ENDIAN = 1, /* 1234 */
+    LV_BIG_ENDIAN         /* 4321 */
+};
+
+enum {
+    BACKING_STORE_OK,
+    BACKING_STORE_INVALID,
+    BACKING_STORE_ERROR,
+};
+
+/* Either 'magic' or 'extension' *must* be provided */
+struct FileTypeInfo {
+    int type;           /* One of the constants above */
+    const char *magic;  /* Optional string of file magic
+                         * to check at head of file */
+    const char *extension; /* Optional file extension to check */
+    enum lv_endian endian; /* Endianness of file format */
+    int versionOffset;    /* Byte offset from start of file
+                           * where we find version number,
+                           * -1 to skip version test */
+    int versionNumber;    /* Version number to validate */
+    int sizeOffset;       /* Byte offset from start of file
+                           * where we find capacity info,
+                           * -1 to use st_size as capacity */
+    int sizeBytes;        /* Number of bytes for size field */
+    int sizeMultiplier;   /* A scaling factor if size is not in bytes */
+                          /* Store a COW base image path (possibly relative),
+                           * or NULL if there is no COW base image, to RES;
+                           * return BACKING_STORE_* */
+    int qcowCryptOffset;  /* Byte offset from start of file
+                           * where to find encryption mode,
+                           * -1 if encryption is not used */
+    int (*getBackingStore)(virConnectPtr conn, char **res,
+                           const unsigned char *buf, size_t buf_size);
+};
+
+static int cowGetBackingStore(virConnectPtr, char **,
+                              const unsigned char *, size_t);
+static int qcowXGetBackingStore(virConnectPtr, char **,
+                                const unsigned char *, size_t);
+static int vmdk4GetBackingStore(virConnectPtr, char **,
+                                const unsigned char *, size_t);
+
+static struct FileTypeInfo const fileTypeInfo[] = {
+    /* Bochs */
+    /* XXX Untested
+    { VIR_STORAGE_FILE_BOCHS, "Bochs Virtual HD Image", NULL,
+      LV_LITTLE_ENDIAN, 64, 0x20000,
+      32+16+16+4+4+4+4+4, 8, 1, -1, NULL },*/
+    /* CLoop */
+    /* XXX Untested
+    { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL,
+      LV_LITTLE_ENDIAN, -1, 0,
+      -1, 0, 0, -1, NULL }, */
+    /* Cow */
+    { VIR_STORAGE_FILE_COW, "OOOM", NULL,
+      LV_BIG_ENDIAN, 4, 2,
+      4+4+1024+4, 8, 1, -1, cowGetBackingStore },
+    /* DMG */
+    /* XXX QEMU says there's no magic for dmg, but we should check... */
+    { VIR_STORAGE_FILE_DMG, NULL, ".dmg",
+      0, -1, 0,
+      -1, 0, 0, -1, NULL },
+    /* XXX there's probably some magic for iso we can validate too... */
+    { VIR_STORAGE_FILE_ISO, NULL, ".iso",
+      0, -1, 0,
+      -1, 0, 0, -1, NULL },
+    /* Parallels */
+    /* XXX Untested
+    { VIR_STORAGE_FILE_PARALLELS, "WithoutFreeSpace", NULL,
+      LV_LITTLE_ENDIAN, 16, 2,
+      16+4+4+4+4, 4, 512, -1, NULL },
+    */
+    /* QCow */
+    { VIR_STORAGE_FILE_QCOW, "QFI", NULL,
+      LV_BIG_ENDIAN, 4, 1,
+      4+4+8+4+4, 8, 1, 4+4+8+4+4+8+1+1+2, qcowXGetBackingStore },
+    /* QCow 2 */
+    { VIR_STORAGE_FILE_QCOW2, "QFI", NULL,
+      LV_BIG_ENDIAN, 4, 2,
+      4+4+8+4+4, 8, 1, 4+4+8+4+4+8, qcowXGetBackingStore },
+    /* VMDK 3 */
+    /* XXX Untested
+    { VIR_STORAGE_FILE_VMDK, "COWD", NULL,
+      LV_LITTLE_ENDIAN, 4, 1,
+      4+4+4, 4, 512, -1, NULL },
+    */
+    /* VMDK 4 */
+    { VIR_STORAGE_FILE_VMDK, "KDMV", NULL,
+      LV_LITTLE_ENDIAN, 4, 1,
+      4+4+4, 8, 512, -1, vmdk4GetBackingStore },
+    /* Connectix / VirtualPC */
+    /* XXX Untested
+    { VIR_STORAGE_FILE_VPC, "conectix", NULL,
+      LV_BIG_ENDIAN, -1, 0,
+      -1, 0, 0, -1, NULL},
+    */
+};
+
+static int
+cowGetBackingStore(virConnectPtr conn,
+                   char **res,
+                   const unsigned char *buf,
+                   size_t buf_size)
+{
+#define COW_FILENAME_MAXLEN 1024
+    *res = NULL;
+    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
+        return BACKING_STORE_INVALID;
+    if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */
+        return BACKING_STORE_OK;
+
+    *res = strndup ((const char*)buf + 4+4, COW_FILENAME_MAXLEN);
+    if (*res == NULL) {
+        virReportOOMError(conn);
+        return BACKING_STORE_ERROR;
+    }
+    return BACKING_STORE_OK;
+}
+
+static int
+qcowXGetBackingStore(virConnectPtr conn,
+                     char **res,
+                     const unsigned char *buf,
+                     size_t buf_size)
+{
+    unsigned long long offset;
+    unsigned long size;
+
+    *res = NULL;
+    if (buf_size < 4+4+8+4)
+        return BACKING_STORE_INVALID;
+    offset = (((unsigned long long)buf[4+4] << 56)
+              | ((unsigned long long)buf[4+4+1] << 48)
+              | ((unsigned long long)buf[4+4+2] << 40)
+              | ((unsigned long long)buf[4+4+3] << 32)
+              | ((unsigned long long)buf[4+4+4] << 24)
+              | ((unsigned long long)buf[4+4+5] << 16)
+              | ((unsigned long long)buf[4+4+6] << 8)
+              | buf[4+4+7]); /* QCowHeader.backing_file_offset */
+    if (offset > buf_size)
+        return BACKING_STORE_INVALID;
+    size = ((buf[4+4+8] << 24)
+            | (buf[4+4+8+1] << 16)
+            | (buf[4+4+8+2] << 8)
+            | buf[4+4+8+3]); /* QCowHeader.backing_file_size */
+    if (size == 0)
+        return BACKING_STORE_OK;
+    if (offset + size > buf_size || offset + size < offset)
+        return BACKING_STORE_INVALID;
+    if (size + 1 == 0)
+        return BACKING_STORE_INVALID;
+    if (VIR_ALLOC_N(*res, size + 1) < 0) {
+        virReportOOMError(conn);
+        return BACKING_STORE_ERROR;
+    }
+    memcpy(*res, buf + offset, size);
+    (*res)[size] = '\0';
+    return BACKING_STORE_OK;
+}
+
+static int
+vmdk4GetBackingStore(virConnectPtr conn,
+                     char **res,
+                     const unsigned char *buf,
+                     size_t buf_size)
+{
+    static const char prefix[] = "parentFileNameHint=\"";
+
+    char desc[20*512 + 1], *start, *end;
+    size_t len;
+
+    *res = NULL;
+
+    if (buf_size <= 0x200)
+        return BACKING_STORE_INVALID;
+    len = buf_size - 0x200;
+    if (len > sizeof(desc) - 1)
+        len = sizeof(desc) - 1;
+    memcpy(desc, buf + 0x200, len);
+    desc[len] = '\0';
+    start = strstr(desc, prefix);
+    if (start == NULL)
+        return BACKING_STORE_OK;
+    start += strlen(prefix);
+    end = strchr(start, '"');
+    if (end == NULL)
+        return BACKING_STORE_INVALID;
+    if (end == start)
+        return BACKING_STORE_OK;
+    *end = '\0';
+    *res = strdup(start);
+    if (*res == NULL) {
+        virReportOOMError(conn);
+        return BACKING_STORE_ERROR;
+    }
+    return BACKING_STORE_OK;
+}
+
+/**
+ * Return an absolute path corresponding to PATH, which is absolute or relative
+ * to the directory containing BASE_FILE, or NULL on error
+ */
+static char *
+absolutePathFromBaseFile(const char *base_file, const char *path)
+{
+    size_t base_size, path_size;
+    char *res, *p;
+
+    if (*path == '/')
+        return strdup(path);
+
+    base_size = strlen(base_file) + 1;
+    path_size = strlen(path) + 1;
+    if (VIR_ALLOC_N(res, base_size - 1 + path_size) < 0)
+        return NULL;
+    memcpy(res, base_file, base_size);
+    p = strrchr(res, '/');
+    if (p != NULL)
+        p++;
+    else
+        p = res;
+    memcpy(p, path, path_size);
+    if (VIR_REALLOC_N(res, (p + path_size) - res) < 0) {
+        /* Ignore failure */
+    }
+    return res;
+}
+
+/**
+ * Probe the header of a file to determine what type of disk image
+ * it is, and info about its capacity if available.
+ */
+int
+virStorageFileProbeHeader(virConnectPtr conn,
+                          const char *path,
+                          int *format,
+                          char **backingStore,
+                          virStorageEncryptionPtr *encryption,
+                          virStorageFilePermsPtr perms,
+                          unsigned long long *allocation,
+                          unsigned long long *capacity)
+{
+    int fd;
+    unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
+    int len, i, ret;
+
+    if (format) /* If all else fails, call it a raw file */
+        *format = VIR_STORAGE_FILE_RAW;
+    if (backingStore)
+        *backingStore = NULL;
+    if (encryption)
+        *encryption = NULL;
+
+    if ((fd = open(path, O_RDONLY)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot open volume '%s'"), path);
+        return -1;
+    }
+
+    if ((ret = virStorageFileGetInfoFromFD(conn, path, fd, perms,
+                                           allocation, capacity)) < 0) {
+        close(fd);
+        return ret; /* Take care to propagate ret, it is not always -1 */
+    }
+
+    if ((len = read(fd, head, sizeof(head))) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot read header '%s'"), path);
+        close(fd);
+        return -1;
+    }
+
+    close(fd);
+
+    /* First check file magic */
+    for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
+        int mlen;
+        bool encrypted_qcow = false;
+
+        if (fileTypeInfo[i].magic == NULL)
+            continue;
+
+        /* Validate magic data */
+        mlen = strlen(fileTypeInfo[i].magic);
+        if (mlen > len)
+            continue;
+        if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0)
+            continue;
+
+        /* Validate version number info */
+        if (fileTypeInfo[i].versionNumber != -1) {
+            int version;
+
+            if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
+                version = (head[fileTypeInfo[i].versionOffset+3] << 24) |
+                    (head[fileTypeInfo[i].versionOffset+2] << 16) |
+                    (head[fileTypeInfo[i].versionOffset+1] << 8) |
+                    head[fileTypeInfo[i].versionOffset];
+            } else {
+                version = (head[fileTypeInfo[i].versionOffset] << 24) |
+                    (head[fileTypeInfo[i].versionOffset+1] << 16) |
+                    (head[fileTypeInfo[i].versionOffset+2] << 8) |
+                    head[fileTypeInfo[i].versionOffset+3];
+            }
+            if (version != fileTypeInfo[i].versionNumber)
+                continue;
+        }
+
+        /* Optionally extract capacity from file */
+        if (fileTypeInfo[i].sizeOffset != -1 && capacity) {
+            if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
+                *capacity =
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset]);
+            } else {
+                *capacity =
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) |
+                    ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]);
+            }
+            /* Avoid unlikely, but theoretically possible overflow */
+            if (*capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
+                continue;
+            *capacity *= fileTypeInfo[i].sizeMultiplier;
+        }
+
+        if (fileTypeInfo[i].qcowCryptOffset != -1) {
+            int crypt_format;
+
+            crypt_format = (head[fileTypeInfo[i].qcowCryptOffset] << 24) |
+                (head[fileTypeInfo[i].qcowCryptOffset+1] << 16) |
+                (head[fileTypeInfo[i].qcowCryptOffset+2] << 8) |
+                head[fileTypeInfo[i].qcowCryptOffset+3];
+            encrypted_qcow = crypt_format != 0;
+        }
+
+        /* Validation passed, we know the file format now */
+        if (format)
+            *format = fileTypeInfo[i].type;
+        if (fileTypeInfo[i].getBackingStore != NULL && backingStore) {
+            char *base;
+
+            switch (fileTypeInfo[i].getBackingStore(conn, &base, head, len)) {
+            case BACKING_STORE_OK:
+                break;
+
+            case BACKING_STORE_INVALID:
+                continue;
+
+            case BACKING_STORE_ERROR:
+                return -1;
+            }
+            if (base != NULL) {
+                *backingStore = absolutePathFromBaseFile(path, base);
+                VIR_FREE(base);
+                if (*backingStore == NULL) {
+                    virReportOOMError(conn);
+                    return -1;
+                }
+            }
+        }
+        if (encryption != NULL && encrypted_qcow) {
+            if (VIR_ALLOC(*encryption) < 0) {
+                virReportOOMError(conn);
+                if (backingStore)
+                    VIR_FREE(*backingStore);
+                return -1;
+            }
+            (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW;
+            /* XXX ideally we'd fill in secret UUID here
+             * but we cannot guarentee 'conn' is non-NULL
+             * at this point in time :-(  So we only fill
+             * in secrets when someone first queries a vol
+             */
+        }
+        return 0;
+    }
+
+    if (!format)
+        return 0;
+
+    /* No magic, so check file extension */
+    for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
+        if (fileTypeInfo[i].extension == NULL)
+            continue;
+
+        if (!virFileHasSuffix(path, fileTypeInfo[i].extension))
+            continue;
+
+        *format = fileTypeInfo[i].type;
+        return 0;
+    }
+
+    return 0;
+}
+
 /*
  * virStorageFileGetInfoFromFD:
  * @conn: connection to report errors on
diff --git a/src/util/storage_file.h b/src/util/storage_file.h
index efacc65..908ca3e 100644
--- a/src/util/storage_file.h
+++ b/src/util/storage_file.h
@@ -25,6 +25,7 @@
 #define __VIR_STORAGE_FILE_H__
 
 #include "util.h"
+#include "storage_encryption.h"
 
 enum virStorageFileFormat {
     VIR_STORAGE_FILE_RAW = 0,
@@ -52,6 +53,15 @@ struct _virStorageFilePerms {
     char *label;
 };
 
+int virStorageFileProbeHeader(virConnectPtr conn,
+                              const char *path,
+                              int *format,
+                              char **backingStore,
+                              virStorageEncryptionPtr *encryption,
+                              virStorageFilePermsPtr perms,
+                              unsigned long long *allocation,
+                              unsigned long long *capacity);
+
 int virStorageFileGetInfo(virConnectPtr conn,
                           const char *path,
                           virStorageFilePermsPtr perms,
-- 
1.6.2.5




More information about the libvir-list mailing list