From nobody Mon Feb 9 09:01:22 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of redhat.com designates 205.139.110.61 as permitted sender) client-ip=205.139.110.61; envelope-from=libvir-list-bounces@redhat.com; helo=us-smtp-delivery-1.mimecast.com; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 205.139.110.61 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1595413686; cv=none; d=zohomail.com; s=zohoarc; b=Bx3C8rA06nI/8hrNCENdptERNSs05XVZLqqJxblwftnN3Vo+vwzX3g3JcI+lEMWOBQBGOzUZVq6Jb33a+Wj+F+El2WKfh0vq1GtqvCQmkNTKXHrf7s1gS/alnMBnjere2CbTqy3reBRXYC/dfP0pd2U1/hF0WySwM5oxZILSD2A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1595413686; h=Content-Type:Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=lpPZH6yAOLXetC8Qp+IxVIJd6/F5qdbyQymce5saeRc=; b=UHvqjhsf9G1w+h/naSJ8AijqMlw0Jtksygi6gjeBCMrDEQXy/bEoewKo8Pb4Rws4RlwqtKk9OimNegfHADr+P4dyVkfx007QZmSY9f23rUh2nryc/ZQkeIeeBWfVp3tPShZSg3mJwkijUNhCNpzlOiTj91lLtLll44y3SLfOQTc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of redhat.com designates 205.139.110.61 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from us-smtp-delivery-1.mimecast.com (us-smtp-2.mimecast.com [205.139.110.61]) by mx.zohomail.com with SMTPS id 1595413686158389.8019610918192; Wed, 22 Jul 2020 03:28:06 -0700 (PDT) Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-114-u4yTvmJ-Ni6FvYhItps5Ag-1; Wed, 22 Jul 2020 06:28:02 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 397CA1005510; Wed, 22 Jul 2020 10:27:56 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 19C978BEC4; Wed, 22 Jul 2020 10:27:56 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id DE498730D1; Wed, 22 Jul 2020 10:27:55 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id 06M9enVi000921 for ; Wed, 22 Jul 2020 05:40:49 -0400 Received: by smtp.corp.redhat.com (Postfix) id C6BC01A8F7; Wed, 22 Jul 2020 09:40:49 +0000 (UTC) Received: from localhost.localdomain (unknown [10.40.195.120]) by smtp.corp.redhat.com (Postfix) with ESMTP id 457012B4DD for ; Wed, 22 Jul 2020 09:40:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1595413684; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=lpPZH6yAOLXetC8Qp+IxVIJd6/F5qdbyQymce5saeRc=; b=a8oX50iJ39vUpgdrMB4fHsESGcjCimokLePCN7m1N5F7+InbZWAXeFFOgfOHypu/xl7MCj 7XG/YYBak4OgcFNWMij9/f+DKU04OZqpGovg4gBXi1QvnmqxRIr3SuAuWzJpsM2fodv3Vq 1NtgWLbzqBzkEooPZ6KRGIVG1647MCc= X-MC-Unique: u4yTvmJ-Ni6FvYhItps5Ag-1 From: Michal Privoznik To: libvir-list@redhat.com Subject: [PATCH v1 11/34] qemuDomainAttachDeviceMknodHelper: Create more files in a single go Date: Wed, 22 Jul 2020 11:40:05 +0200 Message-Id: <56f2b9fd0b85f5c3d617247800b5819ba163f58b.1595410402.git.mprivozn@redhat.com> In-Reply-To: References: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-loop: libvir-list@redhat.com X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=libvir-list-bounces@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable X-ZohoMail-DKIM: pass (identity @redhat.com) Content-Type: text/plain; charset="utf-8" So far, when attaching a device needs two or more /dev nodes created into a domain, we fork off and run the helper for every node separately. For majority of devices this is okay, because they need no or one node created anyway. But the idea is to use this attach code to build the namespace when starting a domain, in which case there will be way more nodes than one. To achieve this, the recursive approach for handling symlinks has to be turned into an iterative one. Signed-off-by: Michal Privoznik Reviewed-by: J=C3=A1n Tomko --- src/qemu/qemu_domain_namespace.c | 298 +++++++++++++++++++------------ 1 file changed, 185 insertions(+), 113 deletions(-) diff --git a/src/qemu/qemu_domain_namespace.c b/src/qemu/qemu_domain_namesp= ace.c index 0c40118938..31acf2bde6 100644 --- a/src/qemu/qemu_domain_namespace.c +++ b/src/qemu/qemu_domain_namespace.c @@ -1098,26 +1098,58 @@ qemuDomainNamespaceAvailable(qemuDomainNamespace ns= G_GNUC_UNUSED) } =20 =20 -struct qemuDomainMknodData { - virQEMUDriverPtr driver; - virDomainObjPtr vm; +typedef struct _qemuDomainMknodItem qemuDomainMknodItem; +typedef qemuDomainMknodItem *qemuDomainMknodItemPtr; +struct _qemuDomainMknodItem { const char *file; char *target; + bool bindmounted; GStatBuf sb; void *acl; -#ifdef WITH_SELINUX char *tcon; +}; + +typedef struct _qemuDomainMknodData qemuDomainMknodData; +typedef qemuDomainMknodData *qemuDomainMknodDataPtr; +struct _qemuDomainMknodData { + virQEMUDriverPtr driver; + virDomainObjPtr vm; + qemuDomainMknodItemPtr items; + size_t nitems; +}; + + +static void +qemuDomainMknodItemClear(qemuDomainMknodItemPtr item) +{ + VIR_FREE(item->target); + virFileFreeACLs(&item->acl); +#ifdef WITH_SELINUX + freecon(item->tcon); #endif -}; +} + + +static void +qemuDomainMknodDataClear(qemuDomainMknodDataPtr data) +{ + size_t i; + + for (i =3D 0; i < data->nitems; i++) { + qemuDomainMknodItemPtr item =3D &data->items[i]; + + qemuDomainMknodItemClear(item); + } + + VIR_FREE(data->items); +} =20 =20 /* Our way of creating devices is highly linux specific */ #if defined(__linux__) static int -qemuDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UNUSED, - void *opaque) +qemuDomainMknodOne(qemuDomainMknodItemPtr data) { - struct qemuDomainMknodData *data =3D opaque; int ret =3D -1; bool delDevice =3D false; bool isLink =3D S_ISLNK(data->sb.st_mode); @@ -1125,8 +1157,6 @@ qemuDomainAttachDeviceMknodHelper(pid_t pid G_GNUC_UN= USED, bool isReg =3D S_ISREG(data->sb.st_mode) || S_ISFIFO(data->sb.st_mode)= || S_ISSOCK(data->sb.st_mode); bool isDir =3D S_ISDIR(data->sb.st_mode); =20 - 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_U= NUSED, else unlink(data->file); } -# ifdef WITH_SELINUX - freecon(data->tcon); -# endif - virFileFreeACLs(&data->acl); - VIR_FREE(data->target); return ret; } =20 @@ -1265,63 +1290,66 @@ qemuDomainMknodItemIsBindMounted(mode_t st_mode) =20 =20 static int -qemuDomainAttachDeviceMknodRecursive(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *file, - char * const *devMountsPath, - size_t ndevMountsPath, - unsigned int ttl) +qemuDomainMknodHelper(pid_t pid G_GNUC_UNUSED, + void *opaque) { - g_autoptr(virQEMUDriverConfig) cfg =3D NULL; - struct qemuDomainMknodData data; + qemuDomainMknodDataPtr data =3D opaque; + size_t i; int ret =3D -1; + + qemuSecurityPostFork(data->driver->securityManager); + + for (i =3D 0; i < data->nitems; i++) { + if (qemuDomainMknodOne(&data->items[i]) < 0) + goto cleanup; + } + + ret =3D 0; + cleanup: + qemuDomainMknodDataClear(data); + return ret; +} + + +static int +qemuDomainMknodItemInit(qemuDomainMknodItemPtr item, + virQEMUDriverConfigPtr cfg, + virDomainObjPtr vm, + const char *file) +{ g_autofree char *target =3D NULL; bool isLink; bool isReg; =20 - if (!ttl) { - virReportSystemError(ELOOP, - _("Too many levels of symbolic links: %s"), - file); - return ret; - } + item->file =3D file; =20 - memset(&data, 0, sizeof(data)); - - data.driver =3D driver; - data.vm =3D vm; - data.file =3D 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; } =20 - isLink =3D S_ISLNK(data.sb.st_mode); - isReg =3D qemuDomainMknodItemIsBindMounted(data.sb.st_mode); + isLink =3D S_ISLNK(item->sb.st_mode); + isReg =3D qemuDomainMknodItemIsBindMounted(item->sb.st_mode); =20 if (isReg && STRPREFIX(file, QEMU_DEVPREFIX)) { - cfg =3D virQEMUDriverGetConfig(driver); if (!(target =3D qemuDomainGetPreservedMountPath(cfg, vm, file))) - goto cleanup; + return -1; =20 - if (virFileBindMountDevice(file, target) < 0) - goto cleanup; - - data.target =3D target; + item->target =3D g_steal_pointer(&target); } else if (isLink) { g_autoptr(GError) gerr =3D NULL; =20 if (!(target =3D g_file_read_link(file, &gerr))) { virReportError(VIR_ERR_SYSTEM_ERROR, _("failed to resolve symlink %s: %s"), file, ge= rr->message); - return ret; + return -1; } =20 if (!g_path_is_absolute(target)) { g_autofree char *fileTmp =3D g_strdup(file); - char *c =3D NULL, *tmp =3D NULL; + char *c =3D NULL; + char *tmp =3D NULL; =20 if ((c =3D strrchr(fileTmp, '/'))) *(c + 1) =3D '\0'; @@ -1331,92 +1359,79 @@ qemuDomainAttachDeviceMknodRecursive(virQEMUDriverP= tr driver, target =3D g_steal_pointer(&tmp); } =20 - data.target =3D target; + item->target =3D g_steal_pointer(&target); } =20 /* Symlinks don't have ACLs. */ if (!isLink && - virFileGetACLs(file, &data.acl) < 0 && + virFileGetACLs(file, &item->acl) < 0 && errno !=3D ENOTSUP) { virReportSystemError(errno, _("Unable to get ACLs on %s"), file); - goto cleanup; + return -1; } =20 # ifdef WITH_SELINUX - if (lgetfilecon_raw(file, &data.tcon) < 0 && + if (lgetfilecon_raw(file, &item->tcon) < 0 && (errno !=3D ENOTSUP && errno !=3D ENODATA)) { virReportSystemError(errno, _("Unable to get SELinux label from %s"), fil= e); - goto cleanup; + return -1; } # endif =20 - if (STRPREFIX(file, QEMU_DEVPREFIX)) { - size_t i; - - for (i =3D 0; i < ndevMountsPath; i++) { - if (STREQ(devMountsPath[i], "/dev")) - continue; - if (STRPREFIX(file, devMountsPath[i])) - break; - } - - if (i =3D=3D ndevMountsPath) { - if (qemuSecurityPreFork(driver->securityManager) < 0) - goto cleanup; - - if (virProcessRunInMountNamespace(vm->pid, - qemuDomainAttachDeviceMknodH= elper, - &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 =3D 0; - cleanup: -# ifdef WITH_SELINUX - freecon(data.tcon); -# endif - virFileFreeACLs(&data.acl); - if (isReg && target) - umount(target); - return ret; + return 0; } =20 =20 -#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_UN= USED, - size_t ndevMountsPath G_GNUC_UNUSED, - unsigned int ttl G_GNUC_UNUSED) +qemuDomainAttachDeviceMknodOne(qemuDomainMknodDataPtr 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 =3D sysconf(_SC_SYMLOOP_MAX); + const char *next =3D file; + size_t i; + + while (1) { + qemuDomainMknodItem item =3D { 0 }; + + if (qemuDomainMknodItemInit(&item, cfg, vm, next) < 0) + return -1; + + if (STRPREFIX(next, QEMU_DEVPREFIX)) { + for (i =3D 0; i < ndevMountsPath; i++) { + if (STREQ(devMountsPath[i], "/dev")) + continue; + if (STRPREFIX(next, devMountsPath[i])) + break; + } + + if (i =3D=3D ndevMountsPath && + VIR_APPEND_ELEMENT_COPY(data->items, data->nitems, item) <= 0) + return -1; + } + + if (!S_ISLNK(item.sb.st_mode)) + break; + + if (ttl-- =3D=3D 0) { + virReportSystemError(ELOOP, + _("Too many levels of symbolic links: %s"= ), + next); + return -1; + } + + next =3D item.target; + } + + return 0; } =20 =20 -#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 =3D sysconf(_SC_SYMLOOP_MAX); + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + qemuDomainMknodData data =3D { 0 }; + size_t i; + int ret =3D -1; =20 - return qemuDomainAttachDeviceMknodRecursive(driver, vm, file, - devMountsPath, ndevMountsP= ath, - symloop_max); + data.driver =3D driver; + data.vm =3D vm; + + if (qemuDomainAttachDeviceMknodOne(&data, cfg, vm, file, + devMountsPath, ndevMountsPath) < 0) + return -1; + + for (i =3D 0; i < data.nitems; i++) { + qemuDomainMknodItemPtr item =3D &data.items[i]; + if (item->target && + qemuDomainMknodItemIsBindMounted(item->sb.st_mode)) { + if (virFileBindMountDevice(item->file, item->target) < 0) + goto cleanup; + item->bindmounted =3D true; + } + } + + if (qemuSecurityPreFork(driver->securityManager) < 0) + goto cleanup; + + if (virProcessRunInMountNamespace(vm->pid, + qemuDomainMknodHelper, + &data) < 0) { + qemuSecurityPostFork(driver->securityManager); + goto cleanup; + } + qemuSecurityPostFork(driver->securityManager); + + ret =3D 0; + cleanup: + for (i =3D 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); + } + } + qemuDomainMknodDataClear(&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; } =20 =20 +#endif /* !defined(__linux__) */ + + static int qemuDomainDetachDeviceUnlinkHelper(pid_t pid G_GNUC_UNUSED, void *opaque) --=20 2.26.2