virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt,
                        xmlNodePtr node,
                        xmlXPathContextPtr ctxt,
                        unsigned int flags)
{
    virDomainNetDefPtr def;
    virDomainHostdevDefPtr hostdev;
    xmlNodePtr cur;
    xmlNodePtr tmpNode;
    virHashTablePtr filterparams = NULL;
    virDomainActualNetDefPtr actual = NULL;
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    virDomainChrSourceReconnectDef reconnect = {0};
    int rv, val;
    g_autofree char *macaddr = NULL;
    g_autofree char *type = NULL;
    g_autofree char *network = NULL;
    g_autofree char *portgroup = NULL;
    g_autofree char *portid = NULL;
    g_autofree char *bridge = NULL;
    g_autofree char *dev = NULL;
    g_autofree char *ifname = NULL;
    g_autofree char *managed_tap = NULL;
    g_autofree char *ifname_guest = NULL;
    g_autofree char *ifname_guest_actual = NULL;
    g_autofree char *script = NULL;
    g_autofree char *address = NULL;
    g_autofree char *port = NULL;
    g_autofree char *localaddr = NULL;
    g_autofree char *localport = NULL;
    g_autofree char *model = NULL;
    g_autofree char *backend = NULL;
    g_autofree char *txmode = NULL;
    g_autofree char *ioeventfd = NULL;
    g_autofree char *event_idx = NULL;
    g_autofree char *queues = NULL;
    g_autofree char *rx_queue_size = NULL;
    g_autofree char *tx_queue_size = NULL;
    g_autofree char *str = NULL;
    g_autofree char *filter = NULL;
    g_autofree char *internal = NULL;
    g_autofree char *mode = NULL;
    g_autofree char *linkstate = NULL;
    g_autofree char *addrtype = NULL;
    g_autofree char *domain_name = NULL;
    g_autofree char *vhostuser_mode = NULL;
    g_autofree char *vhostuser_path = NULL;
    g_autofree char *vhostuser_type = NULL;
    g_autofree char *trustGuestRxFilters = NULL;
    g_autofree char *vhost_path = NULL;
    g_autofree char *teamingType = NULL;
    g_autofree char *teamingPersistent = NULL;
    const char *prefix = xmlopt ? xmlopt->config.netPrefix : NULL;

    if (!(def = virDomainNetDefNew(xmlopt)))
        return NULL;

    ctxt->node = node;

    type = virXMLPropString(node, "type");
    if (type != NULL) {
        if ((int)(def->type = virDomainNetTypeFromString(type)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown interface type '%s'"), type);
            goto error;
        }
    } else {
        def->type = VIR_DOMAIN_NET_TYPE_USER;
    }

    trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters");
    if (trustGuestRxFilters &&
        ((def->trustGuestRxFilters
          = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown trustGuestRxFilters value '%s'"),
                       trustGuestRxFilters);
        goto error;
    }

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
            if (virXMLNodeNameEqual(cur, "source")) {
                xmlNodePtr tmpnode = ctxt->node;

                ctxt->node = cur;
                if (virDomainNetIPInfoParseXML(_("interface host IP"),
                                               ctxt, &def->hostIP) < 0)
                    goto error;
                ctxt->node = tmpnode;
            }
            if (!macaddr && virXMLNodeNameEqual(cur, "mac")) {
                macaddr = virXMLPropString(cur, "address");
            } else if (!network &&
                       def->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
                       virXMLNodeNameEqual(cur, "source")) {
                network = virXMLPropString(cur, "network");
                portgroup = virXMLPropString(cur, "portgroup");
                if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
                    portid = virXMLPropString(cur, "portid");
            } else if (!internal &&
                       def->type == VIR_DOMAIN_NET_TYPE_INTERNAL &&
                       virXMLNodeNameEqual(cur, "source")) {
                internal = virXMLPropString(cur, "name");
            } else if (!bridge &&
                       def->type == VIR_DOMAIN_NET_TYPE_BRIDGE &&
                       virXMLNodeNameEqual(cur, "source")) {
                bridge = virXMLPropString(cur, "bridge");
            } else if (!dev && def->type == VIR_DOMAIN_NET_TYPE_DIRECT &&
                       virXMLNodeNameEqual(cur, "source")) {
                dev  = virXMLPropString(cur, "dev");
                mode = virXMLPropString(cur, "mode");
            } else if (!dev && def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
                       virXMLNodeNameEqual(cur, "source")) {
                /* This clause is only necessary because from 2010 to
                 * 2016 it was possible (but never documented) to
                 * configure the name of the guest-side interface of
                 * an openvz domain with <source dev='blah'/>.  That
                 * was blatant misuse of <source>, so was likely
                 * (hopefully) never used, but just in case there was
                 * somebody using it, we need to generate an error. If
                 * the openvz driver is ever deprecated, this clause
                 * can be removed from here.
                 */
                if ((dev = virXMLPropString(cur, "dev"))) {
                    virReportError(VIR_ERR_XML_ERROR,
                                   _("Invalid attempt to set <interface type='ethernet'> "
                                     "device name with <source dev='%s'/>. "
                                     "Use <target dev='%s'/> (for host-side) "
                                     "or <guest dev='%s'/> (for guest-side) instead."),
                                   dev, dev, dev);
                    goto error;
                }
            } else if (!vhostuser_path && !vhostuser_mode && !vhostuser_type
                       && def->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER
                       && virXMLNodeNameEqual(cur, "source")) {
                vhostuser_type = virXMLPropString(cur, "type");
                vhostuser_path = virXMLPropString(cur, "path");
                vhostuser_mode = virXMLPropString(cur, "mode");
                if (virDomainChrSourceReconnectDefParseXML(&reconnect, cur, ctxt) < 0)
                    goto error;

            } else if (!def->virtPortProfile
                       && virXMLNodeNameEqual(cur, "virtualport")) {
                if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
                    if (!(def->virtPortProfile
                          = virNetDevVPortProfileParse(cur,
                                                       VIR_VPORT_XML_GENERATE_MISSING_DEFAULTS))) {
                        goto error;
                    }
                } else if (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
                           def->type == VIR_DOMAIN_NET_TYPE_DIRECT ||
                           def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
                    if (!(def->virtPortProfile
                          = virNetDevVPortProfileParse(cur,
                                                       VIR_VPORT_XML_GENERATE_MISSING_DEFAULTS|
                                                       VIR_VPORT_XML_REQUIRE_ALL_ATTRIBUTES|
                                                       VIR_VPORT_XML_REQUIRE_TYPE))) {
                        goto error;
                    }
                } else {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("<virtualport> element unsupported for"
                                     " <interface type='%s'>"), type);
                    goto error;
                }
            } else if (!address &&
                       (def->type == VIR_DOMAIN_NET_TYPE_SERVER ||
                        def->type == VIR_DOMAIN_NET_TYPE_CLIENT ||
                        def->type == VIR_DOMAIN_NET_TYPE_MCAST ||
                        def->type == VIR_DOMAIN_NET_TYPE_UDP) &&
                       virXMLNodeNameEqual(cur, "source")) {
                address = virXMLPropString(cur, "address");
                port = virXMLPropString(cur, "port");
                if (!localaddr && def->type == VIR_DOMAIN_NET_TYPE_UDP) {
                    xmlNodePtr tmpnode = ctxt->node;
                    ctxt->node = cur;
                    if ((tmpNode = virXPathNode("./local", ctxt))) {
                        localaddr = virXMLPropString(tmpNode, "address");
                        localport = virXMLPropString(tmpNode, "port");
                    }
                    ctxt->node = tmpnode;
                }
            } else if (!ifname &&
                       virXMLNodeNameEqual(cur, "target")) {
                ifname = virXMLPropString(cur, "dev");
                managed_tap = virXMLPropString(cur, "managed");
            } else if ((!ifname_guest || !ifname_guest_actual) &&
                       virXMLNodeNameEqual(cur, "guest")) {
                ifname_guest = virXMLPropString(cur, "dev");
                ifname_guest_actual = virXMLPropString(cur, "actual");
            } else if (!linkstate &&
                       virXMLNodeNameEqual(cur, "link")) {
                linkstate = virXMLPropString(cur, "state");
            } else if (!script &&
                       virXMLNodeNameEqual(cur, "script")) {
                script = virXMLPropString(cur, "path");
            } else if (!domain_name &&
                       virXMLNodeNameEqual(cur, "backenddomain")) {
                domain_name = virXMLPropString(cur, "name");
            } else if (virXMLNodeNameEqual(cur, "model")) {
                model = virXMLPropString(cur, "type");
            } else if (virXMLNodeNameEqual(cur, "driver")) {
                backend = virXMLPropString(cur, "name");
                txmode = virXMLPropString(cur, "txmode");
                ioeventfd = virXMLPropString(cur, "ioeventfd");
                event_idx = virXMLPropString(cur, "event_idx");
                queues = virXMLPropString(cur, "queues");
                rx_queue_size = virXMLPropString(cur, "rx_queue_size");
                tx_queue_size = virXMLPropString(cur, "tx_queue_size");

                if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0)
                    goto error;
            } else if (virXMLNodeNameEqual(cur, "filterref")) {
                if (filter) {
                    virReportError(VIR_ERR_XML_ERROR, "%s",
                                   _("Invalid specification of multiple <filterref>s "
                                     "in a single <interface>"));
                    goto error;
                }
                filter = virXMLPropString(cur, "filter");
                virHashFree(filterparams);
                filterparams = virNWFilterParseParamAttributes(cur);
            } else if (virXMLNodeNameEqual(cur, "boot")) {
                /* boot is parsed as part of virDomainDeviceInfoParseXML */
            } else if (!actual &&
                       (flags & VIR_DOMAIN_DEF_PARSE_ACTUAL_NET) &&
                       def->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
                       virXMLNodeNameEqual(cur, "actual")) {
                if (virDomainActualNetDefParseXML(cur, ctxt, def,
                                                  &actual, flags) < 0) {
                    goto error;
                }
            } else if (virXMLNodeNameEqual(cur, "bandwidth")) {
                if (virNetDevBandwidthParse(&def->bandwidth,
                                            NULL,
                                            cur,
                                            def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0)
                    goto error;
            } else if (virXMLNodeNameEqual(cur, "vlan")) {
                if (virNetDevVlanParse(cur, ctxt, &def->vlan) < 0)
                    goto error;
            } else if (virXMLNodeNameEqual(cur, "backend")) {
                char *tmp = NULL;

                if ((tmp = virXMLPropString(cur, "tap")))
                    def->backend.tap = virFileSanitizePath(tmp);
                VIR_FREE(tmp);

                if (!vhost_path && (tmp = virXMLPropString(cur, "vhost")))
                    vhost_path = virFileSanitizePath(tmp);
                VIR_FREE(tmp);
            } else if (virXMLNodeNameEqual(cur, "teaming") &&
                       !teamingType && !teamingPersistent) {
                teamingType = virXMLPropString(cur, "type");
                teamingPersistent =  virXMLPropString(cur, "persistent");
            }
        }
        cur = cur->next;
    }

    if (macaddr) {
        if (virMacAddrParse((const char *)macaddr, &def->mac) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("unable to parse mac address '%s'"),
                           (const char *)macaddr);
            goto error;
        }
        if (virMacAddrIsMulticast(&def->mac)) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("expected unicast mac address, found multicast '%s'"),
                           (const char *)macaddr);
            goto error;
        }
    } else {
        virDomainNetGenerateMAC(xmlopt, &def->mac);
        def->mac_generated = true;
    }

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info,
                                    flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT
                                    | VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) < 0) {
        goto error;
    }

    if (model != NULL &&
        virDomainNetSetModelString(def, model) < 0)
        goto error;

    switch (def->type) {
    case VIR_DOMAIN_NET_TYPE_NETWORK:
        if (network == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'network' attribute "
                             "specified with <interface type='network'/>"));
            goto error;
        }
        if (portid &&
            virUUIDParse(portid, def->data.network.portid) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Unable to parse port id '%s'"), portid);
            goto error;
        }

        def->data.network.name = g_steal_pointer(&network);
        def->data.network.portgroup = g_steal_pointer(&portgroup);
        def->data.network.actual = g_steal_pointer(&actual);
        break;

    case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
        if (!virDomainNetIsVirtioModel(def)) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Wrong or no <model> 'type' attribute "
                             "specified with <interface type='vhostuser'/>. "
                             "vhostuser requires the virtio-net* frontend"));
            goto error;
        }

        if (STRNEQ_NULLABLE(vhostuser_type, "unix")) {
            if (vhostuser_type)
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Type='%s' unsupported for"
                                 " <interface type='vhostuser'>"),
                               vhostuser_type);
            else
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                               _("No <source> 'type' attribute "
                                 "specified for <interface "
                                 "type='vhostuser'>"));
            goto error;
        }

        if (vhostuser_path == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'path' attribute "
                             "specified with <interface "
                             "type='vhostuser'/>"));
            goto error;
        }

        if (vhostuser_mode == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'mode' attribute "
                             "specified with <interface "
                             "type='vhostuser'/>"));
            goto error;
        }

        if (!(def->data.vhostuser = virDomainChrSourceDefNew(xmlopt)))
            goto error;

        def->data.vhostuser->type = VIR_DOMAIN_CHR_TYPE_UNIX;
        def->data.vhostuser->data.nix.path = g_steal_pointer(&vhostuser_path);

        if (STREQ(vhostuser_mode, "server")) {
            def->data.vhostuser->data.nix.listen = true;
            if (reconnect.enabled == VIR_TRISTATE_BOOL_YES) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                               _("'reconnect' attribute  unsupported "
                                 "'server' mode for <interface type='vhostuser'>"));
                goto error;
           }
        } else if (STREQ(vhostuser_mode, "client")) {
            def->data.vhostuser->data.nix.listen = false;
            def->data.vhostuser->data.nix.reconnect = reconnect;
        } else {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("Wrong <source> 'mode' attribute "
                             "specified with <interface "
                             "type='vhostuser'/>"));
            goto error;
        }
        break;

    case VIR_DOMAIN_NET_TYPE_BRIDGE:
        if (bridge == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'bridge' attribute "
                             "specified with <interface type='bridge'/>"));
            goto error;
        }
        def->data.bridge.brname = g_steal_pointer(&bridge);
        break;

    case VIR_DOMAIN_NET_TYPE_CLIENT:
    case VIR_DOMAIN_NET_TYPE_SERVER:
    case VIR_DOMAIN_NET_TYPE_MCAST:
    case VIR_DOMAIN_NET_TYPE_UDP:
        if (port == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'port' attribute "
                             "specified with socket interface"));
            goto error;
        }
        if (virStrToLong_i(port, NULL, 10, &def->data.socket.port) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Cannot parse <source> 'port' attribute "
                             "with socket interface"));
            goto error;
        }

        if (address == NULL) {
            if (def->type == VIR_DOMAIN_NET_TYPE_CLIENT ||
                def->type == VIR_DOMAIN_NET_TYPE_MCAST ||
                def->type == VIR_DOMAIN_NET_TYPE_UDP) {
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                               _("No <source> 'address' attribute "
                                 "specified with socket interface"));
                goto error;
            }
        } else {
            def->data.socket.address = g_steal_pointer(&address);
        }

        if (def->type != VIR_DOMAIN_NET_TYPE_UDP)
            break;

        if (localport == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <local> 'port' attribute "
                             "specified with socket interface"));
            goto error;
        }
        if (virStrToLong_i(localport, NULL, 10, &def->data.socket.localport) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Cannot parse <local> 'port' attribute "
                             "with socket interface"));
            goto error;
        }

        if (localaddr == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <local> 'address' attribute "
                             "specified with socket interface"));
            goto error;
        } else {
            def->data.socket.localaddr = g_steal_pointer(&localaddr);
        }
        break;

    case VIR_DOMAIN_NET_TYPE_INTERNAL:
        if (internal == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'name' attribute specified "
                             "with <interface type='internal'/>"));
            goto error;
        }
        def->data.internal.name = g_steal_pointer(&internal);
        break;

    case VIR_DOMAIN_NET_TYPE_DIRECT:
        if (dev == NULL) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("No <source> 'dev' attribute specified "
                             "with <interface type='direct'/>"));
            goto error;
        }

        if (mode != NULL) {
            if ((val = virNetDevMacVLanModeTypeFromString(mode)) < 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                               _("Unknown mode has been specified"));
                goto error;
            }
            def->data.direct.mode = val;
        } else {
            def->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_VEPA;
        }

        def->data.direct.linkdev = g_steal_pointer(&dev);

        if (ifname &&
            flags & VIR_DOMAIN_DEF_PARSE_INACTIVE &&
            (STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) ||
             STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX))) {
            VIR_FREE(ifname);
        }

        break;

    case VIR_DOMAIN_NET_TYPE_HOSTDEV:
        hostdev = &def->data.hostdev.def;
        hostdev->parentnet = def;
        hostdev->info = &def->info;
        /* The helper function expects type to already be found and
         * passed in as a string, since it is in a different place in
         * NetDef vs HostdevDef.
         */
        addrtype = virXPathString("string(./source/address/@type)", ctxt);
        /* if not explicitly stated, source/vendor implies usb device */
        if (!addrtype && virXPathNode("./source/vendor", ctxt))
            addrtype = g_strdup("usb");
        hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
        if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype,
                                              hostdev, flags) < 0) {
            goto error;
        }
        break;

    case VIR_DOMAIN_NET_TYPE_ETHERNET:
    case VIR_DOMAIN_NET_TYPE_USER:
    case VIR_DOMAIN_NET_TYPE_LAST:
        break;
    }

    if (virDomainNetIPInfoParseXML(_("guest interface"),
                                   ctxt, &def->guestIP) < 0)
        goto error;

    if (managed_tap) {
        bool state = false;
        if (virStringParseYesNo(managed_tap, &state) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("invalid 'managed' value '%s'"),
                           managed_tap);
            goto error;
        }
        def->managed_tap = virTristateBoolFromBool(state);
    }

    if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname &&
        (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
        (STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) ||
         (prefix && STRPREFIX(ifname, prefix)))) {
        /* An auto-generated target name, blank it out */
        VIR_FREE(ifname);
    }

    if (script != NULL)
        def->script = g_steal_pointer(&script);
    if (domain_name != NULL)
        def->domain_name = g_steal_pointer(&domain_name);
    if (ifname != NULL)
        def->ifname = g_steal_pointer(&ifname);
    if (ifname_guest != NULL)
        def->ifname_guest = g_steal_pointer(&ifname_guest);
    if (ifname_guest_actual != NULL)
        def->ifname_guest_actual = g_steal_pointer(&ifname_guest_actual);

    if (def->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
        virDomainNetIsVirtioModel(def)) {
        if (backend != NULL) {
            if ((val = virDomainNetBackendTypeFromString(backend)) < 0 ||
                val == VIR_DOMAIN_NET_BACKEND_TYPE_DEFAULT) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Unknown interface <driver name='%s'> "
                                 "has been specified"),
                               backend);
                goto error;
            }
            def->driver.virtio.name = val;
        }
        if (txmode != NULL) {
            if ((val = virDomainNetVirtioTxModeTypeFromString(txmode)) < 0 ||
                val == VIR_DOMAIN_NET_VIRTIO_TX_MODE_DEFAULT) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Unknown interface <driver txmode='%s'> "
                                 "has been specified"),
                               txmode);
                goto error;
            }
            def->driver.virtio.txmode = val;
        }
        if (ioeventfd) {
            if ((val = virTristateSwitchTypeFromString(ioeventfd)) <= 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("unknown interface ioeventfd mode '%s'"),
                               ioeventfd);
                goto error;
            }
            def->driver.virtio.ioeventfd = val;
        }
        if (event_idx) {
            if ((val = virTristateSwitchTypeFromString(event_idx)) <= 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("unknown interface event_idx mode '%s'"),
                               event_idx);
                goto error;
            }
            def->driver.virtio.event_idx = val;
        }
        if (queues) {
            unsigned int q;
            if (virStrToLong_uip(queues, NULL, 10, &q) < 0) {
                virReportError(VIR_ERR_XML_DETAIL,
                               _("'queues' attribute must be positive number: %s"),
                               queues);
                goto error;
            }
            if (q > 1)
                def->driver.virtio.queues = q;
        }
        if (rx_queue_size) {
            unsigned int q;
            if (virStrToLong_uip(rx_queue_size, NULL, 10, &q) < 0) {
                virReportError(VIR_ERR_XML_DETAIL,
                               _("'rx_queue_size' attribute must be positive number: %s"),
                               rx_queue_size);
                goto error;
            }
            def->driver.virtio.rx_queue_size = q;
        }
        if (tx_queue_size) {
            unsigned int q;
            if (virStrToLong_uip(tx_queue_size, NULL, 10, &q) < 0) {
                virReportError(VIR_ERR_XML_DETAIL,
                               _("'tx_queue_size' attribute must be positive number: %s"),
                               tx_queue_size);
                goto error;
            }
            def->driver.virtio.tx_queue_size = q;
        }

        if ((tmpNode = virXPathNode("./driver/host", ctxt))) {
            if ((str = virXMLPropString(tmpNode, "csum"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host csum mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.csum = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "gso"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host gso mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.gso = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "tso4"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host tso4 mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.tso4 = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "tso6"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host tso6 mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.tso6 = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "ecn"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host ecn mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.ecn = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "ufo"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host ufo mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.ufo = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "mrg_rxbuf"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown host mrg_rxbuf mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.host.mrg_rxbuf = val;
            }
            VIR_FREE(str);
        }

        if ((tmpNode = virXPathNode("./driver/guest", ctxt))) {
            if ((str = virXMLPropString(tmpNode, "csum"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown guest csum mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.guest.csum = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "tso4"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown guest tso4 mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.guest.tso4 = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "tso6"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown guest tso6 mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.guest.tso6 = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "ecn"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown guest ecn mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.guest.ecn = val;
            }
            VIR_FREE(str);
            if ((str = virXMLPropString(tmpNode, "ufo"))) {
                if ((val = virTristateSwitchTypeFromString(str)) <= 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("unknown guest ufo mode '%s'"),
                                   str);
                    goto error;
                }
                def->driver.virtio.guest.ufo = val;
            }
        }
        def->backend.vhost = g_steal_pointer(&vhost_path);
    }

    def->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DEFAULT;
    if (linkstate != NULL) {
        if ((def->linkstate = virDomainNetInterfaceLinkStateTypeFromString(linkstate)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown interface link state '%s'"),
                           linkstate);
            goto error;
        }
    }

    if (filter != NULL) {
        switch (def->type) {
        case VIR_DOMAIN_NET_TYPE_ETHERNET:
        case VIR_DOMAIN_NET_TYPE_NETWORK:
        case VIR_DOMAIN_NET_TYPE_BRIDGE:
            def->filter = g_steal_pointer(&filter);
            def->filterparams = g_steal_pointer(&filterparams);
            break;
        case VIR_DOMAIN_NET_TYPE_USER:
        case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
        case VIR_DOMAIN_NET_TYPE_SERVER:
        case VIR_DOMAIN_NET_TYPE_CLIENT:
        case VIR_DOMAIN_NET_TYPE_MCAST:
        case VIR_DOMAIN_NET_TYPE_INTERNAL:
        case VIR_DOMAIN_NET_TYPE_DIRECT:
        case VIR_DOMAIN_NET_TYPE_HOSTDEV:
        case VIR_DOMAIN_NET_TYPE_UDP:
            break;
        case VIR_DOMAIN_NET_TYPE_LAST:
        default:
            virReportEnumRangeError(virDomainNetType, def->type);
            goto error;
        }
    }

    if (teamingType) {
        int tmpTeaming;

        if ((tmpTeaming = virDomainNetTeamingTypeFromString(teamingType)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown teaming type '%s'"),
                           teamingType);
            goto error;
        }
        def->teaming.type = tmpTeaming;
    }
    def->teaming.persistent = g_steal_pointer(&teamingPersistent);

    rv = virXPathULong("string(./tune/sndbuf)", ctxt, &def->tune.sndbuf);
    if (rv >= 0) {
        def->tune.sndbuf_specified = true;
    } else if (rv == -2) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("sndbuf must be a positive integer"));
        goto error;
    }

    if (virXPathUInt("string(./mtu/@size)", ctxt, &def->mtu) < -1) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("malformed mtu size"));
        goto error;
    }

    node = virXPathNode("./coalesce", ctxt);
    if (node) {
        def->coalesce = virDomainNetDefCoalesceParseXML(node, ctxt);
        if (!def->coalesce)
            goto error;
    }

    if (virNetworkPortOptionsParseXML(ctxt, &def->isolatedPort) < 0)
        goto error;

 cleanup:
    virDomainActualNetDefFree(actual);
    virHashFree(filterparams);
    return def;

 error:
    virDomainNetDefFree(def);
    def = NULL;
    goto cleanup;
}


virDomainSoundDefRemove(virDomainDefPtr def, size_t idx)
{
    virDomainSoundDefPtr ret = def->sounds[idx];
    VIR_DELETE_ELEMENT(def->sounds, idx, def->nsounds);
    return ret;
}


virDomainStorageSourceParseBase(const char *type,
                                const char *format,
                                const char *index)
{
    g_autoptr(virStorageSource) src = NULL;

    if (!(src = virStorageSourceNew()))
        return NULL;

    src->type = VIR_STORAGE_TYPE_FILE;

    if (type &&
        (src->type = virStorageTypeFromString(type)) <= 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("unknown storage source type '%s'"), type);
        return NULL;
    }

    if (format &&
        (src->format = virStorageFileFormatTypeFromString(format)) <= 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("unknown storage source format '%s'"), format);
        return NULL;
    }

    if (index &&
        virStrToLong_uip(index, NULL, 10, &src->id) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("invalid storage source index '%s'"), index);
        return NULL;
    }

    return g_steal_pointer(&src);
}


virDomainResctrlNew(xmlNodePtr node,
                    virResctrlAllocPtr alloc,
                    virBitmapPtr vcpus,
                    unsigned int flags)
{
    virDomainResctrlDefPtr resctrl = NULL;
    virDomainResctrlDefPtr ret = NULL;
    g_autofree char *vcpus_str = NULL;
    g_autofree char *alloc_id = NULL;

    /* We need to format it back because we need to be consistent in the naming
     * even when users specify some "sub-optimal" string there. */
    vcpus_str = virBitmapFormat(vcpus);
    if (!vcpus_str)
        return NULL;

    if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))
        alloc_id = virXMLPropString(node, "id");

    if (!alloc_id) {
        /* The number of allocations is limited and the directory structure is flat,
         * not hierarchical, so we need to have all same allocations in one
         * directory, so it's nice to have it named appropriately.  For now it's
         * 'vcpus_...' but it's designed in order for it to be changeable in the
         * future (it's part of the status XML). */
        alloc_id = g_strdup_printf("vcpus_%s", vcpus_str);
    }

    if (virResctrlAllocSetID(alloc, alloc_id) < 0)
        goto cleanup;

    if (VIR_ALLOC(resctrl) < 0)
        goto cleanup;

    if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("failed to copy 'vcpus'"));
        goto cleanup;
    }

    resctrl->alloc = virObjectRef(alloc);

    ret = g_steal_pointer(&resctrl);
 cleanup:
    virDomainResctrlDefFree(resctrl);
    return ret;
}


virDomainFSDefParseXML(virDomainXMLOptionPtr xmlopt,
                       xmlNodePtr node,
                       xmlXPathContextPtr ctxt,
                       unsigned int flags)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    virDomainFSDefPtr def;
    xmlNodePtr cur;
    g_autofree char *type = NULL;
    g_autofree char *fsdriver = NULL;
    g_autofree char *source = NULL;
    g_autofree char *target = NULL;
    g_autofree char *format = NULL;
    g_autofree char *accessmode = NULL;
    g_autofree char *wrpolicy = NULL;
    g_autofree char *usage = NULL;
    g_autofree char *units = NULL;
    g_autofree char *model = NULL;
    g_autofree char *multidevs = NULL;

    ctxt->node = node;

    if (!(def = virDomainFSDefNew(xmlopt)))
        return NULL;

    type = virXMLPropString(node, "type");
    if (type) {
        if ((def->type = virDomainFSTypeFromString(type)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown filesystem type '%s'"), type);
            goto error;
        }
    } else {
        def->type = VIR_DOMAIN_FS_TYPE_MOUNT;
    }

    accessmode = virXMLPropString(node, "accessmode");
    if (accessmode) {
        if ((def->accessmode = virDomainFSAccessModeTypeFromString(accessmode)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown accessmode '%s'"), accessmode);
            goto error;
        }
    } else {
        def->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH;
    }

    model = virXMLPropString(node, "model");
    if (model) {
        if ((def->model = virDomainFSModelTypeFromString(model)) < 0 ||
            def->model == VIR_DOMAIN_FS_MODEL_DEFAULT) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown model '%s'"), model);
            goto error;
        }
    }

    multidevs = virXMLPropString(node, "multidevs");
    if (multidevs) {
        if ((def->multidevs = virDomainFSMultidevsTypeFromString(multidevs)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown multidevs '%s'"), multidevs);
            goto error;
        }
    } else {
        def->multidevs = VIR_DOMAIN_FS_MULTIDEVS_DEFAULT;
    }

    if (virDomainParseScaledValue("./space_hard_limit[1]",
                                  NULL, ctxt, &def->space_hard_limit,
                                  1, ULLONG_MAX, false) < 0)
        goto error;

    if (virDomainParseScaledValue("./space_soft_limit[1]",
                                  NULL, ctxt, &def->space_soft_limit,
                                  1, ULLONG_MAX, false) < 0)
        goto error;

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
            if (!source &&
                virXMLNodeNameEqual(cur, "source")) {

                if (def->type == VIR_DOMAIN_FS_TYPE_MOUNT ||
                    def->type == VIR_DOMAIN_FS_TYPE_BIND) {
                    source = virXMLPropString(cur, "dir");
                } else if (def->type == VIR_DOMAIN_FS_TYPE_FILE) {
                    source = virXMLPropString(cur, "file");
                } else if (def->type == VIR_DOMAIN_FS_TYPE_BLOCK) {
                    source = virXMLPropString(cur, "dev");
                } else if (def->type == VIR_DOMAIN_FS_TYPE_TEMPLATE) {
                    source = virXMLPropString(cur, "name");
                } else if (def->type == VIR_DOMAIN_FS_TYPE_RAM) {
                    usage = virXMLPropString(cur, "usage");
                    units = virXMLPropString(cur, "units");
                } else if (def->type == VIR_DOMAIN_FS_TYPE_VOLUME) {
                    def->src->type = VIR_STORAGE_TYPE_VOLUME;
                    if (virDomainDiskSourcePoolDefParse(cur, &def->src->srcpool) < 0)
                        goto error;
                }
            } else if (!target &&
                       virXMLNodeNameEqual(cur, "target")) {
                target = virXMLPropString(cur, "dir");
            } else if (virXMLNodeNameEqual(cur, "readonly")) {
                def->readonly = true;
            } else if (virXMLNodeNameEqual(cur, "driver")) {
                if (!fsdriver)
                    fsdriver = virXMLPropString(cur, "type");
                if (!wrpolicy)
                    wrpolicy = virXMLPropString(cur, "wrpolicy");
                if (!format)
                    format = virXMLPropString(cur, "format");

                if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0)
                    goto error;
            }
        }
        cur = cur->next;
    }

    if (fsdriver) {
        if ((def->fsdriver = virDomainFSDriverTypeFromString(fsdriver)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown fs driver type '%s'"), fsdriver);
            goto error;
        }
    }

    if (def->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS) {
        g_autofree char *queue_size = virXPathString("string(./driver/@queue)", ctxt);
        g_autofree char *binary = virXPathString("string(./binary/@path)", ctxt);
        g_autofree char *xattr = virXPathString("string(./binary/@xattr)", ctxt);
        g_autofree char *cache = virXPathString("string(./binary/cache/@mode)", ctxt);
        g_autofree char *posix_lock = virXPathString("string(./binary/lock/@posix)", ctxt);
        g_autofree char *flock = virXPathString("string(./binary/lock/@flock)", ctxt);
        int val;

        if (queue_size && virStrToLong_ull(queue_size, NULL, 10, &def->queue_size) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("cannot parse queue size '%s' for virtiofs"),
                           queue_size);
            goto error;
        }

        if (binary)
            def->binary = virFileSanitizePath(binary);

        if (xattr) {
            if ((val = virTristateSwitchTypeFromString(xattr)) <= 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("unknown xattr value '%s'"), xattr);
                goto error;
            }
            def->xattr = val;
        }

        if (cache) {
            if ((val = virDomainFSCacheModeTypeFromString(cache)) <= 0) {
                virReportError(VIR_ERR_XML_ERROR,
                               _("cannot parse cache mode '%s' for virtiofs"),
                               cache);
                goto error;
            }
            def->cache = val;
        }

        if (posix_lock) {
            if ((val = virTristateSwitchTypeFromString(posix_lock)) <= 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("unknown posix lock value '%s'"), posix_lock);
                goto error;
            }
            def->posix_lock = val;
        }

        if (flock) {
            if ((val = virTristateSwitchTypeFromString(flock)) <= 0) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("unknown flock value '%s'"), flock);
                goto error;
            }
            def->flock = val;
        }
    }

    if (format) {
        if ((def->format = virStorageFileFormatTypeFromString(format)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown driver format value '%s'"), format);
            goto error;
        }
    }

    if (wrpolicy) {
        if ((def->wrpolicy = virDomainFSWrpolicyTypeFromString(wrpolicy)) <= 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown filesystem write policy '%s'"), wrpolicy);
            goto error;
        }
    } else {
        def->wrpolicy = VIR_DOMAIN_FS_WRPOLICY_DEFAULT;
    }

    if (source == NULL && def->type != VIR_DOMAIN_FS_TYPE_RAM
        && def->type != VIR_DOMAIN_FS_TYPE_VOLUME) {
        virReportError(VIR_ERR_NO_SOURCE,
                       target ? "%s" : NULL, target);
        goto error;
    }

    if (target == NULL) {
        virReportError(VIR_ERR_NO_TARGET,
                       source ? "%s" : NULL, source);
        goto error;
    }

    if (def->type == VIR_DOMAIN_FS_TYPE_RAM) {
        if (!usage) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("missing 'usage' attribute for RAM filesystem"));
            goto error;
        }
        if (virStrToLong_ull(usage, NULL, 10, &def->usage) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("cannot parse usage '%s' for RAM filesystem"),
                           usage);
            goto error;
        }
        if (virScaleInteger(&def->usage, units,
                            1024, ULLONG_MAX) < 0)
            goto error;
    }

    def->src->path = g_steal_pointer(&source);
    def->dst = g_steal_pointer(&target);

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0)
        goto error;

    return def;

 error:
    virDomainFSDefFree(def);
    return NULL;
}


virDomainChrSourceDefValidate(const virDomainChrSourceDef *src_def,
                              const virDomainChrDef *chr_def,
                              const virDomainDef *def)
{
    switch ((virDomainChrType) src_def->type) {
    case VIR_DOMAIN_CHR_TYPE_NULL:
    case VIR_DOMAIN_CHR_TYPE_PTY:
    case VIR_DOMAIN_CHR_TYPE_VC:
    case VIR_DOMAIN_CHR_TYPE_STDIO:
    case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
    case VIR_DOMAIN_CHR_TYPE_LAST:
        break;

    case VIR_DOMAIN_CHR_TYPE_FILE:
    case VIR_DOMAIN_CHR_TYPE_DEV:
    case VIR_DOMAIN_CHR_TYPE_PIPE:
        if (!src_def->data.file.path) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing source path attribute for char device"));
            return -1;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_NMDM:
        if (!src_def->data.nmdm.master) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing master path attribute for nmdm device"));
            return -1;
        }

        if (!src_def->data.nmdm.slave) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing slave path attribute for nmdm device"));
            return -1;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_TCP:
        if (!src_def->data.tcp.host) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing source host attribute for char device"));
            return -1;
        }

        if (!src_def->data.tcp.service) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing source service attribute for char device"));
            return -1;
        }

        if (src_def->data.tcp.listen && src_def->data.tcp.reconnect.enabled) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("chardev reconnect is possible only for connect mode"));
            return -1;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_UDP:
        if (!src_def->data.udp.connectService) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing source service attribute for char device"));
            return -1;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_UNIX:
        /* The source path can be auto generated for certain specific
         * types of channels, but in most cases we should report an
         * error if the user didn't provide it */
        if (!src_def->data.nix.path &&
            !(chr_def &&
              chr_def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL &&
              (chr_def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN ||
               chr_def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO))) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Missing source path attribute for char device"));
            return -1;
        }

        if (src_def->data.nix.listen && src_def->data.nix.reconnect.enabled) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("chardev reconnect is possible only for connect mode"));
            return -1;
        }
        break;

    case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
        if (!src_def->data.spiceport.channel) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("Missing source channel attribute for char device"));
            return -1;
        }
        if (strspn(src_def->data.spiceport.channel,
                   SERIAL_CHANNEL_NAME_CHARS) < strlen(src_def->data.spiceport.channel)) {
            virReportError(VIR_ERR_INVALID_ARG, "%s",
                           _("Invalid character in source channel for char device"));
            return -1;
        }
        break;
    }

    if (virSecurityDeviceLabelDefValidateXML(src_def->seclabels,
                                             src_def->nseclabels,
                                             def->seclabels,
                                             def->nseclabels) < 0)
        return -1;

    return 0;
}


virDomainNetIsVirtioModel(const virDomainNetDef *net)
{
    return (net->model == VIR_DOMAIN_NET_MODEL_VIRTIO ||
            net->model == VIR_DOMAIN_NET_MODEL_VIRTIO_TRANSITIONAL ||
            net->model == VIR_DOMAIN_NET_MODEL_VIRTIO_NON_TRANSITIONAL);
}


virDomainDiskBackingStoreFormat(virBufferPtr buf,
                                virStorageSourcePtr src,
                                virDomainXMLOptionPtr xmlopt,
                                unsigned int flags)
{
    g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
    g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
    bool inactive = flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE;
    virStorageSourcePtr backingStore = src->backingStore;

    if (!backingStore)
        return 0;

    /* don't write detected backing chain members to inactive xml */
    if (inactive && backingStore->detected)
        return 0;

    if (backingStore->type == VIR_STORAGE_TYPE_NONE) {
        virBufferAddLit(buf, "<backingStore/>\n");
        return 0;
    }

    if (backingStore->format <= 0 || backingStore->format >= VIR_STORAGE_FILE_LAST) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected disk backing store format %d"),
                       backingStore->format);
        return -1;
    }

    virBufferAsprintf(&attrBuf, " type='%s'",
                      virStorageTypeToString(backingStore->type));
    if (backingStore->id != 0)
        virBufferAsprintf(&attrBuf, " index='%u'", backingStore->id);

    virBufferAsprintf(&childBuf, "<format type='%s'/>\n",
                      virStorageFileFormatTypeToString(backingStore->format));
    if (virDomainDiskSourceFormat(&childBuf, backingStore, "source", 0, false,
                                  flags, true, xmlopt) < 0)
        return -1;

    if (virDomainDiskBackingStoreFormat(&childBuf, backingStore, xmlopt, flags) < 0)
        return -1;

    virXMLFormatElement(buf, "backingStore", &attrBuf, &childBuf);

    return 0;
}


virDomainDefGetVcpuPinInfoHelper(virDomainDefPtr def,
                                 int maplen,
                                 int ncpumaps,
                                 unsigned char *cpumaps,
                                 int hostcpus,
                                 virBitmapPtr autoCpuset)
{
    int maxvcpus = virDomainDefGetVcpusMax(def);
    size_t i;
    g_autoptr(virBitmap) allcpumap = NULL;

    if (hostcpus < 0)
        return -1;

    if (!(allcpumap = virBitmapNew(hostcpus)))
        return -1;

    virBitmapSetAll(allcpumap);

    for (i = 0; i < maxvcpus && i < ncpumaps; i++) {
        virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(def, i);
        virBitmapPtr bitmap = NULL;

        if (vcpu && vcpu->cpumask)
            bitmap = vcpu->cpumask;
        else if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO &&
                 autoCpuset)
            bitmap = autoCpuset;
        else if (def->cpumask)
            bitmap = def->cpumask;
        else
            bitmap = allcpumap;

        virBitmapToDataBuf(bitmap, VIR_GET_CPUMAP(cpumaps, maplen, i), maplen);
    }

    return i;
}


virDomainDeviceFindSCSIController(const virDomainDef *def,
                                  const virDomainDeviceDriveAddress *addr)
{
    size_t i;

    for (i = 0; i < def->ncontrollers; i++) {
        if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
            def->controllers[i]->idx == addr->controller)
            return def->controllers[i];
    }

    return NULL;
}


virDomainObjSave(virDomainObjPtr obj,
                 virDomainXMLOptionPtr xmlopt,
                 const char *statusDir)
{
    unsigned int flags = (VIR_DOMAIN_DEF_FORMAT_SECURE |
                          VIR_DOMAIN_DEF_FORMAT_STATUS |
                          VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET |
                          VIR_DOMAIN_DEF_FORMAT_PCI_ORIG_STATES |
                          VIR_DOMAIN_DEF_FORMAT_CLOCK_ADJUST);

    g_autofree char *xml = NULL;

    if (!(xml = virDomainObjFormat(obj, xmlopt, flags)))
        return -1;

    return virDomainDefSaveXML(obj->def, statusDir, xml);
}


virDomainDiskRemove(virDomainDefPtr def, size_t i)
{
    virDomainDiskDefPtr disk = def->disks[i];

    VIR_DELETE_ELEMENT(def->disks, i, def->ndisks);
    return disk;
}


virDomainHostdevMatch(virDomainHostdevDefPtr a,
                      virDomainHostdevDefPtr b)
{
    if (a->mode != b->mode)
        return 0;

    switch (a->mode) {
    case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
        return virDomainHostdevMatchSubsys(a, b);
    case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
        return virDomainHostdevMatchCaps(a, b);
    }
    return 0;
}


virDomainDefBootOrderPostParse(virDomainDefPtr def)
{
    virHashTablePtr bootHash = NULL;
    int ret = -1;

    if (!(bootHash = virHashCreate(5, NULL)))
        goto cleanup;

    if (virDomainDeviceInfoIterate(def, virDomainDefCollectBootOrder, bootHash) < 0)
        goto cleanup;

    if (def->os.nBootDevs > 0 && virHashSize(bootHash) > 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("per-device boot elements cannot be used"
                         " together with os/boot elements"));
        goto cleanup;
    }

    if (def->os.nBootDevs == 0 && virHashSize(bootHash) == 0) {
        def->os.nBootDevs = 1;
        def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK;
    }

    ret = 0;

 cleanup:
    virHashFree(bootHash);
    return ret;
}


virDomainObjSetMetadata(virDomainObjPtr vm,
                        int type,
                        const char *metadata,
                        const char *key,
                        const char *uri,
                        virDomainXMLOptionPtr xmlopt,
                        const char *stateDir,
                        const char *configDir,
                        unsigned int flags)
{
    virDomainDefPtr def;
    virDomainDefPtr persistentDef;

    virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
                  VIR_DOMAIN_AFFECT_CONFIG, -1);

    if (virDomainObjGetDefs(vm, flags, &def, &persistentDef) < 0)
        return -1;

    if (def) {
        if (virDomainDefSetMetadata(def, type, metadata, key, uri) < 0)
            return -1;

        if (virDomainObjSave(vm, xmlopt, stateDir) < 0)
            return -1;
    }

    if (persistentDef) {
        if (virDomainDefSetMetadata(persistentDef, type, metadata, key,
                                    uri) < 0)
            return -1;

        if (virDomainDefSave(persistentDef, xmlopt, configDir) < 0)
            return -1;
    }

    return 0;
}


virDomainDiskExpandGroupIoTune(virDomainDiskDefPtr disk,
                               const virDomainDef *def)
{
    size_t i;

    if (!disk->blkdeviotune.group_name ||
        virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune))
        return;

    for (i = 0; i < def->ndisks; i++) {
        virDomainDiskDefPtr d = def->disks[i];

        if (STRNEQ_NULLABLE(disk->blkdeviotune.group_name, d->blkdeviotune.group_name) ||
            !virDomainBlockIoTuneInfoHasAny(&d->blkdeviotune))
            continue;

        VIR_FREE(disk->blkdeviotune.group_name);
        virDomainBlockIoTuneInfoCopy(&d->blkdeviotune, &disk->blkdeviotune);

        return;
    }
}


virDomainResctrlParseVcpus(virDomainDefPtr def,
                           xmlNodePtr node,
                           virBitmapPtr *vcpus)
{
    g_autofree char *vcpus_str = NULL;

    vcpus_str = virXMLPropString(node, "vcpus");
    if (!vcpus_str) {
        virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"),
                       node->name);
        return -1;
    }
    if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("Invalid %s attribute 'vcpus' value '%s'"),
                       node->name, vcpus_str);
        return -1;
    }

    /* We need to limit the bitmap to number of vCPUs.  If there's nothing left,
     * then we can just clean up and return 0 immediately */
    virBitmapShrink(*vcpus, def->maxvcpus);

    return 0;
}


virDomainMemoryFindByDefInternal(virDomainDefPtr def,
                                 virDomainMemoryDefPtr mem,
                                 bool allowAddressFallback)
{
    size_t i;

    for (i = 0; i < def->nmems; i++) {
        virDomainMemoryDefPtr tmp = def->mems[i];

        /* address, if present */
        if (allowAddressFallback) {
            if (tmp->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
                continue;
        } else {
            if (mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
                !virDomainDeviceInfoAddressIsEqual(&tmp->info, &mem->info))
                continue;
        }

        /* alias, if present */
        if (mem->info.alias &&
            STRNEQ_NULLABLE(tmp->info.alias, mem->info.alias))
            continue;

        /* target info -> always present */
        if (tmp->model != mem->model ||
            tmp->targetNode != mem->targetNode ||
            tmp->size != mem->size)
            continue;

        switch ((virDomainMemoryModel) mem->model) {
        case VIR_DOMAIN_MEMORY_MODEL_DIMM:
            /* source stuff -> match with device */
            if (tmp->pagesize != mem->pagesize)
                continue;

            if (!virBitmapEqual(tmp->sourceNodes, mem->sourceNodes))
                continue;
            break;

        case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
            if (STRNEQ(tmp->nvdimmPath, mem->nvdimmPath))
                continue;
            break;

        case VIR_DOMAIN_MEMORY_MODEL_NONE:
        case VIR_DOMAIN_MEMORY_MODEL_LAST:
            break;
        }

        break;
    }

    if (i == def->nmems)
        return -1;

    return i;
}


virDomainStateReasonToString(virDomainState state, int reason)
{
    switch (state) {
    case VIR_DOMAIN_NOSTATE:
        return virDomainNostateReasonTypeToString(reason);
    case VIR_DOMAIN_RUNNING:
        return virDomainRunningReasonTypeToString(reason);
    case VIR_DOMAIN_BLOCKED:
        return virDomainBlockedReasonTypeToString(reason);
    case VIR_DOMAIN_PAUSED:
        return virDomainPausedReasonTypeToString(reason);
    case VIR_DOMAIN_SHUTDOWN:
        return virDomainShutdownReasonTypeToString(reason);
    case VIR_DOMAIN_SHUTOFF:
        return virDomainShutoffReasonTypeToString(reason);
    case VIR_DOMAIN_CRASHED:
        return virDomainCrashedReasonTypeToString(reason);
    case VIR_DOMAIN_PMSUSPENDED:
        return virDomainPMSuspendedReasonTypeToString(reason);
    case VIR_DOMAIN_LAST:
        break;
    }
    VIR_WARN("Unexpected domain state: %d", state);
    return NULL;
}


virDomainSmartcardDefParseXML(virDomainXMLOptionPtr xmlopt,
                              xmlNodePtr node,
                              xmlXPathContextPtr ctxt,
                              unsigned int flags)
{
    xmlNodePtr cur;
    virDomainSmartcardDefPtr def;
    size_t i;
    g_autofree char *mode = NULL;
    g_autofree char *type = NULL;

    if (VIR_ALLOC(def) < 0)
        return NULL;

    mode = virXMLPropString(node, "mode");
    if (mode == NULL) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("missing smartcard device mode"));
        goto error;
    }
    if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown smartcard device mode: %s"),
                       mode);
        goto error;
    }

    switch (def->type) {
    case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
        break;

    case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
        i = 0;
        cur = node->children;
        while (cur) {
            if (cur->type == XML_ELEMENT_NODE &&
                virXMLNodeNameEqual(cur, "certificate")) {
                if (i == 3) {
                    virReportError(VIR_ERR_XML_ERROR, "%s",
                                   _("host-certificates mode needs "
                                     "exactly three certificates"));
                    goto error;
                }
                def->data.cert.file[i] = (char *)xmlNodeGetContent(cur);
                if (!def->data.cert.file[i]) {
                    virReportOOMError();
                    goto error;
                }
                i++;
            } else if (cur->type == XML_ELEMENT_NODE &&
                       virXMLNodeNameEqual(cur, "database") &&
                       !def->data.cert.database) {
                def->data.cert.database = (char *)xmlNodeGetContent(cur);
                if (!def->data.cert.database) {
                    virReportOOMError();
                    goto error;
                }
                if (*def->data.cert.database != '/') {
                    virReportError(VIR_ERR_XML_ERROR,
                                   _("expecting absolute path: %s"),
                                   def->data.cert.database);
                    goto error;
                }
            }
            cur = cur->next;
        }
        if (i < 3) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("host-certificates mode needs "
                             "exactly three certificates"));
            goto error;
        }
        break;

    case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
        type = virXMLPropString(node, "type");
        if (type == NULL) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("passthrough mode requires a character "
                             "device type attribute"));
            goto error;
        }

        if (!(def->data.passthru = virDomainChrSourceDefNew(xmlopt)))
            goto error;

        if ((def->data.passthru->type = virDomainChrTypeFromString(type)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown type presented to host for "
                             "character device: %s"), type);
            goto error;
        }

        cur = node->children;
        if (virDomainChrSourceDefParseXML(def->data.passthru, cur, flags,
                                          NULL, ctxt) < 0)
            goto error;

        if (def->data.passthru->type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) {
            def->data.passthru->data.spicevmc
                = VIR_DOMAIN_CHR_SPICEVMC_SMARTCARD;
        }

        break;

    default:
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("unknown smartcard mode"));
        goto error;
    }

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0)
        goto error;
    if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
        def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Controllers must use the 'ccid' address type"));
        goto error;
    }

    return def;

 error:
    virDomainSmartcardDefFree(def);
    return NULL;
}


void virDomainRedirFilterDefFree(virDomainRedirFilterDefPtr def)
{
    size_t i;

    if (!def)
        return;

    for (i = 0; i < def->nusbdevs; i++)
        VIR_FREE(def->usbdevs[i]);

    VIR_FREE(def->usbdevs);
    VIR_FREE(def);
}


virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt,
                                xmlNodePtr node,
                                virResctrlAllocPtr alloc)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    unsigned int level;
    unsigned int cache;
    int type;
    unsigned long long size;
    g_autofree char *tmp = NULL;

    ctxt->node = node;

    tmp = virXMLPropString(node, "id");
    if (!tmp) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Missing cachetune attribute 'id'"));
        return -1;
    }
    if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("Invalid cachetune attribute 'id' value '%s'"),
                       tmp);
        return -1;
    }
    VIR_FREE(tmp);

    tmp = virXMLPropString(node, "level");
    if (!tmp) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Missing cachetune attribute 'level'"));
        return -1;
    }
    if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("Invalid cachetune attribute 'level' value '%s'"),
                       tmp);
        return -1;
    }
    VIR_FREE(tmp);

    tmp = virXMLPropString(node, "type");
    if (!tmp) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Missing cachetune attribute 'type'"));
        return -1;
    }
    type = virCacheTypeFromString(tmp);
    if (type < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("Invalid cachetune attribute 'type' value '%s'"),
                       tmp);
        return -1;
    }

    if (virDomainParseScaledValue("./@size", "./@unit",
                                  ctxt, &size, 1024,
                                  ULLONG_MAX, true) < 0)
        return -1;

    if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0)
        return -1;

    return 0;
}


virDomainControllerDefFormat(virBufferPtr buf,
                             virDomainControllerDefPtr def,
                             unsigned int flags)
{
    const char *type = virDomainControllerTypeToString(def->type);
    const char *model = NULL;
    const char *modelName = NULL;
    g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
    g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);

    if (!type) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("unexpected controller type %d"), def->type);
        return -1;
    }

    if (def->model != -1) {
        model = virDomainControllerModelTypeToString(def, def->model);

        if (!model) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unexpected model type %d"), def->model);
            return -1;
        }
    }

    virBufferAsprintf(&attrBuf,
                      " type='%s' index='%d'",
                      type, def->idx);

    if (model)
        virBufferEscapeString(&attrBuf, " model='%s'", model);

    switch (def->type) {
    case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
        if (def->opts.vioserial.ports != -1) {
            virBufferAsprintf(&attrBuf, " ports='%d'",
                              def->opts.vioserial.ports);
        }
        if (def->opts.vioserial.vectors != -1) {
            virBufferAsprintf(&attrBuf, " vectors='%d'",
                              def->opts.vioserial.vectors);
        }
        break;

    case VIR_DOMAIN_CONTROLLER_TYPE_USB:
        if (def->opts.usbopts.ports != -1) {
            virBufferAsprintf(&attrBuf, " ports='%d'",
                              def->opts.usbopts.ports);
        }
        break;

    case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS:
        if (def->opts.xenbusopts.maxGrantFrames != -1) {
            virBufferAsprintf(&attrBuf, " maxGrantFrames='%d'",
                              def->opts.xenbusopts.maxGrantFrames);
        }
        if (def->opts.xenbusopts.maxEventChannels != -1) {
            virBufferAsprintf(&attrBuf, " maxEventChannels='%d'",
                              def->opts.xenbusopts.maxEventChannels);
        }
        break;

    default:
        break;
    }

    if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
        bool formatModelName = true;

        if (def->opts.pciopts.modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
            formatModelName = false;

        /* Historically, libvirt didn't support specifying a model name for
         * pci-root controllers; starting from 3.6.0, however, pSeries guests
         * use pci-root controllers with model name spapr-pci-host-bridge to
         * represent all PHBs, including the default one.
         *
         * In order to allow migration of pSeries guests from older libvirt
         * versions and back, we don't format the model name in the migratable
         * XML if it's spapr-pci-host-bridge, thus making "no model name" and
         * "spapr-pci-host-bridge model name" basically equivalent.
         *
         * The spapr-pci-host-bridge device is specific to pSeries.
         */
        if (def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT &&
            def->opts.pciopts.modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE &&
            flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) {
            formatModelName = false;
        }

        if (formatModelName) {
            modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
            if (!modelName) {
                virReportError(VIR_ERR_INTERNAL_ERROR,
                               _("unexpected model name value %d"),
                               def->opts.pciopts.modelName);
                return -1;
            }
            virBufferAsprintf(&childBuf, "<model name='%s'/>\n", modelName);
        }

        if (def->opts.pciopts.chassisNr != -1 ||
            def->opts.pciopts.chassis != -1 ||
            def->opts.pciopts.port != -1 ||
            def->opts.pciopts.busNr != -1 ||
            def->opts.pciopts.targetIndex != -1 ||
            def->opts.pciopts.numaNode != -1 ||
            def->opts.pciopts.hotplug != VIR_TRISTATE_SWITCH_ABSENT) {
            virBufferAddLit(&childBuf, "<target");
            if (def->opts.pciopts.chassisNr != -1)
                virBufferAsprintf(&childBuf, " chassisNr='%d'",
                                  def->opts.pciopts.chassisNr);
            if (def->opts.pciopts.chassis != -1)
                virBufferAsprintf(&childBuf, " chassis='%d'",
                                  def->opts.pciopts.chassis);
            if (def->opts.pciopts.port != -1)
                virBufferAsprintf(&childBuf, " port='0x%x'",
                                  def->opts.pciopts.port);
            if (def->opts.pciopts.busNr != -1)
                virBufferAsprintf(&childBuf, " busNr='%d'",
                                  def->opts.pciopts.busNr);
            if (def->opts.pciopts.targetIndex != -1)
                virBufferAsprintf(&childBuf, " index='%d'",
                                  def->opts.pciopts.targetIndex);
            if (def->opts.pciopts.hotplug != VIR_TRISTATE_SWITCH_ABSENT) {
                virBufferAsprintf(&childBuf, " hotplug='%s'",
                                  virTristateSwitchTypeToString(def->opts.pciopts.hotplug));
            }
            if (def->opts.pciopts.numaNode == -1) {
                virBufferAddLit(&childBuf, "/>\n");
            } else {
                virBufferAddLit(&childBuf, ">\n");
                virBufferAdjustIndent(&childBuf, 2);
                virBufferAsprintf(&childBuf, "<node>%d</node>\n",
                                  def->opts.pciopts.numaNode);
                virBufferAdjustIndent(&childBuf, -2);
                virBufferAddLit(&childBuf, "</target>\n");
            }
        }
    }

    if (virDomainControllerDriverFormat(&childBuf, def) < 0)
        return -1;

    if (virDomainDeviceInfoFormat(&childBuf, &def->info, flags) < 0)
        return -1;

    if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
        def->opts.pciopts.pcihole64) {
        virBufferAsprintf(&childBuf, "<pcihole64 unit='KiB'>%lu</"
                          "pcihole64>\n", def->opts.pciopts.pcihole64size);
    }

    virXMLFormatElement(buf, "controller", &attrBuf, &childBuf);

    return 0;
}


virDomainVirtioOptionsFormat(virBufferPtr buf,
                             virDomainVirtioOptionsPtr virtio)
{
    if (!virtio)
        return;

    if (virtio->iommu != VIR_TRISTATE_SWITCH_ABSENT) {
        virBufferAsprintf(buf, " iommu='%s'",
                          virTristateSwitchTypeToString(virtio->iommu));
    }
    if (virtio->ats != VIR_TRISTATE_SWITCH_ABSENT) {
        virBufferAsprintf(buf, " ats='%s'",
                          virTristateSwitchTypeToString(virtio->ats));
    }
    if (virtio->packed != VIR_TRISTATE_SWITCH_ABSENT) {
        virBufferAsprintf(buf, " packed='%s'",
                          virTristateSwitchTypeToString(virtio->packed));
    }
}


virDomainObjParseFile(const char *filename,
                      virDomainXMLOptionPtr xmlopt,
                      unsigned int flags)
{
    xmlDocPtr xml;
    virDomainObjPtr obj = NULL;
    int keepBlanksDefault = xmlKeepBlanksDefault(0);

    if ((xml = virXMLParseFile(filename))) {
        obj = virDomainObjParseNode(xml, xmlDocGetRootElement(xml),
                                    xmlopt, flags);
        xmlFreeDoc(xml);
    }

    xmlKeepBlanksDefault(keepBlanksDefault);
    return obj;
}


virDomainWatchdogDefParseXML(virDomainXMLOptionPtr xmlopt,
                             xmlNodePtr node,
                             unsigned int flags)
{
    virDomainWatchdogDefPtr def;
    g_autofree char *model = NULL;
    g_autofree char *action = NULL;

    if (VIR_ALLOC(def) < 0)
        return NULL;

    model = virXMLPropString(node, "model");
    if (model == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("watchdog must contain model name"));
        goto error;
    }
    def->model = virDomainWatchdogModelTypeFromString(model);
    if (def->model < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown watchdog model '%s'"), model);
        goto error;
    }

    action = virXMLPropString(node, "action");
    if (action == NULL) {
        def->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET;
    } else {
        def->action = virDomainWatchdogActionTypeFromString(action);
        if (def->action < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown watchdog action '%s'"), action);
            goto error;
        }
    }

    if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0)
        goto error;

    return def;

 error:
    virDomainWatchdogDefFree(def);
    return NULL;
}


virDomainFSRemove(virDomainDefPtr def, size_t i)
{
    virDomainFSDefPtr fs = def->fss[i];

    VIR_DELETE_ELEMENT(def->fss, i, def->nfss);
    return fs;
}


virDomainSpiceGLDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def)
{
    if (def->data.spice.gl == VIR_TRISTATE_BOOL_ABSENT)
        return;

    virBufferAsprintf(buf, "<gl enable='%s'",
                      virTristateBoolTypeToString(def->data.spice.gl));
    virBufferEscapeString(buf, " rendernode='%s'", def->data.spice.rendernode);
    virBufferAddLit(buf, "/>\n");
}


virDomainDeviceDimmAddressParseXML(xmlNodePtr node,
                                   virDomainDeviceDimmAddressPtr addr)
{
    g_autofree char *tmp = NULL;

    if (!(tmp = virXMLPropString(node, "slot")) ||
        virStrToLong_uip(tmp, NULL, 10, &addr->slot) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("invalid or missing dimm slot id '%s'"),
                       NULLSTR(tmp));
        return -1;
    }
    VIR_FREE(tmp);

    if ((tmp = virXMLPropString(node, "base"))) {
        if (virStrToLong_ullp(tmp, NULL, 16, &addr->base) < 0) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("invalid dimm base address '%s'"), tmp);
            return -1;
        }
    }

    return 0;
}


virDomainDiskIndexByName(virDomainDefPtr def, const char *name,
                         bool allow_ambiguous)
{
    virDomainDiskDefPtr vdisk;
    size_t i;
    int candidate = -1;

    /* We prefer the <target dev='name'/> name (it's shorter, required
     * for all disks, and should be unambiguous), but also support
     * <source file='name'/> (if unambiguous).  Assume dst if there is
     * no leading slash, source name otherwise.  */
    for (i = 0; i < def->ndisks; i++) {
        vdisk = def->disks[i];
        if (*name != '/') {
            if (STREQ(vdisk->dst, name))
                return i;
        } else if (STREQ_NULLABLE(virDomainDiskGetSource(vdisk), name)) {
            if (allow_ambiguous)
                return i;
            if (candidate >= 0)
                return -1;
            candidate = i;
        }
    }
    return candidate;
}


virDomainVirtioNetHostOptsFormat(char **outstr,
                                 virDomainNetDefPtr def)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    if (def->driver.virtio.host.csum) {
        virBufferAsprintf(&buf, "csum='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.csum));
    }
    if (def->driver.virtio.host.gso) {
        virBufferAsprintf(&buf, "gso='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.gso));
    }
    if (def->driver.virtio.host.tso4) {
        virBufferAsprintf(&buf, "tso4='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.tso4));
    }
    if (def->driver.virtio.host.tso6) {
        virBufferAsprintf(&buf, "tso6='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.tso6));
    }
    if (def->driver.virtio.host.ecn) {
        virBufferAsprintf(&buf, "ecn='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.ecn));
    }
    if (def->driver.virtio.host.ufo) {
        virBufferAsprintf(&buf, "ufo='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.ufo));
    }
    if (def->driver.virtio.host.mrg_rxbuf) {
        virBufferAsprintf(&buf, "mrg_rxbuf='%s' ",
                          virTristateSwitchTypeToString(def->driver.virtio.host.mrg_rxbuf));
    }
    virBufferTrim(&buf, " ");

    *outstr = virBufferContentAndReset(&buf);
    return 0;
}


virDomainParallelDefCheckABIStability(virDomainChrDefPtr src,
                                      virDomainChrDefPtr dst)
{
    if (src->target.port != dst->target.port) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target parallel port %d does not match source %d"),
                       dst->target.port, src->target.port);
        return false;
    }

    if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info))
        return false;

    return true;
}


virDomainPerfDefFormat(virBufferPtr buf, virDomainPerfDefPtr perf)
{
    size_t i;
    bool wantPerf = false;

    for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
        if (perf->events[i])
            wantPerf = true;
    }
    if (!wantPerf)
        return;

    virBufferAddLit(buf, "<perf>\n");
    virBufferAdjustIndent(buf, 2);

    for (i = 0; i < VIR_PERF_EVENT_LAST; i++) {
        if (perf->events[i])
            virBufferAsprintf(buf, "<event name='%s' enabled='%s'/>\n",
                              virPerfEventTypeToString(i),
                              virTristateBoolTypeToString(perf->events[i]));
    }

    virBufferAdjustIndent(buf, -2);
    virBufferAddLit(buf, "</perf>\n");
}


void virDomainHubDefFree(virDomainHubDefPtr def)
{
    if (!def)
        return;

    virDomainDeviceInfoClear(&def->info);
    VIR_FREE(def);
}


virDomainControllerIsPSeriesPHB(const virDomainControllerDef *cont)
{
    virDomainControllerPCIModelName name;

    /* PHBs are pci-root controllers */
    if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI ||
        cont->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
        return false;
    }

    name = cont->opts.pciopts.modelName;

    /* The actual device used for PHBs is spapr-pci-host-bridge */
    if (name != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE)
        return false;

    return true;
}


virDomainControllerDefNew(virDomainControllerType type)
{
    virDomainControllerDefPtr def;

    if (VIR_ALLOC(def) < 0)
        return NULL;

    def->type = type;

    /* initialize anything that has a non-0 default */
    def->model = -1;
    def->idx = -1;

    switch ((virDomainControllerType) def->type) {
    case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
        def->opts.vioserial.ports = -1;
        def->opts.vioserial.vectors = -1;
        break;
    case VIR_DOMAIN_CONTROLLER_TYPE_USB:
        def->opts.usbopts.ports = -1;
        break;
    case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
        def->opts.pciopts.chassisNr = -1;
        def->opts.pciopts.chassis = -1;
        def->opts.pciopts.port = -1;
        def->opts.pciopts.busNr = -1;
        def->opts.pciopts.targetIndex = -1;
        def->opts.pciopts.numaNode = -1;
        break;
    case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS:
        def->opts.xenbusopts.maxGrantFrames = -1;
        def->opts.xenbusopts.maxEventChannels = -1;
        break;
    case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
    case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
    case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
    case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
    case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
    case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
        break;
    }

    return def;
}


virDomainDiskControllerMatch(int controller_type, int disk_bus)
{
    if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
        disk_bus == VIR_DOMAIN_DISK_BUS_SCSI)
        return true;

    if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_FDC &&
        disk_bus == VIR_DOMAIN_DISK_BUS_FDC)
        return true;

    if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
        disk_bus == VIR_DOMAIN_DISK_BUS_IDE)
        return true;

    if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SATA &&
        disk_bus == VIR_DOMAIN_DISK_BUS_SATA)
        return true;

    return false;
}


virDomainRedirFilterDefParseXML(xmlNodePtr node,
                                xmlXPathContextPtr ctxt)
{
    int n;
    size_t i;
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    virDomainRedirFilterDefPtr def = NULL;
    g_autofree xmlNodePtr *nodes = NULL;

    if (VIR_ALLOC(def) < 0)
        goto error;

    ctxt->node = node;
    if ((n = virXPathNodeSet("./usbdev", ctxt, &nodes)) < 0)
        goto error;

    if (n && VIR_ALLOC_N(def->usbdevs, n) < 0)
        goto error;

    for (i = 0; i < n; i++) {
        virDomainRedirFilterUSBDevDefPtr usbdev =
            virDomainRedirFilterUSBDevDefParseXML(nodes[i]);

        if (!usbdev)
            goto error;
        def->usbdevs[def->nusbdevs++] = usbdev;
    }

    return def;

 error:
    virDomainRedirFilterDefFree(def);
    return NULL;
}


virDomainDefRejectDuplicateControllers(virDomainDefPtr def)
{
    int max_idx[VIR_DOMAIN_CONTROLLER_TYPE_LAST];
    virBitmapPtr bitmaps[VIR_DOMAIN_CONTROLLER_TYPE_LAST] = { NULL };
    virDomainControllerDefPtr cont;
    size_t nbitmaps = 0;
    int ret = -1;
    size_t i;

    memset(max_idx, -1, sizeof(max_idx));

    for (i = 0; i < def->ncontrollers; i++) {
        cont = def->controllers[i];
        if (cont->idx > max_idx[cont->type])
            max_idx[cont->type] = cont->idx;
    }

    /* multiple USB controllers with the same index are allowed */
    max_idx[VIR_DOMAIN_CONTROLLER_TYPE_USB] = -1;

    for (i = 0; i < VIR_DOMAIN_CONTROLLER_TYPE_LAST; i++) {
        if (max_idx[i] >= 0 && !(bitmaps[i] = virBitmapNew(max_idx[i] + 1)))
            goto cleanup;
        nbitmaps++;
    }

    for (i = 0; i < def->ncontrollers; i++) {
        cont = def->controllers[i];

        if (max_idx[cont->type] == -1)
            continue;

        if (virBitmapIsBitSet(bitmaps[cont->type], cont->idx)) {
            virReportError(VIR_ERR_XML_ERROR,
                           _("Multiple '%s' controllers with index '%d'"),
                           virDomainControllerTypeToString(cont->type),
                           cont->idx);
            goto cleanup;
        }
        ignore_value(virBitmapSetBit(bitmaps[cont->type], cont->idx));
    }

    ret = 0;
 cleanup:
    for (i = 0; i < nbitmaps; i++)
        virBitmapFree(bitmaps[i]);
    return ret;
}


virDomainSchedulerParseCommonAttrs(xmlNodePtr node,
                                   virProcessSchedPolicy *policy,
                                   int *priority)
{
    int pol = 0;
    g_autofree char *tmp = NULL;

    if (!(tmp = virXMLPropString(node, "scheduler"))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Missing scheduler attribute"));
        return -1;
    }

    if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Invalid scheduler attribute: '%s'"), tmp);
        return -1;
    }
    *policy = pol;

    VIR_FREE(tmp);

    if (pol == VIR_PROC_POLICY_FIFO ||
        pol == VIR_PROC_POLICY_RR) {
        if (!(tmp = virXMLPropString(node, "priority"))) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("Missing scheduler priority"));
            return -1;
        }

        if (virStrToLong_i(tmp, NULL, 10, priority) < 0) {
            virReportError(VIR_ERR_XML_ERROR, "%s",
                           _("Invalid value for element priority"));
            return -1;
        }
    }

    return 0;
}