8126a5c00b6b2673b4f0ee05b41e71e3b9e85b10
[reactos.git] / reactos / boot / freeldr / freeldr / reactos / 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 <debug.h>
24
25 DBG_DEFAULT_CHANNEL(REGISTRY);
26
27 static PCMHIVE CmHive;
28 static PCM_KEY_NODE RootKeyNode;
29 static FRLDRHKEY CurrentControlSetKey;
30
31 BOOLEAN
32 RegImportBinaryHive(
33 _In_ PCHAR ChunkBase,
34 _In_ ULONG ChunkSize)
35 {
36 NTSTATUS Status;
37 TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize);
38
39 /* Allocate and initialize the hive */
40 CmHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH');
41 Status = HvInitialize(&CmHive->Hive,
42 HINIT_FLAT,
43 0,
44 0,
45 ChunkBase,
46 NULL,
47 NULL,
48 NULL,
49 NULL,
50 NULL,
51 NULL,
52 1,
53 NULL);
54 if (!NT_SUCCESS(Status))
55 {
56 FrLdrTempFree(CmHive, 'eviH');
57 ERR("Invalid hive Signature!\n");
58 return FALSE;
59 }
60
61 /* Save the root key node */
62 RootKeyNode = HvGetCell(&CmHive->Hive, CmHive->Hive.BaseBlock->RootCell);
63
64 TRACE("RegImportBinaryHive done\n");
65 return TRUE;
66 }
67
68 VOID
69 RegInitializeRegistry(VOID)
70 {
71 /* Nothing to do */
72 }
73
74
75 LONG
76 RegInitCurrentControlSet(
77 _In_ BOOLEAN LastKnownGood)
78 {
79 WCHAR ControlSetKeyName[80];
80 FRLDRHKEY SelectKey;
81 FRLDRHKEY SystemKey;
82 ULONG CurrentSet = 0;
83 ULONG DefaultSet = 0;
84 ULONG LastKnownGoodSet = 0;
85 ULONG DataSize;
86 LONG Error;
87 TRACE("RegInitCurrentControlSet\n");
88
89 Error = RegOpenKey(NULL,
90 L"\\Registry\\Machine\\SYSTEM\\Select",
91 &SelectKey);
92 if (Error != ERROR_SUCCESS)
93 {
94 ERR("RegOpenKey() failed (Error %u)\n", (int)Error);
95 return Error;
96 }
97
98 DataSize = sizeof(ULONG);
99 Error = RegQueryValue(SelectKey,
100 L"Default",
101 NULL,
102 (PUCHAR)&DefaultSet,
103 &DataSize);
104 if (Error != ERROR_SUCCESS)
105 {
106 ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error);
107 return Error;
108 }
109
110 DataSize = sizeof(ULONG);
111 Error = RegQueryValue(SelectKey,
112 L"LastKnownGood",
113 NULL,
114 (PUCHAR)&LastKnownGoodSet,
115 &DataSize);
116 if (Error != ERROR_SUCCESS)
117 {
118 ERR("RegQueryValue('Default') failed (Error %u)\n", (int)Error);
119 return Error;
120 }
121
122 CurrentSet = (LastKnownGood == TRUE) ? LastKnownGoodSet : DefaultSet;
123 wcscpy(ControlSetKeyName, L"ControlSet");
124 switch(CurrentSet)
125 {
126 case 1:
127 wcscat(ControlSetKeyName, L"001");
128 break;
129 case 2:
130 wcscat(ControlSetKeyName, L"002");
131 break;
132 case 3:
133 wcscat(ControlSetKeyName, L"003");
134 break;
135 case 4:
136 wcscat(ControlSetKeyName, L"004");
137 break;
138 case 5:
139 wcscat(ControlSetKeyName, L"005");
140 break;
141 }
142
143 Error = RegOpenKey(NULL,
144 L"\\Registry\\Machine\\SYSTEM",
145 &SystemKey);
146 if (Error != ERROR_SUCCESS)
147 {
148 ERR("RegOpenKey(SystemKey) failed (Error %lu)\n", Error);
149 return Error;
150 }
151
152 Error = RegOpenKey(SystemKey,
153 ControlSetKeyName,
154 &CurrentControlSetKey);
155 if (Error != ERROR_SUCCESS)
156 {
157 ERR("RegOpenKey(CurrentControlSetKey) failed (Error %lu)\n", Error);
158 return Error;
159 }
160
161 TRACE("RegInitCurrentControlSet done\n");
162 return ERROR_SUCCESS;
163 }
164
165 static
166 BOOLEAN
167 GetNextPathElement(
168 _Out_ PUNICODE_STRING NextElement,
169 _Inout_ PUNICODE_STRING RemainingPath)
170 {
171 /* Check if there are any characters left */
172 if (RemainingPath->Length < sizeof(WCHAR))
173 {
174 /* Nothing left, bail out early */
175 return FALSE;
176 }
177
178 /* The next path elements starts with the remaining path */
179 NextElement->Buffer = RemainingPath->Buffer;
180
181 /* Loop until the path element ends */
182 while ((RemainingPath->Length >= sizeof(WCHAR)) &&
183 (RemainingPath->Buffer[0] != '\\'))
184 {
185 /* Skip this character */
186 RemainingPath->Buffer++;
187 RemainingPath->Length -= sizeof(WCHAR);
188 }
189
190 NextElement->Length = (RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR);
191 NextElement->MaximumLength = NextElement->Length;
192
193 /* Check if the path element ended with a path separator */
194 if (RemainingPath->Length >= sizeof(WCHAR))
195 {
196 /* Skip the path separator */
197 ASSERT(RemainingPath->Buffer[0] == '\\');
198 RemainingPath->Buffer++;
199 RemainingPath->Length -= sizeof(WCHAR);
200 }
201
202 /* Return whether we got any characters */
203 return TRUE;
204 }
205
206 static
207 PCM_KEY_NODE
208 RegpFindSubkeyInIndex(
209 _In_ PHHIVE Hive,
210 _In_ PCM_KEY_INDEX IndexCell,
211 _In_ PUNICODE_STRING SubKeyName)
212 {
213 PCM_KEY_NODE SubKeyNode;
214 ULONG i;
215 TRACE("RegpFindSubkeyInIndex('%wZ')\n", SubKeyName);
216
217 /* Check the cell type */
218 if ((IndexCell->Signature == CM_KEY_INDEX_ROOT) ||
219 (IndexCell->Signature == CM_KEY_INDEX_LEAF))
220 {
221 ASSERT(FALSE);
222
223 /* Enumerate subindex cells */
224 for (i = 0; i < IndexCell->Count; i++)
225 {
226 /* Get the subindex cell and call the function recursively */
227 PCM_KEY_INDEX SubIndexCell = HvGetCell(Hive, IndexCell->List[i]);
228
229 SubKeyNode = RegpFindSubkeyInIndex(Hive, SubIndexCell, SubKeyName);
230 if (SubKeyNode != NULL)
231 {
232 return SubKeyNode;
233 }
234 }
235 }
236 else if ((IndexCell->Signature == CM_KEY_FAST_LEAF) ||
237 (IndexCell->Signature == CM_KEY_HASH_LEAF))
238 {
239 /* Directly enumerate subkey nodes */
240 PCM_KEY_FAST_INDEX HashCell = (PCM_KEY_FAST_INDEX)IndexCell;
241 for (i = 0; i < HashCell->Count; i++)
242 {
243 SubKeyNode = HvGetCell(Hive, HashCell->List[i].Cell);
244 ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE);
245
246 TRACE(" RegpFindSubkeyInIndex: checking '%.*s'\n",
247 SubKeyNode->NameLength, SubKeyNode->Name);
248 if (CmCompareKeyName(SubKeyNode, SubKeyName, TRUE))
249 {
250 return SubKeyNode;
251 }
252 }
253 }
254 else
255 {
256 ASSERT(FALSE);
257 }
258
259 return NULL;
260 }
261
262 // FIXME: optionally return the subkey node/handle as optimization
263 LONG
264 RegEnumKey(
265 _In_ FRLDRHKEY Key,
266 _In_ ULONG Index,
267 _Out_ PWCHAR Name,
268 _Inout_ ULONG* NameSize)
269 {
270 PHHIVE Hive = &CmHive->Hive;
271 PCM_KEY_NODE KeyNode, SubKeyNode;
272 PCM_KEY_INDEX IndexCell;
273 PCM_KEY_FAST_INDEX HashCell;
274 TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n",
275 Key, Index, Name, NameSize, NameSize ? *NameSize : 0);
276
277 /* Get the key node */
278 KeyNode = (PCM_KEY_NODE)Key;
279 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
280
281 /* Check if the index is valid */
282 if ((KeyNode->SubKeyCounts[Stable] == 0) ||
283 (Index >= KeyNode->SubKeyCounts[Stable]))
284 {
285 TRACE("RegEnumKey index out of bounds\n");
286 return ERROR_NO_MORE_ITEMS;
287 }
288
289 /* Get the index cell */
290 IndexCell = HvGetCell(Hive, KeyNode->SubKeyLists[Stable]);
291 TRACE("IndexCell: %x, SubKeyCounts: %x\n", IndexCell, KeyNode->SubKeyCounts[Stable]);
292
293 /* Check the cell type */
294 if ((IndexCell->Signature == CM_KEY_FAST_LEAF) ||
295 (IndexCell->Signature == CM_KEY_HASH_LEAF))
296 {
297 /* Get the value cell */
298 HashCell = (PCM_KEY_FAST_INDEX)IndexCell;
299 SubKeyNode = HvGetCell(Hive, HashCell->List[Index].Cell);
300 }
301 else
302 {
303 ASSERT(FALSE);
304 }
305
306 *NameSize = CmCopyKeyName(SubKeyNode, Name, *NameSize);
307
308 TRACE("RegEnumKey done -> %u, '%.*s'\n", *NameSize, *NameSize, Name);
309 return STATUS_SUCCESS;
310 }
311
312 LONG
313 RegOpenKey(
314 _In_ FRLDRHKEY ParentKey,
315 _In_z_ PCWSTR KeyName,
316 _Out_ PFRLDRHKEY Key)
317 {
318 UNICODE_STRING RemainingPath, SubKeyName;
319 UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet");
320 PHHIVE Hive = &CmHive->Hive;
321 PCM_KEY_NODE KeyNode;
322 PCM_KEY_INDEX IndexCell;
323 TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key);
324
325 /* Initialize the remaining path name */
326 RtlInitUnicodeString(&RemainingPath, KeyName);
327
328 /* Get the parent key node */
329 KeyNode = (PCM_KEY_NODE)ParentKey;
330
331 /* Check if we have a parent key */
332 if (KeyNode == NULL)
333 {
334 UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3;
335 UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry");
336 UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE");
337 UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM");
338 TRACE("RegOpenKey: absolute path\n");
339
340 if ((RemainingPath.Length < sizeof(WCHAR)) ||
341 RemainingPath.Buffer[0] != '\\')
342 {
343 /* The key path is not absolute */
344 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
345 return ERROR_PATH_NOT_FOUND;
346 }
347
348 /* Skip initial path separator */
349 RemainingPath.Buffer++;
350 RemainingPath.Length -= sizeof(WCHAR);
351
352 /* Get the first 3 path elements */
353 GetNextPathElement(&SubKeyName1, &RemainingPath);
354 GetNextPathElement(&SubKeyName2, &RemainingPath);
355 GetNextPathElement(&SubKeyName3, &RemainingPath);
356 TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3);
357
358 /* Check if we have the correct path */
359 if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) ||
360 !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) ||
361 !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE))
362 {
363 /* The key path is not inside HKLM\Machine\System */
364 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
365 return ERROR_PATH_NOT_FOUND;
366 }
367
368 /* Use the root key */
369 KeyNode = RootKeyNode;
370 }
371
372 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
373
374 /* Check if this is the root key */
375 if (KeyNode == RootKeyNode)
376 {
377 UNICODE_STRING TempPath = RemainingPath;
378
379 /* Get the first path element */
380 GetNextPathElement(&SubKeyName, &TempPath);
381
382 /* Check if this is CurrentControlSet */
383 if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE))
384 {
385 /* Use the CurrentControlSetKey and update the remaining path */
386 KeyNode = (PCM_KEY_NODE)CurrentControlSetKey;
387 RemainingPath = TempPath;
388 }
389 }
390
391 TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath);
392
393 /* Loop while there are path elements */
394 while (GetNextPathElement(&SubKeyName, &RemainingPath))
395 {
396 TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName);
397
398 /* Check if there is any subkey */
399 if (KeyNode->SubKeyCounts[Stable] == 0)
400 {
401 return ERROR_PATH_NOT_FOUND;
402 }
403
404 /* Get the top level index cell */
405 IndexCell = HvGetCell(Hive, KeyNode->SubKeyLists[Stable]);
406
407 /* Get the next sub key */
408 KeyNode = RegpFindSubkeyInIndex(Hive, IndexCell, &SubKeyName);
409 if (KeyNode == NULL)
410 {
411
412 ERR("Did not find sub key '%wZ' (full %S)\n", &RemainingPath, KeyName);
413 return ERROR_PATH_NOT_FOUND;
414 }
415 }
416
417 TRACE("RegOpenKey done\n");
418 *Key = (FRLDRHKEY)KeyNode;
419 return ERROR_SUCCESS;
420 }
421
422 static
423 VOID
424 RepGetValueData(
425 _In_ PHHIVE Hive,
426 _In_ PCM_KEY_VALUE ValueCell,
427 _Out_opt_ ULONG* Type,
428 _Out_opt_ PUCHAR Data,
429 _Inout_opt_ ULONG* DataSize)
430 {
431 ULONG DataLength;
432
433 /* Does the caller want the type? */
434 if (Type != NULL)
435 {
436 *Type = ValueCell->Type;
437 }
438
439 /* Does the caller provide DataSize? */
440 if (DataSize != NULL)
441 {
442 /* Get the data length */
443 DataLength = ValueCell->DataLength & REG_DATA_SIZE_MASK;
444
445 /* Does the caller want the data? */
446 if ((Data != NULL) && (*DataSize != 0))
447 {
448 /* Check where the data is stored */
449 if ((DataLength <= sizeof(HCELL_INDEX)) &&
450 (ValueCell->DataLength & REG_DATA_IN_OFFSET))
451 {
452 /* The data member contains the data */
453 RtlCopyMemory(Data,
454 &ValueCell->Data,
455 min(*DataSize, DataLength));
456 }
457 else
458 {
459 /* The data member contains the data cell index */
460 PVOID DataCell = HvGetCell(Hive, ValueCell->Data);
461 RtlCopyMemory(Data,
462 DataCell,
463 min(*DataSize, ValueCell->DataLength));
464 }
465
466 }
467
468 /* Return the actual data length */
469 *DataSize = DataLength;
470 }
471 }
472
473 LONG
474 RegQueryValue(
475 _In_ FRLDRHKEY Key,
476 _In_z_ PCWSTR ValueName,
477 _Out_opt_ ULONG* Type,
478 _Out_opt_ PUCHAR Data,
479 _Inout_opt_ ULONG* DataSize)
480 {
481 PHHIVE Hive = &CmHive->Hive;
482 PCM_KEY_NODE KeyNode;
483 PCM_KEY_VALUE ValueCell;
484 PVALUE_LIST_CELL ValueListCell;
485 UNICODE_STRING ValueNameString;
486 ULONG i;
487 TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n",
488 Key, ValueName, Type, Data, DataSize);
489
490 /* Get the key node */
491 KeyNode = (PCM_KEY_NODE)Key;
492 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
493
494 /* Check if there are any values */
495 if (KeyNode->ValueList.Count == 0)
496 {
497 TRACE("RegQueryValue no values in key (%.*s)\n",
498 KeyNode->NameLength, KeyNode->Name);
499 return ERROR_INVALID_PARAMETER;
500 }
501
502 /* Initialize value name string */
503 RtlInitUnicodeString(&ValueNameString, ValueName);
504
505 ValueListCell = (PVALUE_LIST_CELL)HvGetCell(Hive, KeyNode->ValueList.List);
506 TRACE("ValueListCell: %x\n", ValueListCell);
507
508 /* Loop all values */
509 for (i = 0; i < KeyNode->ValueList.Count; i++)
510 {
511 /* Get the subkey node and check the name */
512 ValueCell = HvGetCell(Hive, ValueListCell->ValueOffset[i]);
513
514 /* Compare the value name */
515 TRACE("checking %.*s\n", ValueCell->NameLength, ValueCell->Name);
516 if (CmCompareKeyValueName(ValueCell, &ValueNameString, TRUE))
517 {
518 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
519 TRACE("RegQueryValue success\n");
520 return STATUS_SUCCESS;
521 }
522 }
523
524 TRACE("RegQueryValue value not found\n");
525 return ERROR_INVALID_PARAMETER;
526 }
527
528
529 LONG
530 RegEnumValue(
531 _In_ FRLDRHKEY Key,
532 _In_ ULONG Index,
533 _Out_ PWCHAR ValueName,
534 _Inout_ ULONG* NameSize,
535 _Out_ ULONG* Type,
536 _Out_ PUCHAR Data,
537 _Inout_ ULONG* DataSize)
538 {
539 PHHIVE Hive = &CmHive->Hive;
540 PCM_KEY_NODE KeyNode;
541 PCM_KEY_VALUE ValueCell;
542 PVALUE_LIST_CELL ValueListCell;
543 TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n",
544 Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize);
545
546 /* Get the key node */
547 KeyNode = (PCM_KEY_NODE)Key;
548 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
549
550 /* Check if the index is valid */
551 if ((KeyNode->ValueList.Count == 0) ||
552 (Index >= KeyNode->ValueList.Count))
553 {
554 ERR("RegEnumValue: index invalid\n");
555 return ERROR_NO_MORE_ITEMS;
556 }
557
558 ValueListCell = (PVALUE_LIST_CELL)HvGetCell(Hive, KeyNode->ValueList.List);
559 TRACE("ValueListCell: %x\n", ValueListCell);
560
561 /* Get the value cell */
562 ValueCell = HvGetCell(Hive, ValueListCell->ValueOffset[Index]);
563 ASSERT(ValueCell != NULL);
564
565 if (NameSize != NULL)
566 {
567 *NameSize = CmCopyKeyValueName(ValueCell, ValueName, *NameSize);
568 }
569
570 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
571
572 if (DataSize != NULL)
573 {
574 if ((Data != NULL) && (*DataSize != 0))
575 {
576 RtlCopyMemory(Data,
577 &ValueCell->Data,
578 min(*DataSize, ValueCell->DataLength));
579 }
580
581 *DataSize = ValueCell->DataLength;
582 }
583
584 TRACE("RegEnumValue done\n");
585 return STATUS_SUCCESS;
586 }
587
588 /* EOF */