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 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list