From abd9025c2fd28f76a43148fc5c92f6153c7b1f74 Mon Sep 17 00:00:00 2001 From: Peter Krempa Date: Thu, 24 Feb 2022 17:01:40 +0100 Subject: [PATCH] lib: Introduce virDomainFDAssociate API The API can be used to associate one or more (e.g. a RO and RW fd for a disk backend image) FDs to a VM. They can be then used per definition. The primary use case for now is for complex deployment where libvirtd/virtqemud may be run inside a container and getting the image into the container is complicated. In the future it will also allow passing e.g. vhost FDs and other resources to a VM without the need to have a filesystem representation for it. Passing raw FDs has few intricacies and thus libvirt will by default not restore security labels. Signed-off-by: Peter Krempa Reviewed-by: Pavel Hrdina --- include/libvirt/libvirt-domain.h | 20 ++++++++ src/driver-hypervisor.h | 8 +++ src/libvirt-domain.c | 80 +++++++++++++++++++++++++++++ src/libvirt_public.syms | 5 ++ src/remote/remote_daemon_dispatch.c | 40 +++++++++++++++ src/remote/remote_driver.c | 27 ++++++++++ src/remote/remote_protocol.x | 14 ++++- src/remote_protocol-structs | 6 +++ 8 files changed, 199 insertions(+), 1 deletion(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 4b1de1d5b8..5152ed4551 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -6458,4 +6458,24 @@ int virDomainStartDirtyRateCalc(virDomainPtr domain, int seconds, unsigned int flags); + +/** + * virDomainFDAssociateFlags: + * + * Since: 9.0.0 + */ +typedef enum { + /* Attempt a best-effort restore of security labels after use (Since: 9.0.0) */ + VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE = (1 << 0), + /* Use a seclabel allowing writes for the FD even if usage implies read-only mode (Since: 9.0.0) */ + VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE = (1 << 1), +} virDomainFDAssociateFlags; + + +int virDomainFDAssociate(virDomainPtr domain, + const char *name, + unsigned int nfds, + int *fds, + unsigned int flags); + #endif /* LIBVIRT_DOMAIN_H */ diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h index 016d5cec7c..5219344b72 100644 --- a/src/driver-hypervisor.h +++ b/src/driver-hypervisor.h @@ -1441,6 +1441,13 @@ typedef int int seconds, unsigned int flags); +typedef int +(*virDrvDomainFDAssociate)(virDomainPtr domain, + const char *name, + unsigned int nfds, + int *fds, + unsigned int flags); + typedef struct _virHypervisorDriver virHypervisorDriver; /** @@ -1712,4 +1719,5 @@ struct _virHypervisorDriver { virDrvDomainAuthorizedSSHKeysSet domainAuthorizedSSHKeysSet; virDrvDomainGetMessages domainGetMessages; virDrvDomainStartDirtyRateCalc domainStartDirtyRateCalc; + virDrvDomainFDAssociate domainFDAssociate; }; diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c index 78c26b2219..3199a27065 100644 --- a/src/libvirt-domain.c +++ b/src/libvirt-domain.c @@ -13972,3 +13972,83 @@ virDomainStartDirtyRateCalc(virDomainPtr domain, virDispatchError(conn); return -1; } + + +/** + * virDomainFDAssociate: + * @domain: a domain object + * @name: name for the file descriptor group + * @nfds: number of fds in @fds + * @fds: file descriptors to associate with domain + * @flags: optional flags; bitwise-OR of supported virDomainFDAssociateFlags + * + * Associate the FDs in @fd with @domain under @name. The FDs are associated as + * long as the connection used to associated exists and are disposed of + * afterwards. FD may still be kept open by the hypervisor for as long as it's + * needed. + * + * Security labelling (e.g. via the selinux) may be applied on the passed FDs + * when required for usage by the VM. By default libvirt does not restore the + * seclabels on the FDs afterwards to avoid keeping it open unnecessarily. + * + * Restoring of the security label can be requested by passing either + * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE for a best-effort attempt to restore + * the security label after use. + * Requesting the restore of security label will require that the file + * descriptors are kept open for the whole time they are used by the hypervisor, + * or other additional overhead. + * + * In certain cases usage of the fd group would imply read-only access. Passing + * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE in @flags ensures that a writable + * security label is picked in case when the file represented by the fds may + * be used in write mode. + * + * Returns 0 on success, -1 on error. + * + * Since: 9.0.0 + */ +int +virDomainFDAssociate(virDomainPtr domain, + const char *name, + unsigned int nfds, + int *fds, + unsigned int flags) +{ + virConnectPtr conn; + int rc; + + VIR_DOMAIN_DEBUG(domain, + "name='%s', nfds=%u, fds=%p, flags=0x%x", + name, nfds, fds, flags); + + virResetLastError(); + + conn = domain->conn; + + if ((rc = VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn, VIR_DRV_FEATURE_FD_PASSING)) < 0) + goto error; + + if (rc == 0) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("fd passing is not supported by this connection")); + goto error; + } + + virCheckNonZeroArgGoto(nfds, error); + virCheckNonNullArgGoto(fds, error); + virCheckReadOnlyGoto(conn->flags, error); + + if (!conn->driver->domainFDAssociate) { + virReportUnsupportedError(); + goto error; + } + + if ((rc = conn->driver->domainFDAssociate(domain, name, nfds, fds, flags)) < 0) + goto error; + + return rc; + + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 297a2c436a..80742f268e 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -927,4 +927,9 @@ LIBVIRT_8.5.0 { virDomainAbortJobFlags; } LIBVIRT_8.4.0; +LIBVIRT_9.0.0 { + global: + virDomainFDAssociate; +} LIBVIRT_8.5.0; + # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_daemon_dispatch.c b/src/remote/remote_daemon_dispatch.c index 7efe58b36b..40c734ce6b 100644 --- a/src/remote/remote_daemon_dispatch.c +++ b/src/remote/remote_daemon_dispatch.c @@ -7443,3 +7443,43 @@ remoteDispatchDomainGetMessages(virNetServer *server G_GNUC_UNUSED, return rv; } + + +static int +remoteDispatchDomainFdAssociate(virNetServer *server G_GNUC_UNUSED, + virNetServerClient *client, + virNetMessage *msg, + struct virNetMessageError *rerr, + remote_domain_fd_associate_args *args) +{ + virDomainPtr dom = NULL; + int *fds = NULL; + unsigned int nfds = 0; + int rv = -1; + virConnectPtr conn = remoteGetHypervisorConn(client); + size_t i; + + if (!conn) + goto cleanup; + + if (!(dom = get_nonnull_domain(conn, args->dom))) + goto cleanup; + + fds = g_new0(int, msg->nfds); + for (i = 0; i < msg->nfds; i++) { + if ((fds[i] = virNetMessageDupFD(msg, i)) < 0) + goto cleanup; + nfds++; + } + + if (virDomainFDAssociate(dom, args->name, nfds, fds, args->flags) < 0) + goto cleanup; + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(dom); + return rv; +} diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8fa9d20593..957635617d 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8198,6 +8198,32 @@ remoteDomainGetMessages(virDomainPtr domain, return rv; } + +static int +remoteDomainFDAssociate(virDomainPtr domain, + const char *name, + unsigned int nfds, + int *fds, + unsigned int flags) +{ + remote_domain_fd_associate_args args; + struct private_data *priv = domain->conn->privateData; + VIR_LOCK_GUARD lock = remoteDriverLock(priv); + + make_nonnull_domain(&args.dom, domain); + args.name = (char *)name; + args.flags = flags; + + if (callFull(domain->conn, priv, 0, fds, nfds, NULL, NULL, + REMOTE_PROC_DOMAIN_FD_ASSOCIATE, + (xdrproc_t) xdr_remote_domain_fd_associate_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + return -1; + + return 0; +} + + /* get_nonnull_domain and get_nonnull_network turn an on-wire * (name, uuid) pair into virDomainPtr or virNetworkPtr object. * These can return NULL if underlying memory allocations fail, @@ -8638,6 +8664,7 @@ static virHypervisorDriver hypervisor_driver = { .domainGetMessages = remoteDomainGetMessages, /* 7.1.0 */ .domainStartDirtyRateCalc = remoteDomainStartDirtyRateCalc, /* 7.2.0 */ .domainSetLaunchSecurityState = remoteDomainSetLaunchSecurityState, /* 8.0.0 */ + .domainFDAssociate = remoteDomainFDAssociate, /* 8.9.0 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 7dfb4548f4..c34d6f189d 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3929,6 +3929,12 @@ struct remote_domain_event_memory_device_size_change_msg { unsigned hyper size; }; + +struct remote_domain_fd_associate_args { + remote_nonnull_domain dom; + remote_nonnull_string name; + unsigned int flags; +}; /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6961,5 +6967,11 @@ enum remote_procedure { * @generate: both * @acl: domain:write */ - REMOTE_PROC_DOMAIN_ABORT_JOB_FLAGS = 442 + REMOTE_PROC_DOMAIN_ABORT_JOB_FLAGS = 442, + + /** + * @generate: none + * @acl: domain:write + */ + REMOTE_PROC_DOMAIN_FD_ASSOCIATE = 443 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index ca5222439d..3c6c230a16 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -3268,6 +3268,11 @@ struct remote_domain_event_memory_device_size_change_msg { remote_nonnull_string alias; uint64_t size; }; +struct remote_domain_fd_associate_args { + remote_nonnull_domain dom; + remote_nonnull_string name; + u_int flags; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3711,4 +3716,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SAVE_PARAMS = 440, REMOTE_PROC_DOMAIN_RESTORE_PARAMS = 441, REMOTE_PROC_DOMAIN_ABORT_JOB_FLAGS = 442, + REMOTE_PROC_DOMAIN_FD_ASSOCIATE = 443, };