[PATCH v2 00/17] Add support for passing FDs to access disk images

v2: - fixed uninitialized value usage when reading image header Peter Krempa (17): lib: Introduce virDomainFDAssociate API virsh: Introduce 'dom-fd-associate' for invoking virDomainFDAssociate() conf: storage_source: Introduce type for storing FDs associated for storage qemu: Implement qemuDomainFDAssociate qemuxml2argvtest: Add support for populating 'fds' in private data conf: Add 'fdgroup' attribute for 'file' disks qemu: domain: Introduce qemuDomainStartupCleanup conf: storage_source: Introduce virStorageSourceIsFD qemu: Prepare data for FD-passed disk image sources qemu: block: Add support for passing FDs of disk images secuirity: DAC: Don't relabel FD-passed virStorageSource images security: selinux: Handle security labelling of FD-passed images qemu: Prepare storage backing chain traversal code for FD passed images qemu: driver: Don't allow certain operations with FD-passed disks qemu: cgroup: Don't setup cgroups for FD-passed images qemu: Enable support for FD passed disk sources qemuxml2*test: Enable testing of disks with 'fdgroup' docs/formatdomain.rst | 8 ++ docs/manpages/virsh.rst | 19 +++ include/libvirt/libvirt-domain.h | 20 +++ src/conf/domain_conf.c | 2 + src/conf/domain_conf.h | 1 + src/conf/domain_postparse.c | 9 ++ src/conf/schemas/domaincommon.rng | 3 + src/conf/storage_source_conf.c | 57 ++++++++ src/conf/storage_source_conf.h | 27 ++++ src/driver-hypervisor.h | 8 ++ src/libvirt-domain.c | 80 +++++++++++ src/libvirt_private.syms | 2 + src/libvirt_public.syms | 5 + src/qemu/qemu_block.c | 31 ++++- src/qemu/qemu_cgroup.c | 4 +- src/qemu/qemu_command.c | 22 +++ src/qemu/qemu_domain.c | 129 ++++++++++++++++-- src/qemu/qemu_domain.h | 11 +- src/qemu/qemu_driver.c | 89 ++++++++++++ src/qemu/qemu_hotplug.c | 1 + src/qemu/qemu_process.c | 2 +- src/remote/remote_daemon_dispatch.c | 40 ++++++ src/remote/remote_driver.c | 27 ++++ src/remote/remote_protocol.x | 14 +- src/remote_protocol-structs | 6 + src/security/security_dac.c | 16 ++- src/security/security_selinux.c | 32 ++++- src/security/virt-aa-helper.c | 3 +- src/storage_file/storage_source.c | 15 ++ .../disk-source-fd.x86_64-latest.args | 49 +++++++ tests/qemuxml2argvdata/disk-source-fd.xml | 40 ++++++ tests/qemuxml2argvtest.c | 9 ++ .../disk-source-fd.x86_64-latest.xml | 52 +++++++ tests/qemuxml2xmltest.c | 2 + tests/testutilsqemu.c | 33 +++++ tests/testutilsqemu.h | 2 + tools/virsh-domain.c | 76 +++++++++++ 37 files changed, 923 insertions(+), 23 deletions(-) create mode 100644 tests/qemuxml2argvdata/disk-source-fd.x86_64-latest.args create mode 100644 tests/qemuxml2argvdata/disk-source-fd.xml create mode 100644 tests/qemuxml2xmloutdata/disk-source-fd.x86_64-latest.xml -- 2.38.1

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

Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- docs/manpages/virsh.rst | 19 +++++++++++ tools/virsh-domain.c | 76 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/docs/manpages/virsh.rst b/docs/manpages/virsh.rst index c85bc8151d..88b7fa1da8 100644 --- a/docs/manpages/virsh.rst +++ b/docs/manpages/virsh.rst @@ -5225,6 +5225,25 @@ If *--print-xml* is specified, the XML that would be used to change media is printed instead of changing the media. +dom-fd-associate +---------------- + +**Syntax:** + +:: + + dom-fd-associate domain --name FDGROUPNAME --pass-fds M,N,.... + [--seclabel-writable] [--seclabel-restore] + +Associate one or more fds described via *--pass-fds* argument to *domain* as +*--name*. The lifetime of the passed fd group is the same as the connection, thus +exitting virsh un-registers them afterwards. + +By default security labels are applied if needed but they are not restored after +use to avoid keeping them open unnecessarily. Best-effort security label restore +may be requested by using the *--seclabel-restore* flag. + + NODEDEV COMMANDS ================ diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 2d162cf8c0..3751b69e03 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -9816,6 +9816,76 @@ cmdDomSetLaunchSecState(vshControl * ctl, const vshCmd * cmd) return ret; } + +/* + * "dom-fd-associate" command + */ +static const vshCmdInfo info_dom_fd_associate[] = { + {.name = "help", + .data = N_("associate a FD with a domain") + }, + {.name = "desc", + .data = N_("associate a FD with a domain") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_dom_fd_associate[] = { + VIRSH_COMMON_OPT_DOMAIN_FULL(0), + {.name = "name", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .completer = virshCompleteEmpty, + .help = N_("name of the FD group") + }, + {.name = "pass-fds", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .completer = virshCompleteEmpty, + .help = N_("file descriptors N,M,... to associate") + }, + {.name = "seclabel-writable", + .type = VSH_OT_BOOL, + .help = N_("use seclabels allowing writes") + }, + {.name = "seclabel-restore", + .type = VSH_OT_BOOL, + .help = N_("try to restore security label after use if possible") + }, + {.name = NULL} +}; + +static bool +cmdDomFdAssociate(vshControl *ctl, const vshCmd *cmd) +{ + g_autoptr(virshDomain) dom = NULL; + const char *name = NULL; + unsigned int flags = 0; + g_autofree int *fds = NULL; + size_t nfds = 0; + + if (vshCommandOptBool(cmd, "seclabel-writable")) + flags |= VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE; + + if (vshCommandOptBool(cmd, "seclabel-restore")) + flags |= VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE; + + if (!(dom = virshCommandOptDomain(ctl, cmd, NULL))) + return false; + + if (vshCommandOptStringReq(ctl, cmd, "name", &name) < 0) + return false; + + if (virshFetchPassFdsList(ctl, cmd, &nfds, &fds) < 0) + return false; + + if (virDomainFDAssociate(dom, name, nfds, fds, flags) < 0) + return false; + + return true; +} + + /* * "qemu-monitor-command" command */ @@ -14417,5 +14487,11 @@ const vshCmdDef domManagementCmds[] = { .info = info_domdirtyrate_calc, .flags = 0 }, + {.name = "dom-fd-associate", + .handler = cmdDomFdAssociate, + .opts = opts_dom_fd_associate, + .info = info_dom_fd_associate, + .flags = 0 + }, {.name = NULL} }; -- 2.38.1

For FD-passing of disk sources we'll need to keep the FDs around. Introduce a data type helper based on a g_object so that we get reference counting. One instance will (due to security labelling) will need to be part of the virStorageSource struct thus it's declared in the storage_source_conf module. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_source_conf.c | 41 ++++++++++++++++++++++++++++++++++ src/conf/storage_source_conf.h | 17 ++++++++++++++ src/libvirt_private.syms | 1 + 3 files changed, 59 insertions(+) diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 6ab9ed6ac5..8e2a85968d 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -28,6 +28,7 @@ #include "virerror.h" #include "virlog.h" #include "virstring.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_STORAGE @@ -1361,3 +1362,43 @@ virStorageSourceInitiatorClear(virStorageSourceInitiatorDef *initiator) { VIR_FREE(initiator->iqn); } + +G_DEFINE_TYPE(virStorageSourceFDTuple, vir_storage_source_fd_tuple, G_TYPE_OBJECT); + +static void +vir_storage_source_fd_tuple_init(virStorageSourceFDTuple *fdt G_GNUC_UNUSED) +{ +} + + +static void +virStorageSourceFDTupleFinalize(GObject *object) +{ + virStorageSourceFDTuple *fdt = VIR_STORAGE_SOURCE_FD_TUPLE(object); + size_t i; + + if (!fdt) + return; + + for (i = 0; i < fdt->nfds; i++) + VIR_FORCE_CLOSE(fdt->fds[i]); + + g_free(fdt->fds); + G_OBJECT_CLASS(vir_storage_source_fd_tuple_parent_class)->finalize(object); +} + + +static void +vir_storage_source_fd_tuple_class_init(virStorageSourceFDTupleClass *klass) +{ + GObjectClass *obj = G_OBJECT_CLASS(klass); + + obj->finalize = virStorageSourceFDTupleFinalize; +} + + +virStorageSourceFDTuple * +virStorageSourceFDTupleNew(void) +{ + return g_object_new(vir_storage_source_fd_tuple_get_type(), NULL); +} diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index f2440cec6a..9cd1a0c137 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -258,6 +258,23 @@ struct _virStorageSourceSlice { }; +struct _virStorageSourceFDTuple { + GObject parent; + int *fds; + size_t nfds; + + bool writable; + bool tryRestoreLabel; + + /* connection this FD tuple is associated with for auto-closing */ + virConnect *conn; +}; +G_DECLARE_FINAL_TYPE(virStorageSourceFDTuple, vir_storage_source_fd_tuple, VIR, STORAGE_SOURCE_FD_TUPLE, GObject); + +virStorageSourceFDTuple * +virStorageSourceFDTupleNew(void); + + typedef struct _virStorageSource virStorageSource; /* Stores information related to a host resource. In the case of backing diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index b81c2cc7da..ef88e2b49f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1117,6 +1117,7 @@ virStorageSourceChainHasManagedPR; virStorageSourceChainHasNVMe; virStorageSourceClear; virStorageSourceCopy; +virStorageSourceFDTupleNew; virStorageSourceGetActualType; virStorageSourceGetSecurityLabelDef; virStorageSourceHasBacking; -- 2.38.1

Implement passing and storage of FDs for the qemu driver. The FD tuples are g_object instances stored in a per-domain hash table and are automatically removed once the connection is closed. In the future we can consider supporting also to not tie the lifetime of the passed FDs bound to the connection. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_domain.c | 2 ++ src/qemu/qemu_domain.h | 3 ++ src/qemu/qemu_driver.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 5c05032ce3..33a9145cc9 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1845,6 +1845,7 @@ qemuDomainObjPrivateFree(void *data) qemuDomainMasterKeyFree(priv); g_clear_pointer(&priv->blockjobs, g_hash_table_unref); + g_clear_pointer(&priv->fds, g_hash_table_unref); /* This should never be non-NULL if we get here, but just in case... */ if (priv->eventThread) { @@ -1872,6 +1873,7 @@ qemuDomainObjPrivateAlloc(void *opaque) return NULL; priv->blockjobs = virHashNew(virObjectUnref); + priv->fds = virHashNew(g_object_unref); /* agent commands block by default, user can choose different behavior */ priv->agentTimeout = VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK; diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 2f027fad87..1cba3fa394 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -253,6 +253,9 @@ struct _qemuDomainObjPrivate { pid_t schedCoreChildFD; GSList *threadContextAliases; /* List of IDs of thread-context objects */ + + /* named file descriptor groups associated with the VM */ + GHashTable *fds; }; #define QEMU_DOMAIN_PRIVATE(vm) \ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f4bd081f3c..e86ebd8330 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -20643,6 +20643,72 @@ qemuDomainStartDirtyRateCalc(virDomainPtr dom, } +static void +qemuDomainFDHashCloseConnect(virDomainObj *vm, + virConnectPtr conn) +{ + qemuDomainObjPrivate *priv = QEMU_DOMAIN_PRIVATE(vm); + virStorageSourceFDTuple *data; + GHashTableIter htitr; + + if (!priv->fds) + return; + + g_hash_table_iter_init(&htitr, priv->fds); + + while (g_hash_table_iter_next(&htitr, NULL, (void **) &data)) { + if (data->conn == conn) + g_hash_table_iter_remove(&htitr); + } +} + + +static int +qemuDomainFDAssociate(virDomainPtr domain, + const char *name, + unsigned int nfds, + int *fds, + unsigned int flags) +{ + virDomainObj *vm = NULL; + qemuDomainObjPrivate *priv; + virStorageSourceFDTuple *new; + int ret = -1; + + virCheckFlags(VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE | + VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE, -1); + + if (nfds == 0) + return 0; + + if (!(vm = qemuDomainObjFromDomain(domain))) + return -1; + + if (virDomainFdAssociateEnsureACL(domain->conn, vm->def)) + goto cleanup; + + priv = vm->privateData; + + new = virStorageSourceFDTupleNew(); + new->fds = fds; + new->nfds = nfds; + new->conn = domain->conn; + + new->writable = flags & VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_WRITABLE; + new->tryRestoreLabel = flags & VIR_DOMAIN_FD_ASSOCIATE_SECLABEL_RESTORE; + + virCloseCallbacksDomainAdd(vm, domain->conn, qemuDomainFDHashCloseConnect); + + g_hash_table_insert(priv->fds, g_strdup(name), new); + + ret = 0; + + cleanup: + virDomainObjEndAPI(&vm); + return ret; +} + + static virHypervisorDriver qemuHypervisorDriver = { .name = QEMU_DRIVER_NAME, .connectURIProbe = qemuConnectURIProbe, @@ -20891,6 +20957,7 @@ static virHypervisorDriver qemuHypervisorDriver = { .domainGetMessages = qemuDomainGetMessages, /* 7.1.0 */ .domainStartDirtyRateCalc = qemuDomainStartDirtyRateCalc, /* 7.2.0 */ .domainSetLaunchSecurityState = qemuDomainSetLaunchSecurityState, /* 8.0.0 */ + .domainFDAssociate = qemuDomainFDAssociate, /* 9.0.0 */ }; -- 2.38.1

Introduce a new argument type for testQemuInfoSetArgs named ARG_FD_GROUP which allows users to instantiate tests with populated FD passing hash table. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_source_conf.c | 1 + src/conf/storage_source_conf.h | 1 + tests/qemuxml2argvtest.c | 5 +++++ tests/testutilsqemu.c | 33 +++++++++++++++++++++++++++++++++ tests/testutilsqemu.h | 2 ++ 5 files changed, 42 insertions(+) diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 8e2a85968d..ad9ff36ff1 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -1384,6 +1384,7 @@ virStorageSourceFDTupleFinalize(GObject *object) VIR_FORCE_CLOSE(fdt->fds[i]); g_free(fdt->fds); + g_free(fdt->testfds); G_OBJECT_CLASS(vir_storage_source_fd_tuple_parent_class)->finalize(object); } diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 9cd1a0c137..7c99ac8976 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -262,6 +262,7 @@ struct _virStorageSourceFDTuple { GObject parent; int *fds; size_t nfds; + int *testfds; /* populated by tests to ensure stable FDs */ bool writable; bool tryRestoreLabel; diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2db0e90f2b..b4b60a0130 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -705,6 +705,11 @@ testCompareXMLToArgv(const void *data) } priv = vm->privateData; + if (info->args.fds) { + g_clear_pointer(&priv->fds, g_hash_table_unref); + priv->fds = g_steal_pointer(&info->args.fds); + } + if (virBitmapParse("0-3", &priv->autoNodeset, 4) < 0) goto cleanup; diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index 6d3decdc16..396803c40b 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -932,6 +932,38 @@ testQemuInfoSetArgs(struct testQemuInfo *info, info->args.hostOS = va_arg(argptr, int); break; + case ARG_FD_GROUP: { + virStorageSourceFDTuple *new = virStorageSourceFDTupleNew(); + const char *fdname = va_arg(argptr, char *); + VIR_AUTOCLOSE fakefd = open("/dev/zero", O_RDWR); + size_t i; + + new->nfds = va_arg(argptr, unsigned int); + new->fds = g_new0(int, new->nfds); + new->testfds = g_new0(int, new->nfds); + + for (i = 0; i < new->nfds; i++) { + new->testfds[i] = va_arg(argptr, unsigned int); + + if (fcntl(new->testfds[i], F_GETFD) != -1) { + fprintf(stderr, "fd '%d' is already in use\n", new->fds[i]); + abort(); + } + + if ((new->fds[i] = dup(fakefd)) < 0) { + fprintf(stderr, "failed to duplicate fake fd: %s", + g_strerror(errno)); + abort(); + } + } + + if (!info->args.fds) + info->args.fds = virHashNew(g_object_unref); + + g_hash_table_insert(info->args.fds, g_strdup(fdname), new); + break; + } + case ARG_END: default: info->args.invalidarg = true; @@ -1037,6 +1069,7 @@ testQemuInfoClear(struct testQemuInfo *info) VIR_FREE(info->errfile); virObjectUnref(info->qemuCaps); g_clear_pointer(&info->args.fakeCaps, virObjectUnref); + g_clear_pointer(&info->args.fds, g_hash_table_unref); } diff --git a/tests/testutilsqemu.h b/tests/testutilsqemu.h index 943958d02a..51c072cb13 100644 --- a/tests/testutilsqemu.h +++ b/tests/testutilsqemu.h @@ -52,6 +52,7 @@ typedef enum { ARG_CAPS_VER, ARG_CAPS_HOST_CPU_MODEL, ARG_HOST_OS, + ARG_FD_GROUP, /* name, nfds, fd[0], ... fd[n-1] */ ARG_END, } testQemuInfoArgName; @@ -87,6 +88,7 @@ struct testQemuArgs { qemuTestCPUDef capsHostCPUModel; int gic; testQemuHostOS hostOS; + GHashTable *fds; bool invalidarg; }; -- 2.38.1

The 'fdgroup' will allow users to specify a passed FD (via the 'virDomainFDAssociate()' API) to be used instead of opening a path. This is useful in cases when e.g. the file is not accessible from inside a container. Since this uses the same disk type as when we open files via names this patch also introduces a hypervisor feature which the hypervisor asserts that code paths are ready for this possibility. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- docs/formatdomain.rst | 8 +++++ src/conf/domain_conf.c | 2 ++ src/conf/domain_conf.h | 1 + src/conf/domain_postparse.c | 9 +++++ src/conf/schemas/domaincommon.rng | 3 ++ src/conf/storage_source_conf.c | 2 ++ src/conf/storage_source_conf.h | 1 + src/security/virt-aa-helper.c | 3 +- tests/qemuxml2argvdata/disk-source-fd.xml | 40 +++++++++++++++++++++++ 9 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/qemuxml2argvdata/disk-source-fd.xml diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index d7fffc6e0b..109a2ac45a 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2701,6 +2701,14 @@ paravirtualized driver is specified via the ``disk`` element. ``file`` The ``file`` attribute specifies the fully-qualified path to the file holding the disk. :since:`Since 0.0.3` + + :since:`Since 9.0.0` a new optional attribute ``fdgroup`` can be added + instructing to access the disk via file descriptiors associated to the + domain object via the ``virDomainFDAssociate()`` API rather than opening + the files. The files do not necessarily have to be accessible by libvirt + via the filesystem. The filename passed via ``file`` can still be used + to generate paths to write into image metadata when doing block operations + but libvirt will not access these natively. ``block`` The ``dev`` attribute specifies the fully-qualified path to the host device to serve as the disk. :since:`Since 0.0.3` diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d16a247a45..6d27229e99 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -7345,6 +7345,7 @@ virDomainStorageSourceParse(xmlNodePtr node, switch (src->type) { case VIR_STORAGE_TYPE_FILE: src->path = virXMLPropString(node, "file"); + src->fdgroup = virXMLPropString(node, "fdgroup"); break; case VIR_STORAGE_TYPE_BLOCK: src->path = virXMLPropString(node, "dev"); @@ -21877,6 +21878,7 @@ virDomainDiskSourceFormat(virBuffer *buf, switch (src->type) { case VIR_STORAGE_TYPE_FILE: virBufferEscapeString(&attrBuf, " file='%s'", src->path); + virBufferEscapeString(&attrBuf, " fdgroup='%s'", src->fdgroup); break; case VIR_STORAGE_TYPE_BLOCK: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 9e281692ff..c1f1fccf62 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3167,6 +3167,7 @@ typedef enum { VIR_DOMAIN_DEF_FEATURE_NO_BOOT_ORDER = (1 << 6), VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT = (1 << 7), VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING = (1 << 8), + VIR_DOMAIN_DEF_FEATURE_DISK_FD = (1 << 9), } virDomainDefFeatures; diff --git a/src/conf/domain_postparse.c b/src/conf/domain_postparse.c index 9a3e8f494c..d1f0b80338 100644 --- a/src/conf/domain_postparse.c +++ b/src/conf/domain_postparse.c @@ -885,6 +885,15 @@ virDomainDeviceDefPostParseCheckFeatures(virDomainDeviceDef *dev, return -1; } + if (dev->type == VIR_DOMAIN_DEVICE_DISK && + dev->data.disk->src->fdgroup && + UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_DISK_FD)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("driver does not support FD passing for disk '%s'"), + dev->data.disk->dst); + return -1; + } + return 0; } #undef UNSUPPORTED diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index c588a48fd2..ccc114beff 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -1806,6 +1806,9 @@ <ref name="vmwarePath"/> </choice> </attribute> + <optional> + <attribute name="fdgroup"/> + </optional> </optional> <ref name="diskSourceCommon"/> <optional> diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index ad9ff36ff1..4b1df19ab4 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -817,6 +817,7 @@ virStorageSourceCopy(const virStorageSource *src, def->drv = NULL; def->path = g_strdup(src->path); + def->fdgroup = g_strdup(src->fdgroup); def->volume = g_strdup(src->volume); def->relPath = g_strdup(src->relPath); def->backingStoreRaw = g_strdup(src->backingStoreRaw); @@ -1123,6 +1124,7 @@ virStorageSourceClear(virStorageSource *def) return; VIR_FREE(def->path); + VIR_FREE(def->fdgroup); VIR_FREE(def->volume); VIR_FREE(def->snapshot); VIR_FREE(def->configFile); diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 7c99ac8976..ef82104e6c 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -289,6 +289,7 @@ struct _virStorageSource { unsigned int id; /* backing chain identifier, 0 is unset */ virStorageType type; char *path; + char *fdgroup; /* name of group of file descriptors the user wishes to use instead of 'path' */ int protocol; /* virStorageNetProtocol */ char *volume; /* volume name for remote storage */ char *snapshot; /* for storage systems supporting internal snapshots */ diff --git a/src/security/virt-aa-helper.c b/src/security/virt-aa-helper.c index 53a1cd1048..c8db925094 100644 --- a/src/security/virt-aa-helper.c +++ b/src/security/virt-aa-helper.c @@ -607,7 +607,8 @@ virDomainDefParserConfig virAAHelperDomainDefParserConfig = { .features = VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG | VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN | VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS | - VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING, + VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING | + VIR_DOMAIN_DEF_FEATURE_DISK_FD, }; static int diff --git a/tests/qemuxml2argvdata/disk-source-fd.xml b/tests/qemuxml2argvdata/disk-source-fd.xml new file mode 100644 index 0000000000..d8c47fa364 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-source-fd.xml @@ -0,0 +1,40 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/path/to/blah' fdgroup='testgroup2'/> + <target dev='vde' bus='virtio'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071880' fdgroup='testgroup5'/> + <backingStore type='file'> + <format type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071877' fdgroup='testgroup6'/> + <backingStore type='file'> + <format type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071876'/> + <backingStore/> + </backingStore> + </backingStore> + <target dev='vdf' bus='virtio'/> + </disk> + <controller type='usb'/> + <controller type='pci' model='pci-root'/> + <memballoon model='virtio'/> + </devices> +</domain> -- 2.38.1

The new helper qemuDomainStartupCleanup is used to perform cleanup after a startup of a VM (successful or not). The initial implementation just calls qemuDomainSecretDestroy, which can be un-exported. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_domain.c | 15 ++++++++++++++- src/qemu/qemu_domain.h | 3 +-- src/qemu/qemu_process.c | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 33a9145cc9..1f288fa0cf 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1552,7 +1552,7 @@ qemuDomainSecretGraphicsPrepare(virQEMUDriverConfig *cfg, * * Removes all unnecessary data which was needed to generate 'secret' objects. */ -void +static void qemuDomainSecretDestroy(virDomainObj *vm) { size_t i; @@ -12279,3 +12279,16 @@ qemuDomainSchedCoreStop(qemuDomainObjPrivate *priv) priv->schedCoreChildPID = -1; } } + + +/** + * qemuDomainStartupCleanup: + * + * Performs a cleanup of data which is not required after a startup of a VM + * (successful or not). + */ +void +qemuDomainStartupCleanup(virDomainObj *vm) +{ + qemuDomainSecretDestroy(vm); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 1cba3fa394..057de1e974 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -932,8 +932,7 @@ int qemuDomainSecretChardevPrepare(virQEMUDriverConfig *cfg, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); -void qemuDomainSecretDestroy(virDomainObj *vm) - ATTRIBUTE_NONNULL(1); +void qemuDomainStartupCleanup(virDomainObj *vm); int qemuDomainSecretPrepare(virQEMUDriver *driver, virDomainObj *vm) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 49ae7b688b..d8b1ce3abe 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -7936,7 +7936,7 @@ qemuProcessLaunch(virConnectPtr conn, cleanup: qemuDomainSchedCoreStop(priv); - qemuDomainSecretDestroy(vm); + qemuDomainStartupCleanup(vm); return ret; } -- 2.38.1

The helper will be used in various places that need to check that a disk source struct is using FD passing. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_source_conf.c | 7 +++++++ src/conf/storage_source_conf.h | 3 +++ src/libvirt_private.syms | 1 + 3 files changed, 11 insertions(+) diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 4b1df19ab4..8b67e511e2 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -1057,6 +1057,13 @@ virStorageSourceIsLocalStorage(const virStorageSource *src) } +bool +virStorageSourceIsFD(const virStorageSource *src) +{ + return src->fdgroup; +} + + /** * virStorageSourceIsEmpty: * diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index ef82104e6c..9c07eef200 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -494,6 +494,9 @@ virStorageSourceGetActualType(const virStorageSource *def); bool virStorageSourceIsLocalStorage(const virStorageSource *src); +bool +virStorageSourceIsFD(const virStorageSource *src); + bool virStorageSourceIsEmpty(virStorageSource *src); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index ef88e2b49f..b4c6e6a09e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1129,6 +1129,7 @@ virStorageSourceInitiatorParseXML; virStorageSourceIsBacking; virStorageSourceIsBlockLocal; virStorageSourceIsEmpty; +virStorageSourceIsFD; virStorageSourceIsLocalStorage; virStorageSourceIsRelative; virStorageSourceIsSameLocation; -- 2.38.1

When starting up a VM with FD-passed images we need to look up the corresponding named FD set and associate it with the virStorageSource based on the name. The association is brought into virStorageSource as security labelling code will need to access the FD to perform selinux labelling. Similarly when startup is complete in certain cases we no longer need to keep the copy of FDs and thus can close them. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_source_conf.c | 5 ++ src/conf/storage_source_conf.h | 2 + src/qemu/qemu_domain.c | 86 ++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 5 ++ src/qemu/qemu_hotplug.c | 1 + 5 files changed, 99 insertions(+) diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 8b67e511e2..1c11eacb9d 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -886,6 +886,9 @@ virStorageSourceCopy(const virStorageSource *src, return NULL; } + if (src->fdtuple) + def->fdtuple = g_object_ref(src->fdtuple); + /* ssh config passthrough for libguestfs */ def->ssh_host_key_check_disabled = src->ssh_host_key_check_disabled; def->ssh_user = g_strdup(src->ssh_user); @@ -1170,6 +1173,8 @@ virStorageSourceClear(virStorageSource *def) virStorageSourceInitiatorClear(&def->initiator); + g_clear_pointer(&def->fdtuple, g_object_unref); + /* clear everything except the class header as the object APIs * will break otherwise */ memset((char *) def + sizeof(def->parent), 0, diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index 9c07eef200..f981261ff4 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -415,6 +415,8 @@ struct _virStorageSource { * registered with a full index (vda[3]) so that we can properly report just * one event for it */ bool thresholdEventWithIndex; + + virStorageSourceFDTuple *fdtuple; }; G_DEFINE_AUTOPTR_CLEANUP_FUNC(virStorageSource, virObjectUnref); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 1f288fa0cf..7dc4ef4ddb 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -850,6 +850,7 @@ qemuDomainStorageSourcePrivateDispose(void *obj) g_clear_pointer(&priv->encinfo, qemuDomainSecretInfoFree); g_clear_pointer(&priv->httpcookie, qemuDomainSecretInfoFree); g_clear_pointer(&priv->tlsKeySecret, qemuDomainSecretInfoFree); + g_clear_pointer(&priv->fdpass, qemuFDPassFree); } @@ -10892,6 +10893,61 @@ qemuDomainPrepareDiskSourceLegacy(virDomainDiskDef *disk, } +static int +qemuDomainPrepareStorageSourceFDs(virStorageSource *src, + qemuDomainObjPrivate *priv) +{ + qemuDomainStorageSourcePrivate *srcpriv = NULL; + virStorageType actualType = virStorageSourceGetActualType(src); + virStorageSourceFDTuple *fdt = NULL; + size_t i; + + if (actualType != VIR_STORAGE_TYPE_FILE && + actualType != VIR_STORAGE_TYPE_BLOCK) + return 0; + + if (!virStorageSourceIsFD(src)) + return 0; + + if (!(fdt = virHashLookup(priv->fds, src->fdgroup))) { + virReportError(VIR_ERR_INVALID_ARG, + _("file descriptor group '%s' was not associated with the domain"), + src->fdgroup); + return -1; + } + + srcpriv = qemuDomainStorageSourcePrivateFetch(src); + + srcpriv->fdpass = qemuFDPassNew(src->nodestorage, priv); + + for (i = 0; i < fdt->nfds; i++) { + g_autofree char *idx = g_strdup_printf("%zu", i); + int tmpfd; + + if (fdt->testfds) { + /* when testing we want to use stable FD numbers provided by the test + * case */ + tmpfd = dup2(fdt->fds[i], fdt->testfds[i]); + } else { + tmpfd = dup(fdt->fds[i]); + } + + if (tmpfd < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to duplicate file descriptor for fd group '%s'"), + src->fdgroup); + return -1; + } + + qemuFDPassAddFD(srcpriv->fdpass, &tmpfd, idx); + } + + src->fdtuple = g_object_ref(fdt); + + return 0; +} + + int qemuDomainPrepareStorageSourceBlockdevNodename(virDomainDiskDef *disk, virStorageSource *src, @@ -10929,6 +10985,9 @@ qemuDomainPrepareStorageSourceBlockdevNodename(virDomainDiskDef *disk, if (qemuDomainPrepareStorageSourceNFS(src) < 0) return -1; + if (qemuDomainPrepareStorageSourceFDs(src, priv) < 0) + return -1; + return 0; } @@ -12281,6 +12340,28 @@ qemuDomainSchedCoreStop(qemuDomainObjPrivate *priv) } +/** + * qemuDomainCleanupStorageSourceFD: + * @src: start of the chain to clear + * + * Cleans up the backing chain starting at @src of FD tuple structures for + * all FD-tuples which didn't request explicit relabelling and thus the struct + * is no longer needed. + */ +void +qemuDomainCleanupStorageSourceFD(virStorageSource *src) +{ + virStorageSource *n; + + for (n = src; virStorageSourceIsBacking(n); n = n->backingStore) { + if (virStorageSourceIsFD(n) && n->fdtuple) { + if (!n->fdtuple->tryRestoreLabel) + g_clear_pointer(&n->fdtuple, g_object_unref); + } + } +} + + /** * qemuDomainStartupCleanup: * @@ -12290,5 +12371,10 @@ qemuDomainSchedCoreStop(qemuDomainObjPrivate *priv) void qemuDomainStartupCleanup(virDomainObj *vm) { + size_t i; + qemuDomainSecretDestroy(vm); + + for (i = 0; i < vm->def->ndisks; i++) + qemuDomainCleanupStorageSourceFD(vm->def->disks[i]->src); } diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 057de1e974..add653d9db 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -305,6 +305,9 @@ struct _qemuDomainStorageSourcePrivate { /* key for decrypting TLS certificate */ qemuDomainSecretInfo *tlsKeySecret; + + /* file descriptors if user asks for FDs to be passed */ + qemuFDPass *fdpass; }; virObject *qemuDomainStorageSourcePrivateNew(void); @@ -932,6 +935,8 @@ int qemuDomainSecretChardevPrepare(virQEMUDriverConfig *cfg, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); +void qemuDomainCleanupStorageSourceFD(virStorageSource *src); + void qemuDomainStartupCleanup(virDomainObj *vm); int qemuDomainSecretPrepare(virQEMUDriver *driver, diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 6e300f547c..dba699a8a8 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1016,6 +1016,7 @@ qemuDomainAttachDeviceDiskLiveInternal(virQEMUDriver *driver, ignore_value(qemuHotplugRemoveManagedPR(vm, VIR_ASYNC_JOB_NONE)); } qemuDomainSecretDiskDestroy(disk); + qemuDomainCleanupStorageSourceFD(disk->src); return ret; } -- 2.38.1

Prepare the internal data for passing FDs instead of having qemu open the file internally. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_block.c | 31 ++++++++++++++++++++++++++++--- src/qemu/qemu_command.c | 22 ++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index 8a6f601b29..a672ad6f54 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -673,22 +673,47 @@ qemuBlockStorageSourceGetSshProps(virStorageSource *src) static virJSONValue * qemuBlockStorageSourceGetFileProps(virStorageSource *src, - bool onlytarget) + bool onlytarget, + virTristateBool *autoReadOnly, + virTristateBool *readOnly) { + const char *path = src->path; const char *iomode = NULL; const char *prManagerAlias = NULL; virJSONValue *ret = NULL; if (!onlytarget) { + qemuDomainStorageSourcePrivate *srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(src); + if (src->pr) prManagerAlias = src->pr->mgralias; if (src->iomode != VIR_DOMAIN_DISK_IO_DEFAULT) iomode = virDomainDiskIoTypeToString(src->iomode); + + if (srcpriv && srcpriv->fdpass) { + path = qemuFDPassGetPath(srcpriv->fdpass); + + /* when passing a FD to qemu via the /dev/fdset mechanism qemu + * fetches the appropriate FD from the fdset by checking that it has + * the correct accessmode. Now with 'auto-read-only' in effect qemu + * wants to use a read-only FD first. If the user didn't pass multiple + * FDs the feature will not work regardless, so we'll disable it. */ + if (src->fdtuple->nfds == 1) { + *autoReadOnly = VIR_TRISTATE_BOOL_ABSENT; + + /* now we setup the normal readonly flag. If user requested write + * access honour it */ + if (src->fdtuple->writable) + *readOnly = VIR_TRISTATE_BOOL_NO; + else + *readOnly = virTristateBoolFromBool(src->readonly); + } + } } ignore_value(virJSONValueObjectAdd(&ret, - "s:filename", src->path, + "s:filename", path, "S:aio", iomode, "S:pr-manager", prManagerAlias, NULL) < 0); @@ -818,7 +843,7 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src, driver = "file"; } - if (!(fileprops = qemuBlockStorageSourceGetFileProps(src, onlytarget))) + if (!(fileprops = qemuBlockStorageSourceGetFileProps(src, onlytarget, &aro, &ro))) return NULL; break; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index cd3222feac..9dac57c2f2 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -2146,6 +2146,25 @@ qemuBuildBlockStorageSourceAttachDataCommandline(virCommand *cmd, } +static int +qemuBuildDiskSourceCommandLineFDs(virCommand *cmd, + virDomainDiskDef *disk) +{ + virStorageSource *n; + + for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) { + qemuDomainStorageSourcePrivate *srcpriv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(n); + + if (!srcpriv || !srcpriv->fdpass) + continue; + + qemuFDPassTransferCommand(srcpriv->fdpass, cmd); + } + + return 0; +} + + static int qemuBuildDiskSourceCommandLine(virCommand *cmd, virDomainDiskDef *disk, @@ -2163,6 +2182,9 @@ qemuBuildDiskSourceCommandLine(virCommand *cmd, if (virStorageSourceIsEmpty(disk->src)) return 0; + if (qemuBuildDiskSourceCommandLineFDs(cmd, disk) < 0) + return -1; + if (!(data = qemuBuildStorageSourceChainAttachPrepareBlockdev(disk->src))) return -1; -- 2.38.1

DAC security label is irrelevant once you have the FD. Disable all labelling for such images. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/security/security_dac.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 917fcf76a3..4036a2c27a 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -881,6 +881,10 @@ virSecurityDACSetImageLabelInternal(virSecurityManager *mgr, if (!priv->dynamicOwnership) return 0; + /* Images passed via FD don't need DAC seclabel change */ + if (virStorageSourceIsFD(src)) + return 0; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); if (secdef && !secdef->relabel) return 0; @@ -992,6 +996,10 @@ virSecurityDACRestoreImageLabelSingle(virSecurityManager *mgr, if (src->readonly || src->shared) return 0; + /* Images passed via FD don't need DAC seclabel change */ + if (virStorageSourceIsFD(src)) + return 0; + secdef = virDomainDefGetSecurityLabelDef(def, SECURITY_DAC_NAME); if (secdef && !secdef->relabel) return 0; @@ -1112,10 +1120,14 @@ virSecurityDACMoveImageMetadata(virSecurityManager *mgr, if (!priv->dynamicOwnership) return 0; - if (src && virStorageSourceIsLocalStorage(src)) + if (src && + virStorageSourceIsLocalStorage(src) && + !virStorageSourceIsFD(src)) data.src = src->path; - if (dst && virStorageSourceIsLocalStorage(dst)) + if (dst && + virStorageSourceIsLocalStorage(dst) && + !virStorageSourceIsFD(dst)) data.dst = dst->path; if (!data.src) -- 2.38.1

Unfortunately unlike with DAC we can't simply ignore labelling for the FD and it also influences the on-disk state. Thus we need to relabel the FD and we also store the existing label in cases when the user will request best-effort label replacement. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/conf/storage_source_conf.c | 1 + src/conf/storage_source_conf.h | 3 +++ src/security/security_selinux.c | 32 +++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c index 1c11eacb9d..cecd7e811e 100644 --- a/src/conf/storage_source_conf.c +++ b/src/conf/storage_source_conf.c @@ -1399,6 +1399,7 @@ virStorageSourceFDTupleFinalize(GObject *object) g_free(fdt->fds); g_free(fdt->testfds); + g_free(fdt->selinuxLabel); G_OBJECT_CLASS(vir_storage_source_fd_tuple_parent_class)->finalize(object); } diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h index f981261ff4..14a6825d54 100644 --- a/src/conf/storage_source_conf.h +++ b/src/conf/storage_source_conf.h @@ -269,6 +269,9 @@ struct _virStorageSourceFDTuple { /* connection this FD tuple is associated with for auto-closing */ virConnect *conn; + + /* original selinux label when we relabel the image */ + char *selinuxLabel; }; G_DECLARE_FINAL_TYPE(virStorageSourceFDTuple, vir_storage_source_fd_tuple, VIR, STORAGE_SOURCE_FD_TUPLE, GObject); diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 93cc12407a..a42d86216a 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1741,6 +1741,19 @@ virSecuritySELinuxRestoreImageLabelSingle(virSecurityManager *mgr, if (src->readonly || src->shared) return 0; + if (virStorageSourceIsFD(src)) { + if (migrated) + return 0; + + if (!src->fdtuple || + !src->fdtuple->selinuxLabel || + src->fdtuple->nfds == 0) + return 0; + + ignore_value(virSecuritySELinuxFSetFilecon(src->fdtuple->fds[0], + src->fdtuple->selinuxLabel)); + return 0; + } /* If we have a shared FS and are doing migration, we must not change * ownership, because that kills access on the destination host which is @@ -1888,7 +1901,24 @@ virSecuritySELinuxSetImageLabelInternal(virSecurityManager *mgr, path = vfioGroupDev; } - ret = virSecuritySELinuxSetFilecon(mgr, path, use_label, remember); + if (virStorageSourceIsFD(src)) { + /* We can only really do labelling when we have the FD as the path + * may not be accessible for us */ + if (!src->fdtuple || src->fdtuple->nfds == 0) + return 0; + + /* force a writable label for the image if requested */ + if (src->fdtuple->writable && secdef->imagelabel) + use_label = secdef->imagelabel; + + /* store the existing selinux label for the image */ + if (!src->fdtuple->selinuxLabel) + fgetfilecon_raw(src->fdtuple->fds[0], &src->fdtuple->selinuxLabel); + + ret = virSecuritySELinuxFSetFilecon(src->fdtuple->fds[0], use_label); + } else { + ret = virSecuritySELinuxSetFilecon(mgr, path, use_label, remember); + } if (ret == 1 && !disk_seclabel) { /* If we failed to set a label, but virt_use_nfs let us -- 2.38.1

We assume that FD passed images already exist so all existance checks are skipped. For the case that a FD-passed image is passed without a terminated backing chain (thus forcing us to detect) we attempt to read the header from the FD. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- src/qemu/qemu_domain.c | 23 ++++++++++++++--------- src/storage_file/storage_source.c | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 7dc4ef4ddb..38883a57d8 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -7679,16 +7679,20 @@ qemuDomainDetermineDiskChain(virQEMUDriver *driver, disksrc->format > VIR_STORAGE_FILE_NONE && disksrc->format < VIR_STORAGE_FILE_BACKING) { + /* terminate the chain for such images as the code below would do */ + if (!disksrc->backingStore) + disksrc->backingStore = virStorageSourceNew(); + + /* we assume that FD-passed disks always exist */ + if (virStorageSourceIsFD(disksrc)) + return 0; + if (!virFileExists(disksrc->path)) { virStorageSourceReportBrokenChain(errno, disksrc, disksrc); return -1; } - /* terminate the chain for such images as the code below would do */ - if (!disksrc->backingStore) - disksrc->backingStore = virStorageSourceNew(); - /* host cdrom requires special treatment in qemu, so we need to check * whether a block device is a cdrom */ if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM && @@ -7700,12 +7704,14 @@ qemuDomainDetermineDiskChain(virQEMUDriver *driver, return 0; } - src = disksrc; /* skip to the end of the chain if there is any */ - while (virStorageSourceHasBacking(src)) { - int rv = virStorageSourceSupportsAccess(src); + for (src = disksrc; virStorageSourceHasBacking(src); src = src->backingStore) { + int rv; + + if (virStorageSourceIsFD(src)) + continue; - if (rv < 0) + if ((rv = virStorageSourceSupportsAccess(src)) < 0) return -1; if (rv > 0) { @@ -7720,7 +7726,6 @@ qemuDomainDetermineDiskChain(virQEMUDriver *driver, virStorageSourceDeinit(src); } - src = src->backingStore; } /* We skipped to the end of the chain. Skip detection if there's the diff --git a/src/storage_file/storage_source.c b/src/storage_file/storage_source.c index ab0cdf2b12..7954b255a6 100644 --- a/src/storage_file/storage_source.c +++ b/src/storage_file/storage_source.c @@ -1264,6 +1264,21 @@ virStorageSourceGetMetadataRecurseReadHeader(virStorageSource *src, int ret = -1; ssize_t len; + if (virStorageSourceIsFD(src)) { + if (!src->fdtuple) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("fd passed image source not initialized")); + return -1; + } + + if ((len = virFileReadHeaderFD(src->fdtuple->fds[0], + VIR_STORAGE_MAX_HEADER, buf)) < 0) + return -1; + + *headerLen = len; + return 0; + } + if (virStorageSourceInitAs(src, uid, gid) < 0) return -1; -- 2.38.1

Probing stats and block copy to a FD passed image is not yet supported. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_driver.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e86ebd8330..218704c0ad 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10499,6 +10499,13 @@ qemuDomainBlockPeek(virDomainPtr dom, goto cleanup; } + if (virStorageSourceIsFD(disk->src)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("peeking is not supported for FD passed images")); + goto cleanup; + + } + if (qemuDomainStorageFileInit(driver, vm, disk->src, NULL) < 0) goto cleanup; @@ -10858,6 +10865,12 @@ qemuDomainGetBlockInfo(virDomainPtr dom, goto endjob; } + if (virStorageSourceIsFD(disk->src)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("block info is not supported for FD passed disk image")); + goto endjob; + } + /* for inactive domains we have to peek into the files */ if (!virDomainObjIsActive(vm)) { if ((qemuStorageLimitsRefresh(driver, cfg, vm, disk->src, false)) < 0) @@ -14690,6 +14703,12 @@ qemuDomainBlockCopyCommon(virDomainObj *vm, if (!qemuDomainDiskBlockJobIsSupported(disk)) goto endjob; + if (virStorageSourceIsFD(mirror)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("copy to a FD passed disk source is not yet supported")); + goto endjob; + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN && virDomainDiskDefSourceLUNValidate(mirror) < 0) goto endjob; @@ -17943,6 +17962,9 @@ qemuDomainGetStatsOneBlockFallback(virQEMUDriver *driver, if (virStorageSourceIsEmpty(src)) return 0; + if (virStorageSourceIsFD(src)) + return 0; + if (qemuStorageLimitsRefresh(driver, cfg, dom, src, true) <= 0) { virResetLastError(); return 0; -- 2.38.1

Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_cgroup.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index 9cf2d6474a..aac7c70054 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -206,7 +206,9 @@ qemuSetupImageCgroupInternal(virDomainObj *vm, if (qemuSetupImagePathCgroup(vm, QEMU_DEV_VFIO, false) < 0) return -1; } else { - if (!src->path || !virStorageSourceIsLocalStorage(src)) { + if (!src->path || + !virStorageSourceIsLocalStorage(src) || + virStorageSourceIsFD(src)) { VIR_DEBUG("Not updating cgroups for disk path '%s', type: %s", NULLSTR(src->path), virStorageTypeToString(src->type)); return 0; -- 2.38.1

Assert support for VIR_DOMAIN_DEF_FEATURE_DISK_FD in the qemu driver now that all code paths are adapted. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- src/qemu/qemu_domain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 38883a57d8..b341f43b56 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -6002,7 +6002,8 @@ virDomainDefParserConfig virQEMUDriverDomainDefParserConfig = { VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS | VIR_DOMAIN_DEF_FEATURE_USER_ALIAS | VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT | - VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING, + VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING | + VIR_DOMAIN_DEF_FEATURE_DISK_FD, }; -- 2.38.1

Enable the qemuxml2xml variant and add output data for qemuxml2argvtest. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com> --- .../disk-source-fd.x86_64-latest.args | 49 +++++++++++++++++ tests/qemuxml2argvtest.c | 4 ++ .../disk-source-fd.x86_64-latest.xml | 52 +++++++++++++++++++ tests/qemuxml2xmltest.c | 2 + 4 files changed, 107 insertions(+) create mode 100644 tests/qemuxml2argvdata/disk-source-fd.x86_64-latest.args create mode 100644 tests/qemuxml2xmloutdata/disk-source-fd.x86_64-latest.xml diff --git a/tests/qemuxml2argvdata/disk-source-fd.x86_64-latest.args b/tests/qemuxml2argvdata/disk-source-fd.x86_64-latest.args new file mode 100644 index 0000000000..b4a81acfc7 --- /dev/null +++ b/tests/qemuxml2argvdata/disk-source-fd.x86_64-latest.args @@ -0,0 +1,49 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ +/usr/bin/qemu-system-x86_64 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object '{"qom-type":"secret","id":"masterKey0","format":"raw","file":"/tmp/lib/domain--1-QEMUGuest1/master-key.aes"}' \ +-machine pc,usb=off,dump-guest-core=off,memory-backend=pc.ram \ +-accel tcg \ +-cpu qemu64 \ +-m 214 \ +-object '{"qom-type":"memory-backend-ram","id":"pc.ram","size":224395264}' \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server=on,wait=off \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-boot strict=on \ +-device '{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}' \ +-add-fd set=2,fd=700,opaque=libvirt-4-storage0 \ +-add-fd set=2,fd=705,opaque=libvirt-4-storage1 \ +-blockdev '{"driver":"file","filename":"/dev/fdset/2","node-name":"libvirt-4-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-4-format","read-only":false,"driver":"qcow2","file":"libvirt-4-storage"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x2","drive":"libvirt-4-format","id":"virtio-disk4","bootindex":1}' \ +-add-fd set=0,fd=704,opaque=libvirt-1-storage0 \ +-add-fd set=1,fd=777,opaque=libvirt-2-storage0 \ +-add-fd set=1,fd=778,opaque=libvirt-2-storage1 \ +-blockdev '{"driver":"file","filename":"/var/lib/libvirt/images/rhel7.1484071876","node-name":"libvirt-3-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-3-format","read-only":true,"driver":"qcow2","file":"libvirt-3-storage","backing":null}' \ +-blockdev '{"driver":"file","filename":"/dev/fdset/1","node-name":"libvirt-2-storage","auto-read-only":true,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-2-format","read-only":true,"driver":"qcow2","file":"libvirt-2-storage","backing":"libvirt-3-format"}' \ +-blockdev '{"driver":"file","filename":"/dev/fdset/0","node-name":"libvirt-1-storage","read-only":false,"discard":"unmap"}' \ +-blockdev '{"node-name":"libvirt-1-format","read-only":false,"driver":"qcow2","file":"libvirt-1-storage","backing":"libvirt-2-format"}' \ +-device '{"driver":"virtio-blk-pci","bus":"pci.0","addr":"0x3","drive":"libvirt-1-format","id":"virtio-disk5"}' \ +-audiodev '{"id":"audio1","driver":"none"}' \ +-device '{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x4"}' \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index b4b60a0130..6e027cf0bb 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1344,6 +1344,10 @@ mymain(void) DO_TEST_CAPS_LATEST_PARSE_ERROR("disk-scsi-incompatible-address"); DO_TEST_CAPS_LATEST("disk-backing-chains-index"); DO_TEST_CAPS_LATEST("disk-backing-chains-noindex"); + DO_TEST_CAPS_ARCH_LATEST_FULL("disk-source-fd", "x86_64", + ARG_FD_GROUP, "testgroup2", 2, 700, 705, + ARG_FD_GROUP, "testgroup5", 1, 704, + ARG_FD_GROUP, "testgroup6", 2, 777, 778); DO_TEST_CAPS_LATEST("disk-slices"); DO_TEST_CAPS_LATEST("disk-rotation"); diff --git a/tests/qemuxml2xmloutdata/disk-source-fd.x86_64-latest.xml b/tests/qemuxml2xmloutdata/disk-source-fd.x86_64-latest.xml new file mode 100644 index 0000000000..9ab5e9443f --- /dev/null +++ b/tests/qemuxml2xmloutdata/disk-source-fd.x86_64-latest.xml @@ -0,0 +1,52 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>1</vcpu> + <os> + <type arch='x86_64' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <cpu mode='custom' match='exact' check='none'> + <model fallback='forbid'>qemu64</model> + </cpu> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu-system-x86_64</emulator> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/path/to/blah' fdgroup='testgroup2'/> + <target dev='vde' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0'/> + </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071880' fdgroup='testgroup5'/> + <backingStore type='file'> + <format type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071877' fdgroup='testgroup6'/> + <backingStore type='file'> + <format type='qcow2'/> + <source file='/var/lib/libvirt/images/rhel7.1484071876'/> + <backingStore/> + </backingStore> + </backingStore> + <target dev='vdf' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/> + </disk> + <controller type='usb' index='0' model='piix3-uhci'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/> + </controller> + <controller type='pci' index='0' model='pci-root'/> + <input type='mouse' bus='ps2'/> + <input type='keyboard' bus='ps2'/> + <audio id='1' type='none'/> + <memballoon model='virtio'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </memballoon> + </devices> +</domain> diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 8d885d6d9f..5ef3645255 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -909,6 +909,8 @@ mymain(void) DO_TEST_NOCAPS("disk-backing-chains-index"); DO_TEST_NOCAPS("disk-backing-chains-noindex"); + DO_TEST_CAPS_LATEST("disk-source-fd"); + DO_TEST_CAPS_LATEST("disk-network-http"); DO_TEST("chardev-label", -- 2.38.1

On Mon, Jan 09, 2023 at 01:18:38PM +0100, Peter Krempa wrote:
v2: - fixed uninitialized value usage when reading image header
Peter Krempa (17): lib: Introduce virDomainFDAssociate API virsh: Introduce 'dom-fd-associate' for invoking virDomainFDAssociate() conf: storage_source: Introduce type for storing FDs associated for storage qemu: Implement qemuDomainFDAssociate qemuxml2argvtest: Add support for populating 'fds' in private data conf: Add 'fdgroup' attribute for 'file' disks qemu: domain: Introduce qemuDomainStartupCleanup conf: storage_source: Introduce virStorageSourceIsFD qemu: Prepare data for FD-passed disk image sources qemu: block: Add support for passing FDs of disk images secuirity: DAC: Don't relabel FD-passed virStorageSource images security: selinux: Handle security labelling of FD-passed images qemu: Prepare storage backing chain traversal code for FD passed images qemu: driver: Don't allow certain operations with FD-passed disks qemu: cgroup: Don't setup cgroups for FD-passed images qemu: Enable support for FD passed disk sources qemuxml2*test: Enable testing of disks with 'fdgroup'
Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
participants (2)
-
Pavel Hrdina
-
Peter Krempa