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