reactos/boot/freeldr/freeldr/ntldr/registry.c
2018-08-04 19:19:34 +02:00

560 lines
16 KiB
C

/*
* FreeLoader
*
* Copyright (C) 2014 Timo Kreuzer <timo.kreuzer@reactos.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <freeldr.h>
#include <cmlib.h>
#include "registry.h"
#include <debug.h>
DBG_DEFAULT_CHANNEL(REGISTRY);
static PCMHIVE CmHive;
static PCM_KEY_NODE RootKeyNode;
static HKEY CurrentControlSetKey;
PVOID
NTAPI
CmpAllocate(
IN SIZE_T Size,
IN BOOLEAN Paged,
IN ULONG Tag)
{
UNREFERENCED_PARAMETER(Paged);
UNREFERENCED_PARAMETER(Tag);
return FrLdrTempAlloc(Size, Tag);
}
VOID
NTAPI
CmpFree(
IN PVOID Ptr,
IN ULONG Quota)
{
UNREFERENCED_PARAMETER(Quota);
FrLdrTempFree(Ptr, 0);
}
BOOLEAN
RegImportBinaryHive(
_In_ PVOID ChunkBase,
_In_ ULONG ChunkSize)
{
NTSTATUS Status;
TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize);
/* Allocate and initialize the hive */
CmHive = CmpAllocate(sizeof(CMHIVE), FALSE, 'eviH');
Status = HvInitialize(&CmHive->Hive,
HINIT_FLAT, // HINIT_MEMORY_INPLACE
0,
0,
ChunkBase,
CmpAllocate,
CmpFree,
NULL,
NULL,
NULL,
NULL,
1,
NULL);
if (!NT_SUCCESS(Status))
{
CmpFree(CmHive, 0);
ERR("Corrupted hive %p!\n", ChunkBase);
return FALSE;
}
/* Save the root key node */
RootKeyNode = (PCM_KEY_NODE)HvGetCell(&CmHive->Hive, CmHive->Hive.BaseBlock->RootCell);
TRACE("RegImportBinaryHive done\n");
return TRUE;
}
LONG
RegInitCurrentControlSet(
_In_ BOOLEAN LastKnownGood)
{
WCHAR ControlSetKeyName[80];
HKEY SelectKey;
HKEY SystemKey;
ULONG CurrentSet = 0;
ULONG DefaultSet = 0;
ULONG LastKnownGoodSet = 0;
ULONG DataSize;
LONG Error;
TRACE("RegInitCurrentControlSet\n");
Error = RegOpenKey(NULL,
L"\\Registry\\Machine\\SYSTEM\\Select",
&SelectKey);
if (Error != ERROR_SUCCESS)
{
ERR("RegOpenKey() failed (Error %u)\n", (int)Error);
return Error;
}
DataSize = sizeof(ULONG);
Error = RegQueryValue(SelectKey,
L"Default",
NULL,
(PUCHAR)&DefaultSet,
&DataSize);
if (Error != ERROR_SUCCESS)
{
ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error);
return Error;
}
DataSize = sizeof(ULONG);
Error = RegQueryValue(SelectKey,
L"LastKnownGood",
NULL,
(PUCHAR)&LastKnownGoodSet,
&DataSize);
if (Error != ERROR_SUCCESS)
{
ERR("RegQueryValue('LastKnownGood') failed (Error %u)\n", (int)Error);
return Error;
}
CurrentSet = (LastKnownGood) ? LastKnownGoodSet : DefaultSet;
wcscpy(ControlSetKeyName, L"ControlSet");
switch(CurrentSet)
{
case 1:
wcscat(ControlSetKeyName, L"001");
break;
case 2:
wcscat(ControlSetKeyName, L"002");
break;
case 3:
wcscat(ControlSetKeyName, L"003");
break;
case 4:
wcscat(ControlSetKeyName, L"004");
break;
case 5:
wcscat(ControlSetKeyName, L"005");
break;
}
Error = RegOpenKey(NULL,
L"\\Registry\\Machine\\SYSTEM",
&SystemKey);
if (Error != ERROR_SUCCESS)
{
ERR("RegOpenKey(SystemKey) failed (Error %lu)\n", Error);
return Error;
}
Error = RegOpenKey(SystemKey,
ControlSetKeyName,
&CurrentControlSetKey);
if (Error != ERROR_SUCCESS)
{
ERR("RegOpenKey(CurrentControlSetKey) failed (Error %lu)\n", Error);
return Error;
}
TRACE("RegInitCurrentControlSet done\n");
return ERROR_SUCCESS;
}
static
BOOLEAN
GetNextPathElement(
_Out_ PUNICODE_STRING NextElement,
_Inout_ PUNICODE_STRING RemainingPath)
{
/* Check if there are any characters left */
if (RemainingPath->Length < sizeof(WCHAR))
{
/* Nothing left, bail out early */
return FALSE;
}
/* The next path elements starts with the remaining path */
NextElement->Buffer = RemainingPath->Buffer;
/* Loop until the path element ends */
while ((RemainingPath->Length >= sizeof(WCHAR)) &&
(RemainingPath->Buffer[0] != '\\'))
{
/* Skip this character */
RemainingPath->Buffer++;
RemainingPath->Length -= sizeof(WCHAR);
}
NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR);
NextElement->MaximumLength = NextElement->Length;
/* Check if the path element ended with a path separator */
if (RemainingPath->Length >= sizeof(WCHAR))
{
/* Skip the path separator */
ASSERT(RemainingPath->Buffer[0] == '\\');
RemainingPath->Buffer++;
RemainingPath->Length -= sizeof(WCHAR);
}
/* Return whether we got any characters */
return TRUE;
}
LONG
RegEnumKey(
_In_ HKEY Key,
_In_ ULONG Index,
_Out_ PWCHAR Name,
_Inout_ ULONG* NameSize,
_Out_opt_ PHKEY SubKey)
{
PHHIVE Hive = &CmHive->Hive;
PCM_KEY_NODE KeyNode, SubKeyNode;
HCELL_INDEX CellIndex;
USHORT NameLength;
TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n",
Key, Index, Name, NameSize, NameSize ? *NameSize : 0);
/* Get the key node */
KeyNode = (PCM_KEY_NODE)Key;
ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index);
if (CellIndex == HCELL_NIL)
{
TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n",
Index, KeyNode->NameLength, KeyNode->Name);
return ERROR_NO_MORE_ITEMS;
}
/* Get the value cell */
SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
ASSERT(SubKeyNode != NULL);
ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE);
if (SubKeyNode->Flags & KEY_COMP_NAME)
{
NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength);
/* Compressed name */
CmpCopyCompressedName(Name,
*NameSize,
SubKeyNode->Name,
SubKeyNode->NameLength);
}
else
{
NameLength = SubKeyNode->NameLength;
/* Normal name */
RtlCopyMemory(Name, SubKeyNode->Name,
min(*NameSize, SubKeyNode->NameLength));
}
if (*NameSize >= NameLength + sizeof(WCHAR))
{
Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
}
*NameSize = NameLength + sizeof(WCHAR);
/**/HvReleaseCell(Hive, CellIndex);/**/
if (SubKey != NULL)
*SubKey = (HKEY)SubKeyNode;
TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name);
return ERROR_SUCCESS;
}
LONG
RegOpenKey(
_In_ HKEY ParentKey,
_In_z_ PCWSTR KeyName,
_Out_ PHKEY Key)
{
UNICODE_STRING RemainingPath, SubKeyName;
UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet");
PHHIVE Hive = &CmHive->Hive;
PCM_KEY_NODE KeyNode;
HCELL_INDEX CellIndex;
TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key);
/* Initialize the remaining path name */
RtlInitUnicodeString(&RemainingPath, KeyName);
/* Get the parent key node */
KeyNode = (PCM_KEY_NODE)ParentKey;
/* Check if we have a parent key */
if (KeyNode == NULL)
{
UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3;
UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry");
UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE");
UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM");
TRACE("RegOpenKey: absolute path\n");
if ((RemainingPath.Length < sizeof(WCHAR)) ||
RemainingPath.Buffer[0] != '\\')
{
/* The key path is not absolute */
ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
return ERROR_PATH_NOT_FOUND;
}
/* Skip initial path separator */
RemainingPath.Buffer++;
RemainingPath.Length -= sizeof(WCHAR);
/* Get the first 3 path elements */
GetNextPathElement(&SubKeyName1, &RemainingPath);
GetNextPathElement(&SubKeyName2, &RemainingPath);
GetNextPathElement(&SubKeyName3, &RemainingPath);
TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3);
/* Check if we have the correct path */
if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) ||
!RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) ||
!RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE))
{
/* The key path is not inside HKLM\Machine\System */
ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
return ERROR_PATH_NOT_FOUND;
}
/* Use the root key */
KeyNode = RootKeyNode;
}
ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
/* Check if this is the root key */
if (KeyNode == RootKeyNode)
{
UNICODE_STRING TempPath = RemainingPath;
/* Get the first path element */
GetNextPathElement(&SubKeyName, &TempPath);
/* Check if this is CurrentControlSet */
if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE))
{
/* Use the CurrentControlSetKey and update the remaining path */
KeyNode = (PCM_KEY_NODE)CurrentControlSetKey;
RemainingPath = TempPath;
}
}
TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath);
/* Loop while there are path elements */
while (GetNextPathElement(&SubKeyName, &RemainingPath))
{
TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName);
/* Get the next sub key */
CellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName);
if (CellIndex == HCELL_NIL)
{
ERR("Did not find sub key '%wZ' (full %S)\n", &SubKeyName, KeyName);
return ERROR_PATH_NOT_FOUND;
}
/* Get the found key */
KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
ASSERT(KeyNode);
}
*Key = (HKEY)KeyNode;
TRACE("RegOpenKey done\n");
return ERROR_SUCCESS;
}
static
VOID
RepGetValueData(
_In_ PHHIVE Hive,
_In_ PCM_KEY_VALUE ValueCell,
_Out_opt_ ULONG* Type,
_Out_opt_ PUCHAR Data,
_Inout_opt_ ULONG* DataSize)
{
ULONG DataLength;
PVOID DataCell;
/* Does the caller want the type? */
if (Type != NULL)
*Type = ValueCell->Type;
/* Does the caller provide DataSize? */
if (DataSize != NULL)
{
// NOTE: CmpValueToData doesn't support big data (the function will
// bugcheck if so), FreeLdr is not supposed to read such data.
// If big data is needed, use instead CmpGetValueData.
// CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...);
DataCell = CmpValueToData(Hive, ValueCell, &DataLength);
/* Does the caller want the data? */
if ((Data != NULL) && (*DataSize != 0))
{
RtlCopyMemory(Data,
DataCell,
min(*DataSize, DataLength));
}
/* Return the actual data length */
*DataSize = DataLength;
}
}
LONG
RegQueryValue(
_In_ HKEY Key,
_In_z_ PCWSTR ValueName,
_Out_opt_ ULONG* Type,
_Out_opt_ PUCHAR Data,
_Inout_opt_ ULONG* DataSize)
{
PHHIVE Hive = &CmHive->Hive;
PCM_KEY_NODE KeyNode;
PCM_KEY_VALUE ValueCell;
HCELL_INDEX CellIndex;
UNICODE_STRING ValueNameString;
TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n",
Key, ValueName, Type, Data, DataSize);
/* Get the key node */
KeyNode = (PCM_KEY_NODE)Key;
ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
/* Initialize value name string */
RtlInitUnicodeString(&ValueNameString, ValueName);
CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString);
if (CellIndex == HCELL_NIL)
{
TRACE("RegQueryValue value not found in key (%.*s)\n",
KeyNode->NameLength, KeyNode->Name);
return ERROR_FILE_NOT_FOUND;
}
/* Get the value cell */
ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
ASSERT(ValueCell != NULL);
RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
HvReleaseCell(Hive, CellIndex);
TRACE("RegQueryValue success\n");
return ERROR_SUCCESS;
}
/*
* NOTE: This function is currently unused in FreeLdr; however it is kept here
* as an implementation reference of RegEnumValue using CMLIB that may be used
* elsewhere in ReactOS.
*/
#if 0
LONG
RegEnumValue(
_In_ HKEY Key,
_In_ ULONG Index,
_Out_ PWCHAR ValueName,
_Inout_ ULONG* NameSize,
_Out_opt_ ULONG* Type,
_Out_opt_ PUCHAR Data,
_Inout_opt_ ULONG* DataSize)
{
PHHIVE Hive = &CmHive->Hive;
PCM_KEY_NODE KeyNode;
PCELL_DATA ValueListCell;
PCM_KEY_VALUE ValueCell;
USHORT NameLength;
TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n",
Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize);
/* Get the key node */
KeyNode = (PCM_KEY_NODE)Key;
ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
/* Check if the index is valid */
if ((KeyNode->ValueList.Count == 0) ||
(KeyNode->ValueList.List == HCELL_NIL) ||
(Index >= KeyNode->ValueList.Count))
{
ERR("RegEnumValue: index invalid\n");
return ERROR_NO_MORE_ITEMS;
}
ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List);
ASSERT(ValueListCell != NULL);
/* Get the value cell */
ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]);
ASSERT(ValueCell != NULL);
ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE);
if (ValueCell->Flags & VALUE_COMP_NAME)
{
NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength);
/* Compressed name */
CmpCopyCompressedName(ValueName,
*NameSize,
ValueCell->Name,
ValueCell->NameLength);
}
else
{
NameLength = ValueCell->NameLength;
/* Normal name */
RtlCopyMemory(ValueName, ValueCell->Name,
min(*NameSize, ValueCell->NameLength));
}
if (*NameSize >= NameLength + sizeof(WCHAR))
{
ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
}
*NameSize = NameLength + sizeof(WCHAR);
RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
HvReleaseCell(Hive, ValueListCell->KeyList[Index]);
HvReleaseCell(Hive, KeyNode->ValueList.List);
TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName);
return ERROR_SUCCESS;
}
#endif
/* EOF */