//
//  REGQMVAL.C
//
//  Copyright (C) Microsoft Corporation, 1995-1996
//
//  Implementation of RegQueryMultipleValues and supporting functions.
//

#include "pch.h"

#ifdef VXD
#pragma VxD_RARE_CODE_SEG
#endif

#ifdef IS_32

//
//  VMMRegQueryMultipleValues
//
//  See Win32 documentation of RegQueryMultipleValues.  However, the Win95
//  implementation breaks many of the rules that are described in the
//  documentation:
//      *  num_vals is a count of VALENT structures, not a size in bytes.
//      *  data is not DWORD aligned in lpValueBuffer.
//      *  if lpValueBuffer is too small, lpValueBuffer is not filled to the
//         size specified by lpdwTotalSize.
//
//  All of this plus dynamic keys makes this an extremely ugly routine, but
//  every attempt was made to be compatible with the Win95 semantics.
//

LONG
REGAPI
VMMRegQueryMultipleValues(
    HKEY hKey,
    PVALENT val_list,
    DWORD num_vals,
    LPSTR lpValueBuffer,
    LPDWORD lpdwTotalSize
    )
{

    int ErrorCode;
    PVALENT pCurrentValent;
    DWORD Counter;
    DWORD BufferSize;
    DWORD RequiredSize;
    LPKEY_RECORD lpKeyRecord;
    LPVALUE_RECORD lpValueRecord;
    LPSTR lpCurrentBuffer;
#ifdef WANT_DYNKEY_SUPPORT
    PVALCONTEXT pValueContext;
    PINTERNAL_PROVIDER pProvider;
    PPVALUE pProviderValue;
#endif

    if (IsBadHugeReadPtr(val_list, sizeof(VALENT) * num_vals))
        return ERROR_INVALID_PARAMETER;

    if (IsBadHugeWritePtr(lpdwTotalSize, sizeof(DWORD)))
        return ERROR_INVALID_PARAMETER;

    if (IsBadHugeWritePtr(lpValueBuffer, *lpdwTotalSize))
        return ERROR_INVALID_PARAMETER;

    for (Counter = num_vals, pCurrentValent = val_list; Counter > 0; Counter--,
        pCurrentValent++) {
        if (IsBadStringPtr(pCurrentValent-> ve_valuename, (UINT) -1))
            return ERROR_INVALID_PARAMETER;
    }

    if (!RgLockRegistry())
        return ERROR_LOCK_FAILED;

    if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) != ERROR_SUCCESS)
        goto ReturnErrorCode;

#ifdef WANT_DYNKEY_SUPPORT
    //  Check if this a dynamic key that has a "get all" atomic callback.  If
    //  the dynamic key just has "get one" callback, then we'll fall into the
    //  non-dynamic case.
    if (IsDynDataKey(hKey) && !IsNullPtr(hKey-> pProvider-> ipi_R0_allvals)) {

        pProvider = hKey-> pProvider;

        pValueContext = RgSmAllocMemory(sizeof(struct val_context) * num_vals);

        if (IsNullPtr(pValueContext)) {
            ErrorCode = ERROR_OUTOFMEMORY;
            goto ReturnErrorCode;
        }

        //
        //  Compute the required buffer size to hold all the value data and
        //  check it against the provided buffer size.
        //

        RequiredSize = 0;

        for (Counter = 0, pCurrentValent = val_list; Counter < num_vals;
            Counter++, pCurrentValent++) {

            if ((ErrorCode = RgLookupValueByName(hKey, pCurrentValent->
                ve_valuename, &lpKeyRecord, &lpValueRecord)) != ERROR_SUCCESS)
                goto ReturnErrorCode;

            //  The value data contains only part of a PROVIDER structure.
            pProviderValue = CONTAINING_RECORD(&lpValueRecord-> Name +
                lpValueRecord-> NameLength, PVALUE, pv_valuelen);

            pValueContext[Counter].value_context = pProviderValue->
                pv_value_context;
            pCurrentValent-> ve_type = pProviderValue-> pv_type;

            if (hKey-> Flags & KEYF_PROVIDERHASVALUELENGTH) {

                //  Must zero it so that some providers don't try to stomp on
                //  lpData.
                pCurrentValent-> ve_valuelen = 0;

                ErrorCode = pProvider-> ipi_R0_1val(pProvider-> ipi_key_context,
                    &pValueContext[Counter], 1, NULL, &(pCurrentValent->
                    ve_valuelen), 0);

                //  Providers should really be returning either of these errors
                //  to us.
                ASSERT((ErrorCode == ERROR_SUCCESS) || (ErrorCode ==
                    ERROR_MORE_DATA));

            }

            else {
                pCurrentValent-> ve_valuelen = pProviderValue-> pv_valuelen;
            }

            RgUnlockDatablock(hKey-> lpFileInfo, hKey-> BigKeyLockedBlockIndex, FALSE);

            pCurrentValent-> ve_valueptr = (DWORD) NULL;
            RequiredSize += pCurrentValent-> ve_valuelen;

        }

        BufferSize = *lpdwTotalSize;
        *lpdwTotalSize = RequiredSize;

        if (BufferSize < RequiredSize)
            ErrorCode = ERROR_MORE_DATA;

        else if (pProvider-> ipi_R0_allvals(pProvider-> ipi_key_context,
            pValueContext, num_vals, lpValueBuffer, lpdwTotalSize, 0) !=
            ERROR_SUCCESS)
            ErrorCode = ERROR_CANTREAD;

        else {

            ErrorCode = ERROR_SUCCESS;

            //  Copy the pointers to the value data back to the user's buffer.
            //  Don't ask me why, but the Win95 code copies the value length
            //  back again if the provider is maintaining it.
            for (Counter = 0, pCurrentValent = val_list; Counter < num_vals;
                Counter++, pCurrentValent++) {
                pCurrentValent-> ve_valueptr = (DWORD)
                    pValueContext[Counter].val_buff_ptr;
                if (hKey-> Flags & KEYF_PROVIDERHASVALUELENGTH)
                    pCurrentValent-> ve_valuelen = pValueContext[Counter].valuelen;
            }

        }

        RgSmFreeMemory(pValueContext);

        goto ReturnErrorCode;

    }
#endif

    //
    //  First pass over the value names checks for the existence of the value
    //  and its size.  We check the total size against the provided buffer size
    //  and bail out if necessary.
    //

    RequiredSize = 0;

    for (Counter = num_vals, pCurrentValent = val_list; Counter > 0; Counter--,
        pCurrentValent++) {

        if ((ErrorCode = RgLookupValueByName(hKey, pCurrentValent->
            ve_valuename, &lpKeyRecord, &lpValueRecord)) != ERROR_SUCCESS)
            goto ReturnErrorCode;

        ErrorCode = RgCopyFromValueRecord(hKey, lpValueRecord, NULL, NULL,
            &(pCurrentValent-> ve_type), NULL, &(pCurrentValent-> ve_valuelen));

        RgUnlockDatablock(hKey-> lpFileInfo, hKey-> BigKeyLockedBlockIndex, FALSE);

        if (ErrorCode != ERROR_SUCCESS)
            goto ReturnErrorCode;

        pCurrentValent-> ve_valueptr = (DWORD) NULL;
        RequiredSize += pCurrentValent-> ve_valuelen;

    }

    BufferSize = *lpdwTotalSize;
    *lpdwTotalSize = RequiredSize;

    if (BufferSize < RequiredSize) {
        ErrorCode = ERROR_MORE_DATA;
        goto ReturnErrorCode;
    }

    //
    //  Second pass copies the value data back to the user's buffer now that we
    //  know the buffer is large enough to contain the data.
    //

    lpCurrentBuffer = lpValueBuffer;

    for (Counter = num_vals, pCurrentValent = val_list; Counter > 0; Counter--,
        pCurrentValent++) {

        if ((ErrorCode = RgLookupValueByName(hKey, pCurrentValent->
            ve_valuename, &lpKeyRecord, &lpValueRecord)) != ERROR_SUCCESS)
            goto ReturnErrorReading;

        ErrorCode = RgCopyFromValueRecord(hKey, lpValueRecord, NULL, NULL, NULL,
            lpCurrentBuffer, &(pCurrentValent-> ve_valuelen));

        RgUnlockDatablock(hKey-> lpFileInfo, hKey-> BigKeyLockedBlockIndex, FALSE);

        if (ErrorCode != ERROR_SUCCESS) {
ReturnErrorReading:
            TRAP();                     //  Registry is internally inconsistent?
            ErrorCode = ERROR_CANTREAD;
            goto ReturnErrorCode;
        }

        pCurrentValent-> ve_valueptr = (DWORD) lpCurrentBuffer;
        lpCurrentBuffer += pCurrentValent-> ve_valuelen;

    }

    ErrorCode = ERROR_SUCCESS;

ReturnErrorCode:
    RgUnlockRegistry();

    return ErrorCode;

}

#endif