windows-nt/Source/XPSP1/NT/base/ntsetup/opktools/efinvram/efinvram.cpp
2020-09-26 16:20:57 +08:00

928 lines
27 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
efinvram.cpp
Abstract:
Tool that allows you to edit/view EFI
nvram entries.
Author:
Vijay Jayaseelan (vijayj) 02-Feb-2001
Revision History:
None
--*/
extern "C" {
#include <efisbent.h>
#include <setupapi.h>
}
#include <iostream>
#include <string>
#include <exception>
#include <windows.h>
#include <tchar.h>
#include <locale>
#include "msg.h"
#include <libmsg.h>
#define MSFT_PREFIX L"Microsoft "
#define DEFAULT_NAME L"Windows"
#define DEFAULT_TIMEOUT 30
//
// Global variables used to get formatted message for this program.
//
HMODULE ThisModule = NULL;
WCHAR Message[4096];
//
// function prototypes
//
NTSTATUS
QueryCanonicalName(
IN PCWSTR Name,
IN ULONG MaxDepth,
OUT PWSTR CanonicalName,
IN ULONG SizeOfBufferInBytes
);
VOID
GetFriendlyName(
IN const std::wstring &InfFileName,
OUT std::wstring &Buffer
);
//
// Helper dump operators
//
std::ostream& operator<<(std::ostream &os, const std::wstring &str) {
FILE *OutStream = (&os == &std::cerr) ? stderr : stdout;
fwprintf(OutStream, (PWSTR)str.c_str());
return os;
}
//
// Helper dump operators
//
std::ostream& operator<<(std::ostream &os, WCHAR *Str) {
std::wstring WStr = Str;
os << WStr;
return os;
}
PWSTR GetOptionKey(ULONG MsgId) {
Message[0] = TEXT('\0');
GetFormattedMessage(ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MsgId);
return Message;
}
//
// Exceptions
//
struct ProgramException : public std::exception {
virtual void Dump(std::ostream &os) = 0;
};
//
// Abstracts a Win32 error
//
struct W32Error : public ProgramException {
DWORD ErrorCode;
W32Error(DWORD ErrCode = GetLastError()) : ErrorCode(ErrCode){}
void Dump(std::ostream &os) {
WCHAR MsgBuffer[4096];
MsgBuffer[0] = UNICODE_NULL;
if (GetFormattedMessage(ThisModule,
TRUE,
MsgBuffer,
sizeof(MsgBuffer)/sizeof(MsgBuffer[0]),
ErrorCode)){
std::wstring Msg(MsgBuffer);
os << Msg;
} else {
os << std::hex << ErrorCode;
}
}
};
//
// Invalid arguments
//
struct InvalidArguments : public ProgramException {
const char *what() const throw() {
return "Invalid Arguments";
}
void Dump(std::ostream &os) {
os << what() << std::endl;
}
};
//
// Invalid arguments
//
struct ProgramUsage : public ProgramException {
std::wstring PrgUsage;
ProgramUsage(const std::wstring &Usg) : PrgUsage(Usg) {}
const char *what() const throw() {
return "Program Usage exception";
}
void Dump(std::ostream &os) {
os << PrgUsage << std::endl;
}
};
//
// Program Arguments abstraction
//
struct ProgramArguments {
bool ShowUsage;
bool ListEntries;
bool AddNewEntry;
bool DeleteBootEntry;
bool QuiteMode;
bool SetActive;
bool SetOsLoadOptions;
bool SetTimeout;
std::wstring LoaderVolumeName;
std::wstring LoaderPath;
std::wstring BootVolumeName;
std::wstring BootPath;
std::wstring LoadOptions;
std::wstring FriendlyName;
std::wstring OsLoadOptions;
std::wstring AddOptionKey;
std::wstring DeleteOptionKey;
std::wstring ListOptionKey;
std::wstring OptionsOptionKey;
std::wstring SetActiveOptionKey;
std::wstring TimeoutOptionKey;
ULONG Timeout;
ULONG EntryId;
ProgramArguments(INT Argc, WCHAR *Argv[]) {
ShowUsage = false;
QuiteMode = false;
ListEntries = AddNewEntry = DeleteBootEntry = false;
SetActive = false;
EntryId = -1;
Timeout = DEFAULT_TIMEOUT;
SetTimeout = false;
//
// get all the options
//
ListOptionKey = GetOptionKey(MSG_LIST_OPTION);
AddOptionKey = GetOptionKey(MSG_ADD_OPTION);
DeleteOptionKey = GetOptionKey(MSG_DELETE_OPTION);
OptionsOptionKey = GetOptionKey(MSG_OPTIONS_OPTION);
SetActiveOptionKey = GetOptionKey(MSG_SETACTIVE_OPTION);
TimeoutOptionKey = GetOptionKey(MSG_TIMEOUT_OPTION);
//
// parse the arguments
//
for (ULONG Index=1; !ShowUsage && (Index < Argc); Index++) {
if (!_wcsicmp(Argv[Index], L"/q")) {
QuiteMode = true;
} else if (!_wcsicmp(Argv[Index], AddOptionKey.c_str())) {
std::wstring LoaderName;
std::wstring BootVolName;
AddNewEntry = true;
ShowUsage = true;
if (Argc > 3) {
Index++;
LoaderName = Argv[Index++];
BootVolName = Argv[Index++];
ShowUsage = false;
for ( ; (Index < Argc) && (false == ShowUsage); Index++) {
if (!_wcsicmp(Argv[Index], SetActiveOptionKey.c_str())) {
SetActive = true;
} else if (!_wcsicmp(Argv[Index], OptionsOptionKey.c_str())) {
SetOsLoadOptions = true;
Index++;
if (Index < Argc) {
OsLoadOptions = Argv[Index];
} else {
ShowUsage = true;
}
} else if (!_wcsicmp(Argv[Index], TimeoutOptionKey.c_str())) {
SetTimeout = true;
Index++;
if (Index < Argc) {
PWSTR EndChar = NULL;
Timeout = wcstoul(Argv[Index], &EndChar, 10);
if (errno) {
ShowUsage = true;
}
}
} else {
ShowUsage = true;
}
}
//
// Verify the arguments
//
if (!ShowUsage) {
if (_waccess(LoaderName.c_str(), 0) ||
_waccess(BootVolName.c_str(), 0)) {
throw new W32Error(::GetLastError());
}
//
// Get the fully qualified NT name for
// the the loader volume and boot volume
// name
//
WCHAR CanonicalName[MAX_PATH];
NTSTATUS Status;
std::wstring NtName;
std::wstring DosDevices = L"\\DosDevices\\";
std::wstring::size_type LoaderColonPos = LoaderName.find(L':');
std::wstring::size_type BootColonPos = BootVolName.find(L':');
if (LoaderColonPos != LoaderName.npos) {
NtName = DosDevices + LoaderName.substr(0, LoaderColonPos + 1);
Status = QueryCanonicalName(NtName.c_str(),
-1,
CanonicalName,
sizeof(CanonicalName));
if (NT_SUCCESS(Status)) {
LoaderVolumeName = CanonicalName;
LoaderPath = LoaderName.substr(LoaderColonPos + 1);
} else {
throw new W32Error(RtlNtStatusToDosError(Status));
}
} else {
throw new W32Error(ERROR_PATH_NOT_FOUND);
}
if (BootColonPos != BootVolName.npos) {
NtName = DosDevices + BootVolName.substr(0, BootColonPos + 1);
Status = QueryCanonicalName(NtName.c_str(),
-1,
CanonicalName,
sizeof(CanonicalName));
if (NT_SUCCESS(Status)) {
BootVolumeName = CanonicalName;
BootPath = BootVolName.substr(BootColonPos + 1);
} else {
throw new W32Error(RtlNtStatusToDosError(Status));
}
} else {
throw new W32Error(ERROR_PATH_NOT_FOUND);
}
if (BootVolName[BootVolName.length() - 1] != L'\\') {
BootVolName += L"\\";
}
std::wstring LayoutInf = BootVolName + L"inf\\layout.inf";
//
// Verify the inf file path
//
if (_waccess(LayoutInf.c_str(), 0)) {
throw new W32Error(::GetLastError());
}
//
// Extract the product friendly name for the inf file
//
GetFriendlyName(LayoutInf, FriendlyName);
}
}
break;
} else if (!_wcsicmp(Argv[Index], ListOptionKey.c_str())) {
ListEntries = true;
break;
} else if (!_wcsicmp(Argv[Index], DeleteOptionKey.c_str())) {
DeleteBootEntry = true;
Index++;
if (Index < Argc) {
PWSTR EndChar = NULL;
EntryId = wcstoul(Argv[Index], &EndChar, 10);
}
break;
} else if (!_wcsicmp(Argv[Index], L"/?") ||
!_wcsicmp(Argv[Index], L"-?") ||
!_wcsicmp(Argv[Index], L"?") ||
!_wcsicmp(Argv[Index], L"/h") ||
!_wcsicmp(Argv[Index], L"-h")) {
ShowUsage = true;
} else if (!_wcsicmp(Argv[Index], OptionsOptionKey.c_str())) {
Index++;
SetOsLoadOptions = true;
if (Index < Argc) {
OsLoadOptions = Argv[Index];
Index++;
if (Index < Argc) {
PWSTR EndChar = NULL;
EntryId = wcstoul(Argv[Index], &EndChar, 10);
}
}
break;
} else if (!_wcsicmp(Argv[Index], SetActiveOptionKey.c_str())) {
Index++;
SetActive = true;
if (Index < Argc) {
PWSTR EndChar = NULL;
EntryId = wcstoul(Argv[Index], &EndChar, 10);
if (errno) {
ShowUsage = true;
}
} else {
ShowUsage = true;
}
break;
} else if (!_wcsicmp(Argv[Index], TimeoutOptionKey.c_str())) {
Index++;
SetTimeout = true;
if (Index < Argc) {
PWSTR EndChar = NULL;
Timeout = wcstoul(Argv[Index], &EndChar, 10);
if (errno) {
ShowUsage = true;
}
} else {
ShowUsage = true;
}
} else {
ShowUsage = true;
}
}
if (!ShowUsage) {
ShowUsage = (!ListEntries && !AddNewEntry && !SetActive &&
!DeleteBootEntry && !SetOsLoadOptions && !SetTimeout);
}
if (ShowUsage) {
throw new ProgramUsage(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_PGM_USAGE));
}
}
friend std::ostream& operator<<(std::ostream &os, ProgramArguments &Args) {
os << "List Entries : " << Args.ListEntries << std::endl;
os << "Add Entry : " << Args.AddNewEntry << std::endl;
os << "Delete Entry : " << Args.DeleteBootEntry << std::endl;
os << "QuiteMode : " << Args.QuiteMode << std::endl;
os << "Loader Vol : " << Args.LoaderVolumeName << std::endl;
os << "Loader Path : " << Args.LoaderPath << std::endl;
os << "Boot Vol : " << Args.BootVolumeName << std::endl;
os << "Boot Path : " << Args.BootPath << std::endl;
os << "Friendly Name: " << Args.FriendlyName << std::endl;
os << "Load Options : " << Args.OsLoadOptions << std::endl;
os << "Timeout : " << std::dec << Args.Timeout << " Secs" << std::endl;
return os;
}
};
VOID
DumpOsBootEntry(
IN POS_BOOT_ENTRY Entry
)
{
if (Entry) {
wprintf(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_BOOT_ENTRY,
OSBEGetId(Entry),
OSBEGetFriendlyName(Entry),
OSBEGetOsLoaderVolumeName(Entry),
OSBEGetOsLoaderPath(Entry),
OSBEGetBootVolumeName(Entry),
OSBEGetBootPath(Entry),
OSBEGetOsLoadOptions(Entry)));
}
}
VOID
DumpOsBootOptions(
IN POS_BOOT_OPTIONS Options
)
{
if (Options) {
ULONG Index;
wprintf(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_TIMEOUT_AND_BOOT_ORDER,
OSBOGetTimeOut(Options)));
for (Index=0;
Index < OSBOGetOrderedBootEntryCount(Options);
Index++) {
wprintf(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_ORDERED_BOOT_ENTRIES,
OSBOGetBootEntryIdByOrder(Options, Index)));
}
wprintf(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_BOOT_ENTRIES));
POS_BOOT_ENTRY Entry = OSBOGetFirstBootEntry(Options, &Index);
while (Entry) {
DumpOsBootEntry(Entry);
Entry = OSBOGetNextBootEntry(Options, &Index);
}
wprintf(GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_ACTIVE_ENTRY));
DumpOsBootEntry(OSBOGetActiveBootEntry(Options));
}
}
DWORD
ListEntries(
IN POS_BOOT_OPTIONS OsOptions
)
{
DWORD Result = ERROR_INVALID_PARAMETER;
if (OsOptions) {
DumpOsBootOptions(OsOptions);
Result = ERROR_SUCCESS;
}
return Result;
}
DWORD
AddNewEntry(
IN POS_BOOT_OPTIONS OsOptions,
IN ProgramArguments &Args
)
{
DWORD Result = ERROR_INVALID_PARAMETER;
if (OsOptions) {
POS_BOOT_ENTRY NewEntry;
BOOLEAN Status = TRUE;
NewEntry = OSBOAddNewBootEntry(OsOptions,
Args.FriendlyName.c_str(),
Args.LoaderVolumeName.c_str(),
Args.LoaderPath.c_str(),
Args.BootVolumeName.c_str(),
Args.BootPath.c_str(),
Args.OsLoadOptions.c_str());
if (NewEntry) {
if (Args.SetActive) {
Status = (OSBOSetActiveBootEntry(OsOptions,
NewEntry) != NULL);
}
if (Status && Args.SetTimeout) {
OSBOSetTimeOut(OsOptions, Args.Timeout);
}
if (Status) {
Status = OSBOFlush(OsOptions);
}
} else {
Status = FALSE;
}
if (Status) {
Result = ERROR_SUCCESS;
} else {
Result = ERROR_CAN_NOT_COMPLETE;
}
}
return Result;
}
DWORD
SetBootEntryOptions(
IN POS_BOOT_OPTIONS OsOptions,
IN const ProgramArguments &Args
)
{
DWORD ErrorCode = ERROR_INVALID_PARAMETER;
if (OsOptions) {
POS_BOOT_ENTRY BootEntry;
BOOLEAN Status = FALSE;
if (Args.EntryId != -1) {
BootEntry = OSBOFindBootEntry(OsOptions,
Args.EntryId);
} else {
BootEntry = OSBOGetActiveBootEntry(OsOptions);
}
if (BootEntry) {
Status = (OSBESetOsLoadOptions(BootEntry,
Args.OsLoadOptions.c_str()) != NULL);
if (Status) {
Status = OSBOFlush(OsOptions);
}
}
if (Status) {
ErrorCode = ERROR_SUCCESS;
} else {
ErrorCode = ERROR_CAN_NOT_COMPLETE;
}
}
return ErrorCode;
}
DWORD
SetBootEntryActive(
IN POS_BOOT_OPTIONS OsOptions,
IN ProgramArguments &Args
)
{
DWORD ErrorCode = ERROR_INVALID_PARAMETER;
if (OsOptions && (Args.EntryId != -1)) {
POS_BOOT_ENTRY BootEntry;
BOOLEAN Status = FALSE;
BootEntry = OSBOFindBootEntry(OsOptions,
Args.EntryId);
if (BootEntry) {
Status = (OSBOSetActiveBootEntry(OsOptions,
BootEntry) != NULL);
if (Status) {
Status = OSBOFlush(OsOptions);
}
}
if (Status) {
ErrorCode = ERROR_SUCCESS;
} else {
ErrorCode = ERROR_CAN_NOT_COMPLETE;
}
}
return ErrorCode;
}
DWORD
DeleteBootEntry(
IN POS_BOOT_OPTIONS OsOptions,
IN ProgramArguments &Args
)
{
DWORD ErrorCode = ERROR_INVALID_PARAMETER;
if (OsOptions) {
POS_BOOT_ENTRY BootEntry;
BOOLEAN Status = FALSE;
if (Args.EntryId != -1) {
BootEntry = OSBOFindBootEntry(OsOptions,
Args.EntryId);
} else {
BootEntry = OSBOGetActiveBootEntry(OsOptions);
}
if (BootEntry) {
Status = OSBODeleteBootEntry(OsOptions, BootEntry);
if (Status) {
Status = OSBOFlush(OsOptions);
}
}
if (Status) {
ErrorCode = ERROR_SUCCESS;
} else {
ErrorCode = ERROR_CAN_NOT_COMPLETE;
}
}
return ErrorCode;
}
DWORD
SetTimeout(
IN POS_BOOT_OPTIONS OsOptions,
IN const ProgramArguments &Args
)
{
DWORD ErrorCode = ERROR_INVALID_PARAMETER;
if (OsOptions && Args.SetTimeout) {
OSBOSetTimeOut(OsOptions, Args.Timeout);
ErrorCode = OSBOFlush(OsOptions) ? ERROR_SUCCESS : ERROR_CAN_NOT_COMPLETE;
}
return ErrorCode;
}
//
// main() entry point
//
int
__cdecl
wmain(
int Argc,
wchar_t *Argv[]
)
{
int Result = 0;
ThisModule = GetModuleHandle(NULL);
try {
DWORD ErrorCode = ERROR_INVALID_PARAMETER;
ProgramArguments Args(Argc, Argv);
POS_BOOT_OPTIONS BootOptions = NULL;
//
// Initialize the library
//
if (OSBOLibraryInit((SBEMemAllocateRoutine)malloc, (SBEMemFreeRoutine)free)) {
BootOptions = EFIOSBOCreate();
}
if (!BootOptions) {
std::cout << GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_ERROR_READING_BOOT_ENTRIES) << std::endl;
Result = 1;
} else {
if (Args.ListEntries) {
ErrorCode = ListEntries(BootOptions);
} else if (Args.AddNewEntry) {
ErrorCode = AddNewEntry(BootOptions, Args);
} else if (Args.DeleteBootEntry) {
ErrorCode = DeleteBootEntry(BootOptions, Args);
} else if (Args.SetOsLoadOptions) {
ErrorCode = SetBootEntryOptions(BootOptions, Args);
} else if (Args.SetActive) {
ErrorCode = SetBootEntryActive(BootOptions, Args);
} else if (Args.SetTimeout) {
ErrorCode = SetTimeout(BootOptions, Args);
}
OSBODelete(BootOptions);
}
if (ErrorCode != ERROR_SUCCESS) {
throw new W32Error(ErrorCode);
}
}
catch(ProgramArguments *pArgs) {
Result = 1;
std::cout << GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_PGM_USAGE) << std::endl;
if (pArgs) {
delete pArgs;
}
}
catch(W32Error *W32Err) {
if (W32Err) { // to make prefix happy :(
W32Err->Dump(std::cout);
std::cout << GetFormattedMessage( ThisModule,
FALSE,
Message,
sizeof(Message)/sizeof(Message[0]),
MSG_PGM_USAGE) << std::endl;
delete W32Err;
}
Result = 1;
}
catch(ProgramException *PrgExp) {
Result = 1;
PrgExp->Dump(std::cout);
delete PrgExp;
} catch (exception *Exp) {
Result = 1;
std::cout << Exp->what() << std::endl;
}
return Result;
}
NTSTATUS
QueryCanonicalName(
IN PCWSTR 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;
}
#define PRODUCT_NAME_KEY TEXT("productname")
VOID
GetFriendlyName(
IN const std::wstring &InfFileName,
OUT std::wstring &FriendlyName
)
{
UINT ErrorLine = 0;
BOOL Status = FALSE;
HINF InfHandle = ::SetupOpenInfFile(InfFileName.c_str(),
NULL,
INF_STYLE_WIN4,
&ErrorLine);
if (InfHandle != INVALID_HANDLE_VALUE) {
INFCONTEXT InfContext = {0};
WCHAR Buffer[MAX_PATH] = {0};
//
// get the key
//
Status = SetupFindFirstLine(InfHandle,
TEXT("Strings"),
PRODUCT_NAME_KEY,
&InfContext);
if (Status) {
//
// If we found the key extract the description
//
Status = SetupGetStringField(&InfContext,
1,
Buffer,
ARRAY_SIZE(Buffer),
NULL);
if (Status) {
FriendlyName = Buffer;
}
}
SetupCloseInfFile(InfHandle);
}
//
// If we didn't find the description use default description
//
if (!Status) {
FriendlyName = DEFAULT_NAME;
}
FriendlyName = MSFT_PREFIX + FriendlyName;
}