[FREELDR] Always change video mode back to text-mode before starting up ReactOS.
[reactos.git] / boot / freeldr / freeldr / ntldr / registry.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2014 Timo Kreuzer <timo.kreuzer@reactos.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <freeldr.h>
22 #include <cmlib.h>
23 #include "registry.h"
24
25 #include <debug.h>
26 DBG_DEFAULT_CHANNEL(REGISTRY);
27
28 static PCMHIVE CmHive;
29 static PCM_KEY_NODE RootKeyNode;
30 static HKEY CurrentControlSetKey;
31
32 PVOID
33 NTAPI
34 CmpAllocate(
35 IN SIZE_T Size,
36 IN BOOLEAN Paged,
37 IN ULONG Tag)
38 {
39 UNREFERENCED_PARAMETER(Paged);
40 UNREFERENCED_PARAMETER(Tag);
41
42 return FrLdrTempAlloc(Size, Tag);
43 }
44
45 VOID
46 NTAPI
47 CmpFree(
48 IN PVOID Ptr,
49 IN ULONG Quota)
50 {
51 UNREFERENCED_PARAMETER(Quota);
52 FrLdrTempFree(Ptr, 0);
53 }
54
55 BOOLEAN
56 RegImportBinaryHive(
57 _In_ PVOID ChunkBase,
58 _In_ ULONG ChunkSize)
59 {
60 NTSTATUS Status;
61 TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize);
62
63 /* Allocate and initialize the hive */
64 CmHive = CmpAllocate(sizeof(CMHIVE), FALSE, 'eviH');
65 Status = HvInitialize(&CmHive->Hive,
66 HINIT_FLAT, // HINIT_MEMORY_INPLACE
67 0,
68 0,
69 ChunkBase,
70 CmpAllocate,
71 CmpFree,
72 NULL,
73 NULL,
74 NULL,
75 NULL,
76 1,
77 NULL);
78 if (!NT_SUCCESS(Status))
79 {
80 CmpFree(CmHive, 0);
81 ERR("Corrupted hive %p!\n", ChunkBase);
82 return FALSE;
83 }
84
85 /* Save the root key node */
86 RootKeyNode = (PCM_KEY_NODE)HvGetCell(&CmHive->Hive, CmHive->Hive.BaseBlock->RootCell);
87
88 TRACE("RegImportBinaryHive done\n");
89 return TRUE;
90 }
91
92 LONG
93 RegInitCurrentControlSet(
94 _In_ BOOLEAN LastKnownGood)
95 {
96 WCHAR ControlSetKeyName[80];
97 HKEY SelectKey;
98 HKEY SystemKey;
99 ULONG CurrentSet = 0;
100 ULONG DefaultSet = 0;
101 ULONG LastKnownGoodSet = 0;
102 ULONG DataSize;
103 LONG Error;
104 TRACE("RegInitCurrentControlSet\n");
105
106 Error = RegOpenKey(NULL,
107 L"\\Registry\\Machine\\SYSTEM\\Select",
108 &SelectKey);
109 if (Error != ERROR_SUCCESS)
110 {
111 ERR("RegOpenKey() failed (Error %u)\n", (int)Error);
112 return Error;
113 }
114
115 DataSize = sizeof(ULONG);
116 Error = RegQueryValue(SelectKey,
117 L"Default",
118 NULL,
119 (PUCHAR)&DefaultSet,
120 &DataSize);
121 if (Error != ERROR_SUCCESS)
122 {
123 ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error);
124 return Error;
125 }
126
127 DataSize = sizeof(ULONG);
128 Error = RegQueryValue(SelectKey,
129 L"LastKnownGood",
130 NULL,
131 (PUCHAR)&LastKnownGoodSet,
132 &DataSize);
133 if (Error != ERROR_SUCCESS)
134 {
135 ERR("RegQueryValue('LastKnownGood') failed (Error %u)\n", (int)Error);
136 return Error;
137 }
138
139 CurrentSet = (LastKnownGood) ? LastKnownGoodSet : DefaultSet;
140 wcscpy(ControlSetKeyName, L"ControlSet");
141 switch(CurrentSet)
142 {
143 case 1:
144 wcscat(ControlSetKeyName, L"001");
145 break;
146 case 2:
147 wcscat(ControlSetKeyName, L"002");
148 break;
149 case 3:
150 wcscat(ControlSetKeyName, L"003");
151 break;
152 case 4:
153 wcscat(ControlSetKeyName, L"004");
154 break;
155 case 5:
156 wcscat(ControlSetKeyName, L"005");
157 break;
158 }
159
160 Error = RegOpenKey(NULL,
161 L"\\Registry\\Machine\\SYSTEM",
162 &SystemKey);
163 if (Error != ERROR_SUCCESS)
164 {
165 ERR("RegOpenKey(SystemKey) failed (Error %lu)\n", Error);
166 return Error;
167 }
168
169 Error = RegOpenKey(SystemKey,
170 ControlSetKeyName,
171 &CurrentControlSetKey);
172 if (Error != ERROR_SUCCESS)
173 {
174 ERR("RegOpenKey(CurrentControlSetKey) failed (Error %lu)\n", Error);
175 return Error;
176 }
177
178 TRACE("RegInitCurrentControlSet done\n");
179 return ERROR_SUCCESS;
180 }
181
182 static
183 BOOLEAN
184 GetNextPathElement(
185 _Out_ PUNICODE_STRING NextElement,
186 _Inout_ PUNICODE_STRING RemainingPath)
187 {
188 /* Check if there are any characters left */
189 if (RemainingPath->Length < sizeof(WCHAR))
190 {
191 /* Nothing left, bail out early */
192 return FALSE;
193 }
194
195 /* The next path elements starts with the remaining path */
196 NextElement->Buffer = RemainingPath->Buffer;
197
198 /* Loop until the path element ends */
199 while ((RemainingPath->Length >= sizeof(WCHAR)) &&
200 (RemainingPath->Buffer[0] != '\\'))
201 {
202 /* Skip this character */
203 RemainingPath->Buffer++;
204 RemainingPath->Length -= sizeof(WCHAR);
205 }
206
207 NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR);
208 NextElement->MaximumLength = NextElement->Length;
209
210 /* Check if the path element ended with a path separator */
211 if (RemainingPath->Length >= sizeof(WCHAR))
212 {
213 /* Skip the path separator */
214 ASSERT(RemainingPath->Buffer[0] == '\\');
215 RemainingPath->Buffer++;
216 RemainingPath->Length -= sizeof(WCHAR);
217 }
218
219 /* Return whether we got any characters */
220 return TRUE;
221 }
222
223 LONG
224 RegEnumKey(
225 _In_ HKEY Key,
226 _In_ ULONG Index,
227 _Out_ PWCHAR Name,
228 _Inout_ ULONG* NameSize,
229 _Out_opt_ PHKEY SubKey)
230 {
231 PHHIVE Hive = &CmHive->Hive;
232 PCM_KEY_NODE KeyNode, SubKeyNode;
233 HCELL_INDEX CellIndex;
234 USHORT NameLength;
235
236 TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n",
237 Key, Index, Name, NameSize, NameSize ? *NameSize : 0);
238
239 /* Get the key node */
240 KeyNode = (PCM_KEY_NODE)Key;
241 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
242
243 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index);
244 if (CellIndex == HCELL_NIL)
245 {
246 TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n",
247 Index, KeyNode->NameLength, KeyNode->Name);
248 return ERROR_NO_MORE_ITEMS;
249 }
250
251 /* Get the value cell */
252 SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
253 ASSERT(SubKeyNode != NULL);
254 ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE);
255
256 if (SubKeyNode->Flags & KEY_COMP_NAME)
257 {
258 NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength);
259
260 /* Compressed name */
261 CmpCopyCompressedName(Name,
262 *NameSize,
263 SubKeyNode->Name,
264 SubKeyNode->NameLength);
265 }
266 else
267 {
268 NameLength = SubKeyNode->NameLength;
269
270 /* Normal name */
271 RtlCopyMemory(Name, SubKeyNode->Name,
272 min(*NameSize, SubKeyNode->NameLength));
273 }
274
275 if (*NameSize >= NameLength + sizeof(WCHAR))
276 {
277 Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
278 }
279
280 *NameSize = NameLength + sizeof(WCHAR);
281
282 /**/HvReleaseCell(Hive, CellIndex);/**/
283
284 if (SubKey != NULL)
285 *SubKey = (HKEY)SubKeyNode;
286
287 TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name);
288 return ERROR_SUCCESS;
289 }
290
291 LONG
292 RegOpenKey(
293 _In_ HKEY ParentKey,
294 _In_z_ PCWSTR KeyName,
295 _Out_ PHKEY Key)
296 {
297 UNICODE_STRING RemainingPath, SubKeyName;
298 UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet");
299 PHHIVE Hive = &CmHive->Hive;
300 PCM_KEY_NODE KeyNode;
301 HCELL_INDEX CellIndex;
302 TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key);
303
304 /* Initialize the remaining path name */
305 RtlInitUnicodeString(&RemainingPath, KeyName);
306
307 /* Get the parent key node */
308 KeyNode = (PCM_KEY_NODE)ParentKey;
309
310 /* Check if we have a parent key */
311 if (KeyNode == NULL)
312 {
313 UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3;
314 UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry");
315 UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE");
316 UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM");
317 TRACE("RegOpenKey: absolute path\n");
318
319 if ((RemainingPath.Length < sizeof(WCHAR)) ||
320 RemainingPath.Buffer[0] != '\\')
321 {
322 /* The key path is not absolute */
323 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
324 return ERROR_PATH_NOT_FOUND;
325 }
326
327 /* Skip initial path separator */
328 RemainingPath.Buffer++;
329 RemainingPath.Length -= sizeof(WCHAR);
330
331 /* Get the first 3 path elements */
332 GetNextPathElement(&SubKeyName1, &RemainingPath);
333 GetNextPathElement(&SubKeyName2, &RemainingPath);
334 GetNextPathElement(&SubKeyName3, &RemainingPath);
335 TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3);
336
337 /* Check if we have the correct path */
338 if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) ||
339 !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) ||
340 !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE))
341 {
342 /* The key path is not inside HKLM\Machine\System */
343 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
344 return ERROR_PATH_NOT_FOUND;
345 }
346
347 /* Use the root key */
348 KeyNode = RootKeyNode;
349 }
350
351 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
352
353 /* Check if this is the root key */
354 if (KeyNode == RootKeyNode)
355 {
356 UNICODE_STRING TempPath = RemainingPath;
357
358 /* Get the first path element */
359 GetNextPathElement(&SubKeyName, &TempPath);
360
361 /* Check if this is CurrentControlSet */
362 if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE))
363 {
364 /* Use the CurrentControlSetKey and update the remaining path */
365 KeyNode = (PCM_KEY_NODE)CurrentControlSetKey;
366 RemainingPath = TempPath;
367 }
368 }
369
370 TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath);
371
372 /* Loop while there are path elements */
373 while (GetNextPathElement(&SubKeyName, &RemainingPath))
374 {
375 TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName);
376
377 /* Get the next sub key */
378 CellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName);
379 if (CellIndex == HCELL_NIL)
380 {
381 ERR("Did not find sub key '%wZ' (full %S)\n", &SubKeyName, KeyName);
382 return ERROR_PATH_NOT_FOUND;
383 }
384
385 /* Get the found key */
386 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
387 ASSERT(KeyNode);
388 }
389
390 *Key = (HKEY)KeyNode;
391
392 TRACE("RegOpenKey done\n");
393 return ERROR_SUCCESS;
394 }
395
396 static
397 VOID
398 RepGetValueData(
399 _In_ PHHIVE Hive,
400 _In_ PCM_KEY_VALUE ValueCell,
401 _Out_opt_ ULONG* Type,
402 _Out_opt_ PUCHAR Data,
403 _Inout_opt_ ULONG* DataSize)
404 {
405 ULONG DataLength;
406 PVOID DataCell;
407
408 /* Does the caller want the type? */
409 if (Type != NULL)
410 *Type = ValueCell->Type;
411
412 /* Does the caller provide DataSize? */
413 if (DataSize != NULL)
414 {
415 // NOTE: CmpValueToData doesn't support big data (the function will
416 // bugcheck if so), FreeLdr is not supposed to read such data.
417 // If big data is needed, use instead CmpGetValueData.
418 // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...);
419 DataCell = CmpValueToData(Hive, ValueCell, &DataLength);
420
421 /* Does the caller want the data? */
422 if ((Data != NULL) && (*DataSize != 0))
423 {
424 RtlCopyMemory(Data,
425 DataCell,
426 min(*DataSize, DataLength));
427 }
428
429 /* Return the actual data length */
430 *DataSize = DataLength;
431 }
432 }
433
434 LONG
435 RegQueryValue(
436 _In_ HKEY Key,
437 _In_z_ PCWSTR ValueName,
438 _Out_opt_ ULONG* Type,
439 _Out_opt_ PUCHAR Data,
440 _Inout_opt_ ULONG* DataSize)
441 {
442 PHHIVE Hive = &CmHive->Hive;
443 PCM_KEY_NODE KeyNode;
444 PCM_KEY_VALUE ValueCell;
445 HCELL_INDEX CellIndex;
446 UNICODE_STRING ValueNameString;
447
448 TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n",
449 Key, ValueName, Type, Data, DataSize);
450
451 /* Get the key node */
452 KeyNode = (PCM_KEY_NODE)Key;
453 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
454
455 /* Initialize value name string */
456 RtlInitUnicodeString(&ValueNameString, ValueName);
457 CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString);
458 if (CellIndex == HCELL_NIL)
459 {
460 TRACE("RegQueryValue value not found in key (%.*s)\n",
461 KeyNode->NameLength, KeyNode->Name);
462 return ERROR_FILE_NOT_FOUND;
463 }
464
465 /* Get the value cell */
466 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
467 ASSERT(ValueCell != NULL);
468
469 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
470
471 HvReleaseCell(Hive, CellIndex);
472
473 TRACE("RegQueryValue success\n");
474 return ERROR_SUCCESS;
475 }
476
477 /*
478 * NOTE: This function is currently unused in FreeLdr; however it is kept here
479 * as an implementation reference of RegEnumValue using CMLIB that may be used
480 * elsewhere in ReactOS.
481 */
482 #if 0
483 LONG
484 RegEnumValue(
485 _In_ HKEY Key,
486 _In_ ULONG Index,
487 _Out_ PWCHAR ValueName,
488 _Inout_ ULONG* NameSize,
489 _Out_opt_ ULONG* Type,
490 _Out_opt_ PUCHAR Data,
491 _Inout_opt_ ULONG* DataSize)
492 {
493 PHHIVE Hive = &CmHive->Hive;
494 PCM_KEY_NODE KeyNode;
495 PCELL_DATA ValueListCell;
496 PCM_KEY_VALUE ValueCell;
497 USHORT NameLength;
498
499 TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n",
500 Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize);
501
502 /* Get the key node */
503 KeyNode = (PCM_KEY_NODE)Key;
504 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
505
506 /* Check if the index is valid */
507 if ((KeyNode->ValueList.Count == 0) ||
508 (KeyNode->ValueList.List == HCELL_NIL) ||
509 (Index >= KeyNode->ValueList.Count))
510 {
511 ERR("RegEnumValue: index invalid\n");
512 return ERROR_NO_MORE_ITEMS;
513 }
514
515 ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List);
516 ASSERT(ValueListCell != NULL);
517
518 /* Get the value cell */
519 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]);
520 ASSERT(ValueCell != NULL);
521 ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE);
522
523 if (ValueCell->Flags & VALUE_COMP_NAME)
524 {
525 NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength);
526
527 /* Compressed name */
528 CmpCopyCompressedName(ValueName,
529 *NameSize,
530 ValueCell->Name,
531 ValueCell->NameLength);
532 }
533 else
534 {
535 NameLength = ValueCell->NameLength;
536
537 /* Normal name */
538 RtlCopyMemory(ValueName, ValueCell->Name,
539 min(*NameSize, ValueCell->NameLength));
540 }
541
542 if (*NameSize >= NameLength + sizeof(WCHAR))
543 {
544 ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
545 }
546
547 *NameSize = NameLength + sizeof(WCHAR);
548
549 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
550
551 HvReleaseCell(Hive, ValueListCell->KeyList[Index]);
552 HvReleaseCell(Hive, KeyNode->ValueList.List);
553
554 TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName);
555 return ERROR_SUCCESS;
556 }
557 #endif
558
559 /* EOF */