456 lines
12 KiB
C
456 lines
12 KiB
C
//
|
|
// REGQVAL.C
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995
|
|
//
|
|
// Implementation of RegQueryValue, RegQueryValueEx and supporting functions.
|
|
//
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
//
|
|
// RgLookupValueByName
|
|
// (BIGKEY aware)
|
|
//
|
|
// Searches for the value with the specified name and returns a pointer to its
|
|
// KEY_RECORD and VALUE_RECORD.
|
|
//
|
|
// This locks the datablock associated with the KEY_RECORD and VALUE_RECORD.
|
|
// This is always hKey->BigKeyLockedBlockIndex
|
|
// It is the callers responsibility to unlock the datablock.
|
|
//
|
|
int
|
|
INTERNAL
|
|
RgLookupValueByName(
|
|
HKEY hKey,
|
|
LPCSTR lpValueName,
|
|
LPKEY_RECORD FAR* lplpKeyRecord,
|
|
LPVALUE_RECORD FAR* lplpValueRecord
|
|
)
|
|
{
|
|
int ErrorCode;
|
|
HKEY hKeyExtent;
|
|
UINT Index;
|
|
LPSTR ExtentKeyName;
|
|
DWORD cbExtentKeyName;
|
|
|
|
hKey-> BigKeyLockedBlockIndex = hKey-> BlockIndex;
|
|
ErrorCode = RgLookupValueByNameStd(hKey, lpValueName, lplpKeyRecord, lplpValueRecord);
|
|
|
|
// If this is a big key and we couldn't find it in the first key extent, then
|
|
// try the remaining extents.
|
|
if (ErrorCode == ERROR_CANTREAD16_FILENOTFOUND32 && (hKey->Flags & KEYF_BIGKEYROOT)) {
|
|
|
|
if (IsNullPtr(ExtentKeyName = RgSmAllocMemory(MAXIMUM_SUB_KEY_LENGTH)))
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
Index = 0;
|
|
|
|
do {
|
|
cbExtentKeyName = MAXIMUM_SUB_KEY_LENGTH;
|
|
if (RgLookupKeyByIndex(hKey, Index++, ExtentKeyName, &cbExtentKeyName, LK_BIGKEYEXT) != ERROR_SUCCESS) {
|
|
ErrorCode = ERROR_CANTREAD16_FILENOTFOUND32;
|
|
goto lFreeKeyName;
|
|
}
|
|
|
|
if (RgLookupKey(hKey, ExtentKeyName, &hKeyExtent, LK_OPEN | LK_BIGKEYEXT) != ERROR_SUCCESS) {
|
|
ErrorCode = ERROR_CANTREAD16_FILENOTFOUND32;
|
|
goto lFreeKeyName;
|
|
}
|
|
|
|
hKey-> BigKeyLockedBlockIndex = hKeyExtent-> BlockIndex;
|
|
ErrorCode = RgLookupValueByNameStd(hKeyExtent, lpValueName,
|
|
lplpKeyRecord, lplpValueRecord);
|
|
|
|
RgDestroyKeyHandle(hKeyExtent);
|
|
|
|
} while (ErrorCode == ERROR_CANTREAD16_FILENOTFOUND32);
|
|
|
|
lFreeKeyName:
|
|
RgSmFreeMemory(ExtentKeyName);
|
|
}
|
|
|
|
return ErrorCode;
|
|
}
|
|
|
|
|
|
//
|
|
// RgLookupValueByNameStd
|
|
//
|
|
// Searches for the value with the specified name and returns a pointer to its
|
|
// KEY_RECORD and VALUE_RECORD.
|
|
//
|
|
// This locks the datablock associated with the KEY_RECORD and VALUE_RECORD.
|
|
// This is always hKey->BlockIndex.
|
|
// It is the callers responsibility to unlock the datablock.
|
|
//
|
|
|
|
int
|
|
INTERNAL
|
|
RgLookupValueByNameStd(
|
|
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;
|
|
|
|
// Should probably do more sanity checking on lpValueRecord
|
|
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 (IsDynDataKey(hKey)) {
|
|
|
|
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(lpType))
|
|
*lpType = 0; // assume unknown data type
|
|
|
|
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-> BigKeyLockedBlockIndex, 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;
|
|
|
|
}
|