From f9c1b758c33b14025845858015641aff6b2b99df Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Thu, 1 Oct 2009 15:17:31 +0000 Subject: [PATCH] - Re-label qcow2 backing files (#497131) --- ...rt-svirt-relabel-qcow2-backing-files.patch | 1402 +++++++++++++++++ libvirt.spec | 5 + 2 files changed, 1407 insertions(+) create mode 100644 libvirt-svirt-relabel-qcow2-backing-files.patch diff --git a/libvirt-svirt-relabel-qcow2-backing-files.patch b/libvirt-svirt-relabel-qcow2-backing-files.patch new file mode 100644 index 0000000..db5839c --- /dev/null +++ b/libvirt-svirt-relabel-qcow2-backing-files.patch @@ -0,0 +1,1402 @@ +From 5bb2da190bc6d5a36952315dd48a00709f88c3c2 Mon Sep 17 00:00:00 2001 +From: Mark McLoughlin +Date: Fri, 25 Sep 2009 14:20:13 +0100 +Subject: [PATCH] Re-label image file backing stores + +Use virStorageFileGetMetadata() to find any backing stores for images +and re-label them + +Without this, qemu cannot access qcow2 backing files, see: + + https://bugzilla.redhat.com/497131 + +* src/security/security_selinux.c: re-label backing store files in + SELinuxSetSecurityImageLabel() + +(cherry picked from commit fe627697a3830cd2db0efcc201d8caa9e171263d) + +Includes the following commits: + + util.h needs libvirt.h for virConnectPtr + + Seems standard to include internal.h in order to pull in libvirt.h + + * src/util/util.h: include internal.h + + (cherry picked from commit 25e2857c219e7fb91412746f7919931552c4e07a) + + Move file format enum to libvirt_util + + Rename virStorageVolFormatFileSystem to virStorageFileFormat and + move to src/util/storage_file.[ch] + + * src/Makefile.am: add src/util/storage_file.[ch] + + * src/conf/storage_conf.[ch]: move enum from here ... + + * src/util/storage_file.[ch]: .. to here + + * src/libvirt_private.syms: update To/FromString exports + + * src/storage/storage_backend.c, src/storage/storage_backend_fs.c, + src/vbox/vbox_tmpl.c: update for above changes + + (cherry picked from commit 00fd3ff49bb1e4578756a32a812fdbf5ee335d8c) + + Split virStorageGetMetadataFromFD() from virStorageBackendProbeTarget() + + Prepare the code probing a file's format and associated metadata for + moving into libvirt_util. + + * src/storage/storage_backend_fs.c: re-factor the format and metadata + probing code in preparation for moving it + + (cherry picked from commit f5fc670638d94776a4eba55f5affa69f69ba1ae2) + + Introduce virStorageFileMetadata structure + + Introduce a metadata structure and make virStorageGetMetadataFromFD() + fill it in. + + * src/util/storage_file.h: add virStorageFileMetadata + + * src/backend/storage_backend_fs.c: virStorageGetMetadataFromFD() now + fills in the virStorageFileMetadata structure + + (cherry picked from commit 5fede0a90be565e1c44b7c8236cb8910fd06b52f) + + Move virStorageGetMetadataFromFD() to libvirt_util + + 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() + + (cherry picked from commit a010fb58d6bce026852d611e32302da7687639e5) + + Add virStorageFileGetMetadata() helper + + * src/util/storage_file.c: add virStorageFileGetMetadata() so that + the caller does not need to open the file + + (cherry picked from commit 295fd6e8330c7416e2d97634364f2890133c28fa) + +Fedora-patch: libvirt-svirt-relabel-qcow2-backing-files.patch +--- + src/Makefile.am | 1 + + src/libvirt_private.syms | 7 +- + src/security_selinux.c | 28 +++ + src/storage_backend.c | 17 +- + src/storage_backend_fs.c | 418 +++++---------------------------------------- + src/storage_conf.c | 25 +-- + src/storage_conf.h | 17 -- + src/storage_file.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++ + src/storage_file.h | 62 +++++++ + src/util.h | 1 + + src/vbox/vbox_tmpl.c | 15 +- + 11 files changed, 591 insertions(+), 424 deletions(-) + create mode 100644 src/storage_file.c + create mode 100644 src/storage_file.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index 463252e..3d279f2 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -54,6 +54,7 @@ UTIL_SOURCES = \ + hostusb.c hostusb.h \ + qparams.c qparams.h \ + storage_encryption_conf.h storage_encryption_conf.c \ ++ storage_file.c storage_file.h \ + threads.c threads.h \ + threads-pthread.h \ + threads-win32.h \ +diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms +index 867678f..500c209 100644 +--- a/src/libvirt_private.syms ++++ b/src/libvirt_private.syms +@@ -359,8 +359,6 @@ virStorageVolDefParseNode; + virStoragePoolFormatDiskTypeToString; + virStoragePoolFormatFileSystemTypeToString; + virStoragePoolFormatFileSystemNetTypeToString; +-virStorageVolFormatFileSystemTypeToString; +-virStorageVolFormatFileSystemTypeFromString; + virStoragePoolTypeFromString; + virStoragePartedFsTypeTypeToString; + virStoragePoolObjLock; +@@ -373,6 +371,11 @@ virStorageEncryptionParseNode; + virStorageEncryptionFormat; + virStorageGenerateQcowPassphrase; + ++# storage_file.h ++virStorageFileFormatTypeToString; ++virStorageFileFormatTypeFromString; ++virStorageFileGetMetadata; ++virStorageFileGetMetadataFromFD; + + # threads.h + virMutexInit; +diff --git a/src/security_selinux.c b/src/security_selinux.c +index b4dc153..600fc75 100644 +--- a/src/security_selinux.c ++++ b/src/security_selinux.c +@@ -27,6 +27,7 @@ + #include "logging.h" + #include "pci.h" + #include "hostusb.h" ++#include "storage_file.h" + + #define VIR_FROM_THIS VIR_FROM_SECURITY + +@@ -403,10 +404,37 @@ SELinuxSetSecurityImageLabel(virConnectPtr conn, + + { + const virSecurityLabelDefPtr secdef = &vm->def->seclabel; ++ const char *path; + + if (!disk->src) + return 0; + ++ path = disk->src; ++ do { ++ virStorageFileMetadata meta; ++ int ret; ++ ++ memset(&meta, 0, sizeof(meta)); ++ ++ ret = virStorageFileGetMetadata(conn, path, &meta); ++ ++ if (path != disk->src) ++ VIR_FREE(path); ++ path = NULL; ++ ++ if (ret < 0) ++ return -1; ++ ++ if (meta.backingStore != NULL && ++ SELinuxSetFilecon(conn, meta.backingStore, ++ default_content_context) < 0) { ++ VIR_FREE(meta.backingStore); ++ return -1; ++ } ++ ++ path = meta.backingStore; ++ } while (path != NULL); ++ + if (disk->shared) { + return SELinuxSetFilecon(conn, disk->src, default_image_context); + } else if (disk->readonly) { +diff --git a/src/storage_backend.c b/src/storage_backend.c +index 800d4ea..1b65c5d 100644 +--- a/src/storage_backend.c ++++ b/src/storage_backend.c +@@ -51,6 +51,7 @@ + #include "internal.h" + #include "secret_conf.h" + #include "uuid.h" ++#include "storage_file.h" + #include "storage_backend.h" + #include "logging.h" + +@@ -462,16 +463,16 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, + char *create_tool; + short use_kvmimg; + +- const char *type = virStorageVolFormatFileSystemTypeToString(vol->target.format); ++ const char *type = virStorageFileFormatTypeToString(vol->target.format); + const char *backingType = vol->backingStore.path ? +- virStorageVolFormatFileSystemTypeToString(vol->backingStore.format) : NULL; ++ virStorageFileFormatTypeToString(vol->backingStore.format) : NULL; + + const char *inputBackingPath = (inputvol ? inputvol->backingStore.path + : NULL); + const char *inputPath = inputvol ? inputvol->target.path : NULL; + /* Treat input block devices as 'raw' format */ + const char *inputType = inputPath ? +- virStorageVolFormatFileSystemTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_VOL_FILE_RAW : inputvol->target.format) : ++ virStorageFileFormatTypeToString(inputvol->type == VIR_STORAGE_VOL_BLOCK ? VIR_STORAGE_FILE_RAW : inputvol->target.format) : + NULL; + + const char **imgargv; +@@ -552,8 +553,8 @@ virStorageBackendCreateQemuImg(virConnectPtr conn, + if (vol->target.encryption != NULL) { + virStorageEncryptionPtr enc; + +- if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW && +- vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { ++ if (vol->target.format != VIR_STORAGE_FILE_QCOW && ++ vol->target.format != VIR_STORAGE_FILE_QCOW2) { + virStorageReportError(conn, VIR_ERR_NO_SUPPORT, + _("qcow volume encryption unsupported with " + "volume format %s"), type); +@@ -644,7 +645,7 @@ virStorageBackendCreateQcowCreate(virConnectPtr conn, + return -1; + } + +- if (vol->target.format != VIR_STORAGE_VOL_FILE_QCOW2) { ++ if (vol->target.format != VIR_STORAGE_FILE_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported storage vol type %d"), + vol->target.format); +@@ -735,9 +736,9 @@ virStorageBackendGetBuildVolFromFunction(virConnectPtr conn, + * tool for converting + */ + if ((vol->type == VIR_STORAGE_VOL_FILE && +- vol->target.format != VIR_STORAGE_VOL_FILE_RAW) || ++ vol->target.format != VIR_STORAGE_FILE_RAW) || + (inputvol->type == VIR_STORAGE_VOL_FILE && +- inputvol->target.format != VIR_STORAGE_VOL_FILE_RAW)) { ++ inputvol->target.format != VIR_STORAGE_FILE_RAW)) { + + if ((tool_type = virStorageBackendFindFSImageTool(NULL)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, +diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c +index 01cb171..6816da8 100644 +--- a/src/storage_backend_fs.c ++++ b/src/storage_backend_fs.c +@@ -41,259 +41,24 @@ + #include "virterror_internal.h" + #include "storage_backend_fs.h" + #include "storage_conf.h" ++#include "storage_file.h" + #include "util.h" + #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_VOL_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_VOL_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_VOL_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_VOL_FILE_ISO, NULL, ".iso", +- 0, -1, 0, +- -1, 0, 0, -1, NULL }, +- /* Parallels */ +- /* XXX Untested +- { VIR_STORAGE_VOL_FILE_PARALLELS, "WithoutFreeSpace", NULL, +- LV_LITTLE_ENDIAN, 16, 2, +- 16+4+4+4+4, 4, 512, -1, NULL }, +- */ +- /* QCow */ +- { VIR_STORAGE_VOL_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_VOL_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_VOL_FILE_VMDK, "COWD", NULL, +- LV_LITTLE_ENDIAN, 4, 1, +- 4+4+4, 4, 512, -1, NULL }, +- */ +- /* VMDK 4 */ +- { VIR_STORAGE_VOL_FILE_VMDK, "KDMV", NULL, +- LV_LITTLE_ENDIAN, 4, 1, +- 4+4+4, 8, 512, -1, vmdk4GetBackingStore }, +- /* Connectix / VirtualPC */ +- /* XXX Untested +- { VIR_STORAGE_VOL_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) ++virStorageBackendProbeTarget(virConnectPtr conn, ++ virStorageVolTargetPtr target, ++ char **backingStore, ++ unsigned long long *allocation, ++ unsigned long long *capacity, ++ virStorageEncryptionPtr *encryption) + { +- 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; ++ int fd, ret; ++ virStorageFileMetadata meta; + +- if (backingStore) +- *backingStore = NULL; + if (encryption) + *encryption = NULL; + +@@ -311,148 +76,51 @@ static int virStorageBackendProbeTarget(virConnectPtr conn, + 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); ++ memset(&meta, 0, sizeof(meta)); ++ ++ if (virStorageFileGetMetadataFromFD(conn, target->path, fd, &meta) < 0) { + 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; ++ target->format = meta.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; ++ if (backingStore) { ++ *backingStore = meta.backingStore; ++ meta.backingStore = NULL; ++ } + +- switch (fileTypeInfo[i].getBackingStore(conn, &base, head, len)) { +- case BACKING_STORE_OK: +- break; ++ VIR_FREE(meta.backingStore); + +- case BACKING_STORE_INVALID: +- continue; ++ if (capacity && meta.capacity) ++ *capacity = meta.capacity; + +- 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 && meta.encrypted) { ++ if (VIR_ALLOC(*encryption) < 0) { ++ virReportOOMError(conn); ++ if (backingStore) ++ VIR_FREE(*backingStore); ++ 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 +- */ ++ switch (target->format) { ++ case VIR_STORAGE_FILE_QCOW: ++ case VIR_STORAGE_FILE_QCOW2: ++ (*encryption)->format = VIR_STORAGE_ENCRYPTION_FORMAT_QCOW; ++ break; ++ default: ++ break; + } +- 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; ++ /* 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 ++ */ + } + +- /* All fails, so call it a raw file */ +- target->format = VIR_STORAGE_VOL_FILE_RAW; + return 0; + } + +@@ -891,7 +559,7 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn, + goto no_memory; + + vol->type = VIR_STORAGE_VOL_FILE; +- vol->target.format = VIR_STORAGE_VOL_FILE_RAW; /* Real value is filled in during probe */ ++ vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */ + if (virAsprintf(&vol->target.path, "%s/%s", + pool->def->target.path, + vol->name) == -1) +@@ -918,7 +586,7 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn, + } + + if (backingStore != NULL) { +- if (vol->target.format == VIR_STORAGE_VOL_FILE_QCOW2 && ++ if (vol->target.format == VIR_STORAGE_FILE_QCOW2 && + STRPREFIX("fmt:", backingStore)) { + char *fmtstr = backingStore + 4; + char *path = strchr(fmtstr, ':'); +@@ -927,7 +595,7 @@ virStorageBackendFileSystemRefresh(virConnectPtr conn, + } else { + *path = '\0'; + if ((vol->backingStore.format = +- virStorageVolFormatFileSystemTypeFromString(fmtstr)) < 0) { ++ virStorageFileFormatTypeFromString(fmtstr)) < 0) { + VIR_FREE(backingStore); + } else { + memmove(backingStore, path, strlen(path) + 1); +@@ -1121,9 +789,9 @@ _virStorageBackendFileSystemVolBuild(virConnectPtr conn, + inputvol); + if (!create_func) + return -1; +- } else if (vol->target.format == VIR_STORAGE_VOL_FILE_RAW) { ++ } else if (vol->target.format == VIR_STORAGE_FILE_RAW) { + create_func = virStorageBackendCreateRaw; +- } else if (vol->target.format == VIR_STORAGE_VOL_FILE_DIR) { ++ } else if (vol->target.format == VIR_STORAGE_FILE_DIR) { + create_func = createFileDir; + } else if ((tool_type = virStorageBackendFindFSImageTool(NULL)) != -1) { + create_func = virStorageBackendFSImageToolTypeToFunc(conn, tool_type); +diff --git a/src/storage_conf.c b/src/storage_conf.c +index cb063cc..788de15 100644 +--- a/src/storage_conf.c ++++ b/src/storage_conf.c +@@ -36,6 +36,7 @@ + #include "virterror_internal.h" + #include "datatypes.h" + #include "storage_conf.h" ++#include "storage_file.h" + + #include "xml.h" + #include "uuid.h" +@@ -82,12 +83,6 @@ VIR_ENUM_IMPL(virStorageVolFormatDisk, + "linux-lvm", "linux-raid", + "extended") + +-VIR_ENUM_IMPL(virStorageVolFormatFileSystem, +- VIR_STORAGE_VOL_FILE_LAST, +- "raw", "dir", "bochs", +- "cloop", "cow", "dmg", "iso", +- "qcow", "qcow2", "vmdk", "vpc") +- + VIR_ENUM_IMPL(virStoragePartedFsType, + VIR_STORAGE_PARTED_FS_TYPE_LAST, + "ext2", "ext2", "fat16", +@@ -150,9 +145,9 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { + }, + { .poolType = VIR_STORAGE_POOL_DIR, + .volOptions = { +- .defaultFormat = VIR_STORAGE_VOL_FILE_RAW, +- .formatFromString = virStorageVolFormatFileSystemTypeFromString, +- .formatToString = virStorageVolFormatFileSystemTypeToString, ++ .defaultFormat = VIR_STORAGE_FILE_RAW, ++ .formatFromString = virStorageFileFormatTypeFromString, ++ .formatToString = virStorageFileFormatTypeToString, + }, + }, + { .poolType = VIR_STORAGE_POOL_FS, +@@ -162,9 +157,9 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { + .formatToString = virStoragePoolFormatFileSystemTypeToString, + }, + .volOptions = { +- .defaultFormat = VIR_STORAGE_VOL_FILE_RAW, +- .formatFromString = virStorageVolFormatFileSystemTypeFromString, +- .formatToString = virStorageVolFormatFileSystemTypeToString, ++ .defaultFormat = VIR_STORAGE_FILE_RAW, ++ .formatFromString = virStorageFileFormatTypeFromString, ++ .formatToString = virStorageFileFormatTypeToString, + }, + }, + { .poolType = VIR_STORAGE_POOL_NETFS, +@@ -176,9 +171,9 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { + .formatToString = virStoragePoolFormatFileSystemNetTypeToString, + }, + .volOptions = { +- .defaultFormat = VIR_STORAGE_VOL_FILE_RAW, +- .formatFromString = virStorageVolFormatFileSystemTypeFromString, +- .formatToString = virStorageVolFormatFileSystemTypeToString, ++ .defaultFormat = VIR_STORAGE_FILE_RAW, ++ .formatFromString = virStorageFileFormatTypeFromString, ++ .formatToString = virStorageFileFormatTypeToString, + }, + }, + { .poolType = VIR_STORAGE_POOL_ISCSI, +diff --git a/src/storage_conf.h b/src/storage_conf.h +index 421d305..9fedb12 100644 +--- a/src/storage_conf.h ++++ b/src/storage_conf.h +@@ -429,23 +429,6 @@ enum virStoragePoolFormatLogical { + }; + VIR_ENUM_DECL(virStoragePoolFormatLogical) + +- +-enum virStorageVolFormatFileSystem { +- VIR_STORAGE_VOL_FILE_RAW = 0, +- VIR_STORAGE_VOL_FILE_DIR, +- VIR_STORAGE_VOL_FILE_BOCHS, +- VIR_STORAGE_VOL_FILE_CLOOP, +- VIR_STORAGE_VOL_FILE_COW, +- VIR_STORAGE_VOL_FILE_DMG, +- VIR_STORAGE_VOL_FILE_ISO, +- VIR_STORAGE_VOL_FILE_QCOW, +- VIR_STORAGE_VOL_FILE_QCOW2, +- VIR_STORAGE_VOL_FILE_VMDK, +- VIR_STORAGE_VOL_FILE_VPC, +- VIR_STORAGE_VOL_FILE_LAST, +-}; +-VIR_ENUM_DECL(virStorageVolFormatFileSystem) +- + /* + * XXX these are basically partition types. + * +diff --git a/src/storage_file.c b/src/storage_file.c +new file mode 100644 +index 0000000..44057d2 +--- /dev/null ++++ b/src/storage_file.c +@@ -0,0 +1,424 @@ ++/* ++ * storage_file.c: file utility functions for FS storage backend ++ * ++ * Copyright (C) 2007-2009 Red Hat, Inc. ++ * Copyright (C) 2007-2008 Daniel P. Berrange ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Daniel P. Berrange ++ */ ++ ++#include ++#include "storage_file.h" ++ ++#include ++#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; ++} ++ ++int ++virStorageFileGetMetadata(virConnectPtr conn, ++ const char *path, ++ virStorageFileMetadata *meta) ++{ ++ int fd, ret; ++ ++ if ((fd = open(path, O_RDONLY)) < 0) { ++ virReportSystemError(conn, errno, _("cannot open file '%s'"), path); ++ return -1; ++ } ++ ++ ret = virStorageFileGetMetadataFromFD(conn, path, fd, meta); ++ ++ close(fd); ++ ++ return ret; ++} +diff --git a/src/storage_file.h b/src/storage_file.h +new file mode 100644 +index 0000000..b0abcaf +--- /dev/null ++++ b/src/storage_file.h +@@ -0,0 +1,62 @@ ++/* ++ * storage_file.c: file utility functions for FS storage backend ++ * ++ * Copyright (C) 2007-2009 Red Hat, Inc. ++ * Copyright (C) 2007-2008 Daniel P. Berrange ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2.1 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Author: Daniel P. Berrange ++ */ ++ ++#ifndef __VIR_STORAGE_FILE_H__ ++#define __VIR_STORAGE_FILE_H__ ++ ++#include "util.h" ++#include ++ ++enum virStorageFileFormat { ++ VIR_STORAGE_FILE_RAW = 0, ++ VIR_STORAGE_FILE_DIR, ++ VIR_STORAGE_FILE_BOCHS, ++ VIR_STORAGE_FILE_CLOOP, ++ VIR_STORAGE_FILE_COW, ++ VIR_STORAGE_FILE_DMG, ++ VIR_STORAGE_FILE_ISO, ++ VIR_STORAGE_FILE_QCOW, ++ VIR_STORAGE_FILE_QCOW2, ++ VIR_STORAGE_FILE_VMDK, ++ VIR_STORAGE_FILE_VPC, ++ VIR_STORAGE_FILE_LAST, ++}; ++ ++VIR_ENUM_DECL(virStorageFileFormat); ++ ++typedef struct _virStorageFileMetadata { ++ int format; ++ char *backingStore; ++ unsigned long long capacity; ++ bool encrypted; ++} virStorageFileMetadata; ++ ++int virStorageFileGetMetadata(virConnectPtr conn, ++ const char *path, ++ virStorageFileMetadata *meta); ++int virStorageFileGetMetadataFromFD(virConnectPtr conn, ++ const char *path, ++ int fd, ++ virStorageFileMetadata *meta); ++ ++#endif /* __VIR_STORAGE_FILE_H__ */ +diff --git a/src/util.h b/src/util.h +index f9715ab..75afecc 100644 +--- a/src/util.h ++++ b/src/util.h +@@ -26,6 +26,7 @@ + #define __VIR_UTIL_H__ + + #include "verify.h" ++#include "internal.h" + #include + #include + +diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c +index 7270710..783a216 100644 +--- a/src/vbox/vbox_tmpl.c ++++ b/src/vbox/vbox_tmpl.c +@@ -45,6 +45,7 @@ + #include "virterror_internal.h" + #include "domain_event.h" + #include "storage_conf.h" ++#include "storage_file.h" + #include "uuid.h" + #include "event.h" + #include "memory.h" +@@ -5980,14 +5981,14 @@ static virStorageVolPtr vboxStorageVolCreateXML(virStoragePoolPtr pool, + + /* TODO: for now only the vmdk, vpc and vdi type harddisk + * variants can be created, also since there is no vdi +- * type in enum virStorageVolFormatFileSystem {} the default ++ * type in enum virStorageFileFormat {} the default + * will be to create vdi if nothing is specified in + * def->target.format + */ + +- if (def->target.format == VIR_STORAGE_VOL_FILE_VMDK) { ++ if (def->target.format == VIR_STORAGE_FILE_VMDK) { + data->pFuncs->pfnUtf8ToUtf16("VMDK", &hddFormatUtf16); +- } else if (def->target.format == VIR_STORAGE_VOL_FILE_VPC) { ++ } else if (def->target.format == VIR_STORAGE_FILE_VPC) { + data->pFuncs->pfnUtf8ToUtf16("VHD", &hddFormatUtf16); + } else { + data->pFuncs->pfnUtf8ToUtf16("VDI", &hddFormatUtf16); +@@ -6302,13 +6303,13 @@ static char *vboxStorageVolGetXMLDesc(virStorageVolPtr vol, unsigned int flags A + DEBUG("Storage Volume Format: %s", hddFormatUtf8); + + if (STRCASEEQ("vmdk", hddFormatUtf8)) +- def.target.format = VIR_STORAGE_VOL_FILE_VMDK; ++ def.target.format = VIR_STORAGE_FILE_VMDK; + else if (STRCASEEQ("vhd", hddFormatUtf8)) +- def.target.format = VIR_STORAGE_VOL_FILE_VPC; ++ def.target.format = VIR_STORAGE_FILE_VPC; + else +- def.target.format = VIR_STORAGE_VOL_FILE_RAW; ++ def.target.format = VIR_STORAGE_FILE_RAW; + +- /* TODO: need to add vdi to enum virStorageVolFormatFileSystem {} ++ /* TODO: need to add vdi to enum virStorageFileFormat {} + * and then add it here + */ + +-- +1.6.2.5 + diff --git a/libvirt.spec b/libvirt.spec index 5f59d77..bbdb081 100644 --- a/libvirt.spec +++ b/libvirt.spec @@ -172,6 +172,9 @@ Patch05: libvirt-fix-usb-device-passthrough.patch # Disable sound backend (#524499, #508317) Patch06: libvirt-disable-audio-backend.patch +# Re-label qcow2 backing files (#497131) +Patch07: libvirt-svirt-relabel-qcow2-backing-files.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root URL: http://libvirt.org/ BuildRequires: python-devel @@ -390,6 +393,7 @@ of recent versions of Linux (and other OSes). %patch04 -p1 %patch05 -p1 %patch06 -p1 +%patch07 -p1 %build %if ! %{with_xen} @@ -782,6 +786,7 @@ fi %changelog * Thu Oct 1 2009 Mark McLoughlin - 0.7.1-8 - Disable sound backend, even when selinux is disabled (#524499) +- Re-label qcow2 backing files (#497131) * Wed Sep 30 2009 Mark McLoughlin - 0.7.1-7 - Fix USB device passthrough (#522683)