diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index be85da91fbabb218e07590e6535da0a4ef3312cb..9eb4b77974c6f06bd8da9ae08ace4c52154de5f5 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -395,6 +395,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 7db73bf1dd16b6cf7b84fa4184c7436249f8a0df..6816da8ed692674dfeaf082be095e82c6ec6a116 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -46,374 +46,8 @@ #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, @@ -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 e66ec8a8e4cd2d75235da24884171369ee3f2e69..e674713d12d8dfaba085d11454e6cb6fce117a97 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -24,8 +24,381 @@ #include #include "storage_file.h" +#include +#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 b458c0eab18bfa030882e4565fe691d23b6a4feb..e34d7495a6ac83a4564c4b3f2b8c4e28ec923b18 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__ */