Similarly to one of the previous commits, we need to deal properly with symlinks in hotplug case too. Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list