reactos/sdk/lib/rtl/registry.c
Eric Kohl 0f8e720711 [NTDLL_APITEST][LIB_RTL] Add a test for RtlQueryTimeZoneInformation and fix the bug in RtlpQueryRegistryDirect
In working on some problems with Time Zone adjustments in  ReactOS I found that the RtlQueryTimeZoneInformation function is partially broken and wrote an apitest to capture the failures.

Patch by Doug Lyons.

CORE-14658
2018-05-31 16:47:42 +02:00

1349 lines
44 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
* PURPOSE: Rtl registry functions
* FILE: lib/rtl/registry.c
* PROGRAMER: Alex Ionescu (alex.ionescu@reactos.org)
* Eric Kohl
*/
/* INCLUDES *****************************************************************/
#include <rtl.h>
#include <ndk/cmfuncs.h>
#define NDEBUG
#include <debug.h>
#define TAG_RTLREGISTRY 'vrqR'
extern SIZE_T RtlpAllocDeallocQueryBufferSize;
/* DATA **********************************************************************/
PCWSTR RtlpRegPaths[RTL_REGISTRY_MAXIMUM] =
{
NULL,
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services",
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control",
L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
L"\\Registry\\Machine\\Hardware\\DeviceMap",
L"\\Registry\\User\\.Default",
};
/* PRIVATE FUNCTIONS *********************************************************/
NTSTATUS
NTAPI
RtlpQueryRegistryDirect(IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Buffer)
{
USHORT ActualLength;
PUNICODE_STRING ReturnString = Buffer;
PULONG Length = Buffer;
ULONG RealLength;
/* Check if this is a string */
if ((ValueType == REG_SZ) ||
(ValueType == REG_EXPAND_SZ) ||
(ValueType == REG_MULTI_SZ))
{
/* Normalize the length */
if (ValueLength > MAXUSHORT)
ActualLength = MAXUSHORT;
else
ActualLength = (USHORT)ValueLength;
/* Check if the return string has been allocated */
if (!ReturnString->Buffer)
{
/* Allocate it */
ReturnString->Buffer = RtlpAllocateMemory(ActualLength, TAG_RTLREGISTRY);
if (!ReturnString->Buffer) return STATUS_NO_MEMORY;
ReturnString->MaximumLength = ActualLength;
}
else if (ActualLength > ReturnString->MaximumLength)
{
/* The string the caller allocated is too small */
return STATUS_BUFFER_TOO_SMALL;
}
/* Copy the data */
RtlCopyMemory(ReturnString->Buffer, ValueData, ActualLength);
ReturnString->Length = ActualLength - sizeof(UNICODE_NULL);
}
else if (ValueLength <= sizeof(ULONG))
{
/* Check if we can just copy the data */
if ((Buffer != ValueData) && (ValueLength))
{
/* Copy it */
RtlCopyMemory(Buffer, ValueData, ValueLength);
}
}
else
{
/* Check if the length is negative */
if ((LONG)*Length < 0)
{
/* Get the real length and copy the buffer */
RealLength = -(LONG)*Length;
if (RealLength < ValueLength) return STATUS_BUFFER_TOO_SMALL;
RtlCopyMemory(Buffer, ValueData, ValueLength);
}
else
{
if (ValueType != REG_BINARY)
{
/* Check if there's space for the length and type, plus data */
if (*Length < (2 * sizeof(ULONG) + ValueLength))
{
/* Nope, fail */
return STATUS_BUFFER_TOO_SMALL;
}
/* Return the data */
*Length++ = ValueLength;
*Length++ = ValueType;
RtlCopyMemory(Length, ValueData, ValueLength);
}
else
{
/* Return the REG_BINARY data */
RtlCopyMemory(Length, ValueData, ValueLength);
}
}
}
/* All done */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
RtlpCallQueryRegistryRoutine(IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
IN PKEY_VALUE_FULL_INFORMATION KeyValueInfo,
IN OUT PULONG InfoSize,
IN PVOID Context,
IN PVOID Environment)
{
ULONG InfoLength;
SIZE_T Length, SpareLength, c;
ULONG RequiredLength;
PCHAR SpareData, DataEnd;
ULONG Type;
PWCHAR Name, p, ValueEnd;
PVOID Data;
NTSTATUS Status;
BOOLEAN FoundExpander = FALSE;
UNICODE_STRING Source, Destination;
/* Setup defaults */
InfoLength = *InfoSize;
*InfoSize = 0;
/* Check if there's no data */
if (KeyValueInfo->DataOffset == MAXULONG)
{
/* Return proper status code */
return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
}
/* Setup spare data pointers */
SpareData = (PCHAR)KeyValueInfo;
SpareLength = InfoLength;
DataEnd = SpareData + SpareLength;
/* Check if there's no value or data */
if ((KeyValueInfo->Type == REG_NONE) ||
(!(KeyValueInfo->DataLength) &&
(KeyValueInfo->Type == QueryTable->DefaultType)))
{
/* Check if there's no value */
if (QueryTable->DefaultType == REG_NONE)
{
/* Return proper status code */
return (QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED) ?
STATUS_OBJECT_NAME_NOT_FOUND : STATUS_SUCCESS;
}
/* We can setup a default value... capture the defaults */
Name = (PWCHAR)QueryTable->Name;
Type = QueryTable->DefaultType;
Data = QueryTable->DefaultData;
Length = QueryTable->DefaultLength;
if (!Length)
{
/* No default length given, try to calculate it */
p = Data;
if ((Type == REG_SZ) || (Type == REG_EXPAND_SZ))
{
/* This is a string, count the characters */
while (*p++);
Length = (ULONG_PTR)p - (ULONG_PTR)Data;
}
else if (Type == REG_MULTI_SZ)
{
/* This is a multi-string, calculate all characters */
while (*p) while (*p++);
Length = (ULONG_PTR)p - (ULONG_PTR)Data + sizeof(UNICODE_NULL);
}
}
}
else
{
/* Check if this isn't a direct return */
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT))
{
/* Check if we have length */
if (KeyValueInfo->DataLength)
{
/* Increase the spare data */
SpareData += KeyValueInfo->DataOffset +
KeyValueInfo->DataLength;
}
else
{
/* Otherwise, the spare data only has the name data */
SpareData += FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name) +
KeyValueInfo->NameLength;
}
/* Align the pointer and get new size of spare data */
SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
SpareLength = DataEnd - SpareData;
/* Check if we have space to copy the data */
RequiredLength = KeyValueInfo->NameLength + sizeof(UNICODE_NULL);
if ((SpareData > DataEnd) || (SpareLength < RequiredLength))
{
/* Fail and return the missing length */
*InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) + RequiredLength;
return STATUS_BUFFER_TOO_SMALL;
}
/* Copy the data and null-terminate it */
Name = (PWCHAR)SpareData;
RtlCopyMemory(Name, KeyValueInfo->Name, KeyValueInfo->NameLength);
Name[KeyValueInfo->NameLength / sizeof(WCHAR)] = UNICODE_NULL;
/* Update the spare data information */
SpareData += RequiredLength;
SpareData = (PVOID)(((ULONG_PTR)SpareData + 7) & ~7);
SpareLength = DataEnd - SpareData;
}
else
{
/* Just return the name */
Name = (PWCHAR)QueryTable->Name;
}
/* Capture key data */
Type = KeyValueInfo->Type;
Data = (PVOID)((ULONG_PTR)KeyValueInfo + KeyValueInfo->DataOffset);
Length = KeyValueInfo->DataLength;
}
/* Check if we're expanding */
if (!(QueryTable->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
{
/* Check if it's a multi-string */
if (Type == REG_MULTI_SZ)
{
/* Prepare defaults */
Status = STATUS_SUCCESS;
/* Skip the last two UNICODE_NULL chars (the terminating null string) */
ValueEnd = (PWSTR)((ULONG_PTR)Data + Length - 2 * sizeof(UNICODE_NULL));
p = Data;
/* Loop all strings */
while (p < ValueEnd)
{
/* Go to the next string */
while (*p++);
/* Get the length and check if this is direct */
Length = (ULONG_PTR)p - (ULONG_PTR)Data;
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
{
/* Do the query */
Status = RtlpQueryRegistryDirect(REG_SZ,
Data,
(ULONG)Length,
QueryTable->EntryContext);
QueryTable->EntryContext =
(PVOID)((ULONG_PTR)QueryTable->EntryContext +
sizeof(UNICODE_STRING));
}
else
{
/* Call the custom routine */
Status = QueryTable->QueryRoutine(Name,
REG_SZ,
Data,
(ULONG)Length,
Context,
QueryTable->EntryContext);
}
/* Normalize status */
if (Status == STATUS_BUFFER_TOO_SMALL) Status = STATUS_SUCCESS;
if (!NT_SUCCESS(Status)) break;
/* Update data pointer */
Data = p;
}
/* Return */
return Status;
}
/* Check if this is an expand string */
if ((Type == REG_EXPAND_SZ) && (Length >= sizeof(WCHAR)))
{
/* Try to find the expander */
c = Length - sizeof(UNICODE_NULL);
p = (PWCHAR)Data;
while (c)
{
/* Check if this is one */
if (*p == L'%')
{
/* Yup! */
FoundExpander = TRUE;
break;
}
/* Continue in the buffer */
p++;
c -= sizeof(WCHAR);
}
/* So check if we have one */
if (FoundExpander)
{
/* Setup the source string */
RtlInitEmptyUnicodeString(&Source, Data, (USHORT)Length);
Source.Length = Source.MaximumLength - sizeof(UNICODE_NULL);
/* Setup the desination string */
RtlInitEmptyUnicodeString(&Destination, (PWCHAR)SpareData, 0);
/* Check if we're out of space */
if (SpareLength <= 0)
{
/* Then we don't have any space in our string */
Destination.MaximumLength = 0;
}
else if (SpareLength <= MAXUSHORT)
{
/* This is the good case, where we fit into a string */
Destination.MaximumLength = (USHORT)SpareLength;
Destination.Buffer[SpareLength / sizeof(WCHAR) - 1] = UNICODE_NULL;
}
else
{
/* We can't fit into a string, so truncate */
Destination.MaximumLength = MAXUSHORT;
Destination.Buffer[MAXUSHORT / sizeof(WCHAR) - 1] = UNICODE_NULL;
}
/* Expand the strings and set our type as one string */
Status = RtlExpandEnvironmentStrings_U(Environment,
&Source,
&Destination,
&RequiredLength);
Type = REG_SZ;
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Set the value name and length to our string */
Data = Destination.Buffer;
Length = Destination.Length + sizeof(UNICODE_NULL);
}
else
{
/* Check if our buffer is too small */
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Set the required missing length */
*InfoSize = (ULONG)(SpareData - (PCHAR)KeyValueInfo) +
RequiredLength;
/* Notify debugger */
DPRINT1("RTL: Expand variables for %wZ failed - "
"Status == %lx Size %x > %x <%x>\n",
&Source,
Status,
*InfoSize,
InfoLength,
Destination.MaximumLength);
}
else
{
/* Notify debugger */
DPRINT1("RTL: Expand variables for %wZ failed - "
"Status == %lx\n",
&Source,
Status);
}
/* Return the status */
return Status;
}
}
}
}
/* Check if this is a direct query */
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT)
{
/* Return the data */
Status = RtlpQueryRegistryDirect(Type,
Data,
(ULONG)Length,
QueryTable->EntryContext);
}
else
{
/* Call the query routine */
Status = QueryTable->QueryRoutine(Name,
Type,
Data,
(ULONG)Length,
Context,
QueryTable->EntryContext);
}
/* Normalize and return status */
return (Status == STATUS_BUFFER_TOO_SMALL) ? STATUS_SUCCESS : Status;
}
_Success_(return!=NULL || BufferSize==0)
_When_(BufferSize!=NULL,__drv_allocatesMem(Mem))
PVOID
NTAPI
RtlpAllocDeallocQueryBuffer(
_In_opt_ PSIZE_T BufferSize,
_In_opt_ __drv_freesMem(Mem) PVOID OldBuffer,
_In_ SIZE_T OldBufferSize,
_Out_opt_ _On_failure_(_Post_satisfies_(*Status < 0)) PNTSTATUS Status)
{
PVOID Buffer = NULL;
/* Assume success */
if (Status) *Status = STATUS_SUCCESS;
/* Free the old buffer */
if (OldBuffer) RtlpFreeMemory(OldBuffer, TAG_RTLREGISTRY);
/* Check if we need to allocate a new one */
if (BufferSize)
{
/* Allocate */
Buffer = RtlpAllocateMemory(*BufferSize, TAG_RTLREGISTRY);
if (!(Buffer) && (Status)) *Status = STATUS_NO_MEMORY;
}
/* Return the pointer */
return Buffer;
}
NTSTATUS
NTAPI
RtlpGetRegistryHandle(IN ULONG RelativeTo,
IN PCWSTR Path,
IN BOOLEAN Create,
IN PHANDLE KeyHandle)
{
UNICODE_STRING KeyPath, KeyName;
WCHAR KeyBuffer[MAX_PATH];
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
/* Check if we just want the handle */
if (RelativeTo & RTL_REGISTRY_HANDLE)
{
*KeyHandle = (HANDLE)Path;
return STATUS_SUCCESS;
}
/* Check for optional flag */
if (RelativeTo & RTL_REGISTRY_OPTIONAL)
{
/* Mask it out */
RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
}
/* Fail on invalid parameter */
if (RelativeTo >= RTL_REGISTRY_MAXIMUM) return STATUS_INVALID_PARAMETER;
/* Initialize the key name */
RtlInitEmptyUnicodeString(&KeyName, KeyBuffer, sizeof(KeyBuffer));
/* Check if we have to lookup a path to prefix */
if (RelativeTo != RTL_REGISTRY_ABSOLUTE)
{
/* Check if we need the current user key */
if (RelativeTo == RTL_REGISTRY_USER)
{
/* Get the user key path */
Status = RtlFormatCurrentUserKeyPath(&KeyPath);
/* Check if it worked */
if (NT_SUCCESS(Status))
{
/* Append the user key path */
Status = RtlAppendUnicodeStringToString(&KeyName, &KeyPath);
/* Free the user key path */
RtlFreeUnicodeString (&KeyPath);
}
else
{
/* It didn't work so fall back to the default user key */
Status = RtlAppendUnicodeToString(&KeyName, RtlpRegPaths[RTL_REGISTRY_USER]);
}
}
else
{
/* Get one of the prefixes */
Status = RtlAppendUnicodeToString(&KeyName,
RtlpRegPaths[RelativeTo]);
}
/* Check for failure, otherwise, append the path separator */
if (!NT_SUCCESS(Status)) return Status;
Status = RtlAppendUnicodeToString(&KeyName, L"\\");
if (!NT_SUCCESS(Status)) return Status;
}
/* And now append the path */
if (Path[0] == L'\\' && RelativeTo != RTL_REGISTRY_ABSOLUTE) Path++; // HACK!
Status = RtlAppendUnicodeToString(&KeyName, Path);
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the object attributes */
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
/* Check if we want to create it */
if (Create)
{
/* Create the key with write privileges */
Status = ZwCreateKey(KeyHandle,
GENERIC_WRITE,
&ObjectAttributes,
0,
NULL,
0,
NULL);
}
else
{
/* Otherwise, just open it with read access */
Status = ZwOpenKey(KeyHandle,
MAXIMUM_ALLOWED | GENERIC_READ,
&ObjectAttributes);
}
/* Return status */
return Status;
}
FORCEINLINE
VOID
RtlpCloseRegistryHandle(
_In_ ULONG RelativeTo,
_In_ HANDLE KeyHandle)
{
/* Did the caller pass a key handle? */
if (!(RelativeTo & RTL_REGISTRY_HANDLE))
{
/* We opened the key in RtlpGetRegistryHandle, so close it now */
ZwClose(KeyHandle);
}
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlCheckRegistryKey(IN ULONG RelativeTo,
IN PWSTR Path)
{
HANDLE KeyHandle;
NTSTATUS Status;
PAGED_CODE_RTL();
/* Call the helper */
Status = RtlpGetRegistryHandle(RelativeTo,
Path,
FALSE,
&KeyHandle);
if (!NT_SUCCESS(Status)) return Status;
/* Close the handle even for RTL_REGISTRY_HANDLE */
ZwClose(KeyHandle);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlCreateRegistryKey(IN ULONG RelativeTo,
IN PWSTR Path)
{
HANDLE KeyHandle;
NTSTATUS Status;
PAGED_CODE_RTL();
/* Call the helper */
Status = RtlpGetRegistryHandle(RelativeTo,
Path,
TRUE,
&KeyHandle);
if (!NT_SUCCESS(Status)) return Status;
/* All went well, close the handle and return status */
RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlDeleteRegistryValue(IN ULONG RelativeTo,
IN PCWSTR Path,
IN PCWSTR ValueName)
{
HANDLE KeyHandle;
NTSTATUS Status;
UNICODE_STRING Name;
PAGED_CODE_RTL();
/* Call the helper */
Status = RtlpGetRegistryHandle(RelativeTo,
Path,
TRUE,
&KeyHandle);
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the key name and delete it */
RtlInitUnicodeString(&Name, ValueName);
Status = ZwDeleteValueKey(KeyHandle, &Name);
/* Close the handle and return status */
RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlWriteRegistryValue(IN ULONG RelativeTo,
IN PCWSTR Path,
IN PCWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength)
{
HANDLE KeyHandle;
NTSTATUS Status;
UNICODE_STRING Name;
PAGED_CODE_RTL();
/* Call the helper */
Status = RtlpGetRegistryHandle(RelativeTo,
Path,
TRUE,
&KeyHandle);
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the key name and set it */
RtlInitUnicodeString(&Name, ValueName);
Status = ZwSetValueKey(KeyHandle,
&Name,
0,
ValueType,
ValueData,
ValueLength);
/* Close the handle and return status */
RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
OUT PHANDLE KeyHandle)
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING KeyPath;
NTSTATUS Status;
PAGED_CODE_RTL();
/* Get the user key */
Status = RtlFormatCurrentUserKeyPath(&KeyPath);
if (NT_SUCCESS(Status))
{
/* Initialize the attributes and open it */
InitializeObjectAttributes(&ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
/* Free the path and return success if it worked */
RtlFreeUnicodeString(&KeyPath);
if (NT_SUCCESS(Status)) return STATUS_SUCCESS;
}
/* It didn't work, so use the default key */
RtlInitUnicodeString(&KeyPath, RtlpRegPaths[RTL_REGISTRY_USER]);
InitializeObjectAttributes(&ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
/* Return status */
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlFormatCurrentUserKeyPath(OUT PUNICODE_STRING KeyPath)
{
HANDLE TokenHandle;
UCHAR Buffer[256];
PSID_AND_ATTRIBUTES SidBuffer;
ULONG Length;
UNICODE_STRING SidString;
NTSTATUS Status;
PAGED_CODE_RTL();
/* Open the thread token */
Status = ZwOpenThreadTokenEx(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
OBJ_KERNEL_HANDLE,
&TokenHandle);
if (!NT_SUCCESS(Status))
{
/* We failed, is it because we don't have a thread token? */
if (Status != STATUS_NO_TOKEN) return Status;
/* It is, so use the process token */
Status = ZwOpenProcessTokenEx(NtCurrentProcess(),
TOKEN_QUERY,
OBJ_KERNEL_HANDLE,
&TokenHandle);
if (!NT_SUCCESS(Status)) return Status;
}
/* Now query the token information */
SidBuffer = (PSID_AND_ATTRIBUTES)Buffer;
Status = ZwQueryInformationToken(TokenHandle,
TokenUser,
(PVOID)SidBuffer,
sizeof(Buffer),
&Length);
/* Close the handle and handle failure */
ZwClose(TokenHandle);
if (!NT_SUCCESS(Status)) return Status;
/* Convert the SID */
Status = RtlConvertSidToUnicodeString(&SidString, SidBuffer[0].Sid, TRUE);
if (!NT_SUCCESS(Status)) return Status;
/* Add the length of the prefix */
Length = SidString.Length + sizeof(L"\\REGISTRY\\USER\\");
/* Initialize a string */
RtlInitEmptyUnicodeString(KeyPath,
RtlpAllocateStringMemory(Length, TAG_USTR),
(USHORT)Length);
if (!KeyPath->Buffer)
{
/* Free the string and fail */
RtlFreeUnicodeString(&SidString);
return STATUS_NO_MEMORY;
}
/* Append the prefix and SID */
RtlAppendUnicodeToString(KeyPath, L"\\REGISTRY\\USER\\");
RtlAppendUnicodeStringToString(KeyPath, &SidString);
/* Free the temporary string and return success */
RtlFreeUnicodeString(&SidString);
return STATUS_SUCCESS;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtCreateKey(OUT HANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG TitleIndex,
IN PUNICODE_STRING Class,
OUT PULONG Disposition)
{
/* Check if we have object attributes */
if (ObjectAttributes)
{
/* Mask out the unsupported flags */
ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
}
/* Create the key */
return ZwCreateKey(KeyHandle,
DesiredAccess,
ObjectAttributes,
0,
NULL,
0,
Disposition);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtEnumerateSubKey(IN HANDLE KeyHandle,
OUT PUNICODE_STRING SubKeyName,
IN ULONG Index,
IN ULONG Unused)
{
PKEY_BASIC_INFORMATION KeyInfo = NULL;
ULONG BufferLength = 0;
ULONG ReturnedLength;
NTSTATUS Status;
/* Check if we have a name */
if (SubKeyName->MaximumLength)
{
/* Allocate a buffer for it */
BufferLength = SubKeyName->MaximumLength +
sizeof(KEY_BASIC_INFORMATION);
KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
if (!KeyInfo) return STATUS_NO_MEMORY;
}
/* Enumerate the key */
Status = ZwEnumerateKey(KeyHandle,
Index,
KeyBasicInformation,
KeyInfo,
BufferLength,
&ReturnedLength);
if (NT_SUCCESS(Status) && (KeyInfo != NULL))
{
/* Check if the name fits */
if (KeyInfo->NameLength <= SubKeyName->MaximumLength)
{
/* Set the length */
SubKeyName->Length = (USHORT)KeyInfo->NameLength;
/* Copy it */
RtlMoveMemory(SubKeyName->Buffer,
KeyInfo->Name,
SubKeyName->Length);
}
else
{
/* Otherwise, we ran out of buffer space */
Status = STATUS_BUFFER_OVERFLOW;
}
}
/* Free the buffer and return status */
if (KeyInfo) RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtMakeTemporaryKey(IN HANDLE KeyHandle)
{
/* This just deletes the key */
return ZwDeleteKey(KeyHandle);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtOpenKey(OUT HANDLE KeyHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN ULONG Unused)
{
/* Check if we have object attributes */
if (ObjectAttributes)
{
/* Mask out the unsupported flags */
ObjectAttributes->Attributes &= ~(OBJ_PERMANENT | OBJ_EXCLUSIVE);
}
/* Open the key */
return ZwOpenKey(KeyHandle, DesiredAccess, ObjectAttributes);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtQueryValueKey(IN HANDLE KeyHandle,
OUT PULONG Type OPTIONAL,
OUT PVOID Data OPTIONAL,
IN OUT PULONG DataLength OPTIONAL,
IN ULONG Unused)
{
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
UNICODE_STRING ValueName;
ULONG BufferLength = 0;
NTSTATUS Status;
/* Clear the value name */
RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
/* Check if we were already given a length */
if (DataLength) BufferLength = *DataLength;
/* Add the size of the structure */
BufferLength += FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data);
/* Allocate memory for the value */
ValueInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferLength);
if (!ValueInfo) return STATUS_NO_MEMORY;
/* Query the value */
Status = ZwQueryValueKey(KeyHandle,
&ValueName,
KeyValuePartialInformation,
ValueInfo,
BufferLength,
&BufferLength);
if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_OVERFLOW))
{
/* Return the length and type */
if (DataLength) *DataLength = ValueInfo->DataLength;
if (Type) *Type = ValueInfo->Type;
}
/* Check if the caller wanted data back, and we got it */
if ((NT_SUCCESS(Status)) && (Data))
{
/* Copy it */
RtlMoveMemory(Data, ValueInfo->Data, ValueInfo->DataLength);
}
/* Free the memory and return status */
RtlFreeHeap(RtlGetProcessHeap(), 0, ValueInfo);
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlpNtSetValueKey(IN HANDLE KeyHandle,
IN ULONG Type,
IN PVOID Data,
IN ULONG DataLength)
{
UNICODE_STRING ValueName;
/* Set the value */
RtlInitEmptyUnicodeString(&ValueName, NULL, 0);
return ZwSetValueKey(KeyHandle,
&ValueName,
0,
Type,
Data,
DataLength);
}
/*
* @implemented
*/
NTSTATUS
NTAPI
RtlQueryRegistryValues(IN ULONG RelativeTo,
IN PCWSTR Path,
IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
IN PVOID Context,
IN PVOID Environment OPTIONAL)
{
NTSTATUS Status;
PKEY_VALUE_FULL_INFORMATION KeyValueInfo = NULL;
HANDLE KeyHandle, CurrentKey;
SIZE_T BufferSize, InfoSize;
UNICODE_STRING KeyPath, KeyValueName;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG i, Value;
ULONG ResultLength;
/* Get the registry handle */
Status = RtlpGetRegistryHandle(RelativeTo, Path, FALSE, &KeyHandle);
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the path */
RtlInitUnicodeString(&KeyPath,
(RelativeTo & RTL_REGISTRY_HANDLE) ? NULL : Path);
/* Allocate a query buffer */
BufferSize = RtlpAllocDeallocQueryBufferSize;
KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize, NULL, 0, &Status);
if (!KeyValueInfo)
{
/* Close the handle if we have one and fail */
RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
return Status;
}
/* Set defaults */
KeyValueInfo->DataOffset = 0;
InfoSize = BufferSize - sizeof(UNICODE_NULL);
CurrentKey = KeyHandle;
/* Loop the query table */
while ((QueryTable->QueryRoutine) ||
(QueryTable->Flags & (RTL_QUERY_REGISTRY_SUBKEY |
RTL_QUERY_REGISTRY_DIRECT)))
{
/* Check if the request is invalid */
if ((QueryTable->Flags & RTL_QUERY_REGISTRY_DIRECT) &&
(!(QueryTable->Name) ||
(QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY) ||
(QueryTable->QueryRoutine)))
{
/* Fail */
Status = STATUS_INVALID_PARAMETER;
break;
}
/* Check if we want a specific key */
if (QueryTable->Flags & (RTL_QUERY_REGISTRY_TOPKEY |
RTL_QUERY_REGISTRY_SUBKEY))
{
/* Check if we're working with another handle */
if (CurrentKey != KeyHandle)
{
/* Close our current key and use the top */
NtClose(CurrentKey);
CurrentKey = KeyHandle;
}
}
/* Check if we're querying the subkey */
if (QueryTable->Flags & RTL_QUERY_REGISTRY_SUBKEY)
{
/* Make sure we have a name */
if (!QueryTable->Name)
{
/* Fail */
Status = STATUS_INVALID_PARAMETER;
}
else
{
/* Initialize the name */
RtlInitUnicodeString(&KeyPath, QueryTable->Name);
/* Get the key handle */
InitializeObjectAttributes(&ObjectAttributes,
&KeyPath,
OBJ_CASE_INSENSITIVE |
OBJ_KERNEL_HANDLE,
KeyHandle,
NULL);
Status = ZwOpenKey(&CurrentKey,
MAXIMUM_ALLOWED,
&ObjectAttributes);
if (NT_SUCCESS(Status))
{
/* If we have a query routine, go enumerate values */
if (QueryTable->QueryRoutine) goto ProcessValues;
}
}
}
else if (QueryTable->Name)
{
/* Initialize the path */
RtlInitUnicodeString(&KeyValueName, QueryTable->Name);
/* Start query loop */
i = 0;
while (TRUE)
{
/* Make sure we didn't retry too many times */
if (i++ > 4)
{
/* Fail */
DPRINT1("RtlQueryRegistryValues: Miscomputed buffer size "
"at line %d\n", __LINE__);
break;
}
/* Query key information */
Status = ZwQueryValueKey(CurrentKey,
&KeyValueName,
KeyValueFullInformation,
KeyValueInfo,
(ULONG)InfoSize,
&ResultLength);
if (Status == STATUS_BUFFER_OVERFLOW)
{
/* Normalize status code */
Status = STATUS_BUFFER_TOO_SMALL;
}
/* Check for failure */
if (!NT_SUCCESS(Status))
{
/* Check if we didn't find it */
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
/* Setup a default */
KeyValueInfo->Type = REG_NONE;
KeyValueInfo->DataLength = 0;
ResultLength = (ULONG)InfoSize;
/* Call the query routine */
Status = RtlpCallQueryRegistryRoutine(QueryTable,
KeyValueInfo,
&ResultLength,
Context,
Environment);
}
/* Check for buffer being too small */
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Increase allocation size */
BufferSize = ResultLength +
sizeof(ULONG_PTR) +
sizeof(UNICODE_NULL);
KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
KeyValueInfo,
BufferSize,
&Status);
if (!KeyValueInfo) break;
/* Update the data */
KeyValueInfo->DataOffset = 0;
InfoSize = BufferSize - sizeof(UNICODE_NULL);
continue;
}
}
else
{
/* Check if this is a multi-string */
if (KeyValueInfo->Type == REG_MULTI_SZ)
{
/* Add a null-char */
((PWCHAR)KeyValueInfo)[ResultLength / sizeof(WCHAR)] = UNICODE_NULL;
KeyValueInfo->DataLength += sizeof(UNICODE_NULL);
}
/* Call the query routine */
ResultLength = (ULONG)InfoSize;
Status = RtlpCallQueryRegistryRoutine(QueryTable,
KeyValueInfo,
&ResultLength,
Context,
Environment);
/* Check for buffer being too small */
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Increase allocation size */
BufferSize = ResultLength +
sizeof(ULONG_PTR) +
sizeof(UNICODE_NULL);
KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
KeyValueInfo,
BufferSize,
&Status);
if (!KeyValueInfo) break;
/* Update the data */
KeyValueInfo->DataOffset = 0;
InfoSize = BufferSize - sizeof(UNICODE_NULL);
continue;
}
/* Check if we need to delete the key */
if ((NT_SUCCESS(Status)) &&
(QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE))
{
/* Delete it */
ZwDeleteValueKey(CurrentKey, &KeyValueName);
}
}
/* We're done, break out */
break;
}
}
else if (QueryTable->Flags & RTL_QUERY_REGISTRY_NOVALUE)
{
/* Just call the query routine */
Status = QueryTable->QueryRoutine(NULL,
REG_NONE,
NULL,
0,
Context,
QueryTable->EntryContext);
}
else
{
ProcessValues:
/* Loop every value */
i = Value = 0;
while (TRUE)
{
/* Enumerate the keys */
Status = ZwEnumerateValueKey(CurrentKey,
Value,
KeyValueFullInformation,
KeyValueInfo,
(ULONG)InfoSize,
&ResultLength);
if (Status == STATUS_BUFFER_OVERFLOW)
{
/* Normalize the status */
Status = STATUS_BUFFER_TOO_SMALL;
}
/* Check if we found all the entries */
if (Status == STATUS_NO_MORE_ENTRIES)
{
/* Check if this was the first entry and caller needs it */
if (!(Value) &&
(QueryTable->Flags & RTL_QUERY_REGISTRY_REQUIRED))
{
/* Fail */
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
else
{
/* Otherwise, it's ok */
Status = STATUS_SUCCESS;
}
break;
}
/* Check if enumeration worked */
if (NT_SUCCESS(Status))
{
/* Call the query routine */
ResultLength = (ULONG)InfoSize;
Status = RtlpCallQueryRegistryRoutine(QueryTable,
KeyValueInfo,
&ResultLength,
Context,
Environment);
}
/* Check if the query failed */
if (Status == STATUS_BUFFER_TOO_SMALL)
{
/* Increase allocation size */
BufferSize = ResultLength +
sizeof(ULONG_PTR) +
sizeof(UNICODE_NULL);
KeyValueInfo = RtlpAllocDeallocQueryBuffer(&BufferSize,
KeyValueInfo,
BufferSize,
&Status);
if (!KeyValueInfo) break;
/* Update the data */
KeyValueInfo->DataOffset = 0;
InfoSize = BufferSize - sizeof(UNICODE_NULL);
/* Try the value again unless it's been too many times */
if (i++ <= 4) continue;
break;
}
/* Break out if we failed */
if (!NT_SUCCESS(Status)) break;
/* Reset the number of retries and check if we need to delete */
i = 0;
if (QueryTable->Flags & RTL_QUERY_REGISTRY_DELETE)
{
/* Build the name */
RtlInitEmptyUnicodeString(&KeyValueName,
KeyValueInfo->Name,
(USHORT)KeyValueInfo->NameLength);
KeyValueName.Length = KeyValueName.MaximumLength;
/* Delete the key */
Status = ZwDeleteValueKey(CurrentKey, &KeyValueName);
if (NT_SUCCESS(Status)) Value--;
}
/* Go to the next value */
Value++;
}
}
/* Check if we failed anywhere along the road */
if (!NT_SUCCESS(Status)) break;
/* Continue */
QueryTable++;
}
/* Check if we need to close our handle */
if (KeyHandle) RtlpCloseRegistryHandle(RelativeTo, KeyHandle);
if ((CurrentKey) && (CurrentKey != KeyHandle)) ZwClose(CurrentKey);
/* Free our buffer and return status */
RtlpAllocDeallocQueryBuffer(NULL, KeyValueInfo, BufferSize, NULL);
return Status;
}
/* EOF */