windows-nt/Source/XPSP1/NT/base/ntsetup/winnt32/dll/arc.c
2020-09-26 16:20:57 +08:00

2951 lines
78 KiB
C

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
arc.c
Abstract:
ARC/NV-RAM manipulation routines for 32-bit winnt setup.
Author:
Ted Miller (tedm) 19-December-1993
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include "initguid.h"
#include "diskguid.h"
#ifdef UNICODE // Always true for ARC, never true for Win9x upgrade
#if defined(_X86_)
LOGICAL IsArcChecked = FALSE;
LOGICAL IsArcMachine;
#endif
#if defined(EFI_NVRAM_ENABLED)
#include <ntosp.h> // for ALIGN_UP
LOGICAL IsEfiChecked = FALSE;
LOGICAL IsEfiMachine;
DWORD
InitializeEfiStuff(
IN HWND Parent
);
NTSTATUS
(*AddBootEntry) (
IN PBOOT_ENTRY BootEntry,
OUT PULONG Id OPTIONAL
);
NTSTATUS
(*DeleteBootEntry) (
IN ULONG Id
);
NTSTATUS
(*EnumerateBootEntries) (
OUT PVOID Buffer,
IN OUT PULONG BufferLength
);
NTSTATUS
(*QueryBootEntryOrder) (
OUT PULONG Ids,
IN OUT PULONG Count
);
NTSTATUS
(*SetBootEntryOrder) (
IN PULONG Ids,
IN ULONG Count
);
NTSTATUS
(*QueryBootOptions) (
OUT PBOOT_OPTIONS BootOptions,
IN OUT PULONG BootOptionsLength
);
NTSTATUS
(*SetBootOptions) (
IN PBOOT_OPTIONS BootOptions,
IN ULONG FieldsToChange
);
PBOOT_OPTIONS BootOptions = NULL;
PBOOT_OPTIONS OriginalBootOptions = NULL;
PULONG OriginalBootEntryOrder = NULL;
ULONG OriginalBootEntryOrderCount;
PBOOT_ENTRY_LIST BootEntries = NULL;
//
// MY_BOOT_ENTRY is the internal representation of an EFI NVRAM boot item.
// The NtBootEntry item is the structure passed to/from the NT boot entry APIs.
//
typedef struct _MY_BOOT_ENTRY {
struct _MY_BOOT_ENTRY *Next;
PUCHAR AllocationEnd;
ULONG Status;
PWSTR FriendlyName;
ULONG FriendlyNameLength;
PWSTR OsLoadOptions;
ULONG OsLoadOptionsLength;
PFILE_PATH BootFilePath;
PFILE_PATH OsFilePath;
BOOT_ENTRY NtBootEntry;
} MY_BOOT_ENTRY, *PMY_BOOT_ENTRY;
#define MBE_STATUS_ORDERED 0x00000001
#define MBE_STATUS_NEW 0x00000002
#define MBE_STATUS_DELETED 0x00000004
#define MBE_STATUS_COMMITTED 0x00000008
#define IS_BOOT_ENTRY_ACTIVE(_be) \
(((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_ACTIVE) != 0)
#define IS_BOOT_ENTRY_WINDOWS(_be) \
(((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_WINDOWS) != 0)
#define IS_BOOT_ENTRY_REMOVABLE_MEDIA(_be) \
(((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_REMOVABLE_MEDIA) != 0)
#define IS_BOOT_ENTRY_ORDERED(_be) \
(((_be)->Status & MBE_STATUS_ORDERED) != 0)
#define IS_BOOT_ENTRY_NEW(_be) \
(((_be)->Status & MBE_STATUS_NEW) != 0)
#define IS_BOOT_ENTRY_DELETED(_be) \
(((_be)->Status & MBE_STATUS_DELETED) != 0)
#define IS_BOOT_ENTRY_COMMITTED(_be) \
(((_be)->Status & MBE_STATUS_COMMITTED) != 0)
PMY_BOOT_ENTRY MyBootEntries = NULL;
NTSTATUS
ConvertBootEntries(
VOID
);
BOOL
CreateBootEntry(
PWSTR BootFileDevice,
PWSTR BootFilePath,
PWSTR OsLoadDevice,
PWSTR OsLoadPath,
PWSTR OsLoadOptions,
PWSTR FriendlyName
);
#define ADD_OFFSET(_p,_o) (PVOID)((PUCHAR)(_p) + (_p)->_o)
#endif // defined(EFI_NVRAM_ENABLED)
UINT SystemPartitionCount;
PWSTR* SystemPartitionNtNames;
PWSTR SystemPartitionNtName;
PWSTR SystemPartitionVolumeGuid;
typedef enum {
BootVarSystemPartition,
BootVarOsLoader,
BootVarOsLoadPartition,
BootVarOsLoadFilename,
BootVarLoadIdentifier,
BootVarOsLoadOptions,
BootVarMax
} BootVars;
LPCWSTR BootVarNames[BootVarMax] = { L"SYSTEMPARTITION",
L"OSLOADER",
L"OSLOADPARTITION",
L"OSLOADFILENAME",
L"LOADIDENTIFIER",
L"OSLOADOPTIONS"
};
LPCWSTR szAUTOLOAD = L"AUTOLOAD";
LPCWSTR szCOUNTDOWN = L"COUNTDOWN";
LPWSTR BootVarValues[BootVarMax];
LPCWSTR OriginalBootVarValues[BootVarMax];
LPCWSTR OriginalCountdown;
LPCWSTR OriginalAutoload;
DWORD BootVarComponentCount[BootVarMax];
LPWSTR *BootVarComponents[BootVarMax];
DWORD LargestComponentCount;
LPWSTR DosDeviceTargets[26];
//
// Flag indicating whether we messed with NV-RAM and thus need to
// try to restore it in case the user cancels.
//
BOOL CleanUpNvRam;
//
// Leave as array because some code uses sizeof(ArcNameDirectory)
//
WCHAR ArcNameDirectory[] = L"\\ArcName";
#define GLOBAL_ROOT L"\\\\?\\GLOBALROOT"
#define MAX_COMPONENTS 20
WCHAR ForcedSystemPartition;
//
// Helper macro to make object attribute initialization a little cleaner.
//
#define INIT_OBJA(Obja,UnicodeString,UnicodeText) \
\
RtlInitUnicodeString((UnicodeString),(UnicodeText)); \
\
InitializeObjectAttributes( \
(Obja), \
(UnicodeString), \
OBJ_CASE_INSENSITIVE, \
NULL, \
NULL \
)
UINT
NormalizeArcPath(
IN PCWSTR Path,
OUT LPWSTR *NormalizedPath
)
/*++
Routine Description:
Transform an ARC path into one with no sets of empty parenthesis
(ie, transforom all instances of () to (0).).
Arguments:
Path - ARC path to be normalized.
NormalizedPath - if successful, receives a pointer to the
normalized arc path. The caller must free with FREE().
Return Value:
Win32 error code indicating outcome.
--*/
{
LPWSTR r;
LPCWSTR p,q;
LPWSTR normalizedPath;
if(normalizedPath = MALLOC((lstrlen(Path)+100)*sizeof(WCHAR))) {
ZeroMemory(normalizedPath,(lstrlen(Path)+100)*sizeof(WCHAR));
} else {
return(ERROR_NOT_ENOUGH_MEMORY);
}
for(p=Path; q=wcsstr(p,L"()"); p=q+2) {
r = normalizedPath + lstrlen(normalizedPath);
lstrcpyn(r,p,(int)(q-p)+1);
lstrcat(normalizedPath,L"(0)");
}
lstrcat(normalizedPath,p);
if(r = REALLOC(normalizedPath,(lstrlen(normalizedPath)+1)*sizeof(WCHAR))) {
*NormalizedPath = r;
return(NO_ERROR);
} else {
FREE(normalizedPath);
return(ERROR_NOT_ENOUGH_MEMORY);
}
}
DWORD
GetVarComponents(
IN PCWSTR VarValue,
OUT LPWSTR **Components,
OUT PDWORD ComponentCount
)
/*++
Routine Description:
Split a semi-colon delineated list of arc paths up into
a set of individual strings. For each component
leading and trailing spaces are stripped out.
Arguments:
VarValue - supplies string with list of arc paths to be split apart.
Components - receives array of pointers to individual components
on the variable specified in VarValue.
ComponentCount - receives number of separate arc paths in the
Components array.
Return Value:
Win32 error indicating outcome. If NO_ERROR then the caller
must free the Components array and the strings pointed to by its elements.
--*/
{
LPWSTR *components;
LPWSTR *temp;
DWORD componentCount;
LPCWSTR p;
LPCWSTR Var;
LPWSTR comp;
DWORD len;
UINT ec;
components = MALLOC(MAX_COMPONENTS * sizeof(LPWSTR));
if(!components) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
ZeroMemory(components,MAX_COMPONENTS * sizeof(LPWSTR));
ec = NO_ERROR;
for(Var=VarValue,componentCount=0; *Var; ) {
//
// Skip leading spaces.
//
while((*Var == L' ') || (*Var == L'\t')) {
Var++;
}
if(*Var == 0) {
break;
}
p = Var;
while(*p && (*p != L';')) {
p++;
}
len = (DWORD)((PUCHAR)p - (PUCHAR)Var);
comp = MALLOC(len + sizeof(WCHAR));
if(!comp) {
ec = ERROR_NOT_ENOUGH_MEMORY;
break;
}
len /= sizeof(WCHAR);
lstrcpynW(comp,Var,len+1);
ec = NormalizeArcPath(comp,&components[componentCount]);
FREE(comp);
if(ec != NO_ERROR) {
break;
}
componentCount++;
if(componentCount == MAX_COMPONENTS) {
break;
}
Var = p;
if(*Var) {
Var++; // skip ;
}
}
if(ec == NO_ERROR) {
if(componentCount) {
temp = REALLOC(components,componentCount*sizeof(LPWSTR));
if(!temp) {
ec = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
temp = NULL;
}
}
if(ec == NO_ERROR) {
*Components = temp;
*ComponentCount = componentCount;
} else {
for(len=0; components[len] && (len<MAX_COMPONENTS); len++) {
FREE(components[len]);
}
FREE(components);
}
return(ec);
}
NTSTATUS
QueryCanonicalName(
IN PWSTR Name,
IN ULONG MaxDepth,
OUT PWSTR CanonicalName,
IN ULONG SizeOfBufferInBytes
)
/*++
Routine Description:
Resolves the symbolic name to the specified depth. To resolve
a symbolic name completely specify the MaxDepth as -1
Arguments:
Name - Symbolic name to be resolved
MaxDepth - The depth till which the resolution needs to
be carried out
CanonicalName - The fully resolved name
SizeOfBufferInBytes - The size of the CanonicalName buffer in
bytes
Return Value:
Appropriate NT status code
--*/
{
UNICODE_STRING name, canonName;
OBJECT_ATTRIBUTES oa;
NTSTATUS status;
HANDLE handle;
ULONG CurrentDepth;
RtlInitUnicodeString(&name, Name);
canonName.MaximumLength = (USHORT) (SizeOfBufferInBytes - sizeof(WCHAR));
canonName.Length = 0;
canonName.Buffer = CanonicalName;
if (name.Length >= canonName.MaximumLength) {
return STATUS_BUFFER_TOO_SMALL;
}
RtlCopyMemory(canonName.Buffer, name.Buffer, name.Length);
canonName.Length = name.Length;
canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0;
for (CurrentDepth = 0; CurrentDepth < MaxDepth; CurrentDepth++) {
InitializeObjectAttributes(&oa, &canonName, OBJ_CASE_INSENSITIVE, 0, 0);
status = NtOpenSymbolicLinkObject(&handle,
READ_CONTROL | SYMBOLIC_LINK_QUERY,
&oa);
if (!NT_SUCCESS(status)) {
break;
}
status = NtQuerySymbolicLinkObject(handle, &canonName, NULL);
NtClose(handle);
if (!NT_SUCCESS(status)) {
return status;
}
canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0;
}
return STATUS_SUCCESS;
}
//
// Structure to map from old NT partition names like
// \device\harddisk0\partition1 to new NT partition names
// like \device\harddiskvolume1
//
typedef struct _NAME_TRANSLATIONS {
WCHAR OldNtName[MAX_PATH];
WCHAR NewNtName[MAX_PATH];
} NT_NAME_TRANSLATION, * PNT_NAME_TRANSLATION;
//
// Map of old style NT partition names to new style NT
// partition names
//
NT_NAME_TRANSLATION OldNewNtNames[256] = {0};
PWSTR
OldNtNameToNewNtName(
IN PCWSTR OldNtName
)
/*++
Routine Description:
Given a old format NT name tries to lookup at new format
NT name in the global map
Arguments:
OldNtName - The partition name specified in the old
format
Return Value:
The new NT name if there exists one, otherwise NULL.
--*/
{
ULONG Index = 0;
ULONG MaxEntries = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION);
PWSTR NewNtName = NULL;
for (Index = 0; (Index < MaxEntries); Index++) {
if (OldNewNtNames[Index].OldNtName[0] &&
!_wcsicmp(OldNewNtNames[Index].OldNtName, OldNtName)) {
NewNtName = OldNewNtNames[Index].NewNtName;
}
}
return NewNtName;
}
PWSTR
NewNtNameToOldNtName(
IN PCWSTR NewNtName
)
/*++
Routine Description:
Given a new format NT name tries to lookup at old format
NT name in the global map
Arguments:
NewNtName - The partition name specified in the new
format
Return Value:
The old NT name if there exists one, otherwise NULL.
--*/
{
ULONG Index = 0;
ULONG MaxEntries = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION);
PWSTR OldNtName = NULL;
for (Index=0; (Index < MaxEntries); Index++) {
if (OldNewNtNames[Index].NewNtName[0] &&
!_wcsicmp(OldNewNtNames[Index].NewNtName, NewNtName)) {
OldNtName = OldNewNtNames[Index].OldNtName;
}
}
return OldNtName;
}
DWORD
InitOldToNewNtNameTranslations(
VOID
)
/*++
Routine Description:
Initializes the global old NT partition names to
new NT partition names mapping.
Arguments:
None.
Return Value:
The number of valid entries in the map
--*/
{
DWORD MappingCount = 0;
SYSTEM_DEVICE_INFORMATION SysDevInfo = {0};
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjAttrs;
UNICODE_STRING ObjName;
Status = NtQuerySystemInformation(SystemDeviceInformation,
&SysDevInfo,
sizeof(SYSTEM_DEVICE_INFORMATION),
NULL);
if (NT_SUCCESS(Status)) {
ULONG Index;
WCHAR OldNtPath[MAX_PATH];
DWORD ErrorCode = 0;
ULONG SlotIndex = 0;
ULONG MaxSlots = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION);
for (Index=0;
(!ErrorCode) && (Index < SysDevInfo.NumberOfDisks) &&
(SlotIndex < MaxSlots);
Index++) {
HANDLE DirectoryHandle;
swprintf(OldNtPath,
L"\\device\\Harddisk%d",
Index);
//
// Open the disk directory.
//
INIT_OBJA(&ObjAttrs, &ObjName, OldNtPath);
Status = NtOpenDirectoryObject(&DirectoryHandle,
DIRECTORY_QUERY,
&ObjAttrs);
if(NT_SUCCESS(Status)) {
BOOLEAN RestartScan = TRUE;
ULONG Context = 0;
BOOLEAN MoreEntries = TRUE;
WCHAR Buffer[MAX_PATH * 2] = {0};
POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
do {
Status = NtQueryDirectoryObject(
DirectoryHandle,
Buffer,
sizeof(Buffer),
TRUE, // return single entry
RestartScan,
&Context,
NULL // return length
);
if(NT_SUCCESS(Status)) {
//
// Make sure this name is a symbolic link.
//
if(DirInfo->Name.Length &&
(DirInfo->TypeName.Length >= 24) &&
CharUpperBuff((LPWSTR)DirInfo->TypeName.Buffer,12) &&
!memcmp(DirInfo->TypeName.Buffer,L"SYMBOLICLINK",24)) {
WCHAR EntryName[MAX_PATH];
lstrcpy(EntryName, OldNtPath);
ConcatenatePaths(EntryName,
DirInfo->Name.Buffer,
(DWORD)(-1));
Status = QueryCanonicalName(EntryName, -1, Buffer, sizeof(Buffer));
if (NT_SUCCESS(Status)) {
wcscpy(OldNewNtNames[SlotIndex].OldNtName, EntryName);
wcscpy(OldNewNtNames[SlotIndex].NewNtName, Buffer);
SlotIndex++;
}
}
} else {
MoreEntries = FALSE;
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
ErrorCode = RtlNtStatusToDosError(Status);
}
RestartScan = FALSE;
} while(MoreEntries && (SlotIndex < MaxSlots));
NtClose(DirectoryHandle);
} else {
ErrorCode = RtlNtStatusToDosError(Status);
}
}
if (!ErrorCode && NT_SUCCESS(Status)) {
MappingCount = SlotIndex;
}
}
return MappingCount;
}
DWORD
NtNameToArcPath (
IN PCWSTR NtName,
OUT LPWSTR *ArcPath
)
/*++
Routine Description:
Convert an NT volume name to an ARC path.
Arguments:
NtName - supplies name of drive to be converted.
ArcPath - receives pointer to buffer containing arc path
if the routine is successful. Caller must free with FREE().
Return Value:
Win32 error code indicating outcome.
--*/
{
UNICODE_STRING UnicodeString;
HANDLE DirectoryHandle;
OBJECT_ATTRIBUTES Obja;
NTSTATUS Status;
BOOLEAN RestartScan;
DWORD Context;
BOOL MoreEntries;
LPWSTR ArcName = NULL;
WCHAR Buffer[512];
WCHAR ArcDiskName[MAX_PATH] = {0};
WCHAR NtDiskName[MAX_PATH] = {0};
WCHAR ArcPartitionName[MAX_PATH] = {0};
PWSTR PartitionName = NULL;
PWSTR PartitionNumStr = NULL;
POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer;
DWORD ErrorCode;
ErrorCode = NO_ERROR;
*ArcPath = NULL;
//
// Get hold of the NT disk name
//
PartitionName = NewNtNameToOldNtName(NtName);
if (PartitionName) {
PWSTR PartitionNameStart = PartitionName;
PartitionName = wcsrchr(PartitionName, L'\\');
if (PartitionName && wcsstr(PartitionName, L"Partition")) {
wcsncpy(NtDiskName, PartitionNameStart, PartitionName - PartitionNameStart);
wcscat(NtDiskName, L"\\Partition0");
PartitionNumStr = PartitionName + wcslen(L"\\Partition");
}
}
//
// Open the \ArcName directory.
//
INIT_OBJA(&Obja,&UnicodeString,ArcNameDirectory);
Status = NtOpenDirectoryObject(&DirectoryHandle,DIRECTORY_QUERY,&Obja);
if(NT_SUCCESS(Status)) {
RestartScan = TRUE;
Context = 0;
MoreEntries = TRUE;
do {
Status = NtQueryDirectoryObject(
DirectoryHandle,
Buffer,
sizeof(Buffer),
TRUE, // return single entry
RestartScan,
&Context,
NULL // return length
);
if(NT_SUCCESS(Status)) {
CharLower(DirInfo->Name.Buffer);
//
// Make sure this name is a symbolic link.
//
if(DirInfo->Name.Length
&& (DirInfo->TypeName.Length >= 24)
&& CharUpperBuff((LPWSTR)DirInfo->TypeName.Buffer,12)
&& !memcmp(DirInfo->TypeName.Buffer,L"SYMBOLICLINK",24))
{
WCHAR OldNtName[MAX_PATH] = {0};
ArcName = MALLOC(DirInfo->Name.Length + sizeof(ArcNameDirectory) + sizeof(WCHAR));
if(!ArcName) {
ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
break;
}
lstrcpy(ArcName,ArcNameDirectory);
ConcatenatePaths(ArcName,DirInfo->Name.Buffer,(DWORD)(-1));
//
// We have the entire arc name in ArcName. Now open the first
// level symbolic link.
//
Status = QueryCanonicalName(ArcName, 1, Buffer, sizeof(Buffer));
if (NT_SUCCESS(Status)) {
wcscpy(OldNtName, Buffer);
//
// Now resolve the complete symbolic link
//
Status = QueryCanonicalName(ArcName, -1, Buffer, sizeof(Buffer));
if (NT_SUCCESS(Status)) {
if(!lstrcmpi(Buffer, NtName)) {
*ArcPath = ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR));
} else {
if (!lstrcmpi(OldNtName, NtDiskName)) {
wcscpy(ArcDiskName,
ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR)));
}
}
} else {
if(!lstrcmpi(OldNtName, NtName)) {
*ArcPath = ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR));
}
}
}
if(!(*ArcPath)) {
FREE(ArcName);
ArcName = NULL;
}
}
} else {
MoreEntries = FALSE;
if(Status == STATUS_NO_MORE_ENTRIES) {
Status = STATUS_SUCCESS;
}
ErrorCode = RtlNtStatusToDosError(Status);
}
RestartScan = FALSE;
} while(MoreEntries && !(*ArcPath));
NtClose(DirectoryHandle);
} else {
ErrorCode = RtlNtStatusToDosError(Status);
}
//
// If we found a match for the disk but not for the actual
// partition specified then guess thepartition number
// (based on the current nt partition number )
//
if ((!*ArcPath) && ArcDiskName[0] && PartitionName && PartitionNumStr) {
PWSTR EndPtr = NULL;
ULONG PartitionNumber = wcstoul(PartitionNumStr, &EndPtr, 10);
if (PartitionNumber) {
swprintf(ArcPartitionName,
L"%wspartition(%d)",
ArcDiskName,
PartitionNumber);
*ArcPath = DupString(ArcPartitionName);
ErrorCode = NO_ERROR;
DebugLog( Winnt32LogInformation,
TEXT("\nCould not find arcname mapping for %1 partition.\r\n")
TEXT("Guessing the arcname to be %2"),
0,
NtName,
ArcPartitionName);
}
}
if (ErrorCode == NO_ERROR) {
if(*ArcPath) {
//
// ArcPath points into the middle of a buffer.
// The caller needs to be able to free it, so place it in its
// own buffer here.
//
*ArcPath = DupString(*ArcPath);
if (ArcName) {
FREE(ArcName);
}
if(*ArcPath == NULL) {
ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// No matching drive.
//
ErrorCode = ERROR_INVALID_DRIVE;
}
}
return ErrorCode;
}
DWORD
DriveLetterToArcPath(
IN WCHAR DriveLetter,
OUT LPWSTR *ArcPath
)
/*++
Routine Description:
Convert a drive letter to an ARC path.
This routine relies on the DosDeviceTargets array being set up
beforehand.
Arguments:
DriveLetter - supplies letter of drive to be converted.
ArcPath - receives pointer to buffer containing arc path
if the routine is successful. Caller must free with FREE().
Return Value:
Win32 error code indicating outcome.
--*/
{
LPWSTR NtPath;
NtPath = DosDeviceTargets[(WCHAR)CharUpper((PWCHAR)DriveLetter)-L'A'];
if(!NtPath) {
return(ERROR_INVALID_DRIVE);
}
return NtNameToArcPath (NtPath, ArcPath);
}
DWORD
ArcPathToDriveLetterAndNtName (
IN PCWSTR ArcPath,
OUT PWCHAR DriveLetter,
OUT PWSTR NtName,
IN DWORD BufferSizeInBytes
)
/*++
Routine Description:
Convert an arc path to a drive letter.
This routine relies on the DosDeviceTargets array being set up
beforehand.
Arguments:
ArcPath - specifies arc path to be converted.
DriveLetter - if successful, receives letter of drive.
Return Value:
Win32 error code indicating outcome.
--*/
{
NTSTATUS Status;
WCHAR drive;
LPWSTR arcPath;
DWORD ec;
//
// Assume failure
//
*DriveLetter = 0;
arcPath = MALLOC(((lstrlen(ArcPath)+1)*sizeof(WCHAR)) + sizeof(ArcNameDirectory));
if(!arcPath) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
lstrcpy(arcPath,ArcNameDirectory);
lstrcat(arcPath,L"\\");
lstrcat(arcPath,ArcPath);
Status = QueryCanonicalName(arcPath, -1, NtName, BufferSizeInBytes);
if (NT_SUCCESS(Status)) {
ec = ERROR_INVALID_DRIVE;
for(drive=L'A'; drive<=L'Z'; drive++) {
if(DosDeviceTargets[drive-L'A']
&& !lstrcmpi(NtName,DosDeviceTargets[drive-L'A']))
{
*DriveLetter = drive;
ec = NO_ERROR;
break;
}
}
} else {
ec = RtlNtStatusToDosError(Status);
}
FREE(arcPath);
return(ec);
}
DWORD
InitDriveNameTranslations(
VOID
)
{
WCHAR DriveName[15];
WCHAR Drive;
WCHAR Buffer[512];
NTSTATUS status;
swprintf(DriveName, L"\\DosDevices\\c:");
//
// Calculate NT names for all local hard disks C-Z.
//
for(Drive=L'A'; Drive<=L'Z'; Drive++) {
DosDeviceTargets[Drive-L'A'] = NULL;
if(MyGetDriveType(Drive) == DRIVE_FIXED) {
DriveName[12] = Drive;
status = QueryCanonicalName(DriveName, -1, Buffer, sizeof(Buffer));
if (NT_SUCCESS(status)) {
DosDeviceTargets[Drive-L'A'] = DupString(Buffer);
if(!DosDeviceTargets[Drive-L'A']) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
}
}
}
//
// Initialize old Nt Parition names to new partition name
// mapping
//
InitOldToNewNtNameTranslations();
return(NO_ERROR);
}
DWORD
DetermineSystemPartitions(
VOID
)
{
LPWSTR *SyspartComponents;
DWORD NumSyspartComponents;
DWORD d;
DWORD rc;
UINT u;
WCHAR drive;
WCHAR DeviceNtName[512];
SyspartComponents = BootVarComponents[BootVarSystemPartition];
NumSyspartComponents = BootVarComponentCount[BootVarSystemPartition];
SystemPartitionNtNames = MALLOC ((NumSyspartComponents + 1) * sizeof (PWSTR));
if (!SystemPartitionNtNames) {
return ERROR_NOT_ENOUGH_MEMORY;
}
ZeroMemory(SystemPartitionNtNames, (NumSyspartComponents + 1) * sizeof (PWSTR));
ZeroMemory(SystemPartitionDriveLetters,27*sizeof(WCHAR));
//
// Convert each system partition to a drive letter.
//
for(d=0; d<NumSyspartComponents; d++) {
//
// check for duplicates
//
if (SystemPartitionCount > 0) {
for (u = 0; u < SystemPartitionCount; u++) {
if (lstrcmpi (SyspartComponents[d], SystemPartitionNtNames[u]) == 0) {
break;
}
}
if (u < SystemPartitionCount) {
continue;
}
}
rc = ArcPathToDriveLetterAndNtName (
SyspartComponents[d],
&drive,
DeviceNtName,
(DWORD) sizeof (DeviceNtName)
);
if(rc == ERROR_NOT_ENOUGH_MEMORY) {
return(ERROR_NOT_ENOUGH_MEMORY);
}
if (rc == ERROR_SUCCESS) {
SystemPartitionDriveLetters[SystemPartitionCount] = drive;
}
SystemPartitionNtNames[SystemPartitionCount++] = DupString (DeviceNtName);
}
return(NO_ERROR);
}
DWORD
DoInitializeArcStuff(
VOID
)
{
DWORD ec;
DWORD var;
UNICODE_STRING UnicodeString;
NTSTATUS Status;
WCHAR Buffer[4096];
ec = InitDriveNameTranslations();
if(ec != NO_ERROR) {
goto c0;
}
//
// Get relevent boot vars.
//
// Enable privilege -- since we check this privilege up front
// in main() this should not fail.
//
if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) {
ec = ERROR_ACCESS_DENIED;
goto c0;
}
for(var=0; var<BootVarMax; var++) {
RtlInitUnicodeString(&UnicodeString,BootVarNames[var]);
Status = NtQuerySystemEnvironmentValue(
&UnicodeString,
Buffer,
sizeof(Buffer) / sizeof(WCHAR),
NULL
);
if(NT_SUCCESS(Status)) {
BootVarValues[var] = DupString(Buffer);
OriginalBootVarValues[var] = DupString(Buffer);
} else {
//
// We may get back failure if the variable is empty.
//
BootVarValues[var] = DupString(L"");
OriginalBootVarValues[var] = DupString(L"");
}
if(!BootVarValues[var] || !OriginalBootVarValues[var]) {
ec = ERROR_NOT_ENOUGH_MEMORY;
goto c2;
}
ec = GetVarComponents(
BootVarValues[var],
&BootVarComponents[var],
&BootVarComponentCount[var]
);
if(ec != NO_ERROR) {
goto c2;
}
//
// Track the variable with the most number of components.
//
if(BootVarComponentCount[var] > LargestComponentCount) {
LargestComponentCount = BootVarComponentCount[var];
}
}
//
// Get original countdown and autoload values.
// If not successful, oh well, we won't be able to restore them
// if the user cancels.
//
RtlInitUnicodeString(&UnicodeString,szCOUNTDOWN);
Status = NtQuerySystemEnvironmentValue(
&UnicodeString,
Buffer,
sizeof(Buffer) / sizeof(WCHAR),
NULL
);
if(NT_SUCCESS(Status)) {
OriginalCountdown = DupString(Buffer);
} else {
OriginalCountdown = DupString(L"");
}
RtlInitUnicodeString(&UnicodeString,szAUTOLOAD);
Status = NtQuerySystemEnvironmentValue(
&UnicodeString,
Buffer,
sizeof(Buffer) / sizeof(WCHAR),
NULL
);
if(NT_SUCCESS(Status)) {
OriginalAutoload = DupString(Buffer);
} else {
OriginalAutoload = DupString(L"NO");
}
ec = DetermineSystemPartitions();
if(ec != NO_ERROR) {
goto c2;
}
return(NO_ERROR);
c2:
c0:
return(ec);
}
BOOL
ArcInitializeArcStuff(
IN HWND Parent
)
{
DWORD ec;
BOOL b;
HKEY key;
DWORD type;
DWORD size;
PBYTE buffer = NULL;
DWORD i;
#if defined(EFI_NVRAM_ENABLED)
//
// Try to initialize as an EFI machine. If we're on an EFI machine,
// this will succeed. Otherwise it will fail, in which case we try
// to initialize as an ARC machine.
//
ec = InitializeEfiStuff(Parent);
if (!IsEfi())
#endif
{
//
// Try to initialize as an ARC machine. This is expect to
// always succeed.
//
ec = DoInitializeArcStuff();
}
switch(ec) {
case NO_ERROR:
#if defined(EFI_NVRAM_ENABLED)
//
// On an EFI machine, the rest of this code (determining system
// partitions) is not necessary.
//
if (IsEfi()) {
b = TRUE;
} else
#endif
{
//
// Make sure there is at least one valid system partition.
//
if(!SystemPartitionCount) {
MessageBoxFromMessage(
Parent,
MSG_SYSTEM_PARTITION_INVALID,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
b = FALSE;
} else {
i = 0;
//
// On ARC machines we set up a local boot directory that is
// placed in the root of the system partition.
//
//
// read the SystemPartition value from registry
//
ec = RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("System\\Setup"), &key);
if (ec == ERROR_SUCCESS) {
ec = RegQueryValueEx (key, TEXT("SystemPartition"), NULL, &type, NULL, &size);
if (ec == ERROR_SUCCESS && type == REG_SZ) {
buffer = MALLOC (size);
if (buffer) {
ec = RegQueryValueEx (key, TEXT("SystemPartition"), NULL, &type, buffer, &size);
if (ec != ERROR_SUCCESS) {
FREE (buffer);
buffer = NULL;
}
}
}
RegCloseKey (key);
}
#if defined(EFI_NVRAM_ENABLED)
//
// we just trust the value that comes from the regkey -- EFI
// systems only have one system partition, so it doesn't make
// sense to try to match this up against a list of potential
// system partitions.
//
SystemPartitionNtName = (PWSTR) buffer;
#else
//
// look for this system partition to make sure things are OK
//
if (buffer) {
while (i < SystemPartitionCount) {
if (lstrcmpi (SystemPartitionNtNames[i], (PCTSTR)buffer) == 0) {
SystemPartitionNtName = SystemPartitionNtNames[i];
break;
}
i++;
}
FREE (buffer);
}
#endif
if(!SystemPartitionNtName) {
MessageBoxFromMessage(
Parent,
MSG_SYSTEM_PARTITION_INVALID,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
b = FALSE;
break;
}
#if !defined(EFI_NVRAM_ENABLED)
if (SystemPartitionDriveLetters[i]) {
SystemPartitionDriveLetter = ForcedSystemPartition
? ForcedSystemPartition
: SystemPartitionDriveLetters[i];
LocalBootDirectory[0] = SystemPartitionDriveLetter;
LocalBootDirectory[1] = TEXT(':');
LocalBootDirectory[2] = TEXT('\\');
LocalBootDirectory[3] = 0;
} else
#endif
{
// SystemPartitionNtNtname is valid at this point thanks to
// the check above.
size = sizeof(GLOBAL_ROOT) +
lstrlen(SystemPartitionNtName)*sizeof(WCHAR) +
sizeof(WCHAR) + sizeof(WCHAR);
SystemPartitionVolumeGuid = MALLOC (size);
if(!SystemPartitionVolumeGuid) {
goto MemoryError;
}
lstrcpy (SystemPartitionVolumeGuid, GLOBAL_ROOT);
lstrcat (SystemPartitionVolumeGuid, SystemPartitionNtName);
lstrcat (SystemPartitionVolumeGuid, L"\\");
lstrcpy (LocalBootDirectory, SystemPartitionVolumeGuid);
}
b = TRUE;
}
}
break;
case ERROR_NOT_ENOUGH_MEMORY:
MemoryError:
MessageBoxFromMessage(
Parent,
MSG_OUT_OF_MEMORY,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
b = FALSE;
break;
default:
//
// Some other unknown error.
//
MessageBoxFromMessage(
Parent,
MSG_COULDNT_READ_NVRAM,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
b = FALSE;
break;
}
#if defined(EFI_NVRAM_ENABLED)
//
// make sure the system partition is on a GPT disk.
//
if (b) {
HANDLE hDisk;
PARTITION_INFORMATION_EX partitionEx;
DWORD sizePartitionEx = 0;
UNICODE_STRING uString;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status;
PWSTR p,q;
b = FALSE;
MYASSERT( SystemPartitionVolumeGuid != NULL );
//
// SystemPartitionVolumeGuid may have a '\' at the end of it.
// delete this character or we won't open the partition properly
//
p = DupString( SystemPartitionVolumeGuid + wcslen(GLOBAL_ROOT) );
if (p) {
if (*(p+wcslen(p)-1) == L'\\') {
*(p+wcslen(p)-1) = L'\0';
}
INIT_OBJA( &ObjectAttributes, &uString, p );
Status = NtCreateFile(&hDisk,
(ACCESS_MASK)FILE_GENERIC_READ,
&ObjectAttributes,
&IoStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (NT_SUCCESS(Status)) {
Status = NtDeviceIoControlFile(
hDisk,
NULL,
NULL,
NULL,
&IoStatus,
IOCTL_DISK_GET_PARTITION_INFO_EX,
NULL,
0,
&partitionEx,
sizeof(PARTITION_INFORMATION_EX) );
if (NT_SUCCESS(Status)) {
if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT) {
b = TRUE;
}
} else if (Status == STATUS_INVALID_DEVICE_REQUEST) {
//
// we must be running on an older build where the IOCTL
// code is different
//
Status = NtDeviceIoControlFile(
hDisk,
NULL,
NULL,
NULL,
&IoStatus,
CTL_CODE(IOCTL_DISK_BASE, 0x0012, METHOD_BUFFERED, FILE_READ_ACCESS),
NULL,
0,
&partitionEx,
sizeof(PARTITION_INFORMATION_EX) );
if (NT_SUCCESS(Status)) {
if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT) {
b = TRUE;
}
}
}
NtClose(hDisk);
}
FREE( p );
}
if (!b) {
MessageBoxFromMessage(
Parent,
MSG_SYSTEM_PARTITIONTYPE_INVALID,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
}
}
#endif
return(b);
}
#if defined(EFI_NVRAM_ENABLED)
DWORD
LocateEfiSystemPartition(
OUT PWSTR SystemPartitionName
)
/*++
Routine Description:
Locates the EFI system partition on a GPT disk
by scanning all the available hard disks.
Arguments:
SystemPartitionName : Buffer to receive system partition
name, if one is present
Return Value:
Win32 error code indicating outcome.
--*/
{
DWORD ErrorCode = ERROR_BAD_ARGUMENTS;
if (SystemPartitionName) {
SYSTEM_DEVICE_INFORMATION SysDevInfo;
NTSTATUS Status;
*SystemPartitionName = UNICODE_NULL;
//
// Get hold of number of hard disks on the system
//
ZeroMemory(&SysDevInfo, sizeof(SYSTEM_DEVICE_INFORMATION));
Status = NtQuerySystemInformation(SystemDeviceInformation,
&SysDevInfo,
sizeof(SYSTEM_DEVICE_INFORMATION),
NULL);
if (NT_SUCCESS(Status)) {
ULONG HardDiskCount = SysDevInfo.NumberOfDisks;
ULONG CurrentDisk;
ULONG BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) +
(sizeof(PARTITION_INFORMATION_EX) * 128);
PCHAR Buffer = MALLOC(BufferSize);
BOOL Found = FALSE;
if (Buffer) {
//
// Go through each disk and find out its partition
// layout
//
for (CurrentDisk = 0;
(!Found && (CurrentDisk < HardDiskCount));
CurrentDisk++) {
WCHAR DiskName[MAX_PATH];
HANDLE DiskHandle;
swprintf(DiskName,
L"\\\\.\\PHYSICALDRIVE%d",
CurrentDisk);
DiskHandle = CreateFile(DiskName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ((DiskHandle) &&
(DiskHandle != INVALID_HANDLE_VALUE)) {
DWORD ReturnSize = 0;
ZeroMemory(Buffer, BufferSize);
if (DeviceIoControl(DiskHandle,
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
NULL,
0,
Buffer,
BufferSize,
&ReturnSize,
NULL)) {
//
// Only search in GPT disks on IA64
//
PDRIVE_LAYOUT_INFORMATION_EX DriveLayout;
DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)Buffer;
if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) {
ULONG PartitionIndex;
for (PartitionIndex = 0;
(PartitionIndex < DriveLayout->PartitionCount);
PartitionIndex++) {
PPARTITION_INFORMATION_EX Partition;
GUID *PartitionType;
Partition = DriveLayout->PartitionEntry + PartitionIndex;
PartitionType = &(Partition->Gpt.PartitionType);
if (IsEqualGUID(PartitionType, &PARTITION_SYSTEM_GUID)) {
swprintf(SystemPartitionName,
L"\\Device\\Harddisk%d\\Partition%d",
CurrentDisk,
Partition->PartitionNumber
);
Found = TRUE;
break;
}
}
}
}
CloseHandle(DiskHandle);
}
}
FREE(Buffer);
} else {
ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
}
if (!Found) {
ErrorCode = ERROR_FILE_NOT_FOUND;
} else {
ErrorCode = ERROR_SUCCESS;
}
}
}
return ErrorCode;
}
DWORD
InitializeEfiStuff(
IN HWND Parent
)
{
DWORD ec;
NTSTATUS status;
HMODULE h;
WCHAR dllName[MAX_PATH];
ULONG length;
HKEY key;
DWORD type;
LONG i;
PMY_BOOT_ENTRY bootEntry;
PMY_BOOT_ENTRY previousBootEntry;
MYASSERT(!IsEfiChecked);
//
// IsEfi() uses IsEfiMachine to determine its return value. Assume that
// we're not on an EFI machine.
//
IsEfiChecked = TRUE;
IsEfiMachine = FALSE;
//
// Enable the privilege that is necessary to query/set NVRAM.
//
if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) {
ec = GetLastError();
return ec;
}
//
// Load ntdll.dll from the system directory.
//
GetSystemDirectory(dllName, MAX_PATH);
ConcatenatePaths(dllName, TEXT("ntdll.dll"), MAX_PATH);
h = LoadLibrary(dllName);
if (h == NULL) {
ec = GetLastError();
return ec;
}
//
// Get the addresses of the NVRAM APIs that we need to use. If any of
// these APIs are not available, this must be a pre-EFI NVRAM build.
//
(FARPROC)AddBootEntry = GetProcAddress(h, "NtAddBootEntry");
(FARPROC)DeleteBootEntry = GetProcAddress(h, "NtDeleteBootEntry");
(FARPROC)EnumerateBootEntries = GetProcAddress(h, "NtEnumerateBootEntries");
(FARPROC)QueryBootEntryOrder = GetProcAddress(h, "NtQueryBootEntryOrder");
(FARPROC)SetBootEntryOrder = GetProcAddress(h, "NtSetBootEntryOrder");
(FARPROC)QueryBootOptions = GetProcAddress(h, "NtQueryBootOptions");
(FARPROC)SetBootOptions = GetProcAddress(h, "NtSetBootOptions");
if ((AddBootEntry == NULL) ||
(DeleteBootEntry == NULL) ||
(EnumerateBootEntries == NULL) ||
(QueryBootEntryOrder == NULL) ||
(SetBootEntryOrder == NULL) ||
(QueryBootOptions == NULL) ||
(SetBootOptions == NULL)) {
return ERROR_OLD_WIN_VERSION;
}
//
// Get the global system boot options. If the call fails with
// STATUS_NOT_IMPLEMENTED, this is not an EFI machine.
//
length = 0;
status = QueryBootOptions(NULL, &length);
if (status != STATUS_NOT_IMPLEMENTED) {
IsEfiMachine = TRUE;
}
if (status != STATUS_BUFFER_TOO_SMALL) {
if (status == STATUS_SUCCESS) {
status = STATUS_UNSUCCESSFUL;
}
return RtlNtStatusToDosError(status);
}
BootOptions = MALLOC(length);
OriginalBootOptions = MALLOC(length);
if ((BootOptions == NULL) || (OriginalBootOptions == NULL)) {
return RtlNtStatusToDosError(ERROR_NOT_ENOUGH_MEMORY);
}
status = QueryBootOptions(BootOptions, &length);
if (status != STATUS_SUCCESS) {
FREE(BootOptions);
FREE(OriginalBootOptions);
BootOptions = NULL;
OriginalBootOptions = NULL;
return RtlNtStatusToDosError(status);
}
memcpy(OriginalBootOptions, BootOptions, length);
//
// Get the system boot order list.
//
length = 0;
status = QueryBootEntryOrder(NULL, &length);
if (status != STATUS_BUFFER_TOO_SMALL) {
if (status == STATUS_SUCCESS) {
status = STATUS_UNSUCCESSFUL;
}
return RtlNtStatusToDosError(status);
}
OriginalBootEntryOrder = MALLOC(length * sizeof(ULONG));
if (OriginalBootEntryOrder == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
status = QueryBootEntryOrder(OriginalBootEntryOrder, &length);
if (status != STATUS_SUCCESS) {
FREE(OriginalBootEntryOrder);
OriginalBootEntryOrder = NULL;
return RtlNtStatusToDosError(status);
}
OriginalBootEntryOrderCount = length;
//
// Get all existing boot entries.
//
length = 0;
status = EnumerateBootEntries(NULL, &length);
if (status != STATUS_BUFFER_TOO_SMALL) {
if (status == STATUS_SUCCESS) {
status = STATUS_UNSUCCESSFUL;
}
return RtlNtStatusToDosError(status);
}
BootEntries = MALLOC(length);
if (BootEntries == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
status = EnumerateBootEntries(BootEntries, &length);
if (status != STATUS_SUCCESS) {
FREE(BootEntries);
BootEntries = NULL;
return RtlNtStatusToDosError(status);
}
//
// Initialize drive name translations, which are needed for converting
// the boot entries into their internal representations.
//
ec = InitDriveNameTranslations();
if(ec != NO_ERROR) {
return ec;
}
//
// Convert the boot entries into an internal representation.
//
status = ConvertBootEntries();
if (!NT_SUCCESS(status)) {
return RtlNtStatusToDosError(status);
}
//
// Free the enumeration buffer.
//
FREE(BootEntries);
BootEntries = NULL;
//
// Boot entries are returned in an unspecified order. They are currently
// in the MyBootEntries list in the order in which they were returned.
// Sort the boot entry list based on the boot order. Do this by walking
// the boot order array backwards, reinserting the entry corresponding to
// each element of the array at the head of the list.
//
for (i = (LONG)OriginalBootEntryOrderCount - 1; i >= 0; i--) {
for (previousBootEntry = NULL, bootEntry = MyBootEntries;
bootEntry != NULL;
previousBootEntry = bootEntry, bootEntry = bootEntry->Next) {
if (bootEntry->NtBootEntry.Id == OriginalBootEntryOrder[i] ) {
//
// We found the boot entry with this ID. If it's not already
// at the front of the list, move it there.
//
bootEntry->Status |= MBE_STATUS_ORDERED;
if (previousBootEntry != NULL) {
previousBootEntry->Next = bootEntry->Next;
bootEntry->Next = MyBootEntries;
MyBootEntries = bootEntry;
} else {
ASSERT(MyBootEntries == bootEntry);
}
break;
}
}
}
//
// Get the NT name of the system partition from the registry.
//
ec = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("System\\Setup"), &key);
if (ec == ERROR_SUCCESS) {
ec = RegQueryValueEx(key, TEXT("SystemPartition"), NULL, &type, NULL, &length);
if (ec == ERROR_SUCCESS) {
if (type == REG_SZ) {
SystemPartitionNtName = MALLOC(length);
if (SystemPartitionNtName != NULL) {
ec = RegQueryValueEx(
key,
TEXT("SystemPartition"),
NULL,
&type,
(PBYTE)SystemPartitionNtName,
&length);
if (ec != ERROR_SUCCESS) {
FREE(SystemPartitionNtName);
}
} else {
return ERROR_NOT_ENOUGH_MEMORY;
}
} else {
return ERROR_INVALID_PARAMETER;
}
}
RegCloseKey (key);
}
if (ec != NO_ERROR) {
if (IsWinPEMode()) {
WCHAR OldSysPartName[MAX_PATH] = {0};
WCHAR NewSysPartName[MAX_PATH] = {0};
ec = LocateEfiSystemPartition(OldSysPartName);
if ((ec == NO_ERROR) && OldSysPartName[0]) {
NTSTATUS Status = QueryCanonicalName(OldSysPartName,
-1,
NewSysPartName,
sizeof(NewSysPartName));
if (NT_SUCCESS(Status) && NewSysPartName[0]) {
SystemPartitionNtName = DupString(NewSysPartName);
} else {
ec = ERROR_FILE_NOT_FOUND;
}
}
if ((ec == NO_ERROR) && (NewSysPartName[0] == UNICODE_NULL)) {
ec = ERROR_FILE_NOT_FOUND;
}
}
if (ec != NO_ERROR) {
return ec;
}
}
//
// Get the volume name for the NT name.
//
length = sizeof(GLOBAL_ROOT) +
lstrlen(SystemPartitionNtName)*sizeof(WCHAR) +
sizeof(WCHAR) + sizeof(WCHAR);
SystemPartitionVolumeGuid = MALLOC (length);
if(!SystemPartitionVolumeGuid) {
return ERROR_NOT_ENOUGH_MEMORY;
}
lstrcpy (SystemPartitionVolumeGuid, GLOBAL_ROOT);
lstrcat (SystemPartitionVolumeGuid, SystemPartitionNtName);
lstrcat (SystemPartitionVolumeGuid, L"\\");
lstrcpy (LocalBootDirectory, SystemPartitionVolumeGuid);
return NO_ERROR;
} // InitializeEfiStuff
#endif // defined(EFI_NVRAM_ENABLED)
/////////////////////////////////////////////////////////////////////
//
// Everything above this line is concerned with reading NV-RAM.
// Everything below this line is concerned with setting NV-RAM.
//
/////////////////////////////////////////////////////////////////////
BOOL
DoSetNvRamVar(
IN LPCWSTR VarName,
IN LPCWSTR VarValue
)
{
UNICODE_STRING U1,U2;
RtlInitUnicodeString(&U1,VarName);
RtlInitUnicodeString(&U2,VarValue);
return(NT_SUCCESS(NtSetSystemEnvironmentValue(&U1,&U2)));
}
BOOL
WriteNewBootSetVar(
IN DWORD var,
IN PTSTR NewPart
)
{
WCHAR Buffer[2048];
DWORD i;
//
// Write the new part first.
//
lstrcpy(Buffer,NewPart);
//
// Append all components that were not deleted.
//
for(i=0; i<BootVarComponentCount[var]; i++) {
if(BootVarComponents[var][i]) {
lstrcat(Buffer,L";");
lstrcat(Buffer,BootVarComponents[var][i]);
}
}
//
// Remember new value for this var.
//
if(BootVarValues[var]) {
FREE(BootVarValues[var]);
}
BootVarValues[var] = DupString(Buffer);
//
// Write the var into nvram and return.
//
return(DoSetNvRamVar(BootVarNames[var],BootVarValues[var]));
}
BOOL
WriteBootSet(
VOID
)
{
DWORD set;
DWORD var;
LPWSTR SystemPartition;
WCHAR Buffer[2048];
LPWSTR LocalSourceArc;
LPWSTR OsLoader;
WCHAR LoadId[128];
BOOL b;
CleanUpNvRam = TRUE;
//
// Find and remove any remnants of previously attempted
// winnt32 runs. Such runs are identified by 'winnt32'
// in their osloadoptions.
//
#if defined(EFI_NVRAM_ENABLED)
if (IsEfi()) {
NTSTATUS status;
PMY_BOOT_ENTRY bootEntry;
PWSTR NtPath;
//
// EFI machine. Walk the boot entry list.
//
for (bootEntry = MyBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
if (IS_BOOT_ENTRY_WINDOWS(bootEntry)) {
if (!lstrcmpi(bootEntry->OsLoadOptions, L"WINNT32")) {
//
// Delete this boot entry. Note that we don't update the
// boot entry order list at this point. CreateBootEntry()
// will do that.
//
status = DeleteBootEntry(bootEntry->NtBootEntry.Id);
bootEntry->Status |= MBE_STATUS_DELETED;
}
}
}
//
// Now create a new boot entry for textmode setup.
//
MYASSERT(LocalSourceDrive);
NtPath = DosDeviceTargets[(WCHAR)CharUpper((PWCHAR)LocalSourceDrive)-L'A'];
LoadString(hInst,IDS_RISCBootString,LoadId,sizeof(LoadId)/sizeof(TCHAR));
b = CreateBootEntry(
SystemPartitionNtName,
L"\\" SETUPLDR_FILENAME,
NtPath,
LocalSourceWithPlatform + 2,
L"WINNT32",
LoadId
);
if (b) {
//
// Set up for automatic startup, 10 second countdown. We don't
// care if this fails.
//
// Set the boot entry we added to be booted automatically on
// the next boot, without waiting for a timeout at the boot menu.
//
// NB: CreateBootEntry() sets BootOptions->NextBootEntryId.
//
BootOptions->Timeout = 10;
status = SetBootOptions(
BootOptions,
BOOT_OPTIONS_FIELD_TIMEOUT | BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID
);
}
return b;
}
#endif // defined(EFI_NVRAM_ENABLED)
//
// We get here if we're NOT on an EFI machine.
//
// Find and remove any remnants of previously attempted
// winnt32 runs. Such runs are identified by 'winnt32'
// in their osloadoptions.
//
for(set=0; set<min(LargestComponentCount,BootVarComponentCount[BootVarOsLoadOptions]); set++) {
//
// See if the os load options indicate that this is a winnt32 set.
//
if(!lstrcmpi(BootVarComponents[BootVarOsLoadOptions][set],L"WINNT32")) {
//
// Delete this boot set.
//
for(var=0; var<BootVarMax; var++) {
if(set < BootVarComponentCount[var]) {
FREE(BootVarComponents[var][set]);
BootVarComponents[var][set] = NULL;
}
}
}
}
//
// Now we want to write out each variable with the appropriate
// part of the new boot set added to the front.
//
if (SystemPartitionDriveLetter) {
if(DriveLetterToArcPath(SystemPartitionDriveLetter,&SystemPartition) != NO_ERROR) {
return(FALSE);
}
} else {
if(NtNameToArcPath (SystemPartitionNtName, &SystemPartition) != NO_ERROR) {
return(FALSE);
}
}
MYASSERT (LocalSourceDrive);
if(DriveLetterToArcPath(LocalSourceDrive,&LocalSourceArc) != NO_ERROR) {
FREE(SystemPartition);
return(FALSE);
}
LoadString(hInst,IDS_RISCBootString,LoadId,sizeof(LoadId)/sizeof(TCHAR));
lstrcpy(Buffer,SystemPartition);
lstrcat(Buffer,L"\\" SETUPLDR_FILENAME);
OsLoader = DupString(Buffer);
//
// System partition: use the selected system partition as the
// new system partition component.
//
if(WriteNewBootSetVar(BootVarSystemPartition,SystemPartition)
//
// Os Loader: use the system partition + setupldr as the
// new os loader component.
//
&& WriteNewBootSetVar(BootVarOsLoader,OsLoader)
//
// Os Load Partition: use the local source drive as the
// new os load partition component.
//
&& WriteNewBootSetVar(BootVarOsLoadPartition,LocalSourceArc)
//
// Os Load Filename: use the platform-specific local source directory
// as the new os load filename component (do not include the drive letter).
//
&& WriteNewBootSetVar(BootVarOsLoadFilename,LocalSourceWithPlatform+2)
//
// Os Load Options: use WINNT32 as the new os load options component.
//
&& WriteNewBootSetVar(BootVarOsLoadOptions,L"WINNT32")
//
// Load Identifier: use a string we get from the resources as the
// new load identifier component.
//
&& WriteNewBootSetVar(BootVarLoadIdentifier,LoadId))
{
//
// Set up for automatic startup, 10 second countdown.
// Note the order so that if setting countdown fails we don't
// set of for autoload. Also note that we don't really care
// if this fails.
//
if(DoSetNvRamVar(szCOUNTDOWN,L"10")) {
DoSetNvRamVar(szAUTOLOAD,L"YES");
}
b = TRUE;
} else {
//
// Setting nv-ram failed. Code in cleanup.c will come along and
// restore to original state later.
//
b = FALSE;
}
FREE(SystemPartition);
FREE(LocalSourceArc);
FREE(OsLoader);
return(b);
}
BOOL
SetUpNvRam(
IN HWND ParentWindow
)
{
if(!WriteBootSet()) {
MessageBoxFromMessage(
ParentWindow,
MSG_COULDNT_WRITE_NVRAM,
FALSE,
AppTitleStringId,
MB_OK | MB_ICONERROR | MB_TASKMODAL
);
return(FALSE);
}
return(TRUE);
}
BOOL
RestoreNvRam(
VOID
)
{
UINT var;
BOOL b;
b = TRUE;
if(CleanUpNvRam) {
#if defined(EFI_NVRAM_ENABLED)
if (IsEfi()) {
NTSTATUS status;
PMY_BOOT_ENTRY bootEntry;
//
// EFI machine. Walk the boot entry list. For any boot entry that
// was added, delete it.
//
for (bootEntry = MyBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
if (IS_BOOT_ENTRY_COMMITTED(bootEntry)) {
MYASSERT(IS_BOOT_ENTRY_NEW(bootEntry));
status = DeleteBootEntry(bootEntry->NtBootEntry.Id);
if (!NT_SUCCESS(status)) {
b = FALSE;
}
}
}
//
// Restore the original boot order list and the original timeout.
//
status = SetBootEntryOrder(OriginalBootEntryOrder, OriginalBootEntryOrderCount);
if (!NT_SUCCESS(status)) {
b = FALSE;
}
status = SetBootOptions(OriginalBootOptions, BOOT_OPTIONS_FIELD_TIMEOUT);
if (!NT_SUCCESS(status)) {
b = FALSE;
}
}
} else {
#endif // defined(EFI_NVRAM_ENABLED)
for(var=0; var<BootVarMax; var++) {
if(!DoSetNvRamVar(BootVarNames[var],OriginalBootVarValues[var])) {
b = FALSE;
}
}
if(OriginalAutoload) {
if(!DoSetNvRamVar(szAUTOLOAD,OriginalAutoload)) {
b = FALSE;
}
}
if(OriginalCountdown) {
if(!DoSetNvRamVar(szCOUNTDOWN,OriginalCountdown)) {
b = FALSE;
}
}
}
return(b);
}
VOID
MigrateBootVarData(
VOID
)
/*++
Routine Description:
This routine retreives any boot data we want to migrate into a global
variable so that it can be written into winnt.sif.
Currently we only retreive the countdown
Arguments:
None
Return Value:
None. updates the Timeout global variable
--*/
{
UNICODE_STRING UnicodeString;
NTSTATUS Status;
WCHAR Buffer[4096];
MYASSERT(IsArc());
//
// If this is an EFI machine, use the cached BootOptions to get the timeout.
// (See IsEfi().) Otherwise, use the old version of the system service to
// query the "COUNTDOWN" variable.
//
#if defined(EFI_NVRAM_ENABLED)
if (IsEfi()) {
MYASSERT(BootOptions != NULL);
swprintf( Timeout, L"%d", BootOptions->Timeout );
} else
#endif // defined(EFI_NVRAM_ENABLED)
{
RtlInitUnicodeString(&UnicodeString,szCOUNTDOWN);
Status = NtQuerySystemEnvironmentValue(
&UnicodeString,
Buffer,
sizeof(Buffer) / sizeof(WCHAR),
NULL
);
if(NT_SUCCESS(Status)) {
lstrcpy(Timeout,Buffer);
}
}
}
#if defined(_X86_)
BOOL
IsArc(
VOID
)
/*++
Routine Description:
Run time check to determine if this is an Arc system. We attempt to read an
Arc variable using the Hal. This will fail for Bios based systems.
Arguments:
None
Return Value:
True = This is an Arc system.
--*/
{
UNICODE_STRING UnicodeString;
NTSTATUS Status;
WCHAR Buffer[4096];
//
// If we've already done the check once, don't bother doing it again.
//
if (IsArcChecked) {
return IsArcMachine;
}
IsArcChecked = TRUE;
IsArcMachine = FALSE;
if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) {
return FALSE; // need better error handling?
}
//
// Get the env var into the temp buffer.
//
RtlInitUnicodeString(&UnicodeString,BootVarNames[BootVarOsLoader]);
Status = NtQuerySystemEnvironmentValue(
&UnicodeString,
Buffer,
sizeof(Buffer)/sizeof(WCHAR),
NULL
);
if (NT_SUCCESS(Status)) {
IsArcMachine = TRUE;
}
return IsArcMachine;
}
#endif // defined(_X86_)
#if defined(EFI_NVRAM_ENABLED)
BOOL
IsEfi(
VOID
)
/*++
Routine Description:
Run time check to determine if this is an EFI system.
Arguments:
None
Return Value:
True = This is an EFI system.
--*/
{
//
// InitializeEfiStuff() must be called first to do the actual check.
//
MYASSERT(IsEfiChecked);
return IsEfiMachine;
} // IsEfi
NTSTATUS
ConvertBootEntries(
VOID
)
/*++
Routine Description:
Convert boot entries read from EFI NVRAM into our internal format.
Arguments:
None.
Return Value:
NTSTATUS - Not STATUS_SUCCESS if an unexpected error occurred.
--*/
{
PBOOT_ENTRY_LIST bootEntryList;
PBOOT_ENTRY bootEntry;
PBOOT_ENTRY bootEntryCopy;
PMY_BOOT_ENTRY myBootEntry;
PMY_BOOT_ENTRY previousEntry;
PWINDOWS_OS_OPTIONS osOptions;
ULONG length;
bootEntryList = BootEntries;
previousEntry = NULL;
while (TRUE) {
bootEntry = &bootEntryList->BootEntry;
//
// Calculate the length of our internal structure. This includes
// the base part of MY_BOOT_ENTRY plus the NT BOOT_ENTRY.
//
length = FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry) + bootEntry->Length;
myBootEntry = MALLOC(length);
if (myBootEntry == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(myBootEntry, length);
//
// Link the new entry into the list.
//
if (previousEntry != NULL) {
previousEntry->Next = myBootEntry;
} else {
MyBootEntries = myBootEntry;
}
previousEntry = myBootEntry;
//
// Copy the NT BOOT_ENTRY into the allocated buffer.
//
bootEntryCopy = &myBootEntry->NtBootEntry;
//
// work around till bootentry has the correct length specified
//
__try {
memcpy(bootEntryCopy, bootEntry, bootEntry->Length);
}
__except(EXCEPTION_EXECUTE_HANDLER) {
if (bootEntry->Length > sizeof(ULONG)) {
bootEntry->Length -= sizeof(ULONG);
memcpy(bootEntryCopy, bootEntry, bootEntry->Length);
} else {
//
// Lets atleast AV rather than having invalid
// in memory data structures
//
memcpy(bootEntryCopy, bootEntry, bootEntry->Length);
}
}
//
// Fill in the base part of the structure.
//
myBootEntry->Next = NULL;
myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + length - 1;
myBootEntry->FriendlyName = ADD_OFFSET(bootEntryCopy, FriendlyNameOffset);
myBootEntry->FriendlyNameLength = (wcslen(myBootEntry->FriendlyName) + 1) * sizeof(WCHAR);
myBootEntry->BootFilePath = ADD_OFFSET(bootEntryCopy, BootFilePathOffset);
//
// If this is an NT boot entry, capture the NT-specific information in
// the OsOptions.
//
osOptions = (PWINDOWS_OS_OPTIONS)bootEntryCopy->OsOptions;
if (!IS_BOOT_ENTRY_WINDOWS(myBootEntry)) {
//
// The original implementation of NtEnumerateBootEntries() didn't
// set BOOT_ENTRY_ATTRIBUTE_WINDOWS, so we need to check for that
// here.
//
if ((bootEntryCopy->OsOptionsLength >= FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions)) &&
(strcmp(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE) == 0)) {
myBootEntry->NtBootEntry.Attributes |= BOOT_ENTRY_ATTRIBUTE_WINDOWS;
}
}
if (IS_BOOT_ENTRY_WINDOWS(myBootEntry)) {
myBootEntry->OsLoadOptions = osOptions->OsLoadOptions;
myBootEntry->OsLoadOptionsLength = (wcslen(myBootEntry->OsLoadOptions) + 1) * sizeof(WCHAR);
myBootEntry->OsFilePath = ADD_OFFSET(osOptions, OsLoadPathOffset);
} else {
//
// It's not an NT entry. Check to see if it represents a removable
// media device. We want to know this so that we don't put our
// boot entry ahead of the floppy or the CD, if they're already
// at the front of the list. A boot entry represents a
}
//
// Move to the next entry in the enumeration list, if any.
//
if (bootEntryList->NextEntryOffset == 0) {
break;
}
bootEntryList = ADD_OFFSET(bootEntryList, NextEntryOffset);
}
return STATUS_SUCCESS;
} // ConvertBootEntries
BOOL
CreateBootEntry(
PWSTR BootFileDevice,
PWSTR BootFilePath,
PWSTR OsLoadDevice,
PWSTR OsLoadPath,
PWSTR OsLoadOptions,
PWSTR FriendlyName
)
/*++
Routine Description:
Create an internal-format boot entry.
Arguments:
BootFileDevice - The NT name of the device on which the OS loader resides.
BootFilePath - The volume-relative path to the OS loader. Must start with
a backslash.
OsLoadDevice - The NT name ofthe device on which the OS resides.
OsLoadPath - The volume-relative path to the OS root directory (\WINDOWS).
Must start with a backslash.
OsLoadOptions - Boot options for the OS. Can be an empty string.
FriendlyName - The user-visible name for the boot entry. (This is ARC's
LOADIDENTIFIER.)
Return Value:
BOOLEAN - FALSE if an unexpected error occurred.
--*/
{
NTSTATUS status;
ULONG requiredLength;
ULONG osOptionsOffset;
ULONG osLoadOptionsLength;
ULONG osLoadPathOffset;
ULONG osLoadPathLength;
ULONG osOptionsLength;
ULONG friendlyNameOffset;
ULONG friendlyNameLength;
ULONG bootPathOffset;
ULONG bootPathLength;
PMY_BOOT_ENTRY myBootEntry;
PMY_BOOT_ENTRY previousBootEntry;
PMY_BOOT_ENTRY nextBootEntry;
PBOOT_ENTRY ntBootEntry;
PWINDOWS_OS_OPTIONS osOptions;
PFILE_PATH osLoadPath;
PWSTR friendlyName;
PFILE_PATH bootPath;
PWSTR p;
PULONG order;
ULONG count;
ULONG savedAttributes;
//
// Calculate how long the internal boot entry needs to be. This includes
// our internal structure, plus the BOOT_ENTRY structure that the NT APIs
// use.
//
// Our structure:
//
requiredLength = FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry);
//
// Base part of NT structure:
//
requiredLength += FIELD_OFFSET(BOOT_ENTRY, OsOptions);
//
// Save offset to BOOT_ENTRY.OsOptions. Add in base part of
// WINDOWS_OS_OPTIONS. Calculate length in bytes of OsLoadOptions
// and add that in.
//
osOptionsOffset = requiredLength;
requiredLength += FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions);
osLoadOptionsLength = (wcslen(OsLoadOptions) + 1) * sizeof(WCHAR);
requiredLength += osLoadOptionsLength;
//
// Round up to a ULONG boundary for the OS FILE_PATH in the
// WINDOWS_OS_OPTIONS. Save offset to OS FILE_PATH. Add in base part
// of FILE_PATH. Add in length in bytes of OS device NT name and OS
// directory. Calculate total length of OS FILE_PATH and of
// WINDOWS_OS_OPTIONS.
//
requiredLength = ALIGN_UP(requiredLength, ULONG);
osLoadPathOffset = requiredLength;
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
requiredLength += (wcslen(OsLoadDevice) + 1 + wcslen(OsLoadPath) + 1) * sizeof(WCHAR);
osLoadPathLength = requiredLength - osLoadPathOffset;
osOptionsLength = requiredLength - osOptionsOffset;
//
// Round up to a ULONG boundary for the friendly name in the BOOT_ENTRY.
// Save offset to friendly name. Calculate length in bytes of friendly name
// and add that in.
//
requiredLength = ALIGN_UP(requiredLength, ULONG);
friendlyNameOffset = requiredLength;
friendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR);
requiredLength += friendlyNameLength;
//
// Round up to a ULONG boundary for the boot FILE_PATH in the BOOT_ENTRY.
// Save offset to boot FILE_PATH. Add in base part of FILE_PATH. Add in
// length in bytes of boot device NT name and boot file. Calculate total
// length of boot FILE_PATH.
//
requiredLength = ALIGN_UP(requiredLength, ULONG);
bootPathOffset = requiredLength;
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
requiredLength += (wcslen(BootFileDevice) + 1 + wcslen(BootFilePath) + 1) * sizeof(WCHAR);
bootPathLength = requiredLength - bootPathOffset;
//
// Allocate memory for the boot entry.
//
myBootEntry = MALLOC(requiredLength);
if (myBootEntry == NULL) {
return FALSE;
}
RtlZeroMemory(myBootEntry, requiredLength);
//
// Calculate addresses of various substructures using the saved offsets.
//
ntBootEntry = &myBootEntry->NtBootEntry;
osOptions = (PWINDOWS_OS_OPTIONS)ntBootEntry->OsOptions;
osLoadPath = (PFILE_PATH)((PUCHAR)myBootEntry + osLoadPathOffset);
friendlyName = (PWSTR)((PUCHAR)myBootEntry + friendlyNameOffset);
bootPath = (PFILE_PATH)((PUCHAR)myBootEntry + bootPathOffset);
//
// Fill in the internal-format structure.
//
myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + requiredLength;
myBootEntry->Status = MBE_STATUS_NEW | MBE_STATUS_ORDERED;
myBootEntry->FriendlyName = friendlyName;
myBootEntry->FriendlyNameLength = friendlyNameLength;
myBootEntry->OsLoadOptions = osOptions->OsLoadOptions;
myBootEntry->OsLoadOptionsLength = osLoadOptionsLength;
myBootEntry->BootFilePath = bootPath;
myBootEntry->OsFilePath = osLoadPath;
//
// Fill in the base part of the NT boot entry.
//
ntBootEntry->Version = BOOT_ENTRY_VERSION;
ntBootEntry->Length = requiredLength - FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry);
ntBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE | BOOT_ENTRY_ATTRIBUTE_WINDOWS;
ntBootEntry->FriendlyNameOffset = (ULONG)((PUCHAR)friendlyName - (PUCHAR)ntBootEntry);
ntBootEntry->BootFilePathOffset = (ULONG)((PUCHAR)bootPath - (PUCHAR)ntBootEntry);
ntBootEntry->OsOptionsLength = osOptionsLength;
//
// Fill in the base part of the WINDOWS_OS_OPTIONS, including the
// OsLoadOptions.
//
strcpy(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE);
osOptions->Version = WINDOWS_OS_OPTIONS_VERSION;
osOptions->Length = osOptionsLength;
osOptions->OsLoadPathOffset = (ULONG)((PUCHAR)osLoadPath - (PUCHAR)osOptions);
wcscpy(osOptions->OsLoadOptions, OsLoadOptions);
//
// Fill in the OS FILE_PATH.
//
osLoadPath->Version = FILE_PATH_VERSION;
osLoadPath->Length = osLoadPathLength;
osLoadPath->Type = FILE_PATH_TYPE_NT;
p = (PWSTR)osLoadPath->FilePath;
wcscpy(p, OsLoadDevice);
p += wcslen(p) + 1;
wcscpy(p, OsLoadPath);
//
// Copy the friendly name.
//
wcscpy(friendlyName, FriendlyName);
//
// Fill in the boot FILE_PATH.
//
bootPath->Version = FILE_PATH_VERSION;
bootPath->Length = bootPathLength;
bootPath->Type = FILE_PATH_TYPE_NT;
p = (PWSTR)bootPath->FilePath;
wcscpy(p, BootFileDevice);
p += wcslen(p) + 1;
wcscpy(p, BootFilePath);
//
// Add the new boot entry.
//
// NB: The original implementation of NtAddBootEntry didn't like it
// when attribute bits other than _ACTIVE and _DEFAULT were set, so
// we need to mask the other bits off here.
//
savedAttributes = ntBootEntry->Attributes;
ntBootEntry->Attributes &= (BOOT_ENTRY_ATTRIBUTE_DEFAULT | BOOT_ENTRY_ATTRIBUTE_ACTIVE);
status = AddBootEntry(ntBootEntry, &ntBootEntry->Id);
ntBootEntry->Attributes = savedAttributes;
if (!NT_SUCCESS(status)) {
FREE(myBootEntry);
return FALSE;
}
myBootEntry->Status |= MBE_STATUS_COMMITTED;
//
// Remember the ID of the new boot entry as the entry to be booted
// immediately on the next boot.
//
BootOptions->NextBootEntryId = ntBootEntry->Id;
//
// Link the new boot entry into the list, after any removable media
// entries that are at the front of the list.
//
previousBootEntry = NULL;
nextBootEntry = MyBootEntries;
while ((nextBootEntry != NULL) &&
IS_BOOT_ENTRY_REMOVABLE_MEDIA(nextBootEntry)) {
previousBootEntry = nextBootEntry;
nextBootEntry = nextBootEntry->Next;
}
myBootEntry->Next = nextBootEntry;
if (previousBootEntry == NULL) {
MyBootEntries = myBootEntry;
} else {
previousBootEntry->Next = myBootEntry;
}
//
// Build the new boot order list. Insert all boot entries with
// MBE_STATUS_ORDERED into the list. (Don't insert deleted entries.)
//
count = 0;
nextBootEntry = MyBootEntries;
while (nextBootEntry != NULL) {
if (IS_BOOT_ENTRY_ORDERED(nextBootEntry) && !IS_BOOT_ENTRY_DELETED(nextBootEntry)) {
count++;
}
nextBootEntry = nextBootEntry->Next;
}
order = MALLOC(count * sizeof(ULONG));
if (order == NULL) {
return FALSE;
}
count = 0;
nextBootEntry = MyBootEntries;
while (nextBootEntry != NULL) {
if (IS_BOOT_ENTRY_ORDERED(nextBootEntry) && !IS_BOOT_ENTRY_DELETED(nextBootEntry)) {
order[count++] = nextBootEntry->NtBootEntry.Id;
}
nextBootEntry = nextBootEntry->Next;
}
//
// Write the new boot entry order list.
//
status = SetBootEntryOrder(order, count);
FREE(order);
if (!NT_SUCCESS(status)) {
return FALSE;
}
return TRUE;
} // CreateBootEntry
#endif // defined(EFI_NVRAM_ENABLED)
#endif // UNICODE