6509 lines
184 KiB
C
6509 lines
184 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-2001 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
sysenv.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the NT query and set system environment
|
||
variable services.
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 10-Nov-1991
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "exp.h"
|
||
#pragma hdrstop
|
||
|
||
#include <arccodes.h>
|
||
|
||
#include <ntdddisk.h>
|
||
|
||
|
||
#if defined(EFI_NVRAM_ENABLED)
|
||
#include <efi.h>
|
||
#include <efiboot.h>
|
||
|
||
GUID ExpUnknownDeviceGuid = UNKNOWN_DEVICE_GUID;
|
||
|
||
#endif
|
||
|
||
#define ADD_OFFSET(_p,_o) (PVOID)((PUCHAR)(_p) + (_p)->_o)
|
||
|
||
//
|
||
// Signature type
|
||
//
|
||
typedef union _DISK_SIGNATURE_NEW {
|
||
GUID Guid; // GPT disk signature
|
||
ULONG Signature; // MBR disk signature
|
||
} DISK_SIGNATURE_NEW, *PDISK_SIGNATURE_NEW;
|
||
|
||
|
||
//
|
||
// Define local subroutines.
|
||
//
|
||
|
||
NTSTATUS
|
||
ExpSetBootEntry (
|
||
IN LOGICAL CreateNewEntry,
|
||
IN PBOOT_ENTRY BootEntry,
|
||
OUT PULONG Id OPTIONAL
|
||
);
|
||
|
||
#if defined(EFI_NVRAM_ENABLED)
|
||
|
||
ULONG
|
||
ExpSafeWcslen (
|
||
IN PWSTR String,
|
||
IN PWSTR Max
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateArcPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateEfiPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateNtPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
);
|
||
|
||
LOGICAL
|
||
ExpTranslateBootEntryNameToId (
|
||
IN PWSTR Name,
|
||
OUT PULONG Id
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateSymbolicLink (
|
||
IN PWSTR LinkName,
|
||
OUT PUNICODE_STRING ResultName
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpVerifyFilePath (
|
||
PFILE_PATH FilePath,
|
||
PUCHAR Max
|
||
);
|
||
|
||
LOGICAL
|
||
ExpIsDevicePathForRemovableMedia (
|
||
EFI_DEVICE_PATH *DevicePath
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpVerifyWindowsOsOptions (
|
||
PWINDOWS_OS_OPTIONS WindowsOsOptions,
|
||
ULONG Length
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpParseArcPathName (
|
||
IN PWSTR ArcName,
|
||
OUT PWSTR *ppDeviceName,
|
||
OUT PWSTR *ppPathName,
|
||
OUT PULONG pDeviceNameCount,
|
||
OUT PBOOLEAN pSignatureFormat
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpParseSignatureName (
|
||
IN PWSTR deviceName,
|
||
IN ULONG deviceNameCount,
|
||
OUT PDISK_SIGNATURE_NEW diskSignature,
|
||
OUT PULONG partitionNumber,
|
||
OUT PULONGLONG partitionStart,
|
||
OUT PULONGLONG partitionSize,
|
||
OUT PBOOLEAN GPTpartition,
|
||
OUT PBOOLEAN longSignature
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpParseEfiPath (
|
||
IN EFI_DEVICE_PATH *pDevicePath,
|
||
OUT HARDDRIVE_DEVICE_PATH **ppHardDriveDP,
|
||
OUT PWSTR *ppPathName,
|
||
OUT PBOOLEAN GPTpartition
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpConvertArcName (
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PWSTR pDeviceName,
|
||
IN PWSTR pPathName,
|
||
IN ULONG DeviceNameCount
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpConvertSignatureName (
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PWSTR pDeviceName,
|
||
IN PWSTR pPathName,
|
||
IN ULONG DeviceNameCount
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToULONG (
|
||
IN PWSTR Name,
|
||
OUT PULONG Number
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToULONGLONG (
|
||
IN PWSTR Name,
|
||
OUT PULONGLONG Number
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToGUID (
|
||
IN PWSTR Name,
|
||
OUT GUID *pGuid
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputEFI (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PDISK_SIGNATURE_NEW pDiskSignature,
|
||
IN PULONG pPartitionNumber,
|
||
IN PULONGLONG pPartitionStart,
|
||
IN PULONGLONG pPartitionSize,
|
||
IN PWSTR pPathName,
|
||
IN BOOLEAN GPTpartition
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputNT (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
IN PWSTR pPathName
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputARC (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
IN PWSTR pPathName
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputSIGNATURE (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PDISK_SIGNATURE_NEW pDiskSignature,
|
||
IN PULONG pPartitionNumber,
|
||
IN PULONGLONG pPartitionStart,
|
||
IN PULONGLONG pPartitionSize,
|
||
IN PWSTR pPathName,
|
||
IN BOOLEAN GPTpartition
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpFindArcName (
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
OUT PWSTR *pArcName
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpFindDiskSignature (
|
||
IN PDISK_SIGNATURE_NEW pSignature,
|
||
IN OUT PULONG pPartitionNumber,
|
||
OUT PULONG pDiskNumber,
|
||
OUT PULONGLONG pPartitionStart,
|
||
OUT PULONGLONG pPartitionSize,
|
||
IN BOOLEAN GPTpartition
|
||
);
|
||
|
||
NTSTATUS
|
||
ExpGetPartitionTableInfo (
|
||
IN PWSTR pDeviceName,
|
||
OUT PDRIVE_LAYOUT_INFORMATION_EX *ppDriveLayout
|
||
);
|
||
|
||
#endif // defined(EFI_NVRAM_ENABLED)
|
||
|
||
#if defined(ALLOC_PRAGMA)
|
||
#pragma alloc_text(PAGE, NtQuerySystemEnvironmentValue)
|
||
#pragma alloc_text(PAGE, NtSetSystemEnvironmentValue)
|
||
#pragma alloc_text(PAGE, NtQuerySystemEnvironmentValueEx)
|
||
#pragma alloc_text(PAGE, NtSetSystemEnvironmentValueEx)
|
||
#pragma alloc_text(PAGE, NtEnumerateSystemEnvironmentValuesEx)
|
||
#pragma alloc_text(PAGE, NtAddBootEntry)
|
||
#pragma alloc_text(PAGE, NtDeleteBootEntry)
|
||
#pragma alloc_text(PAGE, NtModifyBootEntry)
|
||
#pragma alloc_text(PAGE, NtEnumerateBootEntries)
|
||
#pragma alloc_text(PAGE, NtQueryBootEntryOrder)
|
||
#pragma alloc_text(PAGE, NtSetBootEntryOrder)
|
||
#pragma alloc_text(PAGE, NtQueryBootOptions)
|
||
#pragma alloc_text(PAGE, NtSetBootOptions)
|
||
#pragma alloc_text(PAGE, NtTranslateFilePath)
|
||
#pragma alloc_text(PAGE, ExpSetBootEntry)
|
||
#if defined(EFI_NVRAM_ENABLED)
|
||
#pragma alloc_text(PAGE, ExpSafeWcslen)
|
||
#pragma alloc_text(PAGE, ExpTranslateArcPath)
|
||
#pragma alloc_text(PAGE, ExpTranslateEfiPath)
|
||
#pragma alloc_text(PAGE, ExpTranslateNtPath)
|
||
#pragma alloc_text(PAGE, ExpTranslateBootEntryNameToId)
|
||
#pragma alloc_text(PAGE, ExpTranslateSymbolicLink)
|
||
#pragma alloc_text(PAGE, ExpVerifyFilePath)
|
||
#pragma alloc_text(PAGE, ExpVerifyWindowsOsOptions)
|
||
#pragma alloc_text(PAGE, ExpParseArcPathName)
|
||
#pragma alloc_text(PAGE, ExpParseSignatureName)
|
||
#pragma alloc_text(PAGE, ExpParseEfiPath)
|
||
#pragma alloc_text(PAGE, ExpConvertArcName)
|
||
#pragma alloc_text(PAGE, ExpConvertSignatureName)
|
||
#pragma alloc_text(PAGE, ExpTranslateHexStringToULONG)
|
||
#pragma alloc_text(PAGE, ExpTranslateHexStringToULONGLONG)
|
||
#pragma alloc_text(PAGE, ExpTranslateHexStringToGUID)
|
||
#pragma alloc_text(PAGE, ExpCreateOutputEFI)
|
||
#pragma alloc_text(PAGE, ExpCreateOutputNT)
|
||
#pragma alloc_text(PAGE, ExpCreateOutputARC)
|
||
#pragma alloc_text(PAGE, ExpCreateOutputSIGNATURE)
|
||
#pragma alloc_text(PAGE, ExpFindArcName)
|
||
#pragma alloc_text(PAGE, ExpFindDiskSignature)
|
||
#pragma alloc_text(PAGE, ExpGetPartitionTableInfo)
|
||
#endif // defined(EFI_NVRAM_ENABLED)
|
||
#endif // defined(ALLOC_PRAGMA)
|
||
|
||
//
|
||
// Define maximum size of environment value.
|
||
//
|
||
|
||
#define MAXIMUM_ENVIRONMENT_VALUE 1024
|
||
|
||
//
|
||
// Define query/set environment variable synchronization fast mutex.
|
||
//
|
||
|
||
FAST_MUTEX ExpEnvironmentLock;
|
||
|
||
#if defined(EFI_NVRAM_ENABLED)
|
||
//
|
||
// Define vendor GUID for EFI boot variables.
|
||
//
|
||
|
||
GUID EfiBootVariablesGuid = EFI_GLOBAL_VARIABLE;
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
NtQuerySystemEnvironmentValue (
|
||
IN PUNICODE_STRING VariableName,
|
||
OUT PWSTR VariableValue,
|
||
IN USHORT ValueLength,
|
||
OUT PUSHORT ReturnLength OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function locates the specified system environment variable and
|
||
returns its value.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Variable - Supplies a pointer to a UNICODE descriptor for the specified
|
||
system environment variable.
|
||
|
||
Value - Supplies a pointer to a buffer that receives the value of the
|
||
specified system environment variable.
|
||
|
||
ValueLength - Supplies the length of the value buffer in bytes.
|
||
|
||
ReturnLength - Supplies an optional pointer to a variable that receives
|
||
the length of the system environment variable value.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the service is successfully executed.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD is returned if the caller does not have the
|
||
privilege to query a system environment variable.
|
||
|
||
STATUS_ACCESS_VIOLATION is returned if the output parameter for the
|
||
system environment value or the return length cannot be written,
|
||
or the descriptor or the name of the system environment variable
|
||
cannot be read.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
|
||
for this request to complete.
|
||
|
||
STATUS_UNSUCCESSFUL - The specified environment variable could not
|
||
be located.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG AnsiLength;
|
||
ANSI_STRING AnsiString;
|
||
ARC_STATUS ArcStatus;
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
UNICODE_STRING UnicodeString;
|
||
PCHAR ValueBuffer;
|
||
|
||
//
|
||
// Clear address of ANSI buffer.
|
||
//
|
||
|
||
AnsiString.Buffer = NULL;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and read the
|
||
// name of the specified system environment variable, and probe the
|
||
// variable value buffer and return length. If the probe or read
|
||
// attempt fails, then return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the string descriptor for the system
|
||
// environment variable name.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VariableName,
|
||
sizeof(UNICODE_STRING),
|
||
sizeof(ULONG));
|
||
|
||
UnicodeString = *VariableName;
|
||
|
||
//
|
||
// Probe the system environment variable name.
|
||
//
|
||
|
||
if (UnicodeString.Length == 0) {
|
||
return STATUS_ACCESS_VIOLATION;
|
||
}
|
||
|
||
ProbeForRead((PVOID)UnicodeString.Buffer,
|
||
UnicodeString.Length,
|
||
sizeof(WCHAR));
|
||
|
||
//
|
||
// Probe the system environment value buffer.
|
||
//
|
||
|
||
ProbeForWrite((PVOID)VariableValue, ValueLength, sizeof(WCHAR));
|
||
|
||
//
|
||
// If argument is present, probe the return length value.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
ProbeForWriteUshort(ReturnLength);
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query a system
|
||
// environment variable.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
} else {
|
||
UnicodeString = *VariableName;
|
||
}
|
||
|
||
|
||
//
|
||
// Compute the size of the ANSI variable name, allocate a nonpaged
|
||
// buffer, and convert the specified UNICODE variable name to ANSI.
|
||
//
|
||
|
||
AnsiLength = RtlUnicodeStringToAnsiSize(&UnicodeString);
|
||
AnsiString.Buffer = (PCHAR)ExAllocatePoolWithTag(NonPagedPool, AnsiLength, 'rvnE');
|
||
if (AnsiString.Buffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
AnsiString.MaximumLength = (USHORT)AnsiLength;
|
||
NtStatus = RtlUnicodeStringToAnsiString(&AnsiString,
|
||
&UnicodeString,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(NtStatus) == FALSE) {
|
||
ExFreePool((PVOID)AnsiString.Buffer);
|
||
return NtStatus;
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the read of the variable descriptor,
|
||
// the read of the variable name, the probe of the variable value, or
|
||
// the probe of the return length, then always handle the exception,
|
||
// free the ANSI string buffer if necessary, and return the exception
|
||
// code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (AnsiString.Buffer != NULL) {
|
||
ExFreePool((PVOID)AnsiString.Buffer);
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Allocate nonpaged pool to receive variable value.
|
||
//
|
||
|
||
ValueBuffer = (PCHAR)ExAllocatePoolWithTag(NonPagedPool, MAXIMUM_ENVIRONMENT_VALUE, 'rvnE');
|
||
if (ValueBuffer == NULL) {
|
||
ExFreePool((PVOID)AnsiString.Buffer);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Get the system environment variable value.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
ArcStatus = HalGetEnvironmentVariable(AnsiString.Buffer,
|
||
MAXIMUM_ENVIRONMENT_VALUE,
|
||
ValueBuffer);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Free the ANSI string buffer used to hold the variable name.
|
||
//
|
||
|
||
ExFreePool((PVOID)AnsiString.Buffer);
|
||
|
||
//
|
||
// If the specified environment variable was not found, then free
|
||
// the value buffer and return an unsuccessful status.
|
||
//
|
||
|
||
if (ArcStatus != ESUCCESS) {
|
||
ExFreePool((PVOID)ValueBuffer);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the value of the
|
||
// specified system environment variable. If the write attempt fails,
|
||
// then return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Initialize an ANSI string descriptor, set the maximum length and
|
||
// buffer address for a UNICODE string descriptor, and convert the
|
||
// ANSI variable value to UNICODE.
|
||
//
|
||
|
||
RtlInitString(&AnsiString, ValueBuffer);
|
||
UnicodeString.Buffer = (PWSTR)VariableValue;
|
||
UnicodeString.MaximumLength = ValueLength;
|
||
NtStatus = RtlAnsiStringToUnicodeString(&UnicodeString,
|
||
&AnsiString,
|
||
FALSE);
|
||
|
||
//
|
||
// If argument is present, then write the length of the UNICODE
|
||
// variable value.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(ReturnLength)) {
|
||
*ReturnLength = UnicodeString.Length;
|
||
}
|
||
|
||
//
|
||
// Free the value buffer used to hold the variable value.
|
||
//
|
||
|
||
ExFreePool((PVOID)ValueBuffer);
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the variable value, or
|
||
// the write of the return length, then always handle the exception
|
||
// and return the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool((PVOID)ValueBuffer);
|
||
return GetExceptionCode();
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
NtSetSystemEnvironmentValue (
|
||
IN PUNICODE_STRING VariableName,
|
||
IN PUNICODE_STRING VariableValue
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the specified system environment variable to the
|
||
specified value.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Variable - Supplies a pointer to a UNICODE descriptor for the specified
|
||
system environment variable name.
|
||
|
||
Value - Supplies a pointer to a UNICODE descriptor for the specified
|
||
system environment variable value.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the service is successfully executed.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD is returned if the caller does not have the
|
||
privilege to set a system environment variable.
|
||
|
||
STATUS_ACCESS_VIOLATION is returned if the input parameter for the
|
||
system environment variable or value cannot be read.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
|
||
for this request to complete.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG AnsiLength1;
|
||
ULONG AnsiLength2;
|
||
ANSI_STRING AnsiString1;
|
||
ANSI_STRING AnsiString2;
|
||
ARC_STATUS ArcStatus;
|
||
BOOLEAN HasPrivilege;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
NTSTATUS NtStatus;
|
||
UNICODE_STRING UnicodeString1;
|
||
UNICODE_STRING UnicodeString2;
|
||
|
||
//
|
||
// Clear address of ANSI buffers.
|
||
//
|
||
|
||
AnsiString1.Buffer = NULL;
|
||
AnsiString2.Buffer = NULL;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to set the value of the
|
||
// specified system environment variable. If the read attempt for the
|
||
// system environment variable or value fails, then return the exception
|
||
// code as the service status. Otherwise, return either success or access
|
||
// denied as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the string descriptor for the system
|
||
// environment variable name.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VariableName,
|
||
sizeof(UNICODE_STRING),
|
||
sizeof(ULONG));
|
||
|
||
UnicodeString1 = *VariableName;
|
||
|
||
//
|
||
// Handle a zero length string explicitly since probing does not,
|
||
// the error code is unusual, but it's what we would have done with
|
||
// the HAL return code too.
|
||
//
|
||
|
||
if (UnicodeString1.Length == 0) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Probe the system environment variable name.
|
||
//
|
||
|
||
ProbeForRead((PVOID)UnicodeString1.Buffer,
|
||
UnicodeString1.Length,
|
||
sizeof(WCHAR));
|
||
|
||
//
|
||
// Probe and capture the string descriptor for the system
|
||
// environment variable value.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VariableValue,
|
||
sizeof(UNICODE_STRING),
|
||
sizeof(ULONG));
|
||
|
||
UnicodeString2 = *VariableValue;
|
||
|
||
//
|
||
// Handle a zero length string explicitly since probing does not
|
||
// the error code is unusual, but it's what we would have done with
|
||
// the HAL return code too.
|
||
//
|
||
|
||
if (UnicodeString2.Length == 0) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Probe the system environment variable value.
|
||
//
|
||
|
||
ProbeForRead((PVOID)UnicodeString2.Buffer,
|
||
UnicodeString2.Length,
|
||
sizeof(WCHAR));
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query a system
|
||
// environment variable.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
} else {
|
||
UnicodeString1 = *VariableName;
|
||
UnicodeString2 = *VariableValue;
|
||
}
|
||
|
||
|
||
//
|
||
// Compute the size of the ANSI variable name, allocate a nonpaged
|
||
// buffer, and convert the specified UNICODE variable name to ANSI.
|
||
//
|
||
|
||
AnsiLength1 = RtlUnicodeStringToAnsiSize(&UnicodeString1);
|
||
AnsiString1.Buffer = (PCHAR)ExAllocatePoolWithTag(NonPagedPool, AnsiLength1, 'rvnE');
|
||
if (AnsiString1.Buffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
AnsiString1.MaximumLength = (USHORT)AnsiLength1;
|
||
NtStatus = RtlUnicodeStringToAnsiString(&AnsiString1,
|
||
&UnicodeString1,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(NtStatus) == FALSE) {
|
||
ExFreePool((PVOID)AnsiString1.Buffer);
|
||
return NtStatus;
|
||
}
|
||
|
||
//
|
||
// Compute the size of the ANSI variable value, allocate a nonpaged
|
||
// buffer, and convert the specified UNICODE variable value to ANSI.
|
||
//
|
||
|
||
AnsiLength2 = RtlUnicodeStringToAnsiSize(&UnicodeString2);
|
||
AnsiString2.Buffer = (PCHAR)ExAllocatePoolWithTag(NonPagedPool, AnsiLength2, 'rvnE');
|
||
if (AnsiString2.Buffer == NULL) {
|
||
ExFreePool((PVOID)AnsiString1.Buffer);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
AnsiString2.MaximumLength = (USHORT)AnsiLength2;
|
||
NtStatus = RtlUnicodeStringToAnsiString(&AnsiString2,
|
||
&UnicodeString2,
|
||
FALSE);
|
||
|
||
if (NT_SUCCESS(NtStatus) == FALSE) {
|
||
ExFreePool((PVOID)AnsiString1.Buffer);
|
||
ExFreePool((PVOID)AnsiString2.Buffer);
|
||
return NtStatus;
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the read of the variable descriptor,
|
||
// the read of the variable name, the read of the value descriptor, or
|
||
// the read of the value, then always handle the exception, free the
|
||
// ANSI string buffers if necessary, and return the exception code as
|
||
// the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (AnsiString1.Buffer != NULL) {
|
||
ExFreePool((PVOID)AnsiString1.Buffer);
|
||
}
|
||
|
||
if (AnsiString2.Buffer != NULL) {
|
||
ExFreePool((PVOID)AnsiString2.Buffer);
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Set the system environment variable value.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
ArcStatus = HalSetEnvironmentVariable(AnsiString1.Buffer,
|
||
AnsiString2.Buffer);
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Free the ANSI string buffers used to hold the variable name and value.
|
||
//
|
||
|
||
ExFreePool((PVOID)AnsiString1.Buffer);
|
||
ExFreePool((PVOID)AnsiString2.Buffer);
|
||
|
||
//
|
||
// If the specified value of the specified environment variable was
|
||
// successfully set, then return a success status. Otherwise, return
|
||
// insufficient resources.
|
||
//
|
||
|
||
if (ArcStatus == ESUCCESS) {
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
NTSTATUS
|
||
NtQuerySystemEnvironmentValueEx (
|
||
IN PUNICODE_STRING VariableName,
|
||
IN LPGUID VendorGuid,
|
||
OUT PVOID Value,
|
||
IN OUT PULONG ValueLength,
|
||
OUT PULONG Attributes OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function locates the specified system environment variable and
|
||
return its value.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
VariableName - Supplies a pointer to a UNICODE descriptor for the specified
|
||
system environment variable.
|
||
|
||
VendorGuid - Supplies the GUID for the vendor associated with the variable.
|
||
Variables are grouped into namespaces based on their vendor GUIDs. Some
|
||
platforms may not support vendor GUIDs. On these platforms, all
|
||
variables are in a single namespace, and this routine ignores VendorGuid.
|
||
|
||
Value - Supplies a pointer to a buffer that receives the value of the
|
||
specified system environment variable.
|
||
|
||
ValueLength - On input, supplies the length in bytes of the Value buffer.
|
||
On output, returns the length in bytes of the variable value. If the
|
||
input buffer is large enough, then ValueLength indicates the amount
|
||
of data copied into Value. If the input buffer is too small, then
|
||
nothing is copied into the buffer, and ValueLength indicates the
|
||
required buffer length.
|
||
|
||
Attributes - Supplies an optional pointer to a ULONG to receive the
|
||
attributes of the variable.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INSUFFICIENT_RESOURCES Insufficient system resources exist
|
||
for this request to complete.
|
||
STATUS_BUFFER_TOO_SMALL The input buffer was too small.
|
||
STATUS_VARIABLE_NOT_FOUND The requested variable does not exist.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(VariableName);
|
||
UNREFERENCED_PARAMETER(VendorGuid);
|
||
UNREFERENCED_PARAMETER(Value);
|
||
UNREFERENCED_PARAMETER(ValueLength);
|
||
UNREFERENCED_PARAMETER(Attributes);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
UNICODE_STRING UnicodeString;
|
||
PWSTR LocalUnicodeBuffer = NULL;
|
||
GUID LocalGuid;
|
||
PCHAR LockedValueBuffer;
|
||
ULONG LocalValueLength;
|
||
ULONG LocalAttributes;
|
||
PVOID LockVariable;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and read the name
|
||
// of the specified system environment variable, probe the variable value
|
||
// buffer, probe and read the length argument, and probe the attributes
|
||
// argument. If the probe attempt fails, then return the exception code
|
||
// as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the string descriptor for the system
|
||
// environment variable name.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VariableName,
|
||
sizeof(UNICODE_STRING),
|
||
sizeof(ULONG));
|
||
|
||
UnicodeString = *VariableName;
|
||
|
||
//
|
||
// Probe the system environment variable name.
|
||
//
|
||
|
||
if (UnicodeString.Length == 0) {
|
||
return STATUS_ACCESS_VIOLATION;
|
||
}
|
||
|
||
ProbeForRead((PVOID)UnicodeString.Buffer,
|
||
UnicodeString.Length,
|
||
sizeof(WCHAR));
|
||
|
||
//
|
||
// Probe the vendor GUID.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VendorGuid, sizeof(GUID), sizeof(ULONG));
|
||
|
||
//
|
||
// Probe and capture the length value.
|
||
//
|
||
|
||
ProbeForWriteUlong(ValueLength);
|
||
|
||
LocalValueLength = *ValueLength;
|
||
|
||
//
|
||
// Probe the system environment value buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Value)) {
|
||
LocalValueLength = 0;
|
||
}
|
||
|
||
if (LocalValueLength != 0) {
|
||
ProbeForWrite((PVOID)Value, LocalValueLength, sizeof(UCHAR));
|
||
}
|
||
|
||
//
|
||
// If argument is present, probe the attributes parameter.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Attributes)) {
|
||
ProbeForWriteUlong(Attributes);
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query a system
|
||
// environment variable.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
UnicodeString = *VariableName;
|
||
LocalValueLength = *ValueLength;
|
||
if (!ARGUMENT_PRESENT(Value)) {
|
||
LocalValueLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Capture the vendor GUID.
|
||
//
|
||
|
||
RtlCopyMemory( &LocalGuid, VendorGuid, sizeof(GUID) );
|
||
|
||
//
|
||
// Allocate a nonpaged buffer and copy the specified Unicode variable
|
||
// name into that buffer. We do this for two reasons: 1) we need the
|
||
// string to be in nonpaged pool; and 2) the string needs to be null-
|
||
// terminated, and it might not be already.
|
||
//
|
||
|
||
LocalUnicodeBuffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPool,
|
||
UnicodeString.Length + sizeof(WCHAR),
|
||
'rvnE');
|
||
if (LocalUnicodeBuffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(LocalUnicodeBuffer, UnicodeString.Buffer, UnicodeString.Length);
|
||
LocalUnicodeBuffer[UnicodeString.Length/sizeof(WCHAR)] = 0;
|
||
|
||
//
|
||
// If an exception occurs during the read of the variable descriptor,
|
||
// the read of the variable name, the read of the vendor GUID, the probe
|
||
// of the variable value, the read of the input length, or the probe
|
||
// of the attributes parameter, then always handle the exception,
|
||
// free the Unicode string buffer if necessary, and return the exception
|
||
// code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (LocalUnicodeBuffer != NULL) {
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Lock the caller's value buffer in memory.
|
||
//
|
||
|
||
if (LocalValueLength != 0) {
|
||
NtStatus = ExLockUserBuffer(Value,
|
||
LocalValueLength,
|
||
&LockedValueBuffer,
|
||
&LockVariable);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
return NtStatus;
|
||
}
|
||
} else {
|
||
LockedValueBuffer = NULL;
|
||
LockVariable = NULL;
|
||
}
|
||
|
||
//
|
||
// Get the system environment variable value.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = HalGetEnvironmentVariableEx(LocalUnicodeBuffer,
|
||
&LocalGuid,
|
||
LockedValueBuffer,
|
||
&LocalValueLength,
|
||
&LocalAttributes);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Free the Unicode string buffer used to hold the variable name.
|
||
//
|
||
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
|
||
//
|
||
// Unlock the value buffer.
|
||
//
|
||
|
||
if (LockVariable != NULL) {
|
||
ExUnlockUserBuffer(LockVariable);
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the return
|
||
// length and the attributes. If either of the write attempts fail,
|
||
// then return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the length of the variable value.
|
||
//
|
||
|
||
*ValueLength = LocalValueLength;
|
||
|
||
//
|
||
// If argument is present, then write the variable attributes.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Attributes)) {
|
||
*Attributes = LocalAttributes;
|
||
}
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return length or
|
||
// the write of the attributes, then always handle the exception
|
||
// and return the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtQuerySystemEnvironmentValueEx
|
||
|
||
NTSTATUS
|
||
NtSetSystemEnvironmentValueEx (
|
||
IN PUNICODE_STRING VariableName,
|
||
IN LPGUID VendorGuid,
|
||
IN PVOID Value,
|
||
IN ULONG ValueLength,
|
||
IN ULONG Attributes
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function sets the specified system environment variable to the
|
||
specified value.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
VariableName - Supplies a pointer to a UNICODE descriptor for the specified
|
||
system environment variable.
|
||
|
||
VendorGuid - Supplies the GUID for the vendor associated with the variable.
|
||
Variables are grouped into namespaces based on their vendor GUIDs. Some
|
||
platforms may not support vendor GUIDs. On these platforms, all
|
||
variables are in a single namespace, and this routine ignores VendorGuid.
|
||
|
||
Value - Supplies a pointer to a buffer that contains the new variable value.
|
||
|
||
ValueLength - Supplies the length in bytes of the Value buffer.
|
||
|
||
Attributes - Supplies the attributes of the variable. The attribute bit
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE MUST be set.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INSUFFICIENT_RESOURCES Insufficient system resources exist
|
||
for this request to complete.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(VariableName);
|
||
UNREFERENCED_PARAMETER(VendorGuid);
|
||
UNREFERENCED_PARAMETER(Value);
|
||
UNREFERENCED_PARAMETER(ValueLength);
|
||
UNREFERENCED_PARAMETER(Attributes);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
UNICODE_STRING UnicodeString;
|
||
PWSTR LocalUnicodeBuffer = NULL;
|
||
GUID LocalGuid;
|
||
PCHAR LockedValueBuffer;
|
||
PVOID LockVariable;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and read the
|
||
// name of the specified system environment variable, probe and read
|
||
// the vendor GUID, and probe the variable value buffer. If the probe
|
||
// attempt fails, then return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the string descriptor for the system
|
||
// environment variable name.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VariableName,
|
||
sizeof(UNICODE_STRING),
|
||
sizeof(ULONG));
|
||
|
||
UnicodeString = *VariableName;
|
||
|
||
//
|
||
// Probe the system environment variable name.
|
||
//
|
||
|
||
if (UnicodeString.Length == 0) {
|
||
return STATUS_ACCESS_VIOLATION;
|
||
}
|
||
|
||
ProbeForRead((PVOID)UnicodeString.Buffer,
|
||
UnicodeString.Length,
|
||
sizeof(WCHAR));
|
||
|
||
//
|
||
// Probe the vendor GUID.
|
||
//
|
||
|
||
ProbeForReadSmallStructure((PVOID)VendorGuid, sizeof(GUID), sizeof(ULONG));
|
||
|
||
//
|
||
// Probe the system environment value buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Value)) {
|
||
ValueLength = 0;
|
||
}
|
||
|
||
if (ValueLength != 0) {
|
||
ProbeForWrite((PVOID)Value, ValueLength, sizeof(UCHAR));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to set a system
|
||
// environment variable.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
UnicodeString = *VariableName;
|
||
if (!ARGUMENT_PRESENT(Value)) {
|
||
ValueLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Capture the vendor GUID.
|
||
//
|
||
|
||
RtlCopyMemory( &LocalGuid, VendorGuid, sizeof(GUID) );
|
||
|
||
//
|
||
// Allocate a nonpaged buffer and copy the specified Unicode variable
|
||
// name into that buffer. We do this for two reasons: 1) we need the
|
||
// string to be in nonpaged pool; and 2) the string needs to be null-
|
||
// terminated, and it might not be already.
|
||
//
|
||
|
||
LocalUnicodeBuffer = (PWSTR)ExAllocatePoolWithTag(NonPagedPool,
|
||
UnicodeString.Length + sizeof(WCHAR),
|
||
'rvnE');
|
||
if (LocalUnicodeBuffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(LocalUnicodeBuffer, UnicodeString.Buffer, UnicodeString.Length);
|
||
LocalUnicodeBuffer[UnicodeString.Length/sizeof(WCHAR)] = 0;
|
||
|
||
//
|
||
// If an exception occurs during the read of the variable descriptor,
|
||
// the read of the variable name, the read of the vendor GUID or the probe
|
||
// of the variable value, then always handle the exception, free the Unicode
|
||
// string buffer if necessary, and return the exception code as the status
|
||
// value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (LocalUnicodeBuffer != NULL) {
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
}
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Lock the caller's value buffer in memory.
|
||
//
|
||
|
||
if (ValueLength != 0) {
|
||
NtStatus = ExLockUserBuffer(Value,
|
||
ValueLength,
|
||
&LockedValueBuffer,
|
||
&LockVariable);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
return NtStatus;
|
||
}
|
||
} else {
|
||
LockedValueBuffer = NULL;
|
||
LockVariable = NULL;
|
||
}
|
||
|
||
//
|
||
// Set the system environment variable value.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(LocalUnicodeBuffer,
|
||
&LocalGuid,
|
||
LockedValueBuffer,
|
||
ValueLength,
|
||
Attributes);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Free the Unicode string buffer used to hold the variable name.
|
||
//
|
||
|
||
ExFreePool((PVOID)LocalUnicodeBuffer);
|
||
|
||
//
|
||
// Unlock the value buffer.
|
||
//
|
||
|
||
if (LockVariable != NULL) {
|
||
ExUnlockUserBuffer(LockVariable);
|
||
}
|
||
|
||
return NtStatus;
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtSetSystemEnvironmentValueEx
|
||
|
||
NTSTATUS
|
||
NtEnumerateSystemEnvironmentValuesEx (
|
||
IN ULONG InformationClass,
|
||
OUT PVOID Buffer,
|
||
IN OUT PULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns information about system environment variables.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
InformationClass - Specifies the type of information to return.
|
||
|
||
Buffer - Supplies the address of the buffer that is to receive the
|
||
returned data. The format of the returned data depends on
|
||
InformationClass.
|
||
|
||
BufferLength - On input, supplies the length in bytes of the buffer.
|
||
On output, returns the length in bytes of the returned data.
|
||
If the input buffer is large enough, then BufferLength indicates
|
||
the amount of data copied into Buffer. If the input buffer is too
|
||
small, then BufferLength indicates the required buffer length.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_BUFFER_TOO_SMALL The input buffer was too small.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(InformationClass);
|
||
UNREFERENCED_PARAMETER(Buffer);
|
||
UNREFERENCED_PARAMETER(BufferLength);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PCHAR LockedBuffer;
|
||
ULONG LocalBufferLength;
|
||
PVOID LockVariable;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe the return buffer
|
||
// and probe and read the buffer length. If the probe attempt fails, then
|
||
// return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the input buffer length.
|
||
//
|
||
|
||
ProbeForWriteUlong(BufferLength);
|
||
|
||
LocalBufferLength = *BufferLength;
|
||
|
||
//
|
||
// Probe the return buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Buffer)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
|
||
if (LocalBufferLength != 0) {
|
||
ProbeForWrite((PVOID)Buffer, LocalBufferLength, sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to enumerate
|
||
// system environment variables.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
LocalBufferLength = *BufferLength;
|
||
if (!ARGUMENT_PRESENT(Buffer)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the return buffer or the
|
||
// read of the input length, then always handle the exception and return
|
||
// the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Lock the caller's return buffer in memory.
|
||
//
|
||
|
||
if (LocalBufferLength != 0) {
|
||
NtStatus = ExLockUserBuffer(Buffer,
|
||
LocalBufferLength,
|
||
&LockedBuffer,
|
||
&LockVariable);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return NtStatus;
|
||
}
|
||
} else {
|
||
LockedBuffer = NULL;
|
||
LockVariable = NULL;
|
||
}
|
||
|
||
//
|
||
// Enumerate the system environment variables.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = HalEnumerateEnvironmentVariablesEx(InformationClass,
|
||
LockedBuffer,
|
||
&LocalBufferLength);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// Unlock the return buffer.
|
||
//
|
||
|
||
if (LockVariable != NULL) {
|
||
ExUnlockUserBuffer(LockVariable);
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the return length.
|
||
// If the write attempt fails, then return the exception code as the
|
||
// service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the length of the returned data.
|
||
//
|
||
|
||
*BufferLength = LocalBufferLength;
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return length, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtEnumerateSystemEnvironmentValuesEx
|
||
|
||
NTSTATUS
|
||
NtAddBootEntry (
|
||
IN PBOOT_ENTRY BootEntry,
|
||
OUT PULONG Id OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds a boot entry to the system environment.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
BootEntry - Supplies the address of a BOOT_ENTRY that describes the
|
||
new boot entry.
|
||
|
||
Id - Supplies the address of a ULONG that is to receive the identifier
|
||
assigned to the new boot entry.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
return ExpSetBootEntry(TRUE, BootEntry, Id);
|
||
|
||
} // NtAddBootEntry
|
||
|
||
NTSTATUS
|
||
NtDeleteBootEntry (
|
||
IN ULONG Id
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function deletes an existing boot entry from the system environment.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Id - Supplies the identifier of the boot entry that is to be deleted.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_VARIABLE_NOT_FOUND The Id specifies a boot entry that does not exist.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(Id);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
WCHAR idString[9];
|
||
ULONG length;
|
||
|
||
//
|
||
// Verify that the input identifier is in range.
|
||
//
|
||
|
||
if (Id > MAXUSHORT) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Verify that the provided identifier exists.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
swprintf( idString, L"Boot%04x", Id);
|
||
length = 0;
|
||
NtStatus = HalGetEnvironmentVariableEx(idString,
|
||
&EfiBootVariablesGuid,
|
||
NULL,
|
||
&length,
|
||
NULL);
|
||
if ((NtStatus == STATUS_SUCCESS) || (NtStatus == STATUS_BUFFER_TOO_SMALL)) {
|
||
|
||
//
|
||
// Delete the boot entry environment variable by writing a zero length
|
||
// value.
|
||
//
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(idString,
|
||
&EfiBootVariablesGuid,
|
||
NULL,
|
||
0,
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE);
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
return NtStatus;
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtDeleteBootEntry
|
||
|
||
NTSTATUS
|
||
NtModifyBootEntry (
|
||
IN PBOOT_ENTRY BootEntry
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function modifies an existing boot entry in the system environment.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
BootEntry - Supplies the address of a BOOT_ENTRY that describes the
|
||
modified boot entry. The Id field of this structure specifies the
|
||
boot entry that is to be modified.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_VARIABLE_NOT_FOUND The Id specifies a boot entry that does not exist.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
return ExpSetBootEntry(FALSE, BootEntry, NULL);
|
||
|
||
} // NtModifyBootEntry
|
||
|
||
NTSTATUS
|
||
NtEnumerateBootEntries (
|
||
OUT PVOID Buffer,
|
||
IN OUT PULONG BufferLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns a list of all existing boot entries.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Buffer - Supplies the address of the buffer that is to receive the
|
||
returned data. The returned data is a sequence of BOOT_ENTRY_LIST
|
||
structures.
|
||
|
||
BufferLength - On input, supplies the length in bytes of the buffer.
|
||
On output, returns the length in bytes of the returned data.
|
||
If the input buffer is large enough, then BufferLength indicates
|
||
the amount of data copied into Buffer. If the input buffer
|
||
is too small, then BufferLength indicates the required buffer length.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_BUFFER_TOO_SMALL The input buffer was too small.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(Buffer);
|
||
UNREFERENCED_PARAMETER(BufferLength);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PCHAR LockedBuffer;
|
||
ULONG LocalBufferLength;
|
||
PVOID LockVariable;
|
||
PVARIABLE_NAME_AND_VALUE variableBuffer = NULL;
|
||
ULONG variableBufferLength;
|
||
PBOOT_ENTRY_LIST currentPtr;
|
||
PBOOT_ENTRY_LIST previousEntry;
|
||
ULONG remainingLength;
|
||
LOGICAL filling;
|
||
NTSTATUS fillStatus;
|
||
PVARIABLE_NAME_AND_VALUE variablePtr;
|
||
PWSTR maxVariablePtr;
|
||
|
||
//
|
||
// Verify that the input buffer is properly aligned.
|
||
//
|
||
|
||
if ( ALIGN_DOWN_POINTER(Buffer, ULONG) != Buffer ) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe the return buffer
|
||
// and probe and read the buffer length. If the probe attempt fails, then
|
||
// return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the input buffer length.
|
||
//
|
||
|
||
ProbeForWriteUlong(BufferLength);
|
||
|
||
LocalBufferLength = *BufferLength;
|
||
|
||
//
|
||
// Probe the return buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Buffer)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
|
||
if (LocalBufferLength != 0) {
|
||
ProbeForWrite((PVOID)Buffer, LocalBufferLength, sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot entry list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
LocalBufferLength = *BufferLength;
|
||
if (!ARGUMENT_PRESENT(Buffer)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the return buffer or the
|
||
// read of the input length, then always handle the exception and return
|
||
// the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Lock the caller's return buffer in memory.
|
||
//
|
||
|
||
if (LocalBufferLength != 0) {
|
||
NtStatus = ExLockUserBuffer(Buffer,
|
||
LocalBufferLength,
|
||
&LockedBuffer,
|
||
&LockVariable);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return NtStatus;
|
||
}
|
||
} else {
|
||
LockedBuffer = NULL;
|
||
LockVariable = NULL;
|
||
}
|
||
|
||
//
|
||
// Initialize variables for filling the output buffer.
|
||
//
|
||
|
||
currentPtr = (PBOOT_ENTRY_LIST)LockedBuffer;
|
||
remainingLength = LocalBufferLength;
|
||
|
||
filling = (LOGICAL)(remainingLength != 0);
|
||
fillStatus = STATUS_SUCCESS;
|
||
if ( !filling ) {
|
||
fillStatus = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
previousEntry = NULL;
|
||
|
||
//
|
||
// Enumerate all existing environment variables.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
variableBufferLength = 0;
|
||
NtStatus = HalEnumerateEnvironmentVariablesEx(VARIABLE_INFORMATION_VALUES,
|
||
NULL,
|
||
&variableBufferLength);
|
||
if (NtStatus != STATUS_BUFFER_TOO_SMALL) {
|
||
variableBufferLength = 0;
|
||
} else {
|
||
variableBuffer = ExAllocatePoolWithTag(NonPagedPool, variableBufferLength, 'rvnE');
|
||
if (variableBuffer == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
} else {
|
||
NtStatus = HalEnumerateEnvironmentVariablesEx(VARIABLE_INFORMATION_VALUES,
|
||
variableBuffer,
|
||
&variableBufferLength);
|
||
}
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
if ((NtStatus != STATUS_SUCCESS) || (variableBufferLength == 0)) {
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Each variable whose name is of the form Boot####, where #### is a
|
||
// four-digit hex number, is assumed to define a boot entry. For
|
||
// each such variable, copy its data into the output buffer.
|
||
//
|
||
|
||
variablePtr = variableBuffer;
|
||
maxVariablePtr = (PWSTR)variableBuffer + variableBufferLength;
|
||
|
||
while (TRUE) {
|
||
|
||
ULONG id;
|
||
|
||
if ((memcmp(&variablePtr->VendorGuid, &EfiBootVariablesGuid, sizeof(GUID)) == 0) &&
|
||
ExpTranslateBootEntryNameToId(variablePtr->Name, &id) &&
|
||
(variablePtr->ValueLength >= sizeof(EFI_LOAD_OPTION))) {
|
||
|
||
PEFI_LOAD_OPTION efiLoadOption;
|
||
ULONG descriptionLength;
|
||
ULONG filePathLength;
|
||
ULONG minimumLength;
|
||
|
||
efiLoadOption = ADD_OFFSET(variablePtr, ValueOffset);
|
||
filePathLength = efiLoadOption->FilePathLength;
|
||
descriptionLength = ExpSafeWcslen(efiLoadOption->Description, maxVariablePtr);
|
||
if ( descriptionLength != 0xffffffff ) {
|
||
descriptionLength = (descriptionLength + 1) * sizeof(WCHAR);
|
||
}
|
||
minimumLength = FIELD_OFFSET(EFI_LOAD_OPTION, Description) +
|
||
descriptionLength +
|
||
filePathLength;
|
||
|
||
if ((descriptionLength != 0xffffffff) &&
|
||
(filePathLength < variablePtr->ValueLength) &&
|
||
(variablePtr->ValueLength >= minimumLength)) {
|
||
|
||
EFI_DEVICE_PATH *dp;
|
||
PUCHAR options;
|
||
ULONG optionsLength;
|
||
ULONG actualLength;
|
||
ULONG requiredLength;
|
||
ULONG friendlyNameOffset;
|
||
ULONG bootFilePathOffset;
|
||
|
||
dp = (EFI_DEVICE_PATH *)((PUCHAR)efiLoadOption->Description + descriptionLength);
|
||
options = (PUCHAR)dp + filePathLength;
|
||
optionsLength = variablePtr->ValueLength - minimumLength;
|
||
|
||
if (ALIGN_UP_POINTER(currentPtr, ULONG) != currentPtr) {
|
||
PUCHAR alignedPtr = ALIGN_UP_POINTER( currentPtr, ULONG );
|
||
ULONG fill = (ULONG)(alignedPtr - (PUCHAR)currentPtr);
|
||
currentPtr = (PBOOT_ENTRY_LIST)alignedPtr;
|
||
if (remainingLength < fill) {
|
||
filling = FALSE;
|
||
remainingLength = 0;
|
||
fillStatus = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
remainingLength -= fill;
|
||
}
|
||
}
|
||
|
||
requiredLength = FIELD_OFFSET(BOOT_ENTRY, OsOptions);
|
||
requiredLength += optionsLength;
|
||
requiredLength = ALIGN_UP(requiredLength, ULONG);
|
||
|
||
friendlyNameOffset = requiredLength;
|
||
requiredLength += descriptionLength;
|
||
requiredLength = ALIGN_UP(requiredLength, ULONG);
|
||
|
||
bootFilePathOffset = requiredLength;
|
||
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
|
||
requiredLength += filePathLength;
|
||
|
||
actualLength = requiredLength;
|
||
requiredLength += FIELD_OFFSET(BOOT_ENTRY_LIST, BootEntry);
|
||
|
||
if (remainingLength < requiredLength) {
|
||
|
||
remainingLength = 0;
|
||
filling = FALSE;
|
||
fillStatus = STATUS_BUFFER_TOO_SMALL;
|
||
|
||
} else {
|
||
|
||
remainingLength -= requiredLength;
|
||
}
|
||
|
||
if ( filling ) {
|
||
|
||
PWCHAR friendlyName;
|
||
PFILE_PATH bootFilePath;
|
||
PBOOT_ENTRY bootEntry = ¤tPtr->BootEntry;
|
||
|
||
RtlZeroMemory(currentPtr, requiredLength);
|
||
|
||
bootEntry->Version = BOOT_ENTRY_VERSION;
|
||
bootEntry->Length = actualLength;
|
||
bootEntry->Id = id;
|
||
bootEntry->Attributes = 0;
|
||
if ((efiLoadOption->Attributes & LOAD_OPTION_ACTIVE) != 0) {
|
||
bootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE;
|
||
}
|
||
bootEntry->FriendlyNameOffset = friendlyNameOffset;
|
||
bootEntry->BootFilePathOffset = bootFilePathOffset;
|
||
bootEntry->OsOptionsLength = optionsLength;
|
||
memcpy(bootEntry->OsOptions, options, optionsLength);
|
||
if (optionsLength > FIELD_OFFSET(WINDOWS_OS_OPTIONS,OsLoadOptions)) {
|
||
PWINDOWS_OS_OPTIONS windowsOsOptions;
|
||
windowsOsOptions = (PWINDOWS_OS_OPTIONS)bootEntry->OsOptions;
|
||
if ((strcmp((char *)windowsOsOptions->Signature,
|
||
WINDOWS_OS_OPTIONS_SIGNATURE) == 0) &&
|
||
NT_SUCCESS(ExpVerifyWindowsOsOptions(windowsOsOptions,
|
||
optionsLength))) {
|
||
bootEntry->Attributes |= BOOT_ENTRY_ATTRIBUTE_WINDOWS;
|
||
}
|
||
}
|
||
friendlyName = (PWCHAR)((PUCHAR)bootEntry + friendlyNameOffset);
|
||
memcpy(friendlyName, efiLoadOption->Description, descriptionLength);
|
||
bootFilePath = (PFILE_PATH)((PUCHAR)bootEntry + bootFilePathOffset);
|
||
bootFilePath->Version = FILE_PATH_VERSION;
|
||
bootFilePath->Length = FIELD_OFFSET(FILE_PATH, FilePath) + filePathLength;
|
||
bootFilePath->Type = FILE_PATH_TYPE_EFI;
|
||
memcpy(bootFilePath->FilePath, dp, filePathLength);
|
||
if (NT_SUCCESS(ExpVerifyFilePath(bootFilePath,
|
||
ADD_OFFSET(bootFilePath, Length))) &&
|
||
ExpIsDevicePathForRemovableMedia(dp)) {
|
||
bootEntry->Attributes |= BOOT_ENTRY_ATTRIBUTE_REMOVABLE_MEDIA;
|
||
}
|
||
|
||
if ( previousEntry != NULL ) {
|
||
previousEntry->NextEntryOffset =
|
||
(ULONG)((PUCHAR)currentPtr - (PUCHAR)previousEntry);
|
||
}
|
||
previousEntry = currentPtr;
|
||
}
|
||
|
||
currentPtr = (PBOOT_ENTRY_LIST)((PUCHAR)currentPtr + requiredLength);
|
||
}
|
||
}
|
||
|
||
if (variablePtr->NextEntryOffset == 0) {
|
||
break;
|
||
}
|
||
variablePtr = ADD_OFFSET(variablePtr, NextEntryOffset);
|
||
}
|
||
|
||
if ( previousEntry != NULL ) {
|
||
previousEntry->NextEntryOffset = 0;
|
||
}
|
||
|
||
done:
|
||
|
||
//
|
||
// Free allocated pool.
|
||
//
|
||
|
||
if (variableBuffer != NULL) {
|
||
ExFreePool(variableBuffer);
|
||
}
|
||
|
||
//
|
||
// Unlock the return buffer.
|
||
//
|
||
|
||
if (LockVariable != NULL) {
|
||
ExUnlockUserBuffer(LockVariable);
|
||
}
|
||
|
||
//
|
||
// If the status of service calls is STATUS_SUCCESS, then return the fill
|
||
// status as the final status.
|
||
//
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
NtStatus = fillStatus;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the return length.
|
||
// If the write attempt fails, then return the exception code as the
|
||
// service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the length of the returned data.
|
||
//
|
||
|
||
*BufferLength = (ULONG)((PUCHAR)currentPtr - (PUCHAR)LockedBuffer);
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return length, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtEnumerateBootEntries
|
||
|
||
NTSTATUS
|
||
NtQueryBootEntryOrder (
|
||
OUT PULONG Ids,
|
||
IN OUT PULONG Count
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the system boot order list.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Ids - Supplies the address of the buffer that is to receive the
|
||
returned data. The returned data is an array of ULONG boot
|
||
entry identifiers.
|
||
|
||
Count - On input, supplies the length in ULONGs of the buffer.
|
||
On output, returns the length in ULONGs of the returned data.
|
||
If the input buffer is large enough, then Count indicates
|
||
the amount of data copied into Buffer. If the input buffer
|
||
is too small, then Count indicates the required buffer length.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_BUFFER_TOO_SMALL The input buffer was too small.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(Ids);
|
||
UNREFERENCED_PARAMETER(Count);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PCHAR LockedBuffer;
|
||
ULONG LocalBufferLength;
|
||
PVOID LockVariable;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe the return buffer
|
||
// and probe and read the buffer length. If the probe attempt fails, then
|
||
// return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the input buffer length.
|
||
//
|
||
|
||
ProbeForWriteUlong(Count);
|
||
|
||
LocalBufferLength = *Count * sizeof(ULONG);
|
||
|
||
//
|
||
// Probe the return buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(Ids)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
|
||
if (LocalBufferLength != 0) {
|
||
ProbeForWrite((PVOID)Ids, LocalBufferLength, sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
LocalBufferLength = *Count * sizeof(ULONG);
|
||
if (!ARGUMENT_PRESENT(Ids)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the return buffer or the
|
||
// read of the input length, then always handle the exception and return
|
||
// the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Lock the caller's return buffer in memory.
|
||
//
|
||
|
||
if (LocalBufferLength != 0) {
|
||
NtStatus = ExLockUserBuffer(Ids,
|
||
LocalBufferLength,
|
||
&LockedBuffer,
|
||
&LockVariable);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
return NtStatus;
|
||
}
|
||
} else {
|
||
LockedBuffer = NULL;
|
||
LockVariable = NULL;
|
||
}
|
||
|
||
//
|
||
// EFI returns USHORT identifiers, which we will need to translate to
|
||
// ULONGs. Cut the buffer length in half to account for this.
|
||
//
|
||
|
||
LocalBufferLength /= 2;
|
||
|
||
//
|
||
// Query the BootOrder system environment variable.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = HalGetEnvironmentVariableEx(L"BootOrder",
|
||
&EfiBootVariablesGuid,
|
||
LockedBuffer,
|
||
&LocalBufferLength,
|
||
NULL);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
//
|
||
// If the API succeeded, translate the returned USHORTs into ULONGs.
|
||
// Do this by converting each USHORT into a ULONG, starting from the
|
||
// end of the array to avoid stomping on needed data.
|
||
//
|
||
|
||
if (NT_SUCCESS(NtStatus)) {
|
||
|
||
ULONG count = LocalBufferLength / sizeof(USHORT);
|
||
PUSHORT sp = &((PUSHORT)LockedBuffer)[count - 1];
|
||
PULONG lp = &((PULONG)LockedBuffer)[count - 1];
|
||
while (count > 0) {
|
||
*lp-- = *sp--;
|
||
count--;
|
||
}
|
||
|
||
} else if (NtStatus == STATUS_VARIABLE_NOT_FOUND) {
|
||
|
||
//
|
||
// The BootOrder variable doesn't exist. This is unusual,
|
||
// but possible. We'll just return an empty list.
|
||
//
|
||
|
||
LocalBufferLength = 0;
|
||
NtStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
LocalBufferLength *= 2;
|
||
|
||
//
|
||
// Unlock the buffer.
|
||
//
|
||
|
||
if (LockVariable != NULL) {
|
||
ExUnlockUserBuffer(LockVariable);
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the return length.
|
||
// If the write attempt fails, then return the exception code as the
|
||
// service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the length of the returned data.
|
||
//
|
||
|
||
*Count = LocalBufferLength / sizeof(ULONG);
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return length, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtQueryBootEntryOrder
|
||
|
||
NTSTATUS
|
||
NtSetBootEntryOrder (
|
||
IN PULONG Ids,
|
||
IN ULONG Count
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function modifies the system boot order list.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
Ids - Supplies the address of an array that contains the new boot
|
||
entry order list. The data is an array of ULONG identifiers.
|
||
|
||
Count - Supplies the length in ULONGs of the Ids array.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(Ids);
|
||
UNREFERENCED_PARAMETER(Count);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
ULONG LocalBufferLength;
|
||
PUSHORT shortBuffer;
|
||
ULONG i;
|
||
|
||
//
|
||
// Verify that the input buffer is not empty and is not too large.
|
||
// Calculate the length in bytes of the buffer.
|
||
//
|
||
|
||
if ((Count == 0) || (Count > MAXULONG/sizeof(ULONG))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
LocalBufferLength = Count * sizeof(ULONG);
|
||
|
||
//
|
||
// Allocate a nonpaged buffer to hold the USHORT versions of the IDs.
|
||
//
|
||
|
||
shortBuffer = ExAllocatePoolWithTag(NonPagedPool, Count * sizeof(USHORT), 'rvnE');
|
||
if (shortBuffer == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe the input buffer.
|
||
// If the probe attempt fails, then return the exception code as the
|
||
// service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe the input buffer.
|
||
//
|
||
|
||
ProbeForRead((PVOID)Ids, LocalBufferLength, sizeof(ULONG));
|
||
|
||
//
|
||
// Check if the current thread has the privilege to modify the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
ExFreePool(shortBuffer);
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Truncate the ULONGs in the input buffer into USHORTs in
|
||
// the local buffer.
|
||
//
|
||
|
||
for ( i = 0; i < Count; i++ ) {
|
||
if (Ids[i] > MAXUSHORT) {
|
||
ExFreePool(shortBuffer);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
shortBuffer[i] = (USHORT)Ids[i];
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the input buffer, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
ExFreePool(shortBuffer);
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Set the BootOrder system environment variable.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(L"BootOrder",
|
||
&EfiBootVariablesGuid,
|
||
shortBuffer,
|
||
Count * sizeof(USHORT),
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE);
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
ExFreePool(shortBuffer);
|
||
|
||
return NtStatus;
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtSetBootEntryOrder
|
||
|
||
NTSTATUS
|
||
NtQueryBootOptions (
|
||
OUT PBOOT_OPTIONS BootOptions,
|
||
IN OUT PULONG BootOptionsLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function returns the system's global boot options.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
BootOptions - Supplies the address of the buffer that is to receive the
|
||
returned data.
|
||
|
||
BootOptionsLength - On input, supplies the length in bytes of the buffer.
|
||
On output, returns the length in bytes of the returned data.
|
||
If the input buffer is large enough, then BootOptionsLength indicates
|
||
the amount of data copied into BootOptions. If the input buffer
|
||
is too small, then BootOptionsLength indicates the required buffer
|
||
length.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_BUFFER_TOO_SMALL The input buffer was too small.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(BootOptions);
|
||
UNREFERENCED_PARAMETER(BootOptionsLength);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
ULONG LocalBufferLength;
|
||
ULONG Timeout = 0;
|
||
ULONG BootCurrent = 0;
|
||
ULONG BootNext = 0;
|
||
ULONG VariableLength;
|
||
ULONG requiredLength;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe the return buffer
|
||
// and probe and read the buffer length. If the probe attempt fails, then
|
||
// return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe and capture the input buffer length.
|
||
//
|
||
|
||
ProbeForWriteUlong(BootOptionsLength);
|
||
|
||
LocalBufferLength = *BootOptionsLength;
|
||
|
||
//
|
||
// Probe the return buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(BootOptions)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
|
||
if (LocalBufferLength != 0) {
|
||
ProbeForWrite((PVOID)BootOptions, LocalBufferLength, sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
LocalBufferLength = *BootOptionsLength;
|
||
if (!ARGUMENT_PRESENT(BootOptions)) {
|
||
LocalBufferLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe of the return buffer or the
|
||
// read of the input length, then always handle the exception and return
|
||
// the exception code as the status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Verify that the input buffer is big enough. IA64 always returns
|
||
// HeadlessRedirection as a null string, so we know the required
|
||
// length up front.
|
||
//
|
||
|
||
requiredLength = FIELD_OFFSET(BOOT_OPTIONS,HeadlessRedirection) + sizeof(WCHAR);
|
||
|
||
if (LocalBufferLength < requiredLength) {
|
||
NtStatus = STATUS_BUFFER_TOO_SMALL;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Query the following system environment variables: Timeout, BootCurrent,
|
||
// and BootNext.
|
||
//
|
||
// NB: Some machines seem to have their Timeout variable set as a ULONG
|
||
// instead of a USHORT. Since we have ULONG buffers for the variables that
|
||
// we're querying, we'll pass in the full length of the buffer, even though
|
||
// we only expect to get back a USHORT. And we'll also be prepared for an
|
||
// even bigger variable to exist. If the variable is bigger, then we'll
|
||
// return a default value for the variable.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
VariableLength = 4;
|
||
NtStatus = HalGetEnvironmentVariableEx(L"Timeout",
|
||
&EfiBootVariablesGuid,
|
||
&Timeout,
|
||
&VariableLength,
|
||
NULL);
|
||
|
||
switch (NtStatus) {
|
||
|
||
case STATUS_SUCCESS:
|
||
if (VariableLength > 2) {
|
||
if (Timeout == 0xffffffff) {
|
||
Timeout = 0xffff;
|
||
} else if (Timeout > 0xffff) {
|
||
Timeout = 0xfffe;
|
||
}
|
||
}
|
||
if ( Timeout == 0xffff ) {
|
||
Timeout = 0xffffffff;
|
||
}
|
||
break;
|
||
|
||
case STATUS_VARIABLE_NOT_FOUND:
|
||
Timeout = 0xffffffff;
|
||
break;
|
||
|
||
case STATUS_BUFFER_TOO_SMALL:
|
||
Timeout = 0xfffffffe;
|
||
break;
|
||
|
||
default:
|
||
goto done_unlock;
|
||
}
|
||
|
||
VariableLength = 4;
|
||
NtStatus = HalGetEnvironmentVariableEx(L"BootCurrent",
|
||
&EfiBootVariablesGuid,
|
||
&BootCurrent,
|
||
&VariableLength,
|
||
NULL);
|
||
|
||
switch (NtStatus) {
|
||
|
||
case STATUS_SUCCESS:
|
||
if (VariableLength > 2) {
|
||
BootCurrent &= 0xffff;
|
||
}
|
||
break;
|
||
|
||
case STATUS_VARIABLE_NOT_FOUND:
|
||
case STATUS_BUFFER_TOO_SMALL:
|
||
BootCurrent = 0xfffffffe;
|
||
break;
|
||
|
||
default:
|
||
goto done_unlock;
|
||
}
|
||
|
||
VariableLength = 2;
|
||
NtStatus = HalGetEnvironmentVariableEx(L"BootNext",
|
||
&EfiBootVariablesGuid,
|
||
&BootNext,
|
||
&VariableLength,
|
||
NULL);
|
||
|
||
switch (NtStatus) {
|
||
|
||
case STATUS_SUCCESS:
|
||
if (VariableLength > 2) {
|
||
BootNext &= 0xffff;
|
||
}
|
||
break;
|
||
|
||
case STATUS_VARIABLE_NOT_FOUND:
|
||
case STATUS_BUFFER_TOO_SMALL:
|
||
BootNext = 0xfffffffe;
|
||
NtStatus = STATUS_SUCCESS;
|
||
break;
|
||
|
||
default:
|
||
goto done_unlock;
|
||
}
|
||
|
||
done_unlock:
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
done:
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the output buffer
|
||
// and the return length. If the write attempt fails, then return the
|
||
// exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the output buffer.
|
||
//
|
||
|
||
if ((NtStatus == STATUS_SUCCESS) && ARGUMENT_PRESENT(BootOptions)) {
|
||
BootOptions->Version = BOOT_OPTIONS_VERSION;
|
||
BootOptions->Length = (FIELD_OFFSET(BOOT_OPTIONS,HeadlessRedirection) + sizeof(WCHAR));
|
||
BootOptions->Timeout = Timeout;
|
||
BootOptions->CurrentBootEntryId = BootCurrent;
|
||
BootOptions->NextBootEntryId = BootNext;
|
||
BootOptions->HeadlessRedirection[0] = 0;
|
||
}
|
||
|
||
//
|
||
// Write the return length.
|
||
//
|
||
|
||
*BootOptionsLength = requiredLength;
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return data, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtQueryBootOptions
|
||
|
||
NTSTATUS
|
||
NtSetBootOptions (
|
||
IN PBOOT_OPTIONS BootOptions,
|
||
IN ULONG FieldsToChange
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function modifies the system's global boot options.
|
||
|
||
N.B. This service requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
BootOptions - Supplies the address of the buffer that contains the new
|
||
boot options.
|
||
|
||
FieldsToChange - Supplies a bit mask indicating with fields in BootOptions
|
||
are to be used to modify global boot options.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(BootOptions);
|
||
UNREFERENCED_PARAMETER(FieldsToChange);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
ULONG LocalBufferLength;
|
||
ULONG Timeout = 0;
|
||
ULONG BootNext = 0;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and validate the
|
||
// input buffer. If the probe attempt fails, then return the exception
|
||
// code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Verify that the input buffer is big enough. It must extend at
|
||
// least to the HeadlessRedirection field.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
LocalBufferLength = ProbeAndReadUlong(&BootOptions->Length);
|
||
} else {
|
||
LocalBufferLength = BootOptions->Length;
|
||
}
|
||
|
||
if (LocalBufferLength < FIELD_OFFSET(BOOT_OPTIONS,HeadlessRedirection)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe the input buffer.
|
||
//
|
||
|
||
ProbeForRead((PVOID)BootOptions, LocalBufferLength, sizeof(ULONG));
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Verify the structure version.
|
||
//
|
||
|
||
if ((BootOptions->Version == 0) ||
|
||
(BootOptions->Version > BOOT_OPTIONS_VERSION)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Capture the Timeout and BootNext fields.
|
||
//
|
||
|
||
Timeout = BootOptions->Timeout;
|
||
BootNext = BootOptions->NextBootEntryId;
|
||
|
||
//
|
||
// If an exception occurs during the probe and capture of the input buffer,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// If requested, set the Timeout and BootNext system environment variables.
|
||
//
|
||
|
||
if ((FieldsToChange & BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID) != 0) {
|
||
if (BootNext > MAXUSHORT) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
NtStatus = STATUS_SUCCESS;
|
||
|
||
if ((FieldsToChange & BOOT_OPTIONS_FIELD_TIMEOUT) != 0) {
|
||
|
||
if (Timeout == 0xffffffff) {
|
||
Timeout = 0xffff;
|
||
} else if (Timeout > 0xfffe) {
|
||
Timeout = 0xfffe;
|
||
}
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(L"Timeout",
|
||
&EfiBootVariablesGuid,
|
||
&Timeout,
|
||
2,
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE);
|
||
}
|
||
|
||
if (NT_SUCCESS(NtStatus) &&
|
||
((FieldsToChange & BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID) != 0)) {
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(L"BootNext",
|
||
&EfiBootVariablesGuid,
|
||
&BootNext,
|
||
2,
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE);
|
||
}
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
return NtStatus;
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtSetBootOptions
|
||
|
||
NTSTATUS
|
||
NtTranslateFilePath (
|
||
IN PFILE_PATH InputFilePath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputFilePath,
|
||
IN OUT PULONG OutputFilePathLength
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function translates a FILE_PATH from one format to another.
|
||
|
||
Arguments:
|
||
|
||
InputFilePath - Supplies the address of the buffer that contains the
|
||
FILE_PATH that is to be translated.
|
||
|
||
OutputType - Specifies the desired output file path type. One of
|
||
FILE_PATH_TYPE_ARC, FILE_PATH_TYPE_ARC_SIGNATURE, FILE_PATH_TYPE_NT,
|
||
and FILE_PATH_TYPE_EFI.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(InputFilePath);
|
||
UNREFERENCED_PARAMETER(OutputType);
|
||
UNREFERENCED_PARAMETER(OutputFilePath);
|
||
UNREFERENCED_PARAMETER(OutputFilePathLength);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS status;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
ULONG localInputPathLength;
|
||
ULONG localOutputPathLength;
|
||
PFILE_PATH localInputPath = NULL;
|
||
PFILE_PATH localOutputPath;
|
||
|
||
//
|
||
// Verify the output type.
|
||
//
|
||
|
||
if ((OutputType < FILE_PATH_TYPE_MIN) ||
|
||
(OutputType > FILE_PATH_TYPE_MAX)) {
|
||
//DbgPrint( "NtTranslateFilePath: OutputType outside range\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and read the
|
||
// input buffer, and probe the output buffer and the output length. If
|
||
// the probe attempt fails, then return the exception code as the service
|
||
// status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Verify that the input buffer is big enough. It must extend at
|
||
// least to the FilePath field.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
localInputPathLength = ProbeAndReadUlong(&InputFilePath->Length);
|
||
} else {
|
||
localInputPathLength = InputFilePath->Length;
|
||
}
|
||
|
||
if (localInputPathLength < FIELD_OFFSET(FILE_PATH,FilePath)) {
|
||
//DbgPrint( "NtTranslateFilePath: input buffer too short\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe the input buffer.
|
||
//
|
||
|
||
ProbeForRead((PVOID)InputFilePath, localInputPathLength, sizeof(ULONG));
|
||
|
||
//
|
||
// Probe and capture the output length.
|
||
//
|
||
|
||
ProbeForWriteUlong(OutputFilePathLength);
|
||
|
||
localOutputPathLength = *OutputFilePathLength;
|
||
|
||
//
|
||
// Probe the output buffer.
|
||
//
|
||
|
||
if (!ARGUMENT_PRESENT(OutputFilePath)) {
|
||
localOutputPathLength = 0;
|
||
}
|
||
|
||
if (localOutputPathLength != 0) {
|
||
ProbeForWrite((PVOID)OutputFilePath, localOutputPathLength, sizeof(ULONG));
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
} else {
|
||
localOutputPathLength = *OutputFilePathLength;
|
||
if (!ARGUMENT_PRESENT(OutputFilePath)) {
|
||
localOutputPathLength = 0;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate a nonpaged buffer to hold a copy of the input buffer.
|
||
// Copy the input buffer into the local buffer.
|
||
//
|
||
|
||
localInputPath = ExAllocatePoolWithTag(NonPagedPool, localInputPathLength, 'rvnE');
|
||
if (localInputPath == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(localInputPath, InputFilePath, localInputPathLength);
|
||
|
||
//
|
||
// Allocate a nonpaged buffer into which to build the output path.
|
||
//
|
||
|
||
if (localOutputPathLength != 0) {
|
||
localOutputPath = ExAllocatePoolWithTag(NonPagedPool, localOutputPathLength, 'rvnE');
|
||
if (localOutputPath == NULL) {
|
||
ExFreePool(localInputPath);
|
||
localInputPath = NULL;
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
} else {
|
||
localOutputPath = NULL;
|
||
}
|
||
|
||
//
|
||
// If an exception occurs during the probe and capture of the input buffer,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (localInputPath != NULL) {
|
||
ExFreePool(localInputPath);
|
||
}
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Verify the format of the input file path.
|
||
//
|
||
|
||
status = ExpVerifyFilePath(localInputPath, ADD_OFFSET(localInputPath, Length));
|
||
if (NT_SUCCESS(status)) {
|
||
|
||
//
|
||
// If the output type is the same as the input type, just copy the input
|
||
// path to the output path.
|
||
//
|
||
|
||
if (OutputType == localInputPath->Type) {
|
||
|
||
if (localOutputPathLength >= localInputPathLength) {
|
||
RtlCopyMemory(localOutputPath, localInputPath, localInputPathLength);
|
||
} else {
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
localOutputPathLength = localInputPathLength;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Conversion is required.
|
||
//
|
||
|
||
switch (localInputPath->Type) {
|
||
|
||
case FILE_PATH_TYPE_ARC:
|
||
case FILE_PATH_TYPE_ARC_SIGNATURE:
|
||
status = ExpTranslateArcPath(
|
||
localInputPath,
|
||
OutputType,
|
||
localOutputPath,
|
||
&localOutputPathLength
|
||
);
|
||
break;
|
||
|
||
case FILE_PATH_TYPE_NT:
|
||
status = ExpTranslateNtPath(
|
||
localInputPath,
|
||
OutputType,
|
||
localOutputPath,
|
||
&localOutputPathLength);
|
||
break;
|
||
|
||
case FILE_PATH_TYPE_EFI:
|
||
status = ExpTranslateEfiPath(
|
||
localInputPath,
|
||
OutputType,
|
||
localOutputPath,
|
||
&localOutputPathLength);
|
||
break;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
//DbgPrint( "NtTranslateFilePath: input type outside range\n" );
|
||
status = STATUS_INVALID_PARAMETER;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
ExFreePool(localInputPath);
|
||
|
||
//
|
||
// Establish an exception handler and attempt to copy to the output
|
||
// buffer and write the output length. If the write attempt fails, then
|
||
// return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Copy the output path.
|
||
//
|
||
|
||
if (NT_SUCCESS(status) && (localOutputPath != NULL)) {
|
||
RtlCopyMemory(OutputFilePath, localOutputPath, localOutputPathLength);
|
||
}
|
||
|
||
if (localOutputPath != NULL) {
|
||
ExFreePool(localOutputPath);
|
||
localOutputPath = NULL;
|
||
}
|
||
|
||
//
|
||
// Write the output length.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(OutputFilePathLength)) {
|
||
*OutputFilePathLength = localOutputPathLength;
|
||
}
|
||
|
||
return status;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return data, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (localOutputPath != NULL) {
|
||
ExFreePool(localOutputPath);
|
||
}
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // NtTranslateFilePath
|
||
|
||
NTSTATUS
|
||
ExpSetBootEntry (
|
||
IN LOGICAL CreateNewEntry,
|
||
IN PBOOT_ENTRY BootEntry,
|
||
OUT PULONG Id OPTIONAL
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function adds a boot entry to the system environment or modifies
|
||
an existing boot entry. It is a local routine called by NtAddBootEntry
|
||
and NtModifyBootEntry.
|
||
|
||
N.B. This function requires the system environment privilege.
|
||
|
||
Arguments:
|
||
|
||
CreateNewEntry - Indicates whether this function is to add a new boot
|
||
entry (TRUE - NtAddBootEntry), or modify an existing boot entry
|
||
(FALSE - NtModifyBootEntry).
|
||
BootEntry - Supplies the address of a BOOT_ENTRY that describes the
|
||
new boot entry.
|
||
|
||
Id - Supplies the address of a ULONG that is to receive the identifier
|
||
assigned to the new boot entry.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS The function succeeded.
|
||
STATUS_INVALID_PARAMETER One of the parameters is invalid.
|
||
STATUS_NOT_IMPLEMENTED This function is not supported on this platform.
|
||
STATUS_UNSUCCESSFUL The firmware returned an unrecognized error.
|
||
STATUS_PRIVILEGE_NOT_HELD The caller does not have the required privilege.
|
||
STATUS_ACCESS_VIOLATION One of the input parameters cannot be read,
|
||
or one of the output parameters cannot be written.
|
||
|
||
--*/
|
||
|
||
{
|
||
#if !defined(EFI_NVRAM_ENABLED)
|
||
UNREFERENCED_PARAMETER(CreateNewEntry);
|
||
UNREFERENCED_PARAMETER(BootEntry);
|
||
UNREFERENCED_PARAMETER(Id);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#else
|
||
|
||
BOOLEAN HasPrivilege;
|
||
NTSTATUS NtStatus;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PBOOT_ENTRY localBootEntry = NULL;
|
||
ULONG LocalBufferLength;
|
||
PUCHAR MaxBuffer;
|
||
ULONG id = 0;
|
||
WCHAR idString[9];
|
||
PWCHAR friendlyName;
|
||
ULONG friendlyNameLength;
|
||
PFILE_PATH bootFilePath = NULL;
|
||
PFILE_PATH translatedBootFilePath = NULL;
|
||
LOGICAL isWindowsOs;
|
||
PWINDOWS_OS_OPTIONS windowsOsOptions;
|
||
PFILE_PATH windowsFilePath;
|
||
PEFI_LOAD_OPTION efiLoadOption = NULL;
|
||
PUCHAR efiBootFilePath;
|
||
ULONG efiBootFilePathLength;
|
||
ULONG efiWindowsFilePathLength;
|
||
ULONG osOptionsLength;
|
||
ULONG length;
|
||
ULONG requiredLength;
|
||
PUCHAR efiOsOptions;
|
||
|
||
//
|
||
// Establish an exception handler and attempt to probe and read the
|
||
// input buffer, and probe the output identifier parameter. If the probe
|
||
// attempt fails, then return the exception code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Verify that the input buffer is big enough. It must extend at
|
||
// least to the OsOptions field.
|
||
//
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
if (PreviousMode != KernelMode) {
|
||
LocalBufferLength = ProbeAndReadUlong(&BootEntry->Length);
|
||
} else {
|
||
LocalBufferLength = BootEntry->Length;
|
||
}
|
||
|
||
if (LocalBufferLength < FIELD_OFFSET(BOOT_ENTRY,OsOptions)) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
//
|
||
// Get previous processor mode and probe arguments if necessary.
|
||
//
|
||
|
||
if (PreviousMode != KernelMode) {
|
||
|
||
//
|
||
// Probe the input buffer.
|
||
//
|
||
|
||
ProbeForRead((PVOID)BootEntry, LocalBufferLength, sizeof(ULONG));
|
||
|
||
//
|
||
// Probe the output identifier.
|
||
//
|
||
|
||
if (ARGUMENT_PRESENT(Id)) {
|
||
ProbeForWriteUlong(Id);
|
||
}
|
||
|
||
//
|
||
// Check if the current thread has the privilege to query the
|
||
// system boot order list.
|
||
//
|
||
|
||
HasPrivilege = SeSinglePrivilegeCheck(SeSystemEnvironmentPrivilege,
|
||
PreviousMode);
|
||
|
||
if (HasPrivilege == FALSE) {
|
||
return STATUS_PRIVILEGE_NOT_HELD;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate a nonpaged buffer to hold a copy of the input buffer.
|
||
// Copy the input buffer into the local buffer.
|
||
//
|
||
|
||
localBootEntry = ExAllocatePoolWithTag(NonPagedPool, LocalBufferLength, 'rvnE');
|
||
if (localBootEntry == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyMemory(localBootEntry, BootEntry, LocalBufferLength);
|
||
|
||
//
|
||
// If an exception occurs during the probe and capture of the input buffer,
|
||
// then always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
if (localBootEntry != NULL) {
|
||
ExFreePool(localBootEntry);
|
||
}
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Calculate the address of the byte above the end of the local buffer.
|
||
//
|
||
|
||
MaxBuffer = (PUCHAR)localBootEntry + LocalBufferLength;
|
||
|
||
//
|
||
// Verify the structure version.
|
||
//
|
||
|
||
if ((localBootEntry->Version == 0) ||
|
||
(localBootEntry->Version > BOOT_ENTRY_VERSION)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// If modifying an existing entry, verify that the input identifier is
|
||
// in range.
|
||
//
|
||
|
||
if (!CreateNewEntry && (localBootEntry->Id > MAXUSHORT)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Ignore boot entry attributes that can't be set.
|
||
//
|
||
|
||
localBootEntry->Attributes &= BOOT_ENTRY_ATTRIBUTE_VALID_BITS;
|
||
|
||
//
|
||
// Verify that offsets are aligned correctly.
|
||
//
|
||
|
||
if (((localBootEntry->FriendlyNameOffset & (sizeof(WCHAR) - 1)) != 0) ||
|
||
((localBootEntry->BootFilePathOffset & (sizeof(ULONG) - 1)) != 0)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Verify that OsOptions doesn't extend beyond the end of the buffer.
|
||
//
|
||
|
||
if ((localBootEntry->OsOptionsLength > LocalBufferLength) ||
|
||
((localBootEntry->OsOptions + localBootEntry->OsOptionsLength) >= MaxBuffer)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// If the OsOptions are for a Windows operating system, verify them.
|
||
//
|
||
|
||
windowsOsOptions = (PWINDOWS_OS_OPTIONS)localBootEntry->OsOptions;
|
||
|
||
if ((localBootEntry->OsOptionsLength >= FIELD_OFFSET(WINDOWS_OS_OPTIONS,Version)) &&
|
||
(strcmp((char *)windowsOsOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE) == 0)) {
|
||
|
||
if (localBootEntry->OsOptionsLength <= FIELD_OFFSET(WINDOWS_OS_OPTIONS,OsLoadOptions)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
NtStatus = ExpVerifyWindowsOsOptions(windowsOsOptions,
|
||
localBootEntry->OsOptionsLength);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
goto done;
|
||
}
|
||
|
||
isWindowsOs = TRUE;
|
||
windowsFilePath = ADD_OFFSET(windowsOsOptions, OsLoadPathOffset);
|
||
|
||
} else {
|
||
|
||
isWindowsOs = FALSE;
|
||
windowsFilePath = NULL; // keep the compiler quiet
|
||
}
|
||
|
||
//
|
||
// Verify that FriendlyName doesn't extend beyond the end of the buffer.
|
||
//
|
||
|
||
friendlyName = ADD_OFFSET(localBootEntry, FriendlyNameOffset);
|
||
if ((friendlyNameLength = ExpSafeWcslen(friendlyName, (PWSTR)MaxBuffer)) == 0xffffffff) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Convert friendlyNameLength from a character count into a byte count,
|
||
// including the null terminator.
|
||
//
|
||
|
||
friendlyNameLength = (friendlyNameLength + 1) * sizeof(WCHAR);
|
||
|
||
//
|
||
// Verify that BootFilePath is valid and doesn't extend beyond the end of
|
||
// the buffer.
|
||
//
|
||
|
||
bootFilePath = ADD_OFFSET(localBootEntry, BootFilePathOffset);
|
||
NtStatus = ExpVerifyFilePath(bootFilePath, MaxBuffer);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// Verify that OsOptions doesn't encroach into FriendlyName, and that
|
||
// FriendlyName doesn't encroach into BootFilePath.
|
||
//
|
||
|
||
if (((localBootEntry->OsOptions + localBootEntry->OsOptionsLength) > (PUCHAR)friendlyName) ||
|
||
(((PUCHAR)friendlyName + friendlyNameLength) > (PUCHAR)bootFilePath)) {
|
||
NtStatus = STATUS_INVALID_PARAMETER;
|
||
goto done;
|
||
}
|
||
|
||
//
|
||
// The format of the input buffer has been validated. Build the variable value
|
||
// that will be stored in NVRAM. Begin by determining the lengths of the file
|
||
// paths that will be stored. If the caller provided the paths in non-EFI
|
||
// format, they need to be translated.
|
||
//
|
||
|
||
if (bootFilePath->Type != FILE_PATH_TYPE_EFI) {
|
||
efiBootFilePathLength = 0;
|
||
NtStatus = ZwTranslateFilePath(bootFilePath,
|
||
FILE_PATH_TYPE_EFI,
|
||
NULL,
|
||
&efiBootFilePathLength);
|
||
if (NtStatus != STATUS_BUFFER_TOO_SMALL) {
|
||
goto done;
|
||
}
|
||
translatedBootFilePath = ExAllocatePoolWithTag(NonPagedPool,
|
||
efiBootFilePathLength,
|
||
'rvnE');
|
||
if (translatedBootFilePath == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto done;
|
||
}
|
||
RtlZeroMemory(translatedBootFilePath, efiBootFilePathLength);
|
||
length = efiBootFilePathLength;
|
||
NtStatus = ZwTranslateFilePath(bootFilePath,
|
||
FILE_PATH_TYPE_EFI,
|
||
translatedBootFilePath,
|
||
&length);
|
||
if (!NT_SUCCESS(NtStatus)) {
|
||
goto done;
|
||
}
|
||
if (length != efiBootFilePathLength) {
|
||
NtStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
} else {
|
||
efiBootFilePathLength = bootFilePath->Length;
|
||
translatedBootFilePath = bootFilePath;
|
||
}
|
||
|
||
efiBootFilePathLength = efiBootFilePathLength - FIELD_OFFSET(FILE_PATH, FilePath);
|
||
|
||
efiWindowsFilePathLength = 0;
|
||
if (isWindowsOs &&
|
||
(windowsFilePath->Type != FILE_PATH_TYPE_EFI)) {
|
||
NtStatus = ZwTranslateFilePath(windowsFilePath,
|
||
FILE_PATH_TYPE_EFI,
|
||
NULL,
|
||
&efiWindowsFilePathLength);
|
||
if (NtStatus != STATUS_BUFFER_TOO_SMALL) {
|
||
goto done;
|
||
}
|
||
osOptionsLength = localBootEntry->OsOptionsLength -
|
||
windowsFilePath->Length + efiWindowsFilePathLength;
|
||
} else {
|
||
osOptionsLength = localBootEntry->OsOptionsLength;
|
||
}
|
||
|
||
//
|
||
// Calculate the length required for the variable value.
|
||
//
|
||
|
||
requiredLength = FIELD_OFFSET(EFI_LOAD_OPTION, Description);
|
||
requiredLength += friendlyNameLength;
|
||
requiredLength += efiBootFilePathLength;
|
||
requiredLength += osOptionsLength;
|
||
|
||
//
|
||
// Allocate a buffer to hold the variable value.
|
||
//
|
||
|
||
efiLoadOption = ExAllocatePoolWithTag(NonPagedPool, requiredLength, 'rvnE');
|
||
if (efiLoadOption == NULL) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto done;
|
||
}
|
||
RtlZeroMemory(efiLoadOption, requiredLength);
|
||
|
||
//
|
||
// Build the variable value.
|
||
//
|
||
|
||
efiLoadOption->Attributes = 0;
|
||
if ((localBootEntry->Attributes & BOOT_ENTRY_ATTRIBUTE_ACTIVE) != 0) {
|
||
efiLoadOption->Attributes = LOAD_OPTION_ACTIVE;
|
||
}
|
||
|
||
efiLoadOption->FilePathLength = (USHORT)efiBootFilePathLength;
|
||
|
||
memcpy(efiLoadOption->Description, friendlyName, friendlyNameLength);
|
||
|
||
efiBootFilePath = (PUCHAR)((PUCHAR)efiLoadOption->Description + friendlyNameLength);
|
||
memcpy(efiBootFilePath, translatedBootFilePath->FilePath, efiBootFilePathLength);
|
||
|
||
efiOsOptions = efiBootFilePath + efiBootFilePathLength;
|
||
if (isWindowsOs &&
|
||
(windowsFilePath->Type != FILE_PATH_TYPE_EFI)) {
|
||
|
||
PFILE_PATH efiWindowsFilePath;
|
||
|
||
memcpy(efiOsOptions, windowsOsOptions, windowsOsOptions->OsLoadPathOffset);
|
||
((WINDOWS_OS_OPTIONS UNALIGNED *)efiOsOptions)->Length = osOptionsLength;
|
||
|
||
efiWindowsFilePath = (PFILE_PATH)(efiOsOptions + windowsOsOptions->OsLoadPathOffset);
|
||
length = efiWindowsFilePathLength;
|
||
NtStatus = ZwTranslateFilePath(windowsFilePath,
|
||
FILE_PATH_TYPE_EFI,
|
||
efiWindowsFilePath,
|
||
&efiWindowsFilePathLength);
|
||
if (NtStatus != STATUS_SUCCESS) {
|
||
goto done;
|
||
}
|
||
if (length != efiWindowsFilePathLength) {
|
||
NtStatus = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
} else {
|
||
|
||
memcpy(efiOsOptions, localBootEntry->OsOptions, osOptionsLength);
|
||
}
|
||
|
||
//
|
||
// If CreateNewEntry is true, then find an unused identifier to assign to
|
||
// this boot entry. If CreateNewEntry is false, then verify that the
|
||
// provided identifier exists.
|
||
//
|
||
|
||
KeEnterCriticalRegion();
|
||
ExAcquireFastMutexUnsafe(&ExpEnvironmentLock);
|
||
|
||
if (CreateNewEntry) {
|
||
|
||
for ( id = 0; id <= MAXUSHORT; id++ ) {
|
||
swprintf( idString, L"Boot%04x", id);
|
||
length = 0;
|
||
NtStatus = HalGetEnvironmentVariableEx(idString,
|
||
&EfiBootVariablesGuid,
|
||
NULL,
|
||
&length,
|
||
NULL);
|
||
if (NtStatus == STATUS_VARIABLE_NOT_FOUND) {
|
||
break;
|
||
}
|
||
if ((NtStatus != STATUS_SUCCESS) && (NtStatus != STATUS_BUFFER_TOO_SMALL)) {
|
||
goto done_unlock;
|
||
}
|
||
}
|
||
|
||
if (id > MAXUSHORT) {
|
||
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto done_unlock;
|
||
}
|
||
|
||
} else {
|
||
|
||
id = localBootEntry->Id;
|
||
swprintf( idString, L"Boot%04x", localBootEntry->Id);
|
||
length = 0;
|
||
NtStatus = HalGetEnvironmentVariableEx(idString,
|
||
&EfiBootVariablesGuid,
|
||
NULL,
|
||
&length,
|
||
NULL);
|
||
if ((NtStatus != STATUS_SUCCESS) && (NtStatus != STATUS_BUFFER_TOO_SMALL)) {
|
||
goto done_unlock;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set or update the boot entry environment variable.
|
||
//
|
||
|
||
NtStatus = HalSetEnvironmentVariableEx(idString,
|
||
&EfiBootVariablesGuid,
|
||
efiLoadOption,
|
||
requiredLength,
|
||
VARIABLE_ATTRIBUTE_NON_VOLATILE);
|
||
|
||
done_unlock:
|
||
|
||
ExReleaseFastMutexUnsafe(&ExpEnvironmentLock);
|
||
KeLeaveCriticalRegion();
|
||
|
||
done:
|
||
|
||
if (efiLoadOption != NULL) {
|
||
ExFreePool(efiLoadOption);
|
||
}
|
||
|
||
if ((translatedBootFilePath != NULL) && (translatedBootFilePath != bootFilePath)) {
|
||
ExFreePool(translatedBootFilePath);
|
||
}
|
||
|
||
ExFreePool(localBootEntry);
|
||
|
||
//
|
||
// Establish an exception handler and attempt to write the return
|
||
// identifier. If the write attempt fails, then return the exception
|
||
// code as the service status.
|
||
//
|
||
|
||
try {
|
||
|
||
//
|
||
// Write the return identifier.
|
||
//
|
||
|
||
if (CreateNewEntry && ARGUMENT_PRESENT(Id) && NT_SUCCESS(NtStatus)) {
|
||
*Id = id;
|
||
}
|
||
|
||
return NtStatus;
|
||
|
||
//
|
||
// If an exception occurs during the write of the return data, then
|
||
// always handle the exception and return the exception code as the
|
||
// status value.
|
||
//
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
#endif // else !defined(EFI_NVRAM_ENABLED)
|
||
|
||
} // ExpSetBootEntry
|
||
|
||
//
|
||
// The remainder of this module is routines that are only compiled when
|
||
// EFI_NVRAM_ENABLED is defined.
|
||
//
|
||
|
||
#if defined(EFI_NVRAM_ENABLED)
|
||
|
||
ULONG
|
||
ExpSafeWcslen (
|
||
IN PWSTR String,
|
||
IN PWSTR Max
|
||
)
|
||
{
|
||
PWSTR p = String;
|
||
|
||
while ((p < Max) && (*p != 0)) {
|
||
p++;
|
||
}
|
||
|
||
if (p < Max) {
|
||
return (ULONG)(p - String);
|
||
}
|
||
|
||
return 0xffffffff;
|
||
|
||
} // ExpSafeWcslen
|
||
|
||
NTSTATUS
|
||
ExpTranslateArcPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
)
|
||
{
|
||
#if 0
|
||
UNREFERENCED_PARAMETER(InputPath);
|
||
UNREFERENCED_PARAMETER(OutputType);
|
||
UNREFERENCED_PARAMETER(OutputPath);
|
||
UNREFERENCED_PARAMETER(OutputPathLength);
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
#endif
|
||
|
||
PWSTR deviceName, pathName;
|
||
ULONG deviceNameCount;
|
||
BOOLEAN signatureFormat;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Possible Arc Path formats
|
||
// signature(<guid/signature>-<part#>-<start>-<size>)[\filePart]
|
||
// signature(<guid>)[\filePart]
|
||
// multi(0)disk(0)fdisk(0)[\filePart]
|
||
// multi(0)disk(0)rdisk(0)[\filePart]
|
||
// multi(0)disk(0)rdisk(0)partition(0)[\filePart]
|
||
//
|
||
|
||
//
|
||
// Determine if ArcName has signature() format
|
||
// Parse out DeviceName & FilePart
|
||
//
|
||
status = ExpParseArcPathName (
|
||
(PWSTR)(InputPath->FilePath),
|
||
&deviceName,
|
||
&pathName,
|
||
&deviceNameCount,
|
||
&signatureFormat
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// not signature() format
|
||
//
|
||
if( signatureFormat == FALSE ) {
|
||
if( InputPath->Type != FILE_PATH_TYPE_ARC ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
status = ExpConvertArcName(
|
||
OutputType,
|
||
OutputPath,
|
||
OutputPathLength,
|
||
deviceName,
|
||
pathName,
|
||
deviceNameCount
|
||
);
|
||
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// This arc signature() format should be FILE_PATH_TYPE_ARC_SIGNATURE
|
||
//
|
||
if( InputPath->Type != FILE_PATH_TYPE_ARC_SIGNATURE ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
status = ExpConvertSignatureName(
|
||
OutputType,
|
||
OutputPath,
|
||
OutputPathLength,
|
||
deviceName,
|
||
pathName,
|
||
deviceNameCount
|
||
);
|
||
|
||
return( status );
|
||
} // ExpTranslateArcPath
|
||
|
||
NTSTATUS
|
||
ExpTranslateEfiPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
HARDDRIVE_DEVICE_PATH *dpHarddrive = NULL;
|
||
ULONG requiredLength;
|
||
UNICODE_STRING guidString;
|
||
UNICODE_STRING deviceNameString;
|
||
PWSTR linkName, pPathName;
|
||
BOOLEAN GPTpartition;
|
||
ULONG partitionNumber, diskNumber;
|
||
ULONGLONG partitionStart, partitionSize;
|
||
|
||
|
||
//
|
||
// Find the MEDIA/HARDDRIVE and MEDIA/FILEPATH elements in the device
|
||
// path. Note that although EFI allows multiple device paths to appear
|
||
// in a single device path (as in the PATH variable), we only look at
|
||
// the first one.
|
||
//
|
||
|
||
status = ExpParseEfiPath(
|
||
(EFI_DEVICE_PATH *)InputPath->FilePath,
|
||
&dpHarddrive,
|
||
&pPathName,
|
||
&GPTpartition
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// If the target type is ARC_SIGNATURE, then we have all of the
|
||
// information we need. Otherwise, we need to find the NT device
|
||
// with the given signature.
|
||
//
|
||
|
||
if ( OutputType == FILE_PATH_TYPE_ARC_SIGNATURE ) {
|
||
|
||
partitionNumber = dpHarddrive->PartitionNumber;
|
||
partitionStart = dpHarddrive->PartitionStart;
|
||
partitionSize = dpHarddrive->PartitionSize;
|
||
status = ExpCreateOutputSIGNATURE(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
(PDISK_SIGNATURE_NEW)(dpHarddrive->Signature),
|
||
&(partitionNumber),
|
||
&(partitionStart),
|
||
&(partitionSize),
|
||
pPathName,
|
||
GPTpartition
|
||
);
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( status );
|
||
|
||
}
|
||
|
||
//
|
||
// OutputType is ARC or NT. Find the NT device for this device path.
|
||
// For a GPT partition, this is done by translating the symbolic name
|
||
// \??\Volume{<guid>} which will link to \Device\HarddiskVolume<n>.
|
||
//
|
||
status = STATUS_OBJECT_NAME_NOT_FOUND;
|
||
|
||
//
|
||
// Quick path for GPT disk
|
||
// Translate the symbolic link \??\Volume{<guid>}.
|
||
//
|
||
// First, get the GUID in "pretty" format. Then allocate a buffer to hold
|
||
// the full name string and create that string. Then translate the
|
||
// symbolic name.
|
||
//
|
||
// NB: Because the mount manager doesn't create a symbolic link like this
|
||
// for the EFI system partition, this routine cannot be used to
|
||
// translate an EFI device path for the system partition to an NT path.
|
||
//
|
||
if( GPTpartition == TRUE ) {
|
||
|
||
status = RtlStringFromGUID( (LPGUID)dpHarddrive->Signature, &guidString );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return status;
|
||
}
|
||
|
||
#define LINK_NAME_PREFIX L"\\??\\Volume"
|
||
|
||
requiredLength = ((ULONG)wcslen( LINK_NAME_PREFIX ) + 1) * sizeof(WCHAR);
|
||
requiredLength += guidString.Length;
|
||
linkName = ExAllocatePoolWithTag( NonPagedPool, requiredLength, 'rvnE' );
|
||
if ( linkName == NULL ) {
|
||
ExFreePool( guidString.Buffer );
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
wcscpy( linkName, LINK_NAME_PREFIX );
|
||
wcscat( linkName, guidString.Buffer );
|
||
ExFreePool( guidString.Buffer );
|
||
|
||
status = ExpTranslateSymbolicLink(
|
||
linkName,
|
||
&deviceNameString
|
||
);
|
||
ExFreePool( linkName );
|
||
}
|
||
|
||
//
|
||
// check if the quick path was not taken or no object was found
|
||
//
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
//
|
||
// long path, opens all disks in search of the signature
|
||
//
|
||
partitionNumber = dpHarddrive->PartitionNumber;
|
||
status = ExpFindDiskSignature(
|
||
(PDISK_SIGNATURE_NEW)(dpHarddrive->Signature),
|
||
&partitionNumber,
|
||
&diskNumber,
|
||
&partitionStart,
|
||
&partitionSize,
|
||
GPTpartition
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// The user has provided the partition number, start address,
|
||
// and size; so verify the input with the found results.
|
||
//
|
||
if( (dpHarddrive->PartitionNumber != partitionNumber) ||
|
||
(dpHarddrive->PartitionStart != partitionStart) ||
|
||
(dpHarddrive->PartitionSize != partitionSize) ) {
|
||
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
//
|
||
// create the NT disk Symbolic link name
|
||
// \Device\Harddisk[diskNumber]\Partition[PartitionNumber]
|
||
//
|
||
#define NT_DISK_NAME_FORMAT L"\\Device\\Harddisk%lu\\Partition%lu"
|
||
#define NT_DISK_NAME_COUNT 47 // 7 + 9 + (10) + 10 + (10) + 1
|
||
|
||
linkName = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
( NT_DISK_NAME_COUNT * sizeof( WCHAR ) ),
|
||
'rvnE'
|
||
);
|
||
|
||
if( linkName == NULL ) {
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
_snwprintf(
|
||
linkName,
|
||
NT_DISK_NAME_COUNT,
|
||
NT_DISK_NAME_FORMAT,
|
||
diskNumber,
|
||
partitionNumber
|
||
);
|
||
|
||
status = ExpTranslateSymbolicLink(
|
||
linkName,
|
||
&deviceNameString
|
||
);
|
||
ExFreePool( linkName );
|
||
if( !NT_SUCCESS(status) ) {
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( status );
|
||
}
|
||
}
|
||
|
||
//
|
||
// We now have the NT name of the device. If the target type is NT, then
|
||
// we have all of the information we need.
|
||
//
|
||
if ( OutputType == FILE_PATH_TYPE_NT ) {
|
||
|
||
status = ExpCreateOutputNT(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&deviceNameString,
|
||
pPathName
|
||
);
|
||
ExFreePool( deviceNameString.Buffer );
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// The output type is ARC.
|
||
//
|
||
status = ExpCreateOutputARC(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&deviceNameString,
|
||
pPathName
|
||
);
|
||
ExFreePool( deviceNameString.Buffer );
|
||
if( pPathName != NULL ) {
|
||
ExFreePool( pPathName );
|
||
}
|
||
|
||
ExFreePool(dpHarddrive);
|
||
|
||
return( status );
|
||
|
||
} // ExpTranslateEfiPath
|
||
|
||
NTSTATUS
|
||
ExpTranslateNtPath (
|
||
IN PFILE_PATH InputPath,
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength
|
||
)
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING string, deviceNameString;
|
||
OBJECT_ATTRIBUTES obja;
|
||
IO_STATUS_BLOCK iosb;
|
||
HANDLE handle;
|
||
PARTITION_INFORMATION_EX partitionInfo;
|
||
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
||
ULONG driveLayoutLength;
|
||
PWSTR deviceName, pathName;
|
||
ULONG pathNameLength;
|
||
ULONG signatureMBR = 0;
|
||
PDISK_SIGNATURE_NEW pDiskSignature;
|
||
BOOLEAN TranslatedSymLink = TRUE;
|
||
BOOLEAN GPTpartition;
|
||
|
||
|
||
deviceName = (PWSTR)InputPath->FilePath;
|
||
RtlInitUnicodeString( &string, deviceName );
|
||
pathName = (PWSTR)((PUCHAR)deviceName + string.Length + sizeof(WCHAR));
|
||
pathNameLength = (ULONG)wcslen(pathName);
|
||
if (pathNameLength == 0) {
|
||
pathName = NULL;
|
||
}
|
||
|
||
//
|
||
// For output type Arc,
|
||
// attempt drill down NT name
|
||
// if NT object exists
|
||
// match with symlink in \ArcName
|
||
//
|
||
if (OutputType == FILE_PATH_TYPE_ARC) {
|
||
status = ExpTranslateSymbolicLink(
|
||
deviceName,
|
||
&deviceNameString
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// If non-symlink NT name exists as an object in the NT namespace,
|
||
// then the return code will be STATUS_OBJECT_TYPE_MISMATCH
|
||
// else the return code will be STATUS_OBJECT_NAME_NOT_FOUND
|
||
//
|
||
if (status != STATUS_OBJECT_TYPE_MISMATCH) {
|
||
return( status );
|
||
}
|
||
deviceNameString.Buffer = string.Buffer;
|
||
deviceNameString.Length = string.Length;
|
||
deviceNameString.MaximumLength = string.MaximumLength;
|
||
TranslatedSymLink = FALSE;
|
||
}
|
||
status = ExpCreateOutputARC(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&deviceNameString,
|
||
pathName
|
||
);
|
||
if (TranslatedSymLink == TRUE) {
|
||
ExFreePool( deviceNameString.Buffer );
|
||
}
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// Open the target partition and get its partition information.
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&obja,
|
||
&string,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenFile(
|
||
&handle,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&obja,
|
||
&iosb,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_NON_DIRECTORY_FILE
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
status = ZwDeviceIoControlFile(
|
||
handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&iosb,
|
||
IOCTL_DISK_GET_PARTITION_INFO_EX,
|
||
NULL,
|
||
0,
|
||
&partitionInfo,
|
||
sizeof(partitionInfo)
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
ZwClose(handle);
|
||
return status;
|
||
}
|
||
|
||
if ((partitionInfo.PartitionStyle != PARTITION_STYLE_MBR) &&
|
||
(partitionInfo.PartitionStyle != PARTITION_STYLE_GPT)) {
|
||
ZwClose(handle);
|
||
return STATUS_UNRECOGNIZED_MEDIA;
|
||
}
|
||
|
||
if (partitionInfo.PartitionStyle == PARTITION_STYLE_MBR) {
|
||
|
||
driveLayoutLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
|
||
(sizeof(PARTITION_INFORMATION_EX) * 16);
|
||
|
||
while (TRUE) {
|
||
|
||
driveLayoutInfo = ExAllocatePoolWithTag(NonPagedPool, driveLayoutLength, 'rvnE');
|
||
if (driveLayoutInfo == NULL ) {
|
||
ZwClose(handle);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
status = ZwDeviceIoControlFile(
|
||
handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&iosb,
|
||
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
||
NULL,
|
||
0,
|
||
driveLayoutInfo,
|
||
driveLayoutLength
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
ExFreePool(driveLayoutInfo);
|
||
if (status == STATUS_BUFFER_TOO_SMALL) {
|
||
driveLayoutLength *= 2;
|
||
continue;
|
||
}
|
||
ZwClose(handle);
|
||
return status;
|
||
}
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
signatureMBR = driveLayoutInfo->Mbr.Signature;
|
||
ExFreePool(driveLayoutInfo);
|
||
}
|
||
}
|
||
|
||
ZwClose(handle);
|
||
|
||
if (partitionInfo.PartitionStyle == PARTITION_STYLE_GPT) {
|
||
pDiskSignature = (PDISK_SIGNATURE_NEW)(&(partitionInfo.Gpt.PartitionId));
|
||
GPTpartition = TRUE;
|
||
} else {
|
||
pDiskSignature = (PDISK_SIGNATURE_NEW)(&signatureMBR);
|
||
GPTpartition = FALSE;
|
||
}
|
||
|
||
if (OutputType == FILE_PATH_TYPE_EFI) {
|
||
|
||
status = ExpCreateOutputEFI(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
pDiskSignature,
|
||
&(partitionInfo.PartitionNumber),
|
||
(PULONGLONG)(&(partitionInfo.StartingOffset.QuadPart)),
|
||
(PULONGLONG)(&(partitionInfo.PartitionLength.QuadPart)),
|
||
pathName,
|
||
GPTpartition
|
||
);
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// OutputType is ARC_SIGNATURE
|
||
//
|
||
status = ExpCreateOutputSIGNATURE(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
pDiskSignature,
|
||
&(partitionInfo.PartitionNumber),
|
||
(PULONGLONG)(&(partitionInfo.StartingOffset.QuadPart)),
|
||
(PULONGLONG)(&(partitionInfo.PartitionLength.QuadPart)),
|
||
pathName,
|
||
GPTpartition
|
||
);
|
||
return( status );
|
||
|
||
} // ExpTranslateNtPath
|
||
|
||
|
||
LOGICAL
|
||
ExpTranslateBootEntryNameToId (
|
||
IN PWSTR Name,
|
||
OUT PULONG Id
|
||
)
|
||
{
|
||
ULONG number;
|
||
ULONG i;
|
||
WCHAR c;
|
||
|
||
if ((towlower(Name[0]) != 'b') ||
|
||
(towlower(Name[1]) != 'o') ||
|
||
(towlower(Name[2]) != 'o') ||
|
||
(towlower(Name[3]) != 't') ) {
|
||
return FALSE;
|
||
}
|
||
|
||
number = 0;
|
||
for (i = 4; i < 8; i++) {
|
||
c = towlower(Name[i]);
|
||
if ((c >= L'0') && (c <= L'9')) {
|
||
number = (number * 16) + (c - L'0');
|
||
} else if ((c >= L'a') && (c <= L'f')) {
|
||
number = (number * 16) + (c - L'a' + 10);
|
||
} else {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (Name[8] != 0) {
|
||
return FALSE;
|
||
}
|
||
|
||
*Id = number;
|
||
return TRUE;
|
||
|
||
} // ExpTranslateBootEntryNameToId
|
||
|
||
NTSTATUS
|
||
ExpTranslateSymbolicLink (
|
||
IN PWSTR LinkName,
|
||
OUT PUNICODE_STRING ResultName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine translates the input symbolic link name by drilling down
|
||
through symbolic links until it finds an object that is not a link.
|
||
|
||
Arguments:
|
||
|
||
LinkName - Supplies the name of the link at which to start translating.
|
||
|
||
ResultName - Supplies the address of a UNICODE_STRING descriptor that
|
||
will receive the result name. The storage for the result name is
|
||
allocated from nonpaged pool using ExAllocatePool.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the input name was a symbolic link and
|
||
all translations completely successfully.
|
||
Failure codes will be returned if the input name was not a link, if
|
||
translations failed, or if allocation of the output buffer failed.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING linkString;
|
||
UNICODE_STRING resultString;
|
||
PWSTR resultBuffer;
|
||
ULONG resultBufferLength;
|
||
ULONG requiredLength;
|
||
OBJECT_ATTRIBUTES objectAttributes;
|
||
HANDLE handle;
|
||
|
||
resultBuffer = NULL;
|
||
resultBufferLength = sizeof(WCHAR);
|
||
|
||
//
|
||
// Open the input link.
|
||
//
|
||
|
||
RtlInitUnicodeString( &linkString, LinkName );
|
||
|
||
InitializeObjectAttributes(
|
||
&objectAttributes,
|
||
&linkString,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenSymbolicLinkObject(
|
||
&handle,
|
||
(ACCESS_MASK)SYMBOLIC_LINK_QUERY,
|
||
&objectAttributes
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
while ( TRUE ) {
|
||
|
||
while ( TRUE ) {
|
||
|
||
//
|
||
// Get the translation for this link, allocating more
|
||
// space as needed.
|
||
//
|
||
|
||
resultString.Length = 0;
|
||
resultString.MaximumLength = (USHORT)(resultBufferLength - sizeof(WCHAR));
|
||
resultString.Buffer = resultBuffer;
|
||
|
||
status = ZwQuerySymbolicLinkObject(
|
||
handle,
|
||
&resultString,
|
||
&requiredLength
|
||
);
|
||
|
||
if ( status != STATUS_BUFFER_TOO_SMALL ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// The buffer was too small. Reallocate it, allowing room for a
|
||
// null terminator, which might not be present in the translation,
|
||
// and try again.
|
||
//
|
||
|
||
if ( resultBuffer != NULL ) {
|
||
ExFreePool( resultBuffer );
|
||
}
|
||
resultBufferLength = requiredLength + sizeof(WCHAR);
|
||
resultBuffer = ExAllocatePoolWithTag( NonPagedPool, resultBufferLength, 'rvnE' );
|
||
if ( resultBuffer == NULL ) {
|
||
ZwClose( handle );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Translation done. Close the link. If translation failed, return
|
||
// the failure status.
|
||
//
|
||
|
||
ZwClose( handle );
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
if ( resultBuffer != NULL) {
|
||
ExFreePool( resultBuffer );
|
||
}
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Terminate the result string, in case it wasn't already terminated.
|
||
//
|
||
|
||
resultBuffer[resultString.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
||
resultString.MaximumLength = (USHORT)(resultBufferLength);
|
||
|
||
//
|
||
// See if the result name is also a symbolic name. Try to open it
|
||
// as a link. If this fails, then break out of the loop and return
|
||
// this name as the result.
|
||
//
|
||
|
||
RtlInitUnicodeString( &linkString, resultBuffer );
|
||
|
||
InitializeObjectAttributes(
|
||
&objectAttributes,
|
||
&linkString,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenSymbolicLinkObject(
|
||
&handle,
|
||
(ACCESS_MASK)SYMBOLIC_LINK_QUERY,
|
||
&objectAttributes
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// This name is also a symbolic link. Loop back and translate it.
|
||
//
|
||
|
||
}
|
||
|
||
//
|
||
// Set up the return string to point to the final result.
|
||
//
|
||
|
||
*ResultName = resultString;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpTranslateSymbolicLink
|
||
|
||
LOGICAL
|
||
ExpIsDevicePathForRemovableMedia (
|
||
EFI_DEVICE_PATH *DevicePath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines whether an EFI device path represents a non-file
|
||
specific pointer to a removable media device. It make this determination
|
||
based on finding a HARDWARE/VENDOR device path element, and NOT finding
|
||
MEDIA/HARDDRIVE and MEDIA/FILEPATH elements. When the EFI boot manager
|
||
boot such a device path, it looks in a default location for the file to
|
||
be loaded (\EFI\BOOT\BOOT<arch>.EFI).
|
||
|
||
We want to identify these removable media device paths because we do not
|
||
want to put our NT boot entries ahead of removable media entries in the
|
||
boot order, if those removable media entries are at the front of the list.
|
||
This allows an x86-like boot order to be set up: floppy first, then CD,
|
||
then NT boot entries.
|
||
|
||
Arguments:
|
||
|
||
DevicePath - Supplies the device path to be checked.
|
||
|
||
Return Value:
|
||
|
||
TRUE is returned if the device path has a HARDWARE/VENDOR element, AND
|
||
that element has the UNKNOWN_DEVICE_GUID, AND the device path does
|
||
NOT have a MEDIA/HARDDRIVE element, AND the device path does NOT
|
||
have a MEDIA/FILEPATH element.
|
||
|
||
--*/
|
||
|
||
{
|
||
EFI_DEVICE_PATH *dp = DevicePath;
|
||
VENDOR_DEVICE_PATH UNALIGNED *vdp;
|
||
VENDOR_DEVICE_PATH UNALIGNED *vendorDp = NULL;
|
||
HARDDRIVE_DEVICE_PATH UNALIGNED *harddriveDp = NULL;
|
||
FILEPATH_DEVICE_PATH UNALIGNED *filepathDp = NULL;
|
||
|
||
//
|
||
// Walk the device path, looking for elements that we care about.
|
||
//
|
||
|
||
while (TRUE) {
|
||
|
||
if (IsDevicePathEndType(dp)) {
|
||
break;
|
||
}
|
||
|
||
if (DevicePathType(dp) == HARDWARE_DEVICE_PATH) {
|
||
if (DevicePathSubType(dp) == HW_VENDOR_DP) {
|
||
|
||
//
|
||
// Found a HARDWARE/VENDOR element. If it has the
|
||
// UNKNOWN_DEVICE_GUID, remember that we found it.
|
||
//
|
||
|
||
vdp = (VENDOR_DEVICE_PATH UNALIGNED *)dp;
|
||
if ( memcmp( &vdp->Guid, &ExpUnknownDeviceGuid, 16 ) == 0 ) {
|
||
vendorDp = vdp;
|
||
}
|
||
}
|
||
|
||
} else if (DevicePathType(dp) == MEDIA_DEVICE_PATH) {
|
||
|
||
if (DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP) {
|
||
|
||
//
|
||
// Found a MEDIA/HARDDRIVE element. Remember it.
|
||
//
|
||
|
||
harddriveDp = (HARDDRIVE_DEVICE_PATH *)dp;
|
||
|
||
} else if (DevicePathSubType(dp) == MEDIA_FILEPATH_DP) {
|
||
|
||
//
|
||
// Found a MEDIA/FILEPATH element. Remember it.
|
||
//
|
||
|
||
filepathDp = (FILEPATH_DEVICE_PATH *)dp;
|
||
}
|
||
}
|
||
|
||
dp = NextDevicePathNode(dp);
|
||
}
|
||
|
||
//
|
||
// If we didn't find a HARDWARE/VENDOR element, or if we did find either
|
||
// a MEDIA/HARDDRIVE element or a MEDIA/FILEPATH element, then this is
|
||
// not a removable media device path.
|
||
//
|
||
|
||
if ((vendorDp == NULL) || (harddriveDp != NULL) || (filepathDp != NULL)) {
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
} // ExpIsDevicePathForRemovableMedia
|
||
|
||
NTSTATUS
|
||
ExpVerifyFilePath (
|
||
PFILE_PATH FilePath,
|
||
PUCHAR Max
|
||
)
|
||
{
|
||
EFI_DEVICE_PATH *dp;
|
||
PUCHAR dpMax;
|
||
ULONG length;
|
||
PWSTR p;
|
||
|
||
if (((PUCHAR)FilePath > Max) ||
|
||
(((PUCHAR)FilePath + FIELD_OFFSET(FILE_PATH, FilePath)) > Max) ||
|
||
(FilePath->Length < FIELD_OFFSET(FILE_PATH, FilePath)) ||
|
||
(((PUCHAR)FilePath + FilePath->Length) < (PUCHAR)FilePath) ||
|
||
(((PUCHAR)FilePath + FilePath->Length) > Max) ||
|
||
(FilePath->Version == 0) ||
|
||
(FilePath->Version > FILE_PATH_VERSION) ||
|
||
(FilePath->Type < FILE_PATH_TYPE_MIN) ||
|
||
(FilePath->Type > FILE_PATH_TYPE_MAX)) {
|
||
//DbgPrint( "ExpVerifyFilePath: file path invalid\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
switch (FilePath->Type) {
|
||
|
||
case FILE_PATH_TYPE_ARC:
|
||
case FILE_PATH_TYPE_ARC_SIGNATURE:
|
||
if (ExpSafeWcslen((PWCHAR)FilePath->FilePath, (PWCHAR)Max) == 0xffffffff) {
|
||
//DbgPrint( "ExpVerifyFilePath: ARC string overruns buffer end\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
break;
|
||
|
||
case FILE_PATH_TYPE_NT:
|
||
p = (PWSTR)FilePath->FilePath;
|
||
length = ExpSafeWcslen(p, (PWCHAR)Max);
|
||
if (length != 0xffffffff) {
|
||
p = p + length + 1;
|
||
length = ExpSafeWcslen(p, (PWCHAR)Max);
|
||
}
|
||
if (length == 0xffffffff) {
|
||
//DbgPrint( "ExpVerifyFilePath: NT string overruns buffer end\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
break;
|
||
|
||
case FILE_PATH_TYPE_EFI:
|
||
dp = (EFI_DEVICE_PATH *)FilePath->FilePath;
|
||
while (TRUE) {
|
||
if (((PUCHAR)dp + sizeof(EFI_DEVICE_PATH)) > Max) {
|
||
//DbgPrint( "ExpVerifyFilePath: EFI device path overruns buffer end\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
length = DevicePathNodeLength(dp);
|
||
if (((PUCHAR)dp + length) > Max) {
|
||
//DbgPrint( "ExpVerifyFilePath: EFI device path overruns buffer end\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
dpMax = (PUCHAR)dp + length;
|
||
if (IsDevicePathEndType(dp)) {
|
||
break;
|
||
}
|
||
if ((DevicePathType(dp) == MEDIA_DEVICE_PATH) &&
|
||
(DevicePathSubType(dp) == MEDIA_FILEPATH_DP)) {
|
||
FILEPATH_DEVICE_PATH *fp = (FILEPATH_DEVICE_PATH *)dp;
|
||
if (ExpSafeWcslen(fp->PathName, (PWCHAR)dpMax) == 0xffffffff) {
|
||
//DbgPrint( "ExpVerifyFilePath: EFI filepath string overruns buffer end\n" );
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
}
|
||
dp = NextDevicePathNode(dp);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpVerifyFilePath
|
||
|
||
NTSTATUS
|
||
ExpVerifyWindowsOsOptions (
|
||
PWINDOWS_OS_OPTIONS WindowsOsOptions,
|
||
ULONG Length
|
||
)
|
||
{
|
||
PUCHAR Max = (PUCHAR)WindowsOsOptions + Length;
|
||
ULONG loadOptionsLength = ExpSafeWcslen(WindowsOsOptions->OsLoadOptions, (PWSTR)Max);
|
||
PFILE_PATH windowsFilePath;
|
||
|
||
if ((WindowsOsOptions->Length < FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions)) ||
|
||
(WindowsOsOptions->Length > Length) ||
|
||
(WindowsOsOptions->Version == 0) ||
|
||
(WindowsOsOptions->Version > WINDOWS_OS_OPTIONS_VERSION) ||
|
||
((WindowsOsOptions->OsLoadPathOffset & (sizeof(ULONG) - 1)) != 0) ||
|
||
(WindowsOsOptions->OsLoadPathOffset >= Length) ||
|
||
(loadOptionsLength == 0xffffffff) ||
|
||
((PUCHAR)(WindowsOsOptions->OsLoadOptions + loadOptionsLength + 1) >
|
||
(PUCHAR)ADD_OFFSET(WindowsOsOptions, OsLoadPathOffset))) {
|
||
return STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
windowsFilePath = ADD_OFFSET(WindowsOsOptions, OsLoadPathOffset);
|
||
return ExpVerifyFilePath(windowsFilePath, Max);
|
||
|
||
} // ExpVerifyWindowsOsOptions
|
||
|
||
|
||
NTSTATUS
|
||
ExpParseArcPathName (
|
||
IN PWSTR ArcName,
|
||
OUT PWSTR *ppDeviceName,
|
||
OUT PWSTR *ppPathName,
|
||
OUT PULONG pDeviceNameCount,
|
||
OUT PBOOLEAN pSignatureFormat
|
||
)
|
||
{
|
||
#define SIGNATURE_PREFIX L"signature("
|
||
#define SIGNATURE_PREFIX_COUNT 10
|
||
#define BUFFER_COUNT (SIGNATURE_PREFIX_COUNT + 1)
|
||
|
||
PWSTR CurrentName, pathName = NULL;
|
||
WCHAR signaturePrefix[ BUFFER_COUNT ];
|
||
ULONG i;
|
||
BOOLEAN SigFormat = FALSE, PrefixFound = TRUE;
|
||
|
||
if( ArcName == NULL ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
wcscpy( signaturePrefix, SIGNATURE_PREFIX );
|
||
|
||
//
|
||
// check if the ArcName has a signature() format
|
||
//
|
||
for( i = 0; i < SIGNATURE_PREFIX_COUNT; i++ ) {
|
||
if( towlower(ArcName[ i ]) != signaturePrefix[ i ] ) {
|
||
PrefixFound = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
|
||
CurrentName = ArcName;
|
||
if( PrefixFound == TRUE ) {
|
||
CurrentName += SIGNATURE_PREFIX_COUNT;
|
||
}
|
||
|
||
i = 0;
|
||
while( CurrentName[ i ] != UNICODE_NULL ) {
|
||
//
|
||
// Check if FilePathName has been reached
|
||
//
|
||
if( CurrentName[ i ] == '\\' ) {
|
||
pathName = CurrentName;
|
||
pathName += i;
|
||
break;
|
||
}
|
||
|
||
if( (PrefixFound == TRUE) && (CurrentName[ i ] == ')') ) {
|
||
SigFormat = TRUE;
|
||
PrefixFound = FALSE; // set to FALSE, to stop checking
|
||
|
||
//
|
||
// the FilePathName or UNICODE_NULL must follow
|
||
//
|
||
if( (CurrentName[ i + 1 ] != '\\') &&
|
||
(CurrentName[ i + 1 ] != UNICODE_NULL) ) {
|
||
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
i++;
|
||
}
|
||
|
||
//
|
||
// if PrefixFound is still set
|
||
// the corresponding ')' was not found
|
||
// if i == 0
|
||
// DeviceName does not exist
|
||
//
|
||
if( (PrefixFound == TRUE) || (i == 0) ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
*ppDeviceName = CurrentName;
|
||
*ppPathName = pathName;
|
||
*pDeviceNameCount = i;
|
||
*pSignatureFormat = SigFormat;
|
||
|
||
return( STATUS_SUCCESS );
|
||
|
||
} // ExpParseArcPathName
|
||
|
||
|
||
NTSTATUS
|
||
ExpParseSignatureName (
|
||
IN PWSTR deviceName,
|
||
IN ULONG deviceNameCount,
|
||
OUT PDISK_SIGNATURE_NEW diskSignature,
|
||
OUT PULONG partitionNumber,
|
||
OUT PULONGLONG partitionStart,
|
||
OUT PULONGLONG partitionSize,
|
||
OUT PBOOLEAN GPTpartition,
|
||
OUT PBOOLEAN longSignature
|
||
)
|
||
{
|
||
UNICODE_STRING bufferString;
|
||
ULONG i, prevI, chCount;
|
||
PWSTR numberString, currentName;
|
||
BOOLEAN foundGUID = FALSE, prettyGUID = FALSE;
|
||
BOOLEAN longSigFound = FALSE;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Possible formats
|
||
//
|
||
if( deviceName[ 0 ] == '{' ) {
|
||
foundGUID = TRUE;
|
||
}
|
||
|
||
//
|
||
// parse the GUID or signature
|
||
//
|
||
i = 0;
|
||
while( i < deviceNameCount ) {
|
||
if( deviceName[ i ] == ')' ) {
|
||
break;
|
||
}
|
||
if( foundGUID == TRUE ) {
|
||
if( deviceName[ i ] == '}' ) {
|
||
prettyGUID = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
if( deviceName[ i ] == '-' ) {
|
||
break;
|
||
}
|
||
}
|
||
i++;
|
||
}
|
||
|
||
//
|
||
// Verify that pretty GUID format has a '}'
|
||
// {33221100-5544-7766-8899-aabbccddeeff}
|
||
//
|
||
if( (foundGUID == TRUE) && (prettyGUID == FALSE) ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
#define MBR_SIGNATURE_COUNT 8
|
||
if( i > MBR_SIGNATURE_COUNT ) {
|
||
foundGUID = TRUE;
|
||
}
|
||
|
||
if( (foundGUID == TRUE) && (prettyGUID == TRUE) ) {
|
||
//
|
||
// pretty GUID format
|
||
// {33221100-5544-7766-8899-aabbccddeeff}
|
||
//
|
||
|
||
bufferString.Buffer = deviceName;
|
||
//
|
||
// (+ 1) for the '}' to be included in the string
|
||
//
|
||
i++;
|
||
bufferString.Length = (USHORT)(i * sizeof(WCHAR));
|
||
bufferString.MaximumLength = bufferString.Length;
|
||
|
||
status = RtlGUIDFromString(
|
||
&bufferString,
|
||
&(diskSignature->Guid)
|
||
);
|
||
if( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
}
|
||
else {
|
||
numberString = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
(i + 1) * sizeof(WCHAR),
|
||
'rvnE'
|
||
);
|
||
|
||
if ( numberString == NULL ) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
wcsncpy( numberString, deviceName, i );
|
||
numberString[ i ] = UNICODE_NULL;
|
||
|
||
if( foundGUID == FALSE ) {
|
||
//
|
||
// MBR Signature format
|
||
// 8459abcc
|
||
//
|
||
status = ExpTranslateHexStringToULONG(
|
||
numberString,
|
||
&(diskSignature->Signature)
|
||
);
|
||
}
|
||
else {
|
||
//
|
||
// ordinary GUID format
|
||
// 00112233445566778899aabbccddeeff
|
||
//
|
||
status = ExpTranslateHexStringToGUID (
|
||
numberString,
|
||
&(diskSignature->Guid)
|
||
);
|
||
}
|
||
ExFreePool( numberString );
|
||
if( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// check if there is more information in the signature name
|
||
//
|
||
if( (i < deviceNameCount) && (deviceName[ i ] == '-') ) {
|
||
longSigFound = TRUE;
|
||
i++;
|
||
//
|
||
// need to parse <part#>-<start>-<size>)
|
||
// <part#> - 8 hex digits representing the ULONG partition number.
|
||
// (Formatted using %08x.)
|
||
// <start> - 16 hex digits representing the ULONGLONG starting LBA.
|
||
// (Formatted using %016I64x.)
|
||
// <size> - 16 hex digits representing the ULONGLONG partition size.
|
||
// (Formatted using %016I64x.)
|
||
//
|
||
if( i >= deviceNameCount ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
#define ULONG_COUNT 8
|
||
#define ULONGLONG_COUNT 16
|
||
//
|
||
// Allocate a buffer to hold a ULONGLONG
|
||
//
|
||
numberString = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
(ULONGLONG_COUNT + 1) * sizeof(WCHAR),
|
||
'rvnE'
|
||
);
|
||
|
||
if ( numberString == NULL ) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
prevI = i;
|
||
currentName = deviceName;
|
||
currentName += i;
|
||
while( i < deviceNameCount ) {
|
||
if( deviceName[ i ] == '-' ) {
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
chCount = i - prevI;
|
||
|
||
if( (chCount == 0) || (chCount > ULONG_COUNT) ) {
|
||
ExFreePool( numberString );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
wcsncpy( numberString, currentName, chCount );
|
||
numberString[ chCount ] = UNICODE_NULL;
|
||
|
||
status = ExpTranslateHexStringToULONG( numberString, partitionNumber );
|
||
if( !NT_SUCCESS(status) ) {
|
||
ExFreePool( numberString );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// get the partition start
|
||
//
|
||
i++;
|
||
if( i >= deviceNameCount ) {
|
||
ExFreePool( numberString );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
prevI = i;
|
||
currentName = deviceName;
|
||
currentName += i;
|
||
while( i < deviceNameCount ) {
|
||
if( deviceName[ i ] == '-' ) {
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
chCount = i - prevI;
|
||
if( (chCount == 0) || (chCount > ULONGLONG_COUNT) ) {
|
||
ExFreePool( numberString );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
wcsncpy( numberString, currentName, chCount );
|
||
numberString[ chCount ] = UNICODE_NULL;
|
||
|
||
status = ExpTranslateHexStringToULONGLONG( numberString, partitionStart );
|
||
if( !NT_SUCCESS(status) ) {
|
||
ExFreePool( numberString );
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// get the partition size
|
||
//
|
||
i++;
|
||
if( i >= deviceNameCount ) {
|
||
ExFreePool( numberString );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
prevI = i;
|
||
currentName = deviceName;
|
||
currentName += i;
|
||
while( i < deviceNameCount ) {
|
||
if( deviceName[ i ] == ')' ) { // should be a ')' delimiter
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
chCount = i - prevI;
|
||
if( (chCount == 0) || (chCount > ULONGLONG_COUNT) ) {
|
||
ExFreePool( numberString );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
wcsncpy( numberString, currentName, chCount );
|
||
numberString[ chCount ] = UNICODE_NULL;
|
||
|
||
status = ExpTranslateHexStringToULONGLONG( numberString, partitionSize );
|
||
ExFreePool( numberString );
|
||
if( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// At this point,
|
||
// current positition should not pass the last char. of the buffer
|
||
// current positition should be a ')'
|
||
// MBR signature must have the long signature() format (need partition number)
|
||
//
|
||
if( (i >= deviceNameCount) ||
|
||
(deviceName[ i ] != ')') ||
|
||
((foundGUID == FALSE) && (longSigFound == FALSE)) ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
*GPTpartition = foundGUID;
|
||
*longSignature = longSigFound;
|
||
return( STATUS_SUCCESS );
|
||
|
||
} // ExpParseSignatureName
|
||
|
||
|
||
NTSTATUS
|
||
ExpParseEfiPath(
|
||
IN EFI_DEVICE_PATH *pDevicePath,
|
||
OUT HARDDRIVE_DEVICE_PATH **ppHardDriveDP,
|
||
OUT PWSTR *ppPathName,
|
||
OUT PBOOLEAN GPTpartition
|
||
)
|
||
|
||
/*++
|
||
Routine Description:
|
||
|
||
Parse the EFI_DEVICE_PATH into the HARDDRIVE node and
|
||
entire PathName from the FILEPATH nodes
|
||
|
||
Assumptions:
|
||
- Parsing will stop at the first END_DEVICE_PATH node
|
||
- The node graph of the Device path should be
|
||
[~(HARDDRIVE, END_DEVICE_PATH)]* -> [HARDRIVE] -> [FILEPATH]* -> [END_DEVICE_PATH]
|
||
|
||
Arguments:
|
||
|
||
pDevicePath - Receives an EFI_DEVICE_PATH
|
||
|
||
ppHardDriveDP - Will receive a pointer to the
|
||
HARDDRIVE_DEVICE_PATH node
|
||
|
||
ppPathName - Will receive a pointer to the
|
||
entire PathName from all the FILEPATH_DEVICE_PATH
|
||
NULL - if the FILEPATH_DEVICE_PATH node does not exist
|
||
|
||
GPTpartition - Will receive the type of partition
|
||
TRUE - GPT partition
|
||
FALSE - MBR partition
|
||
|
||
Return Value:
|
||
|
||
An appropriate status value.
|
||
|
||
--*/
|
||
|
||
{
|
||
EFI_DEVICE_PATH *pDevPath;
|
||
HARDDRIVE_DEVICE_PATH UNALIGNED *pHD_DP = NULL;
|
||
FILEPATH_DEVICE_PATH *pFP_DP = NULL;
|
||
ULONG fpLength,dpLength;
|
||
PWSTR pFilePathName;
|
||
NTSTATUS Status;
|
||
|
||
fpLength = 0;
|
||
dpLength = 0;
|
||
pDevPath = pDevicePath;
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
while( IsDevicePathEndType( pDevPath ) == FALSE ) {
|
||
|
||
if( ( DevicePathType( pDevPath ) != MEDIA_DEVICE_PATH ) ||
|
||
( DevicePathSubType( pDevPath ) != MEDIA_HARDDRIVE_DP ) ) {
|
||
pDevPath = NextDevicePathNode( pDevPath );
|
||
}
|
||
else {
|
||
//
|
||
// return the HardDrive node
|
||
//
|
||
pHD_DP = (HARDDRIVE_DEVICE_PATH UNALIGNED *)pDevPath;
|
||
|
||
//
|
||
// Assume successful operations until an error is detected
|
||
//
|
||
Status = STATUS_SUCCESS;
|
||
dpLength += DevicePathNodeLength( pDevPath );
|
||
pDevPath = NextDevicePathNode( pDevPath );
|
||
|
||
if( ( DevicePathType( pDevPath ) == MEDIA_DEVICE_PATH ) &&
|
||
( DevicePathSubType( pDevPath ) == MEDIA_FILEPATH_DP ) ) {
|
||
|
||
//
|
||
// return the FilePath node
|
||
//
|
||
pFP_DP = (FILEPATH_DEVICE_PATH *)pDevPath;
|
||
|
||
//
|
||
// Sum up the lengths of all PathNames in the
|
||
// FilePath nodes
|
||
//
|
||
do {
|
||
//
|
||
// Length of PathName is
|
||
// FILEPATH_DEVICE_PATH.Length - (offset to PathName field)
|
||
//
|
||
fpLength += (DevicePathNodeLength(pDevPath) -
|
||
FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName));
|
||
dpLength += DevicePathNodeLength( pDevPath );
|
||
pDevPath = NextDevicePathNode( pDevPath );
|
||
|
||
} while( ( DevicePathType( pDevPath ) == MEDIA_DEVICE_PATH ) &&
|
||
( DevicePathSubType( pDevPath ) == MEDIA_FILEPATH_DP ) );
|
||
}
|
||
|
||
//
|
||
// At this point, the node must be a END_DEVICE_PATH
|
||
//
|
||
if( IsDevicePathEndType( pDevPath ) == FALSE ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If no MEDIA/HARDDRIVE element was found, we cannot continue. The
|
||
// MEDIA/FILEPATH element is optional.
|
||
//
|
||
if( !NT_SUCCESS( Status ) ) {
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// Check the partition type, must be GPT or MBR
|
||
//
|
||
if( pHD_DP->SignatureType == SIGNATURE_TYPE_GUID ) {
|
||
*GPTpartition = TRUE;
|
||
}
|
||
else {
|
||
if ( pHD_DP->SignatureType == SIGNATURE_TYPE_MBR ) {
|
||
*GPTpartition = FALSE;
|
||
}
|
||
else {
|
||
//DbgPrint( "ExpParseEfiPath: partition signature type unknown\n" );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
if( fpLength != 0 ) {
|
||
fpLength += sizeof(WCHAR); // add null-terminator
|
||
pFilePathName = ExAllocatePoolWithTag( NonPagedPool, fpLength, 'rvnE' );
|
||
if( pFilePathName == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
wcscpy( pFilePathName, pFP_DP->PathName );
|
||
|
||
pDevPath = (EFI_DEVICE_PATH *)pFP_DP;
|
||
pDevPath = NextDevicePathNode( pDevPath );
|
||
|
||
while( IsDevicePathEndType( pDevPath ) == FALSE ) {
|
||
pFP_DP = (FILEPATH_DEVICE_PATH *)pDevPath;
|
||
wcscat( pFilePathName, pFP_DP->PathName );
|
||
pDevPath = NextDevicePathNode( pDevPath );
|
||
}
|
||
}
|
||
else {
|
||
pFilePathName = NULL;
|
||
}
|
||
|
||
//
|
||
// almost done. allocate an aligned buffer for the device path and copy
|
||
// the unaligned contents into this buffer.
|
||
//
|
||
*ppHardDriveDP = ExAllocatePoolWithTag( NonPagedPool, dpLength, 'rvnE' );
|
||
if (*ppHardDriveDP == NULL) {
|
||
if (pFilePathName) {
|
||
ExFreePool(pFilePathName);
|
||
}
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
RtlCopyMemory( *ppHardDriveDP, pHD_DP, dpLength );
|
||
*ppPathName = pFilePathName;
|
||
|
||
return( Status );
|
||
} // ExpParseEfiPath
|
||
|
||
|
||
NTSTATUS
|
||
ExpConvertArcName(
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PWSTR pDeviceName,
|
||
IN PWSTR pPathName,
|
||
IN ULONG DeviceNameCount
|
||
)
|
||
{
|
||
ULONG requiredCount, requiredLength, filePathLength;
|
||
PWSTR linkName;
|
||
UNICODE_STRING deviceNameString;
|
||
PWCHAR p;
|
||
PFILE_PATH filePath;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Allocate Pool to hold the ArcName's NT Name
|
||
//
|
||
#define ARC_DIR_PREFIX L"\\ArcName\\"
|
||
#define ARC_DIR_PREFIX_COUNT 9
|
||
|
||
requiredCount = DeviceNameCount + ARC_DIR_PREFIX_COUNT + 1;
|
||
requiredLength = requiredCount * sizeof(WCHAR);
|
||
linkName = ExAllocatePoolWithTag( NonPagedPool, requiredLength, 'rvnE' );
|
||
if ( linkName == NULL ) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
wcscpy( linkName, ARC_DIR_PREFIX );
|
||
wcsncat( linkName, pDeviceName, DeviceNameCount );
|
||
linkName[ requiredCount - 1 ] = UNICODE_NULL;
|
||
|
||
if( OutputType == FILE_PATH_TYPE_NT ) {
|
||
//
|
||
// Open the symbolic link object & drill down to the target
|
||
// return symbolic link target
|
||
//
|
||
status = ExpTranslateSymbolicLink(
|
||
linkName,
|
||
&deviceNameString
|
||
);
|
||
ExFreePool( linkName );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return( status );
|
||
}
|
||
|
||
status = ExpCreateOutputNT(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&deviceNameString,
|
||
pPathName
|
||
);
|
||
ExFreePool( deviceNameString.Buffer );
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// Output type is either FILE_PATH_TYPE_EFI or FILE_PATH_TYPE_ARC_SIGNATURE
|
||
// and we have a NT name, so use ExpTranslateNtPath() for the conversion
|
||
// Create a input FILE_PATH with the NT name
|
||
//
|
||
filePathLength = requiredLength + FIELD_OFFSET(FILE_PATH, FilePath);
|
||
if ( pPathName != NULL ) {
|
||
filePathLength += ((ULONG)(wcslen( pPathName )) * sizeof(WCHAR));
|
||
}
|
||
|
||
filePathLength += sizeof(WCHAR);
|
||
|
||
filePath = ExAllocatePoolWithTag( NonPagedPool, filePathLength, 'rvnE' );
|
||
|
||
if ( filePath == NULL ) {
|
||
ExFreePool( linkName );
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Build the input file path.
|
||
//
|
||
filePath->Version = FILE_PATH_VERSION;
|
||
filePath->Length = filePathLength;
|
||
filePath->Type = FILE_PATH_TYPE_NT;
|
||
|
||
p = (PWSTR)filePath->FilePath;
|
||
wcscpy( p, linkName );
|
||
p = (PWSTR)((PUCHAR)p + requiredLength);
|
||
|
||
ExFreePool( linkName );
|
||
|
||
if ( pPathName != NULL ) {
|
||
wcscpy( p, pPathName );
|
||
}
|
||
else {
|
||
*p = UNICODE_NULL;
|
||
}
|
||
|
||
status = ExpTranslateNtPath(
|
||
filePath,
|
||
OutputType,
|
||
OutputPath,
|
||
OutputPathLength
|
||
);
|
||
|
||
ExFreePool( filePath );
|
||
|
||
return( status );
|
||
} // ExpConvertArcName
|
||
|
||
|
||
NTSTATUS
|
||
ExpConvertSignatureName(
|
||
IN ULONG OutputType,
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PWSTR pDeviceName,
|
||
IN PWSTR pPathName,
|
||
IN ULONG DeviceNameCount
|
||
)
|
||
{
|
||
DISK_SIGNATURE_NEW diskSignature;
|
||
ULONG inputPartitionNumber, outputPartitionNumber;
|
||
ULONG diskNumber;
|
||
ULONGLONG inputPartitionStart, outputPartitionStart;
|
||
ULONGLONG inputPartitionSize, outputPartitionSize;
|
||
BOOLEAN GPTpartition, longSignature;
|
||
PWSTR pDiskName;
|
||
UNICODE_STRING DiskNameString;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Determine the signature() format
|
||
//
|
||
status = ExpParseSignatureName (
|
||
pDeviceName,
|
||
DeviceNameCount,
|
||
&diskSignature,
|
||
&inputPartitionNumber,
|
||
&inputPartitionStart,
|
||
&inputPartitionSize,
|
||
&GPTpartition,
|
||
&longSignature
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// if signature(<guid/signature>-<part#>-<start>-<size>) format &&
|
||
// ( OutputType == FILE_PATH_TYPE_EFI )
|
||
// return EFI_DEVICE_PATH format
|
||
//
|
||
if( (longSignature == TRUE) && (OutputType == FILE_PATH_TYPE_EFI) ) {
|
||
status = ExpCreateOutputEFI(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&diskSignature,
|
||
&inputPartitionNumber,
|
||
&inputPartitionStart,
|
||
&inputPartitionSize,
|
||
pPathName,
|
||
GPTpartition
|
||
);
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// open all disks and search for partition GUID
|
||
//
|
||
if( GPTpartition == FALSE ) {
|
||
outputPartitionNumber = inputPartitionNumber;
|
||
}
|
||
status = ExpFindDiskSignature(
|
||
&diskSignature,
|
||
&outputPartitionNumber,
|
||
&diskNumber,
|
||
&outputPartitionStart,
|
||
&outputPartitionSize,
|
||
GPTpartition
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the user has provided the partition number, start address,
|
||
// and size; then verify the input with the found results.
|
||
//
|
||
if( (longSignature == TRUE) &&
|
||
( (inputPartitionNumber != outputPartitionNumber) ||
|
||
(inputPartitionStart != outputPartitionStart) ||
|
||
(inputPartitionSize != outputPartitionSize)
|
||
) ) {
|
||
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
if( OutputType == FILE_PATH_TYPE_EFI ) {
|
||
status = ExpCreateOutputEFI(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&diskSignature,
|
||
&outputPartitionNumber,
|
||
&outputPartitionStart,
|
||
&outputPartitionSize,
|
||
pPathName,
|
||
GPTpartition
|
||
);
|
||
return( status );
|
||
}
|
||
|
||
//
|
||
// translate \Device\Harddisk[diskNumber]\Partition[PartitionNumber]
|
||
//
|
||
#define DISK_NAME_FORMAT L"\\Device\\Harddisk%lu\\Partition%lu"
|
||
#define DISK_NAME_COUNT 47 // 7 + 9 + (10) + 10 + (10) + 1
|
||
|
||
pDiskName = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
( DISK_NAME_COUNT * sizeof( WCHAR ) ),
|
||
'rvnE'
|
||
);
|
||
|
||
if( pDiskName == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
_snwprintf(
|
||
pDiskName,
|
||
DISK_NAME_COUNT,
|
||
DISK_NAME_FORMAT,
|
||
diskNumber,
|
||
outputPartitionNumber
|
||
);
|
||
|
||
status = ExpTranslateSymbolicLink(
|
||
pDiskName,
|
||
&DiskNameString
|
||
);
|
||
ExFreePool( pDiskName );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return( status );
|
||
}
|
||
|
||
if( OutputType == FILE_PATH_TYPE_NT ) {
|
||
status = ExpCreateOutputNT(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&DiskNameString,
|
||
pPathName
|
||
);
|
||
ExFreePool( DiskNameString.Buffer );
|
||
return( status );
|
||
}
|
||
|
||
if( OutputType == FILE_PATH_TYPE_ARC ) {
|
||
status = ExpCreateOutputARC(
|
||
OutputPath,
|
||
OutputPathLength,
|
||
&DiskNameString,
|
||
pPathName
|
||
);
|
||
ExFreePool( DiskNameString.Buffer );
|
||
return( status );
|
||
}
|
||
|
||
ExFreePool( DiskNameString.Buffer );
|
||
return( STATUS_INVALID_PARAMETER );
|
||
} // ExpConvertSignatureName
|
||
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToULONG (
|
||
IN PWSTR Name,
|
||
OUT PULONG Number
|
||
)
|
||
{
|
||
ULONG number;
|
||
ULONG i, max;
|
||
WCHAR c;
|
||
|
||
#define ULONG_HEX_MAX 8
|
||
|
||
max = (ULONG)wcslen( Name );
|
||
|
||
if( max > ULONG_HEX_MAX ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
number = 0;
|
||
for (i = 0; i < max; i++) {
|
||
c = towlower(Name[i]);
|
||
if ((c >= L'0') && (c <= L'9')) {
|
||
number = (number * 16) + (c - L'0');
|
||
} else if ((c >= L'a') && (c <= L'f')) {
|
||
number = (number * 16) + (c - L'a' + 10);
|
||
} else {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
*Number = number;
|
||
return( STATUS_SUCCESS );
|
||
|
||
} // ExpTranslateHexStringToULONG
|
||
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToULONGLONG (
|
||
IN PWSTR Name,
|
||
OUT PULONGLONG Number
|
||
)
|
||
{
|
||
ULONGLONG number;
|
||
ULONG i, max;
|
||
WCHAR c;
|
||
|
||
#define ULONGLONG_HEX_MAX 16
|
||
|
||
max = (ULONG)wcslen( Name );
|
||
|
||
if( max > ULONGLONG_HEX_MAX ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
number = 0;
|
||
for (i = 0; i < max; i++) {
|
||
c = towlower(Name[i]);
|
||
if ((c >= L'0') && (c <= L'9')) {
|
||
number = (number * 16) + (c - L'0');
|
||
} else if ((c >= L'a') && (c <= L'f')) {
|
||
number = (number * 16) + (c - L'a' + 10);
|
||
} else {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
}
|
||
|
||
*Number = number;
|
||
return( STATUS_SUCCESS );
|
||
|
||
} // ExpTranslateHexStringToULONGLONG
|
||
|
||
|
||
NTSTATUS
|
||
ExpTranslateHexStringToGUID (
|
||
IN PWSTR Name,
|
||
OUT GUID *pGuid
|
||
)
|
||
{
|
||
GUID resultGuid;
|
||
ULONG i, max, number, result;
|
||
USHORT formatStyle, position;
|
||
WCHAR c;
|
||
|
||
#define GUID_HEX_MAX 32
|
||
|
||
max = (ULONG)wcslen( Name );
|
||
|
||
if( max != GUID_HEX_MAX ) {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
number = 0;
|
||
formatStyle = 0;
|
||
position = 0;
|
||
result = 0;
|
||
for (i = 0; i < max; i++) {
|
||
c = towlower(Name[i]);
|
||
if ((c >= L'0') && (c <= L'9')) {
|
||
number = (number * 16) + (c - L'0');
|
||
} else if ((c >= L'a') && (c <= L'f')) {
|
||
number = (number * 16) + (c - L'a' + 10);
|
||
} else {
|
||
return( STATUS_INVALID_PARAMETER );
|
||
}
|
||
|
||
if ((i % 2) == 1) {
|
||
switch( formatStyle ) {
|
||
case 0:
|
||
result += (number << (position * 8));
|
||
position++;
|
||
if( position == 4 ) {
|
||
resultGuid.Data1 = result;
|
||
formatStyle++;
|
||
position = 0;
|
||
result = 0;
|
||
}
|
||
break;
|
||
case 1:
|
||
result += (number << (position * 8));
|
||
position++;
|
||
if( position == 2 ) {
|
||
resultGuid.Data2 = (USHORT)result;
|
||
formatStyle++;
|
||
position = 0;
|
||
result = 0;
|
||
}
|
||
break;
|
||
case 2:
|
||
result += (number << (position * 8));
|
||
position++;
|
||
if( position == 2 ) {
|
||
resultGuid.Data3 = (USHORT)result;
|
||
formatStyle++;
|
||
position = 0;
|
||
result = 0;
|
||
}
|
||
break;
|
||
case 3:
|
||
resultGuid.Data4[ position ] = (UCHAR)number;
|
||
position++;
|
||
if( position == 8 ) {
|
||
formatStyle++;
|
||
}
|
||
break;
|
||
default:
|
||
return( STATUS_INVALID_PARAMETER );
|
||
break;
|
||
}
|
||
number = 0;
|
||
}
|
||
}
|
||
|
||
memcpy(pGuid, &(resultGuid), sizeof(GUID));
|
||
return( STATUS_SUCCESS );
|
||
|
||
} // ExpTranslateHexStringToGUID
|
||
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputEFI (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PDISK_SIGNATURE_NEW pDiskSignature,
|
||
IN PULONG pPartitionNumber,
|
||
IN PULONGLONG pPartitionStart,
|
||
IN PULONGLONG pPartitionSize,
|
||
IN PWSTR pPathName,
|
||
IN BOOLEAN GPTpartition
|
||
)
|
||
{
|
||
ULONG requiredLength, pathNameLength = 0;
|
||
EFI_DEVICE_PATH *dp;
|
||
HARDDRIVE_DEVICE_PATH UNALIGNED *dpHarddrive = NULL;
|
||
FILEPATH_DEVICE_PATH *dpFilepath = NULL;
|
||
|
||
//
|
||
// The output EFI file path consists of two elements. First is a
|
||
// MEDIA/HARDDRIVE element describing the partition. Second is an
|
||
// optional MEDIA/FILEPATH element describing the path to a directory
|
||
// or a file.
|
||
//
|
||
|
||
requiredLength = FIELD_OFFSET(FILE_PATH, FilePath);
|
||
requiredLength += sizeof(HARDDRIVE_DEVICE_PATH);
|
||
if (pPathName != NULL) {
|
||
pathNameLength = (ULONG)wcslen(pPathName);
|
||
pathNameLength = (pathNameLength + 1) * sizeof(WCHAR);
|
||
requiredLength += FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName);
|
||
requiredLength += pathNameLength;
|
||
}
|
||
requiredLength += sizeof(EFI_DEVICE_PATH);
|
||
|
||
//
|
||
// Compare the required length against the output buffer length.
|
||
//
|
||
|
||
if ( *OutputPathLength < requiredLength ) {
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Build the output file path.
|
||
//
|
||
|
||
OutputPath->Version = FILE_PATH_VERSION;
|
||
OutputPath->Length = requiredLength;
|
||
OutputPath->Type = FILE_PATH_TYPE_EFI;
|
||
|
||
dp = (EFI_DEVICE_PATH *)OutputPath->FilePath;
|
||
dpHarddrive = (HARDDRIVE_DEVICE_PATH UNALIGNED *)dp;
|
||
dp->Type = MEDIA_DEVICE_PATH;
|
||
dp->SubType = MEDIA_HARDDRIVE_DP;
|
||
SetDevicePathNodeLength(dp, sizeof(HARDDRIVE_DEVICE_PATH));
|
||
dpHarddrive->PartitionNumber = *pPartitionNumber;
|
||
dpHarddrive->PartitionStart = *pPartitionStart;
|
||
dpHarddrive->PartitionSize = *pPartitionSize;
|
||
if (GPTpartition == TRUE) {
|
||
memcpy(dpHarddrive->Signature, &(pDiskSignature->Guid), sizeof(GUID));
|
||
dpHarddrive->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER;
|
||
dpHarddrive->SignatureType = SIGNATURE_TYPE_GUID;
|
||
} else {
|
||
memcpy(dpHarddrive->Signature, &(pDiskSignature->Signature), sizeof(ULONG));
|
||
dpHarddrive->MBRType = MBR_TYPE_PCAT;
|
||
dpHarddrive->SignatureType = SIGNATURE_TYPE_MBR;
|
||
}
|
||
|
||
if (pPathName != NULL) {
|
||
dp = NextDevicePathNode(dp);
|
||
dpFilepath = (FILEPATH_DEVICE_PATH *)dp;
|
||
dp->Type = MEDIA_DEVICE_PATH;
|
||
dp->SubType = MEDIA_FILEPATH_DP;
|
||
SetDevicePathNodeLength(dp, FIELD_OFFSET(FILEPATH_DEVICE_PATH, PathName) + pathNameLength);
|
||
wcscpy(dpFilepath->PathName, pPathName);
|
||
}
|
||
|
||
dp = NextDevicePathNode(dp);
|
||
SetDevicePathEndNode(dp);
|
||
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpCreateOutputEFI
|
||
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputNT (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
IN PWSTR pPathName
|
||
)
|
||
{
|
||
ULONG requiredLength;
|
||
PWCHAR p;
|
||
|
||
requiredLength = pDeviceNameString->Length + sizeof(WCHAR);
|
||
|
||
//
|
||
// If a PathName component is present, then increase the
|
||
// output string length by the length of the path string.
|
||
//
|
||
|
||
if ( pPathName != NULL ) {
|
||
requiredLength += ((ULONG)(wcslen( pPathName )) * sizeof(WCHAR));
|
||
}
|
||
|
||
//
|
||
// always add a UNICODE_NULL for PathName even if PathName is not present
|
||
//
|
||
requiredLength += sizeof(WCHAR);
|
||
|
||
//
|
||
// Add the structure overhead and compare the required length against
|
||
// output buffer length.
|
||
//
|
||
|
||
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
|
||
|
||
if ( *OutputPathLength < requiredLength ) {
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Build the output file path.
|
||
//
|
||
|
||
OutputPath->Version = FILE_PATH_VERSION;
|
||
OutputPath->Length = requiredLength;
|
||
OutputPath->Type = FILE_PATH_TYPE_NT;
|
||
|
||
p = (PWSTR)OutputPath->FilePath;
|
||
wcscpy( p, pDeviceNameString->Buffer );
|
||
p = (PWSTR)((PUCHAR)p + pDeviceNameString->Length + sizeof(WCHAR));
|
||
|
||
if ( pPathName != NULL ) {
|
||
wcscpy( p, pPathName );
|
||
}
|
||
else {
|
||
*p = UNICODE_NULL;
|
||
}
|
||
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpCreateOutputNT
|
||
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputARC (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
IN PWSTR pPathName
|
||
)
|
||
{
|
||
ULONG requiredLength, ArcNameLength;
|
||
PWCHAR p;
|
||
PWSTR pArcDeviceName;
|
||
NTSTATUS status;
|
||
|
||
status = ExpFindArcName(
|
||
pDeviceNameString,
|
||
&pArcDeviceName
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
return( status );
|
||
}
|
||
|
||
ArcNameLength = ((ULONG)wcslen(pArcDeviceName)) * sizeof(WCHAR);
|
||
requiredLength = ArcNameLength + sizeof(WCHAR);
|
||
|
||
//
|
||
// If a PathName component is present, then increase the
|
||
// output string length by the length of the path string.
|
||
//
|
||
|
||
if ( pPathName != NULL ) {
|
||
requiredLength += ((ULONG)(wcslen( pPathName )) * sizeof(WCHAR));
|
||
}
|
||
|
||
//
|
||
// Add the structure overhead and compare the required length against
|
||
// output buffer length.
|
||
//
|
||
|
||
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
|
||
|
||
if ( *OutputPathLength < requiredLength ) {
|
||
*OutputPathLength = requiredLength;
|
||
ExFreePool( pArcDeviceName );
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Build the output file path.
|
||
//
|
||
|
||
OutputPath->Version = FILE_PATH_VERSION;
|
||
OutputPath->Length = requiredLength;
|
||
OutputPath->Type = FILE_PATH_TYPE_ARC;
|
||
|
||
p = (PWSTR)OutputPath->FilePath;
|
||
wcscpy( p, pArcDeviceName );
|
||
p = (PWSTR)((PUCHAR)p + ArcNameLength);
|
||
ExFreePool( pArcDeviceName );
|
||
|
||
if ( pPathName != NULL ) {
|
||
wcscpy( p, pPathName );
|
||
}
|
||
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpCreateOutputARC
|
||
|
||
|
||
NTSTATUS
|
||
ExpCreateOutputSIGNATURE (
|
||
OUT PFILE_PATH OutputPath,
|
||
IN OUT PULONG OutputPathLength,
|
||
IN PDISK_SIGNATURE_NEW pDiskSignature,
|
||
IN PULONG pPartitionNumber,
|
||
IN PULONGLONG pPartitionStart,
|
||
IN PULONGLONG pPartitionSize,
|
||
IN PWSTR pPathName,
|
||
IN BOOLEAN GPTpartition
|
||
)
|
||
{
|
||
ULONG requiredLength, pathNameCount;
|
||
PWCHAR p;
|
||
UNICODE_STRING GuidString;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// We will convert the EFI device path into an ARC name with this
|
||
// format:
|
||
//
|
||
// signature(<guid/signature>-<part#>-<start>-<size>)
|
||
//
|
||
// Where:
|
||
//
|
||
// <guid/signature> - For a GPT disk, the GPT partition GUID in
|
||
// "pretty" format ({33221100-5544-7766-8899-aabbccddeeff}).
|
||
// For an MBR disk, 8 hex digits representing the ULONG MBR
|
||
// disk signature. (Formatted using %08x.)
|
||
// <part#> - 8 hex digits representing the ULONG partition number.
|
||
// (Formatted using %08x.)
|
||
// <start> - 16 hex digits representing the ULONGLONG starting LBA.
|
||
// (Formatted using %016I64x.)
|
||
// <size> - 16 hex digits representing the ULONGLONG partition size.
|
||
// (Formatted using %016I64x.)
|
||
//
|
||
// For a GPT disk, the output string length is 86 WCHARs. For an
|
||
// MBR disk, the output string length is 62 WCHARs.
|
||
//
|
||
|
||
requiredLength = (ULONG)strlen("signature(") +
|
||
1 + // "-"
|
||
(sizeof(ULONG) * 2) + // <part#>
|
||
1 + // "-"
|
||
(sizeof(ULONGLONG) * 2) + // <start>
|
||
1 + // "-"
|
||
(sizeof(ULONGLONG) * 2) + // <size>
|
||
1 + // ")"
|
||
1; // null terminator
|
||
|
||
if ( GPTpartition == TRUE ) {
|
||
requiredLength += (sizeof(GUID) * 2);
|
||
requiredLength += 6; // for the {} & four '-' in the pretty GUID format
|
||
} else {
|
||
requiredLength += sizeof(ULONG) * 2;
|
||
}
|
||
|
||
//
|
||
// If a pathName component is present, then increase the
|
||
// output string length by the length of the path string.
|
||
//
|
||
|
||
if (pPathName != NULL) {
|
||
pathNameCount = (ULONG)wcslen(pPathName);
|
||
requiredLength += pathNameCount;
|
||
}
|
||
else {
|
||
pathNameCount = 0;
|
||
}
|
||
|
||
//
|
||
// Convert the string length to a byte count, add the structure
|
||
// overhead, and compare the required length against output buffer
|
||
// length.
|
||
//
|
||
|
||
requiredLength *= sizeof(WCHAR);
|
||
requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
|
||
|
||
if ( *OutputPathLength < requiredLength ) {
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
//
|
||
// Build the output file path.
|
||
//
|
||
|
||
OutputPath->Version = FILE_PATH_VERSION;
|
||
OutputPath->Length = requiredLength;
|
||
OutputPath->Type = FILE_PATH_TYPE_ARC_SIGNATURE;
|
||
|
||
p = (PWSTR)OutputPath->FilePath;
|
||
wcscpy( p, L"signature(" );
|
||
p += wcslen( p );
|
||
|
||
if ( GPTpartition == TRUE ) {
|
||
status = RtlStringFromGUID(
|
||
(LPGUID)(&(pDiskSignature->Guid)),
|
||
&GuidString
|
||
);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
wcscat( p, GuidString.Buffer );
|
||
p = (PWCHAR)((PUCHAR)p + GuidString.Length);
|
||
ExFreePool( GuidString.Buffer );
|
||
} else {
|
||
swprintf( p, L"%08x", pDiskSignature->Signature );
|
||
p += wcslen( p );
|
||
}
|
||
|
||
swprintf(
|
||
p,
|
||
L"-%08x-%016I64x-%016I64x)",
|
||
*pPartitionNumber,
|
||
*pPartitionStart,
|
||
*pPartitionSize
|
||
);
|
||
p += wcslen( p );
|
||
|
||
if ( pathNameCount != 0 ) {
|
||
wcscpy( p, pPathName );
|
||
}
|
||
|
||
*OutputPathLength = requiredLength;
|
||
return STATUS_SUCCESS;
|
||
|
||
} // ExpCreateOutputSIGNATURE
|
||
|
||
|
||
NTSTATUS
|
||
ExpFindArcName (
|
||
IN PUNICODE_STRING pDeviceNameString,
|
||
OUT PWSTR *pArcName
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING ArcString, SymLinkTypeString;
|
||
OBJECT_ATTRIBUTES Attributes;
|
||
PWSTR pArcDirName, pArcLinkName;
|
||
HANDLE hArcDirectory;
|
||
POBJECT_DIRECTORY_INFORMATION pDirInfo;
|
||
ULONG dirInfoLength, neededLength, dirContext;
|
||
ULONG arcNameCount;
|
||
BOOLEAN restartScan, ArcNameFound = FALSE;
|
||
|
||
//
|
||
// Open a handle to the directory object for \ArcName
|
||
// Get a kernel handle
|
||
//
|
||
#define ARC_DIR_NAME L"\\ArcName"
|
||
#define ARC_DIR_SIZE (9 * sizeof(WCHAR))
|
||
#define ARC_DIR_NAME_PREFIX L"\\ArcName\\"
|
||
#define ARC_DIR_SIZE_PREFIX (9 * sizeof(WCHAR))
|
||
|
||
pArcDirName = ExAllocatePoolWithTag( NonPagedPool, ARC_DIR_SIZE, 'rvnE' );
|
||
if ( pArcDirName == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
wcscpy( pArcDirName, ARC_DIR_NAME );
|
||
|
||
RtlInitUnicodeString( &ArcString, pArcDirName );
|
||
|
||
InitializeObjectAttributes(
|
||
&Attributes,
|
||
&ArcString,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenDirectoryObject(
|
||
&hArcDirectory,
|
||
DIRECTORY_QUERY,
|
||
&Attributes
|
||
);
|
||
ExFreePool( pArcDirName );
|
||
if (!NT_SUCCESS(status)) {
|
||
return( status );
|
||
}
|
||
|
||
pDirInfo = NULL;
|
||
dirInfoLength = 0;
|
||
restartScan = TRUE;
|
||
RtlInitUnicodeString( &SymLinkTypeString, L"SymbolicLink" );
|
||
while (TRUE) {
|
||
status = ZwQueryDirectoryObject(
|
||
hArcDirectory,
|
||
pDirInfo,
|
||
dirInfoLength,
|
||
TRUE, // force one at a time
|
||
restartScan,
|
||
&dirContext,
|
||
&neededLength
|
||
);
|
||
if (status == STATUS_BUFFER_TOO_SMALL) {
|
||
dirInfoLength = neededLength;
|
||
if (pDirInfo != NULL) {
|
||
ExFreePool(pDirInfo);
|
||
}
|
||
pDirInfo = ExAllocatePoolWithTag( NonPagedPool, dirInfoLength, 'rvnE' );
|
||
if (pDirInfo == NULL) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
status = ZwQueryDirectoryObject(
|
||
hArcDirectory,
|
||
pDirInfo,
|
||
dirInfoLength,
|
||
TRUE, // force one at a time
|
||
restartScan,
|
||
&dirContext,
|
||
&neededLength
|
||
);
|
||
}
|
||
restartScan = FALSE;
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
if (status == STATUS_NO_MORE_ENTRIES) {
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check if the element is not a symbolic link
|
||
//
|
||
if (RtlEqualUnicodeString(
|
||
&(pDirInfo->TypeName),
|
||
&SymLinkTypeString,
|
||
FALSE) == FALSE) {
|
||
continue;
|
||
}
|
||
|
||
neededLength = ARC_DIR_SIZE_PREFIX + pDirInfo->Name.Length;
|
||
pArcLinkName = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
neededLength + sizeof(WCHAR),
|
||
'rvnE' );
|
||
if ( pArcLinkName == NULL ) {
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
arcNameCount = pDirInfo->Name.Length/sizeof(WCHAR);
|
||
wcscpy( pArcLinkName, ARC_DIR_NAME_PREFIX );
|
||
wcsncat(
|
||
pArcLinkName,
|
||
pDirInfo->Name.Buffer,
|
||
arcNameCount
|
||
);
|
||
pArcLinkName[ neededLength/sizeof(WCHAR) ] = UNICODE_NULL;
|
||
|
||
//
|
||
// Drill down this symbolic link to the device object
|
||
//
|
||
status = ExpTranslateSymbolicLink(
|
||
pArcLinkName,
|
||
&ArcString
|
||
);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
ExFreePool( pArcLinkName );
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Check if this Arc Name points the same device object
|
||
//
|
||
ArcNameFound = RtlEqualUnicodeString(
|
||
&ArcString,
|
||
pDeviceNameString,
|
||
TRUE
|
||
);
|
||
ExFreePool( ArcString.Buffer );
|
||
|
||
if (ArcNameFound == TRUE) {
|
||
//
|
||
// copy the arc name without the \ArcName\ prefix
|
||
//
|
||
wcsncpy(
|
||
pArcLinkName,
|
||
pDirInfo->Name.Buffer,
|
||
arcNameCount
|
||
);
|
||
pArcLinkName[ arcNameCount ] = UNICODE_NULL;
|
||
*pArcName = pArcLinkName;
|
||
break;
|
||
}
|
||
ExFreePool( pArcLinkName );
|
||
}
|
||
|
||
if( NT_SUCCESS(status) && (ArcNameFound == FALSE ) ) {
|
||
status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
|
||
if (pDirInfo != NULL) {
|
||
ExFreePool(pDirInfo);
|
||
}
|
||
|
||
ZwClose( hArcDirectory );
|
||
return( status );
|
||
|
||
} // ExpFindArcName
|
||
|
||
|
||
NTSTATUS
|
||
ExpFindDiskSignature (
|
||
IN PDISK_SIGNATURE_NEW pSignature,
|
||
IN OUT PULONG pPartitionNumber,
|
||
OUT PULONG pDiskNumber,
|
||
OUT PULONGLONG pPartitionStart,
|
||
OUT PULONGLONG pPartitionSize,
|
||
IN BOOLEAN GPTpartition
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function searches all the disks on the system for the
|
||
partition corresponding to the paritition GUID or
|
||
(MBR signature, paritition number).
|
||
|
||
N.B. for a MBR signature, the partition number must be provided.
|
||
|
||
Arguments:
|
||
|
||
pSignature - Supplies a pointer to a partition GUID (GPT disk) or
|
||
32-bit signature(MBR disk).
|
||
|
||
pPartitionNumber - Supplies a pointer to a partition number when
|
||
pSignature is a MBR signature. For output, receives the
|
||
partition number.
|
||
|
||
pDiskNumber - Receives the disk number
|
||
|
||
pPartitionStart - Receives the start of the partition
|
||
|
||
pPartitionSize - Receives the size of the partition
|
||
|
||
GPTpartition - Supplies the type of partition
|
||
TRUE - GPT disk partition
|
||
FALSE - MBR disk partition
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS is returned if the partition is successfully found.
|
||
|
||
STATUS_OBJECT_PATH_NOT_FOUND is returned if the partition could not
|
||
be found.
|
||
|
||
STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
|
||
for this request to complete.
|
||
|
||
--*/
|
||
{
|
||
PDRIVE_LAYOUT_INFORMATION_EX pDriveLayout = NULL;
|
||
PPARTITION_INFORMATION_EX pPartitionInfoEx = NULL;
|
||
SYSTEM_DEVICE_INFORMATION SysDevInfo;
|
||
ULONG PartitionStyle;
|
||
BOOLEAN PartitionFound = FALSE;
|
||
ULONG Index, PartitionIndex;
|
||
PWSTR pDeviceName;
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// Find all disks on the system
|
||
//
|
||
|
||
Status = ZwQuerySystemInformation(
|
||
SystemDeviceInformation,
|
||
&SysDevInfo,
|
||
sizeof(SYSTEM_DEVICE_INFORMATION),
|
||
NULL
|
||
);
|
||
|
||
if( !NT_SUCCESS( Status ) ) {
|
||
return( Status );
|
||
}
|
||
|
||
#define DEVICE_NAME_FORMAT L"\\Device\\Harddisk%lu\\Partition0"
|
||
#define DEVICE_NAME_CHAR_COUNT 38 // 7 + 9 + (10) + 11 + 1
|
||
//
|
||
// Allocate the buffer for the disk names
|
||
//
|
||
pDeviceName = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
( DEVICE_NAME_CHAR_COUNT * sizeof( WCHAR ) ),
|
||
'rvnE'
|
||
);
|
||
|
||
if( pDeviceName == NULL ) {
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
if( GPTpartition == TRUE ) {
|
||
PartitionStyle = PARTITION_STYLE_GPT;
|
||
}
|
||
else {
|
||
PartitionStyle = PARTITION_STYLE_MBR;
|
||
}
|
||
|
||
//
|
||
// For each disk,
|
||
// Get the Partition Table
|
||
// Verify the partition style (MBR/GPT)
|
||
//
|
||
// if( Partition style matches )
|
||
// search for the Partition in the drive layout
|
||
// else
|
||
// skip the disk
|
||
//
|
||
for( Index = 0; Index < SysDevInfo.NumberOfDisks; Index++ ) {
|
||
|
||
//
|
||
// Form the disk name
|
||
// \Device\Harddisk[DiskNumber]\Partition0
|
||
//
|
||
_snwprintf(
|
||
pDeviceName,
|
||
DEVICE_NAME_CHAR_COUNT,
|
||
DEVICE_NAME_FORMAT,
|
||
Index
|
||
);
|
||
|
||
Status = ExpGetPartitionTableInfo(
|
||
pDeviceName,
|
||
&pDriveLayout
|
||
);
|
||
|
||
if( !NT_SUCCESS( Status ) ) {
|
||
continue;
|
||
}
|
||
|
||
if( pDriveLayout->PartitionStyle != PartitionStyle ) {
|
||
ExFreePool( pDriveLayout );
|
||
continue;
|
||
}
|
||
|
||
if( (PartitionStyle == PARTITION_STYLE_MBR) &&
|
||
(pDriveLayout->Mbr.Signature != pSignature->Signature) ) {
|
||
ExFreePool( pDriveLayout );
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// search partition list
|
||
//
|
||
for( PartitionIndex = 0;
|
||
PartitionIndex < pDriveLayout->PartitionCount;
|
||
PartitionIndex++ ) {
|
||
|
||
//
|
||
// Get the partition entry
|
||
//
|
||
pPartitionInfoEx = (&(pDriveLayout->PartitionEntry[PartitionIndex]));
|
||
|
||
if( PartitionStyle == PARTITION_STYLE_MBR ) {
|
||
if (pPartitionInfoEx->PartitionNumber == *pPartitionNumber) {
|
||
PartitionFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
if (IsEqualGUID( &(pPartitionInfoEx->Gpt.PartitionId),
|
||
&(pSignature->Guid) )) {
|
||
PartitionFound = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if( PartitionFound == TRUE ) {
|
||
break;
|
||
}
|
||
ExFreePool( pDriveLayout );
|
||
}
|
||
|
||
|
||
if( NT_SUCCESS( Status ) && ( PartitionFound == FALSE ) ) {
|
||
Status = STATUS_OBJECT_PATH_NOT_FOUND;
|
||
}
|
||
|
||
//
|
||
// Partition Found - copy the needed information
|
||
//
|
||
if( PartitionFound == TRUE ) {
|
||
*pPartitionNumber = pPartitionInfoEx->PartitionNumber;
|
||
*pDiskNumber = Index;
|
||
*pPartitionStart = pPartitionInfoEx->StartingOffset.QuadPart;
|
||
*pPartitionSize = pPartitionInfoEx->PartitionLength.QuadPart;
|
||
ExFreePool( pDriveLayout );
|
||
}
|
||
|
||
ExFreePool( pDeviceName );
|
||
return( Status );
|
||
|
||
} // ExpFindDiskSignature
|
||
|
||
|
||
NTSTATUS
|
||
ExpGetPartitionTableInfo (
|
||
IN PWSTR pDeviceName,
|
||
OUT PDRIVE_LAYOUT_INFORMATION_EX *ppDriveLayout
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING string;
|
||
OBJECT_ATTRIBUTES obja;
|
||
IO_STATUS_BLOCK iosb;
|
||
HANDLE handle;
|
||
PDRIVE_LAYOUT_INFORMATION_EX driveLayoutInfo = NULL;
|
||
ULONG driveLayoutLength;
|
||
|
||
//
|
||
// Open the disk and get its partition table information.
|
||
//
|
||
|
||
RtlInitUnicodeString(&string, pDeviceName);
|
||
|
||
InitializeObjectAttributes(
|
||
&obja,
|
||
&string,
|
||
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = ZwOpenFile(
|
||
&handle,
|
||
FILE_READ_ATTRIBUTES | SYNCHRONIZE,
|
||
&obja,
|
||
&iosb,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
FILE_NON_DIRECTORY_FILE
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
return status;
|
||
}
|
||
|
||
driveLayoutLength = FIELD_OFFSET(DRIVE_LAYOUT_INFORMATION_EX, PartitionEntry) +
|
||
(sizeof(PARTITION_INFORMATION_EX) * 16);
|
||
|
||
while (TRUE) {
|
||
|
||
driveLayoutInfo = ExAllocatePoolWithTag(NonPagedPool, driveLayoutLength, 'rvnE');
|
||
if (driveLayoutInfo == NULL ) {
|
||
ZwClose(handle);
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
status = ZwDeviceIoControlFile(
|
||
handle,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&iosb,
|
||
IOCTL_DISK_GET_DRIVE_LAYOUT_EX,
|
||
NULL,
|
||
0,
|
||
driveLayoutInfo,
|
||
driveLayoutLength
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
break;
|
||
}
|
||
|
||
ExFreePool(driveLayoutInfo);
|
||
if (status == STATUS_BUFFER_TOO_SMALL) {
|
||
driveLayoutLength *= 2;
|
||
continue;
|
||
}
|
||
ZwClose(handle);
|
||
return status;
|
||
}
|
||
|
||
*ppDriveLayout = driveLayoutInfo;
|
||
ZwClose(handle);
|
||
return status;
|
||
|
||
} // ExpGetPartitionTableInfo
|
||
|
||
#endif // defined(EFI_NVRAM_ENABLED)
|
||
|