7a3408
From 3216788d3cd902dd31705cac5e91589b9b467543 Mon Sep 17 00:00:00 2001
7a3408
Message-Id: <3216788d3cd902dd31705cac5e91589b9b467543@dist-git>
7a3408
From: Martin Kletzander <mkletzan@redhat.com>
7a3408
Date: Thu, 6 Aug 2015 13:10:54 +0200
7a3408
Subject: [PATCH] qemu: Reject updating unsupported disk information
7a3408
7a3408
If one calls update-device with information that is not updatable,
7a3408
libvirt reports success even though no data were updated.  The example
7a3408
used in the bug linked below uses updating device with <boot order='2'/>
7a3408
which, in my opinion, is a valid thing to request from user's
7a3408
perspective.  Mainly since we properly error out if user wants to update
7a3408
such data on a network device for example.
7a3408
7a3408
And since there are many things that might happen (update-device on disk
7a3408
basically knows just how to change removable media), check for what's
7a3408
changing and moreover, since the function might be usable in other
7a3408
drivers (updating only disk path is a valid possibility) let's abstract
7a3408
it for any two disks.
7a3408
7a3408
We can't possibly check for everything since for many fields our code
7a3408
does not properly differentiate between default and unspecified values.
7a3408
Even though this could be changed, I don't feel like it's worth the
7a3408
complexity so it's not the aim of this patch.
7a3408
7a3408
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1007228
7a3408
(cherry picked from commit 717c99f3602354136ca97edca6afc8dce69aae85)
7a3408
Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
7a3408
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
7a3408
---
7a3408
 src/conf/domain_conf.c   | 128 +++++++++++++++++++++++++++++++++++++++++++++++
7a3408
 src/conf/domain_conf.h   |   2 +
7a3408
 src/libvirt_private.syms |   1 +
7a3408
 src/qemu/qemu_driver.c   |   3 ++
7a3408
 4 files changed, 134 insertions(+)
7a3408
7a3408
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
7a3408
index 72d87dd..4fe38ae 100644
7a3408
--- a/src/conf/domain_conf.c
7a3408
+++ b/src/conf/domain_conf.c
7a3408
@@ -5690,6 +5690,134 @@ virDomainDiskFindByBusAndDst(virDomainDefPtr def,
7a3408
     return NULL;
7a3408
 }
7a3408
 
7a3408
+
7a3408
+/*
7a3408
+ * Makes sure the @disk differs from @orig_disk only by the source
7a3408
+ * path and nothing else.  Fields that are being checked and the
7a3408
+ * information whether they are nullable (may not be specified) or is
7a3408
+ * taken from the virDomainDiskDefFormat() code.
7a3408
+ */
7a3408
+bool
7a3408
+virDomainDiskDiffersSourceOnly(virDomainDiskDefPtr disk,
7a3408
+                               virDomainDiskDefPtr orig_disk)
7a3408
+{
7a3408
+#define CHECK_EQ(field, field_name, nullable)                           \
7a3408
+    do {                                                                \
7a3408
+        if (nullable && !disk->field)                                   \
7a3408
+            break;                                                      \
7a3408
+        if (disk->field != orig_disk->field) {                          \
7a3408
+            virReportError(VIR_ERR_OPERATION_UNSUPPORTED,               \
7a3408
+                           _("cannot modify field '%s' of the disk"),   \
7a3408
+                           field_name);                                 \
7a3408
+            return false;                                               \
7a3408
+        }                                                               \
7a3408
+    } while (0)
7a3408
+
7a3408
+    CHECK_EQ(device, "device", false);
7a3408
+    CHECK_EQ(cachemode, "cache", true);
7a3408
+    CHECK_EQ(error_policy, "error_policy", true);
7a3408
+    CHECK_EQ(rerror_policy, "rerror_policy", true);
7a3408
+    CHECK_EQ(iomode, "io", true);
7a3408
+    CHECK_EQ(ioeventfd, "ioeventfd", true);
7a3408
+    CHECK_EQ(event_idx, "event_idx", true);
7a3408
+    CHECK_EQ(copy_on_read, "copy_on_read", true);
7a3408
+    CHECK_EQ(discard, "discard", true);
7a3408
+    CHECK_EQ(iothread, "iothread", true);
7a3408
+
7a3408
+    if (disk->geometry.cylinders &&
7a3408
+        disk->geometry.heads &&
7a3408
+        disk->geometry.sectors) {
7a3408
+        CHECK_EQ(geometry.cylinders, "geometry cylinders", false);
7a3408
+        CHECK_EQ(geometry.heads, "geometry heads", false);
7a3408
+        CHECK_EQ(geometry.sectors, "geometry sectors", false);
7a3408
+        CHECK_EQ(geometry.trans, "BIOS-translation-modus", true);
7a3408
+    }
7a3408
+
7a3408
+    CHECK_EQ(blockio.logical_block_size,
7a3408
+             "blockio logical_block_size", false);
7a3408
+    CHECK_EQ(blockio.physical_block_size,
7a3408
+             "blockio physical_block_size", false);
7a3408
+
7a3408
+    if (disk->bus == VIR_DOMAIN_DISK_BUS_USB)
7a3408
+        CHECK_EQ(removable, "removable", true);
7a3408
+
7a3408
+    CHECK_EQ(blkdeviotune.total_bytes_sec,
7a3408
+             "blkdeviotune total_bytes_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.read_bytes_sec,
7a3408
+             "blkdeviotune read_bytes_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.write_bytes_sec,
7a3408
+             "blkdeviotune write_bytes_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.total_iops_sec,
7a3408
+             "blkdeviotune total_iops_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.read_iops_sec,
7a3408
+             "blkdeviotune read_iops_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.write_iops_sec,
7a3408
+             "blkdeviotune write_iops_sec",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.total_bytes_sec_max,
7a3408
+             "blkdeviotune total_bytes_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.read_bytes_sec_max,
7a3408
+             "blkdeviotune read_bytes_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.write_bytes_sec_max,
7a3408
+             "blkdeviotune write_bytes_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.total_iops_sec_max,
7a3408
+             "blkdeviotune total_iops_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.read_iops_sec_max,
7a3408
+             "blkdeviotune read_iops_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.write_iops_sec_max,
7a3408
+             "blkdeviotune write_iops_sec_max",
7a3408
+             true);
7a3408
+    CHECK_EQ(blkdeviotune.size_iops_sec,
7a3408
+             "blkdeviotune size_iops_sec",
7a3408
+             true);
7a3408
+
7a3408
+    CHECK_EQ(transient, "transient", true);
7a3408
+
7a3408
+    if (disk->serial && STRNEQ(disk->serial, orig_disk->serial)) {
7a3408
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
7a3408
+                       _("cannot modify field '%s' of the disk"),
7a3408
+                       "serial");
7a3408
+        return false;
7a3408
+    }
7a3408
+
7a3408
+    if (disk->wwn && STRNEQ(disk->wwn, orig_disk->wwn)) {
7a3408
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
7a3408
+                       _("cannot modify field '%s' of the disk"),
7a3408
+                       "wwn");
7a3408
+        return false;
7a3408
+    }
7a3408
+
7a3408
+    if (disk->vendor && STRNEQ(disk->vendor, orig_disk->vendor)) {
7a3408
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
7a3408
+                       _("cannot modify field '%s' of the disk"),
7a3408
+                       "vendor");
7a3408
+        return false;
7a3408
+    }
7a3408
+
7a3408
+    if (disk->product && STRNEQ(disk->product, orig_disk->product)) {
7a3408
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
7a3408
+                       _("cannot modify field '%s' of the disk"),
7a3408
+                       "product");
7a3408
+        return false;
7a3408
+    }
7a3408
+
7a3408
+    CHECK_EQ(info.bootIndex, "boot order", true);
7a3408
+
7a3408
+#undef CHECK_EQ
7a3408
+
7a3408
+    return true;
7a3408
+}
7a3408
+
7a3408
 int
7a3408
 virDomainDiskDefAssignAddress(virDomainXMLOptionPtr xmlopt,
7a3408
                               virDomainDiskDefPtr def)
7a3408
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
7a3408
index 604d0b8..1be8e63 100644
7a3408
--- a/src/conf/domain_conf.h
7a3408
+++ b/src/conf/domain_conf.h
7a3408
@@ -2474,6 +2474,8 @@ int virDomainDeviceFindControllerModel(virDomainDefPtr def,
7a3408
 virDomainDiskDefPtr virDomainDiskFindByBusAndDst(virDomainDefPtr def,
7a3408
                                                  int bus,
7a3408
                                                  char *dst);
7a3408
+bool virDomainDiskDiffersSourceOnly(virDomainDiskDefPtr disk,
7a3408
+                                    virDomainDiskDefPtr orig_disk);
7a3408
 void virDomainControllerDefFree(virDomainControllerDefPtr def);
7a3408
 void virDomainFSDefFree(virDomainFSDefPtr def);
7a3408
 void virDomainActualNetDefFree(virDomainActualNetDefPtr def);
7a3408
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
7a3408
index 46e535e..0517c24 100644
7a3408
--- a/src/libvirt_private.syms
7a3408
+++ b/src/libvirt_private.syms
7a3408
@@ -249,6 +249,7 @@ virDomainDiskDefFree;
7a3408
 virDomainDiskDefNew;
7a3408
 virDomainDiskDefSourceParse;
7a3408
 virDomainDiskDeviceTypeToString;
7a3408
+virDomainDiskDiffersSourceOnly;
7a3408
 virDomainDiskDiscardTypeToString;
7a3408
 virDomainDiskErrorPolicyTypeFromString;
7a3408
 virDomainDiskErrorPolicyTypeToString;
7a3408
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
7a3408
index bb0dc7e..8d569fe 100644
7a3408
--- a/src/qemu/qemu_driver.c
7a3408
+++ b/src/qemu/qemu_driver.c
7a3408
@@ -7940,6 +7940,9 @@ qemuDomainChangeDiskMediaLive(virConnectPtr conn,
7a3408
             goto end;
7a3408
         }
7a3408
 
7a3408
+        if (!virDomainDiskDiffersSourceOnly(disk, orig_disk))
7a3408
+            goto end;
7a3408
+
7a3408
         /* Add the new disk src into shared disk hash table */
7a3408
         if (qemuAddSharedDevice(driver, dev, vm->def->name) < 0)
7a3408
             goto end;
7a3408
-- 
7a3408
2.5.0
7a3408