//
//  REGQVAL.C
//
//  Copyright (C) Microsoft Corporation, 1995
//
//  Implementation of RegQueryValue, RegQueryValueEx and supporting functions.
//

#include "pch.h"

//
//  RgLookupValueByName
//
//  Searches for the value with the specified name and returns a pointer to its
//  KEY_RECORD and VALUE_RECORD.
//

int
INTERNAL
RgLookupValueByName(
                   HKEY hKey,
                   LPCSTR lpValueName,
                   LPKEY_RECORD FAR* lplpKeyRecord,
                   LPVALUE_RECORD FAR* lplpValueRecord
                   )
{

    int ErrorCode;
    LPKEY_RECORD lpKeyRecord;
    UINT ValueNameLength;
    LPVALUE_RECORD lpValueRecord;
    UINT ValuesRemaining;

    //  Handle Win95 registries that don't have a key record for the root key.
    if (IsNullBlockIndex(hKey-> BlockIndex))
        return ERROR_CANTREAD16_FILENOTFOUND32;

    if ((ErrorCode = RgLockKeyRecord(hKey-> lpFileInfo, hKey-> BlockIndex,
                                     hKey-> KeyRecordIndex, &lpKeyRecord)) == ERROR_SUCCESS) {

        ValueNameLength = (IsNullPtr(lpValueName) ? 0 : (UINT)
                           StrLen(lpValueName));

        lpValueRecord = (LPVALUE_RECORD) ((LPBYTE) &lpKeyRecord-> Name +
                                          lpKeyRecord-> NameLength + lpKeyRecord-> ClassLength);

        ValuesRemaining = lpKeyRecord-> ValueCount;

        while (ValuesRemaining) {

            if (lpValueRecord-> NameLength == ValueNameLength &&
                (ValueNameLength == 0 || RgStrCmpNI(lpValueName, lpValueRecord->
                                                    Name, ValueNameLength) == 0)) {
                *lplpKeyRecord = lpKeyRecord;
                *lplpValueRecord = lpValueRecord;
                return ERROR_SUCCESS;
            }

            lpValueRecord = (LPVALUE_RECORD) ((LPBYTE) &lpValueRecord->
                                              Name + lpValueRecord-> NameLength + lpValueRecord->
                                              DataLength);

            ValuesRemaining--;

        }

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

        ErrorCode = ERROR_CANTREAD16_FILENOTFOUND32;

    }

    return ErrorCode;

}

//
//  RgCopyFromValueRecord
//
//  Shared routine for RegQueryValue and RegEnumValue.  Copies the information
//  from the VALUE_RECORD to the user-provided buffers.  All parameters should
//  have already been validated.
//
//  Because all parameters have been validated, if lpData is valid, then
//  lpcbData MUST be valid.
//

int
INTERNAL
RgCopyFromValueRecord(
                     HKEY hKey,
                     LPVALUE_RECORD lpValueRecord,
                     LPSTR lpValueName,
                     LPDWORD lpcbValueName,
                     LPDWORD lpType,
                     LPBYTE lpData,
                     LPDWORD lpcbData
                     )
{

    int ErrorCode;
    UINT BytesToTransfer;
#ifdef WANT_DYNKEY_SUPPORT
    PINTERNAL_PROVIDER pProvider;
    PPVALUE pProviderValue;
    struct val_context ValueContext;
#endif

#ifdef WANT_DYNKEY_SUPPORT
    if (hKey-> PredefinedKeyIndex == INDEX_DYN_DATA) {

        pProvider = hKey-> pProvider;

        if (IsNullPtr(pProvider))
            return ERROR_CANTOPEN;

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

        if (!IsNullPtr(lpType))
            *lpType = pProviderValue-> pv_type;

        if (!(hKey-> Flags & KEYF_PROVIDERHASVALUELENGTH)) {

            BytesToTransfer = pProviderValue-> pv_valuelen;

            if (IsNullPtr(lpData))
                goto ValueDataNotNeeded;

            if (BytesToTransfer > *lpcbData) {
                *lpcbData = BytesToTransfer;
                return ERROR_MORE_DATA;
            }

            //	Win95 compatibility: now that we know the required number of
            //	bytes, validate the data buffer.
            if (IsBadHugeWritePtr(lpData, BytesToTransfer))
                return ERROR_INVALID_PARAMETER;

        }

        ValueContext.value_context = pProviderValue-> pv_value_context;

        if (!IsNullPtr(lpcbData)) {

            //  Zero *lpcbData, if we aren't actually copying any data back to
            //  the user's buffer.  This keeps some providers from stomping on
            //  lpData.
            if (IsNullPtr(lpData))
                *lpcbData = 0;

            if ((ErrorCode = (int) pProvider-> ipi_R0_1val(pProvider->
                                                           ipi_key_context, &ValueContext, 1, lpData, lpcbData, 0)) !=
                ERROR_SUCCESS) {

                //  Win95 compatibility: the old code ignored any errors if
                //  lpData is NULL.  The below ASSERT will verify that we aren't
                //  dropping errors.
                if (!IsNullPtr(lpData))
                    return ErrorCode;

                ASSERT((ErrorCode == ERROR_SUCCESS) || (ErrorCode ==
                                                        ERROR_MORE_DATA));

            }

        }

        goto CopyValueName;

    }
#endif

    if (!IsNullPtr(lpType))
        *lpType = lpValueRecord-> DataType;

    BytesToTransfer = lpValueRecord-> DataLength;

    //  The terminating null is not stored in the value record.
    if (lpValueRecord-> DataType == REG_SZ)
        BytesToTransfer++;

    //
    //  Win32 compatibilty: lpData must be filled in before lpValueName.  Word
    //  NT and Excel NT broke when we validated lpValueName and failed the call
    //  before filling in lpData which was valid.  Don't rearrange this code!
    //

    if (!IsNullPtr(lpData)) {

        ErrorCode = ERROR_SUCCESS;

        if (BytesToTransfer > *lpcbData) {
            *lpcbData = BytesToTransfer;
            return ERROR_MORE_DATA;
        }

        //  Win95 compatibility: now that we know the required number of bytes,
        //  validate the data buffer.
        else if (IsBadHugeWritePtr(lpData, BytesToTransfer))
            return ERROR_INVALID_PARAMETER;

        else {

            MoveMemory(lpData, &lpValueRecord-> Name + lpValueRecord->
                       NameLength, lpValueRecord-> DataLength);

            if (lpValueRecord-> DataType == REG_SZ)
                lpData[lpValueRecord-> DataLength] = '\0';

        }

    }

#ifdef WANT_DYNKEY_SUPPORT
    ValueDataNotNeeded:
#endif
    if (!IsNullPtr(lpcbData))
        *lpcbData = BytesToTransfer;

#ifdef WANT_DYNKEY_SUPPORT
    CopyValueName:
#endif
    if (!IsNullPtr(lpValueName)) {

        ErrorCode = ERROR_SUCCESS;

        if (*lpcbValueName <= lpValueRecord-> NameLength) {

            //  Although we will not touch the lpData buffer if it's too small
            //  to hold the value data, we will partially fill lpValueName if
            //  it's too small.
            ErrorCode = ERROR_MORE_DATA;

            if (*lpcbValueName == 0)
                return ErrorCode;

            BytesToTransfer = (UINT) *lpcbValueName - 1;

        }

        else
            BytesToTransfer = lpValueRecord-> NameLength;

        MoveMemory(lpValueName, &lpValueRecord-> Name, BytesToTransfer);
        lpValueName[BytesToTransfer] = '\0';

        //  Does not include terminating null.
        *lpcbValueName = BytesToTransfer;

        return ErrorCode;

    }

    return ERROR_SUCCESS;

}

//
//  VMMRegQueryValueEx
//
//  See Win32 documentation of RegQueryValueEx.
//

LONG
REGAPI
VMMRegQueryValueEx(
                  HKEY hKey,
                  LPCSTR lpValueName,
                  LPDWORD lpReserved,
                  LPDWORD lpType,
                  LPBYTE lpData,
                  LPDWORD lpcbData
                  )
{

    int ErrorCode;
    LPKEY_RECORD lpKeyRecord;
    LPVALUE_RECORD lpValueRecord;

    if (IsBadOptionalStringPtr(lpValueName, (UINT) -1))
        return ERROR_INVALID_PARAMETER;

    if (IsBadHugeOptionalWritePtr(lpType, sizeof(DWORD)))
        return ERROR_INVALID_PARAMETER;

    if (IsNullPtr(lpcbData)) {
        if (!IsNullPtr(lpData))
            return ERROR_INVALID_PARAMETER;
    }

    else {
        //  Win95 compatibility: don't validate lpData is of size *lpcbData.
        //  Instead of validating the entire buffer, we'll validate just the
        //  required buffer length in RgCopyFromValueRecord.
        if (IsBadHugeWritePtr(lpcbData, sizeof(DWORD)))
            return ERROR_INVALID_PARAMETER;
    }

    if (!RgLockRegistry())
        return ERROR_LOCK_FAILED;

    if ((ErrorCode = RgValidateAndConvertKeyHandle(&hKey)) == ERROR_SUCCESS) {

        if ((ErrorCode = RgLookupValueByName(hKey, lpValueName, &lpKeyRecord,
                                             &lpValueRecord)) == ERROR_SUCCESS) {

            ErrorCode = RgCopyFromValueRecord(hKey, lpValueRecord, NULL, NULL,
                                              lpType, lpData, lpcbData);
            RgUnlockDatablock(hKey-> lpFileInfo, hKey-> BlockIndex, FALSE);

        }

        else if (ErrorCode == ERROR_CANTREAD16_FILENOTFOUND32) {

            //
            //  Windows 95 compatibility problem.  If the "value
            //  record" didn't exist in Windows 3.1, then it acted like it was
            //  really a null byte REG_SZ string.  This should have only been
            //  done in RegQueryValue, but we're stuck with it now...
            //

            if (IsNullPtr(lpValueName) || *lpValueName == '\0') {

                if (!IsNullPtr(lpType))
                    *lpType = REG_SZ;

                if (!IsNullPtr(lpData) && *lpcbData > 0)
                    *lpData = 0;

                if (!IsNullPtr(lpcbData))
                    *lpcbData = sizeof(char);

                ErrorCode = ERROR_SUCCESS;

            }

        }

    }

    RgUnlockRegistry();

    return ErrorCode;

    UNREFERENCED_PARAMETER(lpReserved);

}

//
//  VMMRegQueryValue
//
//  See Win32 documentation of RegQueryValue.
//

LONG
REGAPI
VMMRegQueryValue(
                HKEY hKey,
                LPCSTR lpSubKey,
                LPBYTE lpData,
                LPDWORD lpcbData
                )
{

    LONG ErrorCode;
    HKEY hSubKey;

    if ((ErrorCode = RgCreateOrOpenKey(hKey, lpSubKey, &hSubKey, LK_OPEN)) ==
        ERROR_SUCCESS) {
        ErrorCode = VMMRegQueryValueEx(hSubKey, NULL, NULL, NULL, lpData,
                                       lpcbData);
        VMMRegCloseKey(hSubKey);
    }

    return ErrorCode;

}