mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6508 lines
184 KiB
6508 lines
184 KiB
/*++
|
|
|
|
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)
|
|
|