Implement the parsing and formatting of the XML addition of the previous commit. The new XML doesn't affect qemu command line, so we can now test round-trip XML->memory->XML handling. I chose to reuse the existing structure, even though per-device override doesn't use all of those fields, rather than create a new structure, in order to reuse more code. * src/conf/domain_conf.h (_virDomainDiskDef): Add seclabel member. * src/conf/domain_conf.c (virDomainDiskDefFree): Free it. (virSecurityLabelDefFree): New function. (virDomainDiskDefFormat): Print it. (virSecurityLabelDefFormat): Reduce output if model not present. (virDomainDiskDefParseXML): Alter signature, and parse seclabel. (virSecurityLabelDefParseXML): Split... (virSecurityLabelDefParseXMLHelper): ...into new helper. (virDomainDeviceDefParse, virDomainDefParseXML): Update callers. * tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.args: New file. * tests/qemuxml2xmltest.c (mymain): Enhance test. * tests/qemuxml2argvtest.c (mymain): Likewise. --- src/conf/domain_conf.c | 178 +++++++++++++++----- src/conf/domain_conf.h | 1 + .../qemuxml2argv-seclabel-dynamic-override.args | 5 + tests/qemuxml2argvtest.c | 1 + tests/qemuxml2xmltest.c | 1 + 5 files changed, 142 insertions(+), 44 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.args diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 41db117..dcead7f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -797,6 +797,15 @@ virSecurityLabelDefClear(virSecurityLabelDefPtr def) VIR_FREE(def->baselabel); } +static void +virSecurityLabelDefFree(virSecurityLabelDefPtr def) +{ + if (!def) + return; + virSecurityLabelDefClear(def); + VIR_FREE(def); +} + void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { int ii; @@ -866,6 +875,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def) VIR_FREE(def->serial); VIR_FREE(def->src); + virSecurityLabelDefFree(def->seclabel); VIR_FREE(def->dst); VIR_FREE(def->driverName); VIR_FREE(def->driverType); @@ -2517,31 +2527,32 @@ virDomainDiskDefAssignAddress(virCapsPtr caps, virDomainDiskDefPtr def) return 0; } +/* Parse the portion of a SecurityLabel that is common to both the + * top-level <seclabel> and to a per-device override. + * default_seclabel is NULL for top-level, or points to the top-level + * when parsing an override. */ static int -virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, - xmlXPathContextPtr ctxt, - unsigned int flags) +virSecurityLabelDefParseXMLHelper(virSecurityLabelDefPtr def, + xmlNodePtr node, + xmlXPathContextPtr ctxt, + virSecurityLabelDefPtr default_seclabel, + unsigned int flags) { char *p; + xmlNodePtr save_ctxt = ctxt->node; + int ret = -1; - if (virXPathNode("./seclabel", ctxt) == NULL) - return 0; + ctxt->node = node; - p = virXPathStringLimit("string(./seclabel/@type)", - VIR_SECURITY_LABEL_BUFLEN-1, ctxt); - if (p == NULL) { - virDomainReportError(VIR_ERR_XML_ERROR, - "%s", _("missing security type")); - goto error; - } - def->type = virDomainSeclabelTypeFromString(p); - VIR_FREE(p); - if (def->type < 0) { - virDomainReportError(VIR_ERR_XML_ERROR, - "%s", _("invalid security type")); - goto error; + /* Can't use overrides if top-level doesn't allow relabeling. */ + if (default_seclabel && default_seclabel->norelabel) { + virDomainReportError(VIR_ERR_XML_ERROR, "%s", + _("label overrides require relabeling to be " + "enabled at the domain level")); + goto cleanup; } - p = virXPathStringLimit("string(./seclabel/@relabel)", + + p = virXPathStringLimit("string(./@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p != NULL) { if (STREQ(p, "yes")) { @@ -2552,38 +2563,76 @@ virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, virDomainReportError(VIR_ERR_XML_ERROR, _("invalid security relabel value %s"), p); VIR_FREE(p); - goto error; + goto cleanup; } VIR_FREE(p); - if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + if (!default_seclabel && + def->type == VIR_DOMAIN_SECLABEL_DYNAMIC && def->norelabel) { virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("dynamic label type must use resource relabeling")); - goto error; + goto cleanup; } } else { - if (def->type == VIR_DOMAIN_SECLABEL_STATIC) + if (!default_seclabel && def->type == VIR_DOMAIN_SECLABEL_STATIC) def->norelabel = true; else def->norelabel = false; } /* Only parse label, if using static labels, or - * if the 'live' VM XML is requested + * if the 'live' VM XML is requested, or if this is a device override */ if (def->type == VIR_DOMAIN_SECLABEL_STATIC || - !(flags & VIR_DOMAIN_XML_INACTIVE)) { - p = virXPathStringLimit("string(./seclabel/label[1])", + !(flags & VIR_DOMAIN_XML_INACTIVE) || + (default_seclabel && !def->norelabel)) { + p = virXPathStringLimit("string(./label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virDomainReportError(VIR_ERR_XML_ERROR, "%s", _("security label is missing")); - goto error; + goto cleanup; } def->label = p; } + ret = 0; +cleanup: + ctxt->node = save_ctxt; + return ret; +} + +/* Parse the top-level <seclabel>, if present. */ +static int +virSecurityLabelDefParseXML(virSecurityLabelDefPtr def, + xmlXPathContextPtr ctxt, + unsigned int flags) +{ + char *p; + xmlNodePtr node = virXPathNode("./seclabel", ctxt); + + if (node == NULL) + return 0; + + p = virXPathStringLimit("string(./seclabel/@type)", + VIR_SECURITY_LABEL_BUFLEN-1, ctxt); + if (p == NULL) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s", _("missing security type")); + goto error; + } + def->type = virDomainSeclabelTypeFromString(p); + VIR_FREE(p); + if (def->type < 0) { + virDomainReportError(VIR_ERR_XML_ERROR, + "%s", _("invalid security type")); + goto error; + } + + if (virSecurityLabelDefParseXMLHelper(def, node, ctxt, NULL, flags) < 0) + goto error; + /* Only parse imagelabel, if requested live XML with relabeling */ if (!def->norelabel && !(flags & VIR_DOMAIN_XML_INACTIVE)) { @@ -2709,6 +2758,7 @@ virDomainDiskDefParseXML(virCapsPtr caps, xmlNodePtr node, xmlXPathContextPtr ctxt, virBitmapPtr bootMap, + virSecurityLabelDefPtr default_seclabel, unsigned int flags) { virDomainDiskDefPtr def; @@ -3016,6 +3066,16 @@ virDomainDiskDefParseXML(virCapsPtr caps, goto error; } + /* If source is present, check for an optional seclabel override. */ + if (source) { + xmlNodePtr seclabel = virXPathNode("./source/seclabel", ctxt); + if (seclabel && + (VIR_ALLOC(def->seclabel) < 0 || + virSecurityLabelDefParseXMLHelper(def->seclabel, seclabel, ctxt, + default_seclabel, flags) < 0)) + goto error; + } + if (target == NULL) { virDomainReportError(VIR_ERR_NO_TARGET, source ? "%s" : NULL, source); @@ -6344,7 +6404,8 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, if (xmlStrEqual(node->name, BAD_CAST "disk")) { dev->type = VIR_DOMAIN_DEVICE_DISK; if (!(dev->data.disk = virDomainDiskDefParseXML(caps, node, ctxt, - NULL, flags))) + NULL, &def->seclabel, + flags))) goto error; } else if (xmlStrEqual(node->name, BAD_CAST "lease")) { dev->type = VIR_DOMAIN_DEVICE_LEASE; @@ -7446,6 +7507,7 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, nodes[i], ctxt, bootMap, + &def->seclabel, flags); if (!disk) goto error; @@ -9749,23 +9811,32 @@ virSecurityLabelDefFormat(virBufferPtr buf, virSecurityLabelDefPtr def, if (!sectype) goto cleanup; - if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC && + if (def->model && + def->type == VIR_DOMAIN_SECLABEL_DYNAMIC && !def->baselabel && (flags & VIR_DOMAIN_XML_INACTIVE)) { /* This is the default for inactive xml, so nothing to output. */ } else { - virBufferAsprintf(buf, "<seclabel type='%s' model='%s' relabel='%s'>\n", - sectype, def->model, + virBufferAddLit(buf, "<seclabel"); + if (def->model) + virBufferAsprintf(buf, " type='%s' model='%s'", + sectype, def->model); + virBufferAsprintf(buf, " relabel='%s'", def->norelabel ? "no" : "yes"); - virBufferEscapeString(buf, " <label>%s</label>\n", - def->label); - if (!def->norelabel) - virBufferEscapeString(buf, " <imagelabel>%s</imagelabel>\n", - def->imagelabel); - if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) - virBufferEscapeString(buf, " <baselabel>%s</baselabel>\n", - def->baselabel); - virBufferAddLit(buf, "</seclabel>\n"); + if (def->label || def->baselabel) { + virBufferAddLit(buf, ">\n"); + virBufferEscapeString(buf, " <label>%s</label>\n", + def->label); + if (!def->norelabel) + virBufferEscapeString(buf, " <imagelabel>%s</imagelabel>\n", + def->imagelabel); + if (def->type == VIR_DOMAIN_SECLABEL_DYNAMIC) + virBufferEscapeString(buf, " <baselabel>%s</baselabel>\n", + def->baselabel); + virBufferAddLit(buf, "</seclabel>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } } ret = 0; cleanup: @@ -9885,17 +9956,36 @@ virDomainDiskDefFormat(virBufferPtr buf, def->startupPolicy) { switch (def->type) { case VIR_DOMAIN_DISK_TYPE_FILE: - virBufferAsprintf(buf," <source"); + virBufferAddLit(buf, " <source"); if (def->src) virBufferEscapeString(buf, " file='%s'", def->src); if (def->startupPolicy) virBufferEscapeString(buf, " startupPolicy='%s'", startupPolicy); - virBufferAsprintf(buf, "/>\n"); + if (def->seclabel) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 8); + if (virSecurityLabelDefFormat(buf, def->seclabel, flags) < 0) + return -1; + virBufferAdjustIndent(buf, -8); + virBufferAddLit(buf, " </source>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } break; case VIR_DOMAIN_DISK_TYPE_BLOCK: - virBufferEscapeString(buf, " <source dev='%s'/>\n", - def->src); + if (def->src && def->seclabel) { + virBufferEscapeString(buf, " <source dev='%s'>\n", + def->src); + virBufferAdjustIndent(buf, 8); + if (virSecurityLabelDefFormat(buf, def->seclabel, flags) < 0) + return -1; + virBufferAdjustIndent(buf, -8); + virBufferAddLit(buf, " </source>\n"); + } else { + virBufferEscapeString(buf, " <source dev='%s'/>\n", + def->src); + } break; case VIR_DOMAIN_DISK_TYPE_DIR: virBufferEscapeString(buf, " <source dir='%s'/>\n", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 7c5946f..d894288 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -357,6 +357,7 @@ struct _virDomainDiskDef { int device; int bus; char *src; + virSecurityLabelDefPtr seclabel; char *dst; int protocol; int nhosts; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.args b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.args new file mode 100644 index 0000000..8f78dcf --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-seclabel-dynamic-override.args @@ -0,0 +1,5 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \ +pc -m 214 -smp 1 -name QEMUGuest1 -nographic -monitor unix:/tmp/test-monitor,\ +server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 \ +-hdb /dev/HostVG/QEMUGuest2 -net none -serial \ +none -parallel none -usb diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 18e8941..69e2612 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -662,6 +662,7 @@ mymain(void) DO_TEST("seclabel-dynamic", false, QEMU_CAPS_NAME); DO_TEST("seclabel-dynamic-baselabel", false, QEMU_CAPS_NAME); + DO_TEST("seclabel-dynamic-override", false, QEMU_CAPS_NAME); DO_TEST("seclabel-static", false, QEMU_CAPS_NAME); DO_TEST("seclabel-static-relabel", false, QEMU_CAPS_NAME); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index e4b99c4..0a8a28e 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -195,6 +195,7 @@ mymain(void) DO_TEST("blkdeviotune"); DO_TEST("seclabel-dynamic-baselabel"); + DO_TEST("seclabel-dynamic-override"); DO_TEST("seclabel-static"); /* These tests generate different XML */ -- 1.7.7.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list