[libvirt] [PATCH 09/10] qemuDomainAttachDeviceMknod: Deal with symlinks

Michal Privoznik mprivozn at redhat.com
Fri Jan 20 09:42:49 UTC 2017


Similarly to one of the previous commits, we need to deal
properly with symlinks in hotplug case too.

Signed-off-by: Michal Privoznik <mprivozn at redhat.com>
---
 src/qemu/qemu_domain.c | 120 ++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 94 insertions(+), 26 deletions(-)

diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 448583313..bcfb2446f 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -7590,6 +7590,7 @@ struct qemuDomainAttachDeviceMknodData {
     virDomainObjPtr vm;
     virDomainDeviceDefPtr devDef;
     const char *file;
+    const char *target;
     struct stat sb;
     void *acl;
 #ifdef WITH_SELINUX
@@ -7605,6 +7606,7 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
     struct qemuDomainAttachDeviceMknodData *data = opaque;
     int ret = -1;
     bool delDevice = false;
+    bool isLink = S_ISLNK(data->sb.st_mode);
 
     virSecurityManagerPostFork(data->driver->securityManager);
 
@@ -7614,24 +7616,47 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
         goto cleanup;
     }
 
-    VIR_DEBUG("Creating dev %s (%d,%d)",
-              data->file, major(data->sb.st_rdev), minor(data->sb.st_rdev));
-    if (mknod(data->file, data->sb.st_mode, data->sb.st_rdev) < 0) {
-        /* Because we are not removing devices on hotunplug, or
-         * we might be creating part of backing chain that
-         * already exist due to a different disk plugged to
-         * domain, accept EEXIST. */
-        if (errno != EEXIST) {
-            virReportSystemError(errno,
-                                 _("Unable to create device %s"),
-                                 data->file);
-            goto cleanup;
+    if (isLink) {
+        VIR_DEBUG("Creating symlink %s -> %s", data->file, data->target);
+        if (symlink(data->target, data->file) < 0) {
+            if (errno != EEXIST) {
+                virReportSystemError(errno,
+                                     _("Unable to create symlink %s"),
+                                     data->target);
+                goto cleanup;
+            }
+        } else {
+            delDevice = true;
         }
     } else {
-        delDevice = true;
+        VIR_DEBUG("Creating dev %s (%d,%d)",
+                  data->file, major(data->sb.st_rdev), minor(data->sb.st_rdev));
+        if (mknod(data->file, data->sb.st_mode, data->sb.st_rdev) < 0) {
+            /* Because we are not removing devices on hotunplug, or
+             * we might be creating part of backing chain that
+             * already exist due to a different disk plugged to
+             * domain, accept EEXIST. */
+            if (errno != EEXIST) {
+                virReportSystemError(errno,
+                                     _("Unable to create device %s"),
+                                     data->file);
+                goto cleanup;
+            }
+        } else {
+            delDevice = true;
+        }
     }
 
-    if (virFileSetACLs(data->file, data->acl) < 0 &&
+    if (lchown(data->file, data->sb.st_uid, data->sb.st_gid) < 0) {
+        virReportSystemError(errno,
+                             _("Failed to chown device %s"),
+                             data->file);
+        goto cleanup;
+    }
+
+    /* Symlinks don't have ACLs. */
+    if (!isLink &&
+        virFileSetACLs(data->file, data->acl) < 0 &&
         errno != ENOTSUP) {
         virReportSystemError(errno,
                              _("Unable to set ACLs on %s"), data->file);
@@ -7639,7 +7664,8 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid ATTRIBUTE_UNUSED,
     }
 
 #ifdef WITH_SELINUX
-    if (setfilecon_raw(data->file, (VIR_SELINUX_CTX_CONST char *) data->tcon) < 0) {
+    if (data->tcon &&
+        lsetfilecon_raw(data->file, (VIR_SELINUX_CTX_CONST char *) data->tcon) < 0) {
         VIR_WARNINGS_NO_WLOGICALOP_EQUAL_EXPR
         if (errno != EOPNOTSUPP && errno != ENOTSUP) {
         VIR_WARNINGS_RESET
@@ -7671,6 +7697,8 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
 {
     struct qemuDomainAttachDeviceMknodData data;
     int ret = -1;
+    char *target = NULL;
+    bool isLink;
 
     memset(&data, 0, sizeof(data));
 
@@ -7679,21 +7707,55 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
     data.devDef = devDef;
     data.file = file;
 
-    if (stat(file, &data.sb) < 0) {
+    if (lstat(file, &data.sb) < 0) {
         virReportSystemError(errno,
                              _("Unable to access %s"), file);
         return ret;
     }
 
-    if (virFileGetACLs(file, &data.acl) < 0 &&
+    isLink = S_ISLNK(data.sb.st_mode);
+
+    if (isLink) {
+        if (virFileReadLink(file, &target) < 0) {
+            virReportSystemError(errno,
+                                 _("unable to resolve symlink %s"),
+                                 file);
+            return ret;
+        }
+
+        if (IS_RELATIVE_FILE_NAME(target)) {
+            char *c = NULL, *tmp = NULL, *fileTmp = NULL;
+
+            if (VIR_STRDUP(fileTmp, file) < 0)
+                goto cleanup;
+
+            if ((c = strrchr(fileTmp, '/')))
+                *(c + 1) = '\0';
+
+            if (virAsprintf(&tmp, "%s%s", fileTmp, target) < 0) {
+                VIR_FREE(fileTmp);
+                goto cleanup;
+            }
+            VIR_FREE(fileTmp);
+            VIR_FREE(target);
+            target = tmp;
+            tmp = NULL;
+        }
+
+        data.target = target;
+    }
+
+    /* Symlinks don't have ACLs. */
+    if (!isLink &&
+        virFileGetACLs(file, &data.acl) < 0 &&
         errno != ENOTSUP) {
         virReportSystemError(errno,
                              _("Unable to get ACLs on %s"), file);
-        return ret;
+        goto cleanup;
     }
 
 #ifdef WITH_SELINUX
-    if (getfilecon_raw(file, &data.tcon) < 0 &&
+    if (lgetfilecon_raw(file, &data.tcon) < 0 &&
         (errno != ENOTSUP && errno != ENODATA)) {
         virReportSystemError(errno,
                              _("Unable to get SELinux label from %s"), file);
@@ -7701,17 +7763,22 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
     }
 #endif
 
-    if (virSecurityManagerPreFork(driver->securityManager) < 0)
-        goto cleanup;
+    if (STRPREFIX(file, DEVPREFIX)) {
+        if (virSecurityManagerPreFork(driver->securityManager) < 0)
+            goto cleanup;
 
-    if (virProcessRunInMountNamespace(vm->pid,
-                                      qemuDomainAttachDeviceMknodHelper,
-                                      &data) < 0) {
+        if (virProcessRunInMountNamespace(vm->pid,
+                                          qemuDomainAttachDeviceMknodHelper,
+                                          &data) < 0) {
+            virSecurityManagerPostFork(driver->securityManager);
+            goto cleanup;
+        }
         virSecurityManagerPostFork(driver->securityManager);
-        goto cleanup;
     }
 
-    virSecurityManagerPostFork(driver->securityManager);
+    if (isLink &&
+        qemuDomainAttachDeviceMknod(driver, vm, devDef, target) < 0)
+        goto cleanup;
 
     ret = 0;
  cleanup:
@@ -7719,6 +7786,7 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver,
     freecon(data.tcon);
 #endif
     virFileFreeACLs(&data.acl);
+    VIR_FREE(target);
     return ret;
 }
 
-- 
2.11.0




More information about the libvir-list mailing list