[CMLIB/NTOSKRNL]: Move key deletion functions to cmlib, so that UEFI boot library...
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Library
4 * FILE: boot/environ/lib/misc/bcd.c
5 * PURPOSE: Boot Library BCD Routines
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bl.h"
12 #include <bcd.h>
13
14 /* FUNCTIONS *****************************************************************/
15
16 VOID
17 BiNotifyEnumerationError (
18 _In_ HANDLE ObjectHandle,
19 _In_ PWCHAR ElementName,
20 _In_ NTSTATUS Status
21 )
22 {
23 /* Stub for now */
24 UNREFERENCED_PARAMETER(ObjectHandle);
25 UNREFERENCED_PARAMETER(ElementName);
26 UNREFERENCED_PARAMETER(Status);
27 EfiPrintf(L"Error in BiNotify\r\n");
28 }
29
30 ULONG
31 BiConvertElementFormatToValueType (
32 _In_ ULONG Format
33 )
34 {
35 /* Strings and objects are strings */
36 if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT))
37 {
38 return REG_SZ;
39 }
40
41 /* Object lists are arrays of strings */
42 if (Format == BCD_TYPE_OBJECT_LIST)
43 {
44 return REG_MULTI_SZ;
45 }
46
47 /* Everything else is binary */
48 return REG_BINARY;
49 }
50
51 NTSTATUS
52 BiConvertRegistryDataToElement (
53 _In_ HANDLE ObjectHandle,
54 _In_ PVOID Data,
55 _In_ ULONG DataLength,
56 _In_ BcdElementType ElementType,
57 _Out_ PVOID Element,
58 _Out_ PULONG ElementSize
59 )
60 {
61 NTSTATUS Status;
62 ULONG Length, Size, ReturnedLength;
63 PBL_DEVICE_DESCRIPTOR Device;
64 BOOLEAN NullTerminate;
65 PBCD_DEVICE_OPTION BcdDevice, ElementDevice;
66 PWCHAR BcdString, ElementString;
67 PGUID ElementGuid; UNICODE_STRING GuidString;
68 PULONGLONG ElementInteger;
69 PUSHORT ElementWord; PBOOLEAN BcdBoolean;
70
71 /* Assume failure */
72 ReturnedLength = 0;
73
74 /* Check what type of format we are dealing with */
75 switch (ElementType.Format)
76 {
77 /* Devices -- they are in a binary format */
78 case BCD_TYPE_DEVICE:
79
80 /* First, make sure it's at least big enough for an empty descriptor */
81 if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION,
82 DeviceDescriptor.Unknown))
83 {
84 return STATUS_OBJECT_TYPE_MISMATCH;
85 }
86
87 /* Both the registry and BCD format are the same */
88 BcdDevice = (PBCD_DEVICE_OPTION)Data;
89 ElementDevice = (PBCD_DEVICE_OPTION)Element;
90
91 /* Make sure the device fits in the registry data */
92 Device = &BcdDevice->DeviceDescriptor;
93 Size = Device->Size;
94 if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength)
95 {
96 return STATUS_OBJECT_TYPE_MISMATCH;
97 }
98
99 /* Check if this is a locate device */
100 if (Device->DeviceType == LocateDevice)
101 {
102 EfiPrintf(L"Locates not yet supported\r\n");
103 return STATUS_NOT_SUPPORTED;
104 }
105
106 /* Make sure the caller's buffer can fit the device */
107 ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry);
108 if (ReturnedLength > *ElementSize)
109 {
110 Status = STATUS_BUFFER_TOO_SMALL;
111 break;
112 }
113
114 /* It'll fit -- copy it in */
115 RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size);
116 ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry;
117 Status = STATUS_SUCCESS;
118 break;
119
120 /* Strings -- they are stored as is */
121 case BCD_TYPE_STRING:
122
123 /* Make sure the string isn't empty or misaligned */
124 if (!(DataLength) || (DataLength & 1))
125 {
126 return STATUS_OBJECT_TYPE_MISMATCH;
127 }
128
129 /* Both the registry and BCD format are the same */
130 BcdString = (PWCHAR)Data;
131 ElementString = (PWCHAR)Element;
132
133 /* We'll need as much data as the string has to offer */
134 ReturnedLength = DataLength;
135
136 /* If the string isn't NULL-terminated, do it now */
137 NullTerminate = FALSE;
138 if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL)
139 {
140 ReturnedLength += sizeof(UNICODE_NULL);
141 NullTerminate = TRUE;
142 }
143
144 /* Will we fit in the caller's buffer? */
145 if (ReturnedLength > *ElementSize)
146 {
147 Status = STATUS_BUFFER_TOO_SMALL;
148 break;
149 }
150
151 /* Yep -- copy it in, and NULL-terminate if needed */
152 RtlCopyMemory(Element, Data, DataLength);
153 if (NullTerminate)
154 {
155 ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL;
156 }
157
158 Status = STATUS_SUCCESS;
159 break;
160
161 /* Objects -- they are stored as GUID Strings */
162 case BCD_TYPE_OBJECT:
163
164 /* Registry data is a string, BCD data is a GUID */
165 BcdString = (PWCHAR)Data;
166 ElementGuid = (PGUID)Element;
167
168 /* We need a GUID-sized buffer, does the caller have one? */
169 ReturnedLength = sizeof(*ElementGuid);
170 if (*ElementSize < ReturnedLength)
171 {
172 Status = STATUS_BUFFER_TOO_SMALL;
173 break;
174 }
175
176 /* Yep, copy the GUID */
177 RtlInitUnicodeString(&GuidString, BcdString);
178 Status = RtlGUIDFromString(&GuidString, ElementGuid);
179 break;
180
181 /* Object Lists -- they are stored as arrays of GUID strings */
182 case BCD_TYPE_OBJECT_LIST:
183
184 /* Assume an empty list*/
185 ReturnedLength = 0;
186 Length = 0;
187 Status = STATUS_SUCCESS;
188
189 /* Registry data is an array of strings, BCD data is array of GUIDs */
190 BcdString = (PWCHAR)Data;
191 ElementGuid = (PGUID)Element;
192
193 /* Loop as long as the array still has strings */
194 while (*BcdString)
195 {
196 /* Don't read beyond the registry data */
197 if (Length >= DataLength)
198 {
199 break;
200 }
201
202 /* One more GUID -- does the caller have space? */
203 ReturnedLength += sizeof(GUID);
204 if (ReturnedLength <= *ElementSize)
205 {
206 /* Convert and add it in */
207 RtlInitUnicodeString(&GuidString, BcdString);
208 Status = RtlGUIDFromString(&GuidString, ElementGuid);
209 if (!NT_SUCCESS(Status))
210 {
211 break;
212 }
213
214 /* Move to the next GUID in the caller's buffer */
215 ElementGuid++;
216 }
217
218 /* Move to the next string in the registry array */
219 Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL);
220 Length += Size;
221 BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length);
222 }
223
224 /* Check if we failed anywhere */
225 if (!NT_SUCCESS(Status))
226 {
227 break;
228 }
229
230 /* Check if we consumed more space than we have */
231 if (ReturnedLength > *ElementSize)
232 {
233 Status = STATUS_BUFFER_TOO_SMALL;
234 }
235
236 /* All good here */
237 break;
238
239 /* Integer -- stored as binary */
240 case BCD_TYPE_INTEGER:
241
242 /* BCD data is a ULONGLONG, registry data is 8 bytes binary */
243 ElementInteger = (PULONGLONG)Element;
244 ReturnedLength = sizeof(*ElementInteger);
245
246 /* Make sure the registry data makes sense */
247 if (DataLength > ReturnedLength)
248 {
249 return STATUS_OBJECT_TYPE_MISMATCH;
250 }
251
252 /* Make sure the caller has space */
253 if (*ElementSize < ReturnedLength)
254 {
255 Status = STATUS_BUFFER_TOO_SMALL;
256 break;
257 }
258
259 /* Write the integer result */
260 *ElementInteger = 0;
261 RtlCopyMemory(ElementInteger, Data, DataLength);
262 Status = STATUS_SUCCESS;
263 break;
264
265 /* Boolean -- stored as binary */
266 case BCD_TYPE_BOOLEAN:
267
268 /* BCD data is a BOOLEAN, registry data is 2 bytes binary */
269 ElementWord = (PUSHORT)Element;
270 BcdBoolean = (PBOOLEAN)Data;
271 ReturnedLength = sizeof(ElementWord);
272
273 /* Make sure the registry data makes sense */
274 if (DataLength != sizeof(*BcdBoolean))
275 {
276 return STATUS_OBJECT_TYPE_MISMATCH;
277 }
278
279 /* Make sure the caller has space */
280 if (*ElementSize < ReturnedLength)
281 {
282 Status = STATUS_BUFFER_TOO_SMALL;
283 break;
284 }
285
286 /* Write the boolean result */
287 *ElementWord = 0;
288 *ElementWord = *BcdBoolean != 0;
289 Status = STATUS_SUCCESS;
290 break;
291
292 /* Integer list --stored as binary */
293 case BCD_TYPE_INTEGER_LIST:
294
295 /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */
296 ReturnedLength = DataLength;
297 if (!(DataLength) || (DataLength & 7))
298 {
299 return STATUS_OBJECT_TYPE_MISMATCH;
300 }
301
302 /* Make sure the caller has space */
303 if (*ElementSize < ReturnedLength)
304 {
305 Status = STATUS_BUFFER_TOO_SMALL;
306 break;
307 }
308
309 /* Write the integer list result */
310 RtlCopyMemory(Element, Data, DataLength);
311 Status = STATUS_SUCCESS;
312 break;
313
314 /* Arbitrary data */
315 default:
316
317 /* Registry data is copied binary as-is */
318 ReturnedLength = DataLength;
319
320 /* Make sure it's not empty */
321 if (!DataLength)
322 {
323 return STATUS_OBJECT_TYPE_MISMATCH;
324 }
325
326 /* Make sure the caller has space */
327 if (*ElementSize < ReturnedLength)
328 {
329 Status = STATUS_BUFFER_TOO_SMALL;
330 break;
331 }
332
333 /* Write the result */
334 RtlCopyMemory(Element, Data, DataLength);
335 Status = STATUS_SUCCESS;
336 break;
337 }
338
339 /* If we got here due to success or space issues, write the size */
340 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
341 {
342 *ElementSize = ReturnedLength;
343 }
344
345 /* All done, return our conversion result */
346 return Status;
347 }
348
349 NTSTATUS
350 BiConvertBcdElements (
351 _In_ PBCD_PACKED_ELEMENT Elements,
352 _Out_opt_ PBCD_ELEMENT Buffer,
353 _Inout_ PULONG BufferSize,
354 _Inout_ PULONG ElementCount
355 )
356 {
357 NTSTATUS Status;
358 ULONG ElementSize, AlignedElementSize, AlignedDataSize;
359 PBCD_ELEMENT_HEADER Header;
360 PVOID Data;
361 BOOLEAN Exists;
362 ULONG i, j, Count;
363
364 /* Local variable to keep track of objects */
365 Count = 0;
366
367 /* Safely compute the element bytes needed */
368 Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize);
369 if (!NT_SUCCESS(Status))
370 {
371 return Status;
372 }
373
374 /* Safely align the element size */
375 Status = RtlULongAdd(ElementSize,
376 sizeof(ULONG) - 1,
377 &AlignedElementSize);
378 if (!NT_SUCCESS(Status))
379 {
380 return Status;
381 }
382 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
383
384 /* Do a safe version of Add2Ptr to figure out where the headers will start */
385 Status = RtlULongPtrAdd((ULONG_PTR)Buffer,
386 AlignedElementSize,
387 (PULONG_PTR)&Header);
388 if (!NT_SUCCESS(Status))
389 {
390 return Status;
391 }
392
393 /* Safely compute the header bytes needed */
394 Status = RtlULongMult(*ElementCount,
395 sizeof(BCD_ELEMENT_HEADER),
396 &ElementSize);
397 if (!NT_SUCCESS(Status))
398 {
399 return Status;
400 }
401
402 /* Safely align the header size */
403 Status = RtlULongAdd(ElementSize,
404 AlignedElementSize + sizeof(ULONG) - 1,
405 &AlignedElementSize);
406 if (!NT_SUCCESS(Status))
407 {
408 return Status;
409 }
410 AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG);
411
412 /* Do a safe version of Add2Ptr */
413 Status = RtlULongPtrAdd((ULONG_PTR)Buffer,
414 AlignedElementSize,
415 (PULONG_PTR)&Data);
416 if (!NT_SUCCESS(Status))
417 {
418 return Status;
419 }
420
421 /* Iterate over every element */
422 for (i = 0; i < *ElementCount; i++)
423 {
424 /* Safely align the element size */
425 Status = RtlULongAdd(Elements->Size,
426 sizeof(ULONG) - 1,
427 &AlignedDataSize);
428 if (!NT_SUCCESS(Status))
429 {
430 break;
431 }
432 AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG);
433
434 /* Safely add the size of this data element */
435 Status = RtlULongAdd(AlignedElementSize,
436 AlignedDataSize,
437 &AlignedElementSize);
438 if (!NT_SUCCESS(Status))
439 {
440 break;
441 }
442
443 /* Do we have enough space left? */
444 if (*BufferSize >= AlignedElementSize)
445 {
446 /* Check if our root is an inherited object */
447 Exists = FALSE;
448 if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects)
449 {
450 /* Yes, scan for us in the current buffer */
451 for (j = 0; j < Count; j++)
452 {
453 /* Do we already exist? */
454 while (Buffer[j].Header->Type == Elements->RootType.PackedValue)
455 {
456 /* Yep */
457 Exists = TRUE;
458 break;
459 }
460 }
461 }
462
463 /* Have we already found ourselves? */
464 if (!Exists)
465 {
466 /* Nope, one more entry */
467 ++Count;
468
469 /* Write out the unpacked object */
470 Buffer->Body = Data;
471 Buffer->Header = Header;
472
473 /* Fill out its header */
474 Header->Size = Elements->Size;
475 Header->Type = Elements->Type;
476 Header->Version = Elements->Version;
477
478 /* And copy the data */
479 RtlCopyMemory(Data, Elements->Data, Header->Size);
480
481 /* Move to the next unpacked object and header */
482 ++Buffer;
483 ++Header;
484
485 /* Move to the next data entry */
486 Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize);
487 }
488 }
489 else
490 {
491 /* Nope, set failure code, but keep going so we can return count */
492 Status = STATUS_BUFFER_TOO_SMALL;
493 }
494
495 /* Move to the next element entry */
496 Elements = Elements->NextEntry;
497 }
498
499 /* Return the new final buffer size and count */
500 *BufferSize = AlignedElementSize;
501 *ElementCount = Count;
502 return Status;
503 }
504
505 NTSTATUS
506 BcdOpenObject (
507 _In_ HANDLE BcdHandle,
508 _In_ PGUID ObjectId,
509 _Out_ PHANDLE ObjectHandle
510 )
511 {
512 NTSTATUS Status;
513 GUID LocalGuid;
514 UNICODE_STRING GuidString;
515 HANDLE RootObjectHandle;
516
517 /* Assume failure */
518 *ObjectHandle = NULL;
519
520 /* Initialize GUID string */
521 GuidString.Buffer = NULL;
522
523 /* Open the root "Objects" handle */
524 RootObjectHandle = NULL;
525 Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle);
526 if (!NT_SUCCESS(Status))
527 {
528 goto Quickie;
529 }
530
531 /* Capture the object ID and convert it into a string */
532 LocalGuid = *ObjectId;
533 Status = RtlStringFromGUID(&LocalGuid, &GuidString);
534 if (!NT_SUCCESS(Status))
535 {
536 goto Quickie;
537 }
538
539 /* Now open the key containing this object ID */
540 Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle);
541
542 Quickie:
543 /* Free the GUID string if we had one allocated */
544 if (GuidString.Buffer)
545 {
546 RtlFreeUnicodeString(&GuidString);
547 }
548
549 /* Close the root handle if it was open */
550 if (RootObjectHandle)
551 {
552 BiCloseKey(RootObjectHandle);
553 }
554
555 /* Return the final status */
556 return Status;
557 }
558
559 NTSTATUS
560 BcdDeleteElement (
561 _In_ HANDLE ObjectHandle,
562 _In_ ULONG Type
563 )
564 {
565 NTSTATUS Status;
566 HANDLE ElementsHandle, ElementHandle;
567 WCHAR TypeString[22];
568
569 /* Open the elements key */
570 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
571 if (NT_SUCCESS(Status))
572 {
573 /* Convert the element ID into a string */
574 if (!_ultow(Type, TypeString, 16))
575 {
576 /* Failed to do so */
577 Status = STATUS_UNSUCCESSFUL;
578 }
579 else
580 {
581 /* Open the element specifically */
582 Status = BiOpenKey(ElementHandle, TypeString, &ElementHandle);
583 if (NT_SUCCESS(Status))
584 {
585 /* Delete it */
586 Status = BiDeleteKey(ElementHandle);
587 if (NT_SUCCESS(Status))
588 {
589 /* No point in closing the handle anymore */
590 ElementHandle = NULL;
591 }
592 }
593 else
594 {
595 /* The element doesn't exist */
596 Status = STATUS_NOT_FOUND;
597 }
598
599 /* Check if we should close the key */
600 if (ElementHandle)
601 {
602 /* Do it */
603 BiCloseKey(ElementHandle);
604 }
605 }
606 }
607
608 /* Check if we should close the elements handle */
609 if (ElementsHandle)
610 {
611 /* Do it */
612 BiCloseKey(ElementsHandle);
613 }
614
615 /* Return whatever the result was */
616 return Status;
617 }
618
619 NTSTATUS
620 BiEnumerateSubElements (
621 _In_ HANDLE BcdHandle,
622 _In_ PVOID Object,
623 _In_ ULONG ElementType,
624 _In_ ULONG Flags,
625 _Out_opt_ PBCD_PACKED_ELEMENT* Elements,
626 _Inout_ PULONG ElementSize,
627 _Out_ PULONG ElementCount
628 )
629 {
630 NTSTATUS Status;
631 PBCD_PACKED_ELEMENT Element;
632 HANDLE ObjectHandle;
633 ULONG ParsedElements, RequiredSize;
634
635 /* Assume empty */
636 *ElementCount = 0;
637 RequiredSize = 0;
638 ParsedElements = 0;
639
640 /* Open the object */
641 Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle);
642 if (!NT_SUCCESS(Status))
643 {
644 goto Quickie;
645 }
646
647 /* Read the first entry, and the size available */
648 Element = *Elements;
649 RequiredSize = *ElementSize;
650
651 /* Enumerate the object into the element array */
652 Status = BiEnumerateElements(BcdHandle,
653 ObjectHandle,
654 ElementType,
655 Flags,
656 Element,
657 &RequiredSize,
658 &ParsedElements);
659
660 /* Close the handle and bail out if we couldn't enumerate */
661 BiCloseKey(ObjectHandle);
662 if (!NT_SUCCESS(Status))
663 {
664 goto Quickie;
665 }
666
667 /* Check if the and subelements were present */
668 if (ParsedElements)
669 {
670 /* Keep going until the last one */
671 while (Element->NextEntry)
672 {
673 Element = Element->NextEntry;
674 }
675
676 /* Set the new buffer location to the last element */
677 *Elements = Element;
678 }
679
680 Quickie:
681 /* Return the number of sub-elements and their size */
682 *ElementCount = ParsedElements;
683 *ElementSize = RequiredSize;
684 return Status;
685 }
686
687 NTSTATUS
688 BiEnumerateSubObjectElements (
689 _In_ HANDLE BcdHandle,
690 _Out_ PGUID SubObjectList,
691 _In_ ULONG SubObjectCount,
692 _In_ ULONG Flags,
693 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
694 _Inout_ PULONG ElementSize,
695 _Out_ PULONG ElementCount
696 )
697 {
698 NTSTATUS Status;
699 ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i;
700 PBCD_PACKED_ELEMENT PreviousElement;
701
702 /* Assume empty list */
703 *ElementCount = 0;
704 Status = STATUS_SUCCESS;
705
706 /* Initialize variables */
707 TotalSize = 0;
708 PreviousElement = NULL;
709
710 /* Set the currently remaining size based on caller's input */
711 CurrentSize = *ElementSize;
712
713 /* Iterate over every subje object */
714 for (i = 0; i < SubObjectCount; i++)
715 {
716 /* Set the currently remaining buffer space */
717 RequiredSize = CurrentSize;
718
719 /* Enumerate the inherited sub elements */
720 Status = BiEnumerateSubElements(BcdHandle,
721 &SubObjectList[i],
722 BcdLibraryObjectList_InheritedObjects,
723 Flags,
724 &Elements,
725 &RequiredSize,
726 &SubElementCount);
727 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
728 {
729 /* Safely add the length of the sub elements */
730 Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize);
731 if (!NT_SUCCESS(Status))
732 {
733 break;
734 }
735
736 /* Add the sub elements to the total */
737 *ElementCount += SubElementCount;
738
739 /* See if we have enough space*/
740 if (*ElementSize >= TotalSize)
741 {
742 /* Were there any subelements? */
743 if (SubElementCount)
744 {
745 /* Update to keep track of these new subelements */
746 CurrentSize = *ElementSize - TotalSize;
747
748 /* Link the subelements into the chain */
749 PreviousElement = Elements;
750 PreviousElement->NextEntry =
751 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize);
752 Elements = PreviousElement->NextEntry;
753 }
754 }
755 else
756 {
757 /* We're out of space */
758 CurrentSize = 0;
759 }
760 }
761 else if ((Status != STATUS_NOT_FOUND) &&
762 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
763 {
764 /* Some other fatal error, break out */
765 break;
766 }
767 else
768 {
769 /* The sub element was not found, print a warning but keep going */
770 BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
771 (&SubObjectList[i])->Data1,
772 (&SubObjectList[i])->Data2,
773 (&SubObjectList[i])->Data3,
774 (&SubObjectList[i])->Data4[0],
775 (&SubObjectList[i])->Data4[1],
776 (&SubObjectList[i])->Data4[2],
777 (&SubObjectList[i])->Data4[3],
778 (&SubObjectList[i])->Data4[4],
779 (&SubObjectList[i])->Data4[5],
780 (&SubObjectList[i])->Data4[6],
781 (&SubObjectList[i])->Data4[7],
782 (&SubObjectList[i])->Data4[8]);
783 Status = STATUS_SUCCESS;
784 }
785 }
786
787 /* Terminate the last element, if one was left */
788 if (PreviousElement)
789 {
790 PreviousElement->NextEntry = NULL;
791 }
792
793 /* Set failure code if we ran out of space */
794 if (*ElementSize < TotalSize)
795 {
796 Status = STATUS_BUFFER_TOO_SMALL;
797 }
798
799 /* Return final length and status */
800 *ElementSize = TotalSize;
801 return Status;
802 }
803
804 NTSTATUS
805 BiEnumerateElements (
806 _In_ HANDLE BcdHandle,
807 _In_ HANDLE ObjectHandle,
808 _In_ ULONG RootElementType,
809 _In_ ULONG Flags,
810 _Out_opt_ PBCD_PACKED_ELEMENT Elements,
811 _Inout_ PULONG ElementSize,
812 _Out_ PULONG ElementCount
813 )
814 {
815 HANDLE ElementsHandle, ElementHandle;
816 ULONG TotalLength, RegistryElementDataLength, RemainingLength;
817 NTSTATUS Status;
818 ULONG i;
819 PVOID ElementData, SubObjectList, RegistryElementData;
820 BcdElementType ElementType;
821 PBCD_PACKED_ELEMENT PreviousElement;
822 ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength;
823 PWCHAR ElementName;
824 PWCHAR* SubKeys;
825
826 /* Assume failure */
827 *ElementCount = 0;
828
829 /* Initialize all locals that are checked at the end*/
830 SubKeys = NULL;
831 ElementsHandle = NULL;
832 ElementHandle = NULL;
833 ElementData = NULL;
834 RegistryElementData = NULL;
835 PreviousElement = NULL;
836 ElementName = NULL;
837 SubObjectList = NULL;
838 TotalLength = 0;
839 ElementDataLength = 0;
840 SubObjectCount = 0;
841 RemainingLength = 0;
842
843 /* Open the root object key's elements */
844 Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle);
845 if (!NT_SUCCESS(Status))
846 {
847 goto Quickie;
848 }
849
850 /* Enumerate all elements */
851 Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount);
852 if (!NT_SUCCESS(Status))
853 {
854 goto Quickie;
855 }
856
857 /* Iterate over each one */
858 for (i = 0; i < SubKeyCount; i++)
859 {
860 /* Open the element */
861 ElementName = SubKeys[i];
862 Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle);
863 if (!NT_SUCCESS(Status))
864 {
865 break;
866 }
867
868 /* The name of the element is its data type */
869 ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16);
870 if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1))
871 {
872 EfiPrintf(L"Value invald\r\n");
873 BiCloseKey(ElementHandle);
874 ElementHandle = 0;
875 continue;
876 }
877
878 /* Read the appropriate registry value type for this element */
879 Status = BiGetRegistryValue(ElementHandle,
880 L"Element",
881 NULL,
882 BiConvertElementFormatToValueType(
883 ElementType.Format),
884 &RegistryElementData,
885 &RegistryElementDataLength);
886 if (!NT_SUCCESS(Status))
887 {
888 break;
889 }
890
891 /* Now figure out how much space the converted element will need */
892 ElementDataLength = 0;
893 Status = BiConvertRegistryDataToElement(ObjectHandle,
894 RegistryElementData,
895 RegistryElementDataLength,
896 ElementType,
897 NULL,
898 &ElementDataLength);
899 if (Status != STATUS_BUFFER_TOO_SMALL)
900 {
901 break;
902 }
903
904 /* Allocate a buffer big enough for the converted element */
905 ElementData = BlMmAllocateHeap(ElementDataLength);
906 if (!ElementData)
907 {
908 Status = STATUS_INSUFFICIENT_RESOURCES;
909 break;
910 }
911
912 /* And actually convert it this time around */
913 Status = BiConvertRegistryDataToElement(ObjectHandle,
914 RegistryElementData,
915 RegistryElementDataLength,
916 ElementType,
917 ElementData,
918 &ElementDataLength);
919 if (!NT_SUCCESS(Status))
920 {
921 break;
922 }
923
924 /* Safely add space for the packed element header */
925 Status = RtlULongAdd(TotalLength,
926 FIELD_OFFSET(BCD_PACKED_ELEMENT, Data),
927 &TotalLength);
928 if (!NT_SUCCESS(Status))
929 {
930 break;
931 }
932
933 /* Safely add space for the data of the element itself */
934 Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength);
935 if (!NT_SUCCESS(Status))
936 {
937 break;
938 }
939
940 /* One more element */
941 ++*ElementCount;
942
943 /* See how much space we were given */
944 RemainingLength = *ElementSize;
945 if (RemainingLength >= TotalLength)
946 {
947 /* Set the next pointer */
948 Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalLength);
949
950 /* Fill this one out */
951 Elements->RootType.PackedValue = RootElementType;
952 Elements->Version = 1;
953 Elements->Type = ElementType.PackedValue;
954 Elements->Size = ElementDataLength;
955
956 /* Add the data */
957 RtlCopyMemory(Elements->Data, ElementData, ElementDataLength);
958 RemainingLength -= TotalLength;
959
960 /* Move to the next element on the next pass */
961 PreviousElement = Elements;
962 Elements = Elements->NextEntry;
963 }
964 else
965 {
966 /* We're out of space */
967 RemainingLength = 0;
968 }
969
970 /* Are we enumerating devices, and is this a device? */
971 if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) &&
972 (ElementType.Format == BCD_TYPE_DEVICE))
973 {
974 /* Yep, so go inside to enumerate it */
975 Status = BiEnumerateSubElements(BcdHandle,
976 ElementData,
977 ElementType.PackedValue,
978 Flags,
979 &Elements,
980 &ElementDataLength,
981 &SubElementCount);
982 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
983 {
984 /* Safely add the length of the sub elements */
985 Status = RtlULongAdd(TotalLength,
986 ElementDataLength,
987 &TotalLength);
988 if (!NT_SUCCESS(Status))
989 {
990 break;
991 }
992
993 /* Add the sub elements to the total */
994 *ElementCount += SubElementCount;
995
996 /* See if we have enough space*/
997 if (*ElementSize >= TotalLength)
998 {
999 /* Were there any subelements? */
1000 if (SubElementCount)
1001 {
1002 /* Update to keep track of these new subelements */
1003 ElementDataLength = *ElementSize - TotalLength;
1004
1005 /* Link the subelements into the chain */
1006 PreviousElement = Elements;
1007 PreviousElement->NextEntry =
1008 (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements +
1009 TotalLength);
1010 Elements = PreviousElement->NextEntry;
1011 }
1012 }
1013 else
1014 {
1015 /* We're out of space */
1016 ElementDataLength = 0;
1017 }
1018 }
1019 else if ((Status != STATUS_NOT_FOUND) &&
1020 (Status != STATUS_OBJECT_NAME_NOT_FOUND))
1021 {
1022 /* Fatal error trying to read the data, so fail */
1023 break;
1024 }
1025 }
1026 else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) &&
1027 (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects))
1028 {
1029 /* Inherited objects are requsted, so allocate a buffer for them */
1030 SubObjectList = BlMmAllocateHeap(ElementDataLength);
1031 if (!SubObjectList)
1032 {
1033 Status = STATUS_INSUFFICIENT_RESOURCES;
1034 break;
1035 }
1036
1037 /* Copy the elements into the list. They are arrays of GUIDs */
1038 RtlCopyMemory(SubObjectList, ElementData, ElementDataLength);
1039 SubObjectCount = ElementDataLength / sizeof(GUID);
1040 }
1041
1042 /* Free our local buffers */
1043 BlMmFreeHeap(ElementData);
1044 BlMmFreeHeap(RegistryElementData);
1045 ElementData = NULL;
1046 RegistryElementData = NULL;
1047
1048 /* Close the key */
1049 BiCloseKey(ElementHandle);
1050 ElementHandle = NULL;
1051 ElementName = NULL;
1052 }
1053
1054 /* Did we end up here with a sub object list after successful loop parsing? */
1055 if ((i != 0) && (i == SubKeyCount) && (SubObjectList))
1056 {
1057 /* We will actually enumerate it now, at the end */
1058 Status = BiEnumerateSubObjectElements(BcdHandle,
1059 SubObjectList,
1060 SubObjectCount,
1061 Flags,
1062 Elements,
1063 &RemainingLength,
1064 &SubElementCount);
1065 if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL))
1066 {
1067 /* Safely add the length of the sub elements */
1068 Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength);
1069 if ((NT_SUCCESS(Status)) && (SubElementCount))
1070 {
1071 /* Add the sub elements to the total */
1072 *ElementCount += SubElementCount;
1073
1074 /* Don't touch PreviousElement anymore */
1075 PreviousElement = NULL;
1076 }
1077 }
1078 }
1079
1080 Quickie:
1081 /* Free the sub object list, if any */
1082 if (SubObjectList)
1083 {
1084 BlMmFreeHeap(SubObjectList);
1085 }
1086
1087 /* Free any local element data */
1088 if (ElementData)
1089 {
1090 BlMmFreeHeap(ElementData);
1091 }
1092
1093 /* Free any local registry data */
1094 if (RegistryElementData)
1095 {
1096 BlMmFreeHeap(RegistryElementData);
1097 }
1098
1099 /* Close the handle if still opened */
1100 if (ElementHandle)
1101 {
1102 BiCloseKey(ElementHandle);
1103 }
1104
1105 /* Terminate the last element, if any */
1106 if (PreviousElement)
1107 {
1108 PreviousElement->NextEntry = NULL;
1109 }
1110
1111 /* Close the root handle if still opened */
1112 if (ElementsHandle)
1113 {
1114 BiCloseKey(ElementsHandle);
1115 }
1116
1117 /* Set failure code if out of space */
1118 if (*ElementSize < TotalLength)
1119 {
1120 Status = STATUS_BUFFER_TOO_SMALL;
1121 }
1122
1123 /* Other errors will send a notification error */
1124 if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL))
1125 {
1126 BiNotifyEnumerationError(ObjectHandle, ElementName, Status);
1127 }
1128
1129 /* Finally free the subkeys array */
1130 if (SubKeys)
1131 {
1132 BlMmFreeHeap(SubKeys);
1133 }
1134
1135 /* And return the required, final length and status */
1136 *ElementSize = TotalLength;
1137 return Status;
1138 }
1139
1140 NTSTATUS
1141 BiAddStoreFromFile (
1142 _In_ PBL_FILE_PATH_DESCRIPTOR FilePath,
1143 _Out_ PHANDLE StoreHandle
1144 )
1145 {
1146 NTSTATUS Status;
1147 HANDLE HiveHandle, KeyHandle;
1148
1149 /* Load the specified hive */
1150 Status = BiLoadHive(FilePath, &HiveHandle);
1151 if (!NT_SUCCESS(Status))
1152 {
1153 return Status;
1154 }
1155
1156 /* Open the description key to make sure this is really a BCD */
1157 Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle);
1158 if (NT_SUCCESS(Status))
1159 {
1160 /* It is -- close the key as we don't need it */
1161 BiCloseKey(KeyHandle);
1162 *StoreHandle = HiveHandle;
1163 }
1164 else
1165 {
1166 /* Failure, drop a reference on the hive and close the key */
1167 BiDereferenceHive(HiveHandle);
1168 BiCloseKey(HiveHandle);
1169 }
1170
1171 /* Return the status */
1172 return Status;
1173 }
1174
1175 NTSTATUS
1176 BcdEnumerateAndUnpackElements (
1177 _In_ HANDLE BcdHandle,
1178 _In_ HANDLE ObjectHandle,
1179 _Out_opt_ PBCD_ELEMENT Elements,
1180 _Inout_ PULONG ElementSize,
1181 _Out_ PULONG ElementCount
1182 )
1183 {
1184 PVOID LocalElements;
1185 NTSTATUS Status;
1186 ULONG LocalElementCount, LocalElementSize;
1187
1188 /* Make sure required parameters are there */
1189 if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize)))
1190 {
1191 return STATUS_INVALID_PARAMETER;
1192 }
1193
1194 /* Set initial count to zero */
1195 *ElementCount = 0;
1196
1197 /* Do the initial enumeration to figure out the size required */
1198 LocalElementSize = 0;
1199 LocalElementCount = 0;
1200 Status = BiEnumerateElements(BcdHandle,
1201 ObjectHandle,
1202 0,
1203 BCD_ENUMERATE_FLAG_IN_ORDER |
1204 BCD_ENUMERATE_FLAG_DEVICES |
1205 BCD_ENUMERATE_FLAG_DEEP,
1206 NULL,
1207 &LocalElementSize,
1208 &LocalElementCount);
1209 if (Status != STATUS_BUFFER_TOO_SMALL)
1210 {
1211 return Status;
1212 }
1213
1214 /* Now allocate a buffer large enough to hold them */
1215 LocalElements = BlMmAllocateHeap(LocalElementSize);
1216 if (!LocalElements)
1217 {
1218 return STATUS_INSUFFICIENT_RESOURCES;
1219 }
1220
1221 /* Zero out the array and do the real enumeration this time around */
1222 RtlZeroMemory(LocalElements, LocalElementSize);
1223 Status = BiEnumerateElements(BcdHandle,
1224 ObjectHandle,
1225 0,
1226 BCD_ENUMERATE_FLAG_IN_ORDER |
1227 BCD_ENUMERATE_FLAG_DEVICES |
1228 BCD_ENUMERATE_FLAG_DEEP,
1229 LocalElements,
1230 &LocalElementSize,
1231 &LocalElementCount);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 return Status;
1235 }
1236
1237 /* Now we know the real count */
1238 *ElementCount = LocalElementCount;
1239
1240 /* Now unpack the data */
1241 Status = BiConvertBcdElements(LocalElements,
1242 Elements,
1243 ElementSize,
1244 &LocalElementCount);
1245 if (NT_SUCCESS(Status))
1246 {
1247 /* Not all elements may have been converted */
1248 *ElementCount = LocalElementCount;
1249 }
1250
1251 /* Free the local (unpacked) buffer and return status */
1252 BlMmFreeHeap(LocalElements);
1253 return Status;
1254 }
1255
1256 NTSTATUS
1257 BcdOpenStoreFromFile (
1258 _In_ PUNICODE_STRING FileName,
1259 _In_ PHANDLE BcdHandle
1260 )
1261 {
1262 ULONG Length;
1263 PBL_FILE_PATH_DESCRIPTOR FilePath;
1264 NTSTATUS Status;
1265 HANDLE LocalHandle;
1266
1267 /* Assume failure */
1268 LocalHandle = NULL;
1269
1270 /* Allocate a path descriptor */
1271 Length = FileName->Length + sizeof(*FilePath);
1272 FilePath = BlMmAllocateHeap(Length);
1273 if (!FilePath)
1274 {
1275 return STATUS_NO_MEMORY;
1276 }
1277
1278 /* Initialize it */
1279 FilePath->Version = 1;
1280 FilePath->PathType = InternalPath;
1281 FilePath->Length = Length;
1282
1283 /* Copy the name and NULL-terminate it */
1284 RtlCopyMemory(FilePath->Path, FileName->Buffer, Length);
1285 FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL;
1286
1287 /* Open the BCD */
1288 Status = BiAddStoreFromFile(FilePath, &LocalHandle);
1289 if (NT_SUCCESS(Status))
1290 {
1291 /* Return the handle on success */
1292 *BcdHandle = LocalHandle;
1293 }
1294
1295 /* Free the descriptor and return the status */
1296 BlMmFreeHeap(FilePath);
1297 return Status;
1298 }
1299