Windows2000/private/windows/screg/winreg/server/regqval.c
2020-09-30 17:12:32 +02:00

263 lines
12 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
Regqval.c
Abstract:
This module contains the server side implementation for the Win32 Registry query value API. That is:
- BaseRegQueryValue
- BaseRegQueryMultipleValues
Author:
David J. Gilman (davegi) 27-Nov-1991
Notes:
See the Notes in Regkey.c.
--*/
#include <rpc.h>
#include "regrpc.h"
#include "localreg.h"
#include "regclass.h"
#include "regvcls.h"
#define DEFAULT_VALUE_SIZE 128
#define DEFAULT_VALUE_NAME_SIZE 64
error_status_t BaseRegQueryValue(IN HKEY hKey, IN PUNICODE_STRING lpValueName, OUT LPDWORD lpType OPTIONAL, OUT LPBYTE lpData OPTIONAL, OUT LPDWORD lpcbData OPTIONAL, IN OUT LPDWORD lpcbLen OPTIONAL)
/*++
Routine Description:
For an open key, given the ID of the value to query, return the type, and value.
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.
lpValueName - Supplies a pointer to the name of the value.
lpType - If present, supplies a pointer to variable to receive the type code of value entry.
lpData -If present, supplies a pointer to a buffer to receive the data of the value entry.
lpcbData - Supplies a pointer to a variable which on input contains the size of the buffer lpData points to. On output, the variable will
receive the number of bytes returned in lpData. It must be supplied if lpData is, it is ignored otherwise.
lpcbLen - Return the number of bytes to transmit to the client (used by RPC).
Return Value:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
NTSTATUS Status;
ULONG BufferLength;
KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass;
PVOID KeyValueInformation;
ULONG ResultLength;
HKEY hkQueryKey;
BYTE PrivateKeyValueInformation[ sizeof( KEY_VALUE_PARTIAL_INFORMATION ) + DEFAULT_VALUE_SIZE ];
#ifdef LOCAL
HKEY hkUserClasses;
HKEY hkMachineClasses;
hkUserClasses = NULL;
hkMachineClasses = NULL;
#endif LOCAL
hkQueryKey = hKey;
// If the client gave us a bogus size, patch it.
if ( ARGUMENT_PRESENT( lpcbData ) && !ARGUMENT_PRESENT( lpData ) ) {
*lpcbData = 0;
}
// Call out to Perflib if the HKEY is HKEY_PERFOMANCE_DATA.
if(( hKey == HKEY_PERFORMANCE_DATA ) || ( hKey == HKEY_PERFORMANCE_TEXT ) || ( hKey == HKEY_PERFORMANCE_NLSTEXT)) {
error_status_t Error;
// Impersonate the client.
RPC_IMPERSONATE_CLIENT( NULL );
Error = (error_status_t)PerfRegQueryValue (hKey, lpValueName, NULL, lpType, lpData, lpcbData, lpcbLen);
RPC_REVERT_TO_SELF();
return(Error);
}
ASSERT( IsPredefinedRegistryHandle( hKey ) == FALSE );
// Subtract the NULL from the Length. This was added on the client side so that RPC would transmit it.
if ( lpValueName->Length > 0 ) {
lpValueName->Length -= sizeof( UNICODE_NULL );
}
// First we assume that the information we want will fit on PrivateKeyValueInformattion
KeyValueInformationClass = ( ARGUMENT_PRESENT( lpcbData ))?KeyValuePartialInformation :KeyValueBasicInformation;
KeyValueInformation = PrivateKeyValueInformation;
BufferLength = sizeof( PrivateKeyValueInformation );
// Query for the necessary information about the supplied value. This may or may not include the data depending on lpcbData as determined above.
#ifdef LOCAL
if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
Status = BaseRegGetUserAndMachineClass(NULL, hKey, MAXIMUM_ALLOWED, &hkMachineClasses, &hkUserClasses);
if (!NT_SUCCESS(Status)) {
return (error_status_t) RtlNtStatusToDosError(Status);
}
}
if (hkUserClasses && hkMachineClasses) {
hkQueryKey = hkUserClasses;
}
for (;;) {
#endif
Status = NtQueryValueKey( hkQueryKey, lpValueName, KeyValueInformationClass, KeyValueInformation, BufferLength, &ResultLength);
#ifdef LOCAL
// If we don't have two classes keys to worry about, just continue as we normally would
if (!hkUserClasses || !hkMachineClasses) {
break;
}
// If we're using machine, then we don't want to repeat since machine is the last resort
if (hkQueryKey == hkMachineClasses) {
break;
}
// If the key doesn't exist in user, then let's try again in machine
if (STATUS_OBJECT_NAME_NOT_FOUND == Status) {
hkQueryKey = hkMachineClasses;
continue;
}
break;
}
#endif
// 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( ( Status == STATUS_BUFFER_OVERFLOW ) && ( !ARGUMENT_PRESENT( lpData ) ) ) {
// STATUS_BUFFER_OVERFLOW means that the API returned all the information in the fixed portion of the structure KEY_VALUE_BASIC_INFORMATION or KEY_VALUE_PARTIAL_INFORMATION, but not the value name or the value data.
// If KeyValueInformationClass is equal to KeyValueBasicInformation then the API would return the value name.
// But since we are not interested in the value name (it was supplied by the client), we can assume that the API succeeded.
// If KeyValueInformationClass is equal to KeyValuePartialInformation then the API would return the value data.
// But lpData == NULL means that the client is not interested on the value data, but just on its size. For this reason, we can also assume that the API succeeded.
Status = STATUS_SUCCESS;
}
if( Status == STATUS_BUFFER_OVERFLOW ) {
// The buffer defined in the stack wasn't big enough to hold the Value information.
// If the caller's buffer is big enough to hold the value data then allocate a new buffer, and call the NT API again.
if( ( ( KeyValueInformationClass == KeyValuePartialInformation ) && ( ARGUMENT_PRESENT( lpData ) ) && ( *lpcbData >= (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->DataLength))) {
BufferLength = ResultLength;
KeyValueInformation = RtlAllocateHeap( RtlProcessHeap( ), 0, BufferLength);
if( ! KeyValueInformation ) {
return ERROR_OUTOFMEMORY;// If the memory allocation fails, return a Registry error.
}
Status = NtQueryValueKey( hkQueryKey, lpValueName, KeyValueInformationClass, KeyValueInformation, BufferLength, &ResultLength);// Query for the necessary information about the supplied value.
}
}
#ifdef LOCAL
if (hkUserClasses && hkMachineClasses) {
if (hkUserClasses != hKey) {
NtClose(hkUserClasses);
} else {
NtClose(hkMachineClasses);
}
}
#endif // LOCAL
if( NT_SUCCESS( Status ) && ARGUMENT_PRESENT( lpData ) ) {
// If requested, copy the value data
if( *lpcbData >= (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->DataLength ) {
RtlMoveMemory( lpData, (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->Data, (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->DataLength);
} else {
Status = STATUS_BUFFER_OVERFLOW;
}
}
// Certain information is returned on success or in the case of NtQueryValueKey returning STATUS_BUFFER_OVERFLOW.
// This information is always available because we always pass the minimum size required for the NtQueryValueKey API.
if( NT_SUCCESS( Status ) || ( Status == STATUS_BUFFER_OVERFLOW ) ) {
if( KeyValueInformationClass == KeyValueBasicInformation ) {
if( ARGUMENT_PRESENT( lpType )) {// If requested, return the value type.
*lpType = (( PKEY_VALUE_BASIC_INFORMATION ) KeyValueInformation )->Type;
}
} else {
if( ARGUMENT_PRESENT( lpType )) {// If requested, return the value type.
*lpType = (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->Type;
}
*lpcbData = (( PKEY_VALUE_PARTIAL_INFORMATION ) KeyValueInformation )->DataLength;// Return the value data size
}
}
// Transmit all of the data back to the client.
if( ARGUMENT_PRESENT( lpcbLen ) ) {
if( NT_SUCCESS( Status ) && ARGUMENT_PRESENT( lpData ) ) {
*lpcbLen = (( PKEY_VALUE_PARTIAL_INFORMATION )KeyValueInformation )->DataLength;
} else {// The API failed, so make sure that no data is transmitted back to the client. This ensures that the client stub will not attempt to unmarshall data that doesn't exist.
*lpcbLen = 0;
}
}
if( KeyValueInformation != PrivateKeyValueInformation ) {// If memory was allocated, then free it
RtlFreeHeap( RtlProcessHeap( ), 0, KeyValueInformation );
}
return (error_status_t)RtlNtStatusToDosError( Status );
}
error_status_t BaseRegQueryMultipleValues(IN HKEY hKey, IN OUT PRVALENT val_list, IN DWORD num_vals, OUT LPSTR lpvalueBuf, IN OUT LPDWORD ldwTotsize)
/*++
Routine Description:
For an open key, atomically queries a set of values.
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.
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:
Returns ERROR_SUCCESS (0) for success; error-code for failure.
--*/
{
NTSTATUS Status;
ULONG RequiredLength;
ULONG i;
// Call out to Perflib if the HKEY is HKEY_PERFOMANCE_DATA.
if(( hKey == HKEY_PERFORMANCE_DATA ) || ( hKey == HKEY_PERFORMANCE_TEXT ) || ( hKey == HKEY_PERFORMANCE_NLSTEXT)) {
return((error_status_t)ERROR_CALL_NOT_IMPLEMENTED);
}
// Subtract the NULLs from the Length. This was added on the client side so that RPC would transmit it.
for (i=0; i<num_vals; i++) {
if (val_list[i].rv_valuename->Length > 0) {
val_list[i].rv_valuename->Length -= sizeof( UNICODE_NULL );
}
}
#ifdef LOCAL
// For class keys in hkcr, we need to merge the data
if (REG_CLASS_IS_SPECIAL_KEY(hKey)) {
Status = BaseRegQueryMultipleClassKeyValues(hKey, val_list, num_vals, lpvalueBuf, ldwTotsize, &RequiredLength);
} else {
#endif // LOCAL
Status = NtQueryMultipleValueKey(hKey, (PKEY_VALUE_ENTRY)val_list, num_vals, lpvalueBuf, ldwTotsize, &RequiredLength);
#ifdef LOCAL
}
#endif // LOCAL
if (Status == STATUS_BUFFER_OVERFLOW) {
*ldwTotsize = RequiredLength;
} else if (!NT_SUCCESS(Status)) {
// The API failed, so make sure that no data is transmitted back to the client.
// This ensures that the client stub will not attempt to unmarshall data that doesn't exist.
*ldwTotsize = 0;
}
return(error_status_t)RtlNtStatusToDosError(Status);
}