diff --git a/src/libvirt.c b/src/libvirt.c index ae8c9fddec..53b485a9f6 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -17102,10 +17102,13 @@ virDomainSnapshotGetConnect(virDomainSnapshotPtr snapshot) * VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY to be passed as well. * * By default, if the snapshot involves external files, and any of the - * destination files already exist as a regular file, the snapshot is - * rejected to avoid losing contents of those files. However, if - * @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT, then existing - * destination files are instead truncated and reused. + * destination files already exist as a non-empty regular file, the + * snapshot is rejected to avoid losing contents of those files. + * However, if @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT, + * then the destination files must already exist and contain content + * identical to the source files (this allows a management app to + * pre-create files with relative backing file names, rather than the + * default of creating with absolute backing file names). * * Be aware that although libvirt prefers to report errors up front with * no other effect, some hypervisors have certain types of failures where diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 76cb58a96b..3a8317fbcf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9726,6 +9726,12 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, int external = 0; qemuDomainObjPrivatePtr priv = vm->privateData; + if (allow_reuse && !qemuCapsGet(priv->qemuCaps, QEMU_CAPS_TRANSACTION)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("reuse is not supported with this QEMU binary")); + goto cleanup; + } + for (i = 0; i < def->ndisks; i++) { virDomainSnapshotDiskDefPtr disk = &def->disks[i]; @@ -9756,8 +9762,8 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, virReportOOMError(); goto cleanup; } - } else if (STRNEQ(disk->driverType, "qcow2")) { - /* XXX We should also support QED */ + } else if (STRNEQ(disk->driverType, "qcow2") && + STRNEQ(disk->driverType, "qed")) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("external snapshot format for disk %s " "is unsupported: %s"), @@ -9771,7 +9777,7 @@ qemuDomainSnapshotDiskPrepare(virDomainObjPtr vm, virDomainSnapshotDefPtr def, disk->name, disk->file); goto cleanup; } - } else if (!(S_ISBLK(st.st_mode) || allow_reuse)) { + } else if (!(S_ISBLK(st.st_mode) || !st.st_size || allow_reuse)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("external snapshot file for disk %s already " "exists and is not a block device: %s"), @@ -9824,7 +9830,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, virDomainSnapshotDiskDefPtr snap, virDomainDiskDefPtr disk, virDomainDiskDefPtr persistDisk, - virJSONValuePtr actions) + virJSONValuePtr actions, + bool reuse) { qemuDomainObjPrivatePtr priv = vm->privateData; char *device = NULL; @@ -9846,23 +9853,25 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, if (virAsprintf(&device, "drive-%s", disk->info.alias) < 0 || !(source = strdup(snap->file)) || - (STRNEQ_NULLABLE(disk->driverType, "qcow2") && - !(driverType = strdup("qcow2"))) || + (STRNEQ_NULLABLE(disk->driverType, snap->driverType) && + !(driverType = strdup(snap->driverType))) || (persistDisk && (!(persistSource = strdup(source)) || - (STRNEQ_NULLABLE(persistDisk->driverType, "qcow2") && - !(persistDriverType = strdup("qcow2")))))) { + (STRNEQ_NULLABLE(persistDisk->driverType, snap->driverType) && + !(persistDriverType = strdup(snap->driverType)))))) { virReportOOMError(); goto cleanup; } /* create the stub file and set selinux labels; manipulate disk in * place, in a way that can be reverted on failure. */ - fd = qemuOpenFile(driver, source, O_WRONLY | O_TRUNC | O_CREAT, - &need_unlink, NULL); - if (fd < 0) - goto cleanup; - VIR_FORCE_CLOSE(fd); + if (!reuse) { + fd = qemuOpenFile(driver, source, O_WRONLY | O_TRUNC | O_CREAT, + &need_unlink, NULL); + if (fd < 0) + goto cleanup; + VIR_FORCE_CLOSE(fd); + } origsrc = disk->src; disk->src = source; @@ -9884,7 +9893,8 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, origdriver = NULL; /* create the actual snapshot */ - ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source); + ret = qemuMonitorDiskSnapshot(priv->mon, actions, device, source, + driverType, reuse); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0); if (ret < 0) goto cleanup; @@ -10005,6 +10015,7 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn, bool persist = false; int thaw = 0; /* 1 if freeze succeeded, -1 if freeze failed */ bool atomic = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC) != 0; + bool reuse = (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT) != 0; if (qemuDomainObjBeginJobWithDriver(driver, vm, QEMU_JOB_MODIFY) < 0) return -1; @@ -10080,7 +10091,8 @@ qemuDomainSnapshotCreateDiskActive(virConnectPtr conn, ret = qemuDomainSnapshotCreateSingleDiskActive(driver, vm, &snap->def->disks[i], vm->def->disks[i], - persistDisk, actions); + persistDisk, actions, + reuse); if (ret < 0) break; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 9a13e201f6..e1a8d4c9c6 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2657,12 +2657,13 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) * at file. */ int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, - const char *device, const char *file) + const char *device, const char *file, + const char *format, bool reuse) { int ret; - VIR_DEBUG("mon=%p, actions=%p, device=%s, file=%s", - mon, actions, device, file); + VIR_DEBUG("mon=%p, actions=%p, device=%s, file=%s, format=%s, reuse=%d", + mon, actions, device, file, format, reuse); if (!mon) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", @@ -2671,11 +2672,12 @@ qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, } if (mon->json) { - ret = qemuMonitorJSONDiskSnapshot(mon, actions, device, file); + ret = qemuMonitorJSONDiskSnapshot(mon, actions, device, file, format, + reuse); } else { - if (actions) { + if (actions || STRNEQ(format, "qcow2") || reuse) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", - _("actions not supported with text monitor")); + _("text monitor lacks several snapshot features")); return -1; } ret = qemuMonitorTextDiskSnapshot(mon, device, file); diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5ee108349f..b48096661a 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -505,7 +505,9 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, const char *device, - const char *file); + const char *file, + const char *format, + bool reuse); int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a0d6a1be07..eeeb6a603b 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3105,7 +3105,8 @@ cleanup: int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, - const char *device, const char *file) + const char *device, const char *file, + const char *format, bool reuse) { int ret; virJSONValuePtr cmd; @@ -3115,6 +3116,9 @@ qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, "blockdev-snapshot-sync", "s:device", device, "s:snapshot-file", file, + "s:format", format, + reuse ? "s:mode" : NULL, + reuse ? "existing" : NULL, NULL); if (!cmd) return -1; diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e127870b48..a0f67aaba1 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -228,7 +228,9 @@ int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, const char *device, - const char *file); + const char *file, + const char *format, + bool reuse); int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions); int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,