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(a)redhat.com>
Reviewed-by: Pavel Hrdina <phrdina(a)redhat.com>
---
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 295fd30c93..014cd2a1c4 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -6457,4 +6457,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,
};
--
2.38.1