[libvirt] [PATCHv2] Don't allow two or more disks to be mapped to the same image file

Hu Tao hutao at cn.fujitsu.com
Thu Mar 24 08:46:14 UTC 2011


If two or more disks are mapped to the same image file, operating
on these disks at the same time may corrupt data stored in the
image file.

changes:

v2:

- allow it for read-only disks
- compare source files by inode number

---
 src/conf/domain_conf.c   |   54 ++++++++++++++++++++++++++++++++++++++++++++++
 src/conf/domain_conf.h   |    2 +
 src/libvirt_private.syms |    1 +
 src/qemu/qemu_driver.c   |    6 +++++
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 1b02c25..b43dc4a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -5455,6 +5455,14 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         if (!disk)
             goto error;
 
+        if (virDomainDiskConflict(disk, def)) {
+            virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s %s %s",
+                                 _("source"),
+                                 disk->src,
+                                 _("is already mapped to another device, "
+                                   "skip this device."));
+            continue;
+        }
         def->disks[def->ndisks++] = disk;
     }
     VIR_FREE(nodes);
@@ -9088,3 +9096,49 @@ cleanup:
 
     return ret;
 }
+
+bool virDomainDiskConflict(virDomainDiskDefPtr disk, virDomainDefPtr def)
+{
+    struct stat stat1, stat2;
+    int i;
+
+    if (!disk->src)
+        return false;
+
+    if (stat(disk->src, &stat1)) {
+        if (errno != ENOENT) {
+            /* Can't stat file, for safety treate it as conflicted */
+            return true;
+        }
+    }
+
+    for (i = 0; i < def->ndisks; i++) {
+        if (disk->readonly && def->disks[i]->readonly)
+            continue;
+
+        if (stat(def->disks[i]->src, &stat2)) {
+            if (errno != ENOENT) {
+                /* Can't stat file, shouldn't happen but for safety treate
+                 * it as conflicted */
+                return true;
+            }
+        }
+
+        if (S_ISREG(stat1.st_mode) && S_ISREG(stat2.st_mode)
+            && (stat1.st_ino == stat2.st_ino)
+            && (stat1.st_dev == stat2.st_dev)) {
+            return true;
+        }
+
+        if (S_ISCHR(stat1.st_mode) && S_ISCHR(stat2.st_mode)
+            && (stat1.st_rdev == stat2.st_rdev)) {
+            return true;
+        }
+
+        if (S_ISBLK(stat1.st_mode) && S_ISBLK(stat2.st_mode)
+            && (stat1.st_rdev == stat2.st_rdev)) {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 9f595d6..78b2f95 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1350,6 +1350,8 @@ int virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
                                 bool ignoreOpenFailure,
                                 virDomainDiskDefPathIterator iter,
                                 void *opaque);
+bool virDomainDiskConflict(virDomainDiskDefPtr disk,
+                           virDomainDefPtr def);
 
 typedef const char* (*virLifecycleToStringFunc)(int type);
 typedef int (*virLifecycleFromStringFunc)(const char *type);
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index b4b6c63..17e2ec4 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -237,6 +237,7 @@ virDomainDeviceTypeToString;
 virDomainDiskBusTypeToString;
 virDomainDiskCacheTypeFromString;
 virDomainDiskCacheTypeToString;
+virDomainDiskConflict;
 virDomainDiskDefAssignAddress;
 virDomainDiskDefForeachPath;
 virDomainDiskDefFree;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 6f296c9..6de08d3 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -4058,6 +4058,12 @@ static int qemudDomainAttachDevice(virDomainPtr dom,
             break;
 
         case VIR_DOMAIN_DISK_DEVICE_DISK:
+            if (virDomainDiskConflict(dev->data.disk, vm->def)) {
+                qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s %s %s",
+                                _("source"), dev->data.disk->src,
+                                _("is already mapped to another device."));
+                break;
+            }
             if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
                 ret = qemuDomainAttachUsbMassstorageDevice(driver, vm,
                                                            dev->data.disk, qemuCaps);
-- 
1.7.3.1


-- 
Thanks,
Hu Tao
2011/03/24




More information about the libvir-list mailing list