[PATCH 20/36] lib: Introduce virDomainFDAssociate API

Peter Krempa pkrempa at redhat.com
Thu Jan 5 16:30:09 UTC 2023


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 <pkrempa at redhat.com>
---
 include/libvirt/libvirt-domain.h    | 22 ++++++++
 src/driver-hypervisor.h             |  8 +++
 src/libvirt-domain.c                | 82 +++++++++++++++++++++++++++++
 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, 203 insertions(+), 1 deletion(-)

diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index 295fd30c93..a1e39f2f70 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -6457,4 +6457,26 @@ 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),
+    /* Require mandatory restore of security labels after use (Since: 9.0.0) */
+    VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE_REQUIRE = (1 << 1),
+    /* 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 << 2),
+} 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..12056d7ce2 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -13972,3 +13972,85 @@ 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, and
+ * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE_REQUIRE to request a mandatory
+ * restore in all cases. Note that hypervisors currently don't support the
+ * VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE_REQUIRE flag. 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 7946e9d9bd..2c7c17c6c9 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -8206,6 +8206,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,
@@ -8646,6 +8672,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,
 };
-- 
2.38.1



More information about the libvir-list mailing list