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 <pkrempa@redhat.com>
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
This commit is contained in:
Peter Krempa 2022-02-24 17:01:40 +01:00
parent 3ebfeaa206
commit abd9025c2f
8 changed files with 199 additions and 1 deletions

View File

@ -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 */

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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 ....

View File

@ -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;
}

View File

@ -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 = {

View File

@ -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
};

View File

@ -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,
};