[libvirt] Re: [PATCH 4/6] Move virStorageGetMetadataFromFD() to libvirt_util
Daniel P. Berrange
berrange at redhat.com
Wed Sep 30 09:27:49 UTC 2009
On Tue, Sep 29, 2009 at 09:56:47AM +0100, Mark McLoughlin wrote:
> Finally, we get to the point of all this.
>
> Move virStorageGetMetadataFromFD() to virStorageFileGetMetadataFromFD()
> and move to src/util/storage_file.[ch]
>
> There's no functional changes in this patch, just code movement
>
> * src/storage/storage_backend_fs.c: move code from here ...
>
> * src/util/storage_file.[ch]: ... to here
>
> * src/libvirt_private.syms: export virStorageFileGetMetadataFromFD()
> ---
> src/libvirt_private.syms | 1 +
> src/storage/storage_backend_fs.c | 368 +-------------------------------------
> src/util/storage_file.c | 373 ++++++++++++++++++++++++++++++++++++++
> src/util/storage_file.h | 5 +
> 4 files changed, 380 insertions(+), 367 deletions(-)
>
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index ddab21d..4890032 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -393,6 +393,7 @@ virStorageGenerateQcowPassphrase;
> # storage_file.h
> virStorageFileFormatTypeToString;
> virStorageFileFormatTypeFromString;
> +virStorageFileGetMetadataFromFD;
>
> # threads.h
> virMutexInit;
> diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
> index 7db73bf..6816da8 100644
> --- a/src/storage/storage_backend_fs.c
> +++ b/src/storage/storage_backend_fs.c
> @@ -46,375 +46,9 @@
> #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},
> - */
> -};
> -
> #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
> -virStorageGetMetadataFromFD(virConnectPtr conn,
> - const char *path,
> - int fd,
> - virStorageFileMetadata *meta)
> -{
> - unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
> - int len, i;
> -
> - /* If all else fails, call it a raw file */
> - meta->format = VIR_STORAGE_FILE_RAW;
> -
> - if ((len = read(fd, head, sizeof(head))) < 0) {
> - virReportSystemError(conn, errno, _("cannot read header '%s'"), path);
> - return -1;
> - }
> -
> - /* First check file magic */
> - for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
> - int mlen;
> -
> - 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) {
> - if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
> - meta->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 {
> - meta->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 (meta->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
> - continue;
> - meta->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];
> - meta->encrypted = crypt_format != 0;
> - }
> -
> - /* Validation passed, we know the file format now */
> - meta->format = fileTypeInfo[i].type;
> - if (fileTypeInfo[i].getBackingStore != NULL) {
> - 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) {
> - meta->backingStore = absolutePathFromBaseFile(path, base);
> - VIR_FREE(base);
> - if (meta->backingStore == NULL) {
> - virReportOOMError(conn);
> - return -1;
> - }
> - }
> - }
> - 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;
> -
> - meta->format = fileTypeInfo[i].type;
> - return 0;
> - }
> -
> - return 0;
> -}
> -
> -static int
> virStorageBackendProbeTarget(virConnectPtr conn,
> virStorageVolTargetPtr target,
> char **backingStore,
> @@ -444,7 +78,7 @@ virStorageBackendProbeTarget(virConnectPtr conn,
>
> memset(&meta, 0, sizeof(meta));
>
> - if (virStorageGetMetadataFromFD(conn, target->path, fd, &meta) < 0) {
> + if (virStorageFileGetMetadataFromFD(conn, target->path, fd, &meta) < 0) {
> close(fd);
> return -1;
> }
> diff --git a/src/util/storage_file.c b/src/util/storage_file.c
> index e66ec8a..e674713 100644
> --- a/src/util/storage_file.c
> +++ b/src/util/storage_file.c
> @@ -24,8 +24,381 @@
> #include <config.h>
> #include "storage_file.h"
>
> +#include <unistd.h>
> +#include "memory.h"
> +#include "virterror_internal.h"
> +
> +#define VIR_FROM_THIS VIR_FROM_STORAGE
> +
> VIR_ENUM_IMPL(virStorageFileFormat,
> VIR_STORAGE_FILE_LAST,
> "raw", "dir", "bochs",
> "cloop", "cow", "dmg", "iso",
> "qcow", "qcow2", "vmdk", "vpc")
> +
> +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
> +virStorageFileGetMetadataFromFD(virConnectPtr conn,
> + const char *path,
> + int fd,
> + virStorageFileMetadata *meta)
> +{
> + unsigned char head[20*512]; /* vmdk4GetBackingStore needs this much. */
> + int len, i;
> +
> + /* If all else fails, call it a raw file */
> + meta->format = VIR_STORAGE_FILE_RAW;
> +
> + if ((len = read(fd, head, sizeof(head))) < 0) {
> + virReportSystemError(conn, errno, _("cannot read header '%s'"), path);
> + return -1;
> + }
> +
> + /* First check file magic */
> + for (i = 0 ; i < ARRAY_CARDINALITY(fileTypeInfo) ; i++) {
> + int mlen;
> +
> + 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) {
> + if (fileTypeInfo[i].endian == LV_LITTLE_ENDIAN) {
> + meta->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 {
> + meta->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 (meta->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier))
> + continue;
> + meta->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];
> + meta->encrypted = crypt_format != 0;
> + }
> +
> + /* Validation passed, we know the file format now */
> + meta->format = fileTypeInfo[i].type;
> + if (fileTypeInfo[i].getBackingStore != NULL) {
> + 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) {
> + meta->backingStore = absolutePathFromBaseFile(path, base);
> + VIR_FREE(base);
> + if (meta->backingStore == NULL) {
> + virReportOOMError(conn);
> + return -1;
> + }
> + }
> + }
> + 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;
> +
> + meta->format = fileTypeInfo[i].type;
> + return 0;
> + }
> +
> + return 0;
> +}
> diff --git a/src/util/storage_file.h b/src/util/storage_file.h
> index b458c0e..e34d749 100644
> --- a/src/util/storage_file.h
> +++ b/src/util/storage_file.h
> @@ -51,4 +51,9 @@ typedef struct _virStorageFileMetadata {
> bool encrypted;
> } virStorageFileMetadata;
>
> +int virStorageFileGetMetadataFromFD(virConnectPtr conn,
> + const char *path,
> + int fd,
> + virStorageFileMetadata *meta);
> +
> #endif /* __VIR_STORAGE_FILE_H__ */
ACK
Daniel
--
|: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :|
|: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :|
|: http://autobuild.org -o- http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
More information about the libvir-list
mailing list