/*++


Copyright (c) 1991  Microsoft Corporation

Module Name:

    RegvCls.c

Abstract:

    This module contains helper functions for enumerating, 
    setting, and querying registry values in win32

Author:

    Adam Edwards (adamed) 06-May-1998

Key Functions:

Notes:


--*/


#ifdef LOCAL

#include <rpc.h>
#include "regrpc.h"
#include "localreg.h"
#include "regclass.h"
#include "regvcls.h"
#include <malloc.h>



void ValStateGetPhysicalIndexFromLogical(
    ValueState* pValState,
    HKEY        hkLogicalKey,
    DWORD       dwLogicalIndex,
    PHKEY       phkPhysicalKey,
    DWORD*      pdwPhysicalIndex)
/*++

Routine Description:

    Retrieves a logical index for a value to a physical index

Arguments:

    pValState - value state containing values for a logical key
    hkLogicalKey - logical key we wish to index
    dwLogicalIndex - logical index to map
    phkPhysicalKey - handle to key where value is physically located
    pdwPhysicalIndex - index of value in physical key

Return Value:

    None.

Notes:

--*/
{
    //
    // If no value state is supplied, this means no merging is necessary
    // and we can return the supplied logical index as the correct
    // physical index
    //
    if (!pValState) {
        *pdwPhysicalIndex = dwLogicalIndex;
        *phkPhysicalKey = hkLogicalKey;
    } else {
        *pdwPhysicalIndex = pValState->rgIndex[dwLogicalIndex].dwOffset;
        *phkPhysicalKey = pValState->rgIndex[dwLogicalIndex].fUser ?
            pValState->hkUser :
            pValState->hkMachine;
    }
}


NTSTATUS ValStateSetPhysicalIndexFromLogical(
    ValueState*                     pValState,
    DWORD                           dwLogicalIndex)
/*++

Routine Description:

    Updates a state's mapping of logical indexes to physical indexes

Arguments:

    pValState - value state containing values for a logical key
    dwLogicalIndex - logical index used as a clue for whether
        or not we can used cached values or need to refresh the state -- 
        gives us an idea of what index the caller will be interested in 
        mapping after this call.

Return Value:

    None.

Notes:

--*/
{
    NTSTATUS Status;

    Status = STATUS_SUCCESS;

    //
    // If no value state is supplied, this means no merging is necessary
    // and we can return the supplied logical index as the correct
    // physical index
    //
    if (!pValState) {
        return STATUS_SUCCESS;
    }

    if (dwLogicalIndex >= pValState->cValues) {
        
        pValState->fDelete = TRUE;
        return STATUS_NO_MORE_ENTRIES;
    }

    //
    // Always reset if they try to go backward, or
    // if they skip by more than 1, or if they
    // ask for the same index twice and we're
    // not expecting it
    //
    if ((dwLogicalIndex < pValState->dwCurrent) || 
        (dwLogicalIndex > (pValState->dwCurrent + 1)) ||
        ((dwLogicalIndex == pValState->dwCurrent) && !(pValState->fIgnoreResetOnRetry))) {
    
        Status = ValStateUpdate(pValState);

        if (!NT_SUCCESS(Status)) {
            return Status;
        }

        pValState->fIgnoreResetOnRetry = FALSE;
    }

    return Status;
}


void ValStateRelease(
    ValueState* pValState)
/*++

Routine Description:

    Frees resources (handles, memory) associated with a value state

Arguments:

    pValState - value state containing values for a logical key

Return Value:

    None.

Notes:

--*/
{
    if (!pValState) {
        return; 
    }

    if (pValState->hkUser && (pValState->hkUser != pValState->hkLogical)) {
        NtClose(pValState->hkUser);
    }

    if (pValState->hkMachine && (pValState->hkMachine != pValState->hkLogical)) {
        NtClose(pValState->hkMachine);
    }

    if (pValState->rgIndex) {
        RegClassHeapFree(pValState->rgIndex);
    }

    RegClassHeapFree(pValState);
}


NTSTATUS ValStateUpdate(ValueState* pValState)
/*++

Routine Description:

    Updates the value state to reflect the current state
        of the logical key's physical state -- it retrieves
        the names of the values for the logical key from
        the kernel, and re-indexes the table to properly
        merge user and machine state

Arguments:

    pValState - value state containing values for a logical key

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

--*/
{
    NTSTATUS             Status;
    DWORD                cUserValues;
    DWORD                cMachineValues;
    DWORD                cMaxValues;
    DWORD                cbMaxNameLen;
    DWORD                cbMaxDataLen;
    DWORD                cbBufferLen;
    ValueLocation*       rgIndex;
    PKEY_VALUE_BASIC_INFORMATION* ppValueInfo;

    //
    // Init locals
    //
    cUserValues = 0;
    cMachineValues = 0;
    cbMaxNameLen = 0;
    rgIndex = NULL;

    pValState->cValues = 0;

    //
    // Get information about this value
    //
    Status = GetFixedKeyInfo(
        pValState->hkUser,
        pValState->hkMachine,
        &cUserValues,
        &cMachineValues,
        NULL,
        NULL,
        &cbMaxNameLen);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    cMaxValues = cUserValues + cMachineValues;

    //
    // Nothing to do if there are no Values
    //
    if (!cMaxValues) {
        return STATUS_SUCCESS;
    }

    //
    // Now allocate necessary memory
    // First get memory for index vector
    //
    rgIndex = (ValueLocation*) RegClassHeapAlloc(cMaxValues * sizeof(*rgIndex));

    if (!rgIndex) {
        return STATUS_NO_MEMORY;
    }

    //
    // Now get memory for retrieving names -- first allocate an array 
    // of pointers to values
    //
    ppValueInfo = (PKEY_VALUE_BASIC_INFORMATION*) RegClassHeapAlloc(
        sizeof(*ppValueInfo) * cMaxValues);

    if (!ppValueInfo) {

        RegClassHeapFree(rgIndex);

        return STATUS_NO_MEMORY;
    }

    RtlZeroMemory(ppValueInfo, sizeof(*ppValueInfo) * cMaxValues);

    cbBufferLen = sizeof(**ppValueInfo) + cbMaxNameLen;

    //
    // Now allocate each individual value
    //
    {
        DWORD dwValue;

        for (dwValue = 0; dwValue < cMaxValues; dwValue++) 
        {
            ppValueInfo[dwValue] = (PKEY_VALUE_BASIC_INFORMATION) RegClassHeapAlloc(
                cbBufferLen);

            if (!(ppValueInfo)[dwValue]) {
                Status = STATUS_NO_MEMORY;
                break;
            }
        }
    }

    //
    // Now fetch the values.  From this point on we are assuming success
    // and updating the index table
    // 
    {

        HKEY  hKeyPhysical;
        DWORD dwLimit;
        DWORD dwLogical;
        BOOL  fUser;

        //
        // Free the existing index table
        //
        if (pValState->rgIndex) {
            RegClassHeapFree(pValState->rgIndex);
        }

        pValState->rgIndex = rgIndex;

        dwLogical = 0;

        for( hKeyPhysical = pValState->hkUser, fUser = TRUE,
                 dwLimit = cUserValues;
             ;
             hKeyPhysical = pValState->hkMachine, fUser = FALSE,
                 dwLimit = cMachineValues)
        {
            DWORD dwPhysical;

            for (dwPhysical = 0; dwPhysical < dwLimit; dwPhysical++) 
            {
                BOOL fNewValue;

                //
                // Ask the kernel for the value
                //
                Status = EnumerateValue(
                    hKeyPhysical,
                    dwPhysical,
                    ppValueInfo[dwLogical],
                    cbBufferLen,
                    NULL);

                //
                // If we encounter an error, just keep going and try to get
                // as many values as we can
                //
                if (!NT_SUCCESS(Status)) {
                    continue;
                }

                //
                // Mark certain attributes about this value that will
                // be important later
                //
                ppValueInfo[dwLogical]->TitleIndex = dwPhysical;
                ppValueInfo[dwLogical]->Type = fUser;
                

                //
                // This will add the value to our sorted list.  Since
                // the list is sorted, it is easy to eliminated duplicates --
                // don't add duplicates -- since we add 
                // user keys first, this allows us to give user values precedence
                // over machine values of the same name.  The logical key
                // index is also incremented if a key is added.
                //
                fNewValue = ValStateAddValueToSortedValues(
                    ppValueInfo,
                    dwLogical);

                if (fNewValue) {
                    dwLogical++;
                }
            }

            //
            // Break out of this loop if we just added the user values
            // since those are the last values we add
            //
            if (!fUser) {
                break;
            }
        }

        pValState->cValues = dwLogical;
    }

    //
    // Now copy the results back to the state's index array
    //
    {

        DWORD dwLogical;

        for (dwLogical = 0; dwLogical < pValState->cValues; dwLogical++)
        {
    
            pValState->rgIndex[dwLogical].dwOffset = 
                ppValueInfo[dwLogical]->TitleIndex;
            
            pValState->rgIndex[dwLogical].fUser =
                ppValueInfo[dwLogical]->Type;
        }
    }

    //
    // Release this
    //
    ValStateReleaseValues(
        ppValueInfo,
        cMaxValues);

    return STATUS_SUCCESS;
}


void ValStateReleaseValues(
    PKEY_VALUE_BASIC_INFORMATION* ppValueInfo,
    DWORD                         cMaxValues)
/*++

Routine Description:

    Releases resources associated with the values stored
        in the value state.

Arguments:

    pValState - value state containing values for a logical key

Return Value:

    None.

Notes:

--*/
{
    DWORD dwValue;

    //
    // First, free each individual value
    //
    for (dwValue = 0; dwValue < cMaxValues; dwValue++) 
    {
        //
        // Free memory for this value
        //
        if (ppValueInfo[dwValue]) {
            RegClassHeapFree(ppValueInfo[dwValue]);
        }
    }
    
    //
    // Now free the array that held all the values
    //
    RegClassHeapFree(ppValueInfo);
}



NTSTATUS ValStateInitialize( 
    ValueState** ppValState,
    HKEY         hKey)
/*++

Routine Description:

    Initializes a value state 

Arguments:

    pValState - value state containing values for a logical key
    hKey - logical key whose state this value state will represent

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

--*/
{
    NTSTATUS    Status;
    ValueState* pValState;
    HKEY        hkUser;
    HKEY        hkMachine;

    //
    // Initialize conditionally freed resources
    //
    hkUser = NULL;
    hkMachine = NULL;

    pValState = NULL;

    //
    // Get the user and machine keys
    //
    Status = BaseRegGetUserAndMachineClass(
        NULL,
        hKey,
        MAXIMUM_ALLOWED,
        &hkMachine,
        &hkUser);

    if (NT_SUCCESS(Status)) {

        ASSERT(hkUser || hkMachine);

        //
        // We only need to create a state if there are
        // two keys -- if only one exists, we don't
        // need to do merging
        //
        if (!hkUser || !hkMachine) {
            *ppValState = NULL;
            
            return STATUS_SUCCESS;
        }

        //
        // Get memory for the value state
        //
        pValState = RegClassHeapAlloc( sizeof(*pValState) + 
                                   sizeof(DWORD) * DEFAULT_VALUESTATE_SUBKEY_ALLOC );

        //
        // Be sure to release acquired resources on failure
        //
        if (!pValState) {

            if (hkUser != hKey) {
                NtClose(hkUser);
            } else {
                NtClose(hkMachine);
            }

            return STATUS_NO_MEMORY;
        }

        RtlZeroMemory(pValState, sizeof(*pValState));

        pValState->hkUser = hkUser;
        pValState->hkMachine = hkMachine;
        pValState->hkLogical = hKey;
        pValState->fIgnoreResetOnRetry = TRUE;

        //
        // Now update the state to reflect the current registry
        //
        Status = ValStateUpdate(pValState);
    } 

    //
    // On success, set our out param
    //
    if (NT_SUCCESS(Status)) {
        *ppValState = pValState;
    } else {

        if (pValState) {
            ValStateRelease(pValState);
        }
    }

    return Status;

}


BOOL ValStateAddValueToSortedValues(
    PKEY_VALUE_BASIC_INFORMATION* ppValueInfo,
    LONG                          lNewValue)
/*++

Routine Description:

    Inserts a retrieved value into the sorted list
        of values in a value state

Arguments:

    pValState - value state containing values for a logical key
    lNewValue - index of newly added value in the sorted list --
        this value needs to be moved elsewhere in the list to maintain
        the sorted nature of the list

Return Value:

    TRUE if the state was added, FALSE if not.

Notes:

--*/
{
    PKEY_VALUE_BASIC_INFORMATION pNewValue;
    LONG                         lFinalSpot;
    LONG                         lCurrent;
    UNICODE_STRING               NewKeyName;

    lFinalSpot = 0;

    pNewValue = ppValueInfo[lNewValue];
            
    NewKeyName.Buffer = pNewValue->Name;
    NewKeyName.Length = (USHORT) pNewValue->NameLength;

    for (lCurrent = lNewValue - 1; lCurrent >= 0; lCurrent--) 
    {
        UNICODE_STRING               CurrentValueName;
        PKEY_VALUE_BASIC_INFORMATION pCurrentValue;
        LONG                         lCompareResult;

        pCurrentValue = ppValueInfo[lCurrent];

        CurrentValueName.Buffer = pCurrentValue->Name;
        CurrentValueName.Length = (USHORT) pCurrentValue->NameLength;

        lCompareResult = RtlCompareUnicodeString(
            &NewKeyName,
            &CurrentValueName,
            TRUE);

        if (lCompareResult < 0) {

            continue;

        } else if (0 == lCompareResult) {
            //
            // If it's a duplicate, don't add it
            //
            return FALSE;
            
        } else {

            lFinalSpot = lCurrent + 1;

            break;
        }
    }

    //
    // Now we know the final spot, add the value
    //
    
    //
    // Move everything up to make room for the new value
    //
    for (lCurrent = lNewValue - 1; lCurrent >= lFinalSpot; lCurrent--) 
    {
        //
        // Move the current value up one
        //
        ppValueInfo[lCurrent + 1] = ppValueInfo[lCurrent];
    }

    //
    // Copy the value to its final destination
    //
    ppValueInfo[lFinalSpot] = pNewValue;

    //
    // This means we've found no duplicate value
    // so we add it
    //
    return TRUE;
}


NTSTATUS KeyStateGetValueState(
    HKEY         hKey,
    ValueState** ppValState)
/*++

Routine Description:

    Gets the value state for a particular key

Arguments:

    hKey - key whose state we need to retrieve
    ppValState - out param pointing to a pointer to the
        retrieved state.

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

    ATENTION: Right now, this always creates a new state -- in the future,
    we may want to change this to be cached in a table to avoid reconstructing
    on every call.

--*/
{
    //
    // Now build the value state
    //
    return ValStateInitialize(
        ppValState,
        hKey);
}


NTSTATUS BaseRegGetClassKeyValueState(
    HKEY         hKey,
    DWORD        dwLogicalIndex,
    ValueState** ppValState)
/*++

Routine Description:

    Gets the value state for a particular key and optimizes
        it for a given index

Arguments:

    hKey - key whose state we need to retrieve
    dwLogicalIndex - hint that helps us to optimize the state for this
        index so the caller's use of the state is more efficient
    ppValState - out param pointing to a pointer to the
        retrieved state.

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

--*/
{
    NTSTATUS    Status;
    ValueState* pValState;

    //
    // First retrieve the state for this key
    // 
    Status = KeyStateGetValueState(hKey, &pValState);

    if (NT_SUCCESS(Status)) {

        //
        // Now map the logical index to a physical one
        //
        Status = ValStateSetPhysicalIndexFromLogical(pValState, dwLogicalIndex);

        if (!NT_SUCCESS(Status)) {
            ValStateRelease(pValState);
        } else {
            *ppValState = pValState;
        }

    }

    return Status;
}


NTSTATUS EnumerateValue(
    HKEY                            hKey,
    DWORD                           dwValue,
    PKEY_VALUE_BASIC_INFORMATION    pSuggestedBuffer,
    DWORD                           dwSuggestedBufferLength,
    PKEY_VALUE_BASIC_INFORMATION*   ppResult)
/*++

Routine Description:

    Retrieves a value for a physical key from the kernel

Arguments:

    hKey - physical key for which we're trying to read a value
    dwValue - physical index of value to read
    pSuggestedBuffer - basinc info buffer to use by default, may not be large enough
    dwSuggestedBufferLength - size of suggested buffer
    ppResult - pointer to result basic info -- may be allocated by this function if
        suggested buffer was insufficient, which means caller will have to free
        this if it is not the same as the suggested buffer

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

--*/
{
    NTSTATUS                        Status;
    PKEY_VALUE_BASIC_INFORMATION    pKeyValueInformation;        
    DWORD                           dwResultLength;

    pKeyValueInformation = pSuggestedBuffer;

    //
    // Query for the necessary information about the supplied value.
    //
    Status = NtEnumerateValueKey( hKey,
                                  dwValue,
                                  KeyValueBasicInformation,
                                  pKeyValueInformation,
                                  dwSuggestedBufferLength,
                                  &dwResultLength);
    //
    // A return value of STATUS_BUFFER_TOO_SMALL would mean that there
    // was not enough room for even the known (i.e. fixed length portion)
    // of the structure.
    //

    ASSERT( Status != STATUS_BUFFER_TOO_SMALL );

    if (ppResult && (STATUS_BUFFER_OVERFLOW == Status)) {

        pKeyValueInformation = (PKEY_VALUE_BASIC_INFORMATION) RegClassHeapAlloc(
            dwResultLength);

        if (!pKeyValueInformation) {
            return STATUS_NO_MEMORY;
        }

        //
        // Query for the necessary information about the supplied value.
        //
        Status = NtEnumerateValueKey( hKey,
                                      dwValue,
                                      KeyValueBasicInformation,
                                      pKeyValueInformation,
                                      dwResultLength,
                                      &dwResultLength);

        ASSERT( Status != STATUS_BUFFER_TOO_SMALL );

        if (!NT_SUCCESS(Status)) {
            RegClassHeapFree(pKeyValueInformation);
        }
    }

    if (NT_SUCCESS(Status) && ppResult) {
        *ppResult = pKeyValueInformation;
    }

    return Status;
}


NTSTATUS BaseRegQueryMultipleClassKeyValues(
    HKEY     hKey,
    PRVALENT val_list,
    DWORD    num_vals,
    LPSTR    lpvalueBuf,
    LPDWORD  ldwTotsize,
    PULONG   ldwRequiredLength)
/*++

Routine Description:

    Gets the value state for a particular key and optimizes
        it for a given index

Arguments:

    hKey - Supplies a handle to the open key. The value entries returned
           are contained in the key pointed to by this key handle. Any of
           the predefined reserved handles or a previously opened key handle
           may be used for hKey.

    val_list - Supplies a pointer to an array of RVALENT structures, one for
           each value to be queried.

    num_vals - Supplies the size in bytes of the val_list array.

    lpValueBuf - Returns the data for each value

    ldwTotsize - Supplies the length of lpValueBuf. Returns the number of bytes
                 written into lpValueBuf. 

    ldwRequiredLength - If lpValueBuf is not large enough to
                 contain all the data, returns the size of lpValueBuf required
                 to return all the requested data.

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

--*/
{
    NTSTATUS    Status;
    HKEY        hkUser;
    HKEY        hkMachine;
    HKEY        hkQuery;

    //
    // Initialize conditionally freed resources
    //
    hkUser = NULL;
    hkMachine = NULL;

    //
    // First, get both user and machine keys
    //
    Status = BaseRegGetUserAndMachineClass(
        NULL,
        hKey,
        MAXIMUM_ALLOWED,
        &hkMachine,
        &hkUser);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //
    // If we have both, we call a routine
    // to merge the values
    //
    if (hkMachine && hkUser) {

        Status = BaseRegQueryAndMergeValues(
            hkUser,
            hkMachine,
            val_list,
            num_vals,
            lpvalueBuf,
            ldwTotsize,
            ldwRequiredLength);

        goto cleanup;
    }

    //
    // We have only one key -- query the one with the 
    // highest precedence
    //
    hkQuery = hkUser ? hkUser : hkMachine;

    Status = NtQueryMultipleValueKey(hkQuery,
                                     (PKEY_VALUE_ENTRY)val_list,
                                     num_vals,
                                     lpvalueBuf,
                                     ldwTotsize,
                                     ldwRequiredLength);

cleanup:

    //
    // Close extra kernel object
    //
    if (hKey != hkUser) {
        NtClose(hkUser);
    } else {
        NtClose(hkMachine);
    }

    return Status;
}


NTSTATUS BaseRegQueryAndMergeValues(
    HKEY     hkUser,
    HKEY     hkMachine,
    PRVALENT val_list,
    DWORD    num_vals,
    LPSTR    lpvalueBuf,
    LPDWORD  ldwTotsize,
    PULONG   ldwRequiredLength)
/*++

Routine Description:

    Gets the value state for a particular key and optimizes
        it for a given index

Arguments:

    hkUser - user key to query for values
    hkMachine - machine key to query for values

    val_list - Supplies a pointer to an array of RVALENT structures, one for
           each value to be queried.

    num_vals - Supplies the size in bytes of the val_list array.

    lpValueBuf - Returns the data for each value

    ldwTotsize - Supplies the length of lpValueBuf. Returns the number of bytes
                 written into lpValueBuf. 

    ldwRequiredLength - If lpValueBuf is not large enough to
                 contain all the data, returns the size of lpValueBuf required
                 to return all the requested data.

Return Value:

    STATUS_SUCCESS for success, error code otherwise.

Notes:

    ATTENTION: this is non-atomic, unlike the regular RegQueryMultipleValues
    call.  In the future, implementing this in the kernel would make this
    atomic again. 

--*/
{
    NTSTATUS Status;
    DWORD    dwVal;
    BOOL     fOverflow;
    DWORD    dwBufferLength;
    DWORD    dwRequired;
    DWORD    dwKeyInfoLength;
    DWORD    dwBufferUsed;

    PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;

    //
    // Initialize locals
    //
    dwBufferLength = *ldwTotsize;
    dwRequired = 0;
    dwBufferUsed = 0;

    fOverflow = FALSE;

    //
    // Validate out params -- we assume that ldwTotsize and
    // ldwRequiredLength were given to us by winreg client,
    // so they should be safe to read / write from. lpValueBuf
    // comes from the caller of the win32 api, so we need to
    // validate it -- in previous versions of NT, this parameter
    // went straight to the kernel, which did the validation and
    // returned an error if it was pointing to inaccessible memory.
    // Since we're accessing it here in user mode, we need to do 
    // our own validation
    //
    if (IsBadWritePtr( lpvalueBuf, dwBufferLength)) 
    {
        return STATUS_ACCESS_VIOLATION;
    }
        
    //
    // First, we need to allocate enough memory to retrieve
    // all the values -- we can't just use lpvalueBuf 
    // because it doesn't include the overhead of the
    // KEY_VALUE_PARTIAL_INFORMATION structure.  If we allocate
    // for the size of lpvalueBuf + the structure overhead,
    // we will always have enough for our queries.
    //
    dwKeyInfoLength = sizeof(*pKeyInfo) * num_vals + *ldwTotsize;
    
    pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
        RegClassHeapAlloc( dwKeyInfoLength);

    if (!pKeyInfo) {
        return STATUS_NO_MEMORY;
    }

    //
    // For each value requested by the caller, try 
    // to retrieve it from user or machine
    //
    for (dwVal = 0; dwVal < num_vals; dwVal++)
    {
        DWORD dwResultLength;
        
        //
        // Round up the used and required lengths to a ULONG boundary --
        // this means that the required size returned to the caller of the win32
        // api can be an overestimation, as much as 3 bytes per requested value.
        // We could do some work to avoid this, but since the kernel returns a rounded
        // up value in dwResultLength, the kernel api is itself overestimating, although
        // it only overestimates by at most 3 bytes over all the values.  We could avoid
        // this by allocating enough memory ahead of time to query the largest value, either
        // as one large preallocation or continually allocating and reallocating, but this will
        // be slower and / or consume more memory
        //
        dwBufferUsed = (dwBufferUsed + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);
        dwRequired = (dwRequired + sizeof(ULONG)-1) & ~(sizeof(ULONG)-1);

        //
        // Query the user key first since it has highest precedence
        //
        Status = NtQueryValueKey(
            hkUser,
            val_list[dwVal].rv_valuename,
            KeyValuePartialInformation,
            pKeyInfo,
            dwKeyInfoLength,
            &dwResultLength);

        //
        // Check for errors -- if the value didn't exist, we'll look
        // in machine -- for buffer overflow, we'll proceed as if
        // this succeeded so that we can calculate the required
        // buffer size
        //
        if (!NT_SUCCESS(Status) && 
            (STATUS_BUFFER_OVERFLOW != Status)) {
            
            if (STATUS_OBJECT_NAME_NOT_FOUND != Status) {
                goto cleanup;
            }
            
            //
            // If there is no user value, query the machine key
            //
            Status = NtQueryValueKey(
                hkMachine,
                val_list[dwVal].rv_valuename,
                KeyValuePartialInformation,
                pKeyInfo,
                dwKeyInfoLength,
                &dwResultLength);
            
            //
            // Similar error handling to above -- if we don't have enough
            // buffer, pretend we've succeeded so we can calc the required size
            //
            if (!NT_SUCCESS(Status) &&
                (STATUS_BUFFER_OVERFLOW != Status)) {
                goto cleanup;
            }
        }
        
        ASSERT(NT_SUCCESS(Status) || (STATUS_BUFFER_OVERFLOW == Status));
        
        if (NT_SUCCESS(Status)) {
            dwResultLength = pKeyInfo->DataLength;
        }
        
        //
        // Check for buffer overflow
        //
        if ( ( (dwBufferUsed + pKeyInfo->DataLength) <= dwBufferLength) && !fOverflow) {
            
            ASSERT(NT_SUCCESS(Status));
            
            //
            // Copy the data to the fixed part of the client's structure
            //
            val_list[dwVal].rv_valuelen = dwResultLength;
            val_list[dwVal].rv_valueptr = dwRequired;
            val_list[dwVal].rv_type = pKeyInfo->Type;

            //
            // We didn't overflow, so we still have enough room to copy
            // the latest value
            //
            RtlCopyMemory(
                (BYTE*)lpvalueBuf + val_list[dwVal].rv_valueptr,
                &(pKeyInfo->Data),
                dwResultLength);
            
            dwBufferUsed += pKeyInfo->DataLength;
            
        } else {
            //
            // We're out of buffer -- set this flag to
            // signal this state
            //
            fOverflow = TRUE;            
        }
        
        //
        // Update our required length with the size
        // of the data from the current value
        //
        dwRequired += dwResultLength;
    }

    //
    // At this point, we've succeeded in the sense that
    // we've copied all the data or we couldn't due to
    // insufficient buffer but we were able to calculate
    // the required size
    //
    Status = STATUS_SUCCESS;

cleanup:

    //
    // Free the allocated memory
    // 
    RegClassHeapFree(pKeyInfo);

    //
    // If we succeeded, this means we've either copied
    // the data or overflowed and copied the size -- handle
    // both below
    //
    if (NT_SUCCESS(Status)) {

        //
        // Always set this so the caller knows how much
        // was copied or needs to be allocated
        //
        *ldwRequiredLength = dwRequired;
        
        //
        // Return the appropriate error if we overflowed
        //
        if (fOverflow) {
            return STATUS_BUFFER_OVERFLOW;
        }

        //
        // Setting this, although winreg client actually
        // ignores this quantity
        //
        *ldwTotsize = dwBufferUsed;
    }

    return Status;
}

#endif LOCAL