[libvirt] [PATCHv2 00/16] qemu block-commit support

I figured I'd better post this now, while I still iron out the remaining kinks in my SELinux handling (but I'm certainly a lot closer, now that I have libvirt actually identifying which files need the temporary SELinux labels). With this, you can now do things like 'virsh blockcommit $dom $disk --top relative_name --shallow' even when the backing chain refers to an absolute backing file name; the QMP command passed to qemu will have the correct absolute names. Patches 1-6: practically unchanged since v1, although not reviewed yet https://www.redhat.com/archives/libvir-list/2012-October/msg00075.html Patches 7-13: new to this series Patches 14-15: minor changes since v1 ACKs; see details in each commit https://www.redhat.com/archives/libvir-list/2012-October/msg00089.html Patch 16: new to this series Also available at: http://repo.or.cz/w/libvirt/ericb.git/shortlog/refs/heads/blockjob git fetch git://repo.or.cz/libvirt/ericb.git blockjob Eric Blake (16): storage: list more file types storage: treat 'aio' like 'raw' at parse time storage: match RNG to supported driver types storage: use enum for default driver type storage: use enum for disk driver type storage: use enum for snapshot driver type storage: don't probe non-files storage: get entire metadata chain in one call storage: don't require caller to pre-allocate metadata struct storage: remember relative names in backing chain storage: make it easier to find file within chain storage: cache backing chain while qemu domain is live storage: use cache to walk backing chain blockjob: manage qemu block-commit monitor command blockjob: wire up online qemu block-commit blockjob: implement shallow commit flag in qemu docs/schemas/domaincommon.rng | 27 +- docs/schemas/domainsnapshot.rng | 2 +- src/conf/capabilities.h | 4 +- src/conf/domain_conf.c | 160 ++++-------- src/conf/domain_conf.h | 8 +- src/conf/snapshot_conf.c | 23 +- src/conf/snapshot_conf.h | 2 +- src/conf/storage_conf.c | 15 +- src/libvirt.c | 2 - src/libvirt_private.syms | 1 + src/libxl/libxl_conf.c | 42 ++-- src/libxl/libxl_driver.c | 6 +- src/qemu/qemu_capabilities.c | 3 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_cgroup.c | 12 +- src/qemu/qemu_command.c | 18 +- src/qemu/qemu_domain.c | 40 ++- src/qemu/qemu_domain.h | 3 + src/qemu/qemu_driver.c | 236 +++++++++++------ src/qemu/qemu_hotplug.c | 9 +- src/qemu/qemu_monitor.c | 30 +++ src/qemu/qemu_monitor.h | 7 + src/qemu/qemu_monitor_json.c | 34 +++ src/qemu/qemu_monitor_json.h | 7 + src/qemu/qemu_process.c | 23 ++ src/security/security_dac.c | 7 - src/security/security_selinux.c | 11 - src/security/virt-aa-helper.c | 27 +- src/storage/storage_backend_fs.c | 15 +- src/util/storage_file.c | 279 ++++++++++++++++----- src/util/storage_file.h | 43 ++-- src/vbox/vbox_tmpl.c | 6 +- src/xenxs/xen_sxpr.c | 26 +- src/xenxs/xen_xm.c | 28 ++- tests/sexpr2xmldata/sexpr2xml-curmem.xml | 2 +- .../sexpr2xml-disk-block-shareable.xml | 2 +- .../sexpr2xml-disk-drv-blktap-raw.xml | 2 +- .../sexpr2xml-disk-drv-blktap2-raw.xml | 2 +- 38 files changed, 773 insertions(+), 392 deletions(-) -- 1.7.11.7

When an image has no backing file, using VIR_STORAGE_FILE_AUTO for its type is a bit confusing. Additionally, a future patch would like to reserve a default value for the case of no file type specified in the XML, but different from the current use of -1 to imply probing, since probing is not always safe. Also, a couple of file types were missing compared to supported code: libxl supports 'vhd', and qemu supports 'fat' for directories passed through as a file system. * src/util/storage_file.h (virStorageFileFormat): Add VIR_STORAGE_FILE_NONE, VIR_STORAGE_FILE_FAT, VIR_STORAGE_FILE_VHD. * src/util/storage_file.c (virStorageFileMatchesVersion): Match documentation when version probing not supported. (cowGetBackingStore, qcowXGetBackingStore, qcow1GetBackingStore) (qedGetBackingStore, virStorageFileGetMetadataFromBuf) (virStorageFileGetMetadataFromFD): Take NONE into account. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo): Likewise. * src/conf/storage_conf.c (virStorageVolumeFormatFromString): New function. (poolTypeInfo): Use it. --- src/conf/domain_conf.c | 2 +- src/conf/storage_conf.c | 15 ++++++++--- src/qemu/qemu_driver.c | 2 +- src/util/storage_file.c | 69 +++++++++++++++++++++++++++++++++++-------------- src/util/storage_file.h | 8 ++++-- 5 files changed, 69 insertions(+), 27 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c87c615..460a57c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14648,7 +14648,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (STREQ(formatStr, "aio")) formatStr = "raw"; /* Xen compat */ - if ((format = virStorageFileFormatTypeFromString(formatStr)) < 0) { + if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown disk format '%s' for %s"), disk->driverType, disk->src); diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 4daf059..5d9e61a 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -135,6 +135,15 @@ struct _virStoragePoolTypeInfo { virStorageVolOptions volOptions; }; +static int +virStorageVolumeFormatFromString(const char *format) +{ + int ret = virStorageFileFormatTypeFromString(format); + if (ret == VIR_STORAGE_FILE_NONE) + return -1; + return ret; +} + static virStoragePoolTypeInfo poolTypeInfo[] = { { .poolType = VIR_STORAGE_POOL_LOGICAL, .poolOptions = { @@ -148,7 +157,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { { .poolType = VIR_STORAGE_POOL_DIR, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, @@ -161,7 +170,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, @@ -175,7 +184,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0787039..0e4b2d0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9192,7 +9192,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, /* Probe for magic formats */ if (disk->driverType) { - if ((format = virStorageFileFormatTypeFromString(disk->driverType)) < 0) { + if ((format = virStorageFileFormatTypeFromString(disk->driverType)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown disk format %s for %s"), disk->driverType, disk->src); diff --git a/src/util/storage_file.c b/src/util/storage_file.c index eea760c..3d1b7cf 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1,7 +1,7 @@ /* * storage_file.c: file utility functions for FS storage backend * - * Copyright (C) 2007-2011 Red Hat, Inc. + * Copyright (C) 2007-2012 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -45,9 +45,11 @@ VIR_ENUM_IMPL(virStorageFileFormat, VIR_STORAGE_FILE_LAST, + "none", "raw", "dir", "bochs", "cloop", "cow", "dmg", "iso", - "qcow", "qcow2", "qed", "vmdk", "vpc") + "qcow", "qcow2", "qed", "vmdk", "vpc", + "fat", "vhd") enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ @@ -123,8 +125,12 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t); static struct FileTypeInfo const fileTypeInfo[] = { - [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN, -1, 0, 0, 0, 0, 0, NULL }, - [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN, -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_NONE] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, [VIR_STORAGE_FILE_BOCHS] = { /*"Bochs Virtual HD Image", */ /* Untested */ NULL, NULL, @@ -180,6 +186,11 @@ static struct FileTypeInfo const fileTypeInfo[] = { LV_BIG_ENDIAN, 12, 0x10000, 8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, -1, NULL }, + /* Not direct file formats, but used for various drivers */ + [VIR_STORAGE_FILE_FAT] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_VHD] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, }; verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST); @@ -195,8 +206,10 @@ cowGetBackingStore(char **res, if (buf_size < 4+4+ COW_FILENAME_MAXLEN) return BACKING_STORE_INVALID; - if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */ + if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */ + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + } *res = strndup ((const char*)buf + 4+4, COW_FILENAME_MAXLEN); if (*res == NULL) { @@ -298,8 +311,11 @@ qcowXGetBackingStore(char **res, | (buf[QCOWX_HDR_BACKING_FILE_SIZE+1] << 16) | (buf[QCOWX_HDR_BACKING_FILE_SIZE+2] << 8) | buf[QCOWX_HDR_BACKING_FILE_SIZE+3]); /* QCowHeader.backing_file_size */ - if (size == 0) + if (size == 0) { + if (format) + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + } if (offset + size > buf_size || offset + size < offset) return BACKING_STORE_INVALID; if (size + 1 == 0) @@ -335,8 +351,12 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) - qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, offset); + if (isQCow2 && format) { + qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, + offset); + if (*format <= VIR_STORAGE_FILE_NONE) + return BACKING_STORE_INVALID; + } return BACKING_STORE_OK; } @@ -348,10 +368,15 @@ qcow1GetBackingStore(char **res, const unsigned char *buf, size_t buf_size) { + int ret; + /* QCow1 doesn't have the extensions capability * used to store backing format */ *format = VIR_STORAGE_FILE_AUTO; - return qcowXGetBackingStore(res, NULL, buf, buf_size, false); + ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false); + if (ret == 0 && *buf == '\0') + *format = VIR_STORAGE_FILE_NONE; + return ret; } static int @@ -401,6 +426,7 @@ vmdk4GetBackingStore(char **res, desc[len] = '\0'; start = strstr(desc, prefix); if (start == NULL) { + *format = VIR_STORAGE_FILE_NONE; ret = BACKING_STORE_OK; goto cleanup; } @@ -411,6 +437,7 @@ vmdk4GetBackingStore(char **res, goto cleanup; } if (end == start) { + *format = VIR_STORAGE_FILE_NONE; ret = BACKING_STORE_OK; goto cleanup; } @@ -464,8 +491,10 @@ qedGetBackingStore(char **res, if (buf_size < QED_HDR_FEATURES_OFFSET+8) return BACKING_STORE_INVALID; flags = qedGetHeaderULL(buf + QED_HDR_FEATURES_OFFSET); - if (!(flags & QED_F_BACKING_FILE)) + if (!(flags & QED_F_BACKING_FILE)) { + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + } /* Parse the backing file */ if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8) @@ -485,12 +514,10 @@ qedGetBackingStore(char **res, memcpy(*res, buf + offset, size); (*res)[size] = '\0'; - if (format) { - if (flags & QED_F_BACKING_FORMAT_NO_PROBE) - *format = virStorageFileFormatTypeFromString("raw"); - else - *format = VIR_STORAGE_FILE_AUTO_SAFE; - } + if (flags & QED_F_BACKING_FORMAT_NO_PROBE) + *format = virStorageFileFormatTypeFromString("raw"); + else + *format = VIR_STORAGE_FILE_AUTO_SAFE; return BACKING_STORE_OK; } @@ -564,7 +591,7 @@ virStorageFileMatchesVersion(int format, /* Validate version number info */ if (fileTypeInfo[format].versionOffset == -1) - return true; + return false; if ((fileTypeInfo[format].versionOffset + 4) > buflen) return false; @@ -607,7 +634,9 @@ virStorageFileGetMetadataFromBuf(int format, /* XXX we should consider moving virStorageBackendUpdateVolInfo * code into this method, for non-magic files */ - if (!fileTypeInfo[format].magic) { + if (format <= VIR_STORAGE_FILE_NONE || + format >= VIR_STORAGE_FILE_LAST || + !fileTypeInfo[format].magic) { return 0; } @@ -682,7 +711,7 @@ virStorageFileGetMetadataFromBuf(int format, meta->backingStoreFormat = backingFormat; } else { meta->backingStore = NULL; - meta->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + meta->backingStoreFormat = VIR_STORAGE_FILE_NONE; } } @@ -867,7 +896,7 @@ virStorageFileGetMetadataFromFD(const char *path, if (format == VIR_STORAGE_FILE_AUTO) format = virStorageFileProbeFormatFromBuf(path, head, len); - if (format < 0 || + if (format <= VIR_STORAGE_FILE_NONE || format >= VIR_STORAGE_FILE_LAST) { virReportSystemError(EINVAL, _("unknown storage file format %d"), format); diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 2e76b1b..683ec73 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -1,7 +1,7 @@ /* * storage_file.c: file utility functions for FS storage backend * - * Copyright (C) 2007-2009 Red Hat, Inc. + * Copyright (C) 2007-2009, 2012 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -29,7 +29,8 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_AUTO_SAFE = -2, VIR_STORAGE_FILE_AUTO = -1, - VIR_STORAGE_FILE_RAW = 0, + VIR_STORAGE_FILE_NONE = 0, + VIR_STORAGE_FILE_RAW, VIR_STORAGE_FILE_DIR, VIR_STORAGE_FILE_BOCHS, VIR_STORAGE_FILE_CLOOP, @@ -41,6 +42,9 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_QED, VIR_STORAGE_FILE_VMDK, VIR_STORAGE_FILE_VPC, + VIR_STORAGE_FILE_FAT, + VIR_STORAGE_FILE_VHD, + VIR_STORAGE_FILE_LAST, }; -- 1.7.11.7

On Sat, Oct 13, 2012 at 4:59 PM, Eric Blake <eblake@redhat.com> wrote:
When an image has no backing file, using VIR_STORAGE_FILE_AUTO for its type is a bit confusing. Additionally, a future patch would like to reserve a default value for the case of no file type specified in the XML, but different from the current use of -1 to imply probing, since probing is not always safe.
Also, a couple of file types were missing compared to supported code: libxl supports 'vhd', and qemu supports 'fat' for directories passed through as a file system.
* src/util/storage_file.h (virStorageFileFormat): Add VIR_STORAGE_FILE_NONE, VIR_STORAGE_FILE_FAT, VIR_STORAGE_FILE_VHD. * src/util/storage_file.c (virStorageFileMatchesVersion): Match documentation when version probing not supported. (cowGetBackingStore, qcowXGetBackingStore, qcow1GetBackingStore) (qedGetBackingStore, virStorageFileGetMetadataFromBuf) (virStorageFileGetMetadataFromFD): Take NONE into account. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo): Likewise. * src/conf/storage_conf.c (virStorageVolumeFormatFromString): New function. (poolTypeInfo): Use it. --- src/conf/domain_conf.c | 2 +- src/conf/storage_conf.c | 15 ++++++++--- src/qemu/qemu_driver.c | 2 +- src/util/storage_file.c | 69 +++++++++++++++++++++++++++++++++++-------------- src/util/storage_file.h | 8 ++++-- 5 files changed, 69 insertions(+), 27 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index c87c615..460a57c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14648,7 +14648,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (STREQ(formatStr, "aio")) formatStr = "raw"; /* Xen compat */
- if ((format = virStorageFileFormatTypeFromString(formatStr)) < 0) { + if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown disk format '%s' for %s"), disk->driverType, disk->src); diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c index 4daf059..5d9e61a 100644 --- a/src/conf/storage_conf.c +++ b/src/conf/storage_conf.c @@ -135,6 +135,15 @@ struct _virStoragePoolTypeInfo { virStorageVolOptions volOptions; };
+static int +virStorageVolumeFormatFromString(const char *format) +{ + int ret = virStorageFileFormatTypeFromString(format); + if (ret == VIR_STORAGE_FILE_NONE) + return -1; + return ret; +} + static virStoragePoolTypeInfo poolTypeInfo[] = { { .poolType = VIR_STORAGE_POOL_LOGICAL, .poolOptions = { @@ -148,7 +157,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { { .poolType = VIR_STORAGE_POOL_DIR, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, @@ -161,7 +170,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, @@ -175,7 +184,7 @@ static virStoragePoolTypeInfo poolTypeInfo[] = { }, .volOptions = { .defaultFormat = VIR_STORAGE_FILE_RAW, - .formatFromString = virStorageFileFormatTypeFromString, + .formatFromString = virStorageVolumeFormatFromString, .formatToString = virStorageFileFormatTypeToString, }, }, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0787039..0e4b2d0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9192,7 +9192,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom,
/* Probe for magic formats */ if (disk->driverType) { - if ((format = virStorageFileFormatTypeFromString(disk->driverType)) < 0) { + if ((format = virStorageFileFormatTypeFromString(disk->driverType)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown disk format %s for %s"), disk->driverType, disk->src); diff --git a/src/util/storage_file.c b/src/util/storage_file.c index eea760c..3d1b7cf 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1,7 +1,7 @@ /* * storage_file.c: file utility functions for FS storage backend * - * Copyright (C) 2007-2011 Red Hat, Inc. + * Copyright (C) 2007-2012 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -45,9 +45,11 @@
VIR_ENUM_IMPL(virStorageFileFormat, VIR_STORAGE_FILE_LAST, + "none", "raw", "dir", "bochs", "cloop", "cow", "dmg", "iso", - "qcow", "qcow2", "qed", "vmdk", "vpc") + "qcow", "qcow2", "qed", "vmdk", "vpc", + "fat", "vhd")
enum lv_endian { LV_LITTLE_ENDIAN = 1, /* 1234 */ @@ -123,8 +125,12 @@ qedGetBackingStore(char **, int *, const unsigned char *, size_t);
static struct FileTypeInfo const fileTypeInfo[] = { - [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN, -1, 0, 0, 0, 0, 0, NULL }, - [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN, -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_NONE] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, [VIR_STORAGE_FILE_BOCHS] = { /*"Bochs Virtual HD Image", */ /* Untested */ NULL, NULL, @@ -180,6 +186,11 @@ static struct FileTypeInfo const fileTypeInfo[] = { LV_BIG_ENDIAN, 12, 0x10000, 8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, -1, NULL }, + /* Not direct file formats, but used for various drivers */ + [VIR_STORAGE_FILE_FAT] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, + [VIR_STORAGE_FILE_VHD] = { NULL, NULL, LV_LITTLE_ENDIAN, + -1, 0, 0, 0, 0, 0, NULL }, }; verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST);
@@ -195,8 +206,10 @@ cowGetBackingStore(char **res,
if (buf_size < 4+4+ COW_FILENAME_MAXLEN) return BACKING_STORE_INVALID; - if (buf[4+4] == '\0') /* cow_header_v2.backing_file[0] */ + if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */ + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + }
*res = strndup ((const char*)buf + 4+4, COW_FILENAME_MAXLEN); if (*res == NULL) { @@ -298,8 +311,11 @@ qcowXGetBackingStore(char **res, | (buf[QCOWX_HDR_BACKING_FILE_SIZE+1] << 16) | (buf[QCOWX_HDR_BACKING_FILE_SIZE+2] << 8) | buf[QCOWX_HDR_BACKING_FILE_SIZE+3]); /* QCowHeader.backing_file_size */ - if (size == 0) + if (size == 0) { + if (format) + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + } if (offset + size > buf_size || offset + size < offset) return BACKING_STORE_INVALID; if (size + 1 == 0) @@ -335,8 +351,12 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) - qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, offset); + if (isQCow2 && format) { + qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, + offset); + if (*format <= VIR_STORAGE_FILE_NONE) + return BACKING_STORE_INVALID; + }
return BACKING_STORE_OK; } @@ -348,10 +368,15 @@ qcow1GetBackingStore(char **res, const unsigned char *buf, size_t buf_size) { + int ret; + /* QCow1 doesn't have the extensions capability * used to store backing format */ *format = VIR_STORAGE_FILE_AUTO; - return qcowXGetBackingStore(res, NULL, buf, buf_size, false); + ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false); + if (ret == 0 && *buf == '\0') + *format = VIR_STORAGE_FILE_NONE; + return ret; }
static int @@ -401,6 +426,7 @@ vmdk4GetBackingStore(char **res, desc[len] = '\0'; start = strstr(desc, prefix); if (start == NULL) { + *format = VIR_STORAGE_FILE_NONE; ret = BACKING_STORE_OK; goto cleanup; } @@ -411,6 +437,7 @@ vmdk4GetBackingStore(char **res, goto cleanup; } if (end == start) { + *format = VIR_STORAGE_FILE_NONE; ret = BACKING_STORE_OK; goto cleanup; } @@ -464,8 +491,10 @@ qedGetBackingStore(char **res, if (buf_size < QED_HDR_FEATURES_OFFSET+8) return BACKING_STORE_INVALID; flags = qedGetHeaderULL(buf + QED_HDR_FEATURES_OFFSET); - if (!(flags & QED_F_BACKING_FILE)) + if (!(flags & QED_F_BACKING_FILE)) { + *format = VIR_STORAGE_FILE_NONE; return BACKING_STORE_OK; + }
/* Parse the backing file */ if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8) @@ -485,12 +514,10 @@ qedGetBackingStore(char **res, memcpy(*res, buf + offset, size); (*res)[size] = '\0';
- if (format) { - if (flags & QED_F_BACKING_FORMAT_NO_PROBE) - *format = virStorageFileFormatTypeFromString("raw"); - else - *format = VIR_STORAGE_FILE_AUTO_SAFE; - } + if (flags & QED_F_BACKING_FORMAT_NO_PROBE) + *format = virStorageFileFormatTypeFromString("raw"); + else + *format = VIR_STORAGE_FILE_AUTO_SAFE;
return BACKING_STORE_OK; } @@ -564,7 +591,7 @@ virStorageFileMatchesVersion(int format,
/* Validate version number info */ if (fileTypeInfo[format].versionOffset == -1) - return true; + return false;
if ((fileTypeInfo[format].versionOffset + 4) > buflen) return false; @@ -607,7 +634,9 @@ virStorageFileGetMetadataFromBuf(int format, /* XXX we should consider moving virStorageBackendUpdateVolInfo * code into this method, for non-magic files */ - if (!fileTypeInfo[format].magic) { + if (format <= VIR_STORAGE_FILE_NONE || + format >= VIR_STORAGE_FILE_LAST || + !fileTypeInfo[format].magic) { return 0; }
@@ -682,7 +711,7 @@ virStorageFileGetMetadataFromBuf(int format, meta->backingStoreFormat = backingFormat; } else { meta->backingStore = NULL; - meta->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + meta->backingStoreFormat = VIR_STORAGE_FILE_NONE; } }
@@ -867,7 +896,7 @@ virStorageFileGetMetadataFromFD(const char *path, if (format == VIR_STORAGE_FILE_AUTO) format = virStorageFileProbeFormatFromBuf(path, head, len);
- if (format < 0 || + if (format <= VIR_STORAGE_FILE_NONE || format >= VIR_STORAGE_FILE_LAST) { virReportSystemError(EINVAL, _("unknown storage file format %d"), format); diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 2e76b1b..683ec73 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -1,7 +1,7 @@ /* * storage_file.c: file utility functions for FS storage backend * - * Copyright (C) 2007-2009 Red Hat, Inc. + * Copyright (C) 2007-2009, 2012 Red Hat, Inc. * Copyright (C) 2007-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -29,7 +29,8 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_AUTO_SAFE = -2, VIR_STORAGE_FILE_AUTO = -1, - VIR_STORAGE_FILE_RAW = 0, + VIR_STORAGE_FILE_NONE = 0, + VIR_STORAGE_FILE_RAW, VIR_STORAGE_FILE_DIR, VIR_STORAGE_FILE_BOCHS, VIR_STORAGE_FILE_CLOOP, @@ -41,6 +42,9 @@ enum virStorageFileFormat { VIR_STORAGE_FILE_QED, VIR_STORAGE_FILE_VMDK, VIR_STORAGE_FILE_VPC, + VIR_STORAGE_FILE_FAT, + VIR_STORAGE_FILE_VHD, + VIR_STORAGE_FILE_LAST, };
--
Visual ACK. I'll try to apply and build it later. -- Doug Goldstein

On 10/13/2012 03:59 PM, Eric Blake wrote:
When an image has no backing file, using VIR_STORAGE_FILE_AUTO for its type is a bit confusing. Additionally, a future patch would like to reserve a default value for the case of no file type specified in the XML, but different from the current use of -1 to imply probing, since probing is not always safe.
Also, a couple of file types were missing compared to supported code: libxl supports 'vhd', and qemu supports 'fat' for directories passed through as a file system.
+++ b/src/util/storage_file.c
@@ -335,8 +351,12 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) - qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, offset); + if (isQCow2 && format) { + qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, + offset); + if (*format <= VIR_STORAGE_FILE_NONE) + return BACKING_STORE_INVALID; + }
This breaks the case where a qcow2 file has a backing file but did not specify a format, and libvirt is set to allow probing (for example, if you use 'qemu-img rebase -u -b blah file' without '-F format'). I'm working on a fix. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

We have historically allowed 'aio' as a synonym for 'raw' for back-compat to xen, but since a future patch will move to using an enum value, we have to pick one to be our preferred output name. This is a slight change in the XML, but the sexpr and xm outputs should still be identical. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Move aio back-compat... (virDomainDiskDefParseXML): ...to parse time. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenFormatSxprDisk): ...and to output time. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * tests/sexpr2xmldata/sexpr2xml-*.xml: Update tests. --- src/conf/domain_conf.c | 4 ++-- src/xenxs/xen_sxpr.c | 8 ++++++-- src/xenxs/xen_xm.c | 7 ++++++- tests/sexpr2xmldata/sexpr2xml-curmem.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml | 2 +- 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 460a57c..52e8f6c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3700,6 +3700,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "driver")) { driverName = virXMLPropString(cur, "name"); driverType = virXMLPropString(cur, "type"); + if (STREQ_NULLABLE(driverType, "aio")) + memcpy(driverType, "raw", strlen("raw")); cachetag = virXMLPropString(cur, "cache"); error_policy = virXMLPropString(cur, "error_policy"); rerror_policy = virXMLPropString(cur, "rerror_policy"); @@ -14645,8 +14647,6 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (disk->driverType) { const char *formatStr = disk->driverType; - if (STREQ(formatStr, "aio")) - formatStr = "raw"; /* Xen compat */ if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 6ac7dff..3d20350 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -443,6 +443,8 @@ xenParseSxprDisks(virDomainDefPtr def, src); goto error; } + if (STREQ(disk->driverType, "aio")) + memcpy(disk->driverType, "raw", strlen("raw")); src = offset + 1; /* Its possible to use blktap driver for block devs @@ -1831,9 +1833,11 @@ xenFormatSxprDisk(virDomainDiskDefPtr def, if (def->driverName) { if (STREQ(def->driverName, "tap") || STREQ(def->driverName, "tap2")) { + const char *type = def->driverType ? def->driverType : "aio"; + if (STREQ(type, "raw")) + type = "aio"; virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); - virBufferEscapeSexpr(buf, "%s:", - def->driverType ? def->driverType : "aio"); + virBufferEscapeSexpr(buf, "%s:", type); virBufferEscapeSexpr(buf, "%s')", def->src); } else { virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 40a99be..41e6744 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -566,6 +566,8 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, disk->src); goto cleanup; } + if (STREQ(disk->driverType, "aio")) + memcpy(disk->driverType, "raw", strlen("raw")); /* Strip the prefix we found off the source file name */ memmove(disk->src, disk->src+(tmp-disk->src)+1, @@ -1203,9 +1205,12 @@ static int xenFormatXMDisk(virConfValuePtr list, if(disk->src) { if (disk->driverName) { + const char *type = disk->driverType ? disk->driverType : "aio"; + if (STREQ(type, "raw")) + type = "aio"; virBufferAsprintf(&buf, "%s:", disk->driverName); if (STREQ(disk->driverName, "tap")) - virBufferAsprintf(&buf, "%s:", disk->driverType ? disk->driverType : "aio"); + virBufferAsprintf(&buf, "%s:", type); } else { switch (disk->type) { case VIR_DOMAIN_DISK_TYPE_FILE: diff --git a/tests/sexpr2xmldata/sexpr2xml-curmem.xml b/tests/sexpr2xmldata/sexpr2xml-curmem.xml index ac5ab51..1edc52b 100644 --- a/tests/sexpr2xmldata/sexpr2xml-curmem.xml +++ b/tests/sexpr2xmldata/sexpr2xml-curmem.xml @@ -17,7 +17,7 @@ <on_crash>restart</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/xen/rhel5.img'/> <target dev='xvda' bus='xen'/> </disk> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml index e4cd3a8..571b349 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml @@ -14,7 +14,7 @@ <on_crash>restart</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/var/lib/xen/images/rhel5pv.img'/> <target dev='xvda' bus='xen'/> <shareable/> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml index 98ed5c5..df8e7ec 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml @@ -16,7 +16,7 @@ <on_crash>destroy</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/root/some.img'/> <target dev='xvda' bus='xen'/> </disk> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml index b61182b..ea93195 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml @@ -16,7 +16,7 @@ <on_crash>destroy</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap2' type='aio'/> + <driver name='tap2' type='raw'/> <source file='/root/some.img'/> <target dev='xvda' bus='xen'/> </disk> -- 1.7.11.7

On Sat, Oct 13, 2012 at 4:59 PM, Eric Blake <eblake@redhat.com> wrote:
We have historically allowed 'aio' as a synonym for 'raw' for back-compat to xen, but since a future patch will move to using an enum value, we have to pick one to be our preferred output name. This is a slight change in the XML, but the sexpr and xm outputs should still be identical.
* src/conf/domain_conf.c (virDomainDiskDefForeachPath): Move aio back-compat... (virDomainDiskDefParseXML): ...to parse time. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenFormatSxprDisk): ...and to output time. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * tests/sexpr2xmldata/sexpr2xml-*.xml: Update tests. --- src/conf/domain_conf.c | 4 ++-- src/xenxs/xen_sxpr.c | 8 ++++++-- src/xenxs/xen_xm.c | 7 ++++++- tests/sexpr2xmldata/sexpr2xml-curmem.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml | 2 +- tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml | 2 +- 7 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 460a57c..52e8f6c 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3700,6 +3700,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "driver")) { driverName = virXMLPropString(cur, "name"); driverType = virXMLPropString(cur, "type"); + if (STREQ_NULLABLE(driverType, "aio")) + memcpy(driverType, "raw", strlen("raw"));
Two nits here that don't really have to be applied. First is that the string has to be specified twice so I'd almost prefer there to be a macro for this that's like: #define STRREPLACE(dest, src) memcpy((dest), (src), strlen((src)) The second would be to potentially use sizeof() on a buffer that's really a const ro data string. Just cause strlen() is a runtime check. Obviously this appears throughout this whole commit.
cachetag = virXMLPropString(cur, "cache"); error_policy = virXMLPropString(cur, "error_policy"); rerror_policy = virXMLPropString(cur, "rerror_policy"); @@ -14645,8 +14647,6 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
if (disk->driverType) { const char *formatStr = disk->driverType; - if (STREQ(formatStr, "aio")) - formatStr = "raw"; /* Xen compat */
if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 6ac7dff..3d20350 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -443,6 +443,8 @@ xenParseSxprDisks(virDomainDefPtr def, src); goto error; } + if (STREQ(disk->driverType, "aio")) + memcpy(disk->driverType, "raw", strlen("raw"));
src = offset + 1; /* Its possible to use blktap driver for block devs @@ -1831,9 +1833,11 @@ xenFormatSxprDisk(virDomainDiskDefPtr def, if (def->driverName) { if (STREQ(def->driverName, "tap") || STREQ(def->driverName, "tap2")) { + const char *type = def->driverType ? def->driverType : "aio"; + if (STREQ(type, "raw")) + type = "aio"; virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); - virBufferEscapeSexpr(buf, "%s:", - def->driverType ? def->driverType : "aio"); + virBufferEscapeSexpr(buf, "%s:", type); virBufferEscapeSexpr(buf, "%s')", def->src); } else { virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 40a99be..41e6744 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -566,6 +566,8 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, disk->src); goto cleanup; } + if (STREQ(disk->driverType, "aio")) + memcpy(disk->driverType, "raw", strlen("raw"));
/* Strip the prefix we found off the source file name */ memmove(disk->src, disk->src+(tmp-disk->src)+1, @@ -1203,9 +1205,12 @@ static int xenFormatXMDisk(virConfValuePtr list,
if(disk->src) { if (disk->driverName) { + const char *type = disk->driverType ? disk->driverType : "aio"; + if (STREQ(type, "raw")) + type = "aio"; virBufferAsprintf(&buf, "%s:", disk->driverName); if (STREQ(disk->driverName, "tap")) - virBufferAsprintf(&buf, "%s:", disk->driverType ? disk->driverType : "aio"); + virBufferAsprintf(&buf, "%s:", type); } else { switch (disk->type) { case VIR_DOMAIN_DISK_TYPE_FILE: diff --git a/tests/sexpr2xmldata/sexpr2xml-curmem.xml b/tests/sexpr2xmldata/sexpr2xml-curmem.xml index ac5ab51..1edc52b 100644 --- a/tests/sexpr2xmldata/sexpr2xml-curmem.xml +++ b/tests/sexpr2xmldata/sexpr2xml-curmem.xml @@ -17,7 +17,7 @@ <on_crash>restart</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/xen/rhel5.img'/> <target dev='xvda' bus='xen'/> </disk> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml index e4cd3a8..571b349 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-block-shareable.xml @@ -14,7 +14,7 @@ <on_crash>restart</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/var/lib/xen/images/rhel5pv.img'/> <target dev='xvda' bus='xen'/> <shareable/> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml index 98ed5c5..df8e7ec 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap-raw.xml @@ -16,7 +16,7 @@ <on_crash>destroy</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap' type='aio'/> + <driver name='tap' type='raw'/> <source file='/root/some.img'/> <target dev='xvda' bus='xen'/> </disk> diff --git a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml index b61182b..ea93195 100644 --- a/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml +++ b/tests/sexpr2xmldata/sexpr2xml-disk-drv-blktap2-raw.xml @@ -16,7 +16,7 @@ <on_crash>destroy</on_crash> <devices> <disk type='file' device='disk'> - <driver name='tap2' type='aio'/> + <driver name='tap2' type='raw'/> <source file='/root/some.img'/> <target dev='xvda' bus='xen'/> </disk> --
Past the nits above. Visual ACK as well. -- Doug Goldstein

On 10/14/2012 08:57 PM, Doug Goldstein wrote:
On Sat, Oct 13, 2012 at 4:59 PM, Eric Blake <eblake@redhat.com> wrote:
We have historically allowed 'aio' as a synonym for 'raw' for back-compat to xen, but since a future patch will move to using an enum value, we have to pick one to be our preferred output name. This is a slight change in the XML, but the sexpr and xm outputs should still be identical.
+ if (STREQ_NULLABLE(driverType, "aio")) + memcpy(driverType, "raw", strlen("raw"));
Two nits here that don't really have to be applied. First is that the string has to be specified twice so I'd almost prefer there to be a macro for this that's like:
#define STRREPLACE(dest, src) memcpy((dest), (src), strlen((src))
Maybe it would be better to just painfully spell out that we are replacing three bytes (a generic STRREPLACE would have to worry about lengths differing between original and new contents; I got lucky that 'aio' and 'raw' are the same size).
The second would be to potentially use sizeof() on a buffer that's really a const ro data string. Just cause strlen() is a runtime check. Obviously this appears throughout this whole commit.
strlen("constant") is optimized by gcc into a constant, with no call to strlen() in the source code. Furthermore, I seem to recall different semantics between C and C++ about sizeof("") on whether the trailing NUL byte is included in the result, but never remember which is which, so I prefer strlen() over sizeof() to avoid the ambiguity. But if I open code things, then it's no longer a concern :) Should I post a v2 that does: if (STREQ_NULLABLE(driverType, "aio")) { /* In-place conversion to "raw" */ driverType[0] = 'r'; driverType[1] = 'a'; driverType[2] = 'w'; } Or, since this area of code is changed again later in the series when I rename 'char *driverType' to 'int format', should I just leave this as-is since it is just temporary ugliness?
Past the nits above. Visual ACK as well.
We'll see if anyone has an opinion about the above in the next day or so. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/15/2012 12:31 PM, Eric Blake wrote:
Should I post a v2 that does:
if (STREQ_NULLABLE(driverType, "aio")) { /* In-place conversion to "raw" */ driverType[0] = 'r'; driverType[1] = 'a'; driverType[2] = 'w'; }
Or, since this area of code is changed again later in the series when I rename 'char *driverType' to 'int format', should I just leave this as-is since it is just temporary ugliness?
Past the nits above. Visual ACK as well.
We'll see if anyone has an opinion about the above in the next day or so.
Here's what I'm tested squashing in, if you like this alternative (note that two of the three uses are immediately undone later in patch 4/16): diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a2fb7dd..3679e1b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3724,8 +3724,12 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "driver")) { driverName = virXMLPropString(cur, "name"); driverType = virXMLPropString(cur, "type"); - if (STREQ_NULLABLE(driverType, "aio")) - memcpy(driverType, "raw", strlen("raw")); + if (STREQ_NULLABLE(driverType, "aio")) { + /* In-place conversion to "raw", for back-compat */ + driverType[0] = 'r'; + driverType[1] = 'a'; + driverType[2] = 'w'; + } cachetag = virXMLPropString(cur, "cache"); error_policy = virXMLPropString(cur, "error_policy"); rerror_policy = virXMLPropString(cur, "rerror_policy"); diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 3d20350..38a7e13 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -443,8 +443,12 @@ xenParseSxprDisks(virDomainDefPtr def, src); goto error; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw")); + if (STREQ(disk->driverType, "aio")) { + /* In-place conversion to "raw" */ + disk->driverType[0] = 'r'; + disk->driverType[1] = 'a'; + disk->driverType[2] = 'w'; + } src = offset + 1; /* Its possible to use blktap driver for block devs diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 41e6744..c564d35 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -566,8 +566,12 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, disk->src); goto cleanup; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw")); + if (STREQ(disk->driverType, "aio")) { + /* In-place conversion to "raw" */ + disk->driverType[0] = 'r'; + disk->driverType[1] = 'a'; + disk->driverType[2] = 'w'; + } /* Strip the prefix we found off the source file name */ memmove(disk->src, disk->src+(tmp-disk->src)+1, -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

At one point, the code passed through arbitrary strings for file formats, which supposedly lets qemu handle a new file type even before libvirt has been taught to handle it. However, to properly label files, libvirt has to learn the file type anyway, so we might as well make our life easier by only accepting file types that we are prepared to handle. This patch lets the RNG validation ensure that only known strings are let through. * docs/schemas/domaincommon.rng (driverFormat): Limit to list of supported strings. * docs/schemas/domainsnapshot.rng (driver): Likewise. --- docs/schemas/domaincommon.rng | 27 ++++++++++++++++++++++++--- docs/schemas/domainsnapshot.rng | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 70bc0e2..1bfebde 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1188,11 +1188,32 @@ <ref name="genericName"/> </attribute> <optional> - <attribute name="type"> - <ref name="genericName"/> + <attribute name='type'> + <choice> + <ref name='diskFormat'/> + <value>aio</value> <!-- back-compat for 'raw' --> + </choice> </attribute> </optional> </define> + <define name='diskFormat'> + <choice> + <value>raw</value> + <value>dir</value> + <value>bochs</value> + <value>cloop</value> + <value>cow</value> + <value>dmg</value> + <value>iso</value> + <value>qcow</value> + <value>qcow2</value> + <value>qed</value> + <value>vmdk</value> + <value>vpc</value> + <value>fat</value> + <value>vhd</value> + </choice> + </define> <define name="driverCache"> <attribute name="cache"> <choice> @@ -3352,7 +3373,7 @@ </attribute> <optional> <attribute name='format'> - <ref name="genericName"/> + <ref name='diskFormat'/> </attribute> </optional> <optional> diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng index 0ef0631..ecaafe9 100644 --- a/docs/schemas/domainsnapshot.rng +++ b/docs/schemas/domainsnapshot.rng @@ -105,7 +105,7 @@ <element name='driver'> <optional> <attribute name='type'> - <ref name='genericName'/> + <ref name='diskFormat'/> </attribute> </optional> <empty/> -- 1.7.11.7

On Sat, Oct 13, 2012 at 4:59 PM, Eric Blake <eblake@redhat.com> wrote:
At one point, the code passed through arbitrary strings for file formats, which supposedly lets qemu handle a new file type even before libvirt has been taught to handle it. However, to properly label files, libvirt has to learn the file type anyway, so we might as well make our life easier by only accepting file types that we are prepared to handle. This patch lets the RNG validation ensure that only known strings are let through.
* docs/schemas/domaincommon.rng (driverFormat): Limit to list of supported strings. * docs/schemas/domainsnapshot.rng (driver): Likewise. --- docs/schemas/domaincommon.rng | 27 ++++++++++++++++++++++++--- docs/schemas/domainsnapshot.rng | 2 +- 2 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index 70bc0e2..1bfebde 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -1188,11 +1188,32 @@ <ref name="genericName"/> </attribute> <optional> - <attribute name="type"> - <ref name="genericName"/> + <attribute name='type'> + <choice> + <ref name='diskFormat'/> + <value>aio</value> <!-- back-compat for 'raw' --> + </choice> </attribute> </optional> </define> + <define name='diskFormat'> + <choice> + <value>raw</value> + <value>dir</value> + <value>bochs</value> + <value>cloop</value> + <value>cow</value> + <value>dmg</value> + <value>iso</value> + <value>qcow</value> + <value>qcow2</value> + <value>qed</value> + <value>vmdk</value> + <value>vpc</value> + <value>fat</value> + <value>vhd</value> + </choice> + </define> <define name="driverCache"> <attribute name="cache"> <choice> @@ -3352,7 +3373,7 @@ </attribute> <optional> <attribute name='format'> - <ref name="genericName"/> + <ref name='diskFormat'/> </attribute> </optional> <optional> diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng index 0ef0631..ecaafe9 100644 --- a/docs/schemas/domainsnapshot.rng +++ b/docs/schemas/domainsnapshot.rng @@ -105,7 +105,7 @@ <element name='driver'> <optional> <attribute name='type'> - <ref name='genericName'/> + <ref name='diskFormat'/> </attribute> </optional> <empty/> --
Again visually ACK. -- Doug Goldstein

Express the default disk type as an enum, for easier handling. * src/conf/capabilities.h (_virCaps): Store enum rather than string for disk type. * src/conf/domain_conf.c (virDomainDiskDefParseXML): Adjust clients. * src/qemu/qemu_driver.c (qemuCreateCapabilities): Likewise. --- src/conf/capabilities.h | 4 ++-- src/conf/domain_conf.c | 6 ++++-- src/qemu/qemu_driver.c | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 0d56290..99056f8 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -1,7 +1,7 @@ /* * capabilities.h: hypervisor capabilities * - * Copyright (C) 2006-2008, 2010 Red Hat, Inc. + * Copyright (C) 2006-2008, 2010, 2012 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -149,7 +149,7 @@ struct _virCaps { unsigned char macPrefix[VIR_MAC_PREFIX_BUFLEN]; unsigned int emulatorRequired : 1; const char *defaultDiskDriverName; - const char *defaultDiskDriverType; + int defaultDiskDriverType; /* enum virStorageFileFormat */ int (*defaultConsoleTargetType)(const char *ostype); void *(*privateDataAllocFunc)(void); void (*privateDataFreeFunc)(void *); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 52e8f6c..70e3b53 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4160,7 +4160,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (!def->driverType && caps->defaultDiskDriverType && - !(def->driverType = strdup(caps->defaultDiskDriverType))) + !(def->driverType = strdup(virStorageFileFormatTypeToString( + caps->defaultDiskDriverType)))) goto no_memory; if (!def->driverName && @@ -4171,7 +4172,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, if (def->mirror && !def->mirrorFormat && caps->defaultDiskDriverType && - !(def->mirrorFormat = strdup(caps->defaultDiskDriverType))) + !(def->mirrorFormat = strdup(virStorageFileFormatTypeToString( + caps->defaultDiskDriverType)))) goto no_memory; if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0e4b2d0..f514199 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -394,10 +394,10 @@ qemuCreateCapabilities(struct qemud_driver *driver) if (driver->allowDiskFormatProbing) { caps->defaultDiskDriverName = NULL; - caps->defaultDiskDriverType = NULL; + caps->defaultDiskDriverType = VIR_STORAGE_FILE_AUTO; } else { caps->defaultDiskDriverName = "qemu"; - caps->defaultDiskDriverType = "raw"; + caps->defaultDiskDriverType = VIR_STORAGE_FILE_RAW; } qemuDomainSetPrivateDataHooks(caps); -- 1.7.11.7

On Sat, Oct 13, 2012 at 5:00 PM, Eric Blake <eblake@redhat.com> wrote:
Express the default disk type as an enum, for easier handling.
* src/conf/capabilities.h (_virCaps): Store enum rather than string for disk type. * src/conf/domain_conf.c (virDomainDiskDefParseXML): Adjust clients. * src/qemu/qemu_driver.c (qemuCreateCapabilities): Likewise. --- src/conf/capabilities.h | 4 ++-- src/conf/domain_conf.c | 6 ++++-- src/qemu/qemu_driver.c | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/conf/capabilities.h b/src/conf/capabilities.h index 0d56290..99056f8 100644 --- a/src/conf/capabilities.h +++ b/src/conf/capabilities.h @@ -1,7 +1,7 @@ /* * capabilities.h: hypervisor capabilities * - * Copyright (C) 2006-2008, 2010 Red Hat, Inc. + * Copyright (C) 2006-2008, 2010, 2012 Red Hat, Inc. * Copyright (C) 2006-2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -149,7 +149,7 @@ struct _virCaps { unsigned char macPrefix[VIR_MAC_PREFIX_BUFLEN]; unsigned int emulatorRequired : 1; const char *defaultDiskDriverName; - const char *defaultDiskDriverType; + int defaultDiskDriverType; /* enum virStorageFileFormat */ int (*defaultConsoleTargetType)(const char *ostype); void *(*privateDataAllocFunc)(void); void (*privateDataFreeFunc)(void *); diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 52e8f6c..70e3b53 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4160,7 +4160,8 @@ virDomainDiskDefParseXML(virCapsPtr caps,
if (!def->driverType && caps->defaultDiskDriverType && - !(def->driverType = strdup(caps->defaultDiskDriverType))) + !(def->driverType = strdup(virStorageFileFormatTypeToString( + caps->defaultDiskDriverType)))) goto no_memory;
if (!def->driverName && @@ -4171,7 +4172,8 @@ virDomainDiskDefParseXML(virCapsPtr caps,
if (def->mirror && !def->mirrorFormat && caps->defaultDiskDriverType && - !(def->mirrorFormat = strdup(caps->defaultDiskDriverType))) + !(def->mirrorFormat = strdup(virStorageFileFormatTypeToString( + caps->defaultDiskDriverType)))) goto no_memory;
if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0e4b2d0..f514199 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -394,10 +394,10 @@ qemuCreateCapabilities(struct qemud_driver *driver)
if (driver->allowDiskFormatProbing) { caps->defaultDiskDriverName = NULL; - caps->defaultDiskDriverType = NULL; + caps->defaultDiskDriverType = VIR_STORAGE_FILE_AUTO; } else { caps->defaultDiskDriverName = "qemu"; - caps->defaultDiskDriverType = "raw"; + caps->defaultDiskDriverType = VIR_STORAGE_FILE_RAW; }
qemuDomainSetPrivateDataHooks(caps); --
Looks good. ACK. -- Doug Goldstein

Actually use the enum in the domain conf structure. * src/conf/domain_conf.h (_virDomainDiskDef): Store enum rather than string for disk type. * src/conf/domain_conf.c (virDomainDiskDefFree) (virDomainDiskDefParseXML, virDomainDiskDefFormat) (virDomainDiskDefForeachPath): Adjust users. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenFormatSxprDisk): Likewise. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * src/vbox/vbox_tmpl.c (vboxAttachDrives): Likewise. * src/libxl/libxl_conf.c (libxlMakeDisk): Likewise. --- src/conf/domain_conf.c | 60 ++++++++++++++++---------------- src/conf/domain_conf.h | 4 +-- src/libxl/libxl_conf.c | 42 +++++++++++++---------- src/libxl/libxl_driver.c | 6 +--- src/qemu/qemu_command.c | 18 +++++----- src/qemu/qemu_domain.c | 7 ++-- src/qemu/qemu_driver.c | 89 ++++++++++++++++-------------------------------- src/qemu/qemu_hotplug.c | 9 ++--- src/vbox/vbox_tmpl.c | 6 ++-- src/xenxs/xen_sxpr.c | 26 +++++++++----- src/xenxs/xen_xm.c | 29 ++++++++++------ 11 files changed, 145 insertions(+), 151 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 70e3b53..acf1904 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,9 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); - VIR_FREE(def->driverType); VIR_FREE(def->mirror); - VIR_FREE(def->mirrorFormat); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); if (def->auth.secretType == VIR_DOMAIN_DISK_SECRET_TYPE_USAGE) @@ -4144,12 +4142,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, authUsername = NULL; def->driverName = driverName; driverName = NULL; - def->driverType = driverType; - driverType = NULL; def->mirror = mirror; mirror = NULL; - def->mirrorFormat = mirrorFormat; - mirrorFormat = NULL; def->mirroring = mirroring; def->encryption = encryption; encryption = NULL; @@ -4158,23 +4152,34 @@ virDomainDiskDefParseXML(virCapsPtr caps, def->wwn = wwn; wwn = NULL; - if (!def->driverType && - caps->defaultDiskDriverType && - !(def->driverType = strdup(virStorageFileFormatTypeToString( - caps->defaultDiskDriverType)))) - goto no_memory; + if (driverType) { + def->format = virStorageFileFormatTypeFromString(driverType); + if (def->format <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown driver format value '%s'"), + driverType); + goto error; + } + } else { + def->format = caps->defaultDiskDriverType; + } if (!def->driverName && caps->defaultDiskDriverName && !(def->driverName = strdup(caps->defaultDiskDriverName))) goto no_memory; - - if (def->mirror && !def->mirrorFormat && - caps->defaultDiskDriverType && - !(def->mirrorFormat = strdup(virStorageFileFormatTypeToString( - caps->defaultDiskDriverType)))) - goto no_memory; + if (mirrorFormat) { + def->mirrorFormat = virStorageFileFormatTypeFromString(mirrorFormat); + if (def->mirrorFormat <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown mirror format value '%s'"), + driverType); + goto error; + } + } else if (def->mirror) { + def->mirrorFormat = caps->defaultDiskDriverType; + } if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDiskDefAssignAddress(caps, def) < 0) @@ -11715,13 +11720,14 @@ virDomainDiskDefFormat(virBufferPtr buf, virDomainSnapshotLocationTypeToString(def->snapshot)); virBufferAddLit(buf, ">\n"); - if (def->driverName || def->driverType || def->cachemode || + if (def->driverName || def->format > 0 || def->cachemode || def->ioeventfd || def->event_idx || def->copy_on_read) { virBufferAddLit(buf, " <driver"); if (def->driverName) virBufferAsprintf(buf, " name='%s'", def->driverName); - if (def->driverType) - virBufferAsprintf(buf, " type='%s'", def->driverType); + if (def->format > 0) + virBufferAsprintf(buf, " type='%s'", + virStorageFileFormatTypeToString(def->format)); if (def->cachemode) virBufferAsprintf(buf, " cache='%s'", cachemode); if (def->error_policy) @@ -11833,7 +11839,8 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->mirror && !(flags & VIR_DOMAIN_XML_INACTIVE)) { virBufferEscapeString(buf, " <mirror file='%s'", def->mirror); if (def->mirrorFormat) - virBufferAsprintf(buf, " format='%s'", def->mirrorFormat); + virBufferAsprintf(buf, " format='%s'", + virStorageFileFormatTypeToString(def->mirrorFormat)); if (def->mirroring) virBufferAddLit(buf, " ready='yes'"); virBufferAddLit(buf, "/>\n"); @@ -14647,15 +14654,8 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, return ret; } - if (disk->driverType) { - const char *formatStr = disk->driverType; - - if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown disk format '%s' for %s"), - disk->driverType, disk->src); - goto cleanup; - } + if (disk->format > 0) { + format = disk->format; } else { if (allowProbing) { format = VIR_STORAGE_FILE_AUTO; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index cc63da1..1d20522 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -567,10 +567,10 @@ struct _virDomainDiskDef { } secret; } auth; char *driverName; - char *driverType; + int format; /* enum virStorageFileFormat */ char *mirror; - char *mirrorFormat; + int mirrorFormat; /* enum virStorageFileFormat */ bool mirroring; struct { diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index b988fa7..fa931c3 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -41,6 +41,7 @@ #include "capabilities.h" #include "libxl_driver.h" #include "libxl_conf.h" +#include "storage_file.h" #define VIR_FROM_THIS VIR_FROM_LIBXL @@ -505,25 +506,30 @@ libxlMakeDisk(virDomainDefPtr def, virDomainDiskDefPtr l_disk, if (l_disk->driverName) { if (STREQ(l_disk->driverName, "tap") || STREQ(l_disk->driverName, "tap2")) { - if (l_disk->driverType) { - if (STREQ(l_disk->driverType, "qcow")) { - x_disk->format = DISK_FORMAT_QCOW; - x_disk->backend = DISK_BACKEND_QDISK; - } else if (STREQ(l_disk->driverType, "qcow2")) { - x_disk->format = DISK_FORMAT_QCOW2; - x_disk->backend = DISK_BACKEND_QDISK; - } else if (STREQ(l_disk->driverType, "vhd")) { - x_disk->format = DISK_FORMAT_VHD; - x_disk->backend = DISK_BACKEND_TAP; - } else if (STREQ(l_disk->driverType, "aio") || - STREQ(l_disk->driverType, "raw")) { - x_disk->format = DISK_FORMAT_RAW; - x_disk->backend = DISK_BACKEND_TAP; - } - } else { + switch (l_disk->format) { + case VIR_STORAGE_FILE_QCOW: + x_disk->format = DISK_FORMAT_QCOW; + x_disk->backend = DISK_BACKEND_QDISK; + break; + case VIR_STORAGE_FILE_QCOW2: + x_disk->format = DISK_FORMAT_QCOW2; + x_disk->backend = DISK_BACKEND_QDISK; + break; + case VIR_STORAGE_FILE_VHD: + x_disk->format = DISK_FORMAT_VHD; + x_disk->backend = DISK_BACKEND_TAP; + break; + case VIR_STORAGE_FILE_NONE: /* No subtype specified, default to raw/tap */ - x_disk->format = DISK_FORMAT_RAW; - x_disk->backend = DISK_BACKEND_TAP; + case VIR_STORAGE_FILE_RAW: + x_disk->format = DISK_FORMAT_RAW; + x_disk->backend = DISK_BACKEND_TAP; + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight does not support disk driver %s"), + virStorageFileFormatTypeToString(l_disk->format)); + return -1; } } else if (STREQ(l_disk->driverName, "file")) { x_disk->format = DISK_FORMAT_RAW; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 6fe284f..f4e9aa6 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3202,11 +3202,7 @@ libxlDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) orig->driverName = disk->driverName; disk->driverName = NULL; } - if (disk->driverType) { - VIR_FREE(orig->driverType); - orig->driverType = disk->driverType; - disk->driverType = NULL; - } + orig->format = disk->format; disk->src = NULL; break; default: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d590df6..ab046eb 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -43,6 +43,7 @@ #include "virnetdevtap.h" #include "base64.h" #include "device_conf.h" +#include "storage_file.h" #include <sys/utsname.h> #include <sys/stat.h> @@ -2128,11 +2129,10 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { + if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), - disk->driverType); + virStorageFileFormatTypeToString(disk->format)); goto error; } if (!disk->readonly) { @@ -2229,10 +2229,11 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, _("transient disks not supported yet")); goto error; } - if (disk->driverType && *disk->driverType != '\0' && + if (disk->format > 0 && disk->type != VIR_DOMAIN_DISK_TYPE_DIR && qemuCapsGet(caps, QEMU_CAPS_DRIVE_FORMAT)) - virBufferAsprintf(&opt, ",format=%s", disk->driverType); + virBufferAsprintf(&opt, ",format=%s", + virStorageFileFormatTypeToString(disk->format)); /* generate geometry command string */ if (disk->geometry.cylinders > 0 && @@ -5209,11 +5210,10 @@ qemuBuildCommandLine(virConnectPtr conn, if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { + if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), - disk->driverType); + virStorageFileFormatTypeToString(disk->format)); goto error; } if (!disk->readonly) { @@ -7020,7 +7020,7 @@ qemuParseCommandLineDisk(virCapsPtr caps, virReportOOMError(); goto cleanup; } - def->driverType = values[i]; + def->format = virStorageFileFormatTypeFromString(values[i]); values[i] = NULL; } else if (STREQ(keywords[i], "cache")) { if (STREQ(values[i], "off") || diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 427258d..b51edc2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -36,6 +36,7 @@ #include "virfile.h" #include "domain_event.h" #include "virtime.h" +#include "storage_file.h" #include <sys/time.h> #include <fcntl.h> @@ -1408,7 +1409,7 @@ void qemuDomainObjCheckDiskTaint(struct qemud_driver *driver, virDomainDiskDefPtr disk, int logFD) { - if (!disk->driverType && + if ((!disk->format || disk->format == VIR_STORAGE_FILE_AUTO) && driver->allowDiskFormatProbing) qemuDomainObjTaint(driver, obj, VIR_DOMAIN_TAINT_DISK_PROBING, logFD); @@ -1660,8 +1661,8 @@ qemuDomainSnapshotForEachQcow2Raw(struct qemud_driver *driver, for (i = 0; i < ndisks; i++) { /* FIXME: we also need to handle LVM here */ if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - if (!def->disks[i]->driverType || - STRNEQ(def->disks[i]->driverType, "qcow2")) { + if (def->disks[i]->format > 0 && + def->disks[i]->format != VIR_STORAGE_FILE_QCOW2) { if (try_all) { /* Continue on even in the face of error, since other * disks in this VM may have the same snapshot name. diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f514199..35aab74 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6224,11 +6224,8 @@ qemuDomainUpdateDeviceConfig(qemuCapsPtr caps, orig->driverName = disk->driverName; disk->driverName = NULL; } - if (disk->driverType) { - VIR_FREE(orig->driverType); - orig->driverType = disk->driverType; - disk->driverType = NULL; - } + if (disk->format) + orig->format = disk->format; disk->src = NULL; break; @@ -9191,13 +9188,8 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, } /* Probe for magic formats */ - if (disk->driverType) { - if ((format = virStorageFileFormatTypeFromString(disk->driverType)) <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown disk format %s for %s"), - disk->driverType, disk->src); - goto cleanup; - } + if (disk->format) { + format = disk->format; } else { if (driver->allowDiskFormatProbing) { if ((format = virStorageFileProbeFormat(disk->src)) < 0) @@ -10369,7 +10361,7 @@ qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) if ((disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) || (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && - STRNEQ_NULLABLE(disk->driverType, "qcow2"))) { + disk->format > 0 && disk->format != VIR_STORAGE_FILE_QCOW2)) { virReportError(VIR_ERR_OPERATION_INVALID, _("Disk '%s' does not support snapshotting"), disk->src); @@ -10563,13 +10555,14 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, disk->name); goto cleanup; } - if (!vm->def->disks[i]->driverType || - STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { + if (vm->def->disks[i]->format > 0 && + vm->def->disks[i]->format != VIR_STORAGE_FILE_QCOW2) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("internal snapshot for disk %s unsupported " "for storage type %s"), disk->name, - NULLSTR(vm->def->disks[i]->driverType)); + virStorageFileFormatTypeToString( + vm->def->disks[i]->format)); goto cleanup; } found = true; @@ -10656,13 +10649,12 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *device = NULL; char *source = NULL; - char *driverType = NULL; + int format = VIR_STORAGE_FILE_NONE; char *persistSource = NULL; - char *persistDriverType = NULL; int ret = -1; int fd = -1; char *origsrc = NULL; - char *origdriver = NULL; + int origdriver; bool need_unlink = false; if (snap->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { @@ -10671,14 +10663,19 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, return -1; } + if (snap->driverType) { + format = virStorageFileFormatTypeFromString(snap->driverType); + if (format <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown driver type %s"), snap->driverType); + goto cleanup; + } + } + if (virAsprintf(&device, "drive-%s", disk->info.alias) < 0 || !(source = strdup(snap->file)) || - (STRNEQ_NULLABLE(disk->driverType, snap->driverType) && - !(driverType = strdup(snap->driverType))) || (persistDisk && - (!(persistSource = strdup(source)) || - (STRNEQ_NULLABLE(persistDisk->driverType, snap->driverType) && - !(persistDriverType = strdup(snap->driverType)))))) { + !(persistSource = strdup(source)))) { virReportOOMError(); goto cleanup; } @@ -10695,8 +10692,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, origsrc = disk->src; disk->src = source; - origdriver = disk->driverType; - disk->driverType = (char *) "raw"; /* Don't want to probe backing files */ + origdriver = disk->format; + disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) @@ -10717,8 +10714,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->src = origsrc; origsrc = NULL; - disk->driverType = origdriver; - origdriver = NULL; + disk->format = origdriver; /* create the actual snapshot */ ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source, @@ -10732,34 +10728,24 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FREE(disk->src); disk->src = source; source = NULL; - if (driverType) { - VIR_FREE(disk->driverType); - disk->driverType = driverType; - driverType = NULL; - } + disk->format = format; if (persistDisk) { VIR_FREE(persistDisk->src); persistDisk->src = persistSource; persistSource = NULL; - if (persistDriverType) { - VIR_FREE(persistDisk->driverType); - persistDisk->driverType = persistDriverType; - persistDriverType = NULL; - } + persistDisk->format = format; } cleanup: if (origsrc) { disk->src = origsrc; - disk->driverType = origdriver; + disk->format = origdriver; } if (need_unlink && unlink(source)) VIR_WARN("unable to unlink just-created %s", source); VIR_FREE(device); VIR_FREE(source); - VIR_FREE(driverType); VIR_FREE(persistSource); - VIR_FREE(persistDriverType); return ret; } @@ -10776,17 +10762,12 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, bool need_unlink) { char *source = NULL; - char *driverType = NULL; char *persistSource = NULL; - char *persistDriverType = NULL; struct stat st; if (!(source = strdup(origdisk->src)) || - (origdisk->driverType && - !(driverType = strdup(origdisk->driverType))) || (persistDisk && - (!(persistSource = strdup(source)) || - (driverType && !(persistDriverType = strdup(driverType)))))) { + !(persistSource = strdup(source)))) { virReportOOMError(); goto cleanup; } @@ -10806,27 +10787,17 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, VIR_FREE(disk->src); disk->src = source; source = NULL; - VIR_FREE(disk->driverType); - if (driverType) { - disk->driverType = driverType; - driverType = NULL; - } + disk->format = origdisk->format; if (persistDisk) { VIR_FREE(persistDisk->src); persistDisk->src = persistSource; persistSource = NULL; - VIR_FREE(persistDisk->driverType); - if (persistDriverType) { - persistDisk->driverType = persistDriverType; - persistDriverType = NULL; - } + persistDisk->format = origdisk->format; } cleanup: VIR_FREE(source); - VIR_FREE(driverType); VIR_FREE(persistSource); - VIR_FREE(persistDriverType); } /* The domain is expected to be locked and active. */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 78f1278..6bc5cfb 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -44,6 +44,7 @@ #include "virnetdevbridge.h" #include "virnetdevtap.h" #include "device_conf.h" +#include "storage_file.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -106,10 +107,10 @@ int qemuDomainChangeEjectableMedia(struct qemud_driver *driver, if (disk->src) { const char *format = NULL; if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) { - if (disk->driverType) - format = disk->driverType; - else if (origdisk->driverType) - format = origdisk->driverType; + if (disk->format > 0) + format = virStorageFileFormatTypeToString(disk->format); + else if (origdisk->format > 0) + format = virStorageFileFormatTypeToString(origdisk->format); } ret = qemuMonitorChangeMedia(priv->mon, driveAlias, diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 0eeac85..32a903e 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -3846,7 +3846,8 @@ vboxAttachDrives(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) VIR_DEBUG("disk(%d) src: %s", i, def->disks[i]->src); VIR_DEBUG("disk(%d) dst: %s", i, def->disks[i]->dst); VIR_DEBUG("disk(%d) driverName: %s", i, def->disks[i]->driverName); - VIR_DEBUG("disk(%d) driverType: %s", i, def->disks[i]->driverType); + VIR_DEBUG("disk(%d) driverType: %s", i, + virStorageFileFormatTypeToString(def->disks[i]->format)); VIR_DEBUG("disk(%d) cachemode: %d", i, def->disks[i]->cachemode); VIR_DEBUG("disk(%d) readonly: %s", i, (def->disks[i]->readonly ? "True" : "False")); @@ -4125,7 +4126,8 @@ vboxAttachDrives(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) VIR_DEBUG("disk(%d) src: %s", i, def->disks[i]->src); VIR_DEBUG("disk(%d) dst: %s", i, def->disks[i]->dst); VIR_DEBUG("disk(%d) driverName: %s", i, def->disks[i]->driverName); - VIR_DEBUG("disk(%d) driverType: %s", i, def->disks[i]->driverType); + VIR_DEBUG("disk(%d) driverType: %s", i, + virStorageFileFormatTypeToString(def->disks[i]->format)); VIR_DEBUG("disk(%d) cachemode: %d", i, def->disks[i]->cachemode); VIR_DEBUG("disk(%d) readonly: %s", i, (def->disks[i]->readonly ? "True" : "False")); diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 3d20350..35ad496 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -36,6 +36,7 @@ #include "count-one-bits.h" #include "xenxs_private.h" #include "xen_sxpr.h" +#include "storage_file.h" /* Get a domain id from a S-expression string */ int xenGetDomIdFromSxprString(const char *sexpr, int xendConfigVersion) @@ -427,6 +428,8 @@ xenParseSxprDisks(virDomainDefPtr def, if (STREQ (disk->driverName, "tap") || STREQ (disk->driverName, "tap2")) { + char *driverType = NULL; + offset = strchr(src, ':'); if (!offset) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -434,17 +437,19 @@ xenParseSxprDisks(virDomainDefPtr def, goto error; } - if (VIR_ALLOC_N(disk->driverType, (offset-src)+1)< 0) + if (!(driverType = strndup(src, offset - src))) goto no_memory; - if (virStrncpy(disk->driverType, src, offset-src, - (offset-src)+1) == NULL) { + if (STREQ(driverType, "aio")) + disk->format = VIR_STORAGE_FILE_RAW; + else + disk->format = + virStorageFileFormatTypeFromString(driverType); + VIR_FREE(driverType); + if (disk->format <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Driver type %s too big for destination"), - src); + _("Unknown driver type %s"), src); goto error; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw")); src = offset + 1; /* Its possible to use blktap driver for block devs @@ -1833,9 +1838,12 @@ xenFormatSxprDisk(virDomainDiskDefPtr def, if (def->driverName) { if (STREQ(def->driverName, "tap") || STREQ(def->driverName, "tap2")) { - const char *type = def->driverType ? def->driverType : "aio"; - if (STREQ(type, "raw")) + const char *type; + + if (!def->format || def->format == VIR_STORAGE_FILE_RAW) type = "aio"; + else + type = virStorageFileFormatTypeToString(def->format); virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); virBufferEscapeSexpr(buf, "%s:", type); virBufferEscapeSexpr(buf, "%s')", def->src); diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 41e6744..2e560a6 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -37,6 +37,7 @@ #include "xen_xm.h" #include "xen_sxpr.h" #include "domain_conf.h" +#include "storage_file.h" /* Convenience method to grab a long int from the config file object */ static int xenXMConfigGetBool(virConfPtr conf, @@ -554,20 +555,25 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, /* And the sub-type for tap:XXX: type */ if (disk->driverName && STREQ(disk->driverName, "tap")) { + char *driverType; + if (!(tmp = strchr(disk->src, ':'))) goto skipdisk; - if (VIR_ALLOC_N(disk->driverType, (tmp - disk->src) + 1) < 0) + + if (!(driverType = strndup(disk->src, tmp - disk->src))) goto no_memory; - if (virStrncpy(disk->driverType, disk->src, - (tmp - disk->src), - (tmp - disk->src) + 1) == NULL) { + if (STREQ(driverType, "aio")) + disk->format = VIR_STORAGE_FILE_RAW; + else + disk->format = + virStorageFileFormatTypeFromString(driverType); + VIR_FREE(driverType); + if (disk->format <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Driver type %s too big for destination"), + _("Unknown driver type %s"), disk->src); goto cleanup; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw")); /* Strip the prefix we found off the source file name */ memmove(disk->src, disk->src+(tmp-disk->src)+1, @@ -1204,10 +1210,13 @@ static int xenFormatXMDisk(virConfValuePtr list, virConfValuePtr val, tmp; if(disk->src) { - if (disk->driverName) { - const char *type = disk->driverType ? disk->driverType : "aio"; - if (STREQ(type, "raw")) + if (disk->format) { + const char *type; + + if (disk->format == VIR_STORAGE_FILE_RAW) type = "aio"; + else + type = virStorageFileFormatTypeToString(disk->format); virBufferAsprintf(&buf, "%s:", disk->driverName); if (STREQ(disk->driverName, "tap")) virBufferAsprintf(&buf, "%s:", type); -- 1.7.11.7

On Sat, Oct 13, 2012 at 5:00 PM, Eric Blake <eblake@redhat.com> wrote:
Actually use the enum in the domain conf structure.
* src/conf/domain_conf.h (_virDomainDiskDef): Store enum rather than string for disk type. * src/conf/domain_conf.c (virDomainDiskDefFree) (virDomainDiskDefParseXML, virDomainDiskDefFormat) (virDomainDiskDefForeachPath): Adjust users. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenFormatSxprDisk): Likewise. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * src/vbox/vbox_tmpl.c (vboxAttachDrives): Likewise. * src/libxl/libxl_conf.c (libxlMakeDisk): Likewise. --- src/conf/domain_conf.c | 60 ++++++++++++++++---------------- src/conf/domain_conf.h | 4 +-- src/libxl/libxl_conf.c | 42 +++++++++++++---------- src/libxl/libxl_driver.c | 6 +--- src/qemu/qemu_command.c | 18 +++++----- src/qemu/qemu_domain.c | 7 ++-- src/qemu/qemu_driver.c | 89 ++++++++++++++++-------------------------------- src/qemu/qemu_hotplug.c | 9 ++--- src/vbox/vbox_tmpl.c | 6 ++-- src/xenxs/xen_sxpr.c | 26 +++++++++----- src/xenxs/xen_xm.c | 29 ++++++++++------ 11 files changed, 145 insertions(+), 151 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 70e3b53..acf1904 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,9 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); - VIR_FREE(def->driverType); VIR_FREE(def->mirror); - VIR_FREE(def->mirrorFormat); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); if (def->auth.secretType == VIR_DOMAIN_DISK_SECRET_TYPE_USAGE) @@ -4144,12 +4142,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, authUsername = NULL; def->driverName = driverName; driverName = NULL; - def->driverType = driverType; - driverType = NULL; def->mirror = mirror; mirror = NULL; - def->mirrorFormat = mirrorFormat; - mirrorFormat = NULL; def->mirroring = mirroring; def->encryption = encryption; encryption = NULL; @@ -4158,23 +4152,34 @@ virDomainDiskDefParseXML(virCapsPtr caps, def->wwn = wwn; wwn = NULL;
- if (!def->driverType && - caps->defaultDiskDriverType && - !(def->driverType = strdup(virStorageFileFormatTypeToString( - caps->defaultDiskDriverType)))) - goto no_memory; + if (driverType) { + def->format = virStorageFileFormatTypeFromString(driverType); + if (def->format <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown driver format value '%s'"), + driverType); + goto error; + } + } else { + def->format = caps->defaultDiskDriverType; + }
if (!def->driverName && caps->defaultDiskDriverName && !(def->driverName = strdup(caps->defaultDiskDriverName))) goto no_memory;
- - if (def->mirror && !def->mirrorFormat && - caps->defaultDiskDriverType && - !(def->mirrorFormat = strdup(virStorageFileFormatTypeToString( - caps->defaultDiskDriverType)))) - goto no_memory; + if (mirrorFormat) { + def->mirrorFormat = virStorageFileFormatTypeFromString(mirrorFormat); + if (def->mirrorFormat <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown mirror format value '%s'"), + driverType); + goto error; + } + } else if (def->mirror) { + def->mirrorFormat = caps->defaultDiskDriverType; + }
if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDiskDefAssignAddress(caps, def) < 0) @@ -11715,13 +11720,14 @@ virDomainDiskDefFormat(virBufferPtr buf, virDomainSnapshotLocationTypeToString(def->snapshot)); virBufferAddLit(buf, ">\n");
- if (def->driverName || def->driverType || def->cachemode || + if (def->driverName || def->format > 0 || def->cachemode || def->ioeventfd || def->event_idx || def->copy_on_read) { virBufferAddLit(buf, " <driver"); if (def->driverName) virBufferAsprintf(buf, " name='%s'", def->driverName); - if (def->driverType) - virBufferAsprintf(buf, " type='%s'", def->driverType); + if (def->format > 0) + virBufferAsprintf(buf, " type='%s'", + virStorageFileFormatTypeToString(def->format)); if (def->cachemode) virBufferAsprintf(buf, " cache='%s'", cachemode); if (def->error_policy) @@ -11833,7 +11839,8 @@ virDomainDiskDefFormat(virBufferPtr buf, if (def->mirror && !(flags & VIR_DOMAIN_XML_INACTIVE)) { virBufferEscapeString(buf, " <mirror file='%s'", def->mirror); if (def->mirrorFormat) - virBufferAsprintf(buf, " format='%s'", def->mirrorFormat); + virBufferAsprintf(buf, " format='%s'", + virStorageFileFormatTypeToString(def->mirrorFormat)); if (def->mirroring) virBufferAddLit(buf, " ready='yes'"); virBufferAddLit(buf, "/>\n"); @@ -14647,15 +14654,8 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, return ret; }
- if (disk->driverType) { - const char *formatStr = disk->driverType; - - if ((format = virStorageFileFormatTypeFromString(formatStr)) <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown disk format '%s' for %s"), - disk->driverType, disk->src); - goto cleanup; - } + if (disk->format > 0) { + format = disk->format; } else { if (allowProbing) { format = VIR_STORAGE_FILE_AUTO; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index cc63da1..1d20522 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -567,10 +567,10 @@ struct _virDomainDiskDef { } secret; } auth; char *driverName; - char *driverType; + int format; /* enum virStorageFileFormat */
char *mirror; - char *mirrorFormat; + int mirrorFormat; /* enum virStorageFileFormat */ bool mirroring;
struct { diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c index b988fa7..fa931c3 100644 --- a/src/libxl/libxl_conf.c +++ b/src/libxl/libxl_conf.c @@ -41,6 +41,7 @@ #include "capabilities.h" #include "libxl_driver.h" #include "libxl_conf.h" +#include "storage_file.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL @@ -505,25 +506,30 @@ libxlMakeDisk(virDomainDefPtr def, virDomainDiskDefPtr l_disk, if (l_disk->driverName) { if (STREQ(l_disk->driverName, "tap") || STREQ(l_disk->driverName, "tap2")) { - if (l_disk->driverType) { - if (STREQ(l_disk->driverType, "qcow")) { - x_disk->format = DISK_FORMAT_QCOW; - x_disk->backend = DISK_BACKEND_QDISK; - } else if (STREQ(l_disk->driverType, "qcow2")) { - x_disk->format = DISK_FORMAT_QCOW2; - x_disk->backend = DISK_BACKEND_QDISK; - } else if (STREQ(l_disk->driverType, "vhd")) { - x_disk->format = DISK_FORMAT_VHD; - x_disk->backend = DISK_BACKEND_TAP; - } else if (STREQ(l_disk->driverType, "aio") || - STREQ(l_disk->driverType, "raw")) { - x_disk->format = DISK_FORMAT_RAW; - x_disk->backend = DISK_BACKEND_TAP; - } - } else { + switch (l_disk->format) { + case VIR_STORAGE_FILE_QCOW: + x_disk->format = DISK_FORMAT_QCOW; + x_disk->backend = DISK_BACKEND_QDISK; + break; + case VIR_STORAGE_FILE_QCOW2: + x_disk->format = DISK_FORMAT_QCOW2; + x_disk->backend = DISK_BACKEND_QDISK; + break; + case VIR_STORAGE_FILE_VHD: + x_disk->format = DISK_FORMAT_VHD; + x_disk->backend = DISK_BACKEND_TAP; + break; + case VIR_STORAGE_FILE_NONE: /* No subtype specified, default to raw/tap */ - x_disk->format = DISK_FORMAT_RAW; - x_disk->backend = DISK_BACKEND_TAP; + case VIR_STORAGE_FILE_RAW: + x_disk->format = DISK_FORMAT_RAW; + x_disk->backend = DISK_BACKEND_TAP; + break; + default: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("libxenlight does not support disk driver %s"), + virStorageFileFormatTypeToString(l_disk->format)); + return -1; } } else if (STREQ(l_disk->driverName, "file")) { x_disk->format = DISK_FORMAT_RAW; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 6fe284f..f4e9aa6 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -3202,11 +3202,7 @@ libxlDomainUpdateDeviceConfig(virDomainDefPtr vmdef, virDomainDeviceDefPtr dev) orig->driverName = disk->driverName; disk->driverName = NULL; } - if (disk->driverType) { - VIR_FREE(orig->driverType); - orig->driverType = disk->driverType; - disk->driverType = NULL; - } + orig->format = disk->format; disk->src = NULL; break; default: diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index d590df6..ab046eb 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -43,6 +43,7 @@ #include "virnetdevtap.h" #include "base64.h" #include "device_conf.h" +#include "storage_file.h"
#include <sys/utsname.h> #include <sys/stat.h> @@ -2128,11 +2129,10 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) { if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { + if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), - disk->driverType); + virStorageFileFormatTypeToString(disk->format)); goto error; } if (!disk->readonly) { @@ -2229,10 +2229,11 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED, _("transient disks not supported yet")); goto error; } - if (disk->driverType && *disk->driverType != '\0' && + if (disk->format > 0 && disk->type != VIR_DOMAIN_DISK_TYPE_DIR && qemuCapsGet(caps, QEMU_CAPS_DRIVE_FORMAT)) - virBufferAsprintf(&opt, ",format=%s", disk->driverType); + virBufferAsprintf(&opt, ",format=%s", + virStorageFileFormatTypeToString(disk->format));
/* generate geometry command string */ if (disk->geometry.cylinders > 0 && @@ -5209,11 +5210,10 @@ qemuBuildCommandLine(virConnectPtr conn,
if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { + if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), - disk->driverType); + virStorageFileFormatTypeToString(disk->format)); goto error; } if (!disk->readonly) { @@ -7020,7 +7020,7 @@ qemuParseCommandLineDisk(virCapsPtr caps, virReportOOMError(); goto cleanup; } - def->driverType = values[i]; + def->format = virStorageFileFormatTypeFromString(values[i]); values[i] = NULL; } else if (STREQ(keywords[i], "cache")) { if (STREQ(values[i], "off") || diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 427258d..b51edc2 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -36,6 +36,7 @@ #include "virfile.h" #include "domain_event.h" #include "virtime.h" +#include "storage_file.h"
#include <sys/time.h> #include <fcntl.h> @@ -1408,7 +1409,7 @@ void qemuDomainObjCheckDiskTaint(struct qemud_driver *driver, virDomainDiskDefPtr disk, int logFD) { - if (!disk->driverType && + if ((!disk->format || disk->format == VIR_STORAGE_FILE_AUTO) && driver->allowDiskFormatProbing) qemuDomainObjTaint(driver, obj, VIR_DOMAIN_TAINT_DISK_PROBING, logFD);
@@ -1660,8 +1661,8 @@ qemuDomainSnapshotForEachQcow2Raw(struct qemud_driver *driver, for (i = 0; i < ndisks; i++) { /* FIXME: we also need to handle LVM here */ if (def->disks[i]->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - if (!def->disks[i]->driverType || - STRNEQ(def->disks[i]->driverType, "qcow2")) { + if (def->disks[i]->format > 0 && + def->disks[i]->format != VIR_STORAGE_FILE_QCOW2) { if (try_all) { /* Continue on even in the face of error, since other * disks in this VM may have the same snapshot name. diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f514199..35aab74 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6224,11 +6224,8 @@ qemuDomainUpdateDeviceConfig(qemuCapsPtr caps, orig->driverName = disk->driverName; disk->driverName = NULL; } - if (disk->driverType) { - VIR_FREE(orig->driverType); - orig->driverType = disk->driverType; - disk->driverType = NULL; - } + if (disk->format) + orig->format = disk->format; disk->src = NULL; break;
@@ -9191,13 +9188,8 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, }
/* Probe for magic formats */ - if (disk->driverType) { - if ((format = virStorageFileFormatTypeFromString(disk->driverType)) <= 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown disk format %s for %s"), - disk->driverType, disk->src); - goto cleanup; - } + if (disk->format) { + format = disk->format; } else { if (driver->allowDiskFormatProbing) { if ((format = virStorageFileProbeFormat(disk->src)) < 0) @@ -10369,7 +10361,7 @@ qemuDomainSnapshotIsAllowed(virDomainObjPtr vm)
if ((disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) || (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && - STRNEQ_NULLABLE(disk->driverType, "qcow2"))) { + disk->format > 0 && disk->format != VIR_STORAGE_FILE_QCOW2)) { virReportError(VIR_ERR_OPERATION_INVALID, _("Disk '%s' does not support snapshotting"), disk->src); @@ -10563,13 +10555,14 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, disk->name); goto cleanup; } - if (!vm->def->disks[i]->driverType || - STRNEQ(vm->def->disks[i]->driverType, "qcow2")) { + if (vm->def->disks[i]->format > 0 && + vm->def->disks[i]->format != VIR_STORAGE_FILE_QCOW2) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("internal snapshot for disk %s unsupported " "for storage type %s"), disk->name, - NULLSTR(vm->def->disks[i]->driverType)); + virStorageFileFormatTypeToString( + vm->def->disks[i]->format)); goto cleanup; } found = true; @@ -10656,13 +10649,12 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *device = NULL; char *source = NULL; - char *driverType = NULL; + int format = VIR_STORAGE_FILE_NONE; char *persistSource = NULL; - char *persistDriverType = NULL; int ret = -1; int fd = -1; char *origsrc = NULL; - char *origdriver = NULL; + int origdriver; bool need_unlink = false;
if (snap->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { @@ -10671,14 +10663,19 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, return -1; }
+ if (snap->driverType) { + format = virStorageFileFormatTypeFromString(snap->driverType); + if (format <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown driver type %s"), snap->driverType); + goto cleanup; + } + } + if (virAsprintf(&device, "drive-%s", disk->info.alias) < 0 || !(source = strdup(snap->file)) || - (STRNEQ_NULLABLE(disk->driverType, snap->driverType) && - !(driverType = strdup(snap->driverType))) || (persistDisk && - (!(persistSource = strdup(source)) || - (STRNEQ_NULLABLE(persistDisk->driverType, snap->driverType) && - !(persistDriverType = strdup(snap->driverType)))))) { + !(persistSource = strdup(source)))) { virReportOOMError(); goto cleanup; } @@ -10695,8 +10692,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
origsrc = disk->src; disk->src = source; - origdriver = disk->driverType; - disk->driverType = (char *) "raw"; /* Don't want to probe backing files */ + origdriver = disk->format; + disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */
if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) @@ -10717,8 +10714,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
disk->src = origsrc; origsrc = NULL; - disk->driverType = origdriver; - origdriver = NULL; + disk->format = origdriver;
/* create the actual snapshot */ ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source, @@ -10732,34 +10728,24 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FREE(disk->src); disk->src = source; source = NULL; - if (driverType) { - VIR_FREE(disk->driverType); - disk->driverType = driverType; - driverType = NULL; - } + disk->format = format; if (persistDisk) { VIR_FREE(persistDisk->src); persistDisk->src = persistSource; persistSource = NULL; - if (persistDriverType) { - VIR_FREE(persistDisk->driverType); - persistDisk->driverType = persistDriverType; - persistDriverType = NULL; - } + persistDisk->format = format; }
cleanup: if (origsrc) { disk->src = origsrc; - disk->driverType = origdriver; + disk->format = origdriver; } if (need_unlink && unlink(source)) VIR_WARN("unable to unlink just-created %s", source); VIR_FREE(device); VIR_FREE(source); - VIR_FREE(driverType); VIR_FREE(persistSource); - VIR_FREE(persistDriverType); return ret; }
@@ -10776,17 +10762,12 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, bool need_unlink) { char *source = NULL; - char *driverType = NULL; char *persistSource = NULL; - char *persistDriverType = NULL; struct stat st;
if (!(source = strdup(origdisk->src)) || - (origdisk->driverType && - !(driverType = strdup(origdisk->driverType))) || (persistDisk && - (!(persistSource = strdup(source)) || - (driverType && !(persistDriverType = strdup(driverType)))))) { + !(persistSource = strdup(source)))) { virReportOOMError(); goto cleanup; } @@ -10806,27 +10787,17 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, VIR_FREE(disk->src); disk->src = source; source = NULL; - VIR_FREE(disk->driverType); - if (driverType) { - disk->driverType = driverType; - driverType = NULL; - } + disk->format = origdisk->format; if (persistDisk) { VIR_FREE(persistDisk->src); persistDisk->src = persistSource; persistSource = NULL; - VIR_FREE(persistDisk->driverType); - if (persistDriverType) { - persistDisk->driverType = persistDriverType; - persistDriverType = NULL; - } + persistDisk->format = origdisk->format; }
cleanup: VIR_FREE(source); - VIR_FREE(driverType); VIR_FREE(persistSource); - VIR_FREE(persistDriverType); }
/* The domain is expected to be locked and active. */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 78f1278..6bc5cfb 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -44,6 +44,7 @@ #include "virnetdevbridge.h" #include "virnetdevtap.h" #include "device_conf.h" +#include "storage_file.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -106,10 +107,10 @@ int qemuDomainChangeEjectableMedia(struct qemud_driver *driver, if (disk->src) { const char *format = NULL; if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) { - if (disk->driverType) - format = disk->driverType; - else if (origdisk->driverType) - format = origdisk->driverType; + if (disk->format > 0) + format = virStorageFileFormatTypeToString(disk->format); + else if (origdisk->format > 0) + format = virStorageFileFormatTypeToString(origdisk->format); } ret = qemuMonitorChangeMedia(priv->mon, driveAlias, diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index 0eeac85..32a903e 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -3846,7 +3846,8 @@ vboxAttachDrives(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) VIR_DEBUG("disk(%d) src: %s", i, def->disks[i]->src); VIR_DEBUG("disk(%d) dst: %s", i, def->disks[i]->dst); VIR_DEBUG("disk(%d) driverName: %s", i, def->disks[i]->driverName); - VIR_DEBUG("disk(%d) driverType: %s", i, def->disks[i]->driverType); + VIR_DEBUG("disk(%d) driverType: %s", i, + virStorageFileFormatTypeToString(def->disks[i]->format)); VIR_DEBUG("disk(%d) cachemode: %d", i, def->disks[i]->cachemode); VIR_DEBUG("disk(%d) readonly: %s", i, (def->disks[i]->readonly ? "True" : "False")); @@ -4125,7 +4126,8 @@ vboxAttachDrives(virDomainDefPtr def, vboxGlobalData *data, IMachine *machine) VIR_DEBUG("disk(%d) src: %s", i, def->disks[i]->src); VIR_DEBUG("disk(%d) dst: %s", i, def->disks[i]->dst); VIR_DEBUG("disk(%d) driverName: %s", i, def->disks[i]->driverName); - VIR_DEBUG("disk(%d) driverType: %s", i, def->disks[i]->driverType); + VIR_DEBUG("disk(%d) driverType: %s", i, + virStorageFileFormatTypeToString(def->disks[i]->format)); VIR_DEBUG("disk(%d) cachemode: %d", i, def->disks[i]->cachemode); VIR_DEBUG("disk(%d) readonly: %s", i, (def->disks[i]->readonly ? "True" : "False")); diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c index 3d20350..35ad496 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -36,6 +36,7 @@ #include "count-one-bits.h" #include "xenxs_private.h" #include "xen_sxpr.h" +#include "storage_file.h"
/* Get a domain id from a S-expression string */ int xenGetDomIdFromSxprString(const char *sexpr, int xendConfigVersion) @@ -427,6 +428,8 @@ xenParseSxprDisks(virDomainDefPtr def,
if (STREQ (disk->driverName, "tap") || STREQ (disk->driverName, "tap2")) { + char *driverType = NULL; + offset = strchr(src, ':'); if (!offset) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -434,17 +437,19 @@ xenParseSxprDisks(virDomainDefPtr def, goto error; }
- if (VIR_ALLOC_N(disk->driverType, (offset-src)+1)< 0) + if (!(driverType = strndup(src, offset - src))) goto no_memory; - if (virStrncpy(disk->driverType, src, offset-src, - (offset-src)+1) == NULL) { + if (STREQ(driverType, "aio")) + disk->format = VIR_STORAGE_FILE_RAW; + else + disk->format = + virStorageFileFormatTypeFromString(driverType); + VIR_FREE(driverType); + if (disk->format <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Driver type %s too big for destination"), - src); + _("Unknown driver type %s"), src); goto error; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw"));
src = offset + 1; /* Its possible to use blktap driver for block devs @@ -1833,9 +1838,12 @@ xenFormatSxprDisk(virDomainDiskDefPtr def, if (def->driverName) { if (STREQ(def->driverName, "tap") || STREQ(def->driverName, "tap2")) { - const char *type = def->driverType ? def->driverType : "aio"; - if (STREQ(type, "raw")) + const char *type; + + if (!def->format || def->format == VIR_STORAGE_FILE_RAW) type = "aio"; + else + type = virStorageFileFormatTypeToString(def->format); virBufferEscapeSexpr(buf, "(uname '%s:", def->driverName); virBufferEscapeSexpr(buf, "%s:", type); virBufferEscapeSexpr(buf, "%s')", def->src); diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c index 41e6744..2e560a6 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -37,6 +37,7 @@ #include "xen_xm.h" #include "xen_sxpr.h" #include "domain_conf.h" +#include "storage_file.h"
/* Convenience method to grab a long int from the config file object */ static int xenXMConfigGetBool(virConfPtr conf, @@ -554,20 +555,25 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, /* And the sub-type for tap:XXX: type */ if (disk->driverName && STREQ(disk->driverName, "tap")) { + char *driverType; + if (!(tmp = strchr(disk->src, ':'))) goto skipdisk; - if (VIR_ALLOC_N(disk->driverType, (tmp - disk->src) + 1) < 0) + + if (!(driverType = strndup(disk->src, tmp - disk->src))) goto no_memory; - if (virStrncpy(disk->driverType, disk->src, - (tmp - disk->src), - (tmp - disk->src) + 1) == NULL) { + if (STREQ(driverType, "aio")) + disk->format = VIR_STORAGE_FILE_RAW; + else + disk->format = + virStorageFileFormatTypeFromString(driverType); + VIR_FREE(driverType); + if (disk->format <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("Driver type %s too big for destination"), + _("Unknown driver type %s"), disk->src); goto cleanup; } - if (STREQ(disk->driverType, "aio")) - memcpy(disk->driverType, "raw", strlen("raw"));
/* Strip the prefix we found off the source file name */ memmove(disk->src, disk->src+(tmp-disk->src)+1, @@ -1204,10 +1210,13 @@ static int xenFormatXMDisk(virConfValuePtr list, virConfValuePtr val, tmp;
if(disk->src) { - if (disk->driverName) { - const char *type = disk->driverType ? disk->driverType : "aio"; - if (STREQ(type, "raw")) + if (disk->format) { + const char *type; + + if (disk->format == VIR_STORAGE_FILE_RAW) type = "aio"; + else + type = virStorageFileFormatTypeToString(disk->format); virBufferAsprintf(&buf, "%s:", disk->driverName); if (STREQ(disk->driverName, "tap")) virBufferAsprintf(&buf, "%s:", type); --
Looks good. Tested this (and the preceding 4 patches) with vbox briefly. -- Doug Goldstein

On 10/13/2012 04:00 PM, Eric Blake wrote:
Actually use the enum in the domain conf structure.
* src/conf/domain_conf.h (_virDomainDiskDef): Store enum rather than string for disk type. * src/conf/domain_conf.c (virDomainDiskDefFree) (virDomainDiskDefParseXML, virDomainDiskDefFormat) (virDomainDiskDefForeachPath): Adjust users. * src/xenxs/xen_sxpr.c (xenParseSxprDisks, xenFormatSxprDisk): Likewise. * src/xenxs/xen_xm.c (xenParseXM, xenFormatXMDisk): Likewise. * src/vbox/vbox_tmpl.c (vboxAttachDrives): Likewise. * src/libxl/libxl_conf.c (libxlMakeDisk): Likewise. ---
@@ -4158,23 +4152,34 @@ virDomainDiskDefParseXML(virCapsPtr caps, def->wwn = wwn; wwn = NULL;
- if (!def->driverType && - caps->defaultDiskDriverType && - !(def->driverType = strdup(virStorageFileFormatTypeToString( - caps->defaultDiskDriverType)))) - goto no_memory; + if (driverType) { + def->format = virStorageFileFormatTypeFromString(driverType); + if (def->format <= 0) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unknown driver format value '%s'"), + driverType); + goto error; + } + } else { + def->format = caps->defaultDiskDriverType; + }
@@ -5209,11 +5210,10 @@ qemuBuildCommandLine(virConnectPtr conn,
if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { + if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) {
Going by inspection, I need a tweak here - in the old code, if you had a <disk type='dir'> but no <driver type='fat'/>, then disk->driverType was NULL, which we treated as a synonym for auto. But in the new code, disk->format defaults to whatever the driver capabilities state, which in patch 4/16 is either AUTO (-1) or RAW (1), so for the same behavior, this needs to check 'disk->format > 0' to work correctly for AUTO, as well as tweak domain_conf to not apply driver capability defaults to dir or network protocols (formats only make sense on files and block devices). Most of the rest of this patch did it correctly. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

This is the last use of raw strings for disk formats throughout the src/conf directory. * src/conf/snapshot_conf.h (_virDomainSnapshotDiskDef): Store enum rather than string for disk type. * src/conf/snapshot_conf.c (virDomainSnapshotDiskDefClear) (virDomainSnapshotDiskDefParseXML, virDomainSnapshotDefFormat): Adjust users. * src/qemu/qemu_driver.c (qemuDomainSnapshotDiskPrepare) (qemuDomainSnapshotCreateSingleDiskActive): Likewise. --- src/conf/snapshot_conf.c | 23 ++++++++++++++++------- src/conf/snapshot_conf.h | 2 +- src/qemu/qemu_driver.c | 30 +++++++++++------------------- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 0085352..16c844d 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -82,7 +82,6 @@ virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk) { VIR_FREE(disk->name); VIR_FREE(disk->file); - VIR_FREE(disk->driverType); } void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) @@ -134,15 +133,24 @@ virDomainSnapshotDiskDefParseXML(xmlNodePtr node, if (!def->file && xmlStrEqual(cur->name, BAD_CAST "source")) { def->file = virXMLPropString(cur, "file"); - } else if (!def->driverType && + } else if (!def->format && xmlStrEqual(cur->name, BAD_CAST "driver")) { - def->driverType = virXMLPropString(cur, "type"); + char *driver = virXMLPropString(cur, "type"); + def->format = virStorageFileFormatTypeFromString(driver); + if (def->format <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown disk snapshot driver '%s'"), + driver); + VIR_FREE(driver); + goto cleanup; + } + VIR_FREE(driver); } } cur = cur->next; } - if (!def->snapshot && (def->file || def->driverType)) + if (!def->snapshot && (def->file || def->format)) def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; ret = 0; @@ -536,11 +544,12 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, if (disk->snapshot) virBufferAsprintf(&buf, " snapshot='%s'", virDomainSnapshotLocationTypeToString(disk->snapshot)); - if (disk->file || disk->driverType) { + if (disk->file || disk->format > 0) { virBufferAddLit(&buf, ">\n"); - if (disk->driverType) + if (disk->format > 0) virBufferEscapeString(&buf, " <driver type='%s'/>\n", - disk->driverType); + virStorageFileFormatTypeToString( + disk->format)); if (disk->file) virBufferEscapeString(&buf, " <source file='%s'/>\n", disk->file); diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index efb0bf7..c00347f 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -52,7 +52,7 @@ struct _virDomainSnapshotDiskDef { int index; /* index within snapshot->dom->disks that matches name */ int snapshot; /* enum virDomainSnapshotLocation */ char *file; /* new source file when snapshot is external */ - char *driverType; /* file format type of new file */ + int format; /* enum virStorageFileFormat */ }; /* Stores the complete snapshot metadata */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 35aab74..4d1c63c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10569,17 +10569,15 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, break; case VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL: - if (!disk->driverType) { - if (!(disk->driverType = strdup("qcow2"))) { - virReportOOMError(); - goto cleanup; - } - } else if (STRNEQ(disk->driverType, "qcow2") && - STRNEQ(disk->driverType, "qed")) { + if (!disk->format) { + disk->format = VIR_STORAGE_FILE_QCOW2; + } else if (disk->format != VIR_STORAGE_FILE_QCOW2 && + disk->format != VIR_STORAGE_FILE_QED) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("external snapshot format for disk %s " "is unsupported: %s"), - disk->name, disk->driverType); + disk->name, + virStorageFileFormatTypeToString(disk->format)); goto cleanup; } if (stat(disk->file, &st) < 0) { @@ -10649,7 +10647,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *device = NULL; char *source = NULL; - int format = VIR_STORAGE_FILE_NONE; + int format = snap->format; + const char *formatStr = NULL; char *persistSource = NULL; int ret = -1; int fd = -1; @@ -10663,15 +10662,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, return -1; } - if (snap->driverType) { - format = virStorageFileFormatTypeFromString(snap->driverType); - if (format <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown driver type %s"), snap->driverType); - goto cleanup; - } - } - if (virAsprintf(&device, "drive-%s", disk->info.alias) < 0 || !(source = strdup(snap->file)) || (persistDisk && @@ -10717,8 +10707,10 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->format = origdriver; /* create the actual snapshot */ + if (snap->format) + formatStr = virStorageFileFormatTypeToString(snap->format); ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source, - snap->driverType, reuse); + formatStr, reuse); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0); if (ret < 0) goto cleanup; -- 1.7.11.7

On Sat, Oct 13, 2012 at 5:00 PM, Eric Blake <eblake@redhat.com> wrote:
This is the last use of raw strings for disk formats throughout the src/conf directory.
* src/conf/snapshot_conf.h (_virDomainSnapshotDiskDef): Store enum rather than string for disk type. * src/conf/snapshot_conf.c (virDomainSnapshotDiskDefClear) (virDomainSnapshotDiskDefParseXML, virDomainSnapshotDefFormat): Adjust users. * src/qemu/qemu_driver.c (qemuDomainSnapshotDiskPrepare) (qemuDomainSnapshotCreateSingleDiskActive): Likewise. --- src/conf/snapshot_conf.c | 23 ++++++++++++++++------- src/conf/snapshot_conf.h | 2 +- src/qemu/qemu_driver.c | 30 +++++++++++------------------- 3 files changed, 28 insertions(+), 27 deletions(-)
diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 0085352..16c844d 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -82,7 +82,6 @@ virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk) { VIR_FREE(disk->name); VIR_FREE(disk->file); - VIR_FREE(disk->driverType); }
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def) @@ -134,15 +133,24 @@ virDomainSnapshotDiskDefParseXML(xmlNodePtr node, if (!def->file && xmlStrEqual(cur->name, BAD_CAST "source")) { def->file = virXMLPropString(cur, "file"); - } else if (!def->driverType && + } else if (!def->format && xmlStrEqual(cur->name, BAD_CAST "driver")) { - def->driverType = virXMLPropString(cur, "type"); + char *driver = virXMLPropString(cur, "type"); + def->format = virStorageFileFormatTypeFromString(driver); + if (def->format <= 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown disk snapshot driver '%s'"), + driver); + VIR_FREE(driver); + goto cleanup; + } + VIR_FREE(driver); } } cur = cur->next; }
- if (!def->snapshot && (def->file || def->driverType)) + if (!def->snapshot && (def->file || def->format)) def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL;
ret = 0; @@ -536,11 +544,12 @@ char *virDomainSnapshotDefFormat(const char *domain_uuid, if (disk->snapshot) virBufferAsprintf(&buf, " snapshot='%s'", virDomainSnapshotLocationTypeToString(disk->snapshot)); - if (disk->file || disk->driverType) { + if (disk->file || disk->format > 0) { virBufferAddLit(&buf, ">\n"); - if (disk->driverType) + if (disk->format > 0) virBufferEscapeString(&buf, " <driver type='%s'/>\n", - disk->driverType); + virStorageFileFormatTypeToString( + disk->format)); if (disk->file) virBufferEscapeString(&buf, " <source file='%s'/>\n", disk->file); diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index efb0bf7..c00347f 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -52,7 +52,7 @@ struct _virDomainSnapshotDiskDef { int index; /* index within snapshot->dom->disks that matches name */ int snapshot; /* enum virDomainSnapshotLocation */ char *file; /* new source file when snapshot is external */ - char *driverType; /* file format type of new file */ + int format; /* enum virStorageFileFormat */ };
/* Stores the complete snapshot metadata */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 35aab74..4d1c63c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10569,17 +10569,15 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, break;
case VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL: - if (!disk->driverType) { - if (!(disk->driverType = strdup("qcow2"))) { - virReportOOMError(); - goto cleanup; - } - } else if (STRNEQ(disk->driverType, "qcow2") && - STRNEQ(disk->driverType, "qed")) { + if (!disk->format) { + disk->format = VIR_STORAGE_FILE_QCOW2; + } else if (disk->format != VIR_STORAGE_FILE_QCOW2 && + disk->format != VIR_STORAGE_FILE_QED) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("external snapshot format for disk %s " "is unsupported: %s"), - disk->name, disk->driverType); + disk->name, + virStorageFileFormatTypeToString(disk->format)); goto cleanup; } if (stat(disk->file, &st) < 0) { @@ -10649,7 +10647,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *device = NULL; char *source = NULL; - int format = VIR_STORAGE_FILE_NONE; + int format = snap->format; + const char *formatStr = NULL; char *persistSource = NULL; int ret = -1; int fd = -1; @@ -10663,15 +10662,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, return -1; }
- if (snap->driverType) { - format = virStorageFileFormatTypeFromString(snap->driverType); - if (format <= 0) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unknown driver type %s"), snap->driverType); - goto cleanup; - } - } - if (virAsprintf(&device, "drive-%s", disk->info.alias) < 0 || !(source = strdup(snap->file)) || (persistDisk && @@ -10717,8 +10707,10 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->format = origdriver;
/* create the actual snapshot */ + if (snap->format) + formatStr = virStorageFileFormatTypeToString(snap->format); ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source, - snap->driverType, reuse); + formatStr, reuse); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0); if (ret < 0) goto cleanup; --
Visually looks good. ACK. -- Doug Goldstein

Backing chains can end on a network protocol, such as nbd:xxx; we should not attempt to probe the file system in this case. * src/storage/storage_backend_fs.c (virStorageBackendProbeTarget): Only probe files. --- src/storage/storage_backend_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 1e48a51..db19b87 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -108,7 +108,8 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, if (meta->backingStore) { *backingStore = meta->backingStore; meta->backingStore = NULL; - if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO) { + if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO && + meta->backingStoreIsFile) { if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) { /* If the backing file is currently unavailable, only log an error, * but continue. Returning -1 here would disable the whole storage -- 1.7.11.7

On Sat, Oct 13, 2012 at 5:00 PM, Eric Blake <eblake@redhat.com> wrote:
Backing chains can end on a network protocol, such as nbd:xxx; we should not attempt to probe the file system in this case.
* src/storage/storage_backend_fs.c (virStorageBackendProbeTarget): Only probe files. --- src/storage/storage_backend_fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index 1e48a51..db19b87 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -108,7 +108,8 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, if (meta->backingStore) { *backingStore = meta->backingStore; meta->backingStore = NULL; - if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO) { + if (meta->backingStoreFormat == VIR_STORAGE_FILE_AUTO && + meta->backingStoreIsFile) { if ((ret = virStorageFileProbeFormat(*backingStore)) < 0) { /* If the backing file is currently unavailable, only log an error, * but continue. Returning -1 here would disable the whole storage --
Makes sense. ACK. -- Doug Goldstein

Previously, no one was using virStorageFileGetMetadata, and for good reason - it couldn't support root-squash NFS. Change the signature and make it useful to future patches, including enhancing the metadata to recursively track the entire chain. * src/util/storage_file.h (_virStorageFileMetadata): Add field. (virStorageFileGetMetadata): Alter signature. * src/util/storage_file.c (virStorageFileGetMetadata): Rewrite. (virStorageFileGetMetadataRecurse): New function. (virStorageFileFreeMetadata): Handle recursion. --- src/util/storage_file.c | 94 +++++++++++++++++++++++++++++++++++++++---------- src/util/storage_file.h | 18 ++++++---- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 3d1b7cf..0f879bd 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -40,6 +40,7 @@ #include "logging.h" #include "virfile.h" #include "c-ctype.h" +#include "virhash.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -840,7 +841,7 @@ virStorageFileProbeFormat(const char *path) * * Extract metadata about the storage volume with the specified * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. + * will probe to automatically identify the format. Does not recurse. * * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a * format, since a malicious guest can turn a raw file into any @@ -910,12 +911,69 @@ cleanup: return ret; } +/* Recursive workhorse for virStorageFileGetMetadata. */ +static virStorageFileMetadataPtr +virStorageFileGetMetadataRecurse(const char *path, int format, + uid_t uid, gid_t gid, + bool allow_probe, virHashTablePtr cycle) +{ + int fd; + virStorageFileMetadataPtr ret = NULL; + + if (virHashLookup(cycle, path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store for %s is self-referential"), + path); + return NULL; + } + if (virHashAddEntry(cycle, path, (void *)1) < 0) + return NULL; + + if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) { + virReportSystemError(-fd, _("cannot open file '%s'"), path); + return NULL; + } + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + VIR_FORCE_CLOSE(fd); + return NULL; + } + if (virStorageFileGetMetadataFromFD(path, fd, format, ret) < 0) { + virStorageFileFreeMetadata(ret); + VIR_FORCE_CLOSE(fd); + return NULL; + } + + if (VIR_CLOSE(fd) < 0) + virReportSystemError(errno, _("could not close file %s"), path); + + if (ret->backingStoreIsFile) { + if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) + ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; + else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) + ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + format = ret->backingStoreFormat; + ret->backingMeta = virStorageFileGetMetadataRecurse(ret->backingStore, + format, + uid, gid, + allow_probe, + cycle); + } + + return ret; +} + /** * virStorageFileGetMetadata: * * Extract metadata about the storage volume with the specified * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. + * will probe to automatically identify the format. Recurses through + * the entire chain. + * + * Open files using UID and GID (or pass -1 for the current user/group). + * Treat any backing files without explicit type as raw, unless ALLOW_PROBE. * * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a * format, since a malicious guest can turn a raw file into any @@ -923,27 +981,24 @@ cleanup: * * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO * it indicates the image didn't specify an explicit format for its - * backing store. Callers are advised against probing for the - * backing store format in this case. + * backing store. Callers are advised against using ALLOW_PROBE, as + * it would probe the backing store format in this case. * - * Caller MUST free @meta after use via virStorageFileFreeMetadata. + * Caller MUST free result after use via virStorageFileFreeMetadata. */ -int -virStorageFileGetMetadata(const char *path, - int format, - virStorageFileMetadata *meta) +virStorageFileMetadataPtr +virStorageFileGetMetadata(const char *path, int format, + uid_t uid, gid_t gid, + bool allow_probe) { - int fd, ret; - - if ((fd = open(path, O_RDONLY)) < 0) { - virReportSystemError(errno, _("cannot open file '%s'"), path); - return -1; - } - - ret = virStorageFileGetMetadataFromFD(path, fd, format, meta); - - VIR_FORCE_CLOSE(fd); + virHashTablePtr cycle = virHashCreate(5, NULL); + virStorageFileMetadataPtr ret; + if (!cycle) + return NULL; + ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, + allow_probe, cycle); + virHashFree(cycle); return ret; } @@ -958,6 +1013,7 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) if (!meta) return; + virStorageFileFreeMetadata(meta->backingMeta); VIR_FREE(meta->backingStore); VIR_FREE(meta); } diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 683ec73..18dea6a 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -50,13 +50,16 @@ enum virStorageFileFormat { VIR_ENUM_DECL(virStorageFileFormat); -typedef struct _virStorageFileMetadata { +typedef struct _virStorageFileMetadata virStorageFileMetadata; +typedef virStorageFileMetadata *virStorageFileMetadataPtr; +struct _virStorageFileMetadata { char *backingStore; - int backingStoreFormat; + int backingStoreFormat; /* enum virStorageFileFormat */ bool backingStoreIsFile; + virStorageFileMetadataPtr backingMeta; unsigned long long capacity; bool encrypted; -} virStorageFileMetadata; +}; # ifndef DEV_BSIZE # define DEV_BSIZE 512 @@ -66,15 +69,16 @@ int virStorageFileProbeFormat(const char *path); int virStorageFileProbeFormatFromFD(const char *path, int fd); -int virStorageFileGetMetadata(const char *path, - int format, - virStorageFileMetadata *meta); +virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path, + int format, + uid_t uid, gid_t gid, + bool allow_probe); int virStorageFileGetMetadataFromFD(const char *path, int fd, int format, virStorageFileMetadata *meta); -void virStorageFileFreeMetadata(virStorageFileMetadata *meta); +void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta); int virStorageFileResize(const char *path, unsigned long long capacity); -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
Previously, no one was using virStorageFileGetMetadata, and for good reason - it couldn't support root-squash NFS. Change the signature and make it useful to future patches, including enhancing the metadata to recursively track the entire chain.
* src/util/storage_file.h (_virStorageFileMetadata): Add field. (virStorageFileGetMetadata): Alter signature. * src/util/storage_file.c (virStorageFileGetMetadata): Rewrite. (virStorageFileGetMetadataRecurse): New function. (virStorageFileFreeMetadata): Handle recursion. --- src/util/storage_file.c | 94 +++++++++++++++++++++++++++++++++++++++---------- src/util/storage_file.h | 18 ++++++---- 2 files changed, 86 insertions(+), 26 deletions(-)
diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 3d1b7cf..0f879bd 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -40,6 +40,7 @@ #include "logging.h" #include "virfile.h" #include "c-ctype.h" +#include "virhash.h"
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -840,7 +841,7 @@ virStorageFileProbeFormat(const char *path) * * Extract metadata about the storage volume with the specified * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. + * will probe to automatically identify the format. Does not recurse. * * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a * format, since a malicious guest can turn a raw file into any @@ -910,12 +911,69 @@ cleanup: return ret; }
+/* Recursive workhorse for virStorageFileGetMetadata. */ +static virStorageFileMetadataPtr +virStorageFileGetMetadataRecurse(const char *path, int format, + uid_t uid, gid_t gid, + bool allow_probe, virHashTablePtr cycle) +{ + int fd; + virStorageFileMetadataPtr ret = NULL; + + if (virHashLookup(cycle, path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("backing store for %s is self-referential"), + path); + return NULL; + } + if (virHashAddEntry(cycle, path, (void *)1) < 0) + return NULL; + + if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) { + virReportSystemError(-fd, _("cannot open file '%s'"), path); + return NULL; + } + + if (VIR_ALLOC(ret) < 0) { + virReportOOMError(); + VIR_FORCE_CLOSE(fd); + return NULL; + } + if (virStorageFileGetMetadataFromFD(path, fd, format, ret) < 0) { + virStorageFileFreeMetadata(ret); + VIR_FORCE_CLOSE(fd); + return NULL; + } + + if (VIR_CLOSE(fd) < 0) + virReportSystemError(errno, _("could not close file %s"), path);
If this isn't fatal to the operation, shouldn't it be a warning instead of an error? (And if it is fatal, it should free the remaining resources and return NULL)
+ + if (ret->backingStoreIsFile) { + if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) + ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; + else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) + ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + format = ret->backingStoreFormat;
Why are you bothering to set this, instead of just using ret->backingStoreFormat in the args to the following function call? (It's harmless, though, so no big deal)
+ ret->backingMeta = virStorageFileGetMetadataRecurse(ret->backingStore, + format, + uid, gid, + allow_probe, + cycle); + } + + return ret; +} + /** * virStorageFileGetMetadata: * * Extract metadata about the storage volume with the specified * image format. If image format is VIR_STORAGE_FILE_AUTO, it - * will probe to automatically identify the format. + * will probe to automatically identify the format. Recurses through + * the entire chain. + * + * Open files using UID and GID (or pass -1 for the current user/group). + * Treat any backing files without explicit type as raw, unless ALLOW_PROBE. * * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a * format, since a malicious guest can turn a raw file into any @@ -923,27 +981,24 @@ cleanup: * * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO * it indicates the image didn't specify an explicit format for its - * backing store. Callers are advised against probing for the - * backing store format in this case. + * backing store. Callers are advised against using ALLOW_PROBE, as + * it would probe the backing store format in this case. * - * Caller MUST free @meta after use via virStorageFileFreeMetadata. + * Caller MUST free result after use via virStorageFileFreeMetadata. */ -int -virStorageFileGetMetadata(const char *path, - int format, - virStorageFileMetadata *meta) +virStorageFileMetadataPtr +virStorageFileGetMetadata(const char *path, int format, + uid_t uid, gid_t gid, + bool allow_probe) { - int fd, ret; - - if ((fd = open(path, O_RDONLY)) < 0) { - virReportSystemError(errno, _("cannot open file '%s'"), path); - return -1; - } - - ret = virStorageFileGetMetadataFromFD(path, fd, format, meta); - - VIR_FORCE_CLOSE(fd); + virHashTablePtr cycle = virHashCreate(5, NULL); + virStorageFileMetadataPtr ret;
+ if (!cycle) + return NULL; + ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, + allow_probe, cycle); + virHashFree(cycle); return ret; }
@@ -958,6 +1013,7 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) if (!meta) return;
+ virStorageFileFreeMetadata(meta->backingMeta); VIR_FREE(meta->backingStore); VIR_FREE(meta); } diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 683ec73..18dea6a 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -50,13 +50,16 @@ enum virStorageFileFormat {
VIR_ENUM_DECL(virStorageFileFormat);
-typedef struct _virStorageFileMetadata { +typedef struct _virStorageFileMetadata virStorageFileMetadata; +typedef virStorageFileMetadata *virStorageFileMetadataPtr; +struct _virStorageFileMetadata { char *backingStore; - int backingStoreFormat; + int backingStoreFormat; /* enum virStorageFileFormat */ bool backingStoreIsFile; + virStorageFileMetadataPtr backingMeta; unsigned long long capacity; bool encrypted; -} virStorageFileMetadata; +};
# ifndef DEV_BSIZE # define DEV_BSIZE 512 @@ -66,15 +69,16 @@ int virStorageFileProbeFormat(const char *path); int virStorageFileProbeFormatFromFD(const char *path, int fd);
-int virStorageFileGetMetadata(const char *path, - int format, - virStorageFileMetadata *meta); +virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path, + int format, + uid_t uid, gid_t gid, + bool allow_probe); int virStorageFileGetMetadataFromFD(const char *path, int fd, int format, virStorageFileMetadata *meta);
-void virStorageFileFreeMetadata(virStorageFileMetadata *meta); +void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta);
int virStorageFileResize(const char *path, unsigned long long capacity);
ACK, assuming acceptable response to my above questions.

On 10/16/2012 08:43 AM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
Previously, no one was using virStorageFileGetMetadata, and for good reason - it couldn't support root-squash NFS. Change the signature and make it useful to future patches, including enhancing the metadata to recursively track the entire chain.
+ + if (VIR_CLOSE(fd) < 0) + virReportSystemError(errno, _("could not close file %s"), path);
If this isn't fatal to the operation, shouldn't it be a warning instead of an error? (And if it is fatal, it should free the remaining resources and return NULL)
Later in the series, I convert virDomainDiskDefForeachPath, which had an argument that said whether to ignore open failures while recursing through a chain. That implies that there are some callers that care about being able to find every file in a chain, while other callers want to know as much as possible but don't care once they can't get further. I debated about how best to represent this in the new recursive data structure, and ended up with: if meta->backingStoreIsFile is set but meta->backingStoreMeta is clear, the recursion failed partway through, and it is up to the caller to decide if an aborted recursion is fatal. I also debated about whether I should have the helper function return an int instead of a pointer; returning a pointer or NULL is a boolean operation, and does not allow for as much flexibility. Returning an int would let me do things like returning 0 if recursion ended normally, returning 1 if recursion ended because a file could not be opened, and returning -1 if recursion ended due to OOM or other fatal problem. If you want me to revise along those lines I can, but for now, I think I'll just change this to be a VIR_WARN instead of a virReportSystemError().
+ + if (ret->backingStoreIsFile) { + if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) + ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; + else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) + ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + format = ret->backingStoreFormat;
Why are you bothering to set this, instead of just using ret->backingStoreFormat in the args to the following function call? (It's harmless, though, so no big deal)
I did that solely to fit within 80 columns.
ACK, assuming acceptable response to my above questions.
-- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/16/2012 10:58 AM, Eric Blake wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
Previously, no one was using virStorageFileGetMetadata, and for good reason - it couldn't support root-squash NFS. Change the signature and make it useful to future patches, including enhancing the metadata to recursively track the entire chain.
+ + if (VIR_CLOSE(fd) < 0) + virReportSystemError(errno, _("could not close file %s"), path); If this isn't fatal to the operation, shouldn't it be a warning instead of an error? (And if it is fatal, it should free the remaining resources and return NULL) Later in the series, I convert virDomainDiskDefForeachPath, which had an argument that said whether to ignore open failures while recursing
On 10/16/2012 08:43 AM, Laine Stump wrote: through a chain. That implies that there are some callers that care about being able to find every file in a chain, while other callers want to know as much as possible but don't care once they can't get further. I debated about how best to represent this in the new recursive data structure, and ended up with: if meta->backingStoreIsFile is set but meta->backingStoreMeta is clear, the recursion failed partway through, and it is up to the caller to decide if an aborted recursion is fatal.
I also debated about whether I should have the helper function return an int instead of a pointer; returning a pointer or NULL is a boolean operation, and does not allow for as much flexibility. Returning an int would let me do things like returning 0 if recursion ended normally, returning 1 if recursion ended because a file could not be opened, and returning -1 if recursion ended due to OOM or other fatal problem. If you want me to revise along those lines I can, but for now, I think I'll just change this to be a VIR_WARN instead of a virReportSystemError().
Either is fine with me.
+ + if (ret->backingStoreIsFile) { + if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) + ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; + else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) + ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO; + format = ret->backingStoreFormat; Why are you bothering to set this, instead of just using ret->backingStoreFormat in the args to the following function call? (It's harmless, though, so no big deal) I did that solely to fit within 80 columns.
ACK, assuming acceptable response to my above questions.

Requiring pre-allocation was an unusual idiom. It allowed iteration over the backing chain to use fewer mallocs, but made one-shot clients harder to read. Also, this makes it easier for a future patch to move away from opening fds on every iteration over the chain. * src/util/storage_file.h (virStorageFileGetMetadataFromFD): Alter signature. * src/util/storage_file.c (virStorageFileGetMetadataFromFD): Allocate return value. (virStorageFileGetMetadata): Update clients. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendProbeTarget): Likewise. --- src/conf/domain_conf.c | 11 ++++------ src/qemu/qemu_driver.c | 9 +------- src/storage/storage_backend_fs.c | 12 +++------- src/util/storage_file.c | 47 +++++++++++++++++++--------------------- src/util/storage_file.h | 7 +++--- 5 files changed, 33 insertions(+), 53 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index acf1904..a1d5a1a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14644,16 +14644,11 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, int ret = -1; size_t depth = 0; char *nextpath = NULL; - virStorageFileMetadata *meta; + virStorageFileMetadata *meta = NULL; if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0; - if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - return ret; - } - if (disk->format > 0) { format = disk->format; } else { @@ -14696,7 +14691,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, } } - if (virStorageFileGetMetadataFromFD(path, fd, format, meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { VIR_FORCE_CLOSE(fd); goto cleanup; } @@ -14730,6 +14725,8 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, /* Allow probing for image formats that are safe */ if (format == VIR_STORAGE_FILE_AUTO_SAFE) format = VIR_STORAGE_FILE_AUTO; + virStorageFileFreeMetadata(meta); + meta = NULL; } while (nextpath); ret = 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4d1c63c..0e7665e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9202,14 +9202,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, } } - if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (virStorageFileGetMetadataFromFD(path, fd, - format, - meta) < 0) + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) goto cleanup; /* Get info for normal formats */ diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index db19b87..cecc2d2 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -68,12 +68,7 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, { int fd = -1; int ret = -1; - virStorageFileMetadata *meta; - - if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - return ret; - } + virStorageFileMetadata *meta = NULL; *backingStore = NULL; *backingStoreFormat = VIR_STORAGE_FILE_AUTO; @@ -96,9 +91,8 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, goto error; } - if (virStorageFileGetMetadataFromFD(target->path, fd, - target->format, - meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(target->path, fd, + target->format))) { ret = -1; goto error; } diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 0f879bd..5387f1e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -852,41 +852,43 @@ virStorageFileProbeFormat(const char *path) * backing store. Callers are advised against probing for the * backing store format in this case. * - * Caller MUST free @meta after use via virStorageFileFreeMetadata. + * Caller MUST free the result after use via virStorageFileFreeMetadata. */ -int +virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, int fd, - int format, - virStorageFileMetadata *meta) + int format) { + virStorageFileMetadata *meta = NULL; unsigned char *head = NULL; ssize_t len = STORAGE_MAX_HEAD; - int ret = -1; + virStorageFileMetadata *ret = NULL; struct stat sb; - memset(meta, 0, sizeof(*meta)); + if (VIR_ALLOC(meta) < 0) { + virReportOOMError(); + return NULL; + } if (fstat(fd, &sb) < 0) { virReportSystemError(errno, _("cannot stat file '%s'"), path); - return -1; + goto cleanup; } - /* No header to probe for directories */ - if (S_ISDIR(sb.st_mode)) { - return 0; - } + /* No header to probe for directories, but also no backing file */ + if (S_ISDIR(sb.st_mode)) + return meta; if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { virReportSystemError(errno, _("cannot seek to start of '%s'"), path); - return -1; + goto cleanup; } if (VIR_ALLOC_N(head, len) < 0) { virReportOOMError(); - return -1; + goto cleanup; } if ((len = read(fd, head, len)) < 0) { @@ -904,9 +906,13 @@ virStorageFileGetMetadataFromFD(const char *path, goto cleanup; } - ret = virStorageFileGetMetadataFromBuf(format, path, head, len, meta); + if (virStorageFileGetMetadataFromBuf(format, path, head, len, meta) < 0) + goto cleanup; + ret = meta; + meta = NULL; cleanup: + virStorageFileFreeMetadata(meta); VIR_FREE(head); return ret; } @@ -934,21 +940,12 @@ virStorageFileGetMetadataRecurse(const char *path, int format, return NULL; } - if (VIR_ALLOC(ret) < 0) { - virReportOOMError(); - VIR_FORCE_CLOSE(fd); - return NULL; - } - if (virStorageFileGetMetadataFromFD(path, fd, format, ret) < 0) { - virStorageFileFreeMetadata(ret); - VIR_FORCE_CLOSE(fd); - return NULL; - } + ret = virStorageFileGetMetadataFromFD(path, fd, format); if (VIR_CLOSE(fd) < 0) virReportSystemError(errno, _("could not close file %s"), path); - if (ret->backingStoreIsFile) { + if (ret && ret->backingStoreIsFile) { if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 18dea6a..9a60451 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -73,10 +73,9 @@ virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path, int format, uid_t uid, gid_t gid, bool allow_probe); -int virStorageFileGetMetadataFromFD(const char *path, - int fd, - int format, - virStorageFileMetadata *meta); +virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, + int fd, + int format); void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta); -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
Requiring pre-allocation was an unusual idiom. It allowed iteration over the backing chain to use fewer mallocs, but made one-shot clients harder to read. Also, this makes it easier for a future patch to move away from opening fds on every iteration over the chain.
* src/util/storage_file.h (virStorageFileGetMetadataFromFD): Alter signature. * src/util/storage_file.c (virStorageFileGetMetadataFromFD): Allocate return value. (virStorageFileGetMetadata): Update clients. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Likewise. * src/qemu/qemu_driver.c (qemuDomainGetBlockInfo): Likewise. * src/storage/storage_backend_fs.c (virStorageBackendProbeTarget): Likewise. --- src/conf/domain_conf.c | 11 ++++------ src/qemu/qemu_driver.c | 9 +------- src/storage/storage_backend_fs.c | 12 +++------- src/util/storage_file.c | 47 +++++++++++++++++++--------------------- src/util/storage_file.h | 7 +++--- 5 files changed, 33 insertions(+), 53 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index acf1904..a1d5a1a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14644,16 +14644,11 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, int ret = -1; size_t depth = 0; char *nextpath = NULL; - virStorageFileMetadata *meta; + virStorageFileMetadata *meta = NULL;
if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0;
- if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - return ret; - } - if (disk->format > 0) { format = disk->format; } else { @@ -14696,7 +14691,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, } }
- if (virStorageFileGetMetadataFromFD(path, fd, format, meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { VIR_FORCE_CLOSE(fd); goto cleanup; } @@ -14730,6 +14725,8 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, /* Allow probing for image formats that are safe */ if (format == VIR_STORAGE_FILE_AUTO_SAFE) format = VIR_STORAGE_FILE_AUTO; + virStorageFileFreeMetadata(meta); + meta = NULL; } while (nextpath);
ret = 0; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 4d1c63c..0e7665e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9202,14 +9202,7 @@ static int qemuDomainGetBlockInfo(virDomainPtr dom, } }
- if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - goto cleanup; - } - - if (virStorageFileGetMetadataFromFD(path, fd, - format, - meta) < 0) + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) goto cleanup;
/* Get info for normal formats */ diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index db19b87..cecc2d2 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -68,12 +68,7 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, { int fd = -1; int ret = -1; - virStorageFileMetadata *meta; - - if (VIR_ALLOC(meta) < 0) { - virReportOOMError(); - return ret; - } + virStorageFileMetadata *meta = NULL;
*backingStore = NULL; *backingStoreFormat = VIR_STORAGE_FILE_AUTO; @@ -96,9 +91,8 @@ virStorageBackendProbeTarget(virStorageVolTargetPtr target, goto error; }
- if (virStorageFileGetMetadataFromFD(target->path, fd, - target->format, - meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(target->path, fd, + target->format))) { ret = -1; goto error; } diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 0f879bd..5387f1e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -852,41 +852,43 @@ virStorageFileProbeFormat(const char *path) * backing store. Callers are advised against probing for the * backing store format in this case. * - * Caller MUST free @meta after use via virStorageFileFreeMetadata. + * Caller MUST free the result after use via virStorageFileFreeMetadata. */ -int +virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, int fd, - int format, - virStorageFileMetadata *meta) + int format) { + virStorageFileMetadata *meta = NULL; unsigned char *head = NULL; ssize_t len = STORAGE_MAX_HEAD; - int ret = -1; + virStorageFileMetadata *ret = NULL; struct stat sb;
- memset(meta, 0, sizeof(*meta)); + if (VIR_ALLOC(meta) < 0) { + virReportOOMError(); + return NULL; + }
if (fstat(fd, &sb) < 0) { virReportSystemError(errno, _("cannot stat file '%s'"), path); - return -1; + goto cleanup; }
- /* No header to probe for directories */ - if (S_ISDIR(sb.st_mode)) { - return 0; - } + /* No header to probe for directories, but also no backing file */ + if (S_ISDIR(sb.st_mode)) + return meta;
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { virReportSystemError(errno, _("cannot seek to start of '%s'"), path); - return -1; + goto cleanup; }
if (VIR_ALLOC_N(head, len) < 0) { virReportOOMError(); - return -1; + goto cleanup; }
if ((len = read(fd, head, len)) < 0) { @@ -904,9 +906,13 @@ virStorageFileGetMetadataFromFD(const char *path, goto cleanup; }
- ret = virStorageFileGetMetadataFromBuf(format, path, head, len, meta); + if (virStorageFileGetMetadataFromBuf(format, path, head, len, meta) < 0) + goto cleanup; + ret = meta; + meta = NULL;
cleanup: + virStorageFileFreeMetadata(meta); VIR_FREE(head); return ret; } @@ -934,21 +940,12 @@ virStorageFileGetMetadataRecurse(const char *path, int format, return NULL; }
- if (VIR_ALLOC(ret) < 0) { - virReportOOMError(); - VIR_FORCE_CLOSE(fd); - return NULL; - } - if (virStorageFileGetMetadataFromFD(path, fd, format, ret) < 0) { - virStorageFileFreeMetadata(ret); - VIR_FORCE_CLOSE(fd); - return NULL; - } + ret = virStorageFileGetMetadataFromFD(path, fd, format);
if (VIR_CLOSE(fd) < 0) virReportSystemError(errno, _("could not close file %s"), path);
- if (ret->backingStoreIsFile) { + if (ret && ret->backingStoreIsFile) { if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) ret->backingStoreFormat = VIR_STORAGE_FILE_RAW; else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE) diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 18dea6a..9a60451 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -73,10 +73,9 @@ virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path, int format, uid_t uid, gid_t gid, bool allow_probe); -int virStorageFileGetMetadataFromFD(const char *path, - int fd, - int format, - virStorageFileMetadata *meta); +virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, + int fd, + int format);
void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta);
Fairly mechanical and straighforward. ACK.

In order to search for a backing file name as literally present in a chain, we need to remember if the chain had relative names. Also, searching for absolute names is easier if we only have to canonicalize once, rather than on every iteration. * src/util/storage_file.h (_virStorageFileMetadata): Add field. * src/util/storage_file.c (virStorageFileGetMetadataFromBuf): (virStorageFileFreeMetadata): Manage it (absolutePathFromBaseFile): Store absolute names in canonical form. --- src/util/storage_file.c | 28 ++++++++++++++++++++-------- src/util/storage_file.h | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 5387f1e..6f299c0 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -28,6 +28,7 @@ #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <stdlib.h> #ifdef __linux__ # if HAVE_LINUX_MAGIC_H # include <linux/magic.h> @@ -531,18 +532,22 @@ static char * absolutePathFromBaseFile(const char *base_file, const char *path) { char *res; + char *tmp; size_t d_len = dir_len (base_file); /* If path is already absolute, or if dirname(base_file) is ".", just return a copy of path. */ if (*path == '/' || d_len == 0) - return strdup(path); + return canonicalize_file_name(path); /* Ensure that the following cast-to-int is valid. */ if (d_len > INT_MAX) return NULL; - ignore_value(virAsprintf(&res, "%.*s/%s", (int) d_len, base_file, path)); + if (virAsprintf(&tmp, "%.*s/%s", (int) d_len, base_file, path) < 0) + return NULL; + res = canonicalize_file_name(tmp); + VIR_FREE(tmp); return res; } @@ -698,17 +703,23 @@ virStorageFileGetMetadataFromBuf(int format, meta->backingStoreIsFile = false; if (backing != NULL) { + meta->backingStore = strdup(backing); + if (meta->backingStore == NULL) { + virReportOOMError(); + VIR_FREE(backing); + return -1; + } if (virBackingStoreIsFile(backing)) { meta->backingStoreIsFile = true; + meta->backingStoreRaw = meta->backingStore; meta->backingStore = absolutePathFromBaseFile(path, backing); - } else { - meta->backingStore = strdup(backing); + if (meta->backingStore == NULL) { + virReportOOMError(); + VIR_FREE(backing); + return -1; + } } VIR_FREE(backing); - if (meta->backingStore == NULL) { - virReportOOMError(); - return -1; - } meta->backingStoreFormat = backingFormat; } else { meta->backingStore = NULL; @@ -1012,6 +1023,7 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) virStorageFileFreeMetadata(meta->backingMeta); VIR_FREE(meta->backingStore); + VIR_FREE(meta->backingStoreRaw); VIR_FREE(meta); } diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 9a60451..685fcb8 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -53,7 +53,8 @@ VIR_ENUM_DECL(virStorageFileFormat); typedef struct _virStorageFileMetadata virStorageFileMetadata; typedef virStorageFileMetadata *virStorageFileMetadataPtr; struct _virStorageFileMetadata { - char *backingStore; + char *backingStore; /* Canonical name (absolute file, or protocol) */ + char *backingStoreRaw; /* If file, original name, possibly relative */ int backingStoreFormat; /* enum virStorageFileFormat */ bool backingStoreIsFile; virStorageFileMetadataPtr backingMeta; -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
In order to search for a backing file name as literally present in a chain, we need to remember if the chain had relative names. Also, searching for absolute names is easier if we only have to canonicalize once, rather than on every iteration.
* src/util/storage_file.h (_virStorageFileMetadata): Add field. * src/util/storage_file.c (virStorageFileGetMetadataFromBuf): (virStorageFileFreeMetadata): Manage it (absolutePathFromBaseFile): Store absolute names in canonical form. --- src/util/storage_file.c | 28 ++++++++++++++++++++-------- src/util/storage_file.h | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
So I just noticed that this file is in util rather than store. I guess because its functions are called both by the storage driver and directly from qemu, right?
index 5387f1e..6f299c0 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -28,6 +28,7 @@ #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <stdlib.h> #ifdef __linux__ # if HAVE_LINUX_MAGIC_H # include <linux/magic.h> @@ -531,18 +532,22 @@ static char * absolutePathFromBaseFile(const char *base_file, const char *path) { char *res; + char *tmp; size_t d_len = dir_len (base_file);
/* If path is already absolute, or if dirname(base_file) is ".", just return a copy of path. */ if (*path == '/' || d_len == 0) - return strdup(path); + return canonicalize_file_name(path);
/* Ensure that the following cast-to-int is valid. */ if (d_len > INT_MAX) return NULL;
- ignore_value(virAsprintf(&res, "%.*s/%s", (int) d_len, base_file, path)); + if (virAsprintf(&tmp, "%.*s/%s", (int) d_len, base_file, path) < 0) + return NULL; + res = canonicalize_file_name(tmp); + VIR_FREE(tmp); return res; }
@@ -698,17 +703,23 @@ virStorageFileGetMetadataFromBuf(int format,
meta->backingStoreIsFile = false; if (backing != NULL) { + meta->backingStore = strdup(backing); + if (meta->backingStore == NULL) { + virReportOOMError(); + VIR_FREE(backing); + return -1; + } if (virBackingStoreIsFile(backing)) { meta->backingStoreIsFile = true; + meta->backingStoreRaw = meta->backingStore; meta->backingStore = absolutePathFromBaseFile(path, backing); - } else { - meta->backingStore = strdup(backing); + if (meta->backingStore == NULL) { + virReportOOMError(); + VIR_FREE(backing); + return -1; + } } VIR_FREE(backing); - if (meta->backingStore == NULL) { - virReportOOMError(); - return -1; - } meta->backingStoreFormat = backingFormat; } else { meta->backingStore = NULL; @@ -1012,6 +1023,7 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta)
virStorageFileFreeMetadata(meta->backingMeta); VIR_FREE(meta->backingStore); + VIR_FREE(meta->backingStoreRaw); VIR_FREE(meta); }
diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 9a60451..685fcb8 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -53,7 +53,8 @@ VIR_ENUM_DECL(virStorageFileFormat); typedef struct _virStorageFileMetadata virStorageFileMetadata; typedef virStorageFileMetadata *virStorageFileMetadataPtr; struct _virStorageFileMetadata { - char *backingStore; + char *backingStore; /* Canonical name (absolute file, or protocol) */ + char *backingStoreRaw; /* If file, original name, possibly relative */ int backingStoreFormat; /* enum virStorageFileFormat */ bool backingStoreIsFile; virStorageFileMetadataPtr backingMeta;
ACK.

On 10/16/2012 09:09 AM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
In order to search for a backing file name as literally present in a chain, we need to remember if the chain had relative names. Also, searching for absolute names is easier if we only have to canonicalize once, rather than on every iteration.
* src/util/storage_file.h (_virStorageFileMetadata): Add field. * src/util/storage_file.c (virStorageFileGetMetadataFromBuf): (virStorageFileFreeMetadata): Manage it (absolutePathFromBaseFile): Store absolute names in canonical form. --- src/util/storage_file.c | 28 ++++++++++++++++++++-------- src/util/storage_file.h | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-)
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
So I just noticed that this file is in util rather than store. I guess because its functions are called both by the storage driver and directly from qemu, right?
Correct - and it was that way before I started adding to it. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

In order to temporarily label files read/write during a commit operation, we need to crawl the backing chain and find the absolute file name that needs labeling in the first place, as well as the name of the file that owns the backing file. * src/util/storage_file.c (virStorageFileChainLookup): New function. * src/util/storage_file.h: Declare it. * src/libvirt_private.syms (storage_file.h): Export it. --- src/libvirt_private.syms | 1 + src/util/storage_file.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/storage_file.h | 7 ++++++ 3 files changed, 71 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fe31bbe..e40d630 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1125,6 +1125,7 @@ virStorageGenerateQcowPassphrase; # storage_file.h +virStorageFileChainLookup; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileFreeMetadata; diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 6f299c0..218891e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1260,3 +1260,66 @@ const char *virStorageFileGetSCSIKey(const char *path) return NULL; } #endif + +/* Given a CHAIN that starts at the named file START, return the + * canonical name for the backing file NAME within that chain, or pass + * NULL to find the base of the chain. If *META is not NULL, set it + * to the point in the chain that describes NAME (or to NULL if the + * backing element is not a file). If *PARENT is not NULL, set it to + * the canonical name of the parent (or to NULL if NAME matches + * START). The results point within CHAIN, and must not be + * independently freed. */ +const char * +virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, + const char *name, virStorageFileMetadataPtr *meta, + const char **parent) +{ + virStorageFileMetadataPtr owner; + const char *tmp; + + if (!parent) + parent = &tmp; + + *parent = NULL; + if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) : + !chain->backingStore) { + if (meta) + *meta = chain; + return start; + } + + owner = chain; + *parent = start; + while (owner) { + if (!owner->backingStore) + goto error; + if (!name) { + if (!owner->backingMeta || + !owner->backingMeta->backingStore) + break; + } else if (STREQ_NULLABLE(name, owner->backingStoreRaw) || + STREQ(name, owner->backingStore)) { + break; + } else if (owner->backingStoreIsFile) { + char *abs = absolutePathFromBaseFile(*parent, name); + if (abs && STREQ(abs, owner->backingStore)) { + VIR_FREE(abs); + break; + } + VIR_FREE(abs); + } + *parent = owner->backingStore; + owner = owner->backingMeta; + } + if (!owner) + goto error; + if (meta) + *meta = owner->backingMeta; + return owner->backingStore; + +error: + *parent = NULL; + if (meta) + *meta = NULL; + return NULL; +} diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 685fcb8..9b00dba 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -78,6 +78,13 @@ virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, int fd, int format); +const char *virStorageFileChainLookup(virStorageFileMetadataPtr chain, + const char *start, + const char *name, + virStorageFileMetadataPtr *meta, + const char **parent) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta); int virStorageFileResize(const char *path, unsigned long long capacity); -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
In order to temporarily label files read/write during a commit operation, we need to crawl the backing chain and find the absolute file name that needs labeling in the first place, as well as the name of the file that owns the backing file.
* src/util/storage_file.c (virStorageFileChainLookup): New function. * src/util/storage_file.h: Declare it. * src/libvirt_private.syms (storage_file.h): Export it. --- src/libvirt_private.syms | 1 + src/util/storage_file.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/storage_file.h | 7 ++++++ 3 files changed, 71 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fe31bbe..e40d630 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1125,6 +1125,7 @@ virStorageGenerateQcowPassphrase;
# storage_file.h +virStorageFileChainLookup; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileFreeMetadata; diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 6f299c0..218891e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1260,3 +1260,66 @@ const char *virStorageFileGetSCSIKey(const char *path) return NULL; } #endif + +/* Given a CHAIN that starts at the named file START, return the + * canonical name for the backing file NAME within that chain, or pass + * NULL to find the base of the chain. If *META is not NULL, set it + * to the point in the chain that describes NAME (or to NULL if the + * backing element is not a file). If *PARENT is not NULL, set it to + * the canonical name of the parent (or to NULL if NAME matches + * START). The results point within CHAIN, and must not be + * independently freed. */ +const char * +virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, + const char *name, virStorageFileMetadataPtr *meta, + const char **parent) +{ + virStorageFileMetadataPtr owner; + const char *tmp; + + if (!parent) + parent = &tmp; + + *parent = NULL; + if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) : + !chain->backingStore) {
Hmm. First time I've ever seen ?: used inside an if statement (since for me the entire purpose of ?: is to eliminate the need for an if). Maybe this would be more quickly parseable as (the admittedly longer): if ((name & (STREQ(start, name) || virFileLinkPointsTo(start, name))) || (!name && !chain->backingStore)) { Not mandatory though.
+ if (meta) + *meta = chain; + return start;
Don't you need to make sure start is an absolute path?
+ } + + owner = chain; + *parent = start; + while (owner) { + if (!owner->backingStore) + goto error; + if (!name) { + if (!owner->backingMeta || + !owner->backingMeta->backingStore) + break; + } else if (STREQ_NULLABLE(name, owner->backingStoreRaw) || + STREQ(name, owner->backingStore)) { + break; + } else if (owner->backingStoreIsFile) { + char *abs = absolutePathFromBaseFile(*parent, name); + if (abs && STREQ(abs, owner->backingStore)) { + VIR_FREE(abs); + break; + } + VIR_FREE(abs); + } + *parent = owner->backingStore; + owner = owner->backingMeta; + } + if (!owner) + goto error; + if (meta) + *meta = owner->backingMeta; + return owner->backingStore;
and I'm recalling from the previous patch that owner->backingStore is already stored in its canonical form, right?
+ +error: + *parent = NULL; + if (meta) + *meta = NULL; + return NULL; +} diff --git a/src/util/storage_file.h b/src/util/storage_file.h index 685fcb8..9b00dba 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -78,6 +78,13 @@ virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path, int fd, int format);
+const char *virStorageFileChainLookup(virStorageFileMetadataPtr chain, + const char *start, + const char *name, + virStorageFileMetadataPtr *meta, + const char **parent) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); + void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta);
int virStorageFileResize(const char *path, unsigned long long capacity);
ACK as-is if start doesn't need to be canonicalized (i.e. if I didn't understand :-), otherwise ACK with that change.

On 10/16/2012 09:55 AM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
In order to temporarily label files read/write during a commit operation, we need to crawl the backing chain and find the absolute file name that needs labeling in the first place, as well as the name of the file that owns the backing file.
+ *parent = NULL; + if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) : + !chain->backingStore) {
Hmm. First time I've ever seen ?: used inside an if statement (since for me the entire purpose of ?: is to eliminate the need for an if). Maybe this would be more quickly parseable as (the admittedly longer):
if ((name & (STREQ(start, name) || virFileLinkPointsTo(start, name))) || (!name && !chain->backingStore)) {
Not mandatory though.
I've done it before in libvirt, but yeah, it is kind of rare to see. I think I'll leave it, if only because the long form is so much longer.
+ if (meta) + *meta = chain; + return start;
Don't you need to make sure start is an absolute path?
start was passed in by the caller, and should already be absolute (although maybe not canonical), since domain XML always wants an absolute name in the <source> element of each <disk>. Also, this function is easier to use if I guarantee that I either return a pointer to somewhere within the chain, or to the actual start pointer given by the user (patch 16/16 relies on pointer equality rather than STREQ, because of this guarantee) - that is, the pointer I return must NOT be freed, because it is merely an alias to something that the user passed in. If I canonicalize start, then I would also have to strdup() all other names, and require the user to free the result of this function.
+ } else if (owner->backingStoreIsFile) { + char *abs = absolutePathFromBaseFile(*parent, name); + if (abs && STREQ(abs, owner->backingStore)) { + VIR_FREE(abs); + break; + } + VIR_FREE(abs); + } + *parent = owner->backingStore; + owner = owner->backingMeta; + } + if (!owner) + goto error; + if (meta) + *meta = owner->backingMeta; + return owner->backingStore;
and I'm recalling from the previous patch that owner->backingStore is already stored in its canonical form, right?
Correct, which is why I got away with STREQ(abs, owner->backingStore) in order to exit the loop.
ACK as-is if start doesn't need to be canonicalized (i.e. if I didn't understand :-), otherwise ACK with that change.
I think it is more important to return start as-is, even if it is not absolute, than to canonicalize in that corner case; if anything, that may mean a slight tweak to my documentation. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/16/2012 12:06 PM, Eric Blake wrote:
In order to temporarily label files read/write during a commit operation, we need to crawl the backing chain and find the absolute file name that needs labeling in the first place, as well as the name of the file that owns the backing file.
+ *parent = NULL; + if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) : + !chain->backingStore) { Hmm. First time I've ever seen ?: used inside an if statement (since for me the entire purpose of ?: is to eliminate the need for an if). Maybe
On 10/13/2012 06:00 PM, Eric Blake wrote: this would be more quickly parseable as (the admittedly longer):
if ((name & (STREQ(start, name) || virFileLinkPointsTo(start, name))) || (!name && !chain->backingStore)) {
Not mandatory though. I've done it before in libvirt, but yeah, it is kind of rare to see. I
On 10/16/2012 09:55 AM, Laine Stump wrote: think I'll leave it, if only because the long form is so much longer.
+ if (meta) + *meta = chain; + return start; Don't you need to make sure start is an absolute path? start was passed in by the caller, and should already be absolute (although maybe not canonical), since domain XML always wants an absolute name in the <source> element of each <disk>. Also, this function is easier to use if I guarantee that I either return a pointer to somewhere within the chain, or to the actual start pointer given by the user (patch 16/16 relies on pointer equality rather than STREQ, because of this guarantee) - that is, the pointer I return must NOT be freed, because it is merely an alias to something that the user passed in. If I canonicalize start, then I would also have to strdup() all other names, and require the user to free the result of this function.
+ } else if (owner->backingStoreIsFile) { + char *abs = absolutePathFromBaseFile(*parent, name); + if (abs && STREQ(abs, owner->backingStore)) { + VIR_FREE(abs); + break; + } + VIR_FREE(abs); + } + *parent = owner->backingStore; + owner = owner->backingMeta; + } + if (!owner) + goto error; + if (meta) + *meta = owner->backingMeta; + return owner->backingStore; and I'm recalling from the previous patch that owner->backingStore is already stored in its canonical form, right? Correct, which is why I got away with STREQ(abs, owner->backingStore) in order to exit the loop. ACK as-is if start doesn't need to be canonicalized (i.e. if I didn't understand :-), otherwise ACK with that change. I think it is more important to return start as-is, even if it is not absolute, than to canonicalize in that corner case; if anything, that may mean a slight tweak to my documentation.
Okay. You've obviously thought about the implications, which was the most important reason for my mentioning it.

On 10/13/2012 04:00 PM, Eric Blake wrote:
In order to temporarily label files read/write during a commit operation, we need to crawl the backing chain and find the absolute file name that needs labeling in the first place, as well as the name of the file that owns the backing file.
+ } else if (owner->backingStoreIsFile) { + char *abs = absolutePathFromBaseFile(*parent, name); + if (abs && STREQ(abs, owner->backingStore)) { + VIR_FREE(abs); + break; + } + VIR_FREE(abs);
I'll have to rename this variable to avoid a -Wshadow warning from older glibc, such as on RHEL 6.3 (the reduced namespace pollution in newer glibc on F17 is spoiling me). -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Technically, we should not be re-probing any file that qemu might be currently writing to. As such, we should cache the backing file chain prior to starting qemu. This patch adds the cache, but does not use it until the next patch. Ultimately, we want to also store the chain in domain XML, so that it is remembered across libvirtd restarts, and so that the only kosher way to modify the backing chain of an offline domain will be through libvirt API calls, but we aren't there yet. So for now, we merely invalidate the cache any time we do a live operation that alters the chain (block-pull, block-commit, external disk snapshot), as well as tear down the cache when the domain is not running. * src/conf/domain_conf.h (_virDomainDiskDef): New field. * src/conf/domain_conf.c (virDomainDiskDefFree): Clean new field. * src/qemu/qemu_domain.h (qemuDomainDetermineDiskChain): New prototype. * src/qemu/qemu_domain.c (qemuDomainDetermineDiskChain): New function. * src/qemu/qemu_driver.c (qemuDomainAttachDeviceDiskLive) (qemuDomainChangeDiskMediaLive): Pre-populate chain. (qemuDomainSnapshotCreateSingleDiskActive): Uncache chain before snapshot. * src/qemu/qemu_process.c (qemuProcessHandleBlockJob): Update chain after block pull. (qemuProcessStop): Uncache chain after process ends. --- src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 2 ++ src/qemu/qemu_domain.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 20 ++++++++++++++++++++ 6 files changed, 73 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a1d5a1a..f4c05a3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,6 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); + virStorageFileFreeMetadata(def->chain); VIR_FREE(def->mirror); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1d20522..06c1ccd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -47,6 +47,7 @@ # include "virobject.h" # include "device_conf.h" # include "bitmap.h" +# include "storage_file.h" /* forward declarations of all device types, required by * virDomainDeviceDef @@ -568,6 +569,7 @@ struct _virDomainDiskDef { } auth; char *driverName; int format; /* enum virStorageFileFormat */ + virStorageFileMetadataPtr chain; char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b51edc2..9675454 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2010,3 +2010,36 @@ qemuDomainCleanupRun(struct qemud_driver *driver, priv->ncleanupCallbacks = 0; priv->ncleanupCallbacks_max = 0; } + +int +qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force) +{ + int format; + + if (!disk->src) + return 0; + + if (disk->chain) { + if (force) { + virStorageFileFreeMetadata(disk->chain); + disk->chain = NULL; + } else { + return 0; + } + } + if (disk->format > 0) + format = disk->format; + else if (driver->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW; + + disk->chain = virStorageFileGetMetadata(disk->src, format, + driver->user, driver->group, + driver->allowDiskFormatProbing); + if (!disk->chain && !force) + return -1; + return 0; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 26b6c55..8a66f14 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -343,6 +343,9 @@ bool qemuDomainJobAllowed(qemuDomainObjPrivatePtr priv, int qemuDomainCheckDiskPresence(struct qemud_driver *driver, virDomainObjPtr vm, bool start_with_state); +int qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force); int qemuDomainCleanupAdd(virDomainObjPtr vm, qemuDomainCleanupCallback cb); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0e7665e..6588b82 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5705,6 +5705,9 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, goto end; } + if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -5933,6 +5936,9 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, virCgroupPtr cgroup = NULL; int ret = -1; + if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { @@ -10677,6 +10683,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->src = source; origdriver = disk->format; disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ + /* XXX Here, we know we are about to alter disk->chain if + * successful, so we nuke the existing chain so that future + * commands will recompute it. Better would be storing the chain + * ourselves rather than reprobing, but this requires modifying + * domain_conf and our XML to fully track the chain across + * libvirtd restarts. */ + virStorageFileFreeMetadata(disk->chain); + disk->chain = NULL; if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f8a2bfd..58cd8da 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -909,6 +909,14 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (disk) { path = disk->src; event = virDomainEventBlockJobNewFromObj(vm, path, type, status); + /* XXX If we completed a block pull, then recompute the cached + * backing chain to match. Better would be storing the chain + * ourselves rather than reprobing, but this requires + * modifying domain_conf and our XML to fully track the chain + * across libvirtd restarts. */ + if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL && + status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) + qemuDomainDetermineDiskChain(driver, disk, true); } virDomainObjUnlock(vm); @@ -4062,6 +4070,18 @@ void qemuProcessStop(struct qemud_driver *driver, networkReleaseActualDevice(net); } + /* XXX For now, disk chains should only be cached while qemu is + * running. Since we don't track the chain in XML, a user is free + * to update the chain while the domain is offline, and then when + * they next boot the domain we should re-read the chain from the + * files at that point in time. Only when we track the chain in + * XML can we forbid the user from altering the chain of an + * offline domain. */ + for (i = 0; i < def->ndisks; i++) { + virStorageFileFreeMetadata(def->disks[i]->chain); + def->disks[i]->chain = NULL; + } + retry: if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) { if (ret == -EBUSY && (retries++ < 5)) { -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
Technically, we should not be re-probing any file that qemu might be currently writing to. As such, we should cache the backing file chain prior to starting qemu. This patch adds the cache, but does not use it until the next patch.
Ultimately, we want to also store the chain in domain XML, so that it is remembered across libvirtd restarts, and so that the only kosher way to modify the backing chain of an offline domain will be through libvirt API calls, but we aren't there yet. So for now, we merely invalidate the cache any time we do a live operation that alters the chain (block-pull, block-commit, external disk snapshot), as well as tear down the cache when the domain is not running.
* src/conf/domain_conf.h (_virDomainDiskDef): New field. * src/conf/domain_conf.c (virDomainDiskDefFree): Clean new field. * src/qemu/qemu_domain.h (qemuDomainDetermineDiskChain): New prototype. * src/qemu/qemu_domain.c (qemuDomainDetermineDiskChain): New function. * src/qemu/qemu_driver.c (qemuDomainAttachDeviceDiskLive) (qemuDomainChangeDiskMediaLive): Pre-populate chain. (qemuDomainSnapshotCreateSingleDiskActive): Uncache chain before snapshot. * src/qemu/qemu_process.c (qemuProcessHandleBlockJob): Update chain after block pull. (qemuProcessStop): Uncache chain after process ends. --- src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 2 ++ src/qemu/qemu_domain.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 20 ++++++++++++++++++++ 6 files changed, 73 insertions(+)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a1d5a1a..f4c05a3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,6 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); + virStorageFileFreeMetadata(def->chain);
Do you think this name is descriptive enough? (Probably for you, but for someone looking at domain_conf.c for the first time...)
VIR_FREE(def->mirror); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1d20522..06c1ccd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -47,6 +47,7 @@ # include "virobject.h" # include "device_conf.h" # include "bitmap.h" +# include "storage_file.h"
/* forward declarations of all device types, required by * virDomainDeviceDef @@ -568,6 +569,7 @@ struct _virDomainDiskDef { } auth; char *driverName; int format; /* enum virStorageFileFormat */ + virStorageFileMetadataPtr chain;
char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b51edc2..9675454 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2010,3 +2010,36 @@ qemuDomainCleanupRun(struct qemud_driver *driver, priv->ncleanupCallbacks = 0; priv->ncleanupCallbacks_max = 0; } + +int +qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force) +{ + int format; + + if (!disk->src) + return 0; + + if (disk->chain) { + if (force) { + virStorageFileFreeMetadata(disk->chain); + disk->chain = NULL; + } else { + return 0; + } + } + if (disk->format > 0) + format = disk->format; + else if (driver->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW; + + disk->chain = virStorageFileGetMetadata(disk->src, format, + driver->user, driver->group, + driver->allowDiskFormatProbing); + if (!disk->chain && !force) + return -1; + return 0;
I would naively think that if force was true, you would want to return failure if you couldn't get the chain, you're returning failure only if force is false. Since that seems counterintuitive to me, I thought I should point it out (very likely it's correct and I again just don't understand, but better safe than sorry :-)
+} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 26b6c55..8a66f14 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -343,6 +343,9 @@ bool qemuDomainJobAllowed(qemuDomainObjPrivatePtr priv, int qemuDomainCheckDiskPresence(struct qemud_driver *driver, virDomainObjPtr vm, bool start_with_state); +int qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force);
int qemuDomainCleanupAdd(virDomainObjPtr vm, qemuDomainCleanupCallback cb); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0e7665e..6588b82 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5705,6 +5705,9 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, goto end; }
+ if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; +
.. after all - here you have force = false, and expect that it might fail, so I'm probably wrong...
if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -5933,6 +5936,9 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, virCgroupPtr cgroup = NULL; int ret = -1;
+ if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { @@ -10677,6 +10683,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->src = source; origdriver = disk->format; disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ + /* XXX Here, we know we are about to alter disk->chain if + * successful, so we nuke the existing chain so that future + * commands will recompute it. Better would be storing the chain + * ourselves rather than reprobing, but this requires modifying + * domain_conf and our XML to fully track the chain across + * libvirtd restarts. */
So are you avoiding this for now just to reduce the complexity of this series? Or is there some other reason that it's not practical?
+ virStorageFileFreeMetadata(disk->chain); + disk->chain = NULL;
if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f8a2bfd..58cd8da 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -909,6 +909,14 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (disk) { path = disk->src; event = virDomainEventBlockJobNewFromObj(vm, path, type, status); + /* XXX If we completed a block pull, then recompute the cached + * backing chain to match. Better would be storing the chain + * ourselves rather than reprobing, but this requires + * modifying domain_conf and our XML to fully track the chain + * across libvirtd restarts. */ + if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL && + status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) + qemuDomainDetermineDiskChain(driver, disk, true); }
virDomainObjUnlock(vm); @@ -4062,6 +4070,18 @@ void qemuProcessStop(struct qemud_driver *driver, networkReleaseActualDevice(net); }
+ /* XXX For now, disk chains should only be cached while qemu is + * running. Since we don't track the chain in XML, a user is free + * to update the chain while the domain is offline, and then when + * they next boot the domain we should re-read the chain from the + * files at that point in time. Only when we track the chain in + * XML can we forbid the user from altering the chain of an + * offline domain. */
Well, there are two possible levels of caching: 1) cache it in the domain's status for as long as the domain is running (this would alleviate the need to recompute it when libvirtd is restarted), 2) cache it in the domain's persistent config, so that it survives restarts of the domain. Is it used so much that either level of caching would actually be a substantial gain?
+ for (i = 0; i < def->ndisks; i++) { + virStorageFileFreeMetadata(def->disks[i]->chain); + def->disks[i]->chain = NULL; + } +
If you're storing the cache in vm->def rather than vm->newDef, shouldn't this be taken care of automatically? (At domain startup in qemuProcessStart(), virDomainObjSetDefTransient() is called, which makes a copy of vm->def into vm->newDef, then at domain shutdown in qemuProcessStop() (right at the bottom of the function), vm->def is freed, and vm->newDef is moved into its place. Since you're only ever populating the chain in vm->def if the domain is running, and since vm->newDef always exists when the domain is running (unless it's a transient domain, in which case we don't care), you can be guaranteed that vm->def will *always* be freed during qemuProcessStop anyway, making the above loop redundant.
retry: if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) { if (ret == -EBUSY && (retries++ < 5)) {
In the end, the only real issue I see is the extra unneeded loop right there at the bottom. Other than that, if there's a better name to use in the virDomainDiskDef in place of simply "chain" that would be nice, but not necessary.

On 10/16/2012 01:02 PM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
Technically, we should not be re-probing any file that qemu might be currently writing to. As such, we should cache the backing file chain prior to starting qemu. This patch adds the cache, but does not use it until the next patch.
Ultimately, we want to also store the chain in domain XML, so that it is remembered across libvirtd restarts, and so that the only kosher way to modify the backing chain of an offline domain will be through libvirt API calls, but we aren't there yet. So for now, we merely invalidate the cache any time we do a live operation that alters the chain (block-pull, block-commit, external disk snapshot), as well as tear down the cache when the domain is not running.
+++ b/src/conf/domain_conf.c @@ -971,6 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); + virStorageFileFreeMetadata(def->chain);
Do you think this name is descriptive enough? (Probably for you, but for someone looking at domain_conf.c for the first time...)
Is diskdef->backingChain any better?
+ if (!disk->chain && !force) + return -1; + return 0;
I would naively think that if force was true, you would want to return failure if you couldn't get the chain, you're returning failure only if force is false. Since that seems counterintuitive to me, I thought I should point it out (very likely it's correct and I again just don't understand, but better safe than sorry :-)
I was using 'force' more as a flag to invalidate any existing chain, and to optionally provide a new chain if convenient. qemu_process.c was the only caller in this patch that passes force=true, and it doesn't care about the state of the chain afterwards. I guess I can simplify this to just return -1 if !disk->chain, and then it is up to the caller that cares about having a valid chain to check for a 0 result to know if def->chain got populated.
+++ b/src/qemu/qemu_driver.c @@ -5705,6 +5705,9 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, goto end; }
+ if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; +
.. after all - here you have force = false, and expect that it might fail, so I'm probably wrong...
Passing force=false says that 'if there is already a chain cached, assume it is right and reuse it; so the only way I can fail is if there is nothing cached, but I couldn't read the chain'.
@@ -10677,6 +10683,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->src = source; origdriver = disk->format; disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ + /* XXX Here, we know we are about to alter disk->chain if + * successful, so we nuke the existing chain so that future + * commands will recompute it. Better would be storing the chain + * ourselves rather than reprobing, but this requires modifying + * domain_conf and our XML to fully track the chain across + * libvirtd restarts. */
So are you avoiding this for now just to reduce the complexity of this series? Or is there some other reason that it's not practical?
Short answer - avoiding the complexity of this series. I _do_ want to cache the entire backing chain, in both runtime and persistent XML, as a user-visible structure, but that's a much bigger task. Hence the XXX notes to remind whoever gets to that task to remember that this is one of the places where we have told qemu to modify the chain, and therefore we would need to reflect it in the XML we show to users.
+ /* XXX For now, disk chains should only be cached while qemu is + * running. Since we don't track the chain in XML, a user is free + * to update the chain while the domain is offline, and then when + * they next boot the domain we should re-read the chain from the + * files at that point in time. Only when we track the chain in + * XML can we forbid the user from altering the chain of an + * offline domain. */
Well, there are two possible levels of caching: 1) cache it in the domain's status for as long as the domain is running (this would alleviate the need to recompute it when libvirtd is restarted), 2) cache it in the domain's persistent config, so that it survives restarts of the domain. Is it used so much that either level of caching would actually be a substantial gain?
I'm not so sure about the gain for reduced number of open() calls, so much as the real flexibility in allowing the user to specify exactly which files qemu is allowed to open, and to visually see the entire chain at a glance (until this week's qemu.git, you couldn't even get a single 'qemu-img info' to show you an entire chain). But that can come later; for now, I needed only enough chain information to make it possible to implement the block-commit --shallow flag and selinux labeling.
+ for (i = 0; i < def->ndisks; i++) { + virStorageFileFreeMetadata(def->disks[i]->chain); + def->disks[i]->chain = NULL; + } +
If you're storing the cache in vm->def rather than vm->newDef, shouldn't this be taken care of automatically? (At domain startup in qemuProcessStart(), virDomainObjSetDefTransient() is called, which makes a copy of vm->def into vm->newDef, then at domain shutdown in qemuProcessStop() (right at the bottom of the function), vm->def is freed, and vm->newDef is moved into its place.
Ooh, good point. I'll simplify this code for v3. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/16/2012 04:07 PM, Eric Blake wrote:
On 10/16/2012 01:02 PM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
Technically, we should not be re-probing any file that qemu might be currently writing to. As such, we should cache the backing file chain prior to starting qemu. This patch adds the cache, but does not use it until the next patch.
Ultimately, we want to also store the chain in domain XML, so that it is remembered across libvirtd restarts, and so that the only kosher way to modify the backing chain of an offline domain will be through libvirt API calls, but we aren't there yet. So for now, we merely invalidate the cache any time we do a live operation that alters the chain (block-pull, block-commit, external disk snapshot), as well as tear down the cache when the domain is not running.
+++ b/src/conf/domain_conf.c @@ -971,6 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); + virStorageFileFreeMetadata(def->chain); Do you think this name is descriptive enough? (Probably for you, but for someone looking at domain_conf.c for the first time...) Is diskdef->backingChain any better?
It sounds more informative to me anyway.
+ if (!disk->chain && !force) + return -1; + return 0;
I would naively think that if force was true, you would want to return failure if you couldn't get the chain, you're returning failure only if force is false. Since that seems counterintuitive to me, I thought I should point it out (very likely it's correct and I again just don't understand, but better safe than sorry :-) I was using 'force' more as a flag to invalidate any existing chain, and to optionally provide a new chain if convenient. qemu_process.c was the only caller in this patch that passes force=true, and it doesn't care about the state of the chain afterwards. I guess I can simplify this to just return -1 if !disk->chain, and then it is up to the caller that cares about having a valid chain to check for a 0 result to know if def->chain got populated.
If you think that the two things (forcing the invalidation of existing chain, and caring whether or no the chain was populated at the end) would ever be required in a different combination, then I suppose that would be good. On the other hand, if what you've got works for the current situation, it could always be enhanced later - it's not a public API or anything. So I'm fine either way.
+++ b/src/qemu/qemu_driver.c @@ -5705,6 +5705,9 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, goto end; }
+ if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto end; + .. after all - here you have force = false, and expect that it might fail, so I'm probably wrong... Passing force=false says that 'if there is already a chain cached, assume it is right and reuse it; so the only way I can fail is if there is nothing cached, but I couldn't read the chain'.
Okay.
@@ -10677,6 +10683,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, disk->src = source; origdriver = disk->format; disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ + /* XXX Here, we know we are about to alter disk->chain if + * successful, so we nuke the existing chain so that future + * commands will recompute it. Better would be storing the chain + * ourselves rather than reprobing, but this requires modifying + * domain_conf and our XML to fully track the chain across + * libvirtd restarts. */ So are you avoiding this for now just to reduce the complexity of this series? Or is there some other reason that it's not practical? Short answer - avoiding the complexity of this series. I _do_ want to cache the entire backing chain, in both runtime and persistent XML, as a user-visible structure, but that's a much bigger task. Hence the XXX notes to remind whoever gets to that task
Ooh! Hot potato!!!! :-)
to remember that this is one of the places where we have told qemu to modify the chain, and therefore we would need to reflect it in the XML we show to users.
+ /* XXX For now, disk chains should only be cached while qemu is + * running. Since we don't track the chain in XML, a user is free + * to update the chain while the domain is offline, and then when + * they next boot the domain we should re-read the chain from the + * files at that point in time. Only when we track the chain in + * XML can we forbid the user from altering the chain of an + * offline domain. */ Well, there are two possible levels of caching: 1) cache it in the domain's status for as long as the domain is running (this would alleviate the need to recompute it when libvirtd is restarted), 2) cache it in the domain's persistent config, so that it survives restarts of the domain. Is it used so much that either level of caching would actually be a substantial gain? I'm not so sure about the gain for reduced number of open() calls, so much as the real flexibility in allowing the user to specify exactly which files qemu is allowed to open, and to visually see the entire chain at a glance (until this week's qemu.git, you couldn't even get a single 'qemu-img info' to show you an entire chain). But that can come later; for now, I needed only enough chain information to make it possible to implement the block-commit --shallow flag and selinux labeling.
+ for (i = 0; i < def->ndisks; i++) { + virStorageFileFreeMetadata(def->disks[i]->chain); + def->disks[i]->chain = NULL; + } + If you're storing the cache in vm->def rather than vm->newDef, shouldn't this be taken care of automatically? (At domain startup in qemuProcessStart(), virDomainObjSetDefTransient() is called, which makes a copy of vm->def into vm->newDef, then at domain shutdown in qemuProcessStop() (right at the bottom of the function), vm->def is freed, and vm->newDef is moved into its place. Ooh, good point. I'll simplify this code for v3.

We used to walk the backing file chain at least twice per disk, once to set up cgroup device whitelisting, and once to set up security labeling. Rather than walk the chain every iteration, which possibly includes calls to fork() in order to open root-squashed NFS files, we can exploit the cache of the previous patch. * src/conf/domain_conf.h (virDomainDiskDefForeachPath): Alter signature. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Require caller to supply backing chain via disk, if recursion is desired. * src/security/security_dac.c (virSecurityDACSetSecurityImageLabel): Adjust caller. * src/security/security_selinux.c (virSecuritySELinuxSetSecurityImageLabel): Likewise. * src/security/virt-aa-helper.c (get_files): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup): Likewise. (qemuSetupCgroup): Pre-populate chain. --- src/conf/domain_conf.c | 100 +++++++--------------------------------- src/conf/domain_conf.h | 2 - src/qemu/qemu_cgroup.c | 12 ++--- src/security/security_dac.c | 7 --- src/security/security_selinux.c | 11 ----- src/security/virt-aa-helper.c | 27 +++++++---- 6 files changed, 40 insertions(+), 119 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f4c05a3..2c0b296 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14633,110 +14633,42 @@ done: } +/* Call iter(disk, name, depth, opaque) for each element of disk and + its backing chain in the pre-populated disk->chain. + ignoreOpenFailure determines whether to warn about a chain that + mentions a backing file without also having metadata on that + file. */ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, - bool allowProbing, bool ignoreOpenFailure, - uid_t uid, gid_t gid, virDomainDiskDefPathIterator iter, void *opaque) { - virHashTablePtr paths = NULL; - int format; int ret = -1; size_t depth = 0; - char *nextpath = NULL; - virStorageFileMetadata *meta = NULL; + virStorageFileMetadata *tmp; if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0; - if (disk->format > 0) { - format = disk->format; - } else { - if (allowProbing) { - format = VIR_STORAGE_FILE_AUTO; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("no disk format for %s and probing is disabled"), - disk->src); - goto cleanup; - } - } - - paths = virHashCreate(5, NULL); - - do { - const char *path = nextpath ? nextpath : disk->src; - int fd; - - if (iter(disk, path, depth, opaque) < 0) - goto cleanup; + if (iter(disk, disk->src, 0, opaque) < 0) + goto cleanup; - if (virHashLookup(paths, path)) { + tmp = disk->chain; + while (tmp && tmp->backingStoreIsFile) { + if (!ignoreOpenFailure && !tmp->backingMeta) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store for %s is self-referential"), - disk->src); + _("unable to visit backing chain file %s"), + tmp->backingStore); goto cleanup; } - - if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) { - if (ignoreOpenFailure) { - char ebuf[1024]; - VIR_WARN("Ignoring open failure on %s: %s", path, - virStrerror(-fd, ebuf, sizeof(ebuf))); - break; - } else { - virReportSystemError(-fd, _("unable to open disk path %s"), - path); - goto cleanup; - } - } - - if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { - VIR_FORCE_CLOSE(fd); - goto cleanup; - } - - if (VIR_CLOSE(fd) < 0) - virReportSystemError(errno, - _("could not close file %s"), - path); - - if (virHashAddEntry(paths, path, (void*)0x1) < 0) + if (iter(disk, tmp->backingStore, ++depth, opaque) < 0) goto cleanup; - - depth++; - VIR_FREE(nextpath); - nextpath = meta->backingStore; - meta->backingStore = NULL; - - /* Stop iterating if we reach a non-file backing store */ - if (nextpath && !meta->backingStoreIsFile) { - VIR_DEBUG("Stopping iteration on non-file backing store: %s", - nextpath); - break; - } - - format = meta->backingStoreFormat; - - if (format == VIR_STORAGE_FILE_AUTO && - !allowProbing) - format = VIR_STORAGE_FILE_RAW; /* Stops further recursion */ - - /* Allow probing for image formats that are safe */ - if (format == VIR_STORAGE_FILE_AUTO_SAFE) - format = VIR_STORAGE_FILE_AUTO; - virStorageFileFreeMetadata(meta); - meta = NULL; - } while (nextpath); + tmp = tmp->backingMeta; + } ret = 0; cleanup: - virHashFree(paths); - VIR_FREE(nextpath); - virStorageFileFreeMetadata(meta); - return ret; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 06c1ccd..87faa7e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2139,9 +2139,7 @@ typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, void *opaque); int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, - bool allowProbing, bool ignoreOpenFailure, - uid_t uid, gid_t gid, virDomainDiskDefPathIterator iter, void *opaque); diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 166f9b9..10acb26 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,16 +87,14 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, } -int qemuSetupDiskCgroup(struct qemud_driver *driver, +int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { qemuCgroupData data = { vm, cgroup }; return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, true, - driver->user, driver->group, qemuSetupDiskPathAllow, &data); } @@ -129,16 +127,14 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, } -int qemuTeardownDiskCgroup(struct qemud_driver *driver, +int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { qemuCgroupData data = { vm, cgroup }; return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, true, - driver->user, driver->group, qemuTeardownDiskPathDeny, &data); } @@ -232,7 +228,9 @@ int qemuSetupCgroup(struct qemud_driver *driver, } for (i = 0; i < vm->def->ndisks ; i++) { - if (qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], + false) < 0 || + qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; } diff --git a/src/security/security_dac.c b/src/security/security_dac.c index f126aa5..5f11810 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -358,8 +358,6 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { - uid_t user; - gid_t group; void *params[2]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); @@ -369,15 +367,10 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0; - if (virSecurityDACGetImageIds(def, priv, &user, &group)) - return -1; - params[0] = mgr; params[1] = def; return virDomainDiskDefForeachPath(disk, - virSecurityManagerGetAllowDiskFormatProbing(mgr), false, - user, group, virSecurityDACSetSecurityFileLabel, params); } diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 10135ed..00c34c2 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1022,13 +1022,10 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk) { - bool allowDiskFormatProbing; virSecuritySELinuxCallbackData cbdata; cbdata.manager = mgr; cbdata.secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME); - allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); - if (cbdata.secdef == NULL) return -1; @@ -1038,16 +1035,8 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0; - /* XXX On one hand, it would be nice to have the driver's uid:gid - * here so we could retry opens with it. On the other hand, it - * probably doesn't matter because in practice that's only useful - * for files on root-squashed NFS shares, and NFS doesn't properly - * support selinux anyway. - */ return virDomainDiskDefForeachPath(disk, - allowDiskFormatProbing, true, - -1, -1, /* current process uid:gid */ virSecuritySELinuxSetSecurityFileLabel, &cbdata); } diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 51daa4b..729c0d1 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,19 +919,30 @@ get_files(vahControl * ctl) } for (i = 0; i < ctl->def->ndisks; i++) { + int ret; + int format; + virDomainDiskDefPtr disk = ctl->def->disks[i]; + + if (disk->format > 0) + format = disk->format; + else if (ctl->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW; + + /* XXX - if we knew the qemu user:group here we could send it in + * so that the open could be re-tried as that user:group. + */ + disk->chain = virStorageFileGetMetadata(disk->src, format, -1, -1, + ctl->allowDiskFormatProbing, + NULL); + /* XXX passing ignoreOpenFailure = true to get back to the behavior * from before using virDomainDiskDefForeachPath. actually we should * be passing ignoreOpenFailure = false and handle open errors more * careful than just ignoring them. - * XXX2 - if we knew the qemu user:group here we could send it in - * so that the open could be re-tried as that user:group. */ - int ret = virDomainDiskDefForeachPath(ctl->def->disks[i], - ctl->allowDiskFormatProbing, - true, - -1, -1, /* current uid:gid */ - add_file_path, - &buf); + ret = virDomainDiskDefForeachPath(disk, true, add_file_path, &buf); if (ret != 0) goto clean; } -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
We used to walk the backing file chain at least twice per disk, once to set up cgroup device whitelisting, and once to set up security labeling. Rather than walk the chain every iteration, which possibly includes calls to fork() in order to open root-squashed NFS files, we can exploit the cache of the previous patch.
Obviously creating the rule that you can't mess with the backing chain outside the libvirt API, but I think you've already said that :-)
* src/conf/domain_conf.h (virDomainDiskDefForeachPath): Alter signature. * src/conf/domain_conf.c (virDomainDiskDefForeachPath): Require caller to supply backing chain via disk, if recursion is desired. * src/security/security_dac.c (virSecurityDACSetSecurityImageLabel): Adjust caller. * src/security/security_selinux.c (virSecuritySELinuxSetSecurityImageLabel): Likewise. * src/security/virt-aa-helper.c (get_files): Likewise. * src/qemu/qemu_cgroup.c (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup): Likewise. (qemuSetupCgroup): Pre-populate chain. --- src/conf/domain_conf.c | 100 +++++++--------------------------------- src/conf/domain_conf.h | 2 - src/qemu/qemu_cgroup.c | 12 ++--- src/security/security_dac.c | 7 --- src/security/security_selinux.c | 11 ----- src/security/virt-aa-helper.c | 27 +++++++---- 6 files changed, 40 insertions(+), 119 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f4c05a3..2c0b296 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14633,110 +14633,42 @@ done: }
+/* Call iter(disk, name, depth, opaque) for each element of disk and + its backing chain in the pre-populated disk->chain. + ignoreOpenFailure determines whether to warn about a chain that + mentions a backing file without also having metadata on that + file. */ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, - bool allowProbing, bool ignoreOpenFailure, - uid_t uid, gid_t gid, virDomainDiskDefPathIterator iter, void *opaque) { - virHashTablePtr paths = NULL; - int format; int ret = -1; size_t depth = 0; - char *nextpath = NULL; - virStorageFileMetadata *meta = NULL; + virStorageFileMetadata *tmp;
if (!disk->src || disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0;
- if (disk->format > 0) { - format = disk->format; - } else { - if (allowProbing) { - format = VIR_STORAGE_FILE_AUTO; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("no disk format for %s and probing is disabled"), - disk->src); - goto cleanup; - } - } - - paths = virHashCreate(5, NULL); - - do { - const char *path = nextpath ? nextpath : disk->src; - int fd; - - if (iter(disk, path, depth, opaque) < 0) - goto cleanup; + if (iter(disk, disk->src, 0, opaque) < 0) + goto cleanup;
- if (virHashLookup(paths, path)) { + tmp = disk->chain; + while (tmp && tmp->backingStoreIsFile) { + if (!ignoreOpenFailure && !tmp->backingMeta) { virReportError(VIR_ERR_INTERNAL_ERROR, - _("backing store for %s is self-referential"), - disk->src); + _("unable to visit backing chain file %s"), + tmp->backingStore); goto cleanup; } - - if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) { - if (ignoreOpenFailure) { - char ebuf[1024]; - VIR_WARN("Ignoring open failure on %s: %s", path, - virStrerror(-fd, ebuf, sizeof(ebuf))); - break; - } else { - virReportSystemError(-fd, _("unable to open disk path %s"), - path); - goto cleanup; - } - } - - if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { - VIR_FORCE_CLOSE(fd); - goto cleanup; - } - - if (VIR_CLOSE(fd) < 0) - virReportSystemError(errno, - _("could not close file %s"), - path); - - if (virHashAddEntry(paths, path, (void*)0x1) < 0) + if (iter(disk, tmp->backingStore, ++depth, opaque) < 0) goto cleanup; - - depth++; - VIR_FREE(nextpath); - nextpath = meta->backingStore; - meta->backingStore = NULL; - - /* Stop iterating if we reach a non-file backing store */ - if (nextpath && !meta->backingStoreIsFile) { - VIR_DEBUG("Stopping iteration on non-file backing store: %s", - nextpath); - break; - } - - format = meta->backingStoreFormat; - - if (format == VIR_STORAGE_FILE_AUTO && - !allowProbing) - format = VIR_STORAGE_FILE_RAW; /* Stops further recursion */ - - /* Allow probing for image formats that are safe */ - if (format == VIR_STORAGE_FILE_AUTO_SAFE) - format = VIR_STORAGE_FILE_AUTO; - virStorageFileFreeMetadata(meta); - meta = NULL; - } while (nextpath); + tmp = tmp->backingMeta; + }
ret = 0;
cleanup: - virHashFree(paths); - VIR_FREE(nextpath); - virStorageFileFreeMetadata(meta); - return ret; }
Whew! Can't see the code for all the deletions! :-)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 06c1ccd..87faa7e 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2139,9 +2139,7 @@ typedef int (*virDomainDiskDefPathIterator)(virDomainDiskDefPtr disk, void *opaque);
int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, - bool allowProbing, bool ignoreOpenFailure, - uid_t uid, gid_t gid, virDomainDiskDefPathIterator iter, void *opaque);
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 166f9b9..10acb26 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,16 +87,14 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, }
-int qemuSetupDiskCgroup(struct qemud_driver *driver, +int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { qemuCgroupData data = { vm, cgroup }; return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, true, - driver->user, driver->group, qemuSetupDiskPathAllow, &data); } @@ -129,16 +127,14 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, }
-int qemuTeardownDiskCgroup(struct qemud_driver *driver, +int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { qemuCgroupData data = { vm, cgroup }; return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, true, - driver->user, driver->group, qemuTeardownDiskPathDeny, &data); } @@ -232,7 +228,9 @@ int qemuSetupCgroup(struct qemud_driver *driver, }
for (i = 0; i < vm->def->ndisks ; i++) { - if (qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], + false) < 0 || + qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; }
diff --git a/src/security/security_dac.c b/src/security/security_dac.c index f126aa5..5f11810 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -358,8 +358,6 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk)
{ - uid_t user; - gid_t group; void *params[2]; virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr);
@@ -369,15 +367,10 @@ virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0;
- if (virSecurityDACGetImageIds(def, priv, &user, &group)) - return -1; - params[0] = mgr; params[1] = def; return virDomainDiskDefForeachPath(disk, - virSecurityManagerGetAllowDiskFormatProbing(mgr), false, - user, group, virSecurityDACSetSecurityFileLabel, params); } diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 10135ed..00c34c2 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1022,13 +1022,10 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainDiskDefPtr disk)
{ - bool allowDiskFormatProbing; virSecuritySELinuxCallbackData cbdata; cbdata.manager = mgr; cbdata.secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_SELINUX_NAME);
- allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); - if (cbdata.secdef == NULL) return -1;
@@ -1038,16 +1035,8 @@ virSecuritySELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) return 0;
- /* XXX On one hand, it would be nice to have the driver's uid:gid - * here so we could retry opens with it. On the other hand, it - * probably doesn't matter because in practice that's only useful - * for files on root-squashed NFS shares, and NFS doesn't properly - * support selinux anyway. - */ return virDomainDiskDefForeachPath(disk, - allowDiskFormatProbing, true, - -1, -1, /* current process uid:gid */ virSecuritySELinuxSetSecurityFileLabel, &cbdata); } diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 51daa4b..729c0d1 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,19 +919,30 @@ get_files(vahControl * ctl) }
for (i = 0; i < ctl->def->ndisks; i++) { + int ret; + int format; + virDomainDiskDefPtr disk = ctl->def->disks[i]; + + if (disk->format > 0) + format = disk->format; + else if (ctl->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW;
It seems like I've seen this same bit of code a few times now...
+ + /* XXX - if we knew the qemu user:group here we could send it in + * so that the open could be re-tried as that user:group. + */ + disk->chain = virStorageFileGetMetadata(disk->src, format, -1, -1, + ctl->allowDiskFormatProbing, + NULL); + /* XXX passing ignoreOpenFailure = true to get back to the behavior * from before using virDomainDiskDefForeachPath. actually we should * be passing ignoreOpenFailure = false and handle open errors more * careful than just ignoring them. - * XXX2 - if we knew the qemu user:group here we could send it in - * so that the open could be re-tried as that user:group. */ - int ret = virDomainDiskDefForeachPath(ctl->def->disks[i], - ctl->allowDiskFormatProbing, - true, - -1, -1, /* current uid:gid */ - add_file_path, - &buf); + ret = virDomainDiskDefForeachPath(disk, true, add_file_path, &buf); if (ret != 0) goto clean; }
ACK.

On 10/16/2012 02:51 PM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
We used to walk the backing file chain at least twice per disk, once to set up cgroup device whitelisting, and once to set up security labeling. Rather than walk the chain every iteration, which possibly includes calls to fork() in order to open root-squashed NFS files, we can exploit the cache of the previous patch.
Obviously creating the rule that you can't mess with the backing chain outside the libvirt API, but I think you've already said that :-)
Thankfully, since the cache is only kept for running qemu, and you can't change the backing chain behind a running qemu's back except through snapshot-create, block-pull, block-copy, or block-commit, all of which are libvirt APIs that correctly update/invalidate the cache, we're set (for now) :)
Whew! Can't see the code for all the deletions! :-)
Yep, this is one of those nice cleanups enabled by doing things right in the first place.
+ if (disk->format > 0) + format = disk->format; + else if (ctl->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW;
It seems like I've seen this same bit of code a few times now...
+ + /* XXX - if we knew the qemu user:group here we could send it in + * so that the open could be re-tried as that user:group. + */ + disk->chain = virStorageFileGetMetadata(disk->src, format, -1, -1, + ctl->allowDiskFormatProbing, + NULL); +
True - I'll experiment with a followup patch that moves the format selection of the initial layer into virStorageFileGetMetadata; if it works, expect another patch to be added in v3 (and if not, we'll just live with the duplication). -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/16/2012 03:16 PM, Eric Blake wrote:
+ if (disk->format > 0) + format = disk->format; + else if (ctl->allowDiskFormatProbing) + format = VIR_STORAGE_FILE_AUTO; + else + format = VIR_STORAGE_FILE_RAW;
It seems like I've seen this same bit of code a few times now...
+ + /* XXX - if we knew the qemu user:group here we could send it in + * so that the open could be re-tried as that user:group. + */ + disk->chain = virStorageFileGetMetadata(disk->src, format, -1, -1, + ctl->allowDiskFormatProbing, + NULL); +
True - I'll experiment with a followup patch that moves the format selection of the initial layer into virStorageFileGetMetadata; if it works, expect another patch to be added in v3 (and if not, we'll just live with the duplication).
Turns out there were exactly two callers, both introduced in this series, so my v3 will refactor the redundant code into virStorageFileGetMetadata. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

qemu 1.3 will be adding a 'block-commit' monitor command, per qemu.git commit ed61fc1. It matches nicely to the libvirt API virDomainBlockCommit. * src/qemu/qemu_capabilities.h (QEMU_CAPS_BLOCK_COMMIT): New bit. * src/qemu/qemu_capabilities.c (qemuCapsProbeQMPCommands): Set it. * src/qemu/qemu_monitor.h (qemuMonitorBlockCommit): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONBlockCommit): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorBlockCommit): Implement it. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockCommit): Likewise. (qemuMonitorJSONHandleBlockJobImpl) (qemuMonitorJSONGetBlockJobInfoOne): Handle new event type. --- Change from v1: upstream qemu now has the commit recalculate backing chain after commit completes src/qemu/qemu_capabilities.c | 3 +++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_monitor.c | 30 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 7 +++++++ src/qemu/qemu_monitor_json.c | 34 ++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 +++++++ src/qemu/qemu_process.c | 15 +++++++++------ 7 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fb0fe54..73a12f1 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -187,6 +187,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "reboot-timeout", /* 110 */ "dump-guest-core", "seamless-migration", + "block-commit", ); struct _qemuCaps { @@ -1881,6 +1882,8 @@ qemuCapsProbeQMPCommands(qemuCapsPtr caps, qemuCapsSet(caps, QEMU_CAPS_SPICE); else if (STREQ(name, "query-kvm")) qemuCapsSet(caps, QEMU_CAPS_KVM); + else if (STREQ(name, "block-commit")) + qemuCapsSet(caps, QEMU_CAPS_BLOCK_COMMIT); VIR_FREE(name); } VIR_FREE(commands); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 5d343c1..6939c45 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -150,6 +150,7 @@ enum qemuCapsFlags { QEMU_CAPS_REBOOT_TIMEOUT = 110, /* -boot reboot-timeout */ QEMU_CAPS_DUMP_GUEST_CORE = 111, /* dump-guest-core-parameter */ QEMU_CAPS_SEAMLESS_MIGRATION = 112, /* seamless-migration for SPICE */ + QEMU_CAPS_BLOCK_COMMIT = 113, /* block-commit */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 85b0bc2..68c6d3f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2786,6 +2786,36 @@ qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) return ret; } +/* Start a block-commit block job. bandwidth is in MB/sec. */ +int +qemuMonitorBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long bandwidth) +{ + int ret = -1; + unsigned long long speed; + + VIR_DEBUG("mon=%p, device=%s, top=%s, base=%s, bandwidth=%ld", + mon, device, NULLSTR(top), NULLSTR(base), bandwidth); + + /* Convert bandwidth MiB to bytes */ + speed = bandwidth; + if (speed > ULLONG_MAX / 1024 / 1024) { + virReportError(VIR_ERR_OVERFLOW, + _("bandwidth must be less than %llu"), + ULLONG_MAX / 1024 / 1024); + return -1; + } + speed <<= 20; + + if (mon->json) + ret = qemuMonitorJSONBlockCommit(mon, device, top, base, speed); + else + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("block-commit requires JSON monitor")); + return ret; +} + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 54b3a99..9bdc802 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -521,6 +521,13 @@ int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); +int qemuMonitorBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *top, + const char *base, + unsigned long bandwidth) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index bd52ce4..501b338 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -803,6 +803,8 @@ qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon, if (STREQ(type_str, "stream")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; + else if (STREQ(type_str, "commit")) + type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT; switch ((virConnectDomainEventBlockJobStatus) event) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: @@ -3269,6 +3271,36 @@ cleanup: return ret; } +/* speed is in bytes/sec */ +int +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long speed) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuMonitorJSONMakeCommand("block-commit", + "s:device", device, + "U:speed", speed, + "s:top", top, + base ? "s:base" : NULL, base, + NULL); + if (!cmd) + return -1; + + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + ret = qemuMonitorJSONCheckError(cmd, reply); + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str, @@ -3385,6 +3417,8 @@ static int qemuMonitorJSONGetBlockJobInfoOne(virJSONValuePtr entry, } if (STREQ(type, "stream")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; + else if (STREQ(type, "commit")) + info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT; else info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 63b84c6..71bc6aa 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -235,6 +235,13 @@ int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, bool reuse); int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions); +int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *top, + const char *base, + unsigned long bandwidth) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 58cd8da..3396258 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -909,12 +909,15 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (disk) { path = disk->src; event = virDomainEventBlockJobNewFromObj(vm, path, type, status); - /* XXX If we completed a block pull, then recompute the cached - * backing chain to match. Better would be storing the chain - * ourselves rather than reprobing, but this requires - * modifying domain_conf and our XML to fully track the chain - * across libvirtd restarts. */ - if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL && + /* XXX If we completed a block pull or commit, then recompute + * the cached backing chain to match. Better would be storing + * the chain ourselves rather than reprobing, but this + * requires modifying domain_conf and our XML to fully track + * the chain across libvirtd restarts. For that matter, if + * qemu gains support for committing the active layer, we have + * to update disk->src. */ + if ((type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL || + type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT) && status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) qemuDomainDetermineDiskChain(driver, disk, true); } -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
qemu 1.3 will be adding a 'block-commit' monitor command, per qemu.git commit ed61fc1. It matches nicely to the libvirt API virDomainBlockCommit.
Hmm. Imagine that. What serendipity. I wonder how that could have happened. etc.... :-)
* src/qemu/qemu_capabilities.h (QEMU_CAPS_BLOCK_COMMIT): New bit. * src/qemu/qemu_capabilities.c (qemuCapsProbeQMPCommands): Set it. * src/qemu/qemu_monitor.h (qemuMonitorBlockCommit): New prototype. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONBlockCommit): Likewise. * src/qemu/qemu_monitor.c (qemuMonitorBlockCommit): Implement it. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONBlockCommit): Likewise. (qemuMonitorJSONHandleBlockJobImpl) (qemuMonitorJSONGetBlockJobInfoOne): Handle new event type. ---
Change from v1: upstream qemu now has the commit recalculate backing chain after commit completes
src/qemu/qemu_capabilities.c | 3 +++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_monitor.c | 30 ++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 7 +++++++ src/qemu/qemu_monitor_json.c | 34 ++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 7 +++++++ src/qemu/qemu_process.c | 15 +++++++++------ 7 files changed, 91 insertions(+), 6 deletions(-)
diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index fb0fe54..73a12f1 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -187,6 +187,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "reboot-timeout", /* 110 */ "dump-guest-core", "seamless-migration", + "block-commit", );
struct _qemuCaps { @@ -1881,6 +1882,8 @@ qemuCapsProbeQMPCommands(qemuCapsPtr caps, qemuCapsSet(caps, QEMU_CAPS_SPICE); else if (STREQ(name, "query-kvm")) qemuCapsSet(caps, QEMU_CAPS_KVM); + else if (STREQ(name, "block-commit")) + qemuCapsSet(caps, QEMU_CAPS_BLOCK_COMMIT); VIR_FREE(name); } VIR_FREE(commands); diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 5d343c1..6939c45 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -150,6 +150,7 @@ enum qemuCapsFlags { QEMU_CAPS_REBOOT_TIMEOUT = 110, /* -boot reboot-timeout */ QEMU_CAPS_DUMP_GUEST_CORE = 111, /* dump-guest-core-parameter */ QEMU_CAPS_SEAMLESS_MIGRATION = 112, /* seamless-migration for SPICE */ + QEMU_CAPS_BLOCK_COMMIT = 113, /* block-commit */
QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 85b0bc2..68c6d3f 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2786,6 +2786,36 @@ qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) return ret; }
+/* Start a block-commit block job. bandwidth is in MB/sec. */ +int +qemuMonitorBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long bandwidth) +{ + int ret = -1; + unsigned long long speed; + + VIR_DEBUG("mon=%p, device=%s, top=%s, base=%s, bandwidth=%ld", + mon, device, NULLSTR(top), NULLSTR(base), bandwidth); + + /* Convert bandwidth MiB to bytes */ + speed = bandwidth; + if (speed > ULLONG_MAX / 1024 / 1024) { + virReportError(VIR_ERR_OVERFLOW, + _("bandwidth must be less than %llu"), + ULLONG_MAX / 1024 / 1024); + return -1; + } + speed <<= 20; + + if (mon->json) + ret = qemuMonitorJSONBlockCommit(mon, device, top, base, speed); + else + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("block-commit requires JSON monitor")); + return ret; +} + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 54b3a99..9bdc802 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -521,6 +521,13 @@ int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+int qemuMonitorBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *top, + const char *base, + unsigned long bandwidth) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, char **reply, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index bd52ce4..501b338 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -803,6 +803,8 @@ qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon,
if (STREQ(type_str, "stream")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; + else if (STREQ(type_str, "commit")) + type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT;
switch ((virConnectDomainEventBlockJobStatus) event) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: @@ -3269,6 +3271,36 @@ cleanup: return ret; }
+/* speed is in bytes/sec */ +int +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long speed)
up above you're using unsigned long long for speed. If qemuMonitorJSONMakeCommand only accepts unsigned long, shouldn't you be limiting bandwidth in the caller to ULONG_MAX / 1024 / 1024?
+{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuMonitorJSONMakeCommand("block-commit", + "s:device", device, + "U:speed", speed, + "s:top", top, + base ? "s:base" : NULL, base, + NULL); + if (!cmd) + return -1; + + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + ret = qemuMonitorJSONCheckError(cmd, reply); + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str, @@ -3385,6 +3417,8 @@ static int qemuMonitorJSONGetBlockJobInfoOne(virJSONValuePtr entry, } if (STREQ(type, "stream")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; + else if (STREQ(type, "commit")) + info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT; else info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 63b84c6..71bc6aa 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -235,6 +235,13 @@ int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, bool reuse); int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions);
+int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, + const char *device, + const char *top, + const char *base, + unsigned long bandwidth) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); + int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 58cd8da..3396258 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -909,12 +909,15 @@ qemuProcessHandleBlockJob(qemuMonitorPtr mon ATTRIBUTE_UNUSED, if (disk) { path = disk->src; event = virDomainEventBlockJobNewFromObj(vm, path, type, status); - /* XXX If we completed a block pull, then recompute the cached - * backing chain to match. Better would be storing the chain - * ourselves rather than reprobing, but this requires - * modifying domain_conf and our XML to fully track the chain - * across libvirtd restarts. */ - if (type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL && + /* XXX If we completed a block pull or commit, then recompute + * the cached backing chain to match. Better would be storing + * the chain ourselves rather than reprobing, but this + * requires modifying domain_conf and our XML to fully track + * the chain across libvirtd restarts. For that matter, if + * qemu gains support for committing the active layer, we have + * to update disk->src. */ + if ((type == VIR_DOMAIN_BLOCK_JOB_TYPE_PULL || + type == VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT) && status == VIR_DOMAIN_BLOCK_JOB_COMPLETED) qemuDomainDetermineDiskChain(driver, disk, true); }
ACK dependent on your answer about unsigned long vs. unsigned long long and "speed".

On 10/16/2012 03:18 PM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
qemu 1.3 will be adding a 'block-commit' monitor command, per qemu.git commit ed61fc1. It matches nicely to the libvirt API virDomainBlockCommit.
Hmm. Imagine that. What serendipity. I wonder how that could have happened. etc.... :-)
Shall I just look the other way, and pretend that the addition of virDomainBlockCommit API in 0.10.2 with no implementation until now was a pure accident? :)
+/* speed is in bytes/sec */ +int +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long speed)
up above you're using unsigned long long for speed. If qemuMonitorJSONMakeCommand only accepts unsigned long, shouldn't you be limiting bandwidth in the caller to ULONG_MAX / 1024 / 1024?
Oops - you caught me. _This_ speed parameter, like qemuMonitorJSONBlockJob, should have been 'unsigned long long', since qemu_monitor.c already took care of overflow detection while converting MB/sec to bytes/sec. I'll fix that in v3.
ACK dependent on your answer about unsigned long vs. unsigned long long and "speed".
Oh dear, I think I walked right into a bad pun about crack code. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

This is the bare minimum to kick off a block commit. In particular, flags support is missing (shallow requires us to crawl the backing chain to determine the file name to pass to the qemu monitor command; delete requires us to track what needs to be deleted at the time the completion event fires). Also, we are relying on qemu to do error checking (such as validating 'top' and 'base' as being members of the backing chain), including the fact that the current qemu code does not support committing the active layer (although it is still planned to add that before qemu 1.3). Since the active layer won't change, we have it easy and do not have to alter the domain XML. Additionally, this will fail if SELinux is enforcing, because we fail to grant qemu proper read/write access to the files it will modify. * src/qemu/qemu_driver.c (qemuDomainBlockCommit): New function. (qemuDriver): Register it. --- Change from v1: allow NULL top (let qemu reject it instead) drop unused event variable src/qemu/qemu_driver.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6588b82..a9fd93a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12540,6 +12540,80 @@ qemuDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth, return qemuDomainBlockRebase(dom, path, NULL, bandwidth, flags); } + +static int +qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, + const char *top, unsigned long bandwidth, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm = NULL; + char *device = NULL; + int ret = -1; + int idx; + virDomainDiskDefPtr disk; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + if (!qemuCapsGet(priv->caps, QEMU_CAPS_BLOCK_COMMIT)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("online commit not supported with this QEMU binary")); + goto endjob; + } + + device = qemuDiskPathToAlias(vm, path, &idx); + if (!device) + goto endjob; + disk = vm->def->disks[idx]; + + if (!disk->src) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk %s has no source file to be committed"), + disk->dst); + goto endjob; + } + + if (!top) + top = disk->src; + + /* XXX For now, we are relying on qemu to check that 'top' and + * 'base' resolve to members of the backing chain in correct + * order; but if we ever get more paranoid and track the backing + * chain ourself, we should be pre-validating the data rather than + * relying on qemu. For that matter, we need to be changing the + * SELinux label on both 'base' and the parent of 'top', so that + * qemu can open(O_RDWR) those files for the duration of the + * commit. */ + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorBlockCommit(priv->mon, device, top, base, bandwidth); + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + VIR_FREE(device); + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static int qemuDomainOpenGraphics(virDomainPtr dom, unsigned int idx, @@ -13882,6 +13956,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ -- 1.7.11.7

On 10/13/2012 04:00 PM, Eric Blake wrote:
This is the bare minimum to kick off a block commit. In particular, flags support is missing (shallow requires us to crawl the backing chain to determine the file name to pass to the qemu monitor command; delete requires us to track what needs to be deleted at the time the completion event fires). Also, we are relying on qemu to do error checking (such as validating 'top' and 'base' as being members of the backing chain), including the fact that the current qemu code does not support committing the active layer (although it is still planned to add that before qemu 1.3). Since the active layer won't change, we have it easy and do not have to alter the domain XML. Additionally, this will fail if SELinux is enforcing, because we fail to grant qemu proper read/write access to the files it will modify.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): New function. (qemuDriver): Register it. ---
@@ -13882,6 +13956,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */
Given this message[1], I'm fixing this to state 1.0.0. [1] https://www.redhat.com/archives/libvir-list/2012-October/msg00403.html -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/13/2012 06:00 PM, Eric Blake wrote:
This is the bare minimum to kick off a block commit. In particular, flags support is missing (shallow requires us to crawl the backing chain to determine the file name to pass to the qemu monitor command; delete requires us to track what needs to be deleted at the time the completion event fires). Also, we are relying on qemu to do error checking (such as validating 'top' and 'base' as being members of the backing chain), including the fact that the current qemu code does not support committing the active layer (although it is still planned to add that before qemu 1.3). Since the active layer won't change, we have it easy and do not have to alter the domain XML. Additionally, this will fail if SELinux is enforcing, because we fail to grant qemu proper read/write access to the files it will modify.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): New function. (qemuDriver): Register it. ---
Change from v1: allow NULL top (let qemu reject it instead) drop unused event variable
src/qemu/qemu_driver.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 6588b82..a9fd93a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12540,6 +12540,80 @@ qemuDomainBlockPull(virDomainPtr dom, const char *path, unsigned long bandwidth, return qemuDomainBlockRebase(dom, path, NULL, bandwidth, flags); }
+ +static int +qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, + const char *top, unsigned long bandwidth, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + qemuDomainObjPrivatePtr priv; + virDomainObjPtr vm = NULL; + char *device = NULL; + int ret = -1; + int idx; + virDomainDiskDefPtr disk; + + virCheckFlags(0, -1); + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + priv = vm->privateData; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + if (!qemuCapsGet(priv->caps, QEMU_CAPS_BLOCK_COMMIT)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("online commit not supported with this QEMU binary")); + goto endjob; + } + + device = qemuDiskPathToAlias(vm, path, &idx); + if (!device) + goto endjob; + disk = vm->def->disks[idx]; + + if (!disk->src) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk %s has no source file to be committed"), + disk->dst); + goto endjob; + } + + if (!top) + top = disk->src; + + /* XXX For now, we are relying on qemu to check that 'top' and + * 'base' resolve to members of the backing chain in correct + * order; but if we ever get more paranoid and track the backing + * chain ourself, we should be pre-validating the data rather than + * relying on qemu. For that matter, we need to be changing the + * SELinux label on both 'base' and the parent of 'top', so that + * qemu can open(O_RDWR) those files for the duration of the + * commit. */ + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorBlockCommit(priv->mon, device, top, base, bandwidth); + qemuDomainObjExitMonitor(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) { + vm = NULL; + goto cleanup; + } + +cleanup: + VIR_FREE(device); + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static int qemuDomainOpenGraphics(virDomainPtr dom, unsigned int idx, @@ -13882,6 +13956,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */
ACK (with the version change you've already said you would make).

Now that we can crawl the chain of backing files, we can do argument validation and implement the 'shallow' flag. In testing this, I discovered that it can be handy to pass the shallow flag and an explicit base, as a means of validating that the base is indeed the file we expected. * src/qemu/qemu_driver.c (qemuDomainBlockCommit): Crawl through chain to implement shallow flag. * src/libvirt.c (virDomainBlockCommit): Relax API. --- src/libvirt.c | 2 -- src/qemu/qemu_driver.c | 59 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 3c6d67d..9d27240 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -19378,8 +19378,6 @@ int virDomainBlockCommit(virDomainPtr dom, const char *disk, } virCheckNonNullArgGoto(disk, error); - if (flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) - virCheckNullArgGoto(base, error); if (conn->driver->domainBlockCommit) { int ret; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a9fd93a..e9bc215 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12553,8 +12553,12 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, int ret = -1; int idx; virDomainDiskDefPtr disk; + const char *top_canon = NULL; + virStorageFileMetadataPtr top_meta = NULL; + const char *top_parent = NULL; + const char *base_canon = NULL; - virCheckFlags(0, -1); + virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1); if (!(vm = qemuDomObjFromDomain(dom))) goto cleanup; @@ -12585,20 +12589,51 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, disk->dst); goto endjob; } + if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto endjob; - if (!top) - top = disk->src; + if (!top) { + top_canon = disk->src; + top_meta = disk->chain; + } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top, &top_meta, + &top_parent))) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find top '%s' in chain for '%s'"), + top, path); + goto endjob; + } + if (!top_meta || !top_meta->backingStore) { + virReportError(VIR_ERR_INVALID_ARG, + _("top '%s' in chain for '%s' has no backing file"), + top, path); + goto endjob; + } + if (!base && (flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW)) { + base_canon = top_meta->backingStore; + } else if (!(base_canon = virStorageFileChainLookup(top_meta, top_canon, + base, NULL, NULL))) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find base '%s' below '%s' in chain " + "for '%s'"), + base ? base : "(default)", top_canon, path); + goto endjob; + } + if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && + base_canon != top_meta->backingStore) { + virReportError(VIR_ERR_INVALID_ARG, + _("base '%s' is not immediately below '%s' in chain " + "for '%s'"), + base, top_canon, path); + goto endjob; + } - /* XXX For now, we are relying on qemu to check that 'top' and - * 'base' resolve to members of the backing chain in correct - * order; but if we ever get more paranoid and track the backing - * chain ourself, we should be pre-validating the data rather than - * relying on qemu. For that matter, we need to be changing the - * SELinux label on both 'base' and the parent of 'top', so that - * qemu can open(O_RDWR) those files for the duration of the - * commit. */ + /* XXX We need to be changing the SELinux label on both 'base' and + * the parent of 'top', so that qemu can open(O_RDWR) those files + * for the duration of the commit. */ qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorBlockCommit(priv->mon, device, top, base, bandwidth); + ret = qemuMonitorBlockCommit(priv->mon, device, top_canon, base_canon, + bandwidth); qemuDomainObjExitMonitor(driver, vm); endjob: -- 1.7.11.7

On 10/13/2012 06:00 PM, Eric Blake wrote:
Now that we can crawl the chain of backing files, we can do argument validation and implement the 'shallow' flag. In testing this, I discovered that it can be handy to pass the shallow flag and an explicit base, as a means of validating that the base is indeed the file we expected.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): Crawl through chain to implement shallow flag. * src/libvirt.c (virDomainBlockCommit): Relax API. --- src/libvirt.c | 2 -- src/qemu/qemu_driver.c | 59 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index 3c6d67d..9d27240 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -19378,8 +19378,6 @@ int virDomainBlockCommit(virDomainPtr dom, const char *disk, }
virCheckNonNullArgGoto(disk, error); - if (flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) - virCheckNullArgGoto(base, error);
if (conn->driver->domainBlockCommit) { int ret; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a9fd93a..e9bc215 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12553,8 +12553,12 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, int ret = -1; int idx; virDomainDiskDefPtr disk; + const char *top_canon = NULL; + virStorageFileMetadataPtr top_meta = NULL; + const char *top_parent = NULL; + const char *base_canon = NULL;
- virCheckFlags(0, -1); + virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1);
if (!(vm = qemuDomObjFromDomain(dom))) goto cleanup; @@ -12585,20 +12589,51 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, disk->dst); goto endjob; } + if (qemuDomainDetermineDiskChain(driver, disk, false) < 0) + goto endjob;
- if (!top) - top = disk->src; + if (!top) { + top_canon = disk->src; + top_meta = disk->chain; + } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top, &top_meta, + &top_parent))) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find top '%s' in chain for '%s'"), + top, path); + goto endjob; + } + if (!top_meta || !top_meta->backingStore) { + virReportError(VIR_ERR_INVALID_ARG, + _("top '%s' in chain for '%s' has no backing file"), + top, path); + goto endjob; + } + if (!base && (flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW)) { + base_canon = top_meta->backingStore; + } else if (!(base_canon = virStorageFileChainLookup(top_meta, top_canon, + base, NULL, NULL))) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not find base '%s' below '%s' in chain " + "for '%s'"), + base ? base : "(default)", top_canon, path); + goto endjob; + } + if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && + base_canon != top_meta->backingStore) { + virReportError(VIR_ERR_INVALID_ARG, + _("base '%s' is not immediately below '%s' in chain " + "for '%s'"), + base, top_canon, path); + goto endjob; + }
- /* XXX For now, we are relying on qemu to check that 'top' and - * 'base' resolve to members of the backing chain in correct - * order; but if we ever get more paranoid and track the backing - * chain ourself, we should be pre-validating the data rather than - * relying on qemu. For that matter, we need to be changing the - * SELinux label on both 'base' and the parent of 'top', so that - * qemu can open(O_RDWR) those files for the duration of the - * commit. */ + /* XXX We need to be changing the SELinux label on both 'base' and + * the parent of 'top', so that qemu can open(O_RDWR) those files + * for the duration of the commit. */ qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorBlockCommit(priv->mon, device, top, base, bandwidth); + ret = qemuMonitorBlockCommit(priv->mon, device, top_canon, base_canon, + bandwidth); qemuDomainObjExitMonitor(driver, vm);
endjob:
Looks reasonable. ACK.

On 10/17/2012 03:28 PM, Laine Stump wrote:
On 10/13/2012 06:00 PM, Eric Blake wrote:
Now that we can crawl the chain of backing files, we can do argument validation and implement the 'shallow' flag. In testing this, I discovered that it can be handy to pass the shallow flag and an explicit base, as a means of validating that the base is indeed the file we expected.
+ } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top, &top_meta, + &top_parent))) {
+ } else if (!(base_canon = virStorageFileChainLookup(top_meta, top_canon, + base, NULL, NULL))) {
+ if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && + base_canon != top_meta->backingStore) {
Here's one of those places where I'm exploiting the pointer-equality (rather than the more expensive STREQ) given by virStorageFileChainLookup from patch 11; I'll add a comment in v3 so that it is more obvious why I expect it to work.
Looks reasonable. ACK.
Yay - the bulk of the time in me writing this patch was coming up with the right interface for virStorageFileChainLookup (and then rebasing those changes in earlier to the series) so that I had everything I needed. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Previously, snapshot code did its own permission granting (lock manager, cgroup device controller, and security manager labeling) inline. But now that we are adding block-commit and block-copy which also have to change permissions, it's better to reuse common code for the task. While snapshot should fall back to no access if read-write access failed, block-commit will want to fall back to read-only access. The common code doesn't know whether failure to grant read-write access should revert to no access (snapshot, block-copy) or read-only access (block-commit). This code can also be used to revoke access to unused files after block-pull. * src/qemu/qemu_driver.c (qemuDomainPrepareDiskChainElement): New function. (qemuDomainSnapshotCreateSingleDiskActive) (qemuDomainSnapshotUndoSingleDiskActive): Use it. --- src/qemu/qemu_driver.c | 101 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 04b28a1..59c475e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10452,6 +10452,65 @@ cleanup: return ret; } +typedef enum { + VIR_DISK_CHAIN_NO_ACCESS, + VIR_DISK_CHAIN_READ_ONLY, + VIR_DISK_CHAIN_READ_WRITE, +} qemuDomainDiskChainMode; + +/* Several operations end up adding or removing a single element of a + * disk backing file chain; this helper function ensures that the lock + * manager, cgroup device controller, and security manager labelling + * are all aware of each new file before it is added to a chain. */ +static int +qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, + virDomainObjPtr vm, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk, + char *file, + qemuDomainDiskChainMode mode) +{ + /* The easiest way to label a single file with the same + * permissions it would have as if part of the disk chain is to + * temporarily modify the disk in place. */ + char *origsrc = disk->src; + int origformat = disk->format; + virStorageFileMetadataPtr origchain = disk->chain; + bool origreadonly = disk->readonly; + int ret = -1; + + disk->src = file; + disk->format = VIR_STORAGE_FILE_RAW; + disk->chain = NULL; + disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; + + if (mode == VIR_DISK_CHAIN_NO_ACCESS) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm->def, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); + if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) + VIR_WARN("Unable to release lock on %s", disk->src); + } else if (virDomainLockDiskAttach(driver->lockManager, driver->uri, + vm, disk) < 0 || + (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) || + virSecurityManagerSetImageLabel(driver->securityManager, + vm->def, disk) < 0) { + goto cleanup; + } + + ret = 0; + +cleanup: + disk->src = origsrc; + disk->format = origformat; + disk->chain = origchain; + disk->readonly = origreadonly; + return ret; +} + + static bool qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) { @@ -10742,6 +10801,7 @@ cleanup: return ret; } + /* The domain is expected to hold monitor lock. */ static int qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, @@ -10761,8 +10821,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, char *persistSource = NULL; int ret = -1; int fd = -1; - char *origsrc = NULL; - int origdriver; bool need_unlink = false; if (snap->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { @@ -10789,10 +10847,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FORCE_CLOSE(fd); } - origsrc = disk->src; - disk->src = source; - origdriver = disk->format; - disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ /* XXX Here, we know we are about to alter disk->chain if * successful, so we nuke the existing chain so that future * commands will recompute it. Better would be storing the chain @@ -10802,27 +10856,13 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, virStorageFileFreeMetadata(disk->chain); disk->chain = NULL; - if (virDomainLockDiskAttach(driver->lockManager, driver->uri, - vm, disk) < 0) - goto cleanup; - if (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) { - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", source); - goto cleanup; - } - if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, - disk) < 0) { - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", source); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", source); + if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, + VIR_DISK_CHAIN_READ_WRITE) < 0) { + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, + VIR_DISK_CHAIN_NO_ACCESS); goto cleanup; } - disk->src = origsrc; - origsrc = NULL; - disk->format = origdriver; - /* create the actual snapshot */ if (snap->format) formatStr = virStorageFileFormatTypeToString(snap->format); @@ -10846,10 +10886,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, } cleanup: - if (origsrc) { - disk->src = origsrc; - disk->format = origdriver; - } if (need_unlink && unlink(source)) VIR_WARN("unable to unlink just-created %s", source); VIR_FREE(device); @@ -10881,13 +10917,8 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, goto cleanup; } - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm->def, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", disk->src); + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, origdisk->src, + VIR_DISK_CHAIN_NO_ACCESS); if (need_unlink && stat(disk->src, &st) == 0 && S_ISREG(st.st_mode) && unlink(disk->src) < 0) VIR_WARN("Unable to remove just-created %s", disk->src); -- 1.7.11.7

On 10/16/2012 06:03 PM, Eric Blake wrote:
Previously, snapshot code did its own permission granting (lock manager, cgroup device controller, and security manager labeling) inline. But now that we are adding block-commit and block-copy which also have to change permissions, it's better to reuse common code for the task. While snapshot should fall back to no access if read-write access failed, block-commit will want to fall back to read-only access. The common code doesn't know whether failure to grant read-write access should revert to no access (snapshot, block-copy) or read-only access (block-commit). This code can also be used to revoke access to unused files after block-pull.
* src/qemu/qemu_driver.c (qemuDomainPrepareDiskChainElement): New function. (qemuDomainSnapshotCreateSingleDiskActive) (qemuDomainSnapshotUndoSingleDiskActive): Use it. --- src/qemu/qemu_driver.c | 101 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 35 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 04b28a1..59c475e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10452,6 +10452,65 @@ cleanup: return ret; }
+typedef enum { + VIR_DISK_CHAIN_NO_ACCESS, + VIR_DISK_CHAIN_READ_ONLY, + VIR_DISK_CHAIN_READ_WRITE, +} qemuDomainDiskChainMode; + +/* Several operations end up adding or removing a single element of a + * disk backing file chain; this helper function ensures that the lock + * manager, cgroup device controller, and security manager labelling + * are all aware of each new file before it is added to a chain. */ +static int +qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, + virDomainObjPtr vm, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk, + char *file, + qemuDomainDiskChainMode mode) +{ + /* The easiest way to label a single file with the same + * permissions it would have as if part of the disk chain is to + * temporarily modify the disk in place. */ + char *origsrc = disk->src; + int origformat = disk->format; + virStorageFileMetadataPtr origchain = disk->chain; + bool origreadonly = disk->readonly; + int ret = -1; + + disk->src = file; + disk->format = VIR_STORAGE_FILE_RAW; + disk->chain = NULL; + disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; + + if (mode == VIR_DISK_CHAIN_NO_ACCESS) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm->def, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); + if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) + VIR_WARN("Unable to release lock on %s", disk->src);
This feels like a bit of a hackish way to get around an inadequate security driver (and lock and cgroup?) API by hijacking what's available. But then I haven't looked at just how deep the changes would need to be to make it work properly, so I won't dismiss it out of hand. It seems like something that should require a promise of later cleanup though :-)
+ } else if (virDomainLockDiskAttach(driver->lockManager, driver->uri, + vm, disk) < 0 || + (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) || + virSecurityManagerSetImageLabel(driver->securityManager, + vm->def, disk) < 0) { + goto cleanup; + } + + ret = 0; + +cleanup: + disk->src = origsrc; + disk->format = origformat; + disk->chain = origchain; + disk->readonly = origreadonly; + return ret; +} + + static bool qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) { @@ -10742,6 +10801,7 @@ cleanup: return ret; }
+ /* The domain is expected to hold monitor lock. */ static int qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, @@ -10761,8 +10821,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, char *persistSource = NULL; int ret = -1; int fd = -1; - char *origsrc = NULL; - int origdriver; bool need_unlink = false;
if (snap->snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL) { @@ -10789,10 +10847,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FORCE_CLOSE(fd); }
- origsrc = disk->src; - disk->src = source; - origdriver = disk->format; - disk->format = VIR_STORAGE_FILE_RAW; /* Don't want to probe backing files */ /* XXX Here, we know we are about to alter disk->chain if * successful, so we nuke the existing chain so that future * commands will recompute it. Better would be storing the chain @@ -10802,27 +10856,13 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, virStorageFileFreeMetadata(disk->chain); disk->chain = NULL;
- if (virDomainLockDiskAttach(driver->lockManager, driver->uri, - vm, disk) < 0) - goto cleanup; - if (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) { - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", source); - goto cleanup; - } - if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, - disk) < 0) { - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", source); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", source); + if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, + VIR_DISK_CHAIN_READ_WRITE) < 0) { + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, + VIR_DISK_CHAIN_NO_ACCESS); goto cleanup; }
- disk->src = origsrc; - origsrc = NULL; - disk->format = origdriver; - /* create the actual snapshot */ if (snap->format) formatStr = virStorageFileFormatTypeToString(snap->format); @@ -10846,10 +10886,6 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, }
cleanup: - if (origsrc) { - disk->src = origsrc; - disk->format = origdriver; - } if (need_unlink && unlink(source)) VIR_WARN("unable to unlink just-created %s", source); VIR_FREE(device); @@ -10881,13 +10917,8 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, goto cleanup; }
- if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm->def, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", disk->src); + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, origdisk->src, + VIR_DISK_CHAIN_NO_ACCESS); if (need_unlink && stat(disk->src, &st) == 0 && S_ISREG(st.st_mode) && unlink(disk->src) < 0) VIR_WARN("Unable to remove just-created %s", disk->src);

On 10/17/2012 06:09 PM, Laine Stump wrote:
On 10/16/2012 06:03 PM, Eric Blake wrote:
Previously, snapshot code did its own permission granting (lock manager, cgroup device controller, and security manager labeling) inline. But now that we are adding block-commit and block-copy which also have to change permissions, it's better to reuse common code for the task. While snapshot should fall back to no access if read-write access failed, block-commit will want to fall back to read-only access. The common code doesn't know whether failure to grant read-write access should revert to no access (snapshot, block-copy) or read-only access (block-commit). This code can also be used to revoke access to unused files after block-pull.
+ if (mode == VIR_DISK_CHAIN_NO_ACCESS) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm->def, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); + if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) + VIR_WARN("Unable to release lock on %s", disk->src);
This feels like a bit of a hackish way to get around an inadequate security driver (and lock and cgroup?) API by hijacking what's available. But then I haven't looked at just how deep the changes would need to be to make it work properly, so I won't dismiss it out of hand. It seems like something that should require a promise of later cleanup though :-)
This is just code motion; this code was previously here:
@@ -10881,13 +10917,8 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, goto cleanup; }
- if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm->def, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", disk->src); + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, origdisk->src, + VIR_DISK_CHAIN_NO_ACCESS);
Basically, after granting rights, if you want to then revoke rights because some later step failed, you want to log if the revoke fails, but you're already on the failure path, so there's nothing more we can do than logging. I'm not sure what you are proposing would be a more adequate security manager API. Or is that you are really complaining about my mixing of the existing virDomainDiskDefPtr (which stores the read-only and read-write labels to be used by the security manager for all files in that disk's chain) with the hack of temporarily modifying the disk to only point to the one file that I'm adding or removing from the chain? -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/17/2012 09:42 PM, Eric Blake wrote:
On 10/17/2012 06:09 PM, Laine Stump wrote:
Previously, snapshot code did its own permission granting (lock manager, cgroup device controller, and security manager labeling) inline. But now that we are adding block-commit and block-copy which also have to change permissions, it's better to reuse common code for the task. While snapshot should fall back to no access if read-write access failed, block-commit will want to fall back to read-only access. The common code doesn't know whether failure to grant read-write access should revert to no access (snapshot, block-copy) or read-only access (block-commit). This code can also be used to revoke access to unused files after block-pull.
+ if (mode == VIR_DISK_CHAIN_NO_ACCESS) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm->def, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); + if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) + VIR_WARN("Unable to release lock on %s", disk->src); This feels like a bit of a hackish way to get around an inadequate security driver (and lock and cgroup?) API by hijacking what's available. But then I haven't looked at just how deep the changes would need to be to make it work properly, so I won't dismiss it out of hand. It seems like something that should require a promise of later cleanup
On 10/16/2012 06:03 PM, Eric Blake wrote: though :-) This is just code motion; this code was previously here:
@@ -10881,13 +10917,8 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, goto cleanup; }
- if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm->def, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - if (cgroup && qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", disk->src); - if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) - VIR_WARN("Unable to release lock on %s", disk->src); + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, origdisk->src, + VIR_DISK_CHAIN_NO_ACCESS); Basically, after granting rights, if you want to then revoke rights because some later step failed, you want to log if the revoke fails, but you're already on the failure path, so there's nothing more we can do than logging. I'm not sure what you are proposing would be a more adequate security manager API.
Or is that you are really complaining about my mixing of the existing virDomainDiskDefPtr (which stores the read-only and read-write labels to be used by the security manager for all files in that disk's chain) with the hack of temporarily modifying the disk to only point to the one file that I'm adding or removing from the chain?
Right, the 2nd thing. Rather than temporarily poking different values into what is essentially an item in a database in order to trick the API into thinking that is reality, it would be cleaner if the API was flexible enough that you could just send the various pieces in as args. But again, I haven't investigated just how much work this would be, and what you have works, so I'm not going to insist on it being done right away (I just can't make myself let it go without mention, though :-)

I finally have all the pieces in place to perform a block-commit with SELinux enforcing. There's still missing cleanup work when the commit completes, but doing that requires tracking both the backing chain and the base and top files within that chain in domain XML across libvirtd restarts. * src/qemu/qemu_driver.c (qemuDomainBlockCommit): Label disks as needed. (qemuDomainPrepareDiskChainElement): Cast away const. --- src/qemu/qemu_driver.c | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 59c475e..7bd9420 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10467,7 +10467,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk, - char *file, + const char *file, qemuDomainDiskChainMode mode) { /* The easiest way to label a single file with the same @@ -10479,7 +10479,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, bool origreadonly = disk->readonly; int ret = -1; - disk->src = file; + disk->src = (char *) file; /* casting away const is safe here */ disk->format = VIR_STORAGE_FILE_RAW; disk->chain = NULL; disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; @@ -12698,6 +12698,7 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, virStorageFileMetadataPtr top_meta = NULL; const char *top_parent = NULL; const char *base_canon = NULL; + virCgroupPtr cgroup = NULL; virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1); @@ -12769,15 +12770,46 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, goto endjob; } - /* XXX We need to be changing the SELinux label on both 'base' and - * the parent of 'top', so that qemu can open(O_RDWR) those files - * for the duration of the commit. */ + /* For the commit to succeed, we must allow qemu to open both the + * 'base' image and the parent of 'top' as read/write; 'top' might + * not have a parent, or might already be read-write. XXX It + * would also be nice to revert 'base' to read-only, as well as + * revoke access to files removed from the chain, when the commit + * operation succeeds, but doing that requires tracking the + * operation in XML across libvirtd restarts. */ + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES) && + virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s"), + vm->def->name); + goto endjob; + } + if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon, + VIR_DISK_CHAIN_READ_WRITE) < 0 || + (top_parent && top_parent != disk->src && + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, + top_parent, + VIR_DISK_CHAIN_READ_WRITE) < 0)) + goto endjob; + + /* Start the commit operation. */ qemuDomainObjEnterMonitor(driver, vm); ret = qemuMonitorBlockCommit(priv->mon, device, top_canon, base_canon, bandwidth); qemuDomainObjExitMonitor(driver, vm); endjob: + if (ret < 0) { + /* Revert access to read-only, if possible. */ + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon, + VIR_DISK_CHAIN_READ_ONLY); + if (top_parent && top_parent != disk->src) + qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, + top_parent, + VIR_DISK_CHAIN_READ_ONLY); + } + if (cgroup) + virCgroupFree(&cgroup); if (qemuDomainObjEndJob(driver, vm) == 0) { vm = NULL; goto cleanup; -- 1.7.11.7
participants (3)
-
Doug Goldstein
-
Eric Blake
-
Laine Stump