/* * COPYRIGHT: See COPYING.ARM in the top level directory * PROJECT: ReactOS UEFI Boot Library * FILE: boot/environ/lib/misc/bcd.c * PURPOSE: Boot Library BCD Routines * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org) */ /* INCLUDES ******************************************************************/ #include "bl.h" #include /* FUNCTIONS *****************************************************************/ VOID BiNotifyEnumerationError ( _In_ HANDLE ObjectHandle, _In_ PWCHAR ElementName, _In_ NTSTATUS Status ) { /* Stub for now */ UNREFERENCED_PARAMETER(ObjectHandle); UNREFERENCED_PARAMETER(ElementName); UNREFERENCED_PARAMETER(Status); EfiPrintf(L"Error in BiNotify: %lx for element %s\r\n", Status, ElementName); } ULONG BiConvertElementFormatToValueType ( _In_ ULONG Format ) { /* Strings and objects are strings */ if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT)) { return REG_SZ; } /* Object lists are arrays of strings */ if (Format == BCD_TYPE_OBJECT_LIST) { return REG_MULTI_SZ; } /* Everything else is binary */ return REG_BINARY; } NTSTATUS BiConvertRegistryDataToElement ( _In_ HANDLE ObjectHandle, _In_ PVOID Data, _In_ ULONG DataLength, _In_ BcdElementType ElementType, _Out_ PVOID Element, _Out_ PULONG ElementSize ) { NTSTATUS Status; SIZE_T Length, Size, ReturnedLength; PBL_DEVICE_DESCRIPTOR Device; BOOLEAN NullTerminate; PBCD_DEVICE_OPTION BcdDevice, ElementDevice; PWCHAR BcdString, ElementString; PGUID ElementGuid; UNICODE_STRING GuidString; PULONGLONG ElementInteger; PUSHORT ElementWord; PBOOLEAN BcdBoolean; /* Assume failure */ ReturnedLength = 0; /* Check what type of format we are dealing with */ switch (ElementType.Format) { /* Devices -- they are in a binary format */ case BCD_TYPE_DEVICE: /* First, make sure it's at least big enough for an empty descriptor */ if (DataLength < FIELD_OFFSET(BCD_DEVICE_OPTION, DeviceDescriptor.Unknown)) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Both the registry and BCD format are the same */ BcdDevice = (PBCD_DEVICE_OPTION)Data; ElementDevice = (PBCD_DEVICE_OPTION)Element; /* Make sure the device fits in the registry data */ Device = &BcdDevice->DeviceDescriptor; Size = Device->Size; if ((Size + sizeof(BcdDevice->AssociatedEntry)) != DataLength) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Check if this is a locate device */ if (Device->DeviceType == LocateDevice) { EfiPrintf(L"Locates not yet supported\r\n"); return STATUS_NOT_SUPPORTED; } /* Make sure the caller's buffer can fit the device */ ReturnedLength = Size + sizeof(BcdDevice->AssociatedEntry); if (ReturnedLength > *ElementSize) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* It'll fit -- copy it in */ RtlCopyMemory(&ElementDevice->DeviceDescriptor, Device, Size); ElementDevice->AssociatedEntry = BcdDevice->AssociatedEntry; Status = STATUS_SUCCESS; break; /* Strings -- they are stored as is */ case BCD_TYPE_STRING: /* Make sure the string isn't empty or misaligned */ if (!(DataLength) || (DataLength & 1)) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Both the registry and BCD format are the same */ BcdString = (PWCHAR)Data; ElementString = (PWCHAR)Element; /* We'll need as much data as the string has to offer */ ReturnedLength = DataLength; /* If the string isn't NULL-terminated, do it now */ NullTerminate = FALSE; if (BcdString[(DataLength / sizeof(WCHAR)) - 1] != UNICODE_NULL) { ReturnedLength += sizeof(UNICODE_NULL); NullTerminate = TRUE; } /* Will we fit in the caller's buffer? */ if (ReturnedLength > *ElementSize) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Yep -- copy it in, and NULL-terminate if needed */ RtlCopyMemory(Element, Data, DataLength); if (NullTerminate) { ElementString[DataLength / sizeof(WCHAR)] = UNICODE_NULL; } Status = STATUS_SUCCESS; break; /* Objects -- they are stored as GUID Strings */ case BCD_TYPE_OBJECT: /* Registry data is a string, BCD data is a GUID */ BcdString = (PWCHAR)Data; ElementGuid = (PGUID)Element; /* We need a GUID-sized buffer, does the caller have one? */ ReturnedLength = sizeof(*ElementGuid); if (*ElementSize < ReturnedLength) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Yep, copy the GUID */ RtlInitUnicodeString(&GuidString, BcdString); Status = RtlGUIDFromString(&GuidString, ElementGuid); break; /* Object Lists -- they are stored as arrays of GUID strings */ case BCD_TYPE_OBJECT_LIST: /* Assume an empty list*/ ReturnedLength = 0; Length = 0; Status = STATUS_SUCCESS; /* Registry data is an array of strings, BCD data is array of GUIDs */ BcdString = (PWCHAR)Data; ElementGuid = (PGUID)Element; /* Loop as long as the array still has strings */ while (*BcdString) { /* Don't read beyond the registry data */ if (Length >= DataLength) { break; } /* One more GUID -- does the caller have space? */ ReturnedLength += sizeof(GUID); if (ReturnedLength <= *ElementSize) { /* Convert and add it in */ RtlInitUnicodeString(&GuidString, BcdString); Status = RtlGUIDFromString(&GuidString, ElementGuid); if (!NT_SUCCESS(Status)) { break; } /* Move to the next GUID in the caller's buffer */ ElementGuid++; } /* Move to the next string in the registry array */ Size = (wcslen(BcdString) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); Length += Size; BcdString = (PWCHAR)((ULONG_PTR)BcdString + Length); } /* Check if we failed anywhere */ if (!NT_SUCCESS(Status)) { break; } /* Check if we consumed more space than we have */ if (ReturnedLength > *ElementSize) { Status = STATUS_BUFFER_TOO_SMALL; } /* All good here */ break; /* Integer -- stored as binary */ case BCD_TYPE_INTEGER: /* BCD data is a ULONGLONG, registry data is 8 bytes binary */ ElementInteger = (PULONGLONG)Element; ReturnedLength = sizeof(*ElementInteger); /* Make sure the registry data makes sense */ if (DataLength > ReturnedLength) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Make sure the caller has space */ if (*ElementSize < ReturnedLength) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Write the integer result */ *ElementInteger = 0; RtlCopyMemory(ElementInteger, Data, DataLength); Status = STATUS_SUCCESS; break; /* Boolean -- stored as binary */ case BCD_TYPE_BOOLEAN: /* BCD data is a BOOLEAN, registry data is 2 bytes binary */ ElementWord = (PUSHORT)Element; BcdBoolean = (PBOOLEAN)Data; ReturnedLength = sizeof(ElementWord); /* Make sure the registry data makes sense */ if (DataLength != sizeof(*BcdBoolean)) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Make sure the caller has space */ if (*ElementSize < ReturnedLength) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Write the boolean result */ *ElementWord = 0; *ElementWord = *BcdBoolean != 0; Status = STATUS_SUCCESS; break; /* Integer list --stored as binary */ case BCD_TYPE_INTEGER_LIST: /* BCD Data is n ULONGLONGs, registry data is n*8 bytes binary */ ReturnedLength = DataLength; if (!(DataLength) || (DataLength & 7)) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Make sure the caller has space */ if (*ElementSize < ReturnedLength) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Write the integer list result */ RtlCopyMemory(Element, Data, DataLength); Status = STATUS_SUCCESS; break; /* Arbitrary data */ default: /* Registry data is copied binary as-is */ ReturnedLength = DataLength; /* Make sure it's not empty */ if (!DataLength) { return STATUS_OBJECT_TYPE_MISMATCH; } /* Make sure the caller has space */ if (*ElementSize < ReturnedLength) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* Write the result */ RtlCopyMemory(Element, Data, DataLength); Status = STATUS_SUCCESS; break; } /* If we got here due to success or space issues, write the size */ if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { *ElementSize = ReturnedLength; } /* All done, return our conversion result */ return Status; } NTSTATUS BiConvertBcdElements ( _In_ PBCD_PACKED_ELEMENT Elements, _Out_opt_ PBCD_ELEMENT Buffer, _Inout_ PULONG BufferSize, _Inout_ PULONG ElementCount ) { NTSTATUS Status; ULONG ElementSize, AlignedElementSize, AlignedDataSize; PBCD_ELEMENT_HEADER Header; PVOID Data; BOOLEAN Exists; ULONG i, j, Count; /* Local variable to keep track of objects */ Count = 0; /* Safely compute the element bytes needed */ Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT), &ElementSize); if (!NT_SUCCESS(Status)) { return Status; } /* Safely align the element size */ Status = RtlULongAdd(ElementSize, sizeof(ULONG) - 1, &AlignedElementSize); if (!NT_SUCCESS(Status)) { return Status; } AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); /* Do a safe version of Add2Ptr to figure out where the headers will start */ Status = RtlULongPtrAdd((ULONG_PTR)Buffer, AlignedElementSize, (PULONG_PTR)&Header); if (!NT_SUCCESS(Status)) { return Status; } /* Safely compute the header bytes needed */ Status = RtlULongMult(*ElementCount, sizeof(BCD_ELEMENT_HEADER), &ElementSize); if (!NT_SUCCESS(Status)) { return Status; } /* Safely align the header size */ Status = RtlULongAdd(ElementSize, AlignedElementSize + sizeof(ULONG) - 1, &AlignedElementSize); if (!NT_SUCCESS(Status)) { return Status; } AlignedElementSize = ALIGN_DOWN(AlignedElementSize, ULONG); /* Do a safe version of Add2Ptr */ Status = RtlULongPtrAdd((ULONG_PTR)Buffer, AlignedElementSize, (PULONG_PTR)&Data); if (!NT_SUCCESS(Status)) { return Status; } /* Iterate over every element */ for (i = 0; i < *ElementCount; i++) { /* Safely align the element size */ Status = RtlULongAdd(Elements->Size, sizeof(ULONG) - 1, &AlignedDataSize); if (!NT_SUCCESS(Status)) { break; } AlignedDataSize = ALIGN_DOWN(AlignedDataSize, ULONG); /* Safely add the size of this data element */ Status = RtlULongAdd(AlignedElementSize, AlignedDataSize, &AlignedElementSize); if (!NT_SUCCESS(Status)) { break; } /* Do we have enough space left? */ if (*BufferSize >= AlignedElementSize) { /* Check if our root is an inherited object */ Exists = FALSE; if (Elements->RootType.PackedValue == BcdLibraryObjectList_InheritedObjects) { /* Yes, scan for us in the current buffer */ for (j = 0; j < Count; j++) { /* Do we already exist? */ while (Buffer[j].Header->Type == Elements->RootType.PackedValue) { /* Yep */ Exists = TRUE; break; } } } /* Have we already found ourselves? */ if (!Exists) { /* Nope, one more entry */ ++Count; /* Write out the unpacked object */ Buffer->Body = Data; Buffer->Header = Header; /* Fill out its header */ Header->Size = Elements->Size; Header->Type = Elements->Type; Header->Version = Elements->Version; /* And copy the data */ RtlCopyMemory(Data, Elements->Data, Header->Size); /* Move to the next unpacked object and header */ ++Buffer; ++Header; /* Move to the next data entry */ Data = (PVOID)((ULONG_PTR)Data + AlignedDataSize); } } else { /* Nope, set failure code, but keep going so we can return count */ Status = STATUS_BUFFER_TOO_SMALL; } /* Move to the next element entry */ Elements = Elements->NextEntry; } /* Return the new final buffer size and count */ *BufferSize = AlignedElementSize; *ElementCount = Count; return Status; } NTSTATUS BcdOpenObject ( _In_ HANDLE BcdHandle, _In_ PGUID ObjectId, _Out_ PHANDLE ObjectHandle ) { NTSTATUS Status; GUID LocalGuid; UNICODE_STRING GuidString; HANDLE RootObjectHandle; /* Assume failure */ *ObjectHandle = NULL; /* Initialize GUID string */ GuidString.Buffer = NULL; /* Open the root "Objects" handle */ RootObjectHandle = NULL; Status = BiOpenKey(BcdHandle, L"Objects", &RootObjectHandle); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Capture the object ID and convert it into a string */ LocalGuid = *ObjectId; Status = RtlStringFromGUID(&LocalGuid, &GuidString); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Now open the key containing this object ID */ Status = BiOpenKey(RootObjectHandle, GuidString.Buffer, ObjectHandle); Quickie: /* Free the GUID string if we had one allocated */ if (GuidString.Buffer) { RtlFreeUnicodeString(&GuidString); } /* Close the root handle if it was open */ if (RootObjectHandle) { BiCloseKey(RootObjectHandle); } /* Return the final status */ return Status; } NTSTATUS BcdDeleteElement ( _In_ HANDLE ObjectHandle, _In_ ULONG Type ) { NTSTATUS Status; HANDLE ElementsHandle, ElementHandle; WCHAR TypeString[22]; /* Open the elements key */ Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); if (NT_SUCCESS(Status)) { /* Convert the element ID into a string */ if (!_ultow(Type, TypeString, 16)) { /* Failed to do so */ Status = STATUS_UNSUCCESSFUL; } else { /* Open the element specifically */ Status = BiOpenKey(ElementsHandle, TypeString, &ElementHandle); if (NT_SUCCESS(Status)) { /* Delete it */ Status = BiDeleteKey(ElementHandle); if (NT_SUCCESS(Status)) { /* No point in closing the handle anymore */ ElementHandle = NULL; } } else { /* The element doesn't exist */ Status = STATUS_NOT_FOUND; } /* Check if we should close the key */ if (ElementHandle) { /* Do it */ BiCloseKey(ElementHandle); } } } /* Check if we should close the elements handle */ if (ElementsHandle) { /* Do it */ BiCloseKey(ElementsHandle); } /* Return whatever the result was */ return Status; } NTSTATUS BiEnumerateSubElements ( _In_ HANDLE BcdHandle, _In_ PVOID Object, _In_ ULONG ElementType, _In_ ULONG Flags, _Out_opt_ PBCD_PACKED_ELEMENT* Elements, _Inout_ PULONG ElementSize, _Out_ PULONG ElementCount ) { NTSTATUS Status; PBCD_PACKED_ELEMENT Element; HANDLE ObjectHandle; ULONG ParsedElements, RequiredSize; /* Assume empty */ *ElementCount = 0; RequiredSize = 0; ParsedElements = 0; /* Open the object */ Status = BcdOpenObject(BcdHandle, Object, &ObjectHandle); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Read the first entry, and the size available */ Element = *Elements; RequiredSize = *ElementSize; /* Enumerate the object into the element array */ Status = BiEnumerateElements(BcdHandle, ObjectHandle, ElementType, Flags, Element, &RequiredSize, &ParsedElements); /* Close the handle and bail out if we couldn't enumerate */ BiCloseKey(ObjectHandle); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Check if the and subelements were present */ if (ParsedElements) { /* Keep going until the last one */ while (Element->NextEntry) { Element = Element->NextEntry; } /* Set the new buffer location to the last element */ *Elements = Element; } Quickie: /* Return the number of sub-elements and their size */ *ElementCount = ParsedElements; *ElementSize = RequiredSize; return Status; } NTSTATUS BiEnumerateSubObjectElements ( _In_ HANDLE BcdHandle, _Out_ PGUID SubObjectList, _In_ ULONG SubObjectCount, _In_ ULONG Flags, _Out_opt_ PBCD_PACKED_ELEMENT Elements, _Inout_ PULONG ElementSize, _Out_ PULONG ElementCount ) { NTSTATUS Status; ULONG SubElementCount, TotalSize, RequiredSize, CurrentSize, i; PBCD_PACKED_ELEMENT PreviousElement; /* Assume empty list */ *ElementCount = 0; Status = STATUS_SUCCESS; /* Initialize variables */ TotalSize = 0; PreviousElement = NULL; /* Set the currently remaining size based on caller's input */ CurrentSize = *ElementSize; /* Iterate over every subje object */ for (i = 0; i < SubObjectCount; i++) { /* Set the currently remaining buffer space */ RequiredSize = CurrentSize; /* Enumerate the inherited sub elements */ Status = BiEnumerateSubElements(BcdHandle, &SubObjectList[i], BcdLibraryObjectList_InheritedObjects, Flags, &Elements, &RequiredSize, &SubElementCount); if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { /* Safely add the length of the sub elements */ Status = RtlULongAdd(TotalSize, RequiredSize, &TotalSize); if (!NT_SUCCESS(Status)) { break; } /* Add the sub elements to the total */ *ElementCount += SubElementCount; /* See if we have enough space*/ if (*ElementSize >= TotalSize) { /* Were there any subelements? */ if (SubElementCount) { /* Update to keep track of these new subelements */ CurrentSize = *ElementSize - TotalSize; /* Link the subelements into the chain */ PreviousElement = Elements; PreviousElement->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)Elements + TotalSize); Elements = PreviousElement->NextEntry; } } else { /* We're out of space */ CurrentSize = 0; } } else if ((Status != STATUS_NOT_FOUND) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { /* Some other fatal error, break out */ break; } else { /* The sub element was not found, print a warning but keep going */ BlStatusPrint(L"Ignoring missing BCD inherit object: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", (&SubObjectList[i])->Data1, (&SubObjectList[i])->Data2, (&SubObjectList[i])->Data3, (&SubObjectList[i])->Data4[0], (&SubObjectList[i])->Data4[1], (&SubObjectList[i])->Data4[2], (&SubObjectList[i])->Data4[3], (&SubObjectList[i])->Data4[4], (&SubObjectList[i])->Data4[5], (&SubObjectList[i])->Data4[6], (&SubObjectList[i])->Data4[7]); Status = STATUS_SUCCESS; } } /* Terminate the last element, if one was left */ if (PreviousElement) { PreviousElement->NextEntry = NULL; } /* Set failure code if we ran out of space */ if (*ElementSize < TotalSize) { Status = STATUS_BUFFER_TOO_SMALL; } /* Return final length and status */ *ElementSize = TotalSize; return Status; } NTSTATUS BiEnumerateElements ( _In_ HANDLE BcdHandle, _In_ HANDLE ObjectHandle, _In_ ULONG RootElementType, _In_ ULONG Flags, _Out_opt_ PBCD_PACKED_ELEMENT Elements, _Inout_ PULONG ElementSize, _Out_ PULONG ElementCount ) { HANDLE ElementsHandle, ElementHandle; ULONG TotalLength, RegistryElementDataLength, RemainingLength; NTSTATUS Status; ULONG i; PVOID ElementData, SubObjectList, RegistryElementData; BcdElementType ElementType; PBCD_PACKED_ELEMENT PreviousElement, ElementsStart; ULONG SubElementCount, SubKeyCount, SubObjectCount, ElementDataLength; PWCHAR ElementName; PWCHAR* SubKeys; /* Assume failure */ *ElementCount = 0; /* Initialize all locals that are checked at the end*/ SubKeys = NULL; ElementsHandle = NULL; ElementHandle = NULL; ElementData = NULL; RegistryElementData = NULL; PreviousElement = NULL; ElementName = NULL; SubObjectList = NULL; TotalLength = 0; ElementDataLength = 0; SubObjectCount = 0; RemainingLength = 0; ElementsStart = Elements; /* Open the root object key's elements */ Status = BiOpenKey(ObjectHandle, L"Elements", &ElementsHandle); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Enumerate all elements */ Status = BiEnumerateSubKeys(ElementsHandle, &SubKeys, &SubKeyCount); if (!NT_SUCCESS(Status)) { goto Quickie; } /* Iterate over each one */ for (i = 0; i < SubKeyCount; i++) { /* Open the element */ ElementName = SubKeys[i]; Status = BiOpenKey(ElementsHandle, ElementName, &ElementHandle); if (!NT_SUCCESS(Status)) { EfiPrintf(L"ELEMENT ERROR: %lx\r\n", Status); EfiStall(100000); break; } /* The name of the element is its data type */ ElementType.PackedValue = wcstoul(SubKeys[i], NULL, 16); if (!(ElementType.PackedValue) || (ElementType.PackedValue == -1)) { EfiPrintf(L"Value invalid\r\n"); BiCloseKey(ElementHandle); ElementHandle = NULL; continue; } /* Read the appropriate registry value type for this element */ Status = BiGetRegistryValue(ElementHandle, L"Element", BiConvertElementFormatToValueType( ElementType.Format), &RegistryElementData, &RegistryElementDataLength); if (!NT_SUCCESS(Status)) { EfiPrintf(L"Element invalid\r\n"); break; } /* Now figure out how much space the converted element will need */ ElementDataLength = 0; Status = BiConvertRegistryDataToElement(ObjectHandle, RegistryElementData, RegistryElementDataLength, ElementType, NULL, &ElementDataLength); if (Status != STATUS_BUFFER_TOO_SMALL) { break; } /* Allocate a buffer big enough for the converted element */ ElementData = BlMmAllocateHeap(ElementDataLength); if (!ElementData) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* And actually convert it this time around */ Status = BiConvertRegistryDataToElement(ObjectHandle, RegistryElementData, RegistryElementDataLength, ElementType, ElementData, &ElementDataLength); if (!NT_SUCCESS(Status)) { break; } /* Safely add space for the packed element header */ Status = RtlULongAdd(TotalLength, FIELD_OFFSET(BCD_PACKED_ELEMENT, Data), &TotalLength); if (!NT_SUCCESS(Status)) { break; } /* Safely add space for the data of the element itself */ Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength); if (!NT_SUCCESS(Status)) { break; } /* One more element */ ++*ElementCount; /* See how much space we were given */ RemainingLength = *ElementSize; if (RemainingLength >= TotalLength) { /* Set the next pointer */ Elements->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength); /* Fill this one out */ Elements->RootType.PackedValue = RootElementType; Elements->Version = 1; Elements->Type = ElementType.PackedValue; Elements->Size = ElementDataLength; /* Add the data */ RtlCopyMemory(Elements->Data, ElementData, ElementDataLength); RemainingLength -= TotalLength; /* Move to the next element on the next pass */ PreviousElement = Elements; Elements = Elements->NextEntry; } else { /* We're out of space */ RemainingLength = 0; } /* Are we enumerating devices, and is this a device? */ if ((Flags & BCD_ENUMERATE_FLAG_DEVICES) && (ElementType.Format == BCD_TYPE_DEVICE)) { /* Yep, so go inside to enumerate it */ Status = BiEnumerateSubElements(BcdHandle, ElementData, ElementType.PackedValue, Flags, &Elements, &ElementDataLength, &SubElementCount); if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { /* Safely add the length of the sub elements */ Status = RtlULongAdd(TotalLength, ElementDataLength, &TotalLength); if (!NT_SUCCESS(Status)) { break; } /* Add the sub elements to the total */ *ElementCount += SubElementCount; /* See if we have enough space*/ if (*ElementSize >= TotalLength) { /* Were there any subelements? */ if (SubElementCount) { /* Update to keep track of these new subelements */ ElementDataLength = *ElementSize - TotalLength; /* Link the subelements into the chain */ PreviousElement = Elements; PreviousElement->NextEntry = (PBCD_PACKED_ELEMENT)((ULONG_PTR)ElementsStart + TotalLength); Elements = PreviousElement->NextEntry; } } else { /* We're out of space */ ElementDataLength = 0; } } else if ((Status != STATUS_NOT_FOUND) && (Status != STATUS_OBJECT_NAME_NOT_FOUND)) { /* Fatal error trying to read the data, so fail */ break; } } else if ((Flags & BCD_ENUMERATE_FLAG_DEEP) && (ElementType.PackedValue == BcdLibraryObjectList_InheritedObjects)) { /* Inherited objects are requested, so allocate a buffer for them */ SubObjectList = BlMmAllocateHeap(ElementDataLength); if (!SubObjectList) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Copy the elements into the list. They are arrays of GUIDs */ RtlCopyMemory(SubObjectList, ElementData, ElementDataLength); SubObjectCount = ElementDataLength / sizeof(GUID); } /* Free our local buffers */ BlMmFreeHeap(ElementData); BlMmFreeHeap(RegistryElementData); ElementData = NULL; RegistryElementData = NULL; /* Close the key */ BiCloseKey(ElementHandle); ElementHandle = NULL; ElementName = NULL; } /* Did we end up here with a sub object list after successful loop parsing? */ if ((i != 0) && (i == SubKeyCount) && (SubObjectList)) { /* We will actually enumerate it now, at the end */ Status = BiEnumerateSubObjectElements(BcdHandle, SubObjectList, SubObjectCount, Flags, Elements, &RemainingLength, &SubElementCount); if ((NT_SUCCESS(Status)) || (Status == STATUS_BUFFER_TOO_SMALL)) { /* Safely add the length of the sub elements */ Status = RtlULongAdd(TotalLength, RemainingLength, &TotalLength); if ((NT_SUCCESS(Status)) && (SubElementCount)) { /* Add the sub elements to the total */ *ElementCount += SubElementCount; /* Don't touch PreviousElement anymore */ PreviousElement = NULL; } } } Quickie: /* Free the sub object list, if any */ if (SubObjectList) { BlMmFreeHeap(SubObjectList); } /* Free any local element data */ if (ElementData) { BlMmFreeHeap(ElementData); } /* Free any local registry data */ if (RegistryElementData) { BlMmFreeHeap(RegistryElementData); } /* Close the handle if still opened */ if (ElementHandle) { BiCloseKey(ElementHandle); } /* Terminate the last element, if any */ if (PreviousElement) { PreviousElement->NextEntry = NULL; } /* Close the root handle if still opened */ if (ElementsHandle) { BiCloseKey(ElementsHandle); } /* Set failure code if out of space */ if (*ElementSize < TotalLength) { Status = STATUS_BUFFER_TOO_SMALL; } /* Other errors will send a notification error */ if (!(NT_SUCCESS(Status)) && (Status != STATUS_BUFFER_TOO_SMALL)) { BiNotifyEnumerationError(ObjectHandle, ElementName, Status); } /* Finally free the subkeys array */ if (SubKeys) { BlMmFreeHeap(SubKeys); } /* And return the required, final length and status */ *ElementSize = TotalLength; return Status; } NTSTATUS BiAddStoreFromFile ( _In_ PBL_FILE_PATH_DESCRIPTOR FilePath, _Out_ PHANDLE StoreHandle ) { NTSTATUS Status; HANDLE HiveHandle, KeyHandle; /* Load the specified hive */ Status = BiLoadHive(FilePath, &HiveHandle); if (!NT_SUCCESS(Status)) { return Status; } /* Open the description key to make sure this is really a BCD */ Status = BiOpenKey(HiveHandle, L"Description", &KeyHandle); if (NT_SUCCESS(Status)) { /* It is -- close the key as we don't need it */ BiCloseKey(KeyHandle); *StoreHandle = HiveHandle; } else { /* Failure, drop a reference on the hive and close the key */ BiDereferenceHive(HiveHandle); BiCloseKey(HiveHandle); } /* Return the status */ return Status; } NTSTATUS BiGetObjectDescription ( _In_ HANDLE ObjectHandle, _Out_ PBCD_OBJECT_DESCRIPTION Description ) { NTSTATUS Status; HANDLE DescriptionHandle; PULONG Data; ULONG Length; /* Initialize locals */ Data = NULL; DescriptionHandle = NULL; /* Open the description key */ Status = BiOpenKey(ObjectHandle, L"Description", &DescriptionHandle); if (NT_SUCCESS(Status)) { /* It exists */ Description->Valid = TRUE; /* Read the type */ Length = 0; Status = BiGetRegistryValue(DescriptionHandle, L"Type", REG_DWORD, (PVOID*)&Data, &Length); if (NT_SUCCESS(Status)) { /* Make sure it's the length we expected it to be */ if (Length == sizeof(Data)) { /* Return the type that is stored there */ Description->Type = *Data; } else { /* Invalid type value */ Status = STATUS_OBJECT_TYPE_MISMATCH; } } } /* Did we have a handle open? */ if (DescriptionHandle) { /* Close it */ BiCloseKey(DescriptionHandle); } /* Did we have data allocated? */ if (Data) { /* Free it */ BlMmFreeHeap(Data); } /* Return back to caller */ return Status; } NTSTATUS BcdEnumerateAndUnpackElements ( _In_ HANDLE BcdHandle, _In_ HANDLE ObjectHandle, _Out_opt_ PBCD_ELEMENT Elements, _Inout_ PULONG ElementSize, _Out_ PULONG ElementCount ) { PVOID LocalElements; NTSTATUS Status; ULONG LocalElementCount, LocalElementSize; /* Make sure required parameters are there */ if (!(ElementSize) || !(ElementCount) || ((Elements) && (!*ElementSize))) { return STATUS_INVALID_PARAMETER; } /* Set initial count to zero */ *ElementCount = 0; /* Do the initial enumeration to figure out the size required */ LocalElementSize = 0; LocalElementCount = 0; Status = BiEnumerateElements(BcdHandle, ObjectHandle, 0, BCD_ENUMERATE_FLAG_IN_ORDER | BCD_ENUMERATE_FLAG_DEVICES | BCD_ENUMERATE_FLAG_DEEP, NULL, &LocalElementSize, &LocalElementCount); if (Status != STATUS_BUFFER_TOO_SMALL) { return Status; } /* Now allocate a buffer large enough to hold them */ LocalElements = BlMmAllocateHeap(LocalElementSize); if (!LocalElements) { return STATUS_INSUFFICIENT_RESOURCES; } /* Zero out the array and do the real enumeration this time around */ RtlZeroMemory(LocalElements, LocalElementSize); Status = BiEnumerateElements(BcdHandle, ObjectHandle, 0, BCD_ENUMERATE_FLAG_IN_ORDER | BCD_ENUMERATE_FLAG_DEVICES | BCD_ENUMERATE_FLAG_DEEP, LocalElements, &LocalElementSize, &LocalElementCount); if (!NT_SUCCESS(Status)) { return Status; } /* Now we know the real count */ *ElementCount = LocalElementCount; /* Now unpack the data */ Status = BiConvertBcdElements(LocalElements, Elements, ElementSize, &LocalElementCount); if (NT_SUCCESS(Status)) { /* Not all elements may have been converted */ *ElementCount = LocalElementCount; } /* Free the local (unpacked) buffer and return status */ BlMmFreeHeap(LocalElements); return Status; } NTSTATUS BcdOpenStoreFromFile ( _In_ PUNICODE_STRING FileName, _In_ PHANDLE BcdHandle ) { ULONG Length; PBL_FILE_PATH_DESCRIPTOR FilePath; NTSTATUS Status; HANDLE LocalHandle; /* Assume failure */ LocalHandle = NULL; /* Allocate a path descriptor */ Length = FileName->Length + sizeof(*FilePath); FilePath = BlMmAllocateHeap(Length); if (!FilePath) { return STATUS_NO_MEMORY; } /* Initialize it */ FilePath->Version = 1; FilePath->PathType = InternalPath; FilePath->Length = Length; /* Copy the name and NULL-terminate it */ RtlCopyMemory(FilePath->Path, FileName->Buffer, Length); FilePath->Path[Length / sizeof(WCHAR)] = UNICODE_NULL; /* Open the BCD */ Status = BiAddStoreFromFile(FilePath, &LocalHandle); if (NT_SUCCESS(Status)) { /* Return the handle on success */ *BcdHandle = LocalHandle; } /* Free the descriptor and return the status */ BlMmFreeHeap(FilePath); return Status; }