[libvirt] [PATCH] domaincaps: Expose UEFI binary path, if it exists
Laszlo Ersek
lersek at redhat.com
Wed Sep 17 11:10:58 UTC 2014
Hi Cole,
I'm not subscribed to the list; please CC me on UEFI-related patches.
Michal pinged me to review this one. Some comments:
On 09/17/14 01:52, Cole Robinson wrote:
> Check to see if the UEFI binary mentioned in qemu.conf actually
> exists, and if so expose it in domcapabilities like
>
> <loader ...>
> <value>/path/to/ovmf</value>
> </loader>
>
> We introduce some generic domcaps infrastructure for handling
> a dynamic list of string values, it may be of use for future bits.
> ---
> docs/formatdomaincaps.html.in | 6 ++++
> docs/schemas/domaincaps.rng | 17 ++++++---
> src/conf/domain_capabilities.c | 23 ++++++++++++
> src/conf/domain_capabilities.h | 8 +++++
> src/qemu/qemu_driver.c | 24 +++++++++++++
> tests/domaincapsschemadata/domaincaps-full.xml | 1 +
> tests/domaincapstest.c | 49 +++++++++++++++++++++-----
> 7 files changed, 115 insertions(+), 13 deletions(-)
>
> diff --git a/docs/formatdomaincaps.html.in b/docs/formatdomaincaps.html.in
> index 34d746d..6959dfe 100644
> --- a/docs/formatdomaincaps.html.in
> +++ b/docs/formatdomaincaps.html.in
> @@ -105,6 +105,7 @@
> ...
> <os supported='yes'>
> <loader supported='yes'>
> + <value>/usr/share/OVMF/OVMF_CODE.fd</value>
> <enum name='type'>
> <value>rom</value>
> <value>pflash</value>
> @@ -122,6 +123,11 @@
> <p>For the <code>loader</code> element, the following can occur:</p>
>
> <dl>
> + <dt>value</dt>
> + <dd>List of known loader paths. Currently this is only used
> + to advertise known locations of OVMF binaries for qemu. Binaries
> + will only be listed if they actually exist on disk.</dd>
> +
> <dt>type</dt>
> <dd>Whether loader is a typical BIOS (<code>rom</code>) or
> an UEFI binary (<code>pflash</code>). This refers to
> diff --git a/docs/schemas/domaincaps.rng b/docs/schemas/domaincaps.rng
> index ad8d966..dfdb9b9 100644
> --- a/docs/schemas/domaincaps.rng
> +++ b/docs/schemas/domaincaps.rng
> @@ -46,6 +46,9 @@
>
> <define name='loader'>
> <element name='loader'>
> + <optional>
> + <ref name='value'/>
> + </optional>
> <ref name='supported'/>
> <ref name='enum'/>
> </element>
> @@ -85,6 +88,14 @@
> </element>
> </define>
>
> + <define name='value'>
> + <zeroOrMore>
> + <element name='value'>
> + <text/>
> + </element>
> + </zeroOrMore>
> + </define>
> +
> <define name='supported'>
> <attribute name='supported'>
> <choice>
> @@ -100,11 +111,7 @@
> <attribute name='name'>
> <text/>
> </attribute>
> - <zeroOrMore>
> - <element name='value'>
> - <text/>
> - </element>
> - </zeroOrMore>
> + <ref name='value'/>
> </element>
> </zeroOrMore>
> </define>
> diff --git a/src/conf/domain_capabilities.c b/src/conf/domain_capabilities.c
> index 5a3c8e7..44e422a 100644
> --- a/src/conf/domain_capabilities.c
> +++ b/src/conf/domain_capabilities.c
> @@ -46,6 +46,15 @@ static int virDomainCapsOnceInit(void)
>
> VIR_ONCE_GLOBAL_INIT(virDomainCaps)
>
> +static void
> +virDomainCapsValuesFree(virDomainCapsValuesPtr values)
> +{
> + size_t i;
> +
> + for (i = 0; i < values->nvalues; i++) {
> + VIR_FREE(values->values[i]);
> + }
> +}
>
> static void
> virDomainCapsDispose(void *obj)
> @@ -54,6 +63,8 @@ virDomainCapsDispose(void *obj)
>
> VIR_FREE(caps->path);
> VIR_FREE(caps->machine);
> +
> + virDomainCapsValuesFree(&caps->os.loader.values);
> }
(1) This leaks the caps->os.loader.values.values array. (Which is a
dynamically allocated array of pointers.) virDomainCapsValuesFree()
should VIR_FREE() it too.
>
>
> @@ -156,6 +167,17 @@ virDomainCapsEnumFormat(virBufferPtr buf,
> return ret;
> }
>
> +static void
> +virDomainCapsValuesFormat(virBufferPtr buf,
> + virDomainCapsValuesPtr values)
> +{
> + size_t i;
> +
> + for (i = 0; i < values->nvalues; i++) {
> + virBufferAsprintf(buf, "<value>%s</value>\n", values->values[i]);
> + }
> +}
(2) virBufferEscapeString() would probably useful here; the filename
shouldn't be plainly embedded into an XML fragment.
I'm not sure if we paid attention to this with the nvram patches... Hm
yes; I rechecked Michal's commits now, and they seem to use
virBufferEscapeString().
> +
> #define FORMAT_PROLOGUE(item) \
> do { \
> virBufferAsprintf(buf, "<" #item " supported='%s'%s\n", \
> @@ -185,6 +207,7 @@ virDomainCapsLoaderFormat(virBufferPtr buf,
> {
> FORMAT_PROLOGUE(loader);
>
> + virDomainCapsValuesFormat(buf, &loader->values);
> ENUM_PROCESS(loader, type, virDomainLoaderTypeToString);
> ENUM_PROCESS(loader, readonly, virTristateBoolTypeToString);
>
> diff --git a/src/conf/domain_capabilities.h b/src/conf/domain_capabilities.h
> index 768646b..3d5aaa3 100644
> --- a/src/conf/domain_capabilities.h
> +++ b/src/conf/domain_capabilities.h
> @@ -37,6 +37,13 @@ struct _virDomainCapsEnum {
> unsigned int values; /* Bitmask of values supported in the corresponding enum */
> };
>
> +typedef struct _virDomainCapsValues virDomainCapsValues;
> +typedef virDomainCapsValues *virDomainCapsValuesPtr;
> +struct _virDomainCapsValues {
> + char **values; /* raw string values */
> + size_t nvalues; /* number of strings */
> +};
> +
> typedef struct _virDomainCapsDevice virDomainCapsDevice;
> typedef virDomainCapsDevice *virDomainCapsDevicePtr;
> struct _virDomainCapsDevice {
> @@ -47,6 +54,7 @@ typedef struct _virDomainCapsLoader virDomainCapsLoader;
> typedef virDomainCapsLoader *virDomainCapsLoaderPtr;
> struct _virDomainCapsLoader {
> virDomainCapsDevice device;
> + virDomainCapsValues values;
> virDomainCapsEnum type; /* Info about virDomainLoader */
> virDomainCapsEnum readonly; /* Info about readonly:virTristateBool */
> };
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index 6008aeb..4dd9d14 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -17282,6 +17282,8 @@ qemuConnectGetDomainCapabilities(virConnectPtr conn,
> int virttype; /* virDomainVirtType */
> virDomainCapsPtr domCaps = NULL;
> int arch = virArchFromHost(); /* virArch */
> + virQEMUDriverConfigPtr cfg = NULL;
> + size_t i;
>
> virCheckFlags(0, ret);
>
> @@ -17356,8 +17358,30 @@ qemuConnectGetDomainCapabilities(virConnectPtr conn,
>
> virQEMUCapsFillDomainCaps(domCaps, qemuCaps);
>
> + cfg = virQEMUDriverGetConfig(driver);
> + for (i = 0; i < cfg->nloader; i++) {
> + char *filename = cfg->loader[i];
> +
> + if (access(filename, F_OK) == 0) {
> + if (VIR_REALLOC_N(domCaps->os.loader.values.values,
> + domCaps->os.loader.values.nvalues + 1) < 0) {
> + goto cleanup;
> + }
> + domCaps->os.loader.values.nvalues++;
> +
> + if (VIR_STRDUP(domCaps->os.loader.values.values[
> + domCaps->os.loader.values.nvalues - 1],
> + filename) < 0) {
> + goto cleanup;
> + }
> + } else {
> + VIR_DEBUG("loader filename=%s doesn't exist", filename);
> + }
> + }
> +
(3) I propose to simply preallocate "domCaps->os.loader.values.values"
with VIR_ALLOC_N(). There are several reasons:
- This saves you on a bunch of VIR_REALLOC_N() calls. Just preallocate
for cfg->nloader elements, and only populate (and bump "nvalues") only
for loader files that exist. A few extra, NULL filled, unused elements
at the end of the array won't hurt.
- More importantly, the patch as proposed will cause undefined behavior
when VIR_STRDUP() fails and we jump to the cleanup. Namely, at that
point you will have reallocated the array -- and VIR_REALLOC_N() does
*not* zero-fill -- and also incremented nvalues. This will result, on
the error path, the virDomainCapsValuesFree() function to VIR_FREE() a
pointer with indeterminate value.
If, however, you preallocate with VIR_ALLOC_N(), all entries will be
NULL, and VIR_FREE() can handle that. You might also want to consider
bumping nvalues *after* VIR_STRDUP() succeeds.
> ret = virDomainCapsFormat(domCaps);
> cleanup:
> + virObjectUnref(cfg);
> virObjectUnref(domCaps);
> virObjectUnref(qemuCaps);
> return ret;
> diff --git a/tests/domaincapsschemadata/domaincaps-full.xml b/tests/domaincapsschemadata/domaincaps-full.xml
> index 9722772..e7f41d7 100644
> --- a/tests/domaincapsschemadata/domaincaps-full.xml
> +++ b/tests/domaincapsschemadata/domaincaps-full.xml
> @@ -6,6 +6,7 @@
> <vcpu max='255'/>
> <os supported='yes'>
> <loader supported='yes'>
> + <value>/foo/test</value>
> <enum name='type'>
> <value>rom</value>
> <value>pflash</value>
> diff --git a/tests/domaincapstest.c b/tests/domaincapstest.c
> index f240643..ddc4d02 100644
> --- a/tests/domaincapstest.c
> +++ b/tests/domaincapstest.c
> @@ -28,16 +28,37 @@
>
> #define VIR_FROM_THIS VIR_FROM_NONE
>
> -typedef void (*virDomainCapsFill)(virDomainCapsPtr domCaps,
> - void *opaque);
> +typedef int (*virDomainCapsFill)(virDomainCapsPtr domCaps,
> + void *opaque);
>
> #define SET_ALL_BITS(x) \
> memset(&(x.values), 0xff, sizeof(x.values))
>
> -static void
> +static int
> +fillValues(virDomainCapsValuesPtr values)
> +{
> + int ret = -1;
> +
> + if (VIR_ALLOC_N(values->values, 1) < 0) {
> + goto cleanup;
> + }
> + values->nvalues = 1;
> +
> + if (VIR_STRDUP(values->values[0], "/foo/test") < 0) {
> + goto cleanup;
> + }
> +
> + ret = 0;
> + cleanup:
> + return ret;
> +}
For example, this seems to be safe, even if VIR_STRDUP() fails, because
you use VIR_ALLOC_N().
> +
> +static int
> fillAll(virDomainCapsPtr domCaps,
> void *opaque ATTRIBUTE_UNUSED)
> {
> + int ret = -1;
> +
> virDomainCapsOSPtr os = &domCaps->os;
> virDomainCapsLoaderPtr loader = &os->loader;
> virDomainCapsDeviceDiskPtr disk = &domCaps->disk;
> @@ -49,6 +70,9 @@ fillAll(virDomainCapsPtr domCaps,
> loader->device.supported = true;
> SET_ALL_BITS(loader->type);
> SET_ALL_BITS(loader->readonly);
> + if (fillValues(&loader->values) < 0) {
> + goto cleanup;
> + }
... I'll trust that the automatic dispose functions / destructors will
take care of not leaking anything...
>
> disk->device.supported = true;
> SET_ALL_BITS(disk->diskDevice);
> @@ -60,12 +84,16 @@ fillAll(virDomainCapsPtr domCaps,
> SET_ALL_BITS(hostdev->subsysType);
> SET_ALL_BITS(hostdev->capsType);
> SET_ALL_BITS(hostdev->pciBackend);
> +
> + ret = 0;
> + cleanup:
> + return ret;
> }
>
>
> #ifdef WITH_QEMU
> # include "testutilsqemu.h"
> -static void
> +static int
> fillQemuCaps(virDomainCapsPtr domCaps,
> void *opaque)
> {
> @@ -82,6 +110,8 @@ fillQemuCaps(virDomainCapsPtr domCaps,
> VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT,
> VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM,
> VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO);
> +
> + return 0;
> }
> #endif /* WITH_QEMU */
>
> @@ -94,16 +124,19 @@ buildVirDomainCaps(const char *emulatorbin,
> virDomainCapsFill fillFunc,
> void *opaque)
> {
> - virDomainCapsPtr domCaps;
> + virDomainCapsPtr domCaps, ret = NULL;
>
> if (!(domCaps = virDomainCapsNew(emulatorbin, machine, arch, type)))
> goto cleanup;
>
> - if (fillFunc)
> - fillFunc(domCaps, opaque);
> + if (fillFunc && fillFunc(domCaps, opaque) < 0) {
> + virObjectUnref(domCaps);
> + goto cleanup;
> + }
>
> + ret = domCaps;
> cleanup:
> - return domCaps;
> + return ret;
> }
>
> struct test_virDomainCapsFormatData {
>
These look okay to me. Please consider fixing (1) to (3).
Thanks,
Laszlo
More information about the libvir-list
mailing list