1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2014 Timo Kreuzer <timo.kreuzer@reactos.org>
5 * 2022 George Bișoc <george.bisoc@reactos.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <freeldr.h>
23 #include <cmlib.h>
24 #include "registry.h"
25 #include <internal/cmboot.h>
26
27 #include <debug.h>
28 DBG_DEFAULT_CHANNEL(REGISTRY);
29
30 static PCMHIVE CmSystemHive;
31 static HCELL_INDEX SystemRootCell;
32
33 PHHIVE SystemHive = NULL;
34 HKEY CurrentControlSetKey = NULL;
35
36 #define HCI_TO_HKEY(CellIndex) ((HKEY)(ULONG_PTR)(CellIndex))
37 #ifndef HKEY_TO_HCI // See also registry.h
38 #define HKEY_TO_HCI(hKey) ((HCELL_INDEX)(ULONG_PTR)(hKey))
39 #endif
40
41 #define GET_HHIVE(CmHive) (&((CmHive)->Hive))
42 #define GET_HHIVE_FROM_HKEY(hKey) GET_HHIVE(CmSystemHive)
43 #define GET_CM_KEY_NODE(hHive, hKey) ((PCM_KEY_NODE)HvGetCell(hHive, HKEY_TO_HCI(hKey)))
44
45 #define GET_HBASE_BLOCK(ChunkBase) ((PHBASE_BLOCK)ChunkBase)
46
47 PVOID
48 NTAPI
CmpAllocate(IN SIZE_T Size,IN BOOLEAN Paged,IN ULONG Tag)49 CmpAllocate(
50 IN SIZE_T Size,
51 IN BOOLEAN Paged,
52 IN ULONG Tag)
53 {
54 UNREFERENCED_PARAMETER(Paged);
55 return FrLdrHeapAlloc(Size, Tag);
56 }
57
58 VOID
59 NTAPI
CmpFree(IN PVOID Ptr,IN ULONG Quota)60 CmpFree(
61 IN PVOID Ptr,
62 IN ULONG Quota)
63 {
64 UNREFERENCED_PARAMETER(Quota);
65 FrLdrHeapFree(Ptr, 0);
66 }
67
68 /**
69 * @brief
70 * Initializes a flat hive descriptor for the
71 * hive and validates the registry hive.
72 * Volatile data is purged during this procedure
73 * for initialization.
74 *
75 * @param[in] CmHive
76 * A pointer to a CM (in-memory) hive descriptor
77 * containing the hive descriptor to be initialized.
78 *
79 * @param[in] ChunkBase
80 * An arbitrary pointer that points to the registry
81 * chunk base. This pointer serves as the base block
82 * containing the hive file header data.
83 *
84 * @param[in] LoadAlternate
85 * If set to TRUE, the function will initialize the
86 * hive as an alternate hive, otherwise FALSE to initialize
87 * it as primary.
88 *
89 * @return
90 * Returns TRUE if the hive has been initialized
91 * and registry data inside the hive is valid, FALSE
92 * otherwise.
93 */
94 static
95 BOOLEAN
RegInitializeHive(_In_ PCMHIVE CmHive,_In_ PVOID ChunkBase,_In_ BOOLEAN LoadAlternate)96 RegInitializeHive(
97 _In_ PCMHIVE CmHive,
98 _In_ PVOID ChunkBase,
99 _In_ BOOLEAN LoadAlternate)
100 {
101 NTSTATUS Status;
102 /*
103 * FIXME: Disable compilation of some parts of code for AMD64 for now,
104 * since it makes the FreeLdr binary size so large that it prevents
105 * x64 ROS from booting.
106 */
107 #if !defined(_M_AMD64)
108 CM_CHECK_REGISTRY_STATUS CmStatusCode;
109 #endif
110
111 /* Initialize the hive */
112 Status = HvInitialize(GET_HHIVE(CmHive),
113 HINIT_FLAT, // HINIT_MEMORY_INPLACE
114 0,
115 LoadAlternate ? HFILE_TYPE_ALTERNATE : HFILE_TYPE_PRIMARY,
116 ChunkBase,
117 CmpAllocate,
118 CmpFree,
119 NULL,
120 NULL,
121 NULL,
122 NULL,
123 1,
124 NULL);
125 if (!NT_SUCCESS(Status))
126 {
127 ERR("Failed to initialize the flat hive (Status 0x%lx)\n", Status);
128 return FALSE;
129 }
130
131 /* FIXME: See the comment above */
132 #if !defined(_M_AMD64)
133 /* Now check the hive and purge volatile data */
134 CmStatusCode = CmCheckRegistry(CmHive, CM_CHECK_REGISTRY_BOOTLOADER_PURGE_VOLATILES | CM_CHECK_REGISTRY_VALIDATE_HIVE);
135 if (!CM_CHECK_REGISTRY_SUCCESS(CmStatusCode))
136 {
137 ERR("CmCheckRegistry detected problems with the loaded flat hive (check code %lu)\n", CmStatusCode);
138 return FALSE;
139 }
140 #endif
141
142 return TRUE;
143 }
144
145 /* FIXME: See the comment above */
146 #if !defined(_M_AMD64)
147 /**
148 * @brief
149 * Loads and reads a hive log at specified
150 * file offset.
151 *
152 * @param[in] DirectoryPath
153 * A pointer to a string that denotes the directory
154 * path of the hives and logs location.
155 *
156 * @param[in] LogFileOffset
157 * The file offset of which this function uses to
158 * seek at specific position during read.
159 *
160 * @param[in] LogName
161 * A pointer to a string that denotes the name of
162 * the desired hive log (e.g. "SYSTEM").
163 *
164 * @param[out] LogData
165 * A pointer to the returned hive log data that was
166 * read. The following data varies depending on the
167 * specified offset set up by the caller, that is used
168 * to where to start reading from the hive log.
169 *
170 * @return
171 * Returns TRUE if the hive log was loaded and read
172 * successfully, FALSE otherwise.
173 *
174 * @remarks
175 * The returned log data pointer to the caller is a
176 * virtual address. You must use VaToPa that converts
177 * the address to a physical one in order to actually
178 * use it!
179 */
180 static
181 BOOLEAN
RegLoadHiveLog(_In_ PCSTR DirectoryPath,_In_ ULONG LogFileOffset,_In_ PCSTR LogName,_Out_ PVOID * LogData)182 RegLoadHiveLog(
183 _In_ PCSTR DirectoryPath,
184 _In_ ULONG LogFileOffset,
185 _In_ PCSTR LogName,
186 _Out_ PVOID *LogData)
187 {
188 ARC_STATUS Status;
189 ULONG LogId;
190 CHAR LogPath[MAX_PATH];
191 ULONG LogFileSize;
192 FILEINFORMATION FileInfo;
193 LARGE_INTEGER Position;
194 ULONG BytesRead;
195 PVOID LogDataVirtual;
196 PVOID LogDataPhysical;
197
198 /* Build the full path to the hive log */
199 RtlStringCbCopyA(LogPath, sizeof(LogPath), DirectoryPath);
200 RtlStringCbCatA(LogPath, sizeof(LogPath), LogName);
201
202 /* Open the file */
203 Status = ArcOpen(LogPath, OpenReadOnly, &LogId);
204 if (Status != ESUCCESS)
205 {
206 ERR("Failed to open %s (ARC code %lu)\n", LogName, Status);
207 return FALSE;
208 }
209
210 /* Get the file length */
211 Status = ArcGetFileInformation(LogId, &FileInfo);
212 if (Status != ESUCCESS)
213 {
214 ERR("Failed to get file information from %s (ARC code %lu)\n", LogName, Status);
215 ArcClose(LogId);
216 return FALSE;
217 }
218
219 /* Capture the size of the hive log file */
220 LogFileSize = FileInfo.EndingAddress.LowPart;
221 if (LogFileSize == 0)
222 {
223 ERR("LogFileSize is 0, %s is corrupt\n", LogName);
224 ArcClose(LogId);
225 return FALSE;
226 }
227
228 /* Allocate memory blocks for our log data */
229 LogDataPhysical = MmAllocateMemoryWithType(
230 MM_SIZE_TO_PAGES(LogFileSize + MM_PAGE_SIZE - 1) << MM_PAGE_SHIFT,
231 LoaderRegistryData);
232 if (LogDataPhysical == NULL)
233 {
234 ERR("Failed to allocate memory for log data\n");
235 ArcClose(LogId);
236 return FALSE;
237 }
238
239 /* Convert the address to virtual so that it can be useable */
240 LogDataVirtual = PaToVa(LogDataPhysical);
241
242 /* Seek within the log file at desired position */
243 Position.QuadPart = LogFileOffset;
244 Status = ArcSeek(LogId, &Position, SeekAbsolute);
245 if (Status != ESUCCESS)
246 {
247 ERR("Failed to seek at %s (ARC code %lu)\n", LogName, Status);
248 ArcClose(LogId);
249 return FALSE;
250 }
251
252 /* And read the actual data from the log */
253 Status = ArcRead(LogId, LogDataPhysical, LogFileSize, &BytesRead);
254 if (Status != ESUCCESS)
255 {
256 ERR("Failed to read %s (ARC code %lu)\n", LogName, Status);
257 ArcClose(LogId);
258 return FALSE;
259 }
260
261 *LogData = LogDataVirtual;
262 ArcClose(LogId);
263 return TRUE;
264 }
265
266 /**
267 * @brief
268 * Recovers the header base block of a flat
269 * registry hive.
270 *
271 * @param[in] ChunkBase
272 * A pointer to the registry hive chunk base of
273 * which the damaged header block is to be recovered.
274 *
275 * @param[in] DirectoryPath
276 * A pointer to a string that denotes the directory
277 * path of the hives and logs location.
278 *
279 * @param[in] LogName
280 * A pointer to a string that denotes the name of
281 * the desired hive log (e.g. "SYSTEM").
282 *
283 * @return
284 * Returns TRUE if the header base block was successfully
285 * recovered, FALSE otherwise.
286 */
287 static
288 BOOLEAN
RegRecoverHeaderHive(_Inout_ PVOID ChunkBase,_In_ PCSTR DirectoryPath,_In_ PCSTR LogName)289 RegRecoverHeaderHive(
290 _Inout_ PVOID ChunkBase,
291 _In_ PCSTR DirectoryPath,
292 _In_ PCSTR LogName)
293 {
294 BOOLEAN Success;
295 CHAR FullLogFileName[MAX_PATH];
296 PVOID LogData;
297 PHBASE_BLOCK HiveBaseBlock;
298 PHBASE_BLOCK LogBaseBlock;
299
300 /* Build the complete path of the hive log */
301 RtlStringCbCopyA(FullLogFileName, sizeof(FullLogFileName), LogName);
302 RtlStringCbCatA(FullLogFileName, sizeof(FullLogFileName), ".LOG");
303 Success = RegLoadHiveLog(DirectoryPath, 0, FullLogFileName, &LogData);
304 if (!Success)
305 {
306 ERR("Failed to read the hive log\n");
307 return FALSE;
308 }
309
310 /* Make sure the header from the hive log is actually sane */
311 LogData = VaToPa(LogData);
312 LogBaseBlock = GET_HBASE_BLOCK(LogData);
313 if (!HvpVerifyHiveHeader(LogBaseBlock, HFILE_TYPE_LOG))
314 {
315 ERR("The hive log has corrupt base block\n");
316 return FALSE;
317 }
318
319 /* Copy the healthy header base block into the primary hive */
320 HiveBaseBlock = GET_HBASE_BLOCK(ChunkBase);
321 WARN("Recovering the hive base block...\n");
322 RtlCopyMemory(HiveBaseBlock,
323 LogBaseBlock,
324 LogBaseBlock->Cluster * HSECTOR_SIZE);
325 HiveBaseBlock->Type = HFILE_TYPE_PRIMARY;
326 return TRUE;
327 }
328
329 /**
330 * @brief
331 * Recovers the corrupt data of a primary flat
332 * registry hive.
333 *
334 * @param[in] ChunkBase
335 * A pointer to the registry hive chunk base of
336 * which the damaged hive data is to be replaced
337 * with healthy data from the corresponding hive log.
338 *
339 * @param[in] DirectoryPath
340 * A pointer to a string that denotes the directory
341 * path of the hives and logs location.
342 *
343 * @param[in] LogName
344 * A pointer to a string that denotes the name of
345 * the desired hive log (e.g. "SYSTEM").
346 *
347 * @return
348 * Returns TRUE if the hive data was successfully
349 * recovered, FALSE otherwise.
350 *
351 * @remarks
352 * Data recovery of the target hive does not always
353 * guarantee the primary hive is fully recovered.
354 * It could happen a block from a hive log is not
355 * marked dirty (pending to be written to disk) that
356 * has healthy data therefore the following bad block
357 * would still remain in corrupt state in the main primary
358 * hive. In such scenarios an alternate hive must be replayed.
359 */
360 static
361 BOOLEAN
RegRecoverDataHive(_Inout_ PVOID ChunkBase,_In_ PCSTR DirectoryPath,_In_ PCSTR LogName)362 RegRecoverDataHive(
363 _Inout_ PVOID ChunkBase,
364 _In_ PCSTR DirectoryPath,
365 _In_ PCSTR LogName)
366 {
367 BOOLEAN Success;
368 ULONG StorageLength;
369 ULONG BlockIndex, LogIndex;
370 PUCHAR BlockPtr, BlockDest;
371 CHAR FullLogFileName[MAX_PATH];
372 PVOID LogData;
373 PUCHAR LogDataPhysical;
374 PHBASE_BLOCK HiveBaseBlock;
375
376 /* Build the complete path of the hive log */
377 RtlStringCbCopyA(FullLogFileName, sizeof(FullLogFileName), LogName);
378 RtlStringCbCatA(FullLogFileName, sizeof(FullLogFileName), ".LOG");
379 Success = RegLoadHiveLog(DirectoryPath, HV_LOG_HEADER_SIZE, FullLogFileName, &LogData);
380 if (!Success)
381 {
382 ERR("Failed to read the hive log\n");
383 return FALSE;
384 }
385
386 /* Make sure the dirty vector signature is there otherwise the hive log is corrupt */
387 LogDataPhysical = (PUCHAR)VaToPa(LogData);
388 if (*((PULONG)LogDataPhysical) != HV_LOG_DIRTY_SIGNATURE)
389 {
390 ERR("The hive log dirty signature could not be found\n");
391 return FALSE;
392 }
393
394 /* Copy the dirty data into the primary hive */
395 LogIndex = 0;
396 BlockIndex = 0;
397 HiveBaseBlock = GET_HBASE_BLOCK(ChunkBase);
398 StorageLength = HiveBaseBlock->Length / HBLOCK_SIZE;
399 for (; BlockIndex < StorageLength; ++BlockIndex)
400 {
401 /* Skip this block if it's not dirty and go to the next one */
402 if (LogDataPhysical[BlockIndex + sizeof(HV_LOG_DIRTY_SIGNATURE)] != HV_LOG_DIRTY_BLOCK)
403 {
404 continue;
405 }
406
407 /* Read the dirty block and copy it at right offsets */
408 BlockPtr = (PUCHAR)((ULONG_PTR)LogDataPhysical + 2 * HSECTOR_SIZE + LogIndex * HBLOCK_SIZE);
409 BlockDest = (PUCHAR)((ULONG_PTR)ChunkBase + (BlockIndex + 1) * HBLOCK_SIZE);
410 RtlCopyMemory(BlockDest, BlockPtr, HBLOCK_SIZE);
411
412 /* Increment the index in log as we continue further */
413 LogIndex++;
414 }
415
416 /* Fix the secondary sequence of the primary hive and compute a new checksum */
417 HiveBaseBlock->Sequence2 = HiveBaseBlock->Sequence1;
418 HiveBaseBlock->CheckSum = HvpHiveHeaderChecksum(HiveBaseBlock);
419 return TRUE;
420 }
421 #endif
422
423 /**
424 * @brief
425 * Imports the SYSTEM binary hive from
426 * the registry base chunk that's been
427 * provided by the loader block.
428 *
429 * @param[in] ChunkBase
430 * A pointer to the registry base chunk
431 * that serves for SYSTEM hive initialization.
432 *
433 * @param[in] ChunkSize
434 * The size of the registry base chunk. This
435 * parameter refers to the actual size of
436 * the SYSTEM hive. This parameter is currently
437 * unused.
438 *
439 * @param[in] LoadAlternate
440 * If set to TRUE, the function will initialize the
441 * hive as an alternate hive, otherwise FALSE to initialize
442 * it as primary.
443 *
444 * @return
445 * Returns TRUE if hive importing and initialization
446 * have succeeded, FALSE otherwise.
447 */
448 BOOLEAN
RegImportBinaryHive(_In_ PVOID ChunkBase,_In_ ULONG ChunkSize,_In_ PCSTR SearchPath,_In_ BOOLEAN LoadAlternate)449 RegImportBinaryHive(
450 _In_ PVOID ChunkBase,
451 _In_ ULONG ChunkSize,
452 _In_ PCSTR SearchPath,
453 _In_ BOOLEAN LoadAlternate)
454 {
455 BOOLEAN Success;
456 PCM_KEY_NODE KeyNode;
457
458 TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize);
459
460 /* Assume that we don't need boot recover, unless we have to */
461 ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_NO_BOOT_RECOVER;
462
463 /* Allocate and initialize the hive */
464 CmSystemHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH');
465 Success = RegInitializeHive(CmSystemHive, ChunkBase, LoadAlternate);
466 if (!Success)
467 /* FIXME: See the comment above */
468 #if defined(_M_AMD64)
469 {
470 ERR("Corrupted hive %p!\n", ChunkBase);
471 FrLdrTempFree(CmSystemHive, 'eviH');
472 return FALSE;
473 }
474 #else
475 {
476 /* Free the buffer and retry again */
477 FrLdrTempFree(CmSystemHive, 'eviH');
478 CmSystemHive = NULL;
479
480 if (!RegRecoverHeaderHive(ChunkBase, SearchPath, "SYSTEM"))
481 {
482 ERR("Failed to recover the hive header block\n");
483 return FALSE;
484 }
485
486 if (!RegRecoverDataHive(ChunkBase, SearchPath, "SYSTEM"))
487 {
488 ERR("Failed to recover the hive data\n");
489 return FALSE;
490 }
491
492 /* Now retry initializing the hive again */
493 CmSystemHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH');
494 Success = RegInitializeHive(CmSystemHive, ChunkBase, LoadAlternate);
495 if (!Success)
496 {
497 ERR("Corrupted hive (despite recovery) %p\n", ChunkBase);
498 FrLdrTempFree(CmSystemHive, 'eviH');
499 return FALSE;
500 }
501
502 /*
503 * Acknowledge the kernel we recovered the SYSTEM hive
504 * on our side by applying log data.
505 */
506 ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_BOOT_RECOVERED_BY_HIVE_LOG;
507 }
508 #endif
509
510 /* Save the root key node */
511 SystemHive = GET_HHIVE(CmSystemHive);
512 SystemRootCell = SystemHive->BaseBlock->RootCell;
513 ASSERT(SystemRootCell != HCELL_NIL);
514
515 /* Verify it is accessible */
516 KeyNode = (PCM_KEY_NODE)HvGetCell(SystemHive, SystemRootCell);
517 ASSERT(KeyNode);
518 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
519 HvReleaseCell(SystemHive, SystemRootCell);
520
521 return TRUE;
522 }
523
524 BOOLEAN
RegInitCurrentControlSet(_In_ BOOLEAN LastKnownGood)525 RegInitCurrentControlSet(
526 _In_ BOOLEAN LastKnownGood)
527 {
528 UNICODE_STRING ControlSetName;
529 HCELL_INDEX ControlCell;
530 PCM_KEY_NODE KeyNode;
531 BOOLEAN AutoSelect;
532
533 TRACE("RegInitCurrentControlSet\n");
534
535 /* Choose which control set to open and set it as the new "Current" */
536 RtlInitUnicodeString(&ControlSetName,
537 LastKnownGood ? L"LastKnownGood"
538 : L"Default");
539
540 ControlCell = CmpFindControlSet(SystemHive,
541 SystemRootCell,
542 &ControlSetName,
543 &AutoSelect);
544 if (ControlCell == HCELL_NIL)
545 {
546 ERR("CmpFindControlSet('%wZ') failed\n", &ControlSetName);
547 return FALSE;
548 }
549
550 CurrentControlSetKey = HCI_TO_HKEY(ControlCell);
551
552 /* Verify it is accessible */
553 KeyNode = (PCM_KEY_NODE)HvGetCell(SystemHive, ControlCell);
554 ASSERT(KeyNode);
555 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
556 HvReleaseCell(SystemHive, ControlCell);
557
558 return TRUE;
559 }
560
561 static
562 BOOLEAN
GetNextPathElement(_Out_ PUNICODE_STRING NextElement,_Inout_ PUNICODE_STRING RemainingPath)563 GetNextPathElement(
564 _Out_ PUNICODE_STRING NextElement,
565 _Inout_ PUNICODE_STRING RemainingPath)
566 {
567 /* Check if there are any characters left */
568 if (RemainingPath->Length < sizeof(WCHAR))
569 {
570 /* Nothing left, bail out early */
571 return FALSE;
572 }
573
574 /* The next path elements starts with the remaining path */
575 NextElement->Buffer = RemainingPath->Buffer;
576
577 /* Loop until the path element ends */
578 while ((RemainingPath->Length >= sizeof(WCHAR)) &&
579 (RemainingPath->Buffer[0] != '\\'))
580 {
581 /* Skip this character */
582 RemainingPath->Buffer++;
583 RemainingPath->Length -= sizeof(WCHAR);
584 }
585
586 NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR);
587 NextElement->MaximumLength = NextElement->Length;
588
589 /* Check if the path element ended with a path separator */
590 if (RemainingPath->Length >= sizeof(WCHAR))
591 {
592 /* Skip the path separator */
593 ASSERT(RemainingPath->Buffer[0] == '\\');
594 RemainingPath->Buffer++;
595 RemainingPath->Length -= sizeof(WCHAR);
596 }
597
598 /* Return whether we got any characters */
599 return TRUE;
600 }
601
602 #if 0
603 LONG
604 RegEnumKey(
605 _In_ HKEY Key,
606 _In_ ULONG Index,
607 _Out_ PWCHAR Name,
608 _Inout_ PULONG NameSize,
609 _Out_opt_ PHKEY SubKey)
610 {
611 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key);
612 PCM_KEY_NODE KeyNode, SubKeyNode;
613 HCELL_INDEX CellIndex;
614 USHORT NameLength;
615
616 TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n",
617 Key, Index, Name, NameSize, NameSize ? *NameSize : 0);
618
619 /* Get the key node */
620 KeyNode = GET_CM_KEY_NODE(Hive, Key);
621 ASSERT(KeyNode);
622 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
623
624 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index);
625 if (CellIndex == HCELL_NIL)
626 {
627 TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n",
628 Index, KeyNode->NameLength, KeyNode->Name);
629 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
630 return ERROR_NO_MORE_ITEMS;
631 }
632 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
633
634 /* Get the value cell */
635 SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
636 ASSERT(SubKeyNode != NULL);
637 ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE);
638
639 if (SubKeyNode->Flags & KEY_COMP_NAME)
640 {
641 NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength);
642
643 /* Compressed name */
644 CmpCopyCompressedName(Name,
645 *NameSize,
646 SubKeyNode->Name,
647 SubKeyNode->NameLength);
648 }
649 else
650 {
651 NameLength = SubKeyNode->NameLength;
652
653 /* Normal name */
654 RtlCopyMemory(Name, SubKeyNode->Name,
655 min(*NameSize, SubKeyNode->NameLength));
656 }
657
658 if (*NameSize >= NameLength + sizeof(WCHAR))
659 {
660 Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
661 }
662
663 *NameSize = NameLength + sizeof(WCHAR);
664
665 HvReleaseCell(Hive, CellIndex);
666
667 if (SubKey != NULL)
668 *SubKey = HCI_TO_HKEY(CellIndex);
669
670 TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name);
671 return ERROR_SUCCESS;
672 }
673 #endif
674
675 LONG
RegOpenKey(_In_ HKEY ParentKey,_In_z_ PCWSTR KeyName,_Out_ PHKEY Key)676 RegOpenKey(
677 _In_ HKEY ParentKey,
678 _In_z_ PCWSTR KeyName,
679 _Out_ PHKEY Key)
680 {
681 UNICODE_STRING RemainingPath, SubKeyName;
682 UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet");
683 PHHIVE Hive = (ParentKey ? GET_HHIVE_FROM_HKEY(ParentKey) : GET_HHIVE(CmSystemHive));
684 PCM_KEY_NODE KeyNode;
685 HCELL_INDEX CellIndex;
686
687 TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key);
688
689 /* Initialize the remaining path name */
690 RtlInitUnicodeString(&RemainingPath, KeyName);
691
692 /* Check if we have a parent key */
693 if (ParentKey == NULL)
694 {
695 UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3;
696 UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry");
697 UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE");
698 UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM");
699
700 TRACE("RegOpenKey: absolute path\n");
701
702 if ((RemainingPath.Length < sizeof(WCHAR)) ||
703 RemainingPath.Buffer[0] != '\\')
704 {
705 /* The key path is not absolute */
706 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
707 return ERROR_PATH_NOT_FOUND;
708 }
709
710 /* Skip initial path separator */
711 RemainingPath.Buffer++;
712 RemainingPath.Length -= sizeof(WCHAR);
713
714 /* Get the first 3 path elements */
715 GetNextPathElement(&SubKeyName1, &RemainingPath);
716 GetNextPathElement(&SubKeyName2, &RemainingPath);
717 GetNextPathElement(&SubKeyName3, &RemainingPath);
718 TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3);
719
720 /* Check if we have the correct path */
721 if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) ||
722 !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) ||
723 !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE))
724 {
725 /* The key path is not inside HKLM\Machine\System */
726 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath);
727 return ERROR_PATH_NOT_FOUND;
728 }
729
730 /* Use the root key */
731 CellIndex = SystemRootCell;
732 }
733 else
734 {
735 /* Use the parent key */
736 CellIndex = HKEY_TO_HCI(ParentKey);
737 }
738
739 /* Check if this is the root key */
740 if (CellIndex == SystemRootCell)
741 {
742 UNICODE_STRING TempPath = RemainingPath;
743
744 /* Get the first path element */
745 GetNextPathElement(&SubKeyName, &TempPath);
746
747 /* Check if this is CurrentControlSet */
748 if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE))
749 {
750 /* Use the CurrentControlSetKey and update the remaining path */
751 CellIndex = HKEY_TO_HCI(CurrentControlSetKey);
752 RemainingPath = TempPath;
753 }
754 }
755
756 /* Get the key node */
757 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
758 ASSERT(KeyNode);
759 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
760
761 TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath);
762
763 /* Loop while there are path elements */
764 while (GetNextPathElement(&SubKeyName, &RemainingPath))
765 {
766 HCELL_INDEX NextCellIndex;
767
768 TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName);
769
770 /* Get the next sub key */
771 NextCellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName);
772 HvReleaseCell(Hive, CellIndex);
773 CellIndex = NextCellIndex;
774 if (CellIndex == HCELL_NIL)
775 {
776 WARN("Did not find sub key '%wZ' (full: %S)\n", &SubKeyName, KeyName);
777 return ERROR_PATH_NOT_FOUND;
778 }
779
780 /* Get the found key */
781 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
782 ASSERT(KeyNode);
783 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
784 }
785
786 HvReleaseCell(Hive, CellIndex);
787 *Key = HCI_TO_HKEY(CellIndex);
788
789 return ERROR_SUCCESS;
790 }
791
792 static
793 VOID
RepGetValueData(_In_ PHHIVE Hive,_In_ PCM_KEY_VALUE ValueCell,_Out_opt_ PULONG Type,_Out_opt_ PUCHAR Data,_Inout_opt_ PULONG DataSize)794 RepGetValueData(
795 _In_ PHHIVE Hive,
796 _In_ PCM_KEY_VALUE ValueCell,
797 _Out_opt_ PULONG Type,
798 _Out_opt_ PUCHAR Data,
799 _Inout_opt_ PULONG DataSize)
800 {
801 ULONG DataLength;
802 PVOID DataCell;
803
804 /* Does the caller want the type? */
805 if (Type != NULL)
806 *Type = ValueCell->Type;
807
808 /* Does the caller provide DataSize? */
809 if (DataSize != NULL)
810 {
811 // NOTE: CmpValueToData doesn't support big data (the function will
812 // bugcheck if so), FreeLdr is not supposed to read such data.
813 // If big data is needed, use instead CmpGetValueData.
814 // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...);
815 DataCell = CmpValueToData(Hive, ValueCell, &DataLength);
816
817 /* Does the caller want the data? */
818 if ((Data != NULL) && (*DataSize != 0))
819 {
820 RtlCopyMemory(Data,
821 DataCell,
822 min(*DataSize, DataLength));
823 }
824
825 /* Return the actual data length */
826 *DataSize = DataLength;
827 }
828 }
829
830 LONG
RegQueryValue(_In_ HKEY Key,_In_z_ PCWSTR ValueName,_Out_opt_ PULONG Type,_Out_opt_ PUCHAR Data,_Inout_opt_ PULONG DataSize)831 RegQueryValue(
832 _In_ HKEY Key,
833 _In_z_ PCWSTR ValueName,
834 _Out_opt_ PULONG Type,
835 _Out_opt_ PUCHAR Data,
836 _Inout_opt_ PULONG DataSize)
837 {
838 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key);
839 PCM_KEY_NODE KeyNode;
840 PCM_KEY_VALUE ValueCell;
841 HCELL_INDEX CellIndex;
842 UNICODE_STRING ValueNameString;
843
844 TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n",
845 Key, ValueName, Type, Data, DataSize);
846
847 /* Get the key node */
848 KeyNode = GET_CM_KEY_NODE(Hive, Key);
849 ASSERT(KeyNode);
850 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
851
852 /* Initialize value name string */
853 RtlInitUnicodeString(&ValueNameString, ValueName);
854 CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString);
855 if (CellIndex == HCELL_NIL)
856 {
857 TRACE("RegQueryValue value not found in key (%.*s)\n",
858 KeyNode->NameLength, KeyNode->Name);
859 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
860 return ERROR_FILE_NOT_FOUND;
861 }
862 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
863
864 /* Get the value cell */
865 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex);
866 ASSERT(ValueCell != NULL);
867
868 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
869
870 HvReleaseCell(Hive, CellIndex);
871
872 return ERROR_SUCCESS;
873 }
874
875 /*
876 * NOTE: This function is currently unused in FreeLdr; however it is kept here
877 * as an implementation reference of RegEnumValue using CMLIB that may be used
878 * elsewhere in ReactOS.
879 */
880 #if 0
881 LONG
882 RegEnumValue(
883 _In_ HKEY Key,
884 _In_ ULONG Index,
885 _Out_ PWCHAR ValueName,
886 _Inout_ PULONG NameSize,
887 _Out_opt_ PULONG Type,
888 _Out_opt_ PUCHAR Data,
889 _Inout_opt_ PULONG DataSize)
890 {
891 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key);
892 PCM_KEY_NODE KeyNode;
893 PCELL_DATA ValueListCell;
894 PCM_KEY_VALUE ValueCell;
895 USHORT NameLength;
896
897 TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n",
898 Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize);
899
900 /* Get the key node */
901 KeyNode = GET_CM_KEY_NODE(Hive, Key);
902 ASSERT(KeyNode);
903 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE);
904
905 /* Check if the index is valid */
906 if ((KeyNode->ValueList.Count == 0) ||
907 (KeyNode->ValueList.List == HCELL_NIL) ||
908 (Index >= KeyNode->ValueList.Count))
909 {
910 ERR("RegEnumValue: index invalid\n");
911 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
912 return ERROR_NO_MORE_ITEMS;
913 }
914
915 ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List);
916 ASSERT(ValueListCell != NULL);
917
918 /* Get the value cell */
919 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]);
920 ASSERT(ValueCell != NULL);
921 ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE);
922
923 if (ValueCell->Flags & VALUE_COMP_NAME)
924 {
925 NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength);
926
927 /* Compressed name */
928 CmpCopyCompressedName(ValueName,
929 *NameSize,
930 ValueCell->Name,
931 ValueCell->NameLength);
932 }
933 else
934 {
935 NameLength = ValueCell->NameLength;
936
937 /* Normal name */
938 RtlCopyMemory(ValueName, ValueCell->Name,
939 min(*NameSize, ValueCell->NameLength));
940 }
941
942 if (*NameSize >= NameLength + sizeof(WCHAR))
943 {
944 ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL;
945 }
946
947 *NameSize = NameLength + sizeof(WCHAR);
948
949 RepGetValueData(Hive, ValueCell, Type, Data, DataSize);
950
951 HvReleaseCell(Hive, ValueListCell->KeyList[Index]);
952 HvReleaseCell(Hive, KeyNode->ValueList.List);
953 HvReleaseCell(Hive, HKEY_TO_HCI(Key));
954
955 TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName);
956 return ERROR_SUCCESS;
957 }
958 #endif
959
960 /* EOF */
961