1207 lines
28 KiB
C
1207 lines
28 KiB
C
/*++
|
|
|
|
|
|
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:
|
|
|
|
BUGBUG: 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:
|
|
|
|
BUGBUG: 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
|
|
|
|
|
|
|
|
|
|
|