[FORMATTING] Remove trailing whitespace. Addendum to 34593d93.
[reactos.git] / boot / environ / lib / misc / bootreg.c
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 = 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 }