xref: /reactos/boot/freeldr/freeldr/ntldr/registry.c (revision fa80176a)
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