xref: /reactos/boot/environ/lib/misc/bootreg.c (revision bbccad0e)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/misc/bootreg.c
5  * PURPOSE:         Boot Library Boot Registry Wrapper for CMLIB
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 #include <bcd.h>
13 
14 /* DEFINITIONS ***************************************************************/
15 
16 #define BI_FLUSH_HIVE       0x01
17 #define BI_HIVE_WRITEABLE   0x02
18 
19 /* DATA STRUCTURES ***********************************************************/
20 
21 typedef struct _BI_KEY_HIVE
22 {
23     PHBASE_BLOCK BaseBlock;
24     ULONG HiveSize;
25     PBL_FILE_PATH_DESCRIPTOR FilePath;
26     CMHIVE Hive;
27     LONG ReferenceCount;
28     ULONG Flags;
29     PCM_KEY_NODE RootNode;
30 } BI_KEY_HIVE, *PBI_KEY_HIVE;
31 
32 typedef struct _BI_KEY_OBJECT
33 {
34     PBI_KEY_HIVE KeyHive;
35     PCM_KEY_NODE KeyNode;
36     HCELL_INDEX KeyCell;
37     PWCHAR KeyName;
38 } BI_KEY_OBJECT, *PBI_KEY_OBJECT;
39 
40 /* GLOBALS *******************************************************************/
41 
42 BOOLEAN BiHiveHashLibraryInitialized;
43 ULONGLONG HvSymcryptSeed;
44 
45 /* FUNCTIONS *****************************************************************/
46 
47 BOOLEAN
48 HvIsInPlaceBaseBlockValid (
49     _In_ PHBASE_BLOCK BaseBlock
50     )
51 {
52     ULONG HiveLength, HeaderSum;
53     BOOLEAN Valid;
54 
55     /* Assume failure */
56     Valid = FALSE;
57 
58     /* Check for incorrect signature, type, version, or format */
59     if ((BaseBlock->Signature == 'fger') &&
60         (BaseBlock->Type == 0) &&
61         (BaseBlock->Major <= 1) &&
62         (BaseBlock->Minor <= 5) &&
63         (BaseBlock->Minor >= 3) &&
64         (BaseBlock->Format == 1))
65     {
66         /* Check for invalid hive size */
67         HiveLength = BaseBlock->Length;
68         if (HiveLength)
69         {
70             /* Check for misaligned or too large hive size */
71             if (!(HiveLength & 0xFFF) && HiveLength <= 0x7FFFE000)
72             {
73                 /* Check for invalid header checksum */
74                 HeaderSum = HvpHiveHeaderChecksum(BaseBlock);
75                 if (HeaderSum == BaseBlock->CheckSum)
76                 {
77                     /* All good */
78                     Valid = TRUE;
79                 }
80             }
81         }
82     }
83 
84     /* Return validity */
85     return Valid;
86 }
87 
88 PVOID
89 NTAPI
90 CmpAllocate (
91     _In_ SIZE_T Size,
92     _In_ BOOLEAN Paged,
93     _In_ ULONG Tag
94     )
95 {
96     UNREFERENCED_PARAMETER(Paged);
97     UNREFERENCED_PARAMETER(Tag);
98 
99     /* Call the heap allocator */
100     return BlMmAllocateHeap(Size);
101 }
102 
103 VOID
104 NTAPI
105 CmpFree (
106     _In_ PVOID Ptr,
107     _In_ ULONG Quota
108     )
109 {
110     UNREFERENCED_PARAMETER(Quota);
111 
112     /* Call the heap allocator */
113     BlMmFreeHeap(Ptr);
114 }
115 
116 VOID
117 BiDereferenceHive (
118     _In_ HANDLE KeyHandle
119     )
120 {
121     PBI_KEY_OBJECT KeyObject;
122 
123     /* Get the key object */
124     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
125 
126     /* Drop a reference on the parent hive */
127     --KeyObject->KeyHive->ReferenceCount;
128 }
129 
130 VOID
131 BiFlushHive (
132     _In_ HANDLE KeyHandle
133     )
134 {
135     /* Not yet implemented */
136     EfiPrintf(L"NO reg flush\r\n");
137     return;
138 }
139 
140 VOID
141 BiCloseKey (
142     _In_ HANDLE KeyHandle
143     )
144 {
145     PBI_KEY_HIVE KeyHive;
146     PBI_KEY_OBJECT KeyObject;
147 
148     /* Get the key object and hive */
149     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
150     KeyHive = KeyObject->KeyHive;
151 
152     /* Check if we have a hive, or name, or key node */
153     if ((KeyHive) || (KeyObject->KeyNode) || (KeyObject->KeyName))
154     {
155         /* Drop a reference, see if it's the last one */
156         BiDereferenceHive(KeyHandle);
157         if (!KeyHive->ReferenceCount)
158         {
159             /* Check if we should flush it */
160             if (KeyHive->Flags & BI_FLUSH_HIVE)
161             {
162                 BiFlushHive(KeyHandle);
163             }
164 
165             /* Unmap the hive */
166             MmPapFreePages(KeyHive->BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
167 
168             /* Free the hive and hive path */
169             BlMmFreeHeap(KeyHive->FilePath);
170             BlMmFreeHeap(KeyHive);
171         }
172 
173         /* Check if a key name is present */
174         if (KeyObject->KeyName)
175         {
176             /* Free it */
177             BlMmFreeHeap(KeyObject->KeyName);
178         }
179     }
180 
181     /* Free the object */
182     BlMmFreeHeap(KeyObject);
183 }
184 
185 NTSTATUS
186 BiOpenKey(
187     _In_ HANDLE ParentHandle,
188     _In_ PWCHAR KeyName,
189     _Out_ PHANDLE Handle
190     )
191 {
192     PBI_KEY_OBJECT ParentKey, NewKey;
193     PBI_KEY_HIVE ParentHive;
194     NTSTATUS Status;
195     SIZE_T NameLength, SubNameLength, NameBytes;
196     PWCHAR NameStart, NameBuffer;
197     UNICODE_STRING KeyString;
198     HCELL_INDEX KeyCell;
199     PHHIVE Hive;
200     PCM_KEY_NODE ParentNode;
201 
202     /* Convert from a handle to our key object */
203     ParentKey = (PBI_KEY_OBJECT)ParentHandle;
204 
205     /* Extract the hive and node information */
206     ParentHive = ParentKey->KeyHive;
207     ParentNode = ParentKey->KeyNode;
208     Hive = &ParentKey->KeyHive->Hive.Hive;
209 
210     /* Initialize variables */
211     KeyCell = HCELL_NIL;
212     Status = STATUS_SUCCESS;
213     NameBuffer = NULL;
214 
215     /* Loop as long as there's still portions of the key name in play */
216     NameLength = wcslen(KeyName);
217     while (NameLength)
218     {
219         /* Find the first path separator */
220         NameStart = wcschr(KeyName, OBJ_NAME_PATH_SEPARATOR);
221         if (NameStart)
222         {
223             /* Look only at the key before the separator */
224             SubNameLength = NameStart - KeyName;
225             ++NameStart;
226         }
227         else
228         {
229             /* No path separator, this is the final leaf key */
230             SubNameLength = NameLength;
231         }
232 
233         /* Free the name buffer from the previous pass if needed */
234         if (NameBuffer)
235         {
236             BlMmFreeHeap(NameBuffer);
237         }
238 
239         /* Allocate a buffer to hold the name of this specific subkey only */
240         NameBytes = SubNameLength * sizeof(WCHAR);
241         NameBuffer = BlMmAllocateHeap(NameBytes + sizeof(UNICODE_NULL));
242         if (!NameBuffer)
243         {
244             Status = STATUS_NO_MEMORY;
245             goto Quickie;
246         }
247 
248         /* Copy and null-terminate the name of the subkey */
249         RtlCopyMemory(NameBuffer, KeyName, NameBytes);
250         NameBuffer[SubNameLength] = UNICODE_NULL;
251 
252         /* Convert it into a UNICODE_STRING and try to find it */
253         RtlInitUnicodeString(&KeyString, NameBuffer);
254         KeyCell = CmpFindSubKeyByName(Hive, ParentNode, &KeyString);
255         if (KeyCell == HCELL_NIL)
256         {
257             Status = STATUS_OBJECT_NAME_NOT_FOUND;
258             goto Quickie;
259         }
260 
261         /* We found it -- get the key node out of it */
262         ParentNode = (PCM_KEY_NODE)HvGetCell(Hive, KeyCell);
263         if (!ParentNode)
264         {
265             Status = STATUS_REGISTRY_CORRUPT;
266             goto Quickie;
267         }
268 
269         /* Update the key name to the next remaining path element */
270         KeyName = NameStart;
271         if (NameStart)
272         {
273             /* Update the length to the remainder of the path */
274             NameLength += -1 - SubNameLength;
275         }
276         else
277         {
278             /* There's nothing left, this was the leaf key */
279             NameLength = 0;
280         }
281     }
282 
283     /* Allocate a key object */
284     NewKey = BlMmAllocateHeap(sizeof(*NewKey));
285     if (!NewKey)
286     {
287         /* Bail out if we had no memory for it */
288         Status = STATUS_NO_MEMORY;
289         goto Quickie;
290     }
291 
292     /* Fill out the key object data */
293     NewKey->KeyNode = ParentNode;
294     NewKey->KeyHive = ParentHive;
295     NewKey->KeyName = NameBuffer;
296     NewKey->KeyCell = KeyCell;
297 
298     /* Add a reference to the hive */
299     ++ParentHive->ReferenceCount;
300 
301     /* Return the object back to the caller */
302     *Handle = NewKey;
303 
304 Quickie:
305     /* If we had a name buffer, free it */
306     if (NameBuffer)
307     {
308         BlMmFreeHeap(NameBuffer);
309     }
310 
311     /* Return status of the open operation */
312     return Status;
313 }
314 
315 NTSTATUS
316 BiInitializeAndValidateHive (
317     _In_ PBI_KEY_HIVE Hive
318     )
319 {
320     ULONG HiveSize;
321     NTSTATUS Status;
322 
323     /* Make sure the hive is at least the size of a base block */
324     if (Hive->HiveSize < sizeof(HBASE_BLOCK))
325     {
326         return STATUS_REGISTRY_CORRUPT;
327     }
328 
329     /* Make sure that the base block accurately describes the size of the hive */
330     HiveSize = Hive->BaseBlock->Length + sizeof(HBASE_BLOCK);
331     if ((HiveSize < sizeof(HBASE_BLOCK)) || (HiveSize > Hive->HiveSize))
332     {
333         return STATUS_REGISTRY_CORRUPT;
334     }
335 
336     /* Initialize a flat memory hive */
337     RtlZeroMemory(&Hive->Hive, sizeof(Hive->Hive));
338     Status = HvInitialize(&Hive->Hive.Hive,
339                           HINIT_FLAT,
340                           0,
341                           0,
342                           Hive->BaseBlock,
343                           CmpAllocate,
344                           CmpFree,
345                           NULL,
346                           NULL,
347                           NULL,
348                           NULL,
349                           0,
350                           NULL);
351     if (NT_SUCCESS(Status))
352     {
353         /* Cleanup volatile/old data */
354         CmPrepareHive(&Hive->Hive.Hive); // CmCheckRegistry
355         Status = STATUS_SUCCESS;
356     }
357 
358     /* Return the final status */
359     return Status;
360 }
361 
362 NTSTATUS
363 BiLoadHive (
364     _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
365     _Out_ PHANDLE HiveHandle
366     )
367 {
368     ULONG DeviceId;
369     PHBASE_BLOCK BaseBlock, NewBaseBlock;
370     PBI_KEY_OBJECT KeyObject;
371     PBI_KEY_HIVE BcdHive;
372     PBL_DEVICE_DESCRIPTOR BcdDevice;
373     ULONG PathLength, DeviceLength, HiveSize, HiveLength, NewHiveSize;
374     PWCHAR HiveName, LogName;
375     BOOLEAN HaveWriteAccess;
376     NTSTATUS Status;
377     PVOID LogData;
378     PHHIVE Hive;
379     UNICODE_STRING KeyString;
380     PCM_KEY_NODE RootNode;
381     HCELL_INDEX CellIndex;
382 
383     /* Initialize variables */
384     DeviceId = -1;
385     BaseBlock = NULL;
386     BcdHive = NULL;
387     KeyObject = NULL;
388     LogData = NULL;
389     LogName = NULL;
390 
391     /* Initialize the crypto seed */
392     if (!BiHiveHashLibraryInitialized)
393     {
394         HvSymcryptSeed = 0x82EF4D887A4E55C5;
395         BiHiveHashLibraryInitialized = TRUE;
396     }
397 
398     /* Extract and validate the input path */
399     BcdDevice = (PBL_DEVICE_DESCRIPTOR)&FilePath->Path;
400     PathLength = FilePath->Length;
401     DeviceLength = BcdDevice->Size;
402     HiveName = (PWCHAR)((ULONG_PTR)BcdDevice + BcdDevice->Size);
403     if (PathLength <= DeviceLength)
404     {
405         /* Doesn't make sense, bail out */
406         Status = STATUS_INVALID_PARAMETER;
407         goto Quickie;
408     }
409 
410     /* Attempt to open the underlying device for RW access */
411     HaveWriteAccess = TRUE;
412     Status = BlpDeviceOpen(BcdDevice,
413                            BL_DEVICE_READ_ACCESS | BL_DEVICE_WRITE_ACCESS,
414                            0,
415                            &DeviceId);
416     if (!NT_SUCCESS(Status))
417     {
418         /* Try for RO access instead */
419         HaveWriteAccess = FALSE;
420         Status = BlpDeviceOpen(BcdDevice, BL_DEVICE_READ_ACCESS, 0, &DeviceId);
421         if (!NT_SUCCESS(Status))
422         {
423             /* No access at all -- bail out */
424             goto Quickie;
425         }
426     }
427 
428     /* Now try to load the hive on disk */
429     Status = BlImgLoadImageWithProgress2(DeviceId,
430                                          BlLoaderRegistry,
431                                          HiveName,
432                                          (PVOID*)&BaseBlock,
433                                          &HiveSize,
434                                          0,
435                                          FALSE,
436                                          NULL,
437                                          NULL);
438     if (!NT_SUCCESS(Status))
439     {
440         EfiPrintf(L"Hive read failure: % lx\r\n", Status);
441         goto Quickie;
442     }
443 
444     /* Allocate a hive structure */
445     BcdHive = BlMmAllocateHeap(sizeof(*BcdHive));
446     if (!BcdHive)
447     {
448         Status = STATUS_NO_MEMORY;
449         goto Quickie;
450     }
451 
452     /* Initialize it */
453     RtlZeroMemory(BcdHive, sizeof(*BcdHive));
454     BcdHive->BaseBlock = BaseBlock;
455     BcdHive->HiveSize = HiveSize;
456     if (HaveWriteAccess)
457     {
458         BcdHive->Flags |= BI_HIVE_WRITEABLE;
459     }
460 
461     /* Make sure the hive was at least one bin long */
462     if (HiveSize < sizeof(*BaseBlock))
463     {
464         Status = STATUS_REGISTRY_CORRUPT;
465         goto Quickie;
466     }
467 
468     /* Make sure the hive contents are at least one bin long */
469     HiveLength = BaseBlock->Length;
470     if (BaseBlock->Length < sizeof(*BaseBlock))
471     {
472         Status = STATUS_REGISTRY_CORRUPT;
473         goto Quickie;
474     }
475 
476     /* Validate the initial bin (the base block) */
477     if (!HvIsInPlaceBaseBlockValid(BaseBlock))
478     {
479         EfiPrintf(L"Recovery not implemented\r\n");
480         Status = STATUS_REGISTRY_CORRUPT;
481         goto Quickie;
482     }
483 
484     /* Check if there's log recovery that needs to happen */
485     if (BaseBlock->Sequence1 != BaseBlock->Sequence2)
486     {
487         EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
488         Status = STATUS_REGISTRY_CORRUPT;
489         goto Quickie;
490     }
491 
492     /*
493      * Check if the whole hive doesn't fit in the buffer.
494      * Note: HiveLength does not include the size of the baseblock itself
495      */
496     if (HiveSize < (HiveLength + sizeof(*BaseBlock)))
497     {
498         EfiPrintf(L"Need bigger hive buffer path\r\n");
499 
500         /* Allocate a slightly bigger buffer */
501         NewHiveSize = HiveLength + sizeof(*BaseBlock);
502         Status = MmPapAllocatePagesInRange((PVOID*)&NewBaseBlock,
503                                            BlLoaderRegistry,
504                                            NewHiveSize >> PAGE_SHIFT,
505                                            0,
506                                            0,
507                                            NULL,
508                                            0);
509         if (!NT_SUCCESS(Status))
510         {
511             goto Quickie;
512         }
513 
514         /* Copy the current data in there */
515         RtlCopyMemory(NewBaseBlock, BaseBlock, HiveSize);
516 
517         /* Free the old data */
518         MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
519 
520         /* Update our pointers */
521         BaseBlock = NewBaseBlock;
522         HiveSize = NewHiveSize;
523         BcdHive->BaseBlock = BaseBlock;
524         BcdHive->HiveSize = HiveSize;
525     }
526 
527     /* Check if any log stuff needs to happen */
528     if (LogData)
529     {
530         EfiPrintf(L"Log fix not implemented: %lx %lx\r\n");
531         Status = STATUS_REGISTRY_CORRUPT;
532         goto Quickie;
533     }
534 
535     /* Call Hv to setup the hive library */
536     Status = BiInitializeAndValidateHive(BcdHive);
537     if (!NT_SUCCESS(Status))
538     {
539         goto Quickie;
540     }
541 
542     /* Now get the root node */
543     Hive = &BcdHive->Hive.Hive;
544     RootNode = (PCM_KEY_NODE)HvGetCell(Hive, Hive->BaseBlock->RootCell);
545     if (!RootNode)
546     {
547         Status = STATUS_OBJECT_NAME_NOT_FOUND;
548         goto Quickie;
549     }
550 
551     /* Find the Objects subkey under it to see if it's a real BCD hive */
552     RtlInitUnicodeString(&KeyString, L"Objects");
553     CellIndex = CmpFindSubKeyByName(Hive, RootNode, &KeyString);
554     if (CellIndex == HCELL_NIL)
555     {
556         EfiPrintf(L"No OBJECTS subkey found!\r\n");
557         Status = STATUS_OBJECT_NAME_NOT_FOUND;
558         goto Quickie;
559     }
560 
561     /* This is a valid BCD hive, store its root node here */
562     BcdHive->RootNode = RootNode;
563 
564     /* Allocate a copy of the file path */
565     BcdHive->FilePath = BlMmAllocateHeap(FilePath->Length);
566     if (!BcdHive->FilePath)
567     {
568         Status = STATUS_NO_MEMORY;
569         goto Quickie;
570     }
571 
572     /* Make a copy of it */
573     RtlCopyMemory(BcdHive->FilePath, FilePath, FilePath->Length);
574 
575     /* Create a key object to describe the rot */
576     KeyObject = BlMmAllocateHeap(sizeof(*KeyObject));
577     if (!KeyObject)
578     {
579         Status = STATUS_NO_MEMORY;
580         goto Quickie;
581     }
582 
583     /* Fill out the details */
584     KeyObject->KeyNode = RootNode;
585     KeyObject->KeyHive = BcdHive;
586     KeyObject->KeyName = NULL;
587     KeyObject->KeyCell = Hive->BaseBlock->RootCell;
588 
589     /* One reference for the key object, plus one lifetime reference */
590     BcdHive->ReferenceCount = 2;
591 
592     /* This is the hive handle */
593     *HiveHandle  = KeyObject;
594 
595     /* We're all good */
596     Status = STATUS_SUCCESS;
597 
598 Quickie:
599     /* If we had a log name, free it */
600     if (LogName)
601     {
602         BlMmFreeHeap(LogName);
603     }
604 
605     /* If we had logging data, free it */
606     if (LogData)
607     {
608         MmPapFreePages(LogData, BL_MM_INCLUDE_MAPPED_ALLOCATED);
609     }
610 
611     /* Check if this is the failure path */
612     if (!NT_SUCCESS(Status))
613     {
614         /* If we mapped the hive, free it */
615         if (BaseBlock)
616         {
617             MmPapFreePages(BaseBlock, BL_MM_INCLUDE_MAPPED_ALLOCATED);
618         }
619 
620         /* If we opened the device, close it */
621         if (DeviceId != -1)
622         {
623             BlDeviceClose(DeviceId);
624         }
625 
626         /* Did we create a hive object? */
627         if (BcdHive)
628         {
629             /* Free the file path if we made a copy of it */
630             if (BcdHive->FilePath)
631             {
632                 BlMmFreeHeap(BcdHive->FilePath);
633             }
634 
635             /* Free the hive itself */
636             BlMmFreeHeap(BcdHive);
637         }
638 
639         /* Finally, free the root key object if we created one */
640         if (KeyObject)
641         {
642             BlMmFreeHeap(KeyObject);
643         }
644     }
645 
646     /* Return the final status */
647     return Status;
648 }
649 
650 NTSTATUS
651 BiGetRegistryValue (
652     _In_ HANDLE KeyHandle,
653     _In_ PWCHAR ValueName,
654     _In_ ULONG Type,
655     _Out_ PVOID* Buffer,
656     _Out_ PULONG ValueLength
657     )
658 {
659     PCM_KEY_NODE KeyNode;
660     PHHIVE KeyHive;
661     UNICODE_STRING ValueString;
662     PBI_KEY_OBJECT KeyObject;
663     PCM_KEY_VALUE KeyValue;
664     PVOID ValueCopy;
665     ULONG Size;
666     HCELL_INDEX CellIndex;
667     PCELL_DATA ValueData;
668 
669     /* Get the key object, node,and hive */
670     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
671     KeyNode = KeyObject->KeyNode;
672     KeyHive = &KeyObject->KeyHive->Hive.Hive;
673 
674     /* Find the value cell index in the list of values */
675     RtlInitUnicodeString(&ValueString, ValueName);
676     CmpFindNameInList(KeyHive,
677                       &KeyNode->ValueList,
678                       &ValueString,
679                       NULL,
680                       &CellIndex);
681     if (CellIndex == HCELL_NIL)
682     {
683         return STATUS_OBJECT_NAME_NOT_FOUND;
684     }
685 
686     /* Get the cell data for it */
687     KeyValue = (PCM_KEY_VALUE)HvGetCell(KeyHive, CellIndex);
688     if (!KeyValue)
689     {
690         return STATUS_REGISTRY_CORRUPT;
691     }
692 
693     /* Make sure the type matches */
694     if (KeyValue->Type != Type)
695     {
696         return STATUS_OBJECT_TYPE_MISMATCH;
697     }
698 
699     /* Now get the data cell */
700     ValueData = CmpValueToData(KeyHive, KeyValue, &Size);
701 
702     /* Make a copy of it */
703     ValueCopy = BlMmAllocateHeap(Size);
704     if (!ValueCopy)
705     {
706         return STATUS_NO_MEMORY;
707     }
708 
709     /* Copy it in the buffer, and return it and its size */
710     RtlCopyMemory(ValueCopy, ValueData, Size);
711     *Buffer = ValueCopy;
712     *ValueLength = Size;
713     return STATUS_SUCCESS;
714 }
715 
716 NTSTATUS
717 BiEnumerateSubKeys (
718     _In_ HANDLE KeyHandle,
719     _Out_ PWCHAR** SubKeyList,
720     _Out_ PULONG SubKeyCount
721     )
722 {
723     PCM_KEY_NODE KeyNode, Node;
724     PBI_KEY_OBJECT KeyObject;
725     ULONG KeyCount;
726     ULONG NameLength, NewTotalNameLength, FinalLength, TotalNameLength;
727     PHHIVE Hive;
728     PWCHAR KeyName, NameEnd;
729     HCELL_INDEX CellIndex;
730     PWCHAR* SubKeys;
731     NTSTATUS Status;
732     ULONG i;
733 
734     /* Get the key object, node, and hive */
735     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
736     KeyNode = KeyObject->KeyNode;
737     Hive = &KeyObject->KeyHive->Hive.Hive;
738 
739     /* Assume it's empty */
740     *SubKeyList = 0;
741     *SubKeyCount = 0;
742 
743     /* Initialize locals */
744     KeyCount = 0;
745     SubKeys = 0;
746     TotalNameLength = 0;
747 
748     /* Find the first subkey cell index */
749     CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount);
750     while (CellIndex != HCELL_NIL)
751     {
752         /* Move to the next one */
753         KeyCount++;
754 
755         /* Get the cell data for it */
756         Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
757         if (!Node)
758         {
759             return STATUS_REGISTRY_CORRUPT;
760         }
761 
762         /* Check if the value is compressed */
763         if (Node->Flags & KEY_COMP_NAME)
764         {
765             /* Get the compressed name size */
766             NameLength = CmpCompressedNameSize(Node->Name, Node->NameLength);
767         }
768         else
769         {
770             /* Get the real size */
771             NameLength = Node->NameLength;
772         }
773 
774         /* Add up the new length, protecting against overflow */
775         NewTotalNameLength = TotalNameLength + NameLength + sizeof(UNICODE_NULL);
776         if (NewTotalNameLength < TotalNameLength)
777         {
778             Status = STATUS_NAME_TOO_LONG;
779             goto Quickie;
780         }
781 
782         /* We're good, use the new length */
783         TotalNameLength = NewTotalNameLength;
784 
785         /* Find the next subkey cell index */
786         CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, KeyCount);
787     }
788 
789     /* Were there no keys? We're done, if so */
790     if (!KeyCount)
791     {
792         return STATUS_SUCCESS;
793     }
794 
795     /* Safely compute the size of the array needed */
796     Status = RtlULongLongToULong(sizeof(PWCHAR) * KeyCount, &FinalLength);
797     if (!NT_SUCCESS(Status))
798     {
799         goto Quickie;
800     }
801 
802     /* Safely add that to the name length */
803     Status = RtlULongAdd(TotalNameLength, FinalLength, &FinalLength);
804     if (!NT_SUCCESS(Status))
805     {
806         goto Quickie;
807     }
808 
809     /* Allocate an array big enough for the names and pointers */
810     SubKeys = BlMmAllocateHeap(FinalLength);
811     if (!SubKeys)
812     {
813         Status = STATUS_NO_MEMORY;
814         goto Quickie;
815     }
816 
817     /* Go over each key again */
818     NameEnd = (PWCHAR)&SubKeys[KeyCount];
819     for (i = 0; i < KeyCount; i++)
820     {
821         /* Get the cell index for this subkey */
822         CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, i);
823         if (CellIndex == HCELL_NIL)
824         {
825             break;
826         }
827 
828         /* Get the cell data for it */
829         Node = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex);
830         if (!Node)
831         {
832             Status = STATUS_REGISTRY_CORRUPT;
833             goto Quickie;
834         }
835 
836         /* Check if the value is compressed */
837         KeyName = Node->Name;
838         if (Node->Flags & KEY_COMP_NAME)
839         {
840             /* Get the compressed name size */
841             NameLength = CmpCompressedNameSize(KeyName, Node->NameLength);
842             CmpCopyCompressedName(NameEnd, NameLength, KeyName, Node->NameLength);
843         }
844         else
845         {
846             /* Get the real size */
847             NameLength = Node->NameLength;
848             RtlCopyMemory(NameEnd, KeyName, NameLength);
849         }
850 
851         /* Move the name buffer to the next spot, and NULL-terminate */
852         SubKeys[i] = NameEnd;
853         NameEnd += (NameLength / sizeof(WCHAR));
854         *NameEnd = UNICODE_NULL;
855 
856         /* Keep going */
857         NameEnd++;
858     }
859 
860     /* Check if the subkeys were empty */
861     if (i == 0)
862     {
863         /* They disappeared in the middle of enumeration */
864         Status = STATUS_OBJECT_NAME_NOT_FOUND;
865         goto Quickie;
866     }
867 
868     /* Return the count and the array of names */
869     *SubKeyList = SubKeys;
870     *SubKeyCount = i;
871     SubKeys = NULL;
872     Status = STATUS_SUCCESS;
873 
874 Quickie:
875     /* On the failure path, free the subkeys if any exist */
876     if (SubKeys)
877     {
878         BlMmFreeHeap(SubKeys);
879     }
880 
881     /* All done, return the result */
882     return Status;
883 }
884 
885 NTSTATUS
886 BiDeleteKey (
887     _In_ HANDLE KeyHandle
888     )
889 {
890     NTSTATUS Status;
891     PBI_KEY_OBJECT KeyObject;
892     PHHIVE Hive;
893     ULONG SubKeyCount, i;
894     PWCHAR* SubKeyList;
895     HANDLE SubKeyHandle;
896 
897     /* Get the key object and hive */
898     KeyObject = (PBI_KEY_OBJECT)KeyHandle;
899     Hive = &KeyObject->KeyHive->Hive.Hive;
900 
901     /* Make sure the hive is writeable */
902     if (!(KeyObject->KeyHive->Flags & BI_HIVE_WRITEABLE))
903     {
904         return STATUS_MEDIA_WRITE_PROTECTED;
905     }
906 
907     /* Enumerate all of the subkeys */
908     Status = BiEnumerateSubKeys(KeyHandle, &SubKeyList, &SubKeyCount);
909     if ((NT_SUCCESS(Status)) && (SubKeyCount > 0))
910     {
911         /* Loop through each one */
912         for (i = 0; i < SubKeyCount; i++)
913         {
914             /* Open a handle to it */
915             Status = BiOpenKey(KeyHandle, SubKeyList[i], &SubKeyHandle);
916             if (NT_SUCCESS(Status))
917             {
918                 /* Recursively call us to delete it */
919                 Status = BiDeleteKey(SubKeyHandle);
920                 if (Status != STATUS_SUCCESS)
921                 {
922                     /* Close the key on failure */
923                     BiCloseKey(SubKeyHandle);
924                 }
925             }
926         }
927     }
928 
929     /* Check if we had a list of subkeys */
930     if (SubKeyList)
931     {
932         /* Free it */
933         BlMmFreeHeap(SubKeyList);
934     }
935 
936     /* Delete this key cell */
937     Status = CmpFreeKeyByCell(Hive, KeyObject->KeyCell, TRUE);
938     if (NT_SUCCESS(Status))
939     {
940         /* Mark the hive as requiring a flush */
941         KeyObject->KeyHive->Flags |= BI_FLUSH_HIVE;
942         BiCloseKey(KeyHandle);
943     }
944 
945     /* All done */
946     return Status;
947 }