virDomainDefCheckABIStabilityFlags(virDomainDefPtr src,
                                   virDomainDefPtr dst,
                                   virDomainXMLOptionPtr xmlopt,
                                   unsigned int flags)
{
    size_t i;
    virErrorPtr err;
    g_autofree char *strSrc = NULL;
    g_autofree char *strDst = NULL;

    if (src->virtType != dst->virtType) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain virt type %s does not match source %s"),
                       virDomainVirtTypeToString(dst->virtType),
                       virDomainVirtTypeToString(src->virtType));
        goto error;
    }

    if (memcmp(src->uuid, dst->uuid, VIR_UUID_BUFLEN) != 0) {
        char uuidsrc[VIR_UUID_STRING_BUFLEN];
        char uuiddst[VIR_UUID_STRING_BUFLEN];
        virUUIDFormat(src->uuid, uuidsrc);
        virUUIDFormat(dst->uuid, uuiddst);
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain uuid %s does not match source %s"),
                       uuiddst, uuidsrc);
        goto error;
    }

    if (src->genidRequested != dst->genidRequested) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target domain requested genid does not match source"));
        goto error;
    }

    if (src->genidRequested &&
        memcmp(src->genid, dst->genid, VIR_UUID_BUFLEN) != 0) {
        char guidsrc[VIR_UUID_STRING_BUFLEN];
        char guiddst[VIR_UUID_STRING_BUFLEN];

        virUUIDFormat(src->genid, guidsrc);
        virUUIDFormat(dst->genid, guiddst);
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain genid %s does not match source %s"),
                       guiddst, guidsrc);
        goto error;
    }

    /* Not strictly ABI related, but we want to make sure domains
     * don't get silently re-named through the backdoor when passing
     * custom XML into various APIs, since this would create havoc
     */
    if (STRNEQ_NULLABLE(src->name, dst->name)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain name '%s' does not match source '%s'"),
                       dst->name, src->name);
        goto error;
    }

    if (!virDomainMemtuneCheckABIStability(src, dst, flags))
        goto error;

    if (!virDomainNumaCheckABIStability(src->numa, dst->numa))
        goto error;

    if (!virDomainDefVcpuCheckAbiStability(src, dst))
        goto error;

    if (src->os.type != dst->os.type) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain OS type %s does not match source %s"),
                       virDomainOSTypeToString(dst->os.type),
                       virDomainOSTypeToString(src->os.type));
        goto error;
    }
    if (src->os.arch != dst->os.arch) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain architecture %s does not match source %s"),
                       virArchToString(dst->os.arch),
                       virArchToString(src->os.arch));
        goto error;
    }
    if (STRNEQ_NULLABLE(src->os.machine, dst->os.machine)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                    _("Target domain machine type %s does not match source %s"),
                    dst->os.machine, src->os.machine);
        goto error;
    }

    if (src->os.smbios_mode != dst->os.smbios_mode) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain SMBIOS mode %s does not match source %s"),
                       virDomainSmbiosModeTypeToString(dst->os.smbios_mode),
                       virDomainSmbiosModeTypeToString(src->os.smbios_mode));
        goto error;
    }

    if (!virDomainDefFeaturesCheckABIStability(src, dst))
        goto error;

    if (src->clock.ntimers != dst->clock.ntimers) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target domain timers do not match source"));
        goto error;
    }

    for (i = 0; i < src->clock.ntimers; i++) {
        if (!virDomainTimerDefCheckABIStability(src->clock.timers[i],
                                                dst->clock.timers[i]))
            goto error;
    }

    if (!virCPUDefIsEqual(src->cpu, dst->cpu, true))
        goto error;

    if (!virSysinfoIsEqual(src->sysinfo, dst->sysinfo))
        goto error;

    if (src->ndisks != dst->ndisks) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain disk count %zu does not match source %zu"),
                       dst->ndisks, src->ndisks);
        goto error;
    }

    for (i = 0; i < src->ndisks; i++)
        if (!virDomainDiskDefCheckABIStability(src->disks[i], dst->disks[i]))
            goto error;

    if (src->ncontrollers != dst->ncontrollers) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain controller count %zu "
                         "does not match source %zu"),
                       dst->ncontrollers, src->ncontrollers);
        goto error;
    }

    for (i = 0; i < src->ncontrollers; i++)
        if (!virDomainControllerDefCheckABIStability(src->controllers[i],
                                                     dst->controllers[i]))
            goto error;

    if (src->nfss != dst->nfss) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain filesystem count %zu "
                         "does not match source %zu"),
                       dst->nfss, src->nfss);
        goto error;
    }

    for (i = 0; i < src->nfss; i++)
        if (!virDomainFsDefCheckABIStability(src->fss[i], dst->fss[i]))
            goto error;

    if (src->nnets != dst->nnets) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain net card count %zu "
                         "does not match source %zu"),
                       dst->nnets, src->nnets);
        goto error;
    }

    for (i = 0; i < src->nnets; i++)
        if (!virDomainNetDefCheckABIStability(src->nets[i], dst->nets[i]))
            goto error;

    if (src->ninputs != dst->ninputs) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain input device count %zu "
                         "does not match source %zu"),
                       dst->ninputs, src->ninputs);
        goto error;
    }

    for (i = 0; i < src->ninputs; i++)
        if (!virDomainInputDefCheckABIStability(src->inputs[i], dst->inputs[i]))
            goto error;

    if (src->nsounds != dst->nsounds) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain sound card count %zu "
                         "does not match source %zu"),
                       dst->nsounds, src->nsounds);
        goto error;
    }

    for (i = 0; i < src->nsounds; i++)
        if (!virDomainSoundDefCheckABIStability(src->sounds[i], dst->sounds[i]))
            goto error;

    if (src->nvideos != dst->nvideos) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain video card count %zu "
                         "does not match source %zu"),
                       dst->nvideos, src->nvideos);
        goto error;
    }

    for (i = 0; i < src->nvideos; i++)
        if (!virDomainVideoDefCheckABIStability(src->videos[i], dst->videos[i]))
            goto error;

    if (src->nhostdevs != dst->nhostdevs) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain host device count %zu "
                         "does not match source %zu"),
                       dst->nhostdevs, src->nhostdevs);
        goto error;
    }

    for (i = 0; i < src->nhostdevs; i++)
        if (!virDomainHostdevDefCheckABIStability(src->hostdevs[i],
                                                  dst->hostdevs[i]))
            goto error;

    if (src->nsmartcards != dst->nsmartcards) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain smartcard count %zu "
                         "does not match source %zu"),
                       dst->nsmartcards, src->nsmartcards);
        goto error;
    }

    for (i = 0; i < src->nsmartcards; i++)
        if (!virDomainSmartcardDefCheckABIStability(src->smartcards[i],
                                                    dst->smartcards[i]))
            goto error;

    if (src->nserials != dst->nserials) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain serial port count %zu "
                         "does not match source %zu"),
                       dst->nserials, src->nserials);
        goto error;
    }

    for (i = 0; i < src->nserials; i++)
        if (!virDomainSerialDefCheckABIStability(src->serials[i],
                                                 dst->serials[i]))
            goto error;

    if (src->nparallels != dst->nparallels) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain parallel port count %zu "
                         "does not match source %zu"),
                       dst->nparallels, src->nparallels);
        goto error;
    }

    for (i = 0; i < src->nparallels; i++)
        if (!virDomainParallelDefCheckABIStability(src->parallels[i],
                                                   dst->parallels[i]))
            goto error;

    if (src->nchannels != dst->nchannels) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain channel count %zu "
                         "does not match source %zu"),
                       dst->nchannels, src->nchannels);
        goto error;
    }

    for (i = 0; i < src->nchannels; i++)
        if (!virDomainChannelDefCheckABIStability(src->channels[i],
                                                  dst->channels[i]))
            goto error;

    if (src->nconsoles != dst->nconsoles) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain console count %zu "
                         "does not match source %zu"),
                       dst->nconsoles, src->nconsoles);
        goto error;
    }

    for (i = 0; i < src->nconsoles; i++)
        if (!virDomainConsoleDefCheckABIStability(src->consoles[i],
                                                  dst->consoles[i]))
            goto error;

    if (src->nhubs != dst->nhubs) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain hub device count %zu "
                         "does not match source %zu"),
                       dst->nhubs, src->nhubs);
        goto error;
    }

    for (i = 0; i < src->nhubs; i++)
        if (!virDomainHubDefCheckABIStability(src->hubs[i], dst->hubs[i]))
            goto error;

    if (src->nredirdevs != dst->nredirdevs) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain redirected devices count %zu "
                         "does not match source %zu"),
                       dst->nredirdevs, src->nredirdevs);
        goto error;
    }

    for (i = 0; i < src->nredirdevs; i++) {
        if (!virDomainRedirdevDefCheckABIStability(src->redirdevs[i],
                                                   dst->redirdevs[i]))
            goto error;
    }

    if ((!src->redirfilter && dst->redirfilter) ||
        (src->redirfilter && !dst->redirfilter)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain USB redirection filter count %d "
                         "does not match source %d"),
                       dst->redirfilter ? 1 : 0, src->redirfilter ? 1 : 0);
        goto error;
    }

    if (src->redirfilter &&
        !virDomainRedirFilterDefCheckABIStability(src->redirfilter,
                                                  dst->redirfilter))
        goto error;

    if ((!src->watchdog && dst->watchdog) ||
        (src->watchdog && !dst->watchdog)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain watchdog count %d "
                         "does not match source %d"),
                       dst->watchdog ? 1 : 0, src->watchdog ? 1 : 0);
        goto error;
    }

    if (src->watchdog &&
        !virDomainWatchdogDefCheckABIStability(src->watchdog, dst->watchdog))
        goto error;

    if ((!src->memballoon && dst->memballoon) ||
        (src->memballoon && !dst->memballoon)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain memory balloon count %d "
                         "does not match source %d"),
                       dst->memballoon ? 1 : 0, src->memballoon ? 1 : 0);
        goto error;
    }

    if (src->memballoon &&
        !virDomainMemballoonDefCheckABIStability(src->memballoon,
                                                 dst->memballoon))
        goto error;

    if (src->nrngs != dst->nrngs) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain RNG device count %zu "
                         "does not match source %zu"), dst->nrngs, src->nrngs);
        goto error;
    }

    for (i = 0; i < src->nrngs; i++)
        if (!virDomainRNGDefCheckABIStability(src->rngs[i], dst->rngs[i]))
            goto error;

    if (src->npanics != dst->npanics) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain panic device count %zu "
                         "does not match source %zu"), dst->npanics, src->npanics);
        goto error;
    }

    for (i = 0; i < src->npanics; i++) {
        if (!virDomainPanicDefCheckABIStability(src->panics[i], dst->panics[i]))
            goto error;
    }

    if (src->nshmems != dst->nshmems) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain shared memory device count %zu "
                         "does not match source %zu"), dst->nshmems, src->nshmems);
        goto error;
    }

    for (i = 0; i < src->nshmems; i++) {
        if (!virDomainShmemDefCheckABIStability(src->shmems[i], dst->shmems[i]))
            goto error;
    }

    if (src->tpm && dst->tpm) {
        if (!virDomainTPMDefCheckABIStability(src->tpm, dst->tpm))
            goto error;
    } else if (src->tpm || dst->tpm) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Either both target and source domains or none of "
                         "them must have TPM device present"));
        goto error;
    }

    if (src->nmems != dst->nmems) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Target domain memory device count %zu "
                         "does not match source %zu"), dst->nmems, src->nmems);
        goto error;
    }

    for (i = 0; i < src->nmems; i++) {
        if (!virDomainMemoryDefCheckABIStability(src->mems[i], dst->mems[i]))
            goto error;
    }

    if (!!src->iommu != !!dst->iommu) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target domain IOMMU device count "
                         "does not match source"));
        goto error;
    }

    if (src->iommu &&
        !virDomainIOMMUDefCheckABIStability(src->iommu, dst->iommu))
        goto error;

    if (!!src->vsock != !!dst->vsock) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                       _("Target domain vsock device count "
                         "does not match source"));
        goto error;
    }

    if (src->vsock &&
        !virDomainVsockDefCheckABIStability(src->vsock, dst->vsock))
        goto error;

    if (xmlopt && xmlopt->abi.domain &&
        !xmlopt->abi.domain(src, dst))
        goto error;

    /* Coverity is not very happy with this - all dead_error_condition */
#if !STATIC_ANALYSIS
    /* This switch statement is here to trigger compiler warning when adding
     * a new device type. When you are adding a new field to the switch you
     * also have to add an check above. Otherwise the switch statement has no
     * real function here and should be optimized out by the compiler. */
    i = VIR_DOMAIN_DEVICE_LAST;
    switch ((virDomainDeviceType) i) {
    case VIR_DOMAIN_DEVICE_DISK:
    case VIR_DOMAIN_DEVICE_LEASE:
    case VIR_DOMAIN_DEVICE_FS:
    case VIR_DOMAIN_DEVICE_NET:
    case VIR_DOMAIN_DEVICE_INPUT:
    case VIR_DOMAIN_DEVICE_SOUND:
    case VIR_DOMAIN_DEVICE_VIDEO:
    case VIR_DOMAIN_DEVICE_HOSTDEV:
    case VIR_DOMAIN_DEVICE_WATCHDOG:
    case VIR_DOMAIN_DEVICE_CONTROLLER:
    case VIR_DOMAIN_DEVICE_GRAPHICS:
    case VIR_DOMAIN_DEVICE_HUB:
    case VIR_DOMAIN_DEVICE_REDIRDEV:
    case VIR_DOMAIN_DEVICE_NONE:
    case VIR_DOMAIN_DEVICE_SMARTCARD:
    case VIR_DOMAIN_DEVICE_CHR:
    case VIR_DOMAIN_DEVICE_MEMBALLOON:
    case VIR_DOMAIN_DEVICE_NVRAM:
    case VIR_DOMAIN_DEVICE_LAST:
    case VIR_DOMAIN_DEVICE_RNG:
    case VIR_DOMAIN_DEVICE_TPM:
    case VIR_DOMAIN_DEVICE_PANIC:
    case VIR_DOMAIN_DEVICE_SHMEM:
    case VIR_DOMAIN_DEVICE_MEMORY:
    case VIR_DOMAIN_DEVICE_IOMMU:
    case VIR_DOMAIN_DEVICE_VSOCK:
        break;
    }
#endif

    return true;

 error:
    virErrorPreserveLast(&err);

    strSrc = virDomainDefFormat(src, xmlopt, 0);
    strDst = virDomainDefFormat(dst, xmlopt, 0);
    VIR_DEBUG("XMLs that failed stability check were: src=\"%s\", dst=\"%s\"",
              NULLSTR(strSrc), NULLSTR(strDst));

    virErrorRestore(&err);
    return false;
}


virDomainDefParseString(const char *xmlStr,
                        virDomainXMLOptionPtr xmlopt,
                        void *parseOpaque,
                        unsigned int flags)
{
    return virDomainDefParse(xmlStr, NULL, xmlopt, parseOpaque, flags);
}


virDomainIOThreadPinDefParseXML(xmlNodePtr node,
                                virDomainDefPtr def)
{
    virDomainIOThreadIDDefPtr iothrid;
    unsigned int iothreadid;
    g_autofree char *tmp = NULL;
    g_autoptr(virBitmap) cpumask = NULL;

    if (!(tmp = virXMLPropString(node, "iothread"))) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("missing iothread id in iothreadpin"));
        return -1;
    }

    if (virStrToLong_uip(tmp, NULL, 10, &iothreadid) < 0) {
        virReportError(VIR_ERR_XML_ERROR,
                       _("invalid setting for iothread '%s'"), tmp);
        return -1;
    }
    VIR_FREE(tmp);

    if (iothreadid == 0) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("zero is an invalid iothread id value"));
        return -1;
    }

    if (!(iothrid = virDomainIOThreadIDFind(def, iothreadid))) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Cannot find 'iothread' : %u"),
                       iothreadid);
        return -1;
    }

    if (!(tmp = virXMLPropString(node, "cpuset"))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("missing cpuset for iothreadpin"));
        return -1;
    }

    if (virBitmapParse(tmp, &cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0)
        return -1;

    if (virBitmapIsAllClear(cpumask)) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("Invalid value of 'cpuset': %s"),
                       tmp);
        return -1;
    }

    if (iothrid->cpumask) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("duplicate iothreadpin for same iothread '%u'"),
                       iothreadid);
        return -1;
    }

    iothrid->cpumask = g_steal_pointer(&cpumask);
    return 0;
}


virDomainControllerDefPostParse(virDomainControllerDefPtr cdev)
{
    if (cdev->iothread &&
        cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI &&
        cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL &&
        cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("'iothread' attribute only supported for "
                         "virtio scsi controllers"));
        return -1;
    }

    return 0;
}


virDomainChrSourceDefNew(virDomainXMLOptionPtr xmlopt)
{
    virDomainChrSourceDefPtr def = NULL;

    if (virDomainChrSourceDefInitialize() < 0)
        return NULL;

    if (!(def = virObjectNew(virDomainChrSourceDefClass)))
        return NULL;

    if (xmlopt && xmlopt->privateData.chrSourceNew &&
        !(def->privateData = xmlopt->privateData.chrSourceNew())) {
        virObjectUnref(def);
        def = NULL;
    }

    return def;
}


virDomainHubDefFormat(virBufferPtr buf,
                      virDomainHubDefPtr def,
                      unsigned int flags)
{
    const char *type = virDomainHubTypeToString(def->type);
    g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
    g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);

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

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

    virBufferAsprintf(&attrBuf, " type='%s'", type);

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

    return 0;
}


virDomainDeviceInfoParseXML(virDomainXMLOptionPtr xmlopt,
                            xmlNodePtr node,
                            virDomainDeviceInfoPtr info,
                            unsigned int flags)
{
    xmlNodePtr cur;
    xmlNodePtr address = NULL;
    xmlNodePtr master = NULL;
    xmlNodePtr alias = NULL;
    xmlNodePtr boot = NULL;
    xmlNodePtr rom = NULL;
    int ret = -1;
    g_autofree char *romenabled = NULL;
    g_autofree char *rombar = NULL;
    g_autofree char *aliasStr = NULL;

    virDomainDeviceInfoClear(info);

    cur = node->children;
    while (cur != NULL) {
        if (cur->type == XML_ELEMENT_NODE) {
            if (alias == NULL &&
                virXMLNodeNameEqual(cur, "alias")) {
                alias = cur;
            } else if (address == NULL &&
                       virXMLNodeNameEqual(cur, "address")) {
                address = cur;
            } else if (master == NULL &&
                       virXMLNodeNameEqual(cur, "master")) {
                master = cur;
            } else if (boot == NULL &&
                       (flags & VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT) &&
                       virXMLNodeNameEqual(cur, "boot")) {
                boot = cur;
            } else if (rom == NULL &&
                       (flags & VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) &&
                       virXMLNodeNameEqual(cur, "rom")) {
                rom = cur;
            }
        }
        cur = cur->next;
    }

    if (alias) {
        aliasStr = virXMLPropString(alias, "name");

        if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) ||
            (xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_USER_ALIAS &&
             virDomainDeviceAliasIsUserAlias(aliasStr) &&
             strspn(aliasStr, USER_ALIAS_CHARS) == strlen(aliasStr)))
            info->alias = g_steal_pointer(&aliasStr);
    }

    if (master) {
        info->mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB;
        if (virDomainDeviceUSBMasterParseXML(master, &info->master.usb) < 0)
            goto cleanup;
    }

    if (boot) {
        if (virDomainDeviceBootParseXML(boot, info))
            goto cleanup;
    }

    if (rom) {
        if ((romenabled = virXMLPropString(rom, "enabled")) &&
            ((info->romenabled = virTristateBoolTypeFromString(romenabled)) <= 0)) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown rom enabled value '%s'"), romenabled);
            goto cleanup;
        }
        if ((rombar = virXMLPropString(rom, "bar")) &&
            ((info->rombar = virTristateSwitchTypeFromString(rombar)) <= 0)) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown rom bar value '%s'"), rombar);
            goto cleanup;
        }
        info->romfile = virXMLPropString(rom, "file");

        if (info->romenabled == VIR_TRISTATE_BOOL_NO &&
            (info->rombar != VIR_TRISTATE_SWITCH_ABSENT || info->romfile)) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
                           _("ROM tuning is not supported when ROM is disabled"));
            goto cleanup;
        }
    }

    if (address &&
        virDomainDeviceAddressParseXML(address, info) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    if (ret < 0)
        virDomainDeviceInfoClear(info);
    return ret;
}


virDomainResctrlMonDefFormatHelper(virDomainResctrlMonDefPtr domresmon,
                                   virResctrlMonitorType tag,
                                   virBufferPtr buf)
{
    g_autofree char *vcpus = NULL;

    if (domresmon->tag != tag)
        return 0;

    virBufferAddLit(buf, "<monitor ");

    if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) {
        virBufferAsprintf(buf, "level='%u' ",
                          VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL);
    }

    vcpus = virBitmapFormat(domresmon->vcpus);
    if (!vcpus)
        return -1;

    virBufferAsprintf(buf, "vcpus='%s'/>\n", vcpus);

    return 0;
}


virDomainGetBlkioParametersAssignFromDef(virDomainDefPtr def,
                                         virTypedParameterPtr params,
                                         int *nparams,
                                         int maxparams)
{
    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
    char *data = NULL;
    size_t i;

#define VIR_BLKIO_ASSIGN(param, format, name) \
    if (*nparams < maxparams) { \
        for (i = 0; i < def->blkio.ndevices; i++) { \
            if (!def->blkio.devices[i].param) \
                continue; \
            virBufferAsprintf(&buf, "%s," format ",", \
                              def->blkio.devices[i].path, \
                              def->blkio.devices[i].param); \
        } \
        virBufferTrim(&buf, ","); \
        data = virBufferContentAndReset(&buf); \
        if (virTypedParameterAssign(&(params[(*nparams)++]), name, \
                                    VIR_TYPED_PARAM_STRING, data) < 0) \
            goto error; \
        data = NULL; \
    }

    /* blkiotune.device_weight */
    VIR_BLKIO_ASSIGN(weight, "%u", VIR_DOMAIN_BLKIO_DEVICE_WEIGHT);
    /* blkiotune.device_read_iops */
    VIR_BLKIO_ASSIGN(riops, "%u", VIR_DOMAIN_BLKIO_DEVICE_READ_IOPS);
    /* blkiotune.device_write_iops */
    VIR_BLKIO_ASSIGN(wiops, "%u", VIR_DOMAIN_BLKIO_DEVICE_WRITE_IOPS);
    /* blkiotune.device_read_bps */
    VIR_BLKIO_ASSIGN(rbps, "%llu", VIR_DOMAIN_BLKIO_DEVICE_READ_BPS);
    /* blkiotune.device_write_bps */
    VIR_BLKIO_ASSIGN(wbps, "%llu", VIR_DOMAIN_BLKIO_DEVICE_WRITE_BPS);

#undef VIR_BLKIO_ASSIGN

    return 0;

 error:
    VIR_FREE(data);
    return -1;
}


virDomainSEVDefFree(virDomainSEVDefPtr def)
{
    if (!def)
        return;

    VIR_FREE(def->dh_cert);
    VIR_FREE(def->session);

    VIR_FREE(def);
}


virDomainMemorySourceDefParseXML(xmlNodePtr node,
                                 xmlXPathContextPtr ctxt,
                                 virDomainMemoryDefPtr def)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    ctxt->node = node;
    g_autofree char *nodemask = NULL;

    switch ((virDomainMemoryModel) def->model) {
    case VIR_DOMAIN_MEMORY_MODEL_DIMM:
        if (virDomainParseMemory("./pagesize", "./pagesize/@unit", ctxt,
                                 &def->pagesize, false, false) < 0)
            return -1;

        if ((nodemask = virXPathString("string(./nodemask)", ctxt))) {
            if (virBitmapParse(nodemask, &def->sourceNodes,
                               VIR_DOMAIN_CPUMASK_LEN) < 0)
                return -1;

            if (virBitmapIsAllClear(def->sourceNodes)) {
                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                               _("Invalid value of 'nodemask': %s"), nodemask);
                return -1;
            }
        }
        break;

    case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
        if (!(def->nvdimmPath = virXPathString("string(./path)", ctxt))) {
            virReportError(VIR_ERR_XML_DETAIL, "%s",
                           _("path is required for model 'nvdimm'"));
            return -1;
        }

        if (virDomainParseMemory("./alignsize", "./alignsize/@unit", ctxt,
                                 &def->alignsize, false, false) < 0)
            return -1;

        if (virXPathBoolean("boolean(./pmem)", ctxt))
            def->nvdimmPmem = true;

        break;

    case VIR_DOMAIN_MEMORY_MODEL_NONE:
    case VIR_DOMAIN_MEMORY_MODEL_LAST:
        break;
    }

    return 0;
}


virDomainTimerDefParseXML(xmlNodePtr node,
                          xmlXPathContextPtr ctxt)
{
    virDomainTimerDefPtr def;
    VIR_XPATH_NODE_AUTORESTORE(ctxt);
    xmlNodePtr catchup;
    int ret;
    g_autofree char *name = NULL;
    g_autofree char *present = NULL;
    g_autofree char *tickpolicy = NULL;
    g_autofree char *track = NULL;
    g_autofree char *mode = NULL;

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

    ctxt->node = node;

    name = virXMLPropString(node, "name");
    if (name == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("missing timer name"));
        goto error;
    }
    if ((def->name = virDomainTimerNameTypeFromString(name)) < 0) {
        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                       _("unknown timer name '%s'"), name);
        goto error;
    }

    def->present = -1; /* unspecified */
    if ((present = virXMLPropString(node, "present")) != NULL) {
        bool state = false;
        if (virStringParseYesNo(present, &state) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unknown timer present value '%s'"), present);
            goto error;
        }
        def->present = state ? 1 : 0;
    }

    def->tickpolicy = -1;
    tickpolicy = virXMLPropString(node, "tickpolicy");
    if (tickpolicy != NULL) {
        if ((def->tickpolicy = virDomainTimerTickpolicyTypeFromString(tickpolicy)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown timer tickpolicy '%s'"), tickpolicy);
            goto error;
        }
    }

    def->track = -1;
    track = virXMLPropString(node, "track");
    if (track != NULL) {
        if ((def->track = virDomainTimerTrackTypeFromString(track)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown timer track '%s'"), track);
            goto error;
        }
    }

    ret = virXPathULong("string(./@frequency)", ctxt, &def->frequency);
    if (ret == -1) {
        def->frequency = 0;
    } else if (ret < 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("invalid timer frequency"));
        goto error;
    }

    def->mode = -1;
    mode = virXMLPropString(node, "mode");
    if (mode != NULL) {
        if ((def->mode = virDomainTimerModeTypeFromString(mode)) < 0) {
            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                           _("unknown timer mode '%s'"), mode);
            goto error;
        }
    }

    catchup = virXPathNode("./catchup", ctxt);
    if (catchup != NULL) {
        ret = virXPathULong("string(./catchup/@threshold)", ctxt,
                            &def->catchup.threshold);
        if (ret == -1) {
            def->catchup.threshold = 0;
        } else if (ret < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("invalid catchup threshold"));
            goto error;
        }

        ret = virXPathULong("string(./catchup/@slew)", ctxt, &def->catchup.slew);
        if (ret == -1) {
            def->catchup.slew = 0;
        } else if (ret < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("invalid catchup slew"));
            goto error;
        }

        ret = virXPathULong("string(./catchup/@limit)", ctxt, &def->catchup.limit);
        if (ret == -1) {
            def->catchup.limit = 0;
        } else if (ret < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s", _("invalid catchup limit"));
            goto error;
        }
    }

    return def;

 error:
    VIR_FREE(def);
    return def;
}


virDomainGraphicsListenDefFormat(virBufferPtr buf,
                                 virDomainGraphicsListenDefPtr def,
                                 unsigned int flags)
{
    /* If generating migratable XML, skip listen address
     * dragged in from config file */
    if ((flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) && def->fromConfig)
        return;

    virBufferAddLit(buf, "<listen");
    virBufferAsprintf(buf, " type='%s'",
                      virDomainGraphicsListenTypeToString(def->type));

    if (def->address &&
        (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS ||
         (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK &&
          !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)))) {
        /* address may also be set to show current status when type='network',
         * but we don't want to print that if INACTIVE data is requested. */
        virBufferAsprintf(buf, " address='%s'", def->address);
    }

    if (def->network &&
        (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK)) {
        virBufferEscapeString(buf, " network='%s'", def->network);
    }

    if (def->socket &&
        def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
        !(def->autoGenerated &&
          (flags & VIR_DOMAIN_DEF_FORMAT_MIGRATABLE))) {
        virBufferEscapeString(buf, " socket='%s'", def->socket);
    }

    if (flags & VIR_DOMAIN_DEF_FORMAT_STATUS) {
        virBufferAsprintf(buf, " fromConfig='%d'", def->fromConfig);
        virBufferAsprintf(buf, " autoGenerated='%s'",
                          def->autoGenerated ? "yes" : "no");
    }

    virBufferAddLit(buf, "/>\n");
}


_dbus_delete_directory (const DBusString *filename,
                        DBusError        *error)
{
  const char *filename_c;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  filename_c = _dbus_string_get_const_data (filename);

  if (rmdir (filename_c) != 0)
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Failed to remove directory %s: %s\n",
                      filename_c, _dbus_strerror (errno));
      return FALSE;
    }

  return TRUE;
}


_dbus_pid_for_log (void)
{
  return getpid ();
}


_dbus_write_socket (DBusSocket        fd,
                    const DBusString *buffer,
                    int               start,
                    int               len)
{
#if HAVE_DECL_MSG_NOSIGNAL
  const char *data;
  int bytes_written;

  data = _dbus_string_get_const_data_len (buffer, start, len);

 again:

  bytes_written = send (fd.fd, data, len, MSG_NOSIGNAL);

  if (bytes_written < 0 && errno == EINTR)
    goto again;

  return bytes_written;

#else
  return _dbus_write (fd.fd, buffer, start, len);
#endif
}


_dbus_user_info_fill_uid (DBusUserInfo *info,
                          dbus_uid_t    uid,
                          DBusError    *error)
{
  return fill_user_info (info, uid,
                         NULL, error);
}


_dbus_generate_random_bytes (DBusString *str,
                             int         n_bytes,
                             DBusError  *error)
{
  int old_len = _dbus_string_get_length (str);
  int fd;
  int result;
#ifdef HAVE_GETRANDOM
  char *buffer;

  if (!_dbus_string_lengthen (str, n_bytes))
    {
      _DBUS_SET_OOM (error);
      return FALSE;
    }

  buffer = _dbus_string_get_data_len (str, old_len, n_bytes);
  result = getrandom (buffer, n_bytes, GRND_NONBLOCK);

  if (result == n_bytes)
    return TRUE;

  _dbus_string_set_length (str, old_len);
#endif

  /* note, urandom on linux will fall back to pseudorandom */
  fd = open ("/dev/urandom", O_RDONLY);

  if (fd < 0)
    {
      dbus_set_error (error, _dbus_error_from_errno (errno),
                      "Could not open /dev/urandom: %s",
                      _dbus_strerror (errno));
      return FALSE;
    }

  _dbus_verbose ("/dev/urandom fd %d opened\n", fd);

  result = _dbus_read (fd, str, n_bytes);

  if (result != n_bytes)
    {
      if (result < 0)
        dbus_set_error (error, _dbus_error_from_errno (errno),
                        "Could not read /dev/urandom: %s",
                        _dbus_strerror (errno));
      else
        dbus_set_error (error, DBUS_ERROR_IO_ERROR,
                        "Short read from /dev/urandom");

      _dbus_close (fd, NULL);
      _dbus_string_set_length (str, old_len);
      return FALSE;
    }

  _dbus_verbose ("Read %d bytes from /dev/urandom\n",
                 n_bytes);

  _dbus_close (fd, NULL);

  return TRUE;
}


_dbus_set_socket_nonblocking (DBusSocket      fd,
                              DBusError      *error)
{
  return _dbus_set_fd_nonblocking (fd.fd, error);
}


_dbus_atomic_inc (DBusAtomic *atomic)
{
#if DBUS_USE_SYNC
  return __sync_add_and_fetch(&atomic->value, 1)-1;
#else
  dbus_int32_t res;

  pthread_mutex_lock (&atomic_mutex);
  res = atomic->value;
  atomic->value += 1;
  pthread_mutex_unlock (&atomic_mutex);

  return res;
#endif
}


_dbus_create_directory (const DBusString *filename,
                        DBusError        *error)
{
  const char *filename_c;

  _DBUS_ASSERT_ERROR_IS_CLEAR (error);

  filename_c = _dbus_string_get_const_data (filename);

  if (mkdir (filename_c, 0700) < 0)
    {
      dbus_set_error (error, DBUS_ERROR_FAILED,
                      "Failed to create directory %s: %s\n",
                      filename_c, _dbus_strerror (errno));
      return FALSE;
    }
  else
    return TRUE;
}


_dbus_atomic_dec (DBusAtomic *atomic)
{
#if DBUS_USE_SYNC
  return __sync_sub_and_fetch(&atomic->value, 1)+1;
#else
  dbus_int32_t res;

  pthread_mutex_lock (&atomic_mutex);
  res = atomic->value;
  atomic->value -= 1;
  pthread_mutex_unlock (&atomic_mutex);

  return res;
#endif
}


static void ldb_trace_request(struct ldb_context *ldb, struct ldb_request *req)
{
	TALLOC_CTX *tmp_ctx = talloc_new(req);
	unsigned int i;
	struct ldb_ldif ldif;

	switch (req->operation) {
	case LDB_SEARCH:
		ldb_debug_add(ldb, "ldb_trace_request: SEARCH\n");
		ldb_debug_add(ldb, " dn: %s\n",
			      ldb_dn_is_null(req->op.search.base)?"<rootDSE>":
			      ldb_dn_get_linearized(req->op.search.base));
		ldb_debug_add(ldb, " scope: %s\n",
			  req->op.search.scope==LDB_SCOPE_BASE?"base":
			  req->op.search.scope==LDB_SCOPE_ONELEVEL?"one":
			  req->op.search.scope==LDB_SCOPE_SUBTREE?"sub":"UNKNOWN");
		ldb_debug_add(ldb, " expr: %s\n",
			  ldb_filter_from_tree(tmp_ctx, req->op.search.tree));
		if (req->op.search.attrs == NULL) {
			ldb_debug_add(ldb, " attr: <ALL>\n");
		} else {
			for (i=0; req->op.search.attrs[i]; i++) {
				ldb_debug_add(ldb, " attr: %s\n", req->op.search.attrs[i]);
			}
		}
		break;
	case LDB_DELETE:
		ldb_debug_add(ldb, "ldb_trace_request: DELETE\n");
		ldb_debug_add(ldb, " dn: %s\n",
			      ldb_dn_get_linearized(req->op.del.dn));
		break;
	case LDB_RENAME:
		ldb_debug_add(ldb, "ldb_trace_request: RENAME\n");
		ldb_debug_add(ldb, " olddn: %s\n",
			      ldb_dn_get_linearized(req->op.rename.olddn));
		ldb_debug_add(ldb, " newdn: %s\n",
			      ldb_dn_get_linearized(req->op.rename.newdn));
		break;
	case LDB_EXTENDED:
		ldb_debug_add(ldb, "ldb_trace_request: EXTENDED\n");
		ldb_debug_add(ldb, " oid: %s\n", req->op.extended.oid);
		ldb_debug_add(ldb, " data: %s\n", req->op.extended.data?"yes":"no");
		break;
	case LDB_ADD:
		ldif.changetype = LDB_CHANGETYPE_ADD;
		ldif.msg = discard_const_p(struct ldb_message, req->op.add.message);

		ldb_debug_add(ldb, "ldb_trace_request: ADD\n");

		/*
		 * The choice to call
		 * ldb_ldif_write_redacted_trace_string() is CRITICAL
		 * for security.  It ensures that we do not output
		 * passwords into debug logs
		 */

		ldb_debug_add(req->handle->ldb, "%s\n",
			      ldb_ldif_write_redacted_trace_string(req->handle->ldb, tmp_ctx, &ldif));
		break;
	case LDB_MODIFY:
		ldif.changetype = LDB_CHANGETYPE_MODIFY;
		ldif.msg = discard_const_p(struct ldb_message, req->op.mod.message);

		ldb_debug_add(ldb, "ldb_trace_request: MODIFY\n");

		/*
		 * The choice to call
		 * ldb_ldif_write_redacted_trace_string() is CRITICAL
		 * for security.  It ensures that we do not output
		 * passwords into debug logs
		 */

		ldb_debug_add(req->handle->ldb, "%s\n",
			      ldb_ldif_write_redacted_trace_string(req->handle->ldb, tmp_ctx, &ldif));
		break;
	case LDB_REQ_REGISTER_CONTROL:
		ldb_debug_add(ldb, "ldb_trace_request: REGISTER_CONTROL\n");
		ldb_debug_add(req->handle->ldb, "%s\n",
			      req->op.reg_control.oid);
		break;
	case LDB_REQ_REGISTER_PARTITION:
		ldb_debug_add(ldb, "ldb_trace_request: REGISTER_PARTITION\n");
		ldb_debug_add(req->handle->ldb, "%s\n",
			      ldb_dn_get_linearized(req->op.reg_partition.dn));
		break;
	default:
		ldb_debug_add(ldb, "ldb_trace_request: UNKNOWN(%u)\n",
			      req->operation);
		break;
	}

	if (req->controls == NULL) {
		ldb_debug_add(ldb, " control: <NONE>\n");
	} else {
		for (i=0; req->controls && req->controls[i]; i++) {
			if (req->controls[i]->oid) {
				ldb_debug_add(ldb, " control: %s  crit:%u  data:%s\n",
					      req->controls[i]->oid,
					      req->controls[i]->critical,
					      req->controls[i]->data?"yes":"no");
			}
		}
	}

	ldb_debug_end(ldb, LDB_DEBUG_TRACE);

	talloc_free(tmp_ctx);
}


int ldb_build_add_req(struct ldb_request **ret_req,
			struct ldb_context *ldb,
			TALLOC_CTX *mem_ctx,
			const struct ldb_message *message,
			struct ldb_control **controls,
			void *context,
			ldb_request_callback_t callback,
			struct ldb_request *parent)
{
	struct ldb_request *req;

	*ret_req = NULL;

	req = ldb_build_req_common(mem_ctx, ldb, controls,
				   context, callback, parent);
	if (req == NULL) {
		ldb_set_errstring(ldb, "Out of Memory");
		return LDB_ERR_OPERATIONS_ERROR;
	}

	req->operation = LDB_ADD;
	req->op.add.message = message;
	*ret_req = req;
	return LDB_SUCCESS;
}


int ldb_search(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
		struct ldb_result **result, struct ldb_dn *base,
		enum ldb_scope scope, const char * const *attrs,
		const char *exp_fmt, ...)
{
	struct ldb_request *req;
	struct ldb_result *res;
	char *expression;
	va_list ap;
	int ret;

	expression = NULL;
	*result = NULL;
	req = NULL;

	res = talloc_zero(mem_ctx, struct ldb_result);
	if (!res) {
		return LDB_ERR_OPERATIONS_ERROR;
	}

	if (exp_fmt) {
		va_start(ap, exp_fmt);
		expression = talloc_vasprintf(mem_ctx, exp_fmt, ap);
		va_end(ap);

		if (!expression) {
			talloc_free(res);
			return LDB_ERR_OPERATIONS_ERROR;
		}
	}

	ret = ldb_build_search_req(&req, ldb, mem_ctx,
					base?base:ldb_get_default_basedn(ldb),
	       				scope,
					expression,
					attrs,
					NULL,
					res,
					ldb_search_default_callback,
					NULL);
	ldb_req_set_location(req, "ldb_search");

	if (ret != LDB_SUCCESS) goto done;

	ret = ldb_request(ldb, req);

	if (ret == LDB_SUCCESS) {
		ret = ldb_wait(req->handle, LDB_WAIT_ALL);
	}

done:
	if (ret != LDB_SUCCESS) {
		talloc_free(res);
		res = NULL;
	}

	talloc_free(expression);
	talloc_free(req);

	*result = res;
	return ret;
}


int ldb_rename(struct ldb_context *ldb,
		struct ldb_dn *olddn, struct ldb_dn *newdn)
{
	struct ldb_request *req;
	int ret;

	ret = ldb_build_rename_req(&req, ldb, ldb,
					olddn,
					newdn,
					NULL,
					NULL,
					ldb_op_default_callback,
					NULL);
	ldb_req_set_location(req, "ldb_rename");

	if (ret != LDB_SUCCESS) return ret;

	/* do request and autostart a transaction */
	ret = ldb_autotransaction_request(ldb, req);

	talloc_free(req);
	return ret;
}


int ldb_transaction_commit(struct ldb_context *ldb)
{
	struct ldb_module *next_module;
	int status;

	status = ldb_transaction_prepare_commit(ldb);
	if (status != LDB_SUCCESS) {
		return status;
	}

	ldb->transaction_active--;

	ldb_debug(ldb, LDB_DEBUG_TRACE,
		  "commit ldb transaction (nesting: %d)",
		  ldb->transaction_active);

	/* commit only when all nested transactions are complete */
	if (ldb->transaction_active > 0) {
		return LDB_SUCCESS;
	}

	if (ldb->transaction_active < 0) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "commit called but no ldb transactions are active!");
		ldb->transaction_active = 0;
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ldb_reset_err_string(ldb);

	FIRST_OP(ldb, end_transaction);
	status = next_module->ops->end_transaction(next_module);
	if (status != LDB_SUCCESS) {
		if (ldb->err_string == NULL) {
			/* no error string was setup by the backend */
			ldb_asprintf_errstring(ldb,
				"ldb transaction commit: %s (%d)",
				ldb_strerror(status),
				status);
		}
		if ((next_module && next_module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
			ldb_debug(next_module->ldb, LDB_DEBUG_TRACE, "commit ldb transaction error: %s",
				  ldb_errstring(next_module->ldb));
		}
	}
	return status;
}


int ldb_error_at(struct ldb_context *ldb, int ecode,
		 const char *reason, const char *file, int line)
{
	if (reason == NULL) {
		reason = ldb_strerror(ecode);
	}
	ldb_asprintf_errstring(ldb, "%s at %s:%d", reason, file, line);
	return ecode;
}


static int ldb_lock_backend_callback(struct ldb_request *req,
				     struct ldb_reply *ares)
{
	struct ldb_db_lock_context *lock_context;
	int ret;

	if (req->context == NULL) {
		/*
		 * The usual way to get here is to ignore the return codes
		 * and continuing processing after an error.
		 */
		abort();
	}
	lock_context = talloc_get_type(req->context,
				       struct ldb_db_lock_context);

	if (!ares) {
		return ldb_module_done(lock_context->req, NULL, NULL,
					LDB_ERR_OPERATIONS_ERROR);
	}
	if (ares->error != LDB_SUCCESS || ares->type == LDB_REPLY_DONE) {
		ret = ldb_module_done(lock_context->req, ares->controls,
				      ares->response, ares->error);
		/*
		 * If this is a LDB_REPLY_DONE or an error, unlock the
		 * DB by calling the destructor on this context
		 */
		TALLOC_FREE(req->context);
		return ret;
	}

	/* Otherwise pass on the callback */
	switch (ares->type) {
	case LDB_REPLY_ENTRY:
		return ldb_module_send_entry(lock_context->req, ares->message,
					     ares->controls);

	case LDB_REPLY_REFERRAL:
		return ldb_module_send_referral(lock_context->req,
						ares->referral);
	default:
		/* Can't happen */
		return LDB_ERR_OPERATIONS_ERROR;
	}
}


struct ldb_dn *ldb_get_root_basedn(struct ldb_context *ldb)
{
	void *opaque = ldb_get_opaque(ldb, "rootDomainNamingContext");
	return talloc_get_type(opaque, struct ldb_dn);
}


int ldb_transaction_cancel(struct ldb_context *ldb)
{
	struct ldb_module *next_module;
	int status;

	ldb->transaction_active--;

	ldb_debug(ldb, LDB_DEBUG_TRACE,
		  "cancel ldb transaction (nesting: %d)",
		  ldb->transaction_active);

	/* really cancel only if all nested transactions are complete */
	if (ldb->transaction_active > 0) {
		return LDB_SUCCESS;
	}

	if (ldb->transaction_active < 0) {
		ldb_debug(ldb, LDB_DEBUG_FATAL,
			  "cancel called but no ldb transactions are active!");
		ldb->transaction_active = 0;
		return LDB_ERR_OPERATIONS_ERROR;
	}

	FIRST_OP(ldb, del_transaction);

	status = next_module->ops->del_transaction(next_module);
	if (status != LDB_SUCCESS) {
		if (ldb->err_string == NULL) {
			/* no error string was setup by the backend */
			ldb_asprintf_errstring(ldb,
				"ldb transaction cancel: %s (%d)",
				ldb_strerror(status),
				status);
		}
		if ((next_module && next_module->ldb->flags & LDB_FLG_ENABLE_TRACING)) {
			ldb_debug(next_module->ldb, LDB_DEBUG_TRACE, "cancel ldb transaction error: %s",
				  ldb_errstring(next_module->ldb));
		}
	}
	return status;
}


static struct ldb_handle *ldb_handle_new_child(TALLOC_CTX *mem_ctx,
					       struct ldb_request *parent_req)
{
	struct ldb_handle *h;

	h = talloc_zero(mem_ctx, struct ldb_handle);
	if (h == NULL) {
		ldb_set_errstring(parent_req->handle->ldb,
				  "Out of Memory");
		return NULL;
	}

	h->status = LDB_SUCCESS;
	h->state = LDB_ASYNC_INIT;
	h->ldb = parent_req->handle->ldb;
	h->parent = parent_req;
	h->nesting = parent_req->handle->nesting + 1;
	h->flags = parent_req->handle->flags;
	h->custom_flags = parent_req->handle->custom_flags;
	h->event_context = parent_req->handle->event_context;

	return h;
}


_PUBLIC_ enum ndr_err_code ndr_pull_dns_txt_record(struct ndr_pull *ndr, int ndr_flags, struct dns_txt_record *r)
{
	NDR_PULL_CHECK_FLAGS(ndr, ndr_flags);
	if (ndr_flags & NDR_SCALARS) {
		enum ndr_err_code ndr_err;
		uint32_t data_size = ndr->data_size;
		uint32_t record_size = 0;
		ndr_err = ndr_token_retrieve(&ndr->array_size_list, r,
					     &record_size);
		if (NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
			NDR_PULL_NEED_BYTES(ndr, record_size);
			ndr->data_size = ndr->offset + record_size;
		}
		NDR_CHECK(ndr_pull_align(ndr, 1));
		NDR_CHECK(ndr_pull_dnsp_string_list(ndr, NDR_SCALARS, &r->txt));
		NDR_CHECK(ndr_pull_trailer_align(ndr, 1));
		ndr->data_size = data_size;
	}
	if (ndr_flags & NDR_BUFFERS) {
	}
	return NDR_ERR_SUCCESS;
}


_PUBLIC_ enum ndr_err_code ndr_push_dns_string(struct ndr_push *ndr,
					       int ndr_flags,
					       const char *s)
{
	return ndr_push_dns_string_list(ndr,
					&ndr->dns_string_list,
					ndr_flags,
					s,
					false);
}


static int vlv_request_init(struct ldb_module *module)
{
	struct ldb_context *ldb;
	struct private_data *data;
	int ret;

	ldb = ldb_module_get_ctx(module);

	data = talloc(module, struct private_data);
	if (data == NULL) {
		return LDB_ERR_OTHER;
	}

	data->next_free_id = 1;
	data->n_stores = VLV_N_SEARCHES;
	data->store = talloc_zero_array(data, struct results_store *, data->n_stores);

	ldb_module_set_private(module, data);

	ret = ldb_mod_register_control(module, LDB_CONTROL_VLV_REQ_OID);
	if (ret != LDB_SUCCESS) {
		ldb_debug(ldb, LDB_DEBUG_WARNING,
			  "vlv:"
			  "Unable to register control with rootdse!");
	}

	return ldb_next_init(module);
}


static int paged_search_callback(struct ldb_request *req,
				 struct ldb_reply *ares)
{
	struct paged_context *ac;
	struct results_store *store;
	int ret;
	const struct ldb_val *guid_blob;
	struct GUID guid;
	NTSTATUS status;

	ac = talloc_get_type(req->context, struct paged_context);
	store = ac->store;

	if (!ares) {
		return ldb_module_done(ac->req, NULL, NULL,
					LDB_ERR_OPERATIONS_ERROR);
	}
	if (ares->error != LDB_SUCCESS) {
		return ldb_module_done(ac->req, ares->controls,
					ares->response, ares->error);
	}

	switch (ares->type) {
	case LDB_REPLY_ENTRY:
		if (store->results == NULL) {
			store->num_entries = 0;
			store->result_array_size = 16;
			store->results = talloc_array(store, struct GUID,
						     store->result_array_size);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		} else if (store->num_entries == store->result_array_size) {
			if (store->result_array_size > INT_MAX/2) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
			store->result_array_size *= 2;
			store->results = talloc_realloc(store, store->results,
							struct GUID,
						store->result_array_size);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		}

		guid_blob = ldb_dn_get_extended_component(ares->message->dn,
							  "GUID");
		if (guid_blob == NULL) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}
		status = GUID_from_ndr_blob(guid_blob, &guid);
		if (!NT_STATUS_IS_OK(status)) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}

		/* Redundant paranoid check */
		if (store->num_entries > store->result_array_size) {
			return ldb_module_done(ac->req, NULL, NULL,
					       LDB_ERR_OPERATIONS_ERROR);
		}

		store->results[store->num_entries] = guid;
		store->num_entries++;
		break;

	case LDB_REPLY_REFERRAL:
		ret = save_referral(store, ares->referral);
		if (ret != LDB_SUCCESS) {
			return ldb_module_done(ac->req, NULL, NULL, ret);
		}
		break;

	case LDB_REPLY_DONE:
		if (store->num_entries != 0) {
			store->results = talloc_realloc(store, store->results,
							struct GUID,
							store->num_entries);
			if (store->results == NULL) {
				return ldb_module_done(ac->req, NULL, NULL,
						     LDB_ERR_OPERATIONS_ERROR);
			}
		}
		store->result_array_size = store->num_entries;

		ac->store->controls = talloc_move(ac->store, &ares->controls);
		ret = paged_results(ac, ares);
		if (ret != LDB_SUCCESS) {
			/* paged_results will have called ldb_module_done
			 * if an error occurred
			 */
			return ret;
		}
		return ldb_module_done(ac->req, ac->controls,
					ares->response, ret);
	}

	return LDB_SUCCESS;
}


static int paged_search(struct ldb_module *module, struct ldb_request *req)
{
	struct ldb_context *ldb;
	struct ldb_control *control;
	struct ldb_control *vlv_control;
	struct private_data *private_data;
	struct ldb_paged_control *paged_ctrl;
	struct ldb_request *search_req;
	struct paged_context *ac;
	int ret;

	ldb = ldb_module_get_ctx(module);

	/* check if there's a paged request control */
	control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
	if (control == NULL) {
		/* not found go on */
		return ldb_next_request(module, req);
	}

	paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
	if (!paged_ctrl) {
		return LDB_ERR_PROTOCOL_ERROR;
	}

	private_data = talloc_get_type(ldb_module_get_private(module),
					struct private_data);

	vlv_control = ldb_request_get_control(req, LDB_CONTROL_VLV_REQ_OID);
	if (vlv_control != NULL) {
		/*
		 * VLV and paged_results are not allowed at the same
		 * time
		 */
		return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
	}

	ac = talloc_zero(req, struct paged_context);
	if (ac == NULL) {
		ldb_set_errstring(ldb, "Out of Memory");
		return LDB_ERR_OPERATIONS_ERROR;
	}

	ac->module = module;
	ac->req = req;
	ac->size = paged_ctrl->size;
	if (ac->size < 0) {
		/*
		 * Apparently some clients send more than 2^31. This
		 * violates the ldap standard, but we need to cope.
		 * In the future, if maximum result sizes are implemented in
		 * Samba, we should also clamp the page size to the maximum
		 * result size.
		 */
		ac->size = 0x7FFFFFFF;
	}

	/* check if it is a continuation search the store */
	if (paged_ctrl->cookie_len == 0) {
		struct ldb_control *ext_ctrl;
		struct ldb_control **controls;
		static const char * const attrs[1] = { NULL };

		if (paged_ctrl->size == 0) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ac->store = new_store(private_data);
		if (ac->store == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		controls = req->controls;
		ext_ctrl = ldb_request_get_control(req,
					LDB_CONTROL_EXTENDED_DN_OID);
		if (ext_ctrl == NULL) {
			/*
			 * Add extended_dn control to the request if there
			 * isn't already one.  We'll get the GUID out of it in
			 * the callback.  This is a workaround for the case
			 * where ntsecuritydescriptor forbids fetching GUIDs
			 * for the current user.
			 */
			struct ldb_request *req_extended_dn;
			struct ldb_extended_dn_control *ext_ctrl_data;
			req_extended_dn = talloc_zero(req, struct ldb_request);
			req_extended_dn->controls = req->controls;
			ext_ctrl_data = talloc_zero(req,
					struct ldb_extended_dn_control);
			ext_ctrl_data->type = 1;

			ret = ldb_request_add_control(req_extended_dn,
					      LDB_CONTROL_EXTENDED_DN_OID,
						      true,
						      ext_ctrl_data);
			if (ret != LDB_SUCCESS) {
				return ret;
			}
			controls = req_extended_dn->controls;
		}

		ret = ldb_build_search_req_ex(&search_req, ldb, ac,
						req->op.search.base,
						req->op.search.scope,
						req->op.search.tree,
						attrs,
						controls,
						ac,
						paged_search_callback,
						req);
		if (ret != LDB_SUCCESS) {
			return ret;
		}

		ac->store->expr = talloc_steal(ac->store, req->op.search.tree);
		ac->store->expr_str = ldb_filter_from_tree(ac->store,
							  req->op.search.tree);
		ac->store->attrs = paged_copy_attrs(ac->store,
						    req->op.search.attrs);

		/* save it locally and remove it from the list */
		/* we do not need to replace them later as we
		 * are keeping the original req intact */
		if (!ldb_save_controls(control, search_req, NULL)) {
			return LDB_ERR_OPERATIONS_ERROR;
		}
		ac->store->down_controls =
		    paged_results_copy_down_controls(ac->store, req->controls);
		if (ac->store->down_controls == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		return ldb_next_request(module, search_req);

	} else {
		struct results_store *current = NULL;
		char *expr_str;
		bool bool_ret;

		/* TODO: age out old outstanding requests */
		for (current = private_data->store; current != NULL;
		     current = current->next) {
			if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
				current->timestamp = time(NULL);
				break;
			}
		}
		if (current == NULL) {
			return LDB_ERR_UNWILLING_TO_PERFORM;
		}

		/* Get the expression string and make sure it didn't change */
		expr_str = ldb_filter_from_tree(ac, req->op.search.tree);
		if (expr_str == NULL) {
			return LDB_ERR_OPERATIONS_ERROR;
		}

		ret = strcmp(current->expr_str, expr_str);
		if (ret != 0) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		bool_ret = paged_controls_same(req, current->down_controls);
		if (bool_ret == false) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		bool_ret = paged_attrs_same(req->op.search.attrs,
					    current->attrs);
		if (bool_ret == false) {
			return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
		}

		DLIST_PROMOTE(private_data->store, current);

		ac->store = current;

		/* check if it is an abandon */
		if (ac->size == 0) {
			return ldb_module_done(req, NULL, NULL,
								LDB_SUCCESS);
		}

		ret = paged_results(ac, NULL);
		if (ret != LDB_SUCCESS) {
			return ldb_module_done(req, NULL, NULL, ret);
		}
		return ldb_module_done(req, ac->controls, NULL, LDB_SUCCESS);
	}
}


static int vlv_calc_real_offset(int offset, int denominator, int n_entries)
{
	double fraction;

	/* An offset of 0 (or less) is an error, unless the denominator is
	   also zero. */
	if (offset <= 0 && denominator != 0) {
		return -1;
	}

	/* a denominator of zero means the server should use the estimated
	   number of entries. */
	if (denominator == 0) {
		if (offset == 0) {
                        /* 0/0 means the last one */
			return n_entries - 1;
		}
		denominator = n_entries;
	}

	if (denominator == 1) {
		/* The 1/1 case means the LAST index.
		   Strangely, for n > 1, n/1 means the FIRST index.
		*/
		if (offset == 1) {
			return n_entries - 1;
		}
		return 0;
	}

	if (offset >= denominator) {
		/* we want the last one */
		return n_entries - 1;
	}
	/* if the denominator is exactly the number of entries, the offset is
	   already correct. */

	if (denominator == n_entries) {
		return offset - 1;
	}

	/* The following formula was discovered by probing Windows. */
	fraction = (offset - 1.0) / (denominator - 1.0);
	return (int)(fraction * (n_entries - 1.0) + 0.5);
}


static char *x509_get_part (X509_NAME *name, int nid)
{
  static char ret[SHORT_STRING];

  if (!name ||
      X509_NAME_get_text_by_NID (name, nid, ret, sizeof (ret)) < 0)
    strfcpy (ret, _("Unknown"), sizeof (ret));

  return ret;
}


int raw_socket_open (CONNECTION* conn)
{
  int rc;
  int fd;

  char *host_idna = NULL;

#ifdef HAVE_GETADDRINFO
/* --- IPv4/6 --- */

  /* "65536\0" */
  char port[6];
  struct addrinfo hints;
  struct addrinfo* res;
  struct addrinfo* cur;

  /* we accept v4 or v6 STREAM sockets */
  memset (&hints, 0, sizeof (hints));

  if (option (OPTUSEIPV6))
    hints.ai_family = AF_UNSPEC;
  else
    hints.ai_family = AF_INET;

  hints.ai_socktype = SOCK_STREAM;

  snprintf (port, sizeof (port), "%d", conn->account.port);

# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
  if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
  {
    mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
    return -1;
  }
# else
  host_idna = conn->account.host;
# endif

  if (!option(OPTNOCURSES))
    mutt_message (_("Looking up %s..."), conn->account.host);

  rc = getaddrinfo (host_idna, port, &hints, &res);

# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
  FREE (&host_idna);
# endif

  if (rc)
  {
    mutt_error (_("Could not find the host \"%s\""), conn->account.host);
    mutt_sleep (2);
    return -1;
  }

  if (!option(OPTNOCURSES))
    mutt_message (_("Connecting to %s..."), conn->account.host);

  rc = -1;
  for (cur = res; cur != NULL; cur = cur->ai_next)
  {
    fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
    if (fd >= 0)
    {
      if ((rc = socket_connect (fd, cur->ai_addr)) == 0)
      {
	fcntl (fd, F_SETFD, FD_CLOEXEC);
	conn->fd = fd;
	break;
      }
      else
	close (fd);
    }
  }

  freeaddrinfo (res);

#else
  /* --- IPv4 only --- */

  struct sockaddr_in sin;
  struct hostent* he;
  int i;

  memset (&sin, 0, sizeof (sin));
  sin.sin_port = htons (conn->account.port);
  sin.sin_family = AF_INET;

# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
  if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
  {
    mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
    return -1;
  }
# else
  host_idna = conn->account.host;
# endif

  if (!option(OPTNOCURSES))
    mutt_message (_("Looking up %s..."), conn->account.host);

  he = gethostbyname (host_idna);

# if defined(HAVE_LIBIDN) || defined(HAVE_LIBIDN2)
  FREE (&host_idna);
# endif

  if (! he)
  {
    mutt_error (_("Could not find the host \"%s\""), conn->account.host);

    return -1;
  }

  if (!option(OPTNOCURSES))
    mutt_message (_("Connecting to %s..."), conn->account.host);

  rc = -1;
  for (i = 0; he->h_addr_list[i] != NULL; i++)
  {
    memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
    fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);

    if (fd >= 0)
    {
      if ((rc = socket_connect (fd, (struct sockaddr*) &sin)) == 0)
      {
        fcntl (fd, F_SETFD, FD_CLOEXEC);
	conn->fd = fd;
	break;
      }
      else
	close (fd);
    }
  }

#endif
  if (rc)
  {
    mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
                (rc > 0) ? strerror (rc) : _("unknown error"));
    mutt_sleep (2);
    return -1;
  }

  return 0;
}


static void ssl_get_client_cert(sslsockdata *ssldata, CONNECTION *conn)
{
  if (SslClientCert)
  {
    dprint (2, (debugfile, "Using client certificate %s\n", SslClientCert));
    SSL_CTX_set_default_passwd_cb_userdata(ssldata->ctx, &conn->account);
    SSL_CTX_set_default_passwd_cb(ssldata->ctx, ssl_passwd_cb);
    SSL_CTX_use_certificate_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);
    SSL_CTX_use_PrivateKey_file(ssldata->ctx, SslClientCert, SSL_FILETYPE_PEM);

    /* if we are using a client cert, SASL may expect an external auth name */
    mutt_account_getuser (&conn->account);
  }
}


int mutt_socket_readchar (CONNECTION *conn, char *c)
{
  if (conn->bufpos >= conn->available)
  {
    if (conn->fd >= 0)
      conn->available = conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
    else
    {
      dprint (1, (debugfile, "mutt_socket_readchar: attempt to read from closed connection.\n"));
      return -1;
    }
    conn->bufpos = 0;
    if (conn->available == 0)
    {
      mutt_error (_("Connection to %s closed"), conn->account.host);
      mutt_sleep (2);
    }
    if (conn->available <= 0)
    {
      mutt_socket_close (conn);
      return -1;
    }
  }
  *c = conn->inbuf[conn->bufpos];
  conn->bufpos++;
  return 1;
}


static int compare_certificates (X509 *cert, X509 *peercert,
                                 unsigned char *peermd, unsigned int peermdlen)
{
  unsigned char md[EVP_MAX_MD_SIZE];
  unsigned int mdlen;

  /* Avoid CPU-intensive digest calculation if the certificates are
   * not even remotely equal.
   */
  if (X509_subject_name_cmp (cert, peercert) != 0 ||
      X509_issuer_name_cmp (cert, peercert) != 0)
    return -1;

  if (!X509_digest (cert, EVP_sha256(), md, &mdlen) || peermdlen != mdlen)
    return -1;

  if (memcmp(peermd, md, mdlen) != 0)
    return -1;

  return 0;
}


void mutt_socket_clear_buffered_input (CONNECTION *conn)
{
  conn->bufpos = conn->available = 0;
}


static int ssl_set_verify_partial (SSL_CTX *ctx)
{
  int rv = 0;
#ifdef HAVE_SSL_PARTIAL_CHAIN
  X509_VERIFY_PARAM *param;

  if (option (OPTSSLVERIFYPARTIAL))
  {
    param = X509_VERIFY_PARAM_new();
    if (param)
    {
      X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_PARTIAL_CHAIN);
      if (0 == SSL_CTX_set1_param(ctx, param))
      {
        dprint (2, (debugfile, "ssl_set_verify_partial: SSL_CTX_set1_param() failed."));
        rv = -1;
      }
      X509_VERIFY_PARAM_free(param);
    }
    else
    {
      dprint (2, (debugfile, "ssl_set_verify_partial: X509_VERIFY_PARAM_new() failed."));
      rv = -1;
    }
  }
#endif
  return rv;
}


int raw_socket_write (CONNECTION* conn, const char* buf, size_t count)
{
  int rc;
  size_t sent = 0;

  do
  {
    do
    {
      rc = write (conn->fd, buf + sent, count - sent);
    } while (rc < 0 && errno == EINTR);

    if (rc < 0)
    {
      mutt_error (_("Error talking to %s (%s)"), conn->account.host,
                  strerror (errno));
      mutt_sleep (2);
      return -1;
    }

    sent += rc;
  } while (sent < count);

  return sent;
}


DLLEXPORT int tjPlaneHeight(int componentID, int height, int subsamp)
{
	int ph, nc, retval=0;

	if(height<1 || subsamp<0 || subsamp>=TJ_NUMSAMP)
		_throw("tjPlaneHeight(): Invalid argument");
	nc=(subsamp==TJSAMP_GRAY? 1:3);
	if(componentID<0 || componentID>=nc)
		_throw("tjPlaneHeight(): Invalid argument");

	ph=PAD(height, tjMCUHeight[subsamp]/8);
	if(componentID==0)
		retval=ph;
	else
		retval=ph*8/tjMCUHeight[subsamp];

	bailout:
	return retval;
}


DLLEXPORT tjhandle DLLCALL tjInitTransform(void)
{
	tjinstance *this=NULL;  tjhandle handle=NULL;
	if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL)
	{
		snprintf(errStr, JMSG_LENGTH_MAX,
			"tjInitTransform(): Memory allocation failure");
		return NULL;
	}
	MEMZERO(this, sizeof(tjinstance));
	handle=_tjInitCompress(this);
	if(!handle) return NULL;
	handle=_tjInitDecompress(this);
	return handle;
}


int FoFiTrueType::getNumCmaps() {
  return nCmaps;
}


Guint FoFiTrueType::mapToVertGID(Guint orgGID)
{
  Guint mapped;

  if (gsubFeatureTable == 0) return orgGID;
  if ((mapped = doMapToVertGID(orgGID)) != 0) {
    return mapped;
  }
  return orgGID;
}


  bool operator()(const TrueTypeLoca &loca1, const TrueTypeLoca &loca2) {
    return loca1.idx < loca2.idx;
  }


void FoFiTrueType::getFontMatrix(double *mat) {
  char *start;
  int length;
  FoFiType1C *ff;

  if (!getCFFBlock(&start, &length)) {
    return;
  }
  if (!(ff = FoFiType1C::make(start, length))) {
    return;
  }
  ff->getFontMatrix(mat);
  delete ff;
}


debug(pam_handle_t *pamh, const struct if_dqblk *p,
      const char *device, const char *dbgprefix) {
  pam_syslog(pamh, LOG_DEBUG, "%s device=%s bsoftlimit=%llu bhardlimit=%llu "
                              "isoftlimit=%llu ihardlimit=%llu btime=%llu itime=%llu",
	     dbgprefix, device,
	     (unsigned long long) p->dqb_bsoftlimit,
	     (unsigned long long) p->dqb_bhardlimit,
	     (unsigned long long) p->dqb_isoftlimit,
	     (unsigned long long) p->dqb_ihardlimit,
	     (unsigned long long) p->dqb_btime,
	     (unsigned long long) p->dqb_itime);
}


void aarp_proxy_remove(struct net_device *dev, struct atalk_addr *sa)
{
	int hash = sa->s_node % (AARP_HASH_SIZE - 1);
	struct aarp_entry *a;

	write_lock_bh(&aarp_lock);

	a = __aarp_find_entry(proxies[hash], dev, sa);
	if (a)
		a->expires_at = jiffies - 1;

	write_unlock_bh(&aarp_lock);
}


static void __aarp_expire_timer(struct aarp_entry **n)
{
	struct aarp_entry *t;

	while (*n)
		/* Expired ? */
		if (time_after(jiffies, (*n)->expires_at)) {
			t = *n;
			*n = (*n)->next;
			__aarp_expire(t);
		} else
			n = &((*n)->next);
}


	__acquires(aarp_lock)
{
	struct aarp_iter_state *iter = seq->private;

	read_lock_bh(&aarp_lock);
	iter->table     = resolved;
	iter->bucket    = 0;

	return *pos ? iter_next(iter, pos) : SEQ_START_TOKEN;
}


exif_entry_realloc (ExifEntry *e, void *d_orig, unsigned int i)
{
	void *d;
	ExifLog *l = NULL;

	if (!e || !e->priv) return NULL;

	if (!i) { exif_mem_free (e->priv->mem, d_orig); return NULL; }

	d = exif_mem_realloc (e->priv->mem, d_orig, i);
	if (d) return d;

	if (e->parent && e->parent->parent)
		l = exif_data_get_log (e->parent->parent);
	EXIF_LOG_NO_MEMORY (l, "ExifEntry", i);
	return NULL;
}


exif_entry_unref (ExifEntry *e)
{
	if (!e) return;

	e->priv->ref_count--;
	if (!e->priv->ref_count)
		exif_entry_free (e);
}


static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb)
{
	int	retval;
	u16	status;

	/* should look and act halted */
	retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status);
	if (retval < 0) {
		ERROR(tdev, "ep %02x couldn't get halt status, %d\n",
				ep, retval);
		return retval;
	}
	if (status != 1) {
		ERROR(tdev, "ep %02x bogus status: %04x != 1\n", ep, status);
		return -EINVAL;
	}
	retval = simple_io(tdev, urb, 1, 0, -EPIPE, __func__);
	if (retval != -EPIPE)
		return -EINVAL;
	retval = simple_io(tdev, urb, 1, 0, -EPIPE, "verify_still_halted");
	if (retval != -EPIPE)
		return -EINVAL;
	return 0;
}


static void complicated_callback(struct urb *urb)
{
	struct transfer_context	*ctx = urb->context;
	unsigned long flags;

	spin_lock_irqsave(&ctx->lock, flags);
	ctx->count--;

	ctx->packet_count += urb->number_of_packets;
	if (urb->error_count > 0)
		ctx->errors += urb->error_count;
	else if (urb->status != 0)
		ctx->errors += (ctx->is_iso ? urb->number_of_packets : 1);
	else if (urb->actual_length != urb->transfer_buffer_length)
		ctx->errors++;
	else if (check_guard_bytes(ctx->dev, urb) != 0)
		ctx->errors++;

	if (urb->status == 0 && ctx->count > (ctx->pending - 1)
			&& !ctx->submit_error) {
		int status = usb_submit_urb(urb, GFP_ATOMIC);
		switch (status) {
		case 0:
			goto done;
		default:
			dev_err(&ctx->dev->intf->dev,
					"resubmit err %d\n",
					status);
			/* FALLTHROUGH */
		case -ENODEV:			/* disconnected */
		case -ESHUTDOWN:		/* endpoint disabled */
			ctx->submit_error = 1;
			break;
		}
	}

	ctx->pending--;
	if (ctx->pending == 0) {
		if (ctx->errors)
			dev_err(&ctx->dev->intf->dev,
				"during the test, %lu errors out of %lu\n",
				ctx->errors, ctx->packet_count);
		complete(&ctx->done);
	}
done:
	spin_unlock_irqrestore(&ctx->lock, flags);
}


static int __init efivar_ssdt_setup(char *str)
{
	int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);

	if (ret)
		return ret;

	if (strlen(str) < sizeof(efivar_ssdt))
		memcpy(efivar_ssdt, str, strlen(str));
	else
		pr_warn("efivar_ssdt: name too long: %s\n", str);
	return 0;
}


int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
{
	struct linux_efi_memreserve *rsv;
	unsigned long prsv;
	int rc, index;

	if (efi_memreserve_root == (void *)ULONG_MAX)
		return -ENODEV;

	if (!efi_memreserve_root) {
		rc = efi_memreserve_map_root();
		if (rc)
			return rc;
	}

	/* first try to find a slot in an existing linked list entry */
	for (prsv = efi_memreserve_root->next; prsv; prsv = rsv->next) {
		rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB);
		index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
		if (index < rsv->size) {
			rsv->entry[index].base = addr;
			rsv->entry[index].size = size;

			memunmap(rsv);
			return 0;
		}
		memunmap(rsv);
	}

	/* no slot found - allocate a new linked list entry */
	rsv = (struct linux_efi_memreserve *)__get_free_page(GFP_ATOMIC);
	if (!rsv)
		return -ENOMEM;

	/*
	 * The memremap() call above assumes that a linux_efi_memreserve entry
	 * never crosses a page boundary, so let's ensure that this remains true
	 * even when kexec'ing a 4k pages kernel from a >4k pages kernel, by
	 * using SZ_4K explicitly in the size calculation below.
	 */
	rsv->size = EFI_MEMRESERVE_COUNT(SZ_4K);
	atomic_set(&rsv->count, 1);
	rsv->entry[0].base = addr;
	rsv->entry[0].size = size;

	spin_lock(&efi_mem_reserve_persistent_lock);
	rsv->next = efi_memreserve_root->next;
	efi_memreserve_root->next = __pa(rsv);
	spin_unlock(&efi_mem_reserve_persistent_lock);

	return 0;
}


static int __init fdt_find_uefi_params(unsigned long node, const char *uname,
				       int depth, void *data)
{
	struct param_info *info = data;
	int i;

	for (i = 0; i < ARRAY_SIZE(dt_params); i++) {
		const char *subnode = dt_params[i].subnode;

		if (depth != 1 || strcmp(uname, dt_params[i].uname) != 0) {
			info->missing = dt_params[i].params[0].name;
			continue;
		}

		if (subnode) {
			int err = of_get_flat_dt_subnode_by_name(node, subnode);

			if (err < 0)
				return 0;

			node = err;
		}

		return __find_uefi_params(node, info, dt_params[i].params);
	}

	return 0;
}


static int generic_ops_register(void)
{
	generic_ops.get_variable = efi.get_variable;
	generic_ops.set_variable = efi.set_variable;
	generic_ops.set_variable_nonblocking = efi.set_variable_nonblocking;
	generic_ops.get_next_variable = efi.get_next_variable;
	generic_ops.query_variable_store = efi_query_variable_store;

	return efivars_register(&generic_efivars, &generic_ops, efi_kobj);
}


u64 efi_mem_attributes(unsigned long phys_addr)
{
	efi_memory_desc_t *md;

	if (!efi_enabled(EFI_MEMMAP))
		return 0;

	for_each_efi_memory_desc(md) {
		if ((md->phys_addr <= phys_addr) &&
		    (phys_addr < (md->phys_addr +
		    (md->num_pages << EFI_PAGE_SHIFT))))
			return md->attribute;
	}
	return 0;
}


static ssize_t acpi_table_length_show(struct config_item *cfg, char *str)
{
	struct acpi_table_header *h = get_header(cfg);

	if (!h)
		return -EINVAL;

	return sprintf(str, "%d\n", h->length);
}


static ssize_t acpi_table_aml_write(struct config_item *cfg,
				    const void *data, size_t size)
{
	const struct acpi_table_header *header = data;
	struct acpi_table *table;
	int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);

	if (ret)
		return ret;

	table = container_of(cfg, struct acpi_table, cfg);

	if (table->header) {
		pr_err("table already loaded\n");
		return -EBUSY;
	}

	if (header->length != size) {
		pr_err("invalid table length\n");
		return -EINVAL;
	}

	if (memcmp(header->signature, ACPI_SIG_SSDT, 4)) {
		pr_err("invalid table signature\n");
		return -EINVAL;
	}

	table = container_of(cfg, struct acpi_table, cfg);

	table->header = kmemdup(header, header->length, GFP_KERNEL);
	if (!table->header)
		return -ENOMEM;

	ret = acpi_load_table(table->header, &table->index);
	if (ret) {
		kfree(table->header);
		table->header = NULL;
	}

	return ret;
}


static void xgmac_enet_send(XgmacState *s)
{
    struct desc bd;
    int frame_size;
    int len;
    uint8_t frame[8192];
    uint8_t *ptr;

    ptr = frame;
    frame_size = 0;
    while (1) {
        xgmac_read_desc(s, &bd, 0);
        if ((bd.ctl_stat & 0x80000000) == 0) {
            /* Run out of descriptors to transmit.  */
            break;
        }
        len = (bd.buffer1_size & 0xfff) + (bd.buffer2_size & 0xfff);

        /*
         * FIXME: these cases of malformed tx descriptors (bad sizes)
         * should probably be reported back to the guest somehow
         * rather than simply silently stopping processing, but we
         * don't know what the hardware does in this situation.
         * This will only happen for buggy guests anyway.
         */
        if ((bd.buffer1_size & 0xfff) > 2048) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 1 len on send > 2048 (0x%x)\n",
                         __func__, bd.buffer1_size & 0xfff);
            break;
        }
        if ((bd.buffer2_size & 0xfff) != 0) {
            DEBUGF_BRK("qemu:%s:ERROR...ERROR...ERROR... -- "
                        "xgmac buffer 2 len on send != 0 (0x%x)\n",
                        __func__, bd.buffer2_size & 0xfff);
            break;
        }
        if (frame_size + len >= sizeof(frame)) {
            DEBUGF_BRK("qemu:%s: buffer overflow %d read into %zu "
                        "buffer\n" , __func__, frame_size + len, sizeof(frame));
            DEBUGF_BRK("qemu:%s: buffer1.size=%d; buffer2.size=%d\n",
                        __func__, bd.buffer1_size, bd.buffer2_size);
            break;
        }

        cpu_physical_memory_read(bd.buffer1_addr, ptr, len);
        ptr += len;
        frame_size += len;
        if (bd.ctl_stat & 0x20000000) {
            /* Last buffer in frame.  */
            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
            ptr = frame;
            frame_size = 0;
            s->regs[DMA_STATUS] |= DMA_STATUS_TI | DMA_STATUS_NIS;
        }
        bd.ctl_stat &= ~0x80000000;
        /* Write back the modified descriptor.  */
        xgmac_write_desc(s, &bd, 0);
    }
}


static int eth_can_rx(XgmacState *s)
{
    /* RX enabled?  */
    return s->regs[DMA_CONTROL] & DMA_CONTROL_SR;
}


static void enet_write(void *opaque, hwaddr addr,
                       uint64_t value, unsigned size)
{
    XgmacState *s = opaque;

    addr >>= 2;
    switch (addr) {
    case DMA_BUS_MODE:
        s->regs[DMA_BUS_MODE] = value & ~0x1;
        break;
    case DMA_XMT_POLL_DEMAND:
        xgmac_enet_send(s);
        break;
    case DMA_STATUS:
        s->regs[DMA_STATUS] = s->regs[DMA_STATUS] & ~value;
        break;
    case DMA_RCV_BASE_ADDR:
        s->regs[DMA_RCV_BASE_ADDR] = s->regs[DMA_CUR_RX_DESC_ADDR] = value;
        break;
    case DMA_TX_BASE_ADDR:
        s->regs[DMA_TX_BASE_ADDR] = s->regs[DMA_CUR_TX_DESC_ADDR] = value;
        break;
    default:
        if (addr < ARRAY_SIZE(s->regs)) {
            s->regs[addr] = value;
        }
        break;
    }
    enet_update_irq(s);
}


static struct buffer_head *ext4_find_entry(struct inode *dir,
					   const struct qstr *d_name,
					   struct ext4_dir_entry_2 **res_dir,
					   int *inlined)
{
	int err;
	struct ext4_filename fname;
	struct buffer_head *bh;

	err = ext4_fname_setup_filename(dir, d_name, 1, &fname);
	if (err == -ENOENT)
		return NULL;
	if (err)
		return ERR_PTR(err);

	bh = __ext4_find_entry(dir, &fname, res_dir, inlined);

	ext4_fname_free_filename(&fname);
	return bh;
}


static inline ext4_lblk_t dx_get_block(struct dx_entry *entry)
{
	return le32_to_cpu(entry->block) & 0x0fffffff;
}


static int htree_dirblock_to_tree(struct file *dir_file,
				  struct inode *dir, ext4_lblk_t block,
				  struct dx_hash_info *hinfo,
				  __u32 start_hash, __u32 start_minor_hash)
{
	struct buffer_head *bh;
	struct ext4_dir_entry_2 *de, *top;
	int err = 0, count = 0;
	struct fscrypt_str fname_crypto_str = FSTR_INIT(NULL, 0), tmp_str;

	dxtrace(printk(KERN_INFO "In htree dirblock_to_tree: block %lu\n",
							(unsigned long)block));
	bh = ext4_read_dirblock(dir, block, DIRENT_HTREE);
	if (IS_ERR(bh))
		return PTR_ERR(bh);

	de = (struct ext4_dir_entry_2 *) bh->b_data;
	top = (struct ext4_dir_entry_2 *) ((char *) de +
					   dir->i_sb->s_blocksize -
					   EXT4_DIR_REC_LEN(0));
	/* Check if the directory is encrypted */
	if (IS_ENCRYPTED(dir)) {
		err = fscrypt_get_encryption_info(dir);
		if (err < 0) {
			brelse(bh);
			return err;
		}
		err = fscrypt_fname_alloc_buffer(dir, EXT4_NAME_LEN,
						     &fname_crypto_str);
		if (err < 0) {
			brelse(bh);
			return err;
		}
	}

	for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) {
		if (ext4_check_dir_entry(dir, NULL, de, bh,
				bh->b_data, bh->b_size,
				(block<<EXT4_BLOCK_SIZE_BITS(dir->i_sb))
					 + ((char *)de - bh->b_data))) {
			/* silently ignore the rest of the block */
			break;
		}
		ext4fs_dirhash(dir, de->name, de->name_len, hinfo);
		if ((hinfo->hash < start_hash) ||
		    ((hinfo->hash == start_hash) &&
		     (hinfo->minor_hash < start_minor_hash)))
			continue;
		if (de->inode == 0)
			continue;
		if (!IS_ENCRYPTED(dir)) {
			tmp_str.name = de->name;
			tmp_str.len = de->name_len;
			err = ext4_htree_store_dirent(dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
				   &tmp_str);
		} else {
			int save_len = fname_crypto_str.len;
			struct fscrypt_str de_name = FSTR_INIT(de->name,
								de->name_len);

			/* Directory is encrypted */
			err = fscrypt_fname_disk_to_usr(dir, hinfo->hash,
					hinfo->minor_hash, &de_name,
					&fname_crypto_str);
			if (err) {
				count = err;
				goto errout;
			}
			err = ext4_htree_store_dirent(dir_file,
				   hinfo->hash, hinfo->minor_hash, de,
					&fname_crypto_str);
			fname_crypto_str.len = save_len;
		}
		if (err != 0) {
			count = err;
			goto errout;
		}
		count++;
	}
errout:
	brelse(bh);
	fscrypt_fname_free_buffer(&fname_crypto_str);
	return count;
}


static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
		       struct inode *new_dir, struct dentry *new_dentry,
		       unsigned int flags)
{
	handle_t *handle = NULL;
	struct ext4_renament old = {
		.dir = old_dir,
		.dentry = old_dentry,
		.inode = d_inode(old_dentry),
	};
	struct ext4_renament new = {
		.dir = new_dir,
		.dentry = new_dentry,
		.inode = d_inode(new_dentry),
	};
	int force_reread;
	int retval;
	struct inode *whiteout = NULL;
	int credits;
	u8 old_file_type;

	if (new.inode && new.inode->i_nlink == 0) {
		EXT4_ERROR_INODE(new.inode,
				 "target of rename is already freed");
		return -EFSCORRUPTED;
	}

	if ((ext4_test_inode_flag(new_dir, EXT4_INODE_PROJINHERIT)) &&
	    (!projid_eq(EXT4_I(new_dir)->i_projid,
			EXT4_I(old_dentry->d_inode)->i_projid)))
		return -EXDEV;

	retval = dquot_initialize(old.dir);
	if (retval)
		return retval;
	retval = dquot_initialize(new.dir);
	if (retval)
		return retval;

	/* Initialize quotas before so that eventual writes go
	 * in separate transaction */
	if (new.inode) {
		retval = dquot_initialize(new.inode);
		if (retval)
			return retval;
	}

	old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
	if (IS_ERR(old.bh))
		return PTR_ERR(old.bh);
	/*
	 *  Check for inode number is _not_ due to possible IO errors.
	 *  We might rmdir the source, keep it as pwd of some process
	 *  and merrily kill the link to whatever was created under the
	 *  same name. Goodbye sticky bit ;-<
	 */
	retval = -ENOENT;
	if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
		goto end_rename;

	new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
				 &new.de, &new.inlined);
	if (IS_ERR(new.bh)) {
		retval = PTR_ERR(new.bh);
		new.bh = NULL;
		goto end_rename;
	}
	if (new.bh) {
		if (!new.inode) {
			brelse(new.bh);
			new.bh = NULL;
		}
	}
	if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
		ext4_alloc_da_blocks(old.inode);

	credits = (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
		   EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2);
	if (!(flags & RENAME_WHITEOUT)) {
		handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits);
		if (IS_ERR(handle)) {
			retval = PTR_ERR(handle);
			handle = NULL;
			goto end_rename;
		}
	} else {
		whiteout = ext4_whiteout_for_rename(&old, credits, &handle);
		if (IS_ERR(whiteout)) {
			retval = PTR_ERR(whiteout);
			whiteout = NULL;
			goto end_rename;
		}
	}

	if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
		ext4_handle_sync(handle);

	if (S_ISDIR(old.inode->i_mode)) {
		if (new.inode) {
			retval = -ENOTEMPTY;
			if (!ext4_empty_dir(new.inode))
				goto end_rename;
		} else {
			retval = -EMLINK;
			if (new.dir != old.dir && EXT4_DIR_LINK_MAX(new.dir))
				goto end_rename;
		}
		retval = ext4_rename_dir_prepare(handle, &old);
		if (retval)
			goto end_rename;
	}
	/*
	 * If we're renaming a file within an inline_data dir and adding or
	 * setting the new dirent causes a conversion from inline_data to
	 * extents/blockmap, we need to force the dirent delete code to
	 * re-read the directory, or else we end up trying to delete a dirent
	 * from what is now the extent tree root (or a block map).
	 */
	force_reread = (new.dir->i_ino == old.dir->i_ino &&
			ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));

	old_file_type = old.de->file_type;
	if (whiteout) {
		/*
		 * Do this before adding a new entry, so the old entry is sure
		 * to be still pointing to the valid old entry.
		 */
		retval = ext4_setent(handle, &old, whiteout->i_ino,
				     EXT4_FT_CHRDEV);
		if (retval)
			goto end_rename;
		retval = ext4_mark_inode_dirty(handle, whiteout);
		if (unlikely(retval))
			goto end_rename;
	}
	if (!new.bh) {
		retval = ext4_add_entry(handle, new.dentry, old.inode);
		if (retval)
			goto end_rename;
	} else {
		retval = ext4_setent(handle, &new,
				     old.inode->i_ino, old_file_type);
		if (retval)
			goto end_rename;
	}
	if (force_reread)
		force_reread = !ext4_test_inode_flag(new.dir,
						     EXT4_INODE_INLINE_DATA);

	/*
	 * Like most other Unix systems, set the ctime for inodes on a
	 * rename.
	 */
	old.inode->i_ctime = current_time(old.inode);
	retval = ext4_mark_inode_dirty(handle, old.inode);
	if (unlikely(retval))
		goto end_rename;

	if (!whiteout) {
		/*
		 * ok, that's it
		 */
		ext4_rename_delete(handle, &old, force_reread);
	}

	if (new.inode) {
		ext4_dec_count(handle, new.inode);
		new.inode->i_ctime = current_time(new.inode);
	}
	old.dir->i_ctime = old.dir->i_mtime = current_time(old.dir);
	ext4_update_dx_flag(old.dir);
	if (old.dir_bh) {
		retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
		if (retval)
			goto end_rename;

		ext4_dec_count(handle, old.dir);
		if (new.inode) {
			/* checked ext4_empty_dir above, can't have another
			 * parent, ext4_dec_count() won't work for many-linked
			 * dirs */
			clear_nlink(new.inode);
		} else {
			ext4_inc_count(handle, new.dir);
			ext4_update_dx_flag(new.dir);
			retval = ext4_mark_inode_dirty(handle, new.dir);
			if (unlikely(retval))
				goto end_rename;
		}
	}
	retval = ext4_mark_inode_dirty(handle, old.dir);
	if (unlikely(retval))
		goto end_rename;
	if (new.inode) {
		retval = ext4_mark_inode_dirty(handle, new.inode);
		if (unlikely(retval))
			goto end_rename;
		if (!new.inode->i_nlink)
			ext4_orphan_add(handle, new.inode);
	}
	retval = 0;

end_rename:
	brelse(old.dir_bh);
	brelse(old.bh);
	brelse(new.bh);
	if (whiteout) {
		if (retval)
			drop_nlink(whiteout);
		unlock_new_inode(whiteout);
		iput(whiteout);
	}
	if (handle)
		ext4_journal_stop(handle);
	return retval;
}


static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
						ext4_lblk_t block,
						dirblock_type_t type,
						const char *func,
						unsigned int line)
{
	struct buffer_head *bh;
	struct ext4_dir_entry *dirent;
	int is_dx_block = 0;

	if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
		bh = ERR_PTR(-EIO);
	else
		bh = ext4_bread(NULL, inode, block, 0);
	if (IS_ERR(bh)) {
		__ext4_warning(inode->i_sb, func, line,
			       "inode #%lu: lblock %lu: comm %s: "
			       "error %ld reading directory block",
			       inode->i_ino, (unsigned long)block,
			       current->comm, PTR_ERR(bh));

		return bh;
	}
	if (!bh && (type == INDEX || type == DIRENT_HTREE)) {
		ext4_error_inode(inode, func, line, block,
				 "Directory hole found for htree %s block",
				 (type == INDEX) ? "index" : "leaf");
		return ERR_PTR(-EFSCORRUPTED);
	}
	if (!bh)
		return NULL;
	dirent = (struct ext4_dir_entry *) bh->b_data;
	/* Determine whether or not we have an index block */
	if (is_dx(inode)) {
		if (block == 0)
			is_dx_block = 1;
		else if (ext4_rec_len_from_disk(dirent->rec_len,
						inode->i_sb->s_blocksize) ==
			 inode->i_sb->s_blocksize)
			is_dx_block = 1;
	}
	if (!is_dx_block && type == INDEX) {
		ext4_error_inode(inode, func, line, block,
		       "directory leaf block found instead of index block");
		brelse(bh);
		return ERR_PTR(-EFSCORRUPTED);
	}
	if (!ext4_has_metadata_csum(inode->i_sb) ||
	    buffer_verified(bh))
		return bh;

	/*
	 * An empty leaf block can get mistaken for a index block; for
	 * this reason, we can only check the index checksum when the
	 * caller is sure it should be an index block.
	 */
	if (is_dx_block && type == INDEX) {
		if (ext4_dx_csum_verify(inode, dirent) &&
		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
			set_buffer_verified(bh);
		else {
			ext4_error_inode_err(inode, func, line, block,
					     EFSBADCRC,
					     "Directory index failed checksum");
			brelse(bh);
			return ERR_PTR(-EFSBADCRC);
		}
	}
	if (!is_dx_block) {
		if (ext4_dirblock_csum_verify(inode, bh) &&
		    !ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_CRC))
			set_buffer_verified(bh);
		else {
			ext4_error_inode_err(inode, func, line, block,
					     EFSBADCRC,
					     "Directory block failed checksum");
			brelse(bh);
			return ERR_PTR(-EFSBADCRC);
		}
	}
	return bh;
}


static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent,
			   int count_offset, int count, struct dx_tail *t)
{
	struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
	struct ext4_inode_info *ei = EXT4_I(inode);
	__u32 csum;
	int size;
	__u32 dummy_csum = 0;
	int offset = offsetof(struct dx_tail, dt_checksum);

	size = count_offset + (count * sizeof(struct dx_entry));
	csum = ext4_chksum(sbi, ei->i_csum_seed, (__u8 *)dirent, size);
	csum = ext4_chksum(sbi, csum, (__u8 *)t, offset);
	csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum));

	return cpu_to_le32(csum);
}


static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
			     struct inode *dir,
			     struct inode *inode, struct ext4_dir_entry_2 *de,
			     struct buffer_head *bh)
{
	unsigned int	blocksize = dir->i_sb->s_blocksize;
	int		csum_size = 0;
	int		err, err2;

	if (ext4_has_metadata_csum(inode->i_sb))
		csum_size = sizeof(struct ext4_dir_entry_tail);

	if (!de) {
		err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
					blocksize - csum_size, fname, &de);
		if (err)
			return err;
	}
	BUFFER_TRACE(bh, "get_write_access");
	err = ext4_journal_get_write_access(handle, bh);
	if (err) {
		ext4_std_error(dir->i_sb, err);
		return err;
	}

	/* By now the buffer is marked for journaling */
	ext4_insert_dentry(inode, de, blocksize, fname);

	/*
	 * XXX shouldn't update any times until successful
	 * completion of syscall, but too many callers depend
	 * on this.
	 *
	 * XXX similarly, too many callers depend on
	 * ext4_new_inode() setting the times, but error
	 * recovery deletes the inode, so the worst that can
	 * happen is that the times are slightly out of date
	 * and/or different from the directory change time.
	 */
	dir->i_mtime = dir->i_ctime = current_time(dir);
	ext4_update_dx_flag(dir);
	inode_inc_iversion(dir);
	err2 = ext4_mark_inode_dirty(handle, dir);
	BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
	err = ext4_handle_dirty_dirblock(handle, dir, bh);
	if (err)
		ext4_std_error(dir->i_sb, err);
	return err ? err : err2;
}


static struct stats dx_show_leaf(struct inode *dir,
				struct dx_hash_info *hinfo,
				struct ext4_dir_entry_2 *de,
				int size, int show_names)
{
	unsigned names = 0, space = 0;
	char *base = (char *) de;
	struct dx_hash_info h = *hinfo;

	printk("names: ");
	while ((char *) de < base + size)
	{
		if (de->inode)
		{
			if (show_names)
			{
#ifdef CONFIG_FS_ENCRYPTION
				int len;
				char *name;
				struct fscrypt_str fname_crypto_str =
					FSTR_INIT(NULL, 0);
				int res = 0;

				name  = de->name;
				len = de->name_len;
				if (IS_ENCRYPTED(dir))
					res = fscrypt_get_encryption_info(dir);
				if (res) {
					printk(KERN_WARNING "Error setting up"
					       " fname crypto: %d\n", res);
				}
				if (!fscrypt_has_encryption_key(dir)) {
					/* Directory is not encrypted */
					ext4fs_dirhash(dir, de->name,
						de->name_len, &h);
					printk("%*.s:(U)%x.%u ", len,
					       name, h.hash,
					       (unsigned) ((char *) de
							   - base));
				} else {
					struct fscrypt_str de_name =
						FSTR_INIT(name, len);

					/* Directory is encrypted */
					res = fscrypt_fname_alloc_buffer(
						dir, len,
						&fname_crypto_str);
					if (res)
						printk(KERN_WARNING "Error "
							"allocating crypto "
							"buffer--skipping "
							"crypto\n");
					res = fscrypt_fname_disk_to_usr(dir,
						0, 0, &de_name,
						&fname_crypto_str);
					if (res) {
						printk(KERN_WARNING "Error "
							"converting filename "
							"from disk to usr"
							"\n");
						name = "??";
						len = 2;
					} else {
						name = fname_crypto_str.name;
						len = fname_crypto_str.len;
					}
					ext4fs_dirhash(dir, de->name,
						       de->name_len, &h);
					printk("%*.s:(E)%x.%u ", len, name,
					       h.hash, (unsigned) ((char *) de
								   - base));
					fscrypt_fname_free_buffer(
							&fname_crypto_str);
				}
#else
				int len = de->name_len;
				char *name = de->name;
				ext4fs_dirhash(dir, de->name, de->name_len, &h);
				printk("%*.s:%x.%u ", len, name, h.hash,
				       (unsigned) ((char *) de - base));
#endif
			}
			space += EXT4_DIR_REC_LEN(de->name_len);
			names++;
		}
		de = ext4_next_entry(de, size);
	}
	printk(KERN_CONT "(%i)\n", names);
	return (struct stats) { names, space, 1 };
}


void ext4_initialize_dirent_tail(struct buffer_head *bh,
				 unsigned int blocksize)
{
	struct ext4_dir_entry_tail *t = EXT4_DIRENT_TAIL(bh->b_data, blocksize);

	memset(t, 0, sizeof(struct ext4_dir_entry_tail));
	t->det_rec_len = ext4_rec_len_to_disk(
			sizeof(struct ext4_dir_entry_tail), blocksize);
	t->det_reserved_ft = EXT4_FT_DIR_CSUM;
}


struct dentry *ext4_get_parent(struct dentry *child)
{
	__u32 ino;
	static const struct qstr dotdot = QSTR_INIT("..", 2);
	struct ext4_dir_entry_2 * de;
	struct buffer_head *bh;

	bh = ext4_find_entry(d_inode(child), &dotdot, &de, NULL);
	if (IS_ERR(bh))
		return ERR_CAST(bh);
	if (!bh)
		return ERR_PTR(-ENOENT);
	ino = le32_to_cpu(de->inode);
	brelse(bh);

	if (!ext4_valid_inum(child->d_sb, ino)) {
		EXT4_ERROR_INODE(d_inode(child),
				 "bad parent inode number: %u", ino);
		return ERR_PTR(-EFSCORRUPTED);
	}

	return d_obtain_alias(ext4_iget(child->d_sb, ino, EXT4_IGET_NORMAL));
}


const char* sldns_get_errorstr_parse(int e)
{
	sldns_lookup_table *lt;
	lt = sldns_lookup_by_id(sldns_wireparse_errors, LDNS_WIREPARSE_ERROR(e));
	return lt?lt->name:"unknown error";
}


int sldns_str2wire_rdf_buf(const char* str, uint8_t* rd, size_t* len,
	sldns_rdf_type rdftype)
{
	switch (rdftype) {
	case LDNS_RDF_TYPE_DNAME:
		return sldns_str2wire_dname_buf(str, rd, len);
	case LDNS_RDF_TYPE_INT8:
		return sldns_str2wire_int8_buf(str, rd, len);
	case LDNS_RDF_TYPE_INT16:
		return sldns_str2wire_int16_buf(str, rd, len);
	case LDNS_RDF_TYPE_INT32:
		return sldns_str2wire_int32_buf(str, rd, len);
	case LDNS_RDF_TYPE_A:
		return sldns_str2wire_a_buf(str, rd, len);
	case LDNS_RDF_TYPE_AAAA:
		return sldns_str2wire_aaaa_buf(str, rd, len);
	case LDNS_RDF_TYPE_STR:
		return sldns_str2wire_str_buf(str, rd, len);
	case LDNS_RDF_TYPE_APL:
		return sldns_str2wire_apl_buf(str, rd, len);
	case LDNS_RDF_TYPE_B64:
		return sldns_str2wire_b64_buf(str, rd, len);
	case LDNS_RDF_TYPE_B32_EXT:
		return sldns_str2wire_b32_ext_buf(str, rd, len);
	case LDNS_RDF_TYPE_HEX:
		return sldns_str2wire_hex_buf(str, rd, len);
	case LDNS_RDF_TYPE_NSEC:
		return sldns_str2wire_nsec_buf(str, rd, len);
	case LDNS_RDF_TYPE_TYPE:
		return sldns_str2wire_type_buf(str, rd, len);
	case LDNS_RDF_TYPE_CLASS:
		return sldns_str2wire_class_buf(str, rd, len);
	case LDNS_RDF_TYPE_CERT_ALG:
		return sldns_str2wire_cert_alg_buf(str, rd, len);
	case LDNS_RDF_TYPE_ALG:
		return sldns_str2wire_alg_buf(str, rd, len);
	case LDNS_RDF_TYPE_TIME:
		return sldns_str2wire_time_buf(str, rd, len);
	case LDNS_RDF_TYPE_PERIOD:
		return sldns_str2wire_period_buf(str, rd, len);
	case LDNS_RDF_TYPE_TSIGTIME:
		return sldns_str2wire_tsigtime_buf(str, rd, len);
	case LDNS_RDF_TYPE_LOC:
		return sldns_str2wire_loc_buf(str, rd, len);
	case LDNS_RDF_TYPE_WKS:
		return sldns_str2wire_wks_buf(str, rd, len);
	case LDNS_RDF_TYPE_NSAP:
		return sldns_str2wire_nsap_buf(str, rd, len);
	case LDNS_RDF_TYPE_ATMA:
		return sldns_str2wire_atma_buf(str, rd, len);
	case LDNS_RDF_TYPE_IPSECKEY:
		return sldns_str2wire_ipseckey_buf(str, rd, len);
	case LDNS_RDF_TYPE_NSEC3_SALT:
		return sldns_str2wire_nsec3_salt_buf(str, rd, len);
	case LDNS_RDF_TYPE_NSEC3_NEXT_OWNER:
		return sldns_str2wire_b32_ext_buf(str, rd, len);
	case LDNS_RDF_TYPE_ILNP64:
		return sldns_str2wire_ilnp64_buf(str, rd, len);
	case LDNS_RDF_TYPE_EUI48:
		return sldns_str2wire_eui48_buf(str, rd, len);
	case LDNS_RDF_TYPE_EUI64:
		return sldns_str2wire_eui64_buf(str, rd, len);
	case LDNS_RDF_TYPE_TAG:
		return sldns_str2wire_tag_buf(str, rd, len);
	case LDNS_RDF_TYPE_LONG_STR:
		return sldns_str2wire_long_str_buf(str, rd, len);
	case LDNS_RDF_TYPE_TSIGERROR:
		return sldns_str2wire_tsigerror_buf(str, rd, len);
	case LDNS_RDF_TYPE_HIP:
		return sldns_str2wire_hip_buf(str, rd, len);
	case LDNS_RDF_TYPE_INT16_DATA:
		return sldns_str2wire_int16_data_buf(str, rd, len);
	case LDNS_RDF_TYPE_UNKNOWN:
	case LDNS_RDF_TYPE_SERVICE:
		return LDNS_WIREPARSE_ERR_NOT_IMPL;
	case LDNS_RDF_TYPE_NONE:
	default:
		break;
	}
	return LDNS_WIREPARSE_ERR_GENERAL;
}


int sldns_str2wire_atma_buf(const char* str, uint8_t* rd, size_t* len)
{
	const char* s = str;
	size_t slen = strlen(str);
	size_t dlen = 0; /* number of hexdigits parsed */

	/* just a hex string with optional dots? */
	/* notimpl e.164 format */
	if(slen > LDNS_MAX_RDFLEN*2)
		return LDNS_WIREPARSE_ERR_LABEL_OVERFLOW;
	while(*s) {
		if(isspace((unsigned char)*s) || *s == '.') {
			s++;
			continue;
		}
		if(!isxdigit((unsigned char)*s))
			return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
		if(*len < dlen/2 + 1)
			return RET_ERR(LDNS_WIREPARSE_ERR_BUFFER_TOO_SMALL,
				s-str);
		if((dlen&1)==0)
			rd[dlen/2] = (uint8_t)sldns_hexdigit_to_int(*s++) * 16;
		else	rd[dlen/2] += sldns_hexdigit_to_int(*s++);
		dlen++;
	}
	if((dlen&1)!=0)
		return RET_ERR(LDNS_WIREPARSE_ERR_SYNTAX_HEX, s-str);
	*len = dlen/2;
	return LDNS_WIREPARSE_ERR_OK;
}


sldns_str2wire_rr_buf_internal(const char* str, uint8_t* rr, size_t* len,
	size_t* dname_len, uint32_t default_ttl, uint8_t* origin,
	size_t origin_len, uint8_t* prev, size_t prev_len, int question)
{
	int status;
	int not_there = 0;
	char token[LDNS_MAX_RDFLEN+1];
	uint32_t ttl = 0;
	uint16_t tp = 0, cl = 0;
	size_t ddlen = 0;

	/* string in buffer */
	sldns_buffer strbuf;
	sldns_buffer_init_frm_data(&strbuf, (uint8_t*)str, strlen(str));
	if(!dname_len) dname_len = &ddlen;

	/* parse the owner */
	if((status=rrinternal_get_owner(&strbuf, rr, len, dname_len, origin,
		origin_len, prev, prev_len, token, sizeof(token))) != 0)
		return status;

	/* parse the [ttl] [class] <type> */
	if((status=rrinternal_get_ttl(&strbuf, token, sizeof(token),
		&not_there, &ttl, default_ttl)) != 0)
		return status;
	if((status=rrinternal_get_class(&strbuf, token, sizeof(token),
		&not_there, &cl)) != 0)
		return status;
	if((status=rrinternal_get_type(&strbuf, token, sizeof(token),
		&not_there, &tp)) != 0)
		return status;
	/* put ttl, class, type into the rr result */
	if((status=rrinternal_write_typeclassttl(&strbuf, rr, *len, *dname_len, tp, cl,
		ttl, question)) != 0)
		return status;
	/* for a question-RR we are done, no rdata */
	if(question) {
		*len = *dname_len + 4;
		return LDNS_WIREPARSE_ERR_OK;
	}

	/* rdata */
	if((status=rrinternal_parse_rdata(&strbuf, token, sizeof(token),
		rr, len, *dname_len, tp, origin, origin_len)) != 0)
		return status;

	return LDNS_WIREPARSE_ERR_OK;
}