2951 lines
78 KiB
C
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
|