[PATCH 2/6] API/qemu: Introduce 'VIR_DOMAIN_BLOCK_RESIZE_EXTEND' for 'virDomainBlockResize'

Peter Krempa via Devel posted 6 patches 7 hours ago
[PATCH 2/6] API/qemu: Introduce 'VIR_DOMAIN_BLOCK_RESIZE_EXTEND' for 'virDomainBlockResize'
Posted by Peter Krempa via Devel 7 hours ago
From: Peter Krempa <pkrempa@redhat.com>

Introduce a new flag VIR_DOMAIN_BLOCK_RESIZE_EXTEND which will prevent
accidental shrinking of the block device.

Warn callers that they ought to use it.

While this won't prevent any old uses without the flag (which we
couldn't change due to our API guarantees) it will give the users tools
to handle the resizing of devices more safely.

Implement it in the qemu driver.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
---
 include/libvirt/libvirt-domain.h |  3 +++
 src/libvirt-domain.c             |  5 +++++
 src/qemu/qemu_driver.c           | 32 +++++++++++++++++++++++++++++++-
 3 files changed, 39 insertions(+), 1 deletion(-)

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 861795724a..551553df14 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;
-- 
2.53.0