[libvirt] [PATCHv3 00/19] qemu block-commit support

This v3 posting resolves all the comments I had from Doug, Laine, and myself, and has passed my testing with SELinux enabled. v2 was here: https://www.redhat.com/archives/libvir-list/2012-October/msg00633.html See below for interdiff, and individual patches for more notes about changes 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 (19): 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: remove unused parameters after previous patch blockjob: manage qemu block-commit monitor command blockjob: wire up online qemu block-commit blockjob: implement shallow commit flag in qemu blockjob: refactor qemu disk chain permission grants blockjob: properly label disks for qemu block-commit docs/schemas/domaincommon.rng | 27 +- docs/schemas/domainsnapshot.rng | 2 +- src/conf/capabilities.h | 4 +- src/conf/domain_conf.c | 165 +++------ 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 | 14 +- src/qemu/qemu_cgroup.h | 6 +- src/qemu/qemu_command.c | 18 +- src/qemu/qemu_domain.c | 33 +- src/qemu/qemu_domain.h | 3 + src/qemu/qemu_driver.c | 371 ++++++++++++++------- src/qemu/qemu_hotplug.c | 13 +- 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 | 11 + src/security/security_dac.c | 7 - src/security/security_selinux.c | 11 - src/security/virt-aa-helper.c | 20 +- src/storage/storage_backend_fs.c | 15 +- src/util/storage_file.c | 282 ++++++++++++---- 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 +- 39 files changed, 859 insertions(+), 435 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 81cb3aa..afa4cfe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,7 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); - virStorageFileFreeMetadata(def->chain); + virStorageFileFreeMetadata(def->backingChain); VIR_FREE(def->mirror); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); @@ -3723,8 +3723,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 Xen 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"); @@ -4185,7 +4189,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, driverType); goto error; } - } else { + } else if (def->type == VIR_DOMAIN_DISK_TYPE_FILE || + def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) { def->format = caps->defaultDiskDriverType; } @@ -14763,10 +14768,10 @@ 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. */ + * its backing chain in the pre-populated disk->backingChain. + * 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 ignoreOpenFailure, virDomainDiskDefPathIterator iter, @@ -14782,7 +14787,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (iter(disk, disk->src, 0, opaque) < 0) goto cleanup; - tmp = disk->chain; + tmp = disk->backingChain; while (tmp && tmp->backingStoreIsFile) { if (!ignoreOpenFailure && !tmp->backingMeta) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9c3abec..10ef841 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -569,7 +569,7 @@ struct _virDomainDiskDef { } auth; char *driverName; int format; /* enum virStorageFileFormat */ - virStorageFileMetadataPtr chain; + virStorageFileMetadataPtr backingChain; char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 428befd..db371a0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,8 +87,7 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, } -int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -127,8 +126,7 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, } -int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -230,7 +228,7 @@ int qemuSetupCgroup(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks ; i++) { if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], false) < 0 || - qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + qemuSetupDiskCgroup(vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; } diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index 362080a..c552162 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,12 +36,10 @@ typedef struct _qemuCgroupData qemuCgroupData; bool qemuCgroupControllerActive(struct qemud_driver *driver, int controller); -int qemuSetupDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); -int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f071769..4196caf 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2129,7 +2129,7 @@ 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->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); @@ -5210,7 +5210,7 @@ qemuBuildCommandLine(virConnectPtr conn, if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9675454..45f3a5e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2016,30 +2016,23 @@ qemuDomainDetermineDiskChain(struct qemud_driver *driver, virDomainDiskDefPtr disk, bool force) { - int format; + bool probe = driver->allowDiskFormatProbing; if (!disk->src) return 0; - if (disk->chain) { + if (disk->backingChain) { if (force) { - virStorageFileFreeMetadata(disk->chain); - disk->chain = NULL; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = 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) + disk->backingChain = virStorageFileGetMetadata(disk->src, disk->format, + driver->user, driver->group, + probe); + if (!disk->backingChain) return -1; return 0; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7a47cf7..3829a89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5826,7 +5826,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } switch (disk->device) { @@ -5862,7 +5862,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, } if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -6058,7 +6058,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } @@ -6077,7 +6077,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, } if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -10462,7 +10462,8 @@ typedef enum { /* 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. */ + * are all aware of each new file before it is added to a chain, and + * can revoke access to a file no longer needed in a chain. */ static int qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, virDomainObjPtr vm, @@ -10476,26 +10477,26 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, * temporarily modify the disk in place. */ char *origsrc = disk->src; int origformat = disk->format; - virStorageFileMetadataPtr origchain = disk->chain; + virStorageFileMetadataPtr origchain = disk->backingChain; bool origreadonly = disk->readonly; int ret = -1; disk->src = (char *) file; /* casting away const is safe here */ disk->format = VIR_STORAGE_FILE_RAW; - disk->chain = NULL; + disk->backingChain = 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) + if (cgroup && qemuTeardownDiskCgroup(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) || + (cgroup && qemuSetupDiskCgroup(vm, cgroup, disk) < 0) || virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { goto cleanup; @@ -10506,7 +10507,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, cleanup: disk->src = origsrc; disk->format = origformat; - disk->chain = origchain; + disk->backingChain = origchain; disk->readonly = origreadonly; return ret; } @@ -10802,7 +10803,6 @@ cleanup: return ret; } - /* The domain is expected to hold monitor lock. */ static int qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, @@ -10848,14 +10848,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FORCE_CLOSE(fd); } - /* XXX Here, we know we are about to alter disk->chain if + /* XXX Here, we know we are about to alter disk->backingChain 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; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = NULL; if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, VIR_DISK_CHAIN_READ_WRITE) < 0) { @@ -12737,8 +12737,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, if (!top) { top_canon = disk->src; - top_meta = disk->chain; - } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top_meta = disk->backingChain; + } else if (!(top_canon = virStorageFileChainLookup(disk->backingChain, + disk->src, top, &top_meta, &top_parent))) { virReportError(VIR_ERR_INVALID_ARG, @@ -12762,6 +12763,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, base ? base : "(default)", top_canon, path); goto endjob; } + /* Note that this code exploits the fact that + * virStorageFileChainLookup guarantees a simple pointer + * comparison will work, rather than needing full-blown STREQ. */ if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && base_canon != top_meta->backingStore) { virReportError(VIR_ERR_INVALID_ARG, @@ -14165,7 +14169,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ - .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca441f2..7381921 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2006,7 +2006,7 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } @@ -2089,7 +2089,7 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d0ecd1e..d1ce9cc 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3284,7 +3284,7 @@ cleanup: int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long speed) + unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 71bc6aa..61127a7 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -239,7 +239,7 @@ int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long bandwidth) + unsigned long long bandwidth) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1eb93a6..3a087e2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4121,18 +4121,6 @@ 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)) { diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 729c0d1..263fc92 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,21 +919,13 @@ 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, + disk->chain = virStorageFileGetMetadata(disk->src, disk->format, + -1, -1, ctl->allowDiskFormatProbing, NULL); @@ -942,8 +934,7 @@ get_files(vahControl * ctl) * be passing ignoreOpenFailure = false and handle open errors more * careful than just ignoring them. */ - ret = virDomainDiskDefForeachPath(disk, true, add_file_path, &buf); - if (ret != 0) + if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0) goto clean; } diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 218891e..882df6e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -271,7 +271,8 @@ qcow2GetBackingStoreFormat(int *format, break; *format = virStorageFileFormatTypeFromString( ((const char *)buf)+offset); - break; + if (*format <= VIR_STORAGE_FILE_NONE) + return -1; } offset += len; @@ -353,12 +354,10 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) { + if (isQCow2 && format && qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, - offset); - if (*format <= VIR_STORAGE_FILE_NONE) - return BACKING_STORE_INVALID; - } + offset) < 0) + return BACKING_STORE_INVALID; return BACKING_STORE_OK; } @@ -517,7 +516,7 @@ qedGetBackingStore(char **res, (*res)[size] = '\0'; if (flags & QED_F_BACKING_FORMAT_NO_PROBE) - *format = virStorageFileFormatTypeFromString("raw"); + *format = VIR_STORAGE_FILE_RAW; else *format = VIR_STORAGE_FILE_AUTO_SAFE; @@ -954,7 +953,7 @@ virStorageFileGetMetadataRecurse(const char *path, int format, ret = virStorageFileGetMetadataFromFD(path, fd, format); if (VIR_CLOSE(fd) < 0) - virReportSystemError(errno, _("could not close file %s"), path); + VIR_WARN("could not close file %s", path); if (ret && ret->backingStoreIsFile) { if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) @@ -1004,6 +1003,9 @@ virStorageFileGetMetadata(const char *path, int format, if (!cycle) return NULL; + + if (format <= VIR_STORAGE_FILE_NONE) + format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, allow_probe, cycle); virHashFree(cycle); @@ -1261,13 +1263,14 @@ const char *virStorageFileGetSCSIKey(const char *path) } #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 +/* Given a CHAIN that starts at the named file START, return a string + * pointing to either START or within CHAIN that gives the preferred + * name for the backing file NAME within that chain. Pass NULL for + * NAME to find the base of the chain. If META is not NULL, set *META * 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 + * backing element is not a file). If PARENT is not NULL, set *PARENT + * to the preferred name of the parent (or to NULL if NAME matches + * START). Since the results point within CHAIN, they must not be * independently freed. */ const char * virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, @@ -1301,12 +1304,12 @@ virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, STREQ(name, owner->backingStore)) { break; } else if (owner->backingStoreIsFile) { - char *abs = absolutePathFromBaseFile(*parent, name); - if (abs && STREQ(abs, owner->backingStore)) { - VIR_FREE(abs); + char *absName = absolutePathFromBaseFile(*parent, name); + if (absName && STREQ(absName, owner->backingStore)) { + VIR_FREE(absName); break; } - VIR_FREE(abs); + VIR_FREE(absName); } *parent = owner->backingStore; owner = owner->backingMeta; -- 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) (qcow2GetBackingStoreFormat, 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. --- v3: fix case when qcow2 uses probing for backing format src/conf/domain_conf.c | 2 +- src/conf/storage_conf.c | 15 ++++++++--- src/qemu/qemu_driver.c | 2 +- src/util/storage_file.c | 70 ++++++++++++++++++++++++++++++++++--------------- src/util/storage_file.h | 8 ++++-- 5 files changed, 69 insertions(+), 28 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index f0c5d50..74b843d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14777,7 +14777,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 adfbfa6..ead5d8f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9303,7 +9303,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..0d8cb86 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) { @@ -256,7 +269,8 @@ qcow2GetBackingStoreFormat(int *format, break; *format = virStorageFileFormatTypeFromString( ((const char *)buf)+offset); - break; + if (*format <= VIR_STORAGE_FILE_NONE) + return -1; } offset += len; @@ -298,8 +312,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 +352,10 @@ 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) < 0) + return BACKING_STORE_INVALID; return BACKING_STORE_OK; } @@ -348,10 +367,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 +425,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 +436,7 @@ vmdk4GetBackingStore(char **res, goto cleanup; } if (end == start) { + *format = VIR_STORAGE_FILE_NONE; ret = BACKING_STORE_OK; goto cleanup; } @@ -464,8 +490,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 +513,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 = VIR_STORAGE_FILE_RAW; + else + *format = VIR_STORAGE_FILE_AUTO_SAFE; return BACKING_STORE_OK; } @@ -564,7 +590,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 +633,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 +710,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 +895,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

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 output XML, but the sexpr and xm outputs should still be identical, and the input XML can still use either form. * 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. --- v3: open-code in-place string replacement, to avoid confusion with strlen src/conf/domain_conf.c | 8 ++++++-- src/xenxs/xen_sxpr.c | 12 ++++++++++-- src/xenxs/xen_xm.c | 11 ++++++++++- 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, 30 insertions(+), 9 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 74b843d..23dbdb3 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -3724,6 +3724,12 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlStrEqual(cur->name, BAD_CAST "driver")) { driverName = virXMLPropString(cur, "name"); driverType = virXMLPropString(cur, "type"); + if (STREQ_NULLABLE(driverType, "aio")) { + /* In-place conversion to "raw", for Xen 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"); @@ -14774,8 +14780,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..38a7e13 100644 --- a/src/xenxs/xen_sxpr.c +++ b/src/xenxs/xen_sxpr.c @@ -443,6 +443,12 @@ xenParseSxprDisks(virDomainDefPtr def, src); goto error; } + 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 @@ -1831,9 +1837,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..c564d35 100644 --- a/src/xenxs/xen_xm.c +++ b/src/xenxs/xen_xm.c @@ -566,6 +566,12 @@ xenParseXM(virConfPtr conf, int xendConfigVersion, disk->src); goto cleanup; } + 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, @@ -1203,9 +1209,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

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

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 23dbdb3..15d646b 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -4188,7 +4188,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 && @@ -4199,7 +4200,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 ead5d8f..740632d 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

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. --- v3: fix check for fat on qemu dir passthru, and only default to driver caps for file and blocks (not dir or network protocol) src/conf/domain_conf.c | 61 +++++++++++++++++---------------- 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 | 30 +++++++++------- src/xenxs/xen_xm.c | 33 ++++++++++-------- 11 files changed, 146 insertions(+), 159 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 15d646b..93590bf 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) @@ -4172,12 +4170,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; @@ -4186,23 +4180,35 @@ 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 if (def->type == VIR_DOMAIN_DISK_TYPE_FILE || + def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) { + 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) @@ -11814,13 +11820,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) @@ -11932,7 +11939,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"); @@ -14780,15 +14788,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 1ee4b30..eec1f25 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 0c0c400..4196caf 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 > 0 && 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 > 0 && 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) { @@ -7024,7 +7024,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 740632d..5ecb3e8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6335,11 +6335,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; @@ -9302,13 +9299,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) @@ -10480,7 +10472,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); @@ -10674,13 +10666,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; @@ -10767,13 +10760,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) { @@ -10782,14 +10774,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; } @@ -10806,8 +10803,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) @@ -10828,8 +10825,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, @@ -10843,34 +10839,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; } @@ -10887,17 +10873,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; } @@ -10917,27 +10898,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 25540bd..ca441f2 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -45,6 +45,7 @@ #include "virnetdevbridge.h" #include "virnetdevtap.h" #include "device_conf.h" +#include "storage_file.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -107,10 +108,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 38a7e13..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,21 +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")) { - /* 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 @@ -1837,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 c564d35..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,24 +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")) { - /* 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, @@ -1208,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

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 5ecb3e8..f561455 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10680,17 +10680,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) { @@ -10760,7 +10758,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; @@ -10774,15 +10773,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 && @@ -10828,8 +10818,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

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

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. --- v3: warn rather than error when not returning NULL; inline one more bit of functionality regarding probing when format unknown src/util/storage_file.c | 95 +++++++++++++++++++++++++++++++++++++++---------- src/util/storage_file.h | 18 ++++++---- 2 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 0d8cb86..68c7605 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 @@ -839,7 +840,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 @@ -909,12 +910,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) + VIR_WARN("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 @@ -922,27 +980,27 @@ 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; + virHashTablePtr cycle = virHashCreate(5, NULL); + virStorageFileMetadataPtr 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); + if (!cycle) + return NULL; + if (format <= VIR_STORAGE_FILE_NONE) + format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; + ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, + allow_probe, cycle); + virHashFree(cycle); return ret; } @@ -957,6 +1015,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/17/2012 06:30 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.
---
v3: warn rather than error when not returning NULL; inline one more bit of functionality regarding probing when format unknown
src/util/storage_file.c | 95 +++++++++++++++++++++++++++++++++++++++---------- src/util/storage_file.h | 18 ++++++---- 2 files changed, 88 insertions(+), 25 deletions(-)
diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 0d8cb86..68c7605 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
@@ -839,7 +840,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 @@ -909,12 +910,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) + VIR_WARN("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 @@ -922,27 +980,27 @@ 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; + virHashTablePtr cycle = virHashCreate(5, NULL); + virStorageFileMetadataPtr 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); + if (!cycle) + return NULL;
+ if (format <= VIR_STORAGE_FILE_NONE) + format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; + ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, + allow_probe, cycle); + virHashFree(cycle); return ret; }
@@ -957,6 +1015,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.

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 93590bf..a360c25 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14778,16 +14778,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 { @@ -14830,7 +14825,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, } } - if (virStorageFileGetMetadataFromFD(path, fd, format, meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { VIR_FORCE_CLOSE(fd); goto cleanup; } @@ -14864,6 +14859,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 f561455..7f240a0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9313,14 +9313,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 68c7605..76079bb 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -851,41 +851,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) { @@ -903,9 +905,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; } @@ -933,21 +939,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) VIR_WARN("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/17/2012 06:30 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 93590bf..a360c25 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14778,16 +14778,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 { @@ -14830,7 +14825,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, } }
- if (virStorageFileGetMetadataFromFD(path, fd, format, meta) < 0) { + if (!(meta = virStorageFileGetMetadataFromFD(path, fd, format))) { VIR_FORCE_CLOSE(fd); goto cleanup; } @@ -14864,6 +14859,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 f561455..7f240a0 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9313,14 +9313,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 68c7605..76079bb 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -851,41 +851,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) { @@ -903,9 +905,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; } @@ -933,21 +939,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) VIR_WARN("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);
Previous ACK stands.

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 76079bb..2ff444c 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> @@ -530,18 +531,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; } @@ -697,17 +702,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; @@ -1014,6 +1025,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/17/2012 06:30 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 index 76079bb..2ff444c 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> @@ -530,18 +531,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; }
@@ -697,17 +702,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; @@ -1014,6 +1025,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;
Previous ACK stands.

Hello Eric, On Thursday 18 October 2012 00:30:21 Eric Blake wrote:
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; + } }
this broke libvirt again when a storage pool contains storage volumes, which (currently) miss their backing file; see my previous commit 0ed445e7 for details. virsh vol-create-as --format qcow2 default back.qcow2 1G virsh vol-create-as --format qcow2 --backing-vol-format qcow2 --backing-vol back.qcow2 default next.qcow2 1G virsh vol-delete --pool default back.qcow2 pkill libvirtd libvirtd -l 2012-11-07 12:43:33.279+0000: 22175: info : libvirt version: 1.0.0 2012-11-07 12:43:33.279+0000: 22175: error : absolutePathFromBaseFile:542 : Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory 2012-11-07 12:43:33.280+0000: 22175: error : storageDriverAutostart:115 : Failed to autostart storage pool 'default': Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory (gdb) bt #0 absolutePathFromBaseFile (base_file=<value optimized out>, path=0x7f27a0031d00 "/var/lib/libvirt/images/base.qcow2") at util/storage_file.c:541 #1 0x00007f27b6769bdf in virStorageFileGetMetadataFromBuf (path=0x7f27a0009510 "/var/lib/libvirt/images/next.qcow2", fd=<value optimized out>, format=<value optimized out>) at util/storage_file.c:728 #2 virStorageFileGetMetadataFromFD (path=0x7f27a0009510 "/var/lib/libvirt/images/next.qcow2", fd=<value optimized out>, format=<value optimized out>) at util/storage_file.c:932 #3 0x00007f27acd07c94 in virStorageBackendProbeTarget (conn=<value optimized out>, pool=0x7f27a0012060) at storage/storage_backend_fs.c:94 #4 virStorageBackendFileSystemRefresh (conn=<value optimized out>, pool=0x7f27a0012060) at storage/storage_backend_fs.c:849 #5 0x00007f27accfd560 in storagePoolStart (obj=0x1c915a0, flags=<value optimized out>) at storage/storage_driver.c:700 There's another bug when doing a 'virsh vol-dumpxml' on such a broken image file, for which I'm currently working on a patch. That patch works fine with my older version of libvirt, but I noticed that with 1.0.0+git I can't no longer start that pool containing my broken test image. Sincerely Philipp -- Philipp Hahn Open Source Software Engineer hahn@univention.de Univention GmbH be open. fon: +49 421 22 232- 0 Mary-Somerville-Str.1 D-28359 Bremen fax: +49 421 22 232-99 http://www.univention.de/

82507838 refactured the code to keep both the raw and canonicalized form of the backingStore, which breaks badly when the storage pool contains a storage volume, which is missing its backing store file: # ./daemon/libvirtd -l 2012-11-07 12:43:33.279+0000: 22175: info : libvirt version: 1.0.0 2012-11-07 12:43:33.279+0000: 22175: error : absolutePathFromBaseFile:542 : Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory 2012-11-07 12:43:33.280+0000: 22175: error : storageDriverAutostart:115 : Failed to autostart storage pool 'default': Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory This is because virStorageFileGetMetadataFromBuf() aborts with -1 if the filename of the backingStore can not be canonicalized: #0 absolutePathFromBaseFile () at util/storage_file.c:541 #1 virStorageFileGetMetadataFromBuf () at util/storage_file.c:728 #2 virStorageFileGetMetadataFromFD () at util/storage_file.c:932 #3 virStorageBackendProbeTarget () at storage/storage_backend_fs.c:94 #4 virStorageBackendFileSystemRefresh () at storage/storage_backend_fs.c:849 #5 storagePoolStart () at storage/storage_driver.c:700 #6 virStoragePoolCreate () at libvirt.c:12471 ... Treat files which miss their backing file as standalone files. Signed-off-by: Philipp Hahn <hahn@univention.de> --- src/util/storage_file.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/util/storage_file.c b/src/util/storage_file.c index e9771d7..2249212 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -727,8 +727,9 @@ virStorageFileGetMetadataFromBuf(int format, meta->backingStoreRaw = meta->backingStore; meta->backingStore = absolutePathFromBaseFile(path, backing); if (meta->backingStore == NULL) { - VIR_FREE(backing); - return -1; + /* the backing file is (currently) unavailable, treat this + * file as standalone */ + backingFormat = VIR_STORAGE_FILE_NONE; } } VIR_FREE(backing); -- 1.7.1

On 11/07/2012 06:53 AM, Philipp Hahn wrote:
82507838 refactured the code to keep both the raw and canonicalized form
s/refactured/refactored/
of the backingStore, which breaks badly when the storage pool contains a storage volume, which is missing its backing store file: # ./daemon/libvirtd -l 2012-11-07 12:43:33.279+0000: 22175: info : libvirt version: 1.0.0 2012-11-07 12:43:33.279+0000: 22175: error : absolutePathFromBaseFile:542 : Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory 2012-11-07 12:43:33.280+0000: 22175: error : storageDriverAutostart:115 : Failed to autostart storage pool 'default': Can't canonicalize path '/var/lib/libvirt/images/base.qcow2': No such file or directory
This is because virStorageFileGetMetadataFromBuf() aborts with -1 if the filename of the backingStore can not be canonicalized: #0 absolutePathFromBaseFile () at util/storage_file.c:541 #1 virStorageFileGetMetadataFromBuf () at util/storage_file.c:728 #2 virStorageFileGetMetadataFromFD () at util/storage_file.c:932 #3 virStorageBackendProbeTarget () at storage/storage_backend_fs.c:94 #4 virStorageBackendFileSystemRefresh () at storage/storage_backend_fs.c:849 #5 storagePoolStart () at storage/storage_driver.c:700 #6 virStoragePoolCreate () at libvirt.c:12471 ...
Treat files which miss their backing file as standalone files.
Makes sense; I don't know of anything better that we can do. ACK and pushed.
Signed-off-by: Philipp Hahn <hahn@univention.de> --- src/util/storage_file.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/util/storage_file.c b/src/util/storage_file.c index e9771d7..2249212 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -727,8 +727,9 @@ virStorageFileGetMetadataFromBuf(int format, meta->backingStoreRaw = meta->backingStore; meta->backingStore = absolutePathFromBaseFile(path, backing); if (meta->backingStore == NULL) { - VIR_FREE(backing); - return -1; + /* the backing file is (currently) unavailable, treat this + * file as standalone */ + backingFormat = VIR_STORAGE_FILE_NONE; } } VIR_FREE(backing);
-- 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. --- v3: fix -Wshadow failure, improve comments src/libvirt_private.syms | 1 + src/util/storage_file.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/storage_file.h | 7 ++++++ 3 files changed, 72 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6155738..ccee23d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1130,6 +1130,7 @@ virStorageGenerateQcowPassphrase; # storage_file.h +virStorageFileChainLookup; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileFreeMetadata; diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 2ff444c..882df6e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1262,3 +1262,67 @@ const char *virStorageFileGetSCSIKey(const char *path) return NULL; } #endif + +/* Given a CHAIN that starts at the named file START, return a string + * pointing to either START or within CHAIN that gives the preferred + * name for the backing file NAME within that chain. Pass NULL for + * NAME to find the base of the chain. If META is not NULL, set *META + * 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 *PARENT + * to the preferred name of the parent (or to NULL if NAME matches + * START). Since the results point within CHAIN, they 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 *absName = absolutePathFromBaseFile(*parent, name); + if (absName && STREQ(absName, owner->backingStore)) { + VIR_FREE(absName); + break; + } + VIR_FREE(absName); + } + *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/17/2012 06:30 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. ---
v3: fix -Wshadow failure, improve comments
src/libvirt_private.syms | 1 + src/util/storage_file.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ src/util/storage_file.h | 7 ++++++ 3 files changed, 72 insertions(+)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6155738..ccee23d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1130,6 +1130,7 @@ virStorageGenerateQcowPassphrase;
# storage_file.h +virStorageFileChainLookup; virStorageFileFormatTypeFromString; virStorageFileFormatTypeToString; virStorageFileFreeMetadata; diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 2ff444c..882df6e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -1262,3 +1262,67 @@ const char *virStorageFileGetSCSIKey(const char *path) return NULL; } #endif + +/* Given a CHAIN that starts at the named file START, return a string + * pointing to either START or within CHAIN that gives the preferred + * name for the backing file NAME within that chain. Pass NULL for + * NAME to find the base of the chain. If META is not NULL, set *META + * 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 *PARENT + * to the preferred name of the parent (or to NULL if NAME matches + * START). Since the results point within CHAIN, they 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 *absName = absolutePathFromBaseFile(*parent, name); + if (absName && STREQ(absName, owner->backingStore)) { + VIR_FREE(absName); + break; + } + VIR_FREE(absName); + } + *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);
Previous ACK stands.

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. --- v3: drop useless processStop cleanup, s/chain/backingChain/, don't make force discard errors src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 2 ++ src/qemu/qemu_domain.c | 26 ++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 8 ++++++++ 6 files changed, 54 insertions(+) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a360c25..6487e6b 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->backingChain); 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 eec1f25..e7f0e9c 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 backingChain; char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b51edc2..45f3a5e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2010,3 +2010,29 @@ qemuDomainCleanupRun(struct qemud_driver *driver, priv->ncleanupCallbacks = 0; priv->ncleanupCallbacks_max = 0; } + +int +qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force) +{ + bool probe = driver->allowDiskFormatProbing; + + if (!disk->src) + return 0; + + if (disk->backingChain) { + if (force) { + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = NULL; + } else { + return 0; + } + } + disk->backingChain = virStorageFileGetMetadata(disk->src, disk->format, + driver->user, driver->group, + probe); + if (!disk->backingChain) + 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 7f240a0..dfbf04d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5816,6 +5816,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, @@ -6044,6 +6047,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) { @@ -10788,6 +10794,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->backingChain 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->backingChain); + disk->backingChain = 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 e08ec67..0e98c8b 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); -- 1.7.11.7

On 10/17/2012 06:30 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. ---
v3: drop useless processStop cleanup, s/chain/backingChain/, don't make force discard errors
src/conf/domain_conf.c | 1 + src/conf/domain_conf.h | 2 ++ src/qemu/qemu_domain.c | 26 ++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 3 +++ src/qemu/qemu_driver.c | 14 ++++++++++++++ src/qemu/qemu_process.c | 8 ++++++++ 6 files changed, 54 insertions(+)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a360c25..6487e6b 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->backingChain); 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 eec1f25..e7f0e9c 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 backingChain;
char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index b51edc2..45f3a5e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2010,3 +2010,29 @@ qemuDomainCleanupRun(struct qemud_driver *driver, priv->ncleanupCallbacks = 0; priv->ncleanupCallbacks_max = 0; } + +int +qemuDomainDetermineDiskChain(struct qemud_driver *driver, + virDomainDiskDefPtr disk, + bool force) +{ + bool probe = driver->allowDiskFormatProbing; + + if (!disk->src) + return 0; + + if (disk->backingChain) { + if (force) { + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = NULL; + } else { + return 0; + } + } + disk->backingChain = virStorageFileGetMetadata(disk->src, disk->format, + driver->user, driver->group, + probe); + if (!disk->backingChain) + 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 7f240a0..dfbf04d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5816,6 +5816,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, @@ -6044,6 +6047,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) { @@ -10788,6 +10794,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->backingChain 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->backingChain); + disk->backingChain = 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 e08ec67..0e98c8b 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);
ACK.

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. --- v3: rebase to earlier changes, fix comment 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 | 20 ++++---- 6 files changed, 32 insertions(+), 120 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6487e6b..afa4cfe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14767,110 +14767,42 @@ done: } +/* Call iter(disk, name, depth, opaque) for each element of disk and + * its backing chain in the pre-populated disk->backingChain. + * 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->backingChain; + 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 e7f0e9c..10ef841 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2141,9 +2141,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 6b94686..428befd 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 9dbf95d..a67f5d6 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 b5108ab..eee8d71 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1062,13 +1062,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; @@ -1078,16 +1075,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..263fc92 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,20 +919,22 @@ get_files(vahControl * ctl) } for (i = 0; i < ctl->def->ndisks; i++) { + virDomainDiskDefPtr disk = ctl->def->disks[i]; + + /* 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, disk->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); - if (ret != 0) + if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0) goto clean; } -- 1.7.11.7

On 10/17/2012 06:30 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.
* 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. ---
v3: rebase to earlier changes, fix comment
including accounting for the movement of check for VIR_STORAGE_FILE_AUTO infor virStorageFileGetMetaData()
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 | 20 ++++---- 6 files changed, 32 insertions(+), 120 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 6487e6b..afa4cfe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -14767,110 +14767,42 @@ done: }
+/* Call iter(disk, name, depth, opaque) for each element of disk and + * its backing chain in the pre-populated disk->backingChain. + * 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->backingChain; + 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 e7f0e9c..10ef841 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2141,9 +2141,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 6b94686..428befd 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 9dbf95d..a67f5d6 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 b5108ab..eee8d71 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1062,13 +1062,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;
@@ -1078,16 +1075,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..263fc92 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,20 +919,22 @@ get_files(vahControl * ctl) }
for (i = 0; i < ctl->def->ndisks; i++) { + virDomainDiskDefPtr disk = ctl->def->disks[i]; + + /* 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, disk->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); - if (ret != 0) + if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0) goto clean; }
ACK.

Minor cleanup made possible by previous simplifications. * src/qemu/qemu_cgroup.h (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup): Alter signature. * src/qemu/qemu_cgroup.c (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup, qemuSetupCgroup): Update all uses. * src/qemu/qemu_hotplug.c (qemuDomainDetachPciDiskDevice) (qemuDomainDetachDiskDevice): Likewise. * src/qemu/qemu_driver.c (qemuDomainAttachDeviceDiskLive) (qemuDomainChangeDiskMediaLive) (qemuDomainSnapshotCreateSingleDiskActive) (qemuDomainSnapshotUndoSingleDiskActive): Likewise. --- v3: new patch src/qemu/qemu_cgroup.c | 8 +++----- src/qemu/qemu_cgroup.h | 6 ++---- src/qemu/qemu_driver.c | 14 +++++++------- src/qemu/qemu_hotplug.c | 4 ++-- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 428befd..db371a0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,8 +87,7 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, } -int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -127,8 +126,7 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, } -int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -230,7 +228,7 @@ int qemuSetupCgroup(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks ; i++) { if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], false) < 0 || - qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + qemuSetupDiskCgroup(vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; } diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index 362080a..c552162 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,12 +36,10 @@ typedef struct _qemuCgroupData qemuCgroupData; bool qemuCgroupControllerActive(struct qemud_driver *driver, int controller); -int qemuSetupDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); -int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dfbf04d..d5c15ef 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5826,7 +5826,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } switch (disk->device) { @@ -5862,7 +5862,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, } if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -6058,7 +6058,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } @@ -6077,7 +6077,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, } if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -10806,14 +10806,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) goto cleanup; - if (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) { + if (cgroup && qemuSetupDiskCgroup(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) + if (cgroup && qemuTeardownDiskCgroup(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); @@ -10885,7 +10885,7 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, 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) + if (cgroup && qemuTeardownDiskCgroup(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); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca441f2..7381921 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2006,7 +2006,7 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } @@ -2089,7 +2089,7 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } -- 1.7.11.7

On 10/17/2012 06:30 PM, Eric Blake wrote:
Minor cleanup made possible by previous simplifications.
* src/qemu/qemu_cgroup.h (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup): Alter signature. * src/qemu/qemu_cgroup.c (qemuSetupDiskCgroup) (qemuTeardownDiskCgroup, qemuSetupCgroup): Update all uses. * src/qemu/qemu_hotplug.c (qemuDomainDetachPciDiskDevice) (qemuDomainDetachDiskDevice): Likewise. * src/qemu/qemu_driver.c (qemuDomainAttachDeviceDiskLive) (qemuDomainChangeDiskMediaLive) (qemuDomainSnapshotCreateSingleDiskActive) (qemuDomainSnapshotUndoSingleDiskActive): Likewise. ---
v3: new patch
src/qemu/qemu_cgroup.c | 8 +++----- src/qemu/qemu_cgroup.h | 6 ++---- src/qemu/qemu_driver.c | 14 +++++++------- src/qemu/qemu_hotplug.c | 4 ++-- 4 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 428befd..db371a0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,8 +87,7 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, }
-int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -127,8 +126,7 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, }
-int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -230,7 +228,7 @@ int qemuSetupCgroup(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks ; i++) { if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], false) < 0 || - qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + qemuSetupDiskCgroup(vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; }
diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index 362080a..c552162 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,12 +36,10 @@ typedef struct _qemuCgroupData qemuCgroupData;
bool qemuCgroupControllerActive(struct qemud_driver *driver, int controller); -int qemuSetupDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); -int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index dfbf04d..d5c15ef 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5826,7 +5826,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } switch (disk->device) { @@ -5862,7 +5862,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -6058,7 +6058,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; }
@@ -6077,7 +6077,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -10806,14 +10806,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, if (virDomainLockDiskAttach(driver->lockManager, driver->uri, vm, disk) < 0) goto cleanup; - if (cgroup && qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) { + if (cgroup && qemuSetupDiskCgroup(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) + if (cgroup && qemuTeardownDiskCgroup(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); @@ -10885,7 +10885,7 @@ qemuDomainSnapshotUndoSingleDiskActive(struct qemud_driver *driver, 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) + if (cgroup && qemuTeardownDiskCgroup(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); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca441f2..7381921 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2006,7 +2006,7 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } @@ -2089,7 +2089,7 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); }
ACK.

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. --- v3: use long long for JSON speed argument 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 4313451..2d9c44c 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2796,6 +2796,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 f33b94c..8856d9f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -524,6 +524,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 2daf8ea..d1ce9cc 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -805,6 +805,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: @@ -3278,6 +3280,36 @@ cleanup: return ret; } +/* speed is in bytes/sec */ +int +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long 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, @@ -3394,6 +3426,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..61127a7 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 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 0e98c8b..3a087e2 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/17/2012 06:30 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.
* 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. ---
v3: use long long for JSON speed argument
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 4313451..2d9c44c 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2796,6 +2796,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 f33b94c..8856d9f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -524,6 +524,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 2daf8ea..d1ce9cc 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -805,6 +805,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: @@ -3278,6 +3280,36 @@ cleanup: return ret; }
+/* speed is in bytes/sec */ +int +qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, + const char *top, const char *base, + unsigned long 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, @@ -3394,6 +3426,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..61127a7 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 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 0e98c8b..3a087e2 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.

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. --- v3: mention release 1.0.0 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 d5c15ef..c63d16b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12651,6 +12651,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, @@ -13993,6 +14067,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ -- 1.7.11.7

On 10/17/2012 06:30 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. ---
v3: mention release 1.0.0
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 d5c15ef..c63d16b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12651,6 +12651,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, @@ -13993,6 +14067,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */
ACK.

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. --- v3: rebase to changes earlier in series src/libvirt.c | 2 -- src/qemu/qemu_driver.c | 63 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index e3ddf27..6d65965 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -19381,8 +19381,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 c63d16b..73804fe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12664,8 +12664,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; @@ -12696,20 +12700,55 @@ 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->backingChain; + } else if (!(top_canon = virStorageFileChainLookup(disk->backingChain, + 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; + } + /* Note that this code exploits the fact that + * virStorageFileChainLookup guarantees a simple pointer + * comparison will work, rather than needing full-blown STREQ. */ + 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/17/2012 06:30 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. ---
v3: rebase to changes earlier in series
src/libvirt.c | 2 -- src/qemu/qemu_driver.c | 63 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 51 insertions(+), 14 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index e3ddf27..6d65965 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -19381,8 +19381,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 c63d16b..73804fe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12664,8 +12664,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; @@ -12696,20 +12700,55 @@ 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->backingChain; + } else if (!(top_canon = virStorageFileChainLookup(disk->backingChain, + 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; + } + /* Note that this code exploits the fact that + * virStorageFileChainLookup guarantees a simple pointer + * comparison will work, rather than needing full-blown STREQ. */ + 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:
Previous ACK stands.

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. --- v3: rebase to earlier patches in series 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 73804fe..072ec17 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10453,6 +10453,66 @@ 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, and + * can revoke access to a file no longer needed in 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->backingChain; + bool origreadonly = disk->readonly; + int ret = -1; + + disk->src = file; + disk->format = VIR_STORAGE_FILE_RAW; + disk->backingChain = 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(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(vm, cgroup, disk) < 0) || + virSecurityManagerSetImageLabel(driver->securityManager, + vm->def, disk) < 0) { + goto cleanup; + } + + ret = 0; + +cleanup: + disk->src = origsrc; + disk->format = origformat; + disk->backingChain = origchain; + disk->readonly = origreadonly; + return ret; +} + + static bool qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) { @@ -10762,8 +10822,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) { @@ -10790,10 +10848,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->backingChain if * successful, so we nuke the existing chain so that future * commands will recompute it. Better would be storing the chain @@ -10803,27 +10857,13 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, virStorageFileFreeMetadata(disk->backingChain); disk->backingChain = NULL; - if (virDomainLockDiskAttach(driver->lockManager, driver->uri, - vm, disk) < 0) - goto cleanup; - if (cgroup && qemuSetupDiskCgroup(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(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); @@ -10847,10 +10887,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); @@ -10882,13 +10918,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(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/17/2012 06:30 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. ---
v3: rebase to earlier patches in series
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 73804fe..072ec17 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10453,6 +10453,66 @@ 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, and + * can revoke access to a file no longer needed in 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->backingChain; + bool origreadonly = disk->readonly; + int ret = -1; + + disk->src = file; + disk->format = VIR_STORAGE_FILE_RAW; + disk->backingChain = 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(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(vm, cgroup, disk) < 0) || + virSecurityManagerSetImageLabel(driver->securityManager, + vm->def, disk) < 0) { + goto cleanup; + } + + ret = 0; + +cleanup: + disk->src = origsrc; + disk->format = origformat; + disk->backingChain = origchain; + disk->readonly = origreadonly; + return ret; +} + + static bool qemuDomainSnapshotIsAllowed(virDomainObjPtr vm) { @@ -10762,8 +10822,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) { @@ -10790,10 +10848,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->backingChain if * successful, so we nuke the existing chain so that future * commands will recompute it. Better would be storing the chain @@ -10803,27 +10857,13 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, virStorageFileFreeMetadata(disk->backingChain); disk->backingChain = NULL;
- if (virDomainLockDiskAttach(driver->lockManager, driver->uri, - vm, disk) < 0) - goto cleanup; - if (cgroup && qemuSetupDiskCgroup(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(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); @@ -10847,10 +10887,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); @@ -10882,13 +10918,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(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);
ACK, with qualifying remarks from the review of the previous version of the patch (PATCHv2 17/16)

On 10/18/2012 12:50 PM, Laine Stump wrote:
On 10/17/2012 06:30 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.
ACK, with qualifying remarks from the review of the previous version of the patch (PATCHv2 17/16)
I'm adding this to the commit message: It might be nice to clean things up in a future patch by adding new functions to the lock manager, cgroup manager, and security manager that takes a single file name and applies context of a disk to that file, rather than the current semantics of applying context to the entire chain already associated to a disk. That way, we could avoid the games this patch plays of temporarily swapping out the disk->src and related fields of the disk. But that would involve more code changes, so this patch really is the smallest hack for doing the necessary work; besides, this patch is more or less code motion (the hack was already employed by the snapshot creation code, we are just making it reusable). -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

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. Furthermore, from a security standpoint, once you have granted access, you must assume any damage that can be done will be done; later revoking access is nice to minimize the window of damage, but less important as it does not affect the fact that damage can be done in the first place. Therefore, saving the revoke efforts until we have better XML tracking of what chain operations are in effect, including across a libvirtd restart, is reasonable. * src/qemu/qemu_driver.c (qemuDomainBlockCommit): Label disks as needed. (qemuDomainPrepareDiskChainElement): Cast away const. --- v3: enhance commit message 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 072ec17..3829a89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10469,7 +10469,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 @@ -10481,7 +10481,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->backingChain = NULL; disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; @@ -12699,6 +12699,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); @@ -12774,15 +12775,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

On 10/17/2012 06:30 PM, Eric Blake wrote:
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. Furthermore, from a security standpoint, once you have granted access, you must assume any damage that can be done will be done; later revoking access is nice to minimize the window of damage, but less important as it does not affect the fact that damage can be done in the first place. Therefore, saving the revoke efforts until we have better XML tracking of what chain operations are in effect, including across a libvirtd restart, is reasonable.
Umm. I don't know that I agree with your statement about the relative importance of undoing all the labelling when you're done, but if the alternative to this is to require turning off selinux, then it's definitely a win.
* src/qemu/qemu_driver.c (qemuDomainBlockCommit): Label disks as needed. (qemuDomainPrepareDiskChainElement): Cast away const. ---
v3: enhance commit message 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 072ec17..3829a89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10469,7 +10469,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 @@ -10481,7 +10481,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 */
... since you're misusing the diskdef anyway :-)
disk->format = VIR_STORAGE_FILE_RAW; disk->backingChain = NULL; disk->readonly = mode == VIR_DISK_CHAIN_READ_ONLY; @@ -12699,6 +12699,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);
@@ -12774,15 +12775,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)) {
It looks to me like everything in cgroup.c returns < 0 ( -errno in most, if not all cases). Any idea why all its callers are testing for != 0 instead of the more standard < 0 ? (you're just following the convention, so no complaints on your patch, but it looks a bit odd.)
+ 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);
Is it always the case that it was previously read-only, or is this just a naive assumption?
+ } + if (cgroup) + virCgroupFree(&cgroup); if (qemuDomainObjEndJob(driver, vm) == 0) { vm = NULL; goto cleanup;
ACK.

On 10/18/2012 02:27 PM, Laine Stump wrote:
On 10/17/2012 06:30 PM, Eric Blake wrote:
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. Furthermore, from a security standpoint, once you have granted access, you must assume any damage that can be done will be done; later revoking access is nice to minimize the window of damage, but less important as it does not affect the fact that damage can be done in the first place. Therefore, saving the revoke efforts until
I'm going to edit this: s/saving/deferring/
we have better XML tracking of what chain operations are in effect, including across a libvirtd restart, is reasonable.
Umm. I don't know that I agree with your statement about the relative importance of undoing all the labelling when you're done, but if the alternative to this is to require turning off selinux, then it's definitely a win.
Any suggestions on wording I could use instead? Yeah, I'm a bit miffed that I don't have code working to revoke access in all situations, but I do revoke access in the trivial situations (that is, the non-blocking situations are trivial, but the real work will be tracking what rights need to be revoked after an asynchronous operation completes). And block-pull and block-copy suffer from the same problem, but at least once I have the infrastructure in place, then all three code paths will be able to use it.
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES) && + virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)) {
It looks to me like everything in cgroup.c returns < 0 ( -errno in most, if not all cases). Any idea why all its callers are testing for != 0 instead of the more standard < 0 ?
(you're just following the convention, so no complaints on your patch, but it looks a bit odd.)
Eww - I didn't even notice that. It does look like it's worth a followup patch to check for < 0 when considering errors. I'll fix this one in-place, since you noticed, but that's minor, so I won't bother with a v4.
+ 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);
Is it always the case that it was previously read-only, or is this just a naive assumption?
Guaranteed. Remember, the high-level concept is that we are going from: base <- top <- parent <- active to base <- parent <- active Since qemu is already running, we know that 'active' is open read/write, and that 'base', 'top', and 'parent' are open read-only as backing files, prior to starting of the operation. Qemu then has to write to 'base' (to commit the data from 'top'), and to 'parent' (to update its backing file once 'top' is no longer in the chain), hence our grant of read/write to those two files. If the operation fails, then revoking immediately to read-only gets us right back where we started. It also explains why I'm careful to check whether top_parent == disk->src (in that case, the parent is already read-write, and must not be revoked to read-only). On the other hand, if the operation is successful (that is, at the point we receive the VIR_DOMAIN_BLOCK_JOB_COMPLETE event in qemu_process.c), it would be nice for us to then revoke 'base' and 'parent' to read-only and revoke 'top' to no access since it is no longer part of the chain (and that's the patch that I'm deferring, because it requires tracking _which_ files need tightened labels even across libvirtd restarts, and even if the qemu event is missed because it happened during the libvirtd restart). In fact, if the operation starts successfully, but is later canceled mid-stream, we also have access rights to revoke (here, the VIR_DOMAIN_BLOCK_JOB_CANCELLED event in qemu_process.c), it's just that fewer files would be revoked (that is, 'top' is still in the chain as read-only if the job is cancelled).
+ } + if (cgroup) + virCgroupFree(&cgroup); if (qemuDomainObjEndJob(driver, vm) == 0) { vm = NULL; goto cleanup;
ACK.
Given the state of your reviews, I'll make those minor tweaks and push shortly. Thanks again for going through all of these patches. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 10/17/2012 06:30 PM, Eric Blake wrote:
This v3 posting resolves all the comments I had from Doug, Laine, and myself, and has passed my testing with SELinux enabled.
v2 was here: https://www.redhat.com/archives/libvir-list/2012-October/msg00633.html See below for interdiff, and individual patches for more notes about changes
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 (19): 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: remove unused parameters after previous patch blockjob: manage qemu block-commit monitor command blockjob: wire up online qemu block-commit blockjob: implement shallow commit flag in qemu blockjob: refactor qemu disk chain permission grants blockjob: properly label disks for qemu block-commit
docs/schemas/domaincommon.rng | 27 +- docs/schemas/domainsnapshot.rng | 2 +- src/conf/capabilities.h | 4 +- src/conf/domain_conf.c | 165 +++------ 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 | 14 +- src/qemu/qemu_cgroup.h | 6 +- src/qemu/qemu_command.c | 18 +- src/qemu/qemu_domain.c | 33 +- src/qemu/qemu_domain.h | 3 + src/qemu/qemu_driver.c | 371 ++++++++++++++------- src/qemu/qemu_hotplug.c | 13 +- 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 | 11 + src/security/security_dac.c | 7 - src/security/security_selinux.c | 11 - src/security/virt-aa-helper.c | 20 +- src/storage/storage_backend_fs.c | 15 +- src/util/storage_file.c | 282 ++++++++++++---- 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 +- 39 files changed, 859 insertions(+), 435 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 81cb3aa..afa4cfe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,7 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); - virStorageFileFreeMetadata(def->chain); + virStorageFileFreeMetadata(def->backingChain); VIR_FREE(def->mirror); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); @@ -3723,8 +3723,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 Xen 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"); @@ -4185,7 +4189,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, driverType); goto error; } - } else { + } else if (def->type == VIR_DOMAIN_DISK_TYPE_FILE || + def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) { def->format = caps->defaultDiskDriverType; }
@@ -14763,10 +14768,10 @@ 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. */ + * its backing chain in the pre-populated disk->backingChain. + * 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 ignoreOpenFailure, virDomainDiskDefPathIterator iter, @@ -14782,7 +14787,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (iter(disk, disk->src, 0, opaque) < 0) goto cleanup;
- tmp = disk->chain; + tmp = disk->backingChain; while (tmp && tmp->backingStoreIsFile) { if (!ignoreOpenFailure && !tmp->backingMeta) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9c3abec..10ef841 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -569,7 +569,7 @@ struct _virDomainDiskDef { } auth; char *driverName; int format; /* enum virStorageFileFormat */ - virStorageFileMetadataPtr chain; + virStorageFileMetadataPtr backingChain;
char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 428befd..db371a0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,8 +87,7 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, }
-int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -127,8 +126,7 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, }
-int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -230,7 +228,7 @@ int qemuSetupCgroup(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks ; i++) { if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], false) < 0 || - qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + qemuSetupDiskCgroup(vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; }
diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index 362080a..c552162 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,12 +36,10 @@ typedef struct _qemuCgroupData qemuCgroupData;
bool qemuCgroupControllerActive(struct qemud_driver *driver, int controller); -int qemuSetupDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); -int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f071769..4196caf 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2129,7 +2129,7 @@ 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->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); @@ -5210,7 +5210,7 @@ qemuBuildCommandLine(virConnectPtr conn,
if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9675454..45f3a5e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2016,30 +2016,23 @@ qemuDomainDetermineDiskChain(struct qemud_driver *driver, virDomainDiskDefPtr disk, bool force) { - int format; + bool probe = driver->allowDiskFormatProbing;
if (!disk->src) return 0;
- if (disk->chain) { + if (disk->backingChain) { if (force) { - virStorageFileFreeMetadata(disk->chain); - disk->chain = NULL; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = 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) + disk->backingChain = virStorageFileGetMetadata(disk->src, disk->format, + driver->user, driver->group, + probe); + if (!disk->backingChain) return -1; return 0; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7a47cf7..3829a89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5826,7 +5826,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } switch (disk->device) { @@ -5862,7 +5862,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -6058,7 +6058,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; }
@@ -6077,7 +6077,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -10462,7 +10462,8 @@ typedef enum { /* 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. */ + * are all aware of each new file before it is added to a chain, and + * can revoke access to a file no longer needed in a chain. */ static int qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, virDomainObjPtr vm, @@ -10476,26 +10477,26 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, * temporarily modify the disk in place. */ char *origsrc = disk->src; int origformat = disk->format; - virStorageFileMetadataPtr origchain = disk->chain; + virStorageFileMetadataPtr origchain = disk->backingChain; bool origreadonly = disk->readonly; int ret = -1;
disk->src = (char *) file; /* casting away const is safe here */ disk->format = VIR_STORAGE_FILE_RAW; - disk->chain = NULL; + disk->backingChain = 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) + if (cgroup && qemuTeardownDiskCgroup(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) || + (cgroup && qemuSetupDiskCgroup(vm, cgroup, disk) < 0) || virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { goto cleanup; @@ -10506,7 +10507,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, cleanup: disk->src = origsrc; disk->format = origformat; - disk->chain = origchain; + disk->backingChain = origchain; disk->readonly = origreadonly; return ret; } @@ -10802,7 +10803,6 @@ cleanup: return ret; }
- /* The domain is expected to hold monitor lock. */ static int qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, @@ -10848,14 +10848,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FORCE_CLOSE(fd); }
- /* XXX Here, we know we are about to alter disk->chain if + /* XXX Here, we know we are about to alter disk->backingChain 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; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = NULL;
if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, VIR_DISK_CHAIN_READ_WRITE) < 0) { @@ -12737,8 +12737,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base,
if (!top) { top_canon = disk->src; - top_meta = disk->chain; - } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top_meta = disk->backingChain; + } else if (!(top_canon = virStorageFileChainLookup(disk->backingChain, + disk->src, top, &top_meta, &top_parent))) { virReportError(VIR_ERR_INVALID_ARG, @@ -12762,6 +12763,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, base ? base : "(default)", top_canon, path); goto endjob; } + /* Note that this code exploits the fact that + * virStorageFileChainLookup guarantees a simple pointer + * comparison will work, rather than needing full-blown STREQ. */ if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && base_canon != top_meta->backingStore) { virReportError(VIR_ERR_INVALID_ARG, @@ -14165,7 +14169,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ - .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca441f2..7381921 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2006,7 +2006,7 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } @@ -2089,7 +2089,7 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d0ecd1e..d1ce9cc 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3284,7 +3284,7 @@ cleanup: int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long speed) + unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 71bc6aa..61127a7 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -239,7 +239,7 @@ int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long bandwidth) + unsigned long long bandwidth) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1eb93a6..3a087e2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4121,18 +4121,6 @@ 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)) { diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 729c0d1..263fc92 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,21 +919,13 @@ 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, + disk->chain = virStorageFileGetMetadata(disk->src, disk->format, + -1, -1, ctl->allowDiskFormatProbing, NULL);
@@ -942,8 +934,7 @@ get_files(vahControl * ctl) * be passing ignoreOpenFailure = false and handle open errors more * careful than just ignoring them. */ - ret = virDomainDiskDefForeachPath(disk, true, add_file_path, &buf); - if (ret != 0) + if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0) goto clean; }
diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 218891e..882df6e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -271,7 +271,8 @@ qcow2GetBackingStoreFormat(int *format, break; *format = virStorageFileFormatTypeFromString( ((const char *)buf)+offset); - break; + if (*format <= VIR_STORAGE_FILE_NONE) + return -1; }
offset += len; @@ -353,12 +354,10 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) { + if (isQCow2 && format && qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, - offset); - if (*format <= VIR_STORAGE_FILE_NONE) - return BACKING_STORE_INVALID; - } + offset) < 0) + return BACKING_STORE_INVALID;
return BACKING_STORE_OK; } @@ -517,7 +516,7 @@ qedGetBackingStore(char **res, (*res)[size] = '\0';
if (flags & QED_F_BACKING_FORMAT_NO_PROBE) - *format = virStorageFileFormatTypeFromString("raw"); + *format = VIR_STORAGE_FILE_RAW; else *format = VIR_STORAGE_FILE_AUTO_SAFE;
@@ -954,7 +953,7 @@ virStorageFileGetMetadataRecurse(const char *path, int format, ret = virStorageFileGetMetadataFromFD(path, fd, format);
if (VIR_CLOSE(fd) < 0) - virReportSystemError(errno, _("could not close file %s"), path); + VIR_WARN("could not close file %s", path);
if (ret && ret->backingStoreIsFile) { if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) @@ -1004,6 +1003,9 @@ virStorageFileGetMetadata(const char *path, int format,
if (!cycle) return NULL; + + if (format <= VIR_STORAGE_FILE_NONE) + format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, allow_probe, cycle); virHashFree(cycle); @@ -1261,13 +1263,14 @@ const char *virStorageFileGetSCSIKey(const char *path) } #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 +/* Given a CHAIN that starts at the named file START, return a string + * pointing to either START or within CHAIN that gives the preferred + * name for the backing file NAME within that chain. Pass NULL for + * NAME to find the base of the chain. If META is not NULL, set *META * 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 + * backing element is not a file). If PARENT is not NULL, set *PARENT + * to the preferred name of the parent (or to NULL if NAME matches + * START). Since the results point within CHAIN, they must not be * independently freed. */ const char * virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, @@ -1301,12 +1304,12 @@ virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, STREQ(name, owner->backingStore)) { break; } else if (owner->backingStoreIsFile) { - char *abs = absolutePathFromBaseFile(*parent, name); - if (abs && STREQ(abs, owner->backingStore)) { - VIR_FREE(abs); + char *absName = absolutePathFromBaseFile(*parent, name); + if (absName && STREQ(absName, owner->backingStore)) { + VIR_FREE(absName); break; } - VIR_FREE(abs); + VIR_FREE(absName); } *parent = owner->backingStore; owner = owner->backingMeta;
I *think* I recognized everything in the interdiff from my comments, or else they made sense. So ACK to the interdiffs, and I guess if Doug has already ACKed the v2 of PATCH 01-07, then this should extend those ACKs up to v3 (does that make sense? At any rate, you have my explicit ACKs on path 08-19, and on the v2-v3 differences to patch 01-08.)

On Thu, Oct 18, 2012 at 3:48 PM, Laine Stump <laine@laine.org> wrote:
On 10/17/2012 06:30 PM, Eric Blake wrote:
This v3 posting resolves all the comments I had from Doug, Laine, and myself, and has passed my testing with SELinux enabled.
v2 was here: https://www.redhat.com/archives/libvir-list/2012-October/msg00633.html See below for interdiff, and individual patches for more notes about changes
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 (19): 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: remove unused parameters after previous patch blockjob: manage qemu block-commit monitor command blockjob: wire up online qemu block-commit blockjob: implement shallow commit flag in qemu blockjob: refactor qemu disk chain permission grants blockjob: properly label disks for qemu block-commit
docs/schemas/domaincommon.rng | 27 +- docs/schemas/domainsnapshot.rng | 2 +- src/conf/capabilities.h | 4 +- src/conf/domain_conf.c | 165 +++------ 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 | 14 +- src/qemu/qemu_cgroup.h | 6 +- src/qemu/qemu_command.c | 18 +- src/qemu/qemu_domain.c | 33 +- src/qemu/qemu_domain.h | 3 + src/qemu/qemu_driver.c | 371 ++++++++++++++------- src/qemu/qemu_hotplug.c | 13 +- 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 | 11 + src/security/security_dac.c | 7 - src/security/security_selinux.c | 11 - src/security/virt-aa-helper.c | 20 +- src/storage/storage_backend_fs.c | 15 +- src/util/storage_file.c | 282 ++++++++++++---- 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 +- 39 files changed, 859 insertions(+), 435 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 81cb3aa..afa4cfe 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -971,7 +971,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->src); VIR_FREE(def->dst); VIR_FREE(def->driverName); - virStorageFileFreeMetadata(def->chain); + virStorageFileFreeMetadata(def->backingChain); VIR_FREE(def->mirror); VIR_FREE(def->auth.username); VIR_FREE(def->wwn); @@ -3723,8 +3723,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 Xen 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"); @@ -4185,7 +4189,8 @@ virDomainDiskDefParseXML(virCapsPtr caps, driverType); goto error; } - } else { + } else if (def->type == VIR_DOMAIN_DISK_TYPE_FILE || + def->type == VIR_DOMAIN_DISK_TYPE_BLOCK) { def->format = caps->defaultDiskDriverType; }
@@ -14763,10 +14768,10 @@ 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. */ + * its backing chain in the pre-populated disk->backingChain. + * 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 ignoreOpenFailure, virDomainDiskDefPathIterator iter, @@ -14782,7 +14787,7 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk, if (iter(disk, disk->src, 0, opaque) < 0) goto cleanup;
- tmp = disk->chain; + tmp = disk->backingChain; while (tmp && tmp->backingStoreIsFile) { if (!ignoreOpenFailure && !tmp->backingMeta) { virReportError(VIR_ERR_INTERNAL_ERROR, diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9c3abec..10ef841 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -569,7 +569,7 @@ struct _virDomainDiskDef { } auth; char *driverName; int format; /* enum virStorageFileFormat */ - virStorageFileMetadataPtr chain; + virStorageFileMetadataPtr backingChain;
char *mirror; int mirrorFormat; /* enum virStorageFileFormat */ diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 428befd..db371a0 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -87,8 +87,7 @@ qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, }
-int qemuSetupDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -127,8 +126,7 @@ qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, }
-int qemuTeardownDiskCgroup(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk) { @@ -230,7 +228,7 @@ int qemuSetupCgroup(struct qemud_driver *driver, for (i = 0; i < vm->def->ndisks ; i++) { if (qemuDomainDetermineDiskChain(driver, vm->def->disks[i], false) < 0 || - qemuSetupDiskCgroup(driver, vm, cgroup, vm->def->disks[i]) < 0) + qemuSetupDiskCgroup(vm, cgroup, vm->def->disks[i]) < 0) goto cleanup; }
diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h index 362080a..c552162 100644 --- a/src/qemu/qemu_cgroup.h +++ b/src/qemu/qemu_cgroup.h @@ -36,12 +36,10 @@ typedef struct _qemuCgroupData qemuCgroupData;
bool qemuCgroupControllerActive(struct qemud_driver *driver, int controller); -int qemuSetupDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuSetupDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); -int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, +int qemuTeardownDiskCgroup(virDomainObjPtr vm, virCgroupPtr cgroup, virDomainDiskDefPtr disk); int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f071769..4196caf 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2129,7 +2129,7 @@ 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->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); @@ -5210,7 +5210,7 @@ qemuBuildCommandLine(virConnectPtr conn,
if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { /* QEMU only supports magic FAT format for now */ - if (disk->format && disk->format != VIR_STORAGE_FILE_FAT) { + if (disk->format > 0 && disk->format != VIR_STORAGE_FILE_FAT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported disk driver type for '%s'"), virStorageFileFormatTypeToString(disk->format)); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 9675454..45f3a5e 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2016,30 +2016,23 @@ qemuDomainDetermineDiskChain(struct qemud_driver *driver, virDomainDiskDefPtr disk, bool force) { - int format; + bool probe = driver->allowDiskFormatProbing;
if (!disk->src) return 0;
- if (disk->chain) { + if (disk->backingChain) { if (force) { - virStorageFileFreeMetadata(disk->chain); - disk->chain = NULL; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = 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) + disk->backingChain = virStorageFileGetMetadata(disk->src, disk->format, + driver->user, driver->group, + probe); + if (!disk->backingChain) return -1; return 0; } diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7a47cf7..3829a89 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -5826,7 +5826,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; } switch (disk->device) { @@ -5862,7 +5862,7 @@ qemuDomainAttachDeviceDiskLive(virConnectPtr conn, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -6058,7 +6058,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, vm->def->name); goto end; } - if (qemuSetupDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuSetupDiskCgroup(vm, cgroup, disk) < 0) goto end; }
@@ -6077,7 +6077,7 @@ qemuDomainChangeDiskMediaLive(virDomainObjPtr vm, }
if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(disk->src)); } @@ -10462,7 +10462,8 @@ typedef enum { /* 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. */ + * are all aware of each new file before it is added to a chain, and + * can revoke access to a file no longer needed in a chain. */ static int qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, virDomainObjPtr vm, @@ -10476,26 +10477,26 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, * temporarily modify the disk in place. */ char *origsrc = disk->src; int origformat = disk->format; - virStorageFileMetadataPtr origchain = disk->chain; + virStorageFileMetadataPtr origchain = disk->backingChain; bool origreadonly = disk->readonly; int ret = -1;
disk->src = (char *) file; /* casting away const is safe here */ disk->format = VIR_STORAGE_FILE_RAW; - disk->chain = NULL; + disk->backingChain = 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) + if (cgroup && qemuTeardownDiskCgroup(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) || + (cgroup && qemuSetupDiskCgroup(vm, cgroup, disk) < 0) || virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { goto cleanup; @@ -10506,7 +10507,7 @@ qemuDomainPrepareDiskChainElement(struct qemud_driver *driver, cleanup: disk->src = origsrc; disk->format = origformat; - disk->chain = origchain; + disk->backingChain = origchain; disk->readonly = origreadonly; return ret; } @@ -10802,7 +10803,6 @@ cleanup: return ret; }
- /* The domain is expected to hold monitor lock. */ static int qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, @@ -10848,14 +10848,14 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, VIR_FORCE_CLOSE(fd); }
- /* XXX Here, we know we are about to alter disk->chain if + /* XXX Here, we know we are about to alter disk->backingChain 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; + virStorageFileFreeMetadata(disk->backingChain); + disk->backingChain = NULL;
if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, source, VIR_DISK_CHAIN_READ_WRITE) < 0) { @@ -12737,8 +12737,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base,
if (!top) { top_canon = disk->src; - top_meta = disk->chain; - } else if (!(top_canon = virStorageFileChainLookup(disk->chain, disk->src, + top_meta = disk->backingChain; + } else if (!(top_canon = virStorageFileChainLookup(disk->backingChain, + disk->src, top, &top_meta, &top_parent))) { virReportError(VIR_ERR_INVALID_ARG, @@ -12762,6 +12763,9 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, base ? base : "(default)", top_canon, path); goto endjob; } + /* Note that this code exploits the fact that + * virStorageFileChainLookup guarantees a simple pointer + * comparison will work, rather than needing full-blown STREQ. */ if ((flags & VIR_DOMAIN_BLOCK_COMMIT_SHALLOW) && base_canon != top_meta->backingStore) { virReportError(VIR_ERR_INVALID_ARG, @@ -14165,7 +14169,7 @@ static virDriver qemuDriver = { .domainBlockJobSetSpeed = qemuDomainBlockJobSetSpeed, /* 0.9.4 */ .domainBlockPull = qemuDomainBlockPull, /* 0.9.4 */ .domainBlockRebase = qemuDomainBlockRebase, /* 0.9.10 */ - .domainBlockCommit = qemuDomainBlockCommit, /* 0.10.3 */ + .domainBlockCommit = qemuDomainBlockCommit, /* 1.0.0 */ .isAlive = qemuIsAlive, /* 0.9.8 */ .nodeSuspendForDuration = nodeSuspendForDuration, /* 0.9.8 */ .domainSetBlockIoTune = qemuDomainSetBlockIoTune, /* 0.9.8 */ diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index ca441f2..7381921 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -2006,7 +2006,7 @@ int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } @@ -2089,7 +2089,7 @@ int qemuDomainDetachDiskDevice(struct qemud_driver *driver, VIR_WARN("Unable to restore security label on %s", dev->data.disk->src);
if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) + if (qemuTeardownDiskCgroup(vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d0ecd1e..d1ce9cc 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3284,7 +3284,7 @@ cleanup: int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long speed) + unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 71bc6aa..61127a7 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -239,7 +239,7 @@ int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *top, const char *base, - unsigned long bandwidth) + unsigned long long bandwidth) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1eb93a6..3a087e2 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4121,18 +4121,6 @@ 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)) { diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 729c0d1..263fc92 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -919,21 +919,13 @@ 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, + disk->chain = virStorageFileGetMetadata(disk->src, disk->format, + -1, -1, ctl->allowDiskFormatProbing, NULL);
@@ -942,8 +934,7 @@ get_files(vahControl * ctl) * be passing ignoreOpenFailure = false and handle open errors more * careful than just ignoring them. */ - ret = virDomainDiskDefForeachPath(disk, true, add_file_path, &buf); - if (ret != 0) + if (virDomainDiskDefForeachPath(disk, true, add_file_path, &buf) < 0) goto clean; }
diff --git a/src/util/storage_file.c b/src/util/storage_file.c index 218891e..882df6e 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -271,7 +271,8 @@ qcow2GetBackingStoreFormat(int *format, break; *format = virStorageFileFormatTypeFromString( ((const char *)buf)+offset); - break; + if (*format <= VIR_STORAGE_FILE_NONE) + return -1; }
offset += len; @@ -353,12 +354,10 @@ qcowXGetBackingStore(char **res, * between the end of the header (QCOW2_HDR_TOTAL_SIZE) * and the start of the backingStoreName (offset) */ - if (isQCow2 && format) { + if (isQCow2 && format && qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE, - offset); - if (*format <= VIR_STORAGE_FILE_NONE) - return BACKING_STORE_INVALID; - } + offset) < 0) + return BACKING_STORE_INVALID;
return BACKING_STORE_OK; } @@ -517,7 +516,7 @@ qedGetBackingStore(char **res, (*res)[size] = '\0';
if (flags & QED_F_BACKING_FORMAT_NO_PROBE) - *format = virStorageFileFormatTypeFromString("raw"); + *format = VIR_STORAGE_FILE_RAW; else *format = VIR_STORAGE_FILE_AUTO_SAFE;
@@ -954,7 +953,7 @@ virStorageFileGetMetadataRecurse(const char *path, int format, ret = virStorageFileGetMetadataFromFD(path, fd, format);
if (VIR_CLOSE(fd) < 0) - virReportSystemError(errno, _("could not close file %s"), path); + VIR_WARN("could not close file %s", path);
if (ret && ret->backingStoreIsFile) { if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe) @@ -1004,6 +1003,9 @@ virStorageFileGetMetadata(const char *path, int format,
if (!cycle) return NULL; + + if (format <= VIR_STORAGE_FILE_NONE) + format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW; ret = virStorageFileGetMetadataRecurse(path, format, uid, gid, allow_probe, cycle); virHashFree(cycle); @@ -1261,13 +1263,14 @@ const char *virStorageFileGetSCSIKey(const char *path) } #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 +/* Given a CHAIN that starts at the named file START, return a string + * pointing to either START or within CHAIN that gives the preferred + * name for the backing file NAME within that chain. Pass NULL for + * NAME to find the base of the chain. If META is not NULL, set *META * 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 + * backing element is not a file). If PARENT is not NULL, set *PARENT + * to the preferred name of the parent (or to NULL if NAME matches + * START). Since the results point within CHAIN, they must not be * independently freed. */ const char * virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, @@ -1301,12 +1304,12 @@ virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start, STREQ(name, owner->backingStore)) { break; } else if (owner->backingStoreIsFile) { - char *abs = absolutePathFromBaseFile(*parent, name); - if (abs && STREQ(abs, owner->backingStore)) { - VIR_FREE(abs); + char *absName = absolutePathFromBaseFile(*parent, name); + if (absName && STREQ(absName, owner->backingStore)) { + VIR_FREE(absName); break; } - VIR_FREE(abs); + VIR_FREE(absName); } *parent = owner->backingStore; owner = owner->backingMeta;
I *think* I recognized everything in the interdiff from my comments, or else they made sense. So ACK to the interdiffs, and I guess if Doug has already ACKed the v2 of PATCH 01-07, then this should extend those ACKs up to v3 (does that make sense? At any rate, you have my explicit ACKs on path 08-19, and on the v2-v3 differences to patch 01-08.)
I read through the diff and I concur to extend my ACKs. -- Doug Goldstein

On 10/17/2012 04:30 PM, Eric Blake wrote:
This v3 posting resolves all the comments I had from Doug, Laine, and myself, and has passed my testing with SELinux enabled.
v2 was here: https://www.redhat.com/archives/libvir-list/2012-October/msg00633.html See below for interdiff, and individual patches for more notes about changes
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
Series (finally) pushed, with the tweaks in patch 19 made as requested. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

Gcc with optimization warns: ../../src/qemu/qemu_driver.c: In function 'qemuDomainBlockCommit': ../../src/qemu/qemu_driver.c:12813:46: error: 'disk' may be used uninitialized in this function [-Werror=maybe-uninitialized] ../../src/qemu/qemu_driver.c:12698:25: note: 'disk' was declared here cc1: all warnings being treated as errors so obviously I had only been testing with optimization off. * src/qemu/qemu_driver.c (qemuDomainBlockCommit): Guard cleanup. --- Pushing under the build-breaker rule. src/qemu/qemu_driver.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 730c82b..feda4d9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12695,12 +12695,13 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, char *device = NULL; int ret = -1; int idx; - virDomainDiskDefPtr disk; + virDomainDiskDefPtr disk = NULL; const char *top_canon = NULL; virStorageFileMetadataPtr top_meta = NULL; const char *top_parent = NULL; const char *base_canon = NULL; virCgroupPtr cgroup = NULL; + bool clean_access = false; virCheckFlags(VIR_DOMAIN_BLOCK_COMMIT_SHALLOW, -1); @@ -12790,6 +12791,7 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, vm->def->name); goto endjob; } + clean_access = true; if (qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon, VIR_DISK_CHAIN_READ_WRITE) < 0 || (top_parent && top_parent != disk->src && @@ -12805,7 +12807,7 @@ qemuDomainBlockCommit(virDomainPtr dom, const char *path, const char *base, qemuDomainObjExitMonitor(driver, vm); endjob: - if (ret < 0) { + if (ret < 0 && clean_access) { /* Revert access to read-only, if possible. */ qemuDomainPrepareDiskChainElement(driver, vm, cgroup, disk, base_canon, VIR_DISK_CHAIN_READ_ONLY); -- 1.7.11.7
participants (4)
-
Doug Goldstein
-
Eric Blake
-
Laine Stump
-
Philipp Hahn