diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 084debbf21..4a8e3114b3 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -2351,6 +2351,9 @@ typedef enum { /* resize to full the capacity of the source (Since: 10.0.0) */ VIR_DOMAIN_BLOCK_RESIZE_CAPACITY = 1 << 1, + + /* Disallow shrinking (Since: 12.3.0) */ + VIR_DOMAIN_BLOCK_RESIZE_EXTEND = 1 << 2, } virDomainBlockResizeFlags; int virDomainBlockResize (virDomainPtr dom, diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index ed51180c91..db9eea5774 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -6451,6 +6451,11 @@ virDomainBlockPeek(virDomainPtr dom, * size. Depending on the file format, the hypervisor may round up * to the next alignment boundary. * + * *BEWARE*: The block device may be shrunk if @size is less than the current + * guest visible size. Callers who wish to only extend @disk should always pass + * VIR_DOMAIN_BLOCK_RESIZE_EXTEND (since 12.3.0) in @flags which instructs the + * hypervisor to deny an attempt to shrink the device. + * * If @flag contains VIR_DOMAIN_BLOCK_RESIZE_CAPACITY (since 10.0.0) the * hypervisor will resize the guest block device to fully fill the source. * @size must be either set to zero, or to the exact size of the block diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a3bcfc701d..3a64e9edd9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9448,7 +9448,8 @@ qemuDomainBlockResize(virDomainPtr dom, virDomainDiskDef *persistDisk = NULL; virCheckFlags(VIR_DOMAIN_BLOCK_RESIZE_BYTES | - VIR_DOMAIN_BLOCK_RESIZE_CAPACITY, -1); + VIR_DOMAIN_BLOCK_RESIZE_CAPACITY | + VIR_DOMAIN_BLOCK_RESIZE_EXTEND, -1); /* We prefer operating on bytes. */ if ((flags & VIR_DOMAIN_BLOCK_RESIZE_BYTES) == 0) { @@ -9587,11 +9588,40 @@ qemuDomainBlockResize(virDomainPtr dom, if (!qemuDiskBusIsSD(disk->bus)) { nodename = qemuBlockStorageSourceGetEffectiveNodename(disk->src); } else { + if (flags & VIR_DOMAIN_BLOCK_RESIZE_EXTEND) { + /* technically possible, just not implemented */ + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("cannot prevent shrink on SD devices")); + goto endjob; + } + if (!(device = qemuAliasDiskDriveFromDisk(disk))) goto endjob; } qemuDomainObjEnterMonitor(vm); + + if (flags & VIR_DOMAIN_BLOCK_RESIZE_EXTEND) { + g_autoptr(GHashTable) blockNamedNodeData = qemuMonitorBlockGetNamedNodeData(priv->mon); + qemuBlockNamedNodeData *entry; + + if (!(entry = virHashLookup(blockNamedNodeData, nodename))) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("failed to determine current size of '%1$s'"), + path); + qemuDomainObjExitMonitor(vm); + goto endjob; + } + + if (entry->capacity > size) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("resize of '%1$s' would shrink it from '%2$llu' to '%3$llu' bytes"), + path, entry->capacity, size); + qemuDomainObjExitMonitor(vm); + goto endjob; + } + } + if (qemuMonitorBlockResize(priv->mon, device, nodename, size) < 0) { qemuDomainObjExitMonitor(vm); goto endjob;