[BOOTLIB]
[reactos.git] / reactos / boot / environ / lib / misc / bcd.c
index ed4502a..12a94ae 100644 (file)
 /* INCLUDES ******************************************************************/
 
 #include "bl.h"
+#include <bcd.h>
 
 /* 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
-BlGetBootOptionListSize (
-    _In_ PBOOT_ENTRY_OPTION BcdOption
+BiConvertElementFormatToValueType (
+    _In_ ULONG Format
     )
 {
-    ULONG Size = 0, NextOffset = 0;
-    PBOOT_ENTRY_OPTION NextOption;
+    /* Strings and objects are strings */
+    if ((Format == BCD_TYPE_STRING) || (Format == BCD_TYPE_OBJECT))
+    {
+        return REG_SZ;
+    }
 
-    /* Loop all the options*/
-    do
+    /* Object lists are arrays of strings */
+    if (Format == BCD_TYPE_OBJECT_LIST)
     {
-        /* Move to the next one */
-        NextOption = (PBOOT_ENTRY_OPTION)((ULONG_PTR)BcdOption + NextOffset);
+        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;
+    ULONG 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;
+                    }
 
-        /* Compute the size of the next one */
-        Size += BlGetBootOptionSize(NextOption);
+                    /* Move to the next GUID in the caller's buffer */
+                    ElementGuid++;
+                }
 
-        /* Update the offset */
-        NextOffset = NextOption->NextEntryOffset;
-    } while (NextOffset != 0);
+                /* 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);
+            }
 
-    /* Return final computed size */
-    return Size;
+            /* 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;
 }
 
-ULONG
-BlGetBootOptionSize (
-    _In_ PBOOT_ENTRY_OPTION BcdOption
+NTSTATUS
+BiConvertBcdElements (
+    _In_ PBCD_PACKED_ELEMENT Elements,
+    _Out_opt_ PBCD_ELEMENT Buffer,
+    _Inout_ PULONG BufferSize, 
+    _Inout_ PULONG ElementCount
     )
 {
-    ULONG Size, Offset;
+    NTSTATUS Status;
+    ULONG ElementSize, AlignedElementSize, AlignedDataSize;
+    PBCD_ELEMENT_HEADER Header;
+    PVOID Data;
+    BOOLEAN Exists;
+    ULONG i, j, Count;
 
-    /* Check if there's any data */
-    if (BcdOption->DataOffset != 0)
+    /* 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))
     {
-        /* Add the size of the data */
-        Size = BcdOption->DataOffset + BcdOption->DataSize;
+        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],
+                          (&SubObjectList[i])->Data4[8]);
+            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 requsted, 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
     {
-        /* No data, just the structure itself */
-        Size = sizeof(*BcdOption);
+        /* 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;
     }
 
-    /* Any associated options? */
-    Offset = BcdOption->ListOffset;
-    if (Offset != 0)
+    /* 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))
     {
-        /* Go get those too */
-        Size += BlGetBootOptionListSize((PVOID)((ULONG_PTR)BcdOption + Offset));
+        /* Return the handle on success */
+        *BcdHandle = LocalHandle;
     }
 
-    /* Return the final size */
-    return Size;
+    /* Free the descriptor and return the status */
+    BlMmFreeHeap(FilePath);
+    return Status;
 }
+