diff --git a/src/qemu/qemu_namespace.c b/src/qemu/qemu_namespace.c index 7b04691148..7e8f8d2f2c 100644 --- a/src/qemu/qemu_namespace.c +++ b/src/qemu/qemu_namespace.c @@ -1098,26 +1098,58 @@ qemuDomainNamespaceAvailable(qemuDomainNamespace ns G_GNUC_UNUSED) } -struct qemuNamespaceMkondData { - virQEMUDriverPtr driver; - virDomainObjPtr vm; +typedef struct _qemuNamespaceMknodItem qemuNamespaceMknodItem; +typedef qemuNamespaceMknodItem *qemuNamespaceMknodItemPtr; +struct _qemuNamespaceMknodItem { const char *file; char *target; + bool bindmounted; GStatBuf sb; void *acl; -#ifdef WITH_SELINUX char *tcon; -#endif }; +typedef struct _qemuNamespaceMknodData qemuNamespaceMknodData; +typedef qemuNamespaceMknodData *qemuNamespaceMknodDataPtr; +struct _qemuNamespaceMknodData { + virQEMUDriverPtr driver; + virDomainObjPtr vm; + qemuNamespaceMknodItemPtr items; + size_t nitems; +}; + + +static void +qemuNamespaceMknodItemClear(qemuNamespaceMknodItemPtr item) +{ + VIR_FREE(item->target); + virFileFreeACLs(&item->acl); +#ifdef WITH_SELINUX + freecon(item->tcon); +#endif +} + + +static void +qemuNamespaceMknodDataClear(qemuNamespaceMknodDataPtr data) +{ + size_t i; + + for (i = 0; i < data->nitems; i++) { + qemuNamespaceMknodItemPtr item = &data->items[i]; + + qemuNamespaceMknodItemClear(item); + } + + VIR_FREE(data->items); +} + /* Our way of creating devices is highly linux specific */ #if defined(__linux__) static int -qemuDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, - void *opaque) +qemuNamespaceMknodOne(qemuNamespaceMknodItemPtr data) { - struct qemuNamespaceMkondData *data = opaque; int ret = -1; bool delDevice = false; bool isLink = S_ISLNK(data->sb.st_mode); @@ -1125,8 +1157,6 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, bool isReg = S_ISREG(data->sb.st_mode) || S_ISFIFO(data->sb.st_mode) || S_ISSOCK(data->sb.st_mode); bool isDir = S_ISDIR(data->sb.st_mode); - qemuSecurityPostFork(data->driver->securityManager); - if (virFileMakeParentPath(data->file) < 0) { virReportSystemError(errno, _("Unable to create %s"), data->file); @@ -1244,11 +1274,6 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, else unlink(data->file); } -# ifdef WITH_SELINUX - freecon(data->tcon); -# endif - virFileFreeACLs(&data->acl); - VIR_FREE(data->target); return ret; } @@ -1265,63 +1290,66 @@ qemuNamespaceMknodItemNeedsBindMount(mode_t st_mode) static int -qemuDomainAttachDeviceMknodRecursive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *file, - char * const *devMountsPath, - size_t ndevMountsPath, - unsigned int ttl) +qemuNamespaceMknodHelper(pid_t pid G_GNUC_UNUSED, + void *opaque) { - g_autoptr(virQEMUDriverConfig) cfg = NULL; - struct qemuNamespaceMkondData data; + qemuNamespaceMknodDataPtr data = opaque; + size_t i; int ret = -1; + + qemuSecurityPostFork(data->driver->securityManager); + + for (i = 0; i < data->nitems; i++) { + if (qemuNamespaceMknodOne(&data->items[i]) < 0) + goto cleanup; + } + + ret = 0; + cleanup: + qemuNamespaceMknodDataClear(data); + return ret; +} + + +static int +qemuNamespaceMknodItemInit(qemuNamespaceMknodItemPtr item, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + const char *file) +{ g_autofree char *target = NULL; bool isLink; bool needsBindMount; - if (!ttl) { - virReportSystemError(ELOOP, - _("Too many levels of symbolic links: %s"), - file); - return ret; - } + item->file = file; - memset(&data, 0, sizeof(data)); - - data.driver = driver; - data.vm = vm; - data.file = file; - - if (g_lstat(file, &data.sb) < 0) { + if (g_lstat(file, &item->sb) < 0) { virReportSystemError(errno, _("Unable to access %s"), file); - return ret; + return -1; } - isLink = S_ISLNK(data.sb.st_mode); - needsBindMount = qemuNamespaceMknodItemNeedsBindMount(data.sb.st_mode); + isLink = S_ISLNK(item->sb.st_mode); + needsBindMount = qemuNamespaceMknodItemNeedsBindMount(item->sb.st_mode); if (needsBindMount && STRPREFIX(file, QEMU_DEVPREFIX)) { - cfg = virQEMUDriverGetConfig(driver); if (!(target = qemuDomainGetPreservedMountPath(cfg, vm, file))) - goto cleanup; + return -1; - if (virFileBindMountDevice(file, target) < 0) - goto cleanup; - - data.target = target; + item->target = g_steal_pointer(&target); } else if (isLink) { g_autoptr(GError) gerr = NULL; if (!(target = g_file_read_link(file, &gerr))) { virReportError(VIR_ERR_SYSTEM_ERROR, _("failed to resolve symlink %s: %s"), file, gerr->message); - return ret; + return -1; } if (!g_path_is_absolute(target)) { g_autofree char *fileTmp = g_strdup(file); - char *c = NULL, *tmp = NULL; + char *c = NULL; + char *tmp = NULL; if ((c = strrchr(fileTmp, '/'))) *(c + 1) = '\0'; @@ -1331,92 +1359,79 @@ qemuDomainAttachDeviceMknodRecursive(virQEMUDriverPtr driver, target = g_steal_pointer(&tmp); } - data.target = target; + item->target = g_steal_pointer(&target); } /* Symlinks don't have ACLs. */ if (!isLink && - virFileGetACLs(file, &data.acl) < 0 && + virFileGetACLs(file, &item->acl) < 0 && errno != ENOTSUP) { virReportSystemError(errno, _("Unable to get ACLs on %s"), file); - goto cleanup; + return -1; } # ifdef WITH_SELINUX - if (lgetfilecon_raw(file, &data.tcon) < 0 && + if (lgetfilecon_raw(file, &item->tcon) < 0 && (errno != ENOTSUP && errno != ENODATA)) { virReportSystemError(errno, _("Unable to get SELinux label from %s"), file); - goto cleanup; + return -1; } # endif - if (STRPREFIX(file, QEMU_DEVPREFIX)) { - size_t i; - - for (i = 0; i < ndevMountsPath; i++) { - if (STREQ(devMountsPath[i], "/dev")) - continue; - if (STRPREFIX(file, devMountsPath[i])) - break; - } - - if (i == ndevMountsPath) { - if (qemuSecurityPreFork(driver->securityManager) < 0) - goto cleanup; - - if (virProcessRunInMountNamespace(vm->pid, - qemuDomainAttachDeviceMknodHelper, - &data) < 0) { - qemuSecurityPostFork(driver->securityManager); - goto cleanup; - } - qemuSecurityPostFork(driver->securityManager); - } else { - VIR_DEBUG("Skipping dev %s because of %s mount point", - file, devMountsPath[i]); - } - } - - if (isLink && - qemuDomainAttachDeviceMknodRecursive(driver, vm, target, - devMountsPath, ndevMountsPath, - ttl -1) < 0) - goto cleanup; - - ret = 0; - cleanup: -# ifdef WITH_SELINUX - freecon(data.tcon); -# endif - virFileFreeACLs(&data.acl); - if (needsBindMount && target) - umount(target); - return ret; + return 0; } -#else /* !defined(__linux__) */ - - static int -qemuDomainAttachDeviceMknodRecursive(virQEMUDriverPtr driver G_GNUC_UNUSED, - virDomainObjPtr vm G_GNUC_UNUSED, - const char *file G_GNUC_UNUSED, - char * const *devMountsPath G_GNUC_UNUSED, - size_t ndevMountsPath G_GNUC_UNUSED, - unsigned int ttl G_GNUC_UNUSED) +qemuNamespaceAttachDeviceMknodOne(qemuNamespaceMknodDataPtr data, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + const char *file, + char * const *devMountsPath, + size_t ndevMountsPath) { - virReportSystemError(ENOSYS, "%s", - _("Namespaces are not supported on this platform.")); - return -1; + long ttl = sysconf(_SC_SYMLOOP_MAX); + const char *next = file; + size_t i; + + while (1) { + qemuNamespaceMknodItem item = { 0 }; + + if (qemuNamespaceMknodItemInit(&item, cfg, vm, next) < 0) + return -1; + + if (STRPREFIX(next, QEMU_DEVPREFIX)) { + for (i = 0; i < ndevMountsPath; i++) { + if (STREQ(devMountsPath[i], "/dev")) + continue; + if (STRPREFIX(next, devMountsPath[i])) + break; + } + + if (i == ndevMountsPath && + VIR_APPEND_ELEMENT_COPY(data->items, data->nitems, item) < 0) + return -1; + } + + if (!S_ISLNK(item.sb.st_mode)) + break; + + if (ttl-- == 0) { + virReportSystemError(ELOOP, + _("Too many levels of symbolic links: %s"), + next); + return -1; + } + + next = item.target; + } + + return 0; } -#endif /* !defined(__linux__) */ - - static int qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver, virDomainObjPtr vm, @@ -1424,14 +1439,71 @@ qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver, char * const *devMountsPath, size_t ndevMountsPath) { - long symloop_max = sysconf(_SC_SYMLOOP_MAX); + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + qemuNamespaceMknodData data = { 0 }; + size_t i; + int ret = -1; - return qemuDomainAttachDeviceMknodRecursive(driver, vm, file, - devMountsPath, ndevMountsPath, - symloop_max); + data.driver = driver; + data.vm = vm; + + if (qemuNamespaceAttachDeviceMknodOne(&data, cfg, vm, file, + devMountsPath, ndevMountsPath) < 0) + return -1; + + for (i = 0; i < data.nitems; i++) { + qemuNamespaceMknodItemPtr item = &data.items[i]; + if (item->target && + qemuNamespaceMknodItemNeedsBindMount(item->sb.st_mode)) { + if (virFileBindMountDevice(item->file, item->target) < 0) + goto cleanup; + item->bindmounted = true; + } + } + + if (qemuSecurityPreFork(driver->securityManager) < 0) + goto cleanup; + + if (virProcessRunInMountNamespace(vm->pid, + qemuNamespaceMknodHelper, + &data) < 0) { + qemuSecurityPostFork(driver->securityManager); + goto cleanup; + } + qemuSecurityPostFork(driver->securityManager); + + ret = 0; + cleanup: + for (i = 0; i < data.nitems; i++) { + if (data.items[i].bindmounted && + umount(data.items[i].target) < 0) { + VIR_WARN("Unable to unmount %s", data.items[i].target); + } + } + qemuNamespaceMknodDataClear(&data); + return ret; } +#else /* !defined(__linux__) */ + + +static int +qemuDomainAttachDeviceMknod(virQEMUDriverPtr driver G_GNUC_UNUSED, + virDomainObjPtr vm G_GNUC_UNUSED, + const char *file G_GNUC_UNUSED, + char * const *devMountsPath G_GNUC_UNUSED, + size_t ndevMountsPath G_GNUC_UNUSED) +{ + virReportSystemError(ENOSYS, "%s", + _("Namespaces are not supported on this platform.")); + return -1; +} + + +#endif /* !defined(__linux__) */ + + static int qemuDomainDetachDeviceUnlinkHelper(pid_t pid G_GNUC_UNUSED, void *opaque)