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.
2278 lines
73 KiB
2278 lines
73 KiB
#include "pch.h"
|
|
#include "resource.h"
|
|
#include "BootCfg.h"
|
|
#include "BootCfg64.h"
|
|
#include <strsafe.h>
|
|
|
|
//
|
|
// custom macros
|
|
//
|
|
#define NOT !
|
|
|
|
#define FORMAT_FILE_PATH L"signature(%s)"
|
|
#define FORMAT_FILE_PATH_EX L"signature({%s})"
|
|
|
|
//
|
|
// custom error codes
|
|
//
|
|
#define ERROR_PARTIAL_SUCCESS 0x40080001
|
|
#define ERROR_FAILED 0x40080002
|
|
|
|
//
|
|
// externs
|
|
//
|
|
extern LIST_ENTRY BootEntries;
|
|
extern LIST_ENTRY ActiveUnorderedBootEntries;
|
|
extern LIST_ENTRY InactiveUnorderedBootEntries;
|
|
|
|
//
|
|
// parameter / switches index
|
|
#define OI_CLONE_MAIN 0
|
|
#define OI_CLONE_SOURCE_GUID 1
|
|
#define OI_CLONE_TARGET_GUID 2
|
|
#define OI_CLONE_FRIENDLY_NAME_REPLACE 3
|
|
#define OI_CLONE_FRIENDLY_NAME_APPEND 4
|
|
#define OI_CLONE_BOOT_ID 5
|
|
#define OI_CLONE_DRIVER_UPDATE 6
|
|
#define OI_CLONE_HELP 7
|
|
#define OI_CLONE_COUNT 8
|
|
|
|
// switch names
|
|
#define OPTION_CLONE L"clone"
|
|
#define OPTION_CLONE_SOURCE_GUID L"sg"
|
|
#define OPTION_CLONE_TARGET_GUID L"tg"
|
|
#define OPTION_CLONE_FRIENDLY_NAME_REPLACE L"d"
|
|
#define OPTION_CLONE_FRIENDLY_NAME_APPEND L"d+"
|
|
#define OPTION_CLONE_BOOT_ID L"id"
|
|
#define OPTION_CLONE_HELP L"?"
|
|
#define OPTION_CLONE_DRIVER_UPDATE L"upddrv"
|
|
|
|
// default friendly name
|
|
#define DEFAULT_FRIENDLY_NAME GetResString2( IDS_CLONE_DEFAULT_FRIENDLY_NAME, 0 )
|
|
|
|
// resource strings
|
|
#define CLONE_ZERO_BOOT_ENTRIES GetResString2( IDS_CLONE_ZERO_BOOT_ENTRIES, 0 )
|
|
#define CLONE_RANGE_ZERO_BOOT_ENTRIES GetResString2( IDS_CLONE_RANGE_ZERO_BOOT_ENTRIES, 0 )
|
|
#define CLONE_SUCCESS GetResString2( IDS_CLONE_SUCCESS, 0 )
|
|
#define CLONE_FAILED GetResString2( IDS_CLONE_FAILED, 0 )
|
|
#define CLONE_PARTIAL GetResString2( IDS_CLONE_PARTIAL, 0 )
|
|
#define CLONE_INVALID_BOOT_ENTRY GetResString2( IDS_CLONE_INVALID_BOOT_ENTRY, 0 )
|
|
#define CLONE_ALREADY_EXISTS GetResString2( IDS_CLONE_ALREADY_EXISTS, 0 )
|
|
#define CLONE_BOOT_ENTRY_SUCCESS GetResString2( IDS_CLONE_BOOT_ENTRY_SUCCESS, 0 )
|
|
#define CLONE_INVALID_SOURCE_GUID GetResString2( IDS_CLONE_INVALID_SOURCE_GUID, 0 )
|
|
#define CLONE_INVALID_TARGET_GUID GetResString2( IDS_CLONE_INVALID_TARGET_GUID, 0 )
|
|
#define CLONE_INVALID_DRIVER_ENTRY GetResString2( IDS_CLONE_INVALID_DRIVER_ENTRY, 0 )
|
|
#define CLONE_DRIVER_ALREADY_EXISTS GetResString2( IDS_CLONE_DRIVER_ALREADY_EXISTS, 0 )
|
|
#define CLONE_ZERO_DRIVER_ENTRIES GetResString2( IDS_CLONE_ZERO_DRIVER_ENTRIES, 0 )
|
|
#define CLONE_DRIVER_ENTRY_SUCCESS GetResString2( IDS_CLONE_DRIVER_ENTRY_SUCCESS, 0 )
|
|
|
|
#define CLONE_DETAILED_TRACE GetResString2( IDS_CLONE_DETAILED_TRACE, 0 )
|
|
|
|
#define MSG_ERROR_INVALID_USAGE_REQUEST GetResString2( IDS_ERROR_INVALID_USAGE_REQUEST, 0 )
|
|
#define MSG_ERROR_INVALID_DESCRIPTION_COMBINATION GetResString2( IDS_ERROR_INVALID_DESCRIPTION_COMBINATION, 0 )
|
|
#define MSG_ERROR_INVALID_BOOT_ID_COMBINATION GetResString2( IDS_ERROR_INVALID_BOOT_ID_COMBINATION, 0 )
|
|
#define MSG_ERROR_INVALID_UPDDRV_COMBINATION GetResString2( IDS_ERROR_INVALID_UPDDRV_COMBINATION, 0 )
|
|
#define MSG_ERROR_NO_SGUID_WITH_UPDDRV GetResString2( IDS_ERROR_NO_SGUID_WITH_UPDDRV, 0 )
|
|
|
|
//
|
|
// internal structure
|
|
//
|
|
typedef struct __tagCloneParameters
|
|
{
|
|
BOOL bUsage;
|
|
LONG lBootId;
|
|
BOOL bVerbose;
|
|
BOOL bDriverUpdate;
|
|
LPWSTR pwszSourcePath;
|
|
LPWSTR pwszTargetPath;
|
|
LPWSTR pwszSourceGuid;
|
|
LPWSTR pwszTargetGuid;
|
|
LPWSTR pwszFriendlyName;
|
|
DWORD dwFriendlyNameType;
|
|
} TCLONE_PARAMS, *PTCLONE_PARAMS;
|
|
|
|
//
|
|
// enum's
|
|
//
|
|
enum {
|
|
BOOTENTRY_FRIENDLYNAME_NONE = 0,
|
|
BOOTENTRY_FRIENDLYNAME_APPEND, BOOTENTRY_FRIENDLYNAME_REPLACE
|
|
};
|
|
|
|
//
|
|
// prototypes
|
|
//
|
|
|
|
// parser
|
|
DWORD DisplayCloneHelp();
|
|
DWORD ProcessOptions( DWORD argc, LPCWSTR argv[], PTCLONE_PARAMS pParams );
|
|
|
|
// helper functions
|
|
DWORD TranslateEFIPathToNTPath( LPCWSTR pwszGUID, LPVOID* pwszPath );
|
|
BOOL MatchPath( PFILE_PATH pfpSource, LPCWSTR pwszDevicePath, LPCWSTR pwszFilePath );
|
|
DWORD PrepareCompleteEFIPath( PFILE_PATH pfpSource,
|
|
LPCWSTR pwszDevicePath,
|
|
LPWSTR* pwszEFIPath, DWORD* pdwLength );
|
|
|
|
// efi driver cloners
|
|
DWORD LoadDriverEntries( PEFI_DRIVER_ENTRY_LIST* ppDriverEntries );
|
|
LONG FindDriverEntryWithTargetEFI( PEFI_DRIVER_ENTRY_LIST pdeList, DWORD dwSourceIndex,
|
|
PEFI_DRIVER_ENTRY pdeSource, LPCWSTR pwszDevicePath );
|
|
DWORD DoDriverEntryClone( PEFI_DRIVER_ENTRY_LIST pbeList,
|
|
LPCWSTR pwszSourceEFI, LPCWSTR pwszTargetEFI,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType, BOOL bVerbose );
|
|
DWORD CloneDriverEntry( PEFI_DRIVER_ENTRY pbeSource, LPCWSTR pwszEFIPath,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType );
|
|
|
|
// boot entry cloners
|
|
DWORD CloneBootEntry( PBOOT_ENTRY pbeSource, LPCWSTR pwszEFIPath,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType );
|
|
DWORD DoBootEntryClone( PBOOT_ENTRY_LIST pbeList, LPCWSTR pwszSourceEFI,
|
|
LPCWSTR pwszTargetEFI, LONG lIndexFrom, LONG lIndexTo,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType, BOOL bVerbose );
|
|
|
|
//
|
|
// functionality
|
|
//
|
|
|
|
DWORD ProcessCloneSwitch_IA64( IN DWORD argc, IN LPCTSTR argv[] )
|
|
{
|
|
//
|
|
// local variables
|
|
NTSTATUS status;
|
|
DWORD dwResult = 0;
|
|
DWORD dwLength = 0;
|
|
DWORD dwExitCode = 0;
|
|
TCLONE_PARAMS paramsClone;
|
|
BOOLEAN wasEnabled = FALSE;
|
|
PEFI_DRIVER_ENTRY_LIST pDriverEntries = NULL;
|
|
|
|
// init to zero's
|
|
ZeroMemory( ¶msClone, sizeof( TCLONE_PARAMS ) );
|
|
|
|
// process the command line options
|
|
dwResult = ProcessOptions( argc, argv, ¶msClone );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// display one blank line -- for clarity purpose
|
|
ShowMessage( stderr, L"\n" );
|
|
|
|
// ...
|
|
dwExitCode = 1;
|
|
ShowLastErrorEx( stderr, SLE_ERROR | SLE_INTERNAL );
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
// check if user requested for help
|
|
if ( paramsClone.bUsage == TRUE )
|
|
{
|
|
// display usage for this option
|
|
DisplayCloneHelp();
|
|
dwExitCode = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
// display one blank line -- for clarity purpose
|
|
ShowMessage( stderr, L"\n" );
|
|
|
|
// initialize the EFI -- only if user specifies the index
|
|
if ( paramsClone.bDriverUpdate == FALSE )
|
|
{
|
|
dwResult = InitializeEFI();
|
|
|
|
// check the result of load operation
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// NOTE: message will be displayed in the associated funtions itself
|
|
dwExitCode = 1;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else if ( paramsClone.bDriverUpdate == TRUE )
|
|
{
|
|
//
|
|
// load the drivers
|
|
|
|
// enable the privilege that is necessary to query/set NVRAM.
|
|
status = RtlAdjustPrivilege( SE_SYSTEM_ENVIRONMENT_PRIVILEGE, TRUE, FALSE, &wasEnabled );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwExitCode = 1;
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
SetLastError( dwResult );
|
|
ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_SYSTEM );
|
|
goto cleanup;
|
|
}
|
|
|
|
// load the drivers now
|
|
dwResult = LoadDriverEntries( &pDriverEntries );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
dwExitCode = 1;
|
|
SetLastError( dwResult );
|
|
ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_SYSTEM );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// translate the source guid path into NT path - if needed
|
|
if ( paramsClone.pwszSourceGuid != NULL )
|
|
{
|
|
dwResult = TranslateEFIPathToNTPath(
|
|
paramsClone.pwszSourceGuid, ¶msClone.pwszSourcePath );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
dwExitCode = 1;
|
|
ShowMessage( stderr, CLONE_INVALID_SOURCE_GUID );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// translate the target guid into NT path
|
|
dwResult = TranslateEFIPathToNTPath(
|
|
paramsClone.pwszTargetGuid, ¶msClone.pwszTargetPath );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
dwExitCode = 1;
|
|
ShowMessage( stderr, CLONE_INVALID_TARGET_GUID );
|
|
goto cleanup;
|
|
}
|
|
|
|
// actual operation ...
|
|
if ( paramsClone.bDriverUpdate == FALSE )
|
|
{
|
|
// by default, if user did not specify the friendly name, we will assume
|
|
// that users wants to the append the default string viz. "(clone)"
|
|
if ( paramsClone.pwszFriendlyName == NULL )
|
|
{
|
|
// determine the length of the default friendly name
|
|
// and allocate buffer with length
|
|
dwLength = StringLength( DEFAULT_FRIENDLY_NAME, 0 ) + 2;
|
|
paramsClone.pwszFriendlyName = AllocateMemory( dwLength + 5 );
|
|
if ( paramsClone.pwszFriendlyName == NULL )
|
|
{
|
|
dwExitCode = 1;
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_SYSTEM );
|
|
goto cleanup;
|
|
}
|
|
|
|
// copy the default string into this buffer and
|
|
// change the friednly name type to append
|
|
paramsClone.dwFriendlyNameType = BOOTENTRY_FRIENDLYNAME_APPEND;
|
|
StringCopy( paramsClone.pwszFriendlyName, DEFAULT_FRIENDLY_NAME, dwLength );
|
|
}
|
|
|
|
// do the boot entry cloning
|
|
dwResult = DoBootEntryClone(
|
|
NULL, paramsClone.pwszSourcePath,
|
|
paramsClone.pwszTargetPath, paramsClone.lBootId, -1,
|
|
paramsClone.pwszFriendlyName, paramsClone.dwFriendlyNameType, paramsClone.bVerbose );
|
|
}
|
|
else if ( paramsClone.bDriverUpdate == TRUE )
|
|
{
|
|
// do the driver cloning
|
|
dwResult = DoDriverEntryClone(
|
|
pDriverEntries, paramsClone.pwszSourcePath, paramsClone.pwszTargetPath,
|
|
paramsClone.pwszFriendlyName, paramsClone.dwFriendlyNameType, paramsClone.bVerbose );
|
|
}
|
|
|
|
// determine the exit code based on the error code
|
|
switch( dwResult )
|
|
{
|
|
case ERROR_SUCCESS:
|
|
dwExitCode = 0;
|
|
break;
|
|
|
|
case ERROR_FAILED:
|
|
dwExitCode = 1;
|
|
break;
|
|
|
|
case ERROR_PARTIAL_SUCCESS:
|
|
dwExitCode = 0;
|
|
break;
|
|
|
|
default:
|
|
// can never oocur
|
|
dwExitCode = 1;
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
// release the memory
|
|
FreeMemory( &pDriverEntries );
|
|
FreeMemory( ¶msClone.pwszSourcePath );
|
|
FreeMemory( ¶msClone.pwszTargetPath );
|
|
FreeMemory( ¶msClone.pwszSourceGuid );
|
|
FreeMemory( ¶msClone.pwszTargetGuid );
|
|
FreeMemory( ¶msClone.pwszFriendlyName );
|
|
|
|
// return
|
|
return dwExitCode;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// boot entries specific implementation
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD DoBootEntryClone( PBOOT_ENTRY_LIST pbeList, LPCWSTR pwszSourceEFI,
|
|
LPCWSTR pwszTargetEFI, LONG lIndexFrom, LONG lIndexTo,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType, BOOL bVerbose )
|
|
{
|
|
// local variables
|
|
DWORD dwResult = 0;
|
|
BOOL bClone = FALSE;
|
|
LONG lCurrentIndex = 0;
|
|
BOOL bExitFromLoop = FALSE;
|
|
PLIST_ENTRY pBootList = NULL;
|
|
PBOOT_ENTRY pBootEntry = NULL;
|
|
PFILE_PATH pfpBootFilePath = NULL;
|
|
PMY_BOOT_ENTRY pMyBootEntry = NULL;
|
|
DWORD dwAttempted = 0, dwFailed = 0;
|
|
|
|
//
|
|
// check the input parameter
|
|
//
|
|
if ( pwszTargetEFI == NULL || (lIndexTo != -1 && lIndexFrom > lIndexTo) ||
|
|
(dwFriendlyNameType != BOOTENTRY_FRIENDLYNAME_NONE && pwszFriendlyName == NULL) )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// pbeList is a unreferenced parameter
|
|
UNREFERENCED_PARAMETER( pbeList );
|
|
|
|
// if the 'to' index is not specified, then we treat the 'to' index to match
|
|
// with the 'from' index
|
|
if ( lIndexFrom != -1 && lIndexTo == -1 )
|
|
{
|
|
lIndexTo = lIndexFrom;
|
|
}
|
|
|
|
// traverse thru the list of boot entries
|
|
for( pBootList = BootEntries.Flink; pBootList != &BootEntries; pBootList = pBootList->Flink )
|
|
{
|
|
// increment the loop counter
|
|
bClone = FALSE;
|
|
lCurrentIndex = -1;
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
// get the boot entry
|
|
pMyBootEntry = CONTAINING_RECORD( pBootList, MY_BOOT_ENTRY, ListEntry );
|
|
if( NOT MBE_IS_NT( pMyBootEntry ) )
|
|
{
|
|
// this is not a valid boot entry we are looking for skip
|
|
continue;
|
|
}
|
|
|
|
// extract the boot id and actual boot entry
|
|
lCurrentIndex = pMyBootEntry->myId;
|
|
pBootEntry = &pMyBootEntry->NtBootEntry;
|
|
|
|
// check if the current boot index falls within the index or not
|
|
if ( lIndexFrom != -1 )
|
|
{
|
|
bClone = (lCurrentIndex >= lIndexFrom && lCurrentIndex <= lIndexTo);
|
|
}
|
|
|
|
// extended filtering
|
|
if ( pwszSourceEFI != NULL )
|
|
{
|
|
if ( lIndexFrom == -1 || (lIndexFrom != -1 && bClone == TRUE) )
|
|
{
|
|
// extract the boot file path
|
|
pfpBootFilePath = (PFILE_PATH) ADD_OFFSET( pBootEntry, BootFilePathOffset );
|
|
|
|
// check whether it matches or not
|
|
bClone = MatchPath( pfpBootFilePath, pwszSourceEFI, NULL );
|
|
}
|
|
}
|
|
|
|
// clone the boot entry -- only if filtering results in TRUE
|
|
bExitFromLoop = FALSE;
|
|
if ( bClone == TRUE || (pwszSourceEFI == NULL && lIndexFrom == -1) )
|
|
{
|
|
// increment the attempted list
|
|
dwAttempted++;
|
|
|
|
// do the operation
|
|
dwResult = CloneBootEntry( pBootEntry,
|
|
pwszTargetEFI, pwszFriendlyName, dwFriendlyNameType );
|
|
|
|
// check the result
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// increment the failures count
|
|
dwFailed++;
|
|
|
|
// check whether this particular instance of operation is target for
|
|
// multiple entries or single entry
|
|
if ( lIndexFrom == -1 || ((lIndexTo - lIndexFrom + 1) > 1) )
|
|
{
|
|
// check the severity for the error occured
|
|
switch( dwResult )
|
|
{
|
|
case STG_E_UNKNOWN: // unknown error -- unrecoverable
|
|
case ERROR_INVALID_PARAMETER: // code error
|
|
case ERROR_NOT_ENOUGH_MEMORY: // unrecovarable case
|
|
{
|
|
bExitFromLoop = TRUE;
|
|
break;
|
|
}
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
{
|
|
// duplicate boot entry
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
ShowMessageEx( stdout, 1, TRUE, CLONE_ALREADY_EXISTS, lCurrentIndex );
|
|
}
|
|
|
|
// ...
|
|
dwResult = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
{
|
|
// dont know how to handle this case
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
SetLastError( dwResult );
|
|
SaveLastError();
|
|
ShowMessageEx( stdout, 2, TRUE, CLONE_INVALID_BOOT_ENTRY, lCurrentIndex, GetReason() );
|
|
}
|
|
|
|
// ...
|
|
dwResult = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// since this is a single entry clone operation
|
|
// break from the loop
|
|
bExitFromLoop = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
ShowMessageEx( stdout, 1, TRUE, CLONE_BOOT_ENTRY_SUCCESS, lCurrentIndex );
|
|
}
|
|
}
|
|
}
|
|
|
|
// exit from the loop - if needed
|
|
if ( bExitFromLoop == TRUE )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
// check the result of the operation
|
|
if ( dwResult == ERROR_SUCCESS )
|
|
{
|
|
if ( dwAttempted == 0 )
|
|
{
|
|
// no boot entries at all
|
|
dwResult = ERROR_FAILED;
|
|
if ( lIndexFrom == -1 )
|
|
{
|
|
ShowMessage( stdout, CLONE_ZERO_BOOT_ENTRIES );
|
|
}
|
|
else
|
|
{
|
|
ShowMessage( stdout, CLONE_RANGE_ZERO_BOOT_ENTRIES );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// verify whether all requested boot entries are processed or not
|
|
if ( lIndexFrom != -1 && dwAttempted != (lIndexTo - lIndexFrom + 1) )
|
|
{
|
|
// warning - not all boot entries were parsed -- invalid bounds were specified
|
|
|
|
//
|
|
// NOTE: in the current implementation, this can never occur
|
|
// this is because, the input parameters for this option does accept
|
|
// only the /id which will be treated as 'lIndexStart'
|
|
//
|
|
}
|
|
|
|
if ( dwFailed == 0 )
|
|
{
|
|
// nothing failed -- success
|
|
dwResult = ERROR_SUCCESS;
|
|
SetLastError( ERROR_SUCCESS );
|
|
ShowLastErrorEx( stdout, SLE_TYPE_SUCCESS | SLE_SYSTEM );
|
|
}
|
|
else if ( dwAttempted == dwFailed )
|
|
{
|
|
// nothing succeeded -- completely failed
|
|
dwResult = ERROR_FAILED;
|
|
ShowMessage( stderr, CLONE_FAILED );
|
|
|
|
// show verbose hint
|
|
if ( bVerbose == FALSE )
|
|
{
|
|
ShowMessage( stderr, CLONE_DETAILED_TRACE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// parital success
|
|
dwResult = ERROR_PARTIAL_SUCCESS;
|
|
ShowMessage( stderr, CLONE_PARTIAL );
|
|
|
|
// show verbose hint
|
|
if ( bVerbose == FALSE )
|
|
{
|
|
ShowMessage( stderr, CLONE_DETAILED_TRACE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check the reason for failure
|
|
switch( dwResult )
|
|
{
|
|
case STG_E_UNKNOWN: // unknown error -- unrecoverable
|
|
case ERROR_INVALID_PARAMETER: // code error
|
|
case ERROR_NOT_ENOUGH_MEMORY: // unrecovarable case
|
|
{
|
|
SetLastError( dwResult );
|
|
ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_SYSTEM );
|
|
break;
|
|
}
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
{
|
|
ShowMessageEx( stdout, 1, TRUE, CLONE_ALREADY_EXISTS, lIndexFrom );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
{
|
|
SetLastError( dwResult );
|
|
SaveLastError();
|
|
ShowMessageEx( stdout, 2, TRUE, CLONE_INVALID_BOOT_ENTRY, lIndexFrom, GetReason() );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// error code
|
|
dwResult = ERROR_FAILED;
|
|
}
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
DWORD CloneBootEntry( PBOOT_ENTRY pbeSource, LPCWSTR pwszEFIPath,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// friendly name
|
|
DWORD dwFriendlyNameLength = 0;
|
|
LPWSTR pwszTargetFriendlyName = NULL;
|
|
LPCWSTR pwszSourceFriendlyName = NULL;
|
|
|
|
// os options
|
|
DWORD dwOsOptionsLength = 0;
|
|
PWINDOWS_OS_OPTIONS pOsOptions = NULL;
|
|
|
|
// boot file path
|
|
DWORD dwEFIPathLength = 0;
|
|
DWORD dwBootFilePathLength = 0;
|
|
PFILE_PATH pfpBootFilePath = NULL;
|
|
LPWSTR pwszFullEFIPath = NULL;
|
|
|
|
// boot entry
|
|
ULONG ulId = 0;
|
|
ULONG ulIdCount = 0;
|
|
ULONG* pulIdsArray = NULL;
|
|
DWORD dwBootEntryLength = 0;
|
|
PBOOT_ENTRY pBootEntry = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the input
|
|
if ( pbeSource == NULL || pwszEFIPath == NULL ||
|
|
(dwFriendlyNameType != BOOTENTRY_FRIENDLYNAME_NONE && pwszFriendlyName == NULL) )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// validate the boot file in the source boot entry
|
|
//
|
|
|
|
// extract the boot file path
|
|
pfpBootFilePath = (PFILE_PATH) ADD_OFFSET( pbeSource, BootFilePathOffset );
|
|
|
|
// attempt to translate the file path
|
|
status = NtTranslateFilePath( pfpBootFilePath, FILE_PATH_TYPE_NT, NULL, &dwBootFilePathLength );
|
|
|
|
// reset the pBootFilePath and dwBootFilePathLength variables
|
|
pfpBootFilePath = NULL;
|
|
dwBootFilePathLength = 0;
|
|
|
|
// now verify the result of the translation
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
if ( status == STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
// source boot entry is a valid one
|
|
dwResult = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// prepare "friendly name"
|
|
//
|
|
|
|
// determine the source friendly name and its length
|
|
dwFriendlyNameLength = 0;
|
|
pwszSourceFriendlyName = NULL;
|
|
switch( dwFriendlyNameType )
|
|
{
|
|
case BOOTENTRY_FRIENDLYNAME_NONE:
|
|
case BOOTENTRY_FRIENDLYNAME_APPEND:
|
|
{
|
|
pwszSourceFriendlyName = (LPCWSTR) ADD_OFFSET( pbeSource, FriendlyNameOffset );
|
|
dwFriendlyNameLength = StringLengthW( pwszSourceFriendlyName, 0 ) + 1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
|
|
// add the length of the friendly name that needs to be added -- if exists
|
|
if ( pwszFriendlyName != NULL )
|
|
{
|
|
dwFriendlyNameLength += StringLengthW( pwszFriendlyName, 0 ) + 1;
|
|
}
|
|
|
|
// allocate memory for the friendly name
|
|
pwszTargetFriendlyName = (LPWSTR) AllocateMemory( (dwFriendlyNameLength + 1) * sizeof( WCHAR ) );
|
|
if ( pwszTargetFriendlyName == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// prepare the friendly name
|
|
StringCopyW( pwszTargetFriendlyName, L"", dwFriendlyNameLength );
|
|
|
|
// ...
|
|
if ( pwszSourceFriendlyName != NULL )
|
|
{
|
|
StringConcat( pwszTargetFriendlyName, pwszSourceFriendlyName, dwFriendlyNameLength );
|
|
}
|
|
|
|
// ...
|
|
if ( pwszFriendlyName != NULL )
|
|
{
|
|
// add one space b/w the existing and concatenating string
|
|
if ( pwszSourceFriendlyName != NULL )
|
|
{
|
|
StringConcat( pwszTargetFriendlyName, L" ", dwFriendlyNameLength );
|
|
}
|
|
|
|
// ...
|
|
StringConcat( pwszTargetFriendlyName, pwszFriendlyName, dwFriendlyNameLength );
|
|
}
|
|
|
|
//
|
|
// prepare "OS Options"
|
|
//
|
|
// NOTE:
|
|
// -----
|
|
// though the os options are NULL, it will still consume some space (refer the structre of
|
|
// BOOT_ENTRY -- that is the reason why the default length of OS OPTIONS is ANYSIZE_ARRAY)
|
|
pOsOptions = NULL;
|
|
dwOsOptionsLength = ANYSIZE_ARRAY;
|
|
if ( pbeSource->OsOptionsLength != 0 )
|
|
{
|
|
// allocate memory for os options
|
|
dwOsOptionsLength = pbeSource->OsOptionsLength;
|
|
pOsOptions = (PWINDOWS_OS_OPTIONS) AllocateMemory( dwOsOptionsLength );
|
|
if ( pOsOptions == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// copy the contents
|
|
CopyMemory( pOsOptions, &pbeSource->OsOptions, dwOsOptionsLength );
|
|
}
|
|
|
|
//
|
|
// boot file path
|
|
//
|
|
|
|
// the 'FilePath' variable in FILE_PATH variable contains two variables
|
|
// each seperated by a NULL terminated character
|
|
// the first part designates the DEVICE PATH
|
|
// and the second part designated the DIRECTORY / FILE PATH
|
|
// we already have the device path (pwszEFIPath) -- but we need to get the
|
|
// DIRECTORY / FILE PATH -- this we will get from the source boot entry
|
|
pfpBootFilePath = (PFILE_PATH) ADD_OFFSET( pbeSource, BootFilePathOffset );
|
|
dwResult = PrepareCompleteEFIPath( pfpBootFilePath, pwszEFIPath, &pwszFullEFIPath, &dwEFIPathLength );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// since the memory reference in pBootFilePath is not allocated
|
|
// in this function, it is important to reset the pointer to NULL
|
|
// this avoids the crash in the program
|
|
pfpBootFilePath = NULL;
|
|
|
|
// ...
|
|
goto cleanup;
|
|
}
|
|
|
|
// now determine the memory size that needs to be allocated for FILE_PATH structure
|
|
// and align up to the even memory bounday
|
|
pfpBootFilePath = NULL;
|
|
dwBootFilePathLength = FIELD_OFFSET(FILE_PATH, FilePath) + (dwEFIPathLength * sizeof( WCHAR ));
|
|
|
|
// allocate memory
|
|
pfpBootFilePath = (PFILE_PATH) AllocateMemory( dwBootFilePathLength );
|
|
if ( pfpBootFilePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// initialize the file path structure
|
|
ZeroMemory( pfpBootFilePath, dwBootFilePathLength );
|
|
pfpBootFilePath->Length = dwBootFilePathLength;
|
|
pfpBootFilePath->Type = FILE_PATH_TYPE_NT;
|
|
pfpBootFilePath->Version = FILE_PATH_VERSION;
|
|
CopyMemory( pfpBootFilePath->FilePath, pwszFullEFIPath, dwEFIPathLength * sizeof( WCHAR ) );
|
|
|
|
//
|
|
// finally, create the boot entry
|
|
//
|
|
|
|
// determine the size for the BOOT_ENTRY structure
|
|
dwBootEntryLength = FIELD_OFFSET( BOOT_ENTRY, OsOptions ) +
|
|
dwOsOptionsLength +
|
|
(dwFriendlyNameLength * sizeof(WCHAR)) +
|
|
dwBootFilePathLength +
|
|
sizeof(WCHAR) + // align the FriendlyName on WCHAR
|
|
sizeof(DWORD); // align the BootFilePath on DWORD
|
|
|
|
// allocate memory
|
|
pBootEntry = (PBOOT_ENTRY) AllocateMemory( dwBootEntryLength );
|
|
if ( pBootEntry == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
ZeroMemory( pBootEntry, dwBootEntryLength );
|
|
pBootEntry->Id = 0L;
|
|
pBootEntry->Length = dwBootEntryLength;
|
|
pBootEntry->Version = BOOT_ENTRY_VERSION;
|
|
pBootEntry->OsOptionsLength = dwOsOptionsLength;
|
|
pBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_DEFAULT;
|
|
|
|
// align the friendly name on WCHR boundary
|
|
pBootEntry->FriendlyNameOffset =
|
|
ALIGN_UP( FIELD_OFFSET(BOOT_ENTRY, OsOptions) + dwOsOptionsLength, WCHAR );
|
|
|
|
// align the boot file path on DWORD boundary
|
|
pBootEntry->BootFilePathOffset =
|
|
ALIGN_UP( pBootEntry->FriendlyNameOffset + (dwFriendlyNameLength * sizeof(WCHAR)), DWORD );
|
|
|
|
// fill the boot entry
|
|
CopyMemory( pBootEntry->OsOptions, pOsOptions, dwOsOptionsLength );
|
|
CopyMemory( ADD_OFFSET( pBootEntry, BootFilePathOffset ), pfpBootFilePath, dwBootFilePathLength );
|
|
CopyMemory(
|
|
ADD_OFFSET( pBootEntry, FriendlyNameOffset ),
|
|
pwszTargetFriendlyName, (dwFriendlyNameLength * sizeof(WCHAR) ) );
|
|
|
|
//
|
|
// add the prepared boot entry
|
|
//
|
|
status = NtAddBootEntry( pBootEntry, &ulId );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Add the entry to the boot order.
|
|
//
|
|
ulIdCount = 32L;
|
|
pulIdsArray = (PULONG) AllocateMemory( ulIdCount * sizeof(ULONG) );
|
|
if ( pulIdsArray == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// query the boot entry order
|
|
// NOTE: we will doing error check for this function call bit later
|
|
status = NtQueryBootEntryOrder( pulIdsArray, &ulIdCount );
|
|
|
|
// need room in the buffer for the new entry.
|
|
if ( 31L < ulIdCount )
|
|
{
|
|
// release the current memory allocation for id's
|
|
FreeMemory( &pulIdsArray );
|
|
|
|
// allocate new memory and query again
|
|
pulIdsArray = (PULONG) AllocateMemory( (ulIdCount+1) * sizeof(ULONG));
|
|
if ( pulIdsArray == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
status = NtQueryBootEntryOrder( pulIdsArray, &ulIdCount );
|
|
}
|
|
|
|
// check the result of the boot entries query operation
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
// set the boot entry order
|
|
ulIdCount++;
|
|
*(pulIdsArray + (ulIdCount - 1)) = ulId;
|
|
status = NtSetBootEntryOrder( pulIdsArray, ulIdCount );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
// success
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
// release the memory allocated
|
|
FreeMemory( &pOsOptions );
|
|
FreeMemory( &pBootEntry );
|
|
FreeMemory( &pulIdsArray );
|
|
FreeMemory( &pfpBootFilePath );
|
|
FreeMemory( &pwszFullEFIPath );
|
|
FreeMemory( &pwszTargetFriendlyName );
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// efi drivers specific implementation
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD LoadDriverEntries( PEFI_DRIVER_ENTRY_LIST* ppDriverEntries )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwSize = 0;
|
|
BOOL bSecondChance = FALSE;
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
const DWORD dwDefaultSize = 1024;
|
|
PEFI_DRIVER_ENTRY_LIST pDriverEntries = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the input
|
|
if ( ppDriverEntries == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// default size is assumed as 1024 bytes
|
|
bSecondChance = FALSE;
|
|
dwSize = dwDefaultSize;
|
|
|
|
try_again:
|
|
|
|
// allocate memory
|
|
pDriverEntries = AllocateMemory( dwSize );
|
|
if ( pDriverEntries == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// try to get the boot entries
|
|
status = NtEnumerateDriverEntries( pDriverEntries, &dwSize );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
// release the memory that is allocated for target path structure
|
|
FreeMemory( &pDriverEntries );
|
|
|
|
// check the error
|
|
if ( status == STATUS_BUFFER_TOO_SMALL && bSecondChance == FALSE )
|
|
{
|
|
// give a second try
|
|
bSecondChance = TRUE;
|
|
goto try_again;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// operation is succes
|
|
dwResult = ERROR_SUCCESS;
|
|
*ppDriverEntries = pDriverEntries;
|
|
|
|
cleanup:
|
|
|
|
// need to release memory allocated for Driver entries in this function
|
|
// should do this only in case of failure
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
FreeMemory( &pDriverEntries );
|
|
}
|
|
|
|
// return the result
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
DWORD DoDriverEntryClone( PEFI_DRIVER_ENTRY_LIST pdeList,
|
|
LPCWSTR pwszSourceEFI, LPCWSTR pwszTargetEFI,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType, BOOL bVerbose )
|
|
{
|
|
// local variables
|
|
LONG lLoop = 0;
|
|
DWORD dwResult = 0;
|
|
BOOL bClone = FALSE;
|
|
BOOL bExitFromLoop = FALSE;
|
|
LPCWSTR pwszDriverName = NULL;
|
|
PFILE_PATH pfpDriverFilePath = NULL;
|
|
PEFI_DRIVER_ENTRY pDriverEntry = NULL;
|
|
PEFI_DRIVER_ENTRY_LIST pdeMasterList = NULL;
|
|
DWORD dwAttempted = 0, dwFailed = 0;
|
|
|
|
//
|
|
// check the input parameter
|
|
//
|
|
if ( pdeList == NULL || pwszSourceEFI == NULL || pwszTargetEFI == NULL ||
|
|
(dwFriendlyNameType != BOOTENTRY_FRIENDLYNAME_NONE && pwszFriendlyName == NULL) )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// currently, the pwszFriendlyName is not considered -- so it should be NULL
|
|
if ( pwszFriendlyName != NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// traverse thru the list of driver entries
|
|
lLoop = 0;
|
|
bExitFromLoop = FALSE;
|
|
pdeMasterList = pdeList; // save the pointer the original drivers list
|
|
while ( bExitFromLoop == FALSE )
|
|
{
|
|
// increment the loop counter
|
|
lLoop++;
|
|
bClone = FALSE;
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
// get the reference to the current driver entry
|
|
pDriverEntry = &pdeList->DriverEntry;
|
|
if ( pDriverEntry == NULL )
|
|
{
|
|
// should never occur
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
bExitFromLoop = TRUE;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// check whether the current driver's device matches with the requested path
|
|
//
|
|
|
|
// extract the driver file path
|
|
pfpDriverFilePath = (PFILE_PATH) ADD_OFFSET( pDriverEntry, DriverFilePathOffset );
|
|
|
|
// check whether it matches or not
|
|
bClone = MatchPath( pfpDriverFilePath, pwszSourceEFI, NULL );
|
|
|
|
// clone the boot entry -- only if filtering results in TRUE
|
|
if ( bClone == TRUE )
|
|
{
|
|
// updated the attempted count
|
|
dwAttempted++;
|
|
|
|
// get the driver name (it is nothing but the friendly name)
|
|
pwszDriverName = (LPCWSTR) ADD_OFFSET( pDriverEntry, FriendlyNameOffset );
|
|
|
|
// do the operation
|
|
// but before proceeding confirm that this particular
|
|
// driver entry is not existing
|
|
if ( FindDriverEntryWithTargetEFI( pdeMasterList, lLoop,
|
|
pDriverEntry, pwszTargetEFI ) == -1 )
|
|
{
|
|
dwResult = CloneDriverEntry( pDriverEntry,
|
|
pwszTargetEFI, pwszFriendlyName, dwFriendlyNameType );
|
|
}
|
|
else
|
|
{
|
|
dwResult = ERROR_ALREADY_EXISTS;
|
|
}
|
|
|
|
// check the result
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// update the failed count
|
|
dwFailed++;
|
|
|
|
// check the severity for the error occured
|
|
switch( dwResult )
|
|
{
|
|
case STG_E_UNKNOWN: // unknown error -- unrecoverable
|
|
case ERROR_INVALID_PARAMETER: // code error
|
|
case ERROR_NOT_ENOUGH_MEMORY: // unrecovarable case
|
|
{
|
|
bExitFromLoop = TRUE;
|
|
break;
|
|
}
|
|
|
|
case ERROR_ALREADY_EXISTS:
|
|
{
|
|
// duplicate boot entry
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
ShowMessageEx( stdout, 1, TRUE, CLONE_DRIVER_ALREADY_EXISTS, pwszDriverName );
|
|
}
|
|
|
|
// ...
|
|
dwResult = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
{
|
|
// dont know how to handle this case
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
SetLastError( dwResult );
|
|
SaveLastError();
|
|
ShowMessageEx( stdout, 2, TRUE, CLONE_INVALID_DRIVER_ENTRY, pwszDriverName, GetReason() );
|
|
}
|
|
|
|
// ...
|
|
dwResult = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( bVerbose == TRUE )
|
|
{
|
|
ShowMessageEx( stdout, 1, TRUE, CLONE_DRIVER_ENTRY_SUCCESS, pwszDriverName );
|
|
}
|
|
}
|
|
}
|
|
|
|
// fetch the next pointer
|
|
// do this only if the error the bExitFromLoop is not set in above blocks
|
|
if ( bExitFromLoop == FALSE )
|
|
{
|
|
bExitFromLoop = (pdeList->NextEntryOffset == 0);
|
|
pdeList = (PEFI_DRIVER_ENTRY_LIST) ADD_OFFSET( pdeList, NextEntryOffset );
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
// check the result of the operation
|
|
if ( dwResult == ERROR_SUCCESS )
|
|
{
|
|
if ( dwAttempted == 0 )
|
|
{
|
|
// no driver entries at all
|
|
dwResult = ERROR_FAILED;
|
|
ShowMessage( stdout, CLONE_ZERO_DRIVER_ENTRIES );
|
|
}
|
|
else
|
|
{
|
|
if ( dwFailed == 0 )
|
|
{
|
|
// nothing failed -- success
|
|
dwResult = ERROR_SUCCESS;
|
|
SetLastError( ERROR_SUCCESS );
|
|
ShowLastErrorEx( stdout, SLE_TYPE_SUCCESS | SLE_SYSTEM );
|
|
}
|
|
else if ( dwAttempted == dwFailed )
|
|
{
|
|
// nothing succeeded -- completely failed
|
|
dwResult = ERROR_FAILED;
|
|
ShowMessage( stderr, CLONE_FAILED );
|
|
|
|
// show verbose hint
|
|
if ( bVerbose == FALSE )
|
|
{
|
|
ShowMessage( stderr, CLONE_DETAILED_TRACE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// parital success
|
|
dwResult = ERROR_PARTIAL_SUCCESS;
|
|
ShowMessage( stderr, CLONE_PARTIAL );
|
|
|
|
// show verbose hint
|
|
if ( bVerbose == FALSE )
|
|
{
|
|
ShowMessage( stderr, CLONE_DETAILED_TRACE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check the reason for failure
|
|
switch( dwResult )
|
|
{
|
|
default:
|
|
case STG_E_UNKNOWN: // unknown error -- unrecoverable
|
|
case ERROR_INVALID_PARAMETER: // code error
|
|
case ERROR_NOT_ENOUGH_MEMORY: // unrecovarable case
|
|
{
|
|
SetLastError( dwResult );
|
|
ShowLastErrorEx( stderr, SLE_TYPE_ERROR | SLE_SYSTEM );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// error code
|
|
dwResult = ERROR_FAILED;
|
|
}
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
DWORD CloneDriverEntry( PEFI_DRIVER_ENTRY pdeSource, LPCWSTR pwszEFIPath,
|
|
LPCWSTR pwszFriendlyName, DWORD dwFriendlyNameType )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// friendly name
|
|
DWORD dwFriendlyNameLength = 0;
|
|
LPWSTR pwszTargetFriendlyName = NULL;
|
|
LPCWSTR pwszSourceFriendlyName = NULL;
|
|
|
|
// driver file path
|
|
DWORD dwEFIPathLength = 0;
|
|
DWORD dwDriverFilePathLength = 0;
|
|
PFILE_PATH pfpDriverFilePath = NULL;
|
|
LPWSTR pwszFullEFIPath = NULL;
|
|
|
|
// driver entry
|
|
ULONG ulId = 0;
|
|
ULONG ulIdCount = 0;
|
|
ULONG* pulIdsArray = NULL;
|
|
DWORD dwDriverEntryLength = 0;
|
|
PEFI_DRIVER_ENTRY pDriverEntry = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the input
|
|
if ( pdeSource == NULL || pwszEFIPath == NULL ||
|
|
(dwFriendlyNameType != BOOTENTRY_FRIENDLYNAME_NONE && pwszFriendlyName == NULL) )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// validate the driver file in the source driver entry
|
|
//
|
|
|
|
// extract the driver file path
|
|
pfpDriverFilePath = (PFILE_PATH) ADD_OFFSET( pdeSource, DriverFilePathOffset );
|
|
|
|
// attempt to translate the file path
|
|
status = NtTranslateFilePath( pfpDriverFilePath, FILE_PATH_TYPE_NT, NULL, &dwDriverFilePathLength );
|
|
|
|
// reset the pDriverFilePath and dwDriverFilePathLength variables
|
|
pfpDriverFilePath = NULL;
|
|
dwDriverFilePathLength = 0;
|
|
|
|
// now verify the result of the translation
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
if ( status == STATUS_BUFFER_TOO_SMALL )
|
|
{
|
|
// source driver entry is a valid one
|
|
dwResult = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// prepare "friendly name"
|
|
//
|
|
|
|
// determine the source friendly name and its length
|
|
dwFriendlyNameLength = 0;
|
|
pwszSourceFriendlyName = NULL;
|
|
switch( dwFriendlyNameType )
|
|
{
|
|
case BOOTENTRY_FRIENDLYNAME_NONE:
|
|
case BOOTENTRY_FRIENDLYNAME_APPEND:
|
|
{
|
|
pwszSourceFriendlyName = (LPCWSTR) ADD_OFFSET( pdeSource, FriendlyNameOffset );
|
|
dwFriendlyNameLength = StringLengthW( pwszSourceFriendlyName, 0 ) + 1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
|
|
// add the length of the friendly name that needs to be added -- if exists
|
|
if ( pwszFriendlyName != NULL )
|
|
{
|
|
dwFriendlyNameLength += StringLengthW( pwszFriendlyName, 0 ) + 1;
|
|
}
|
|
|
|
// allocate memory for the friendly name
|
|
pwszTargetFriendlyName = (LPWSTR) AllocateMemory( (dwFriendlyNameLength + 1) * sizeof( WCHAR ) );
|
|
if ( pwszTargetFriendlyName == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// prepare the friendly name
|
|
StringCopyW( pwszTargetFriendlyName, L"", dwFriendlyNameLength );
|
|
|
|
// ...
|
|
if ( pwszSourceFriendlyName != NULL )
|
|
{
|
|
StringConcat( pwszTargetFriendlyName, pwszSourceFriendlyName, dwFriendlyNameLength );
|
|
}
|
|
|
|
// ...
|
|
if ( pwszFriendlyName != NULL )
|
|
{
|
|
// add one space b/w the existing and concatenating string
|
|
if ( pwszSourceFriendlyName != NULL )
|
|
{
|
|
StringConcat( pwszTargetFriendlyName, L" ", dwFriendlyNameLength );
|
|
}
|
|
|
|
// ...
|
|
StringConcat( pwszTargetFriendlyName, pwszFriendlyName, dwFriendlyNameLength );
|
|
}
|
|
|
|
//
|
|
// driver file path
|
|
//
|
|
|
|
// the 'FilePath' variable in FILE_PATH variable contains two variables
|
|
// each seperated by a NULL terminated character
|
|
// the first part designates the DEVICE PATH
|
|
// and the second part designated the DIRECTORY / FILE PATH
|
|
// we already have the device path (pwszEFIPath) -- but we need to get the
|
|
// DIRECTORY / FILE PATH -- this we will get from the source driver entry
|
|
pfpDriverFilePath = (PFILE_PATH) ADD_OFFSET( pdeSource, DriverFilePathOffset );
|
|
dwResult = PrepareCompleteEFIPath( pfpDriverFilePath, pwszEFIPath, &pwszFullEFIPath, &dwEFIPathLength );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// since the memory reference in pDriverFilePath is not allocated
|
|
// in this function, it is important to reset the pointer to NULL
|
|
// this avoids the crash in the program
|
|
pfpDriverFilePath = NULL;
|
|
|
|
// ...
|
|
goto cleanup;
|
|
}
|
|
|
|
// now determine the memory size that needs to be allocated for FILE_PATH structure
|
|
// and align up to the even memory bounday
|
|
pfpDriverFilePath = NULL;
|
|
dwDriverFilePathLength = FIELD_OFFSET(FILE_PATH, FilePath) + (dwEFIPathLength * sizeof( WCHAR ));
|
|
|
|
// allocate memory
|
|
pfpDriverFilePath = (PFILE_PATH) AllocateMemory( dwDriverFilePathLength );
|
|
if ( pfpDriverFilePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// initialize the file path structure
|
|
ZeroMemory( pfpDriverFilePath, dwDriverFilePathLength );
|
|
pfpDriverFilePath->Length = dwDriverFilePathLength;
|
|
pfpDriverFilePath->Type = FILE_PATH_TYPE_NT;
|
|
pfpDriverFilePath->Version = FILE_PATH_VERSION;
|
|
CopyMemory( pfpDriverFilePath->FilePath, pwszFullEFIPath, dwEFIPathLength * sizeof( WCHAR ) );
|
|
|
|
//
|
|
// finally, create the driver entry
|
|
//
|
|
|
|
// determine the size for the EFI_DRIVER_ENTRY structure
|
|
dwDriverEntryLength =
|
|
FIELD_OFFSET( EFI_DRIVER_ENTRY, DriverFilePathOffset ) +
|
|
(dwFriendlyNameLength * sizeof(WCHAR)) +
|
|
dwDriverFilePathLength +
|
|
sizeof(WCHAR) + // align the FriendlyName on WCHAR
|
|
sizeof(DWORD); // align the DriverFilePath on DWORD
|
|
|
|
// allocate memory
|
|
pDriverEntry = (PEFI_DRIVER_ENTRY) AllocateMemory( dwDriverEntryLength );
|
|
if ( pDriverEntry == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
ZeroMemory( pDriverEntry, dwDriverEntryLength );
|
|
pDriverEntry->Id = 0L;
|
|
pDriverEntry->Length = dwDriverEntryLength;
|
|
pDriverEntry->Version = EFI_DRIVER_ENTRY_VERSION;
|
|
|
|
// align the friendly name on WCHR boundary
|
|
pDriverEntry->FriendlyNameOffset = ALIGN_UP( sizeof( EFI_DRIVER_ENTRY ), WCHAR );
|
|
|
|
// align the driver file path on DWORD boundary
|
|
pDriverEntry->DriverFilePathOffset =
|
|
ALIGN_UP( pDriverEntry->FriendlyNameOffset + (dwFriendlyNameLength * sizeof(WCHAR)), DWORD );
|
|
|
|
// fill the driver entry
|
|
CopyMemory( ADD_OFFSET( pDriverEntry, DriverFilePathOffset ), pfpDriverFilePath, dwDriverFilePathLength );
|
|
CopyMemory(
|
|
ADD_OFFSET( pDriverEntry, FriendlyNameOffset ),
|
|
pwszTargetFriendlyName, (dwFriendlyNameLength * sizeof(WCHAR) ) );
|
|
|
|
//
|
|
// add the prepared driver entry
|
|
//
|
|
status = NtAddDriverEntry( pDriverEntry, &ulId );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// ddd the entry to the driver order.
|
|
//
|
|
ulIdCount = 32L;
|
|
pulIdsArray = (PULONG) AllocateMemory( ulIdCount * sizeof(ULONG) );
|
|
if ( pulIdsArray == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// query the driver entry order
|
|
// NOTE: we will doing error check for this function call bit later
|
|
status = NtQueryDriverEntryOrder( pulIdsArray, &ulIdCount );
|
|
|
|
// need room in the buffer for the new entry.
|
|
if ( 31L < ulIdCount )
|
|
{
|
|
// release the current memory allocation for id's
|
|
FreeMemory( &pulIdsArray );
|
|
|
|
// allocate new memory and query again
|
|
pulIdsArray = (PULONG) AllocateMemory( (ulIdCount+1) * sizeof(ULONG));
|
|
if ( pulIdsArray == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
status = NtQueryDriverEntryOrder( pulIdsArray, &ulIdCount );
|
|
}
|
|
|
|
// check the result of the driver entries query operation
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
// set the boot entry order
|
|
ulIdCount++;
|
|
*(pulIdsArray + (ulIdCount - 1)) = ulId;
|
|
status = NtSetDriverEntryOrder( pulIdsArray, ulIdCount );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
|
|
// success
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
// release the memory allocated
|
|
FreeMemory( &pulIdsArray );
|
|
FreeMemory( &pDriverEntry );
|
|
FreeMemory( &pwszFullEFIPath );
|
|
FreeMemory( &pfpDriverFilePath );
|
|
FreeMemory( &pwszTargetFriendlyName );
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
LONG FindDriverEntryWithTargetEFI( PEFI_DRIVER_ENTRY_LIST pdeList, DWORD dwSourceIndex,
|
|
PEFI_DRIVER_ENTRY pdeSource, LPCWSTR pwszDevicePath )
|
|
{
|
|
// local variables
|
|
LONG lIndex = 0;
|
|
BOOL bExitFromLoop = FALSE;
|
|
PFILE_PATH pfpFilePath = NULL;
|
|
LPCWSTR pwszFilePath = NULL;
|
|
LPWSTR pwszFullFilePath = NULL;
|
|
PFILE_PATH pfpSourceFilePath = NULL;
|
|
PEFI_DRIVER_ENTRY pDriverEntry = NULL;
|
|
DWORD dw = 0, dwResult = 0, dwLength = 0;
|
|
|
|
// check the input parameters
|
|
if ( pdeList == NULL || pdeSource == NULL || pwszDevicePath == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// extract the file path from the source driver entry
|
|
pfpSourceFilePath = (PFILE_PATH) ADD_OFFSET( pdeSource, DriverFilePathOffset );
|
|
|
|
// preare the efi path from the source path
|
|
// (replacing the source device path with the target device path)
|
|
//
|
|
// the 'FilePath' variable in FILE_PATH variable contains two variables
|
|
// each seperated by a NULL terminated character
|
|
// the first part designates the DEVICE PATH
|
|
// and the second part designated the DIRECTORY / FILE PATH
|
|
// we already have the device path (pwszEFIPath) -- but we need to get the
|
|
// DIRECTORY / FILE PATH -- this we will get from the source driver entry
|
|
//
|
|
dwResult = PrepareCompleteEFIPath( pfpSourceFilePath, pwszDevicePath, &pwszFullFilePath, &dwLength );
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
// since we already the target device path -- we need the file path
|
|
// extract this info more just prepared full file path
|
|
dw = StringLengthW( pwszFullFilePath, 0 ) + 1; // +1 for null character
|
|
if ( dw > dwLength )
|
|
{
|
|
// error case -- this should never occure
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
pwszFilePath = pwszFullFilePath + dw;
|
|
|
|
// traverse thru the list of driver entries
|
|
lIndex = 0;
|
|
bExitFromLoop = FALSE;
|
|
dwResult = ERROR_NOT_FOUND;
|
|
while ( bExitFromLoop == FALSE )
|
|
{
|
|
// increment the loop counter
|
|
lIndex++;
|
|
|
|
// get the reference to the current driver entry
|
|
pDriverEntry = &pdeList->DriverEntry;
|
|
if ( pDriverEntry == NULL )
|
|
{
|
|
// should never occur
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
bExitFromLoop = TRUE;
|
|
continue;
|
|
}
|
|
|
|
// if the current index doesn't match with the one that we are comparing
|
|
// then only proceed with comparision otherwise skip this
|
|
if ( lIndex != dwSourceIndex )
|
|
{
|
|
// extract the driver file path
|
|
pfpFilePath = (PFILE_PATH) ADD_OFFSET( pDriverEntry, DriverFilePathOffset );
|
|
|
|
// compare the file paths
|
|
if ( MatchPath( pfpFilePath, pwszDevicePath, pwszFilePath ) == TRUE )
|
|
{
|
|
bExitFromLoop = TRUE;
|
|
dwResult = ERROR_ALREADY_EXISTS;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// fetch the next pointer
|
|
bExitFromLoop = (pdeList->NextEntryOffset == 0);
|
|
pdeList = (PEFI_DRIVER_ENTRY_LIST) ADD_OFFSET( pdeList, NextEntryOffset );
|
|
}
|
|
|
|
cleanup:
|
|
|
|
// release memory
|
|
FreeMemory( &pwszFullFilePath );
|
|
|
|
// result
|
|
return ((dwResult == ERROR_ALREADY_EXISTS) ? lIndex : -1);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// general helper functions
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD TranslateEFIPathToNTPath( LPCWSTR pwszGUID, LPVOID* pwszPath )
|
|
{
|
|
//
|
|
// local variables
|
|
HRESULT hr = S_OK;
|
|
BOOL bSecondChance = FALSE;
|
|
BOOL bExtendedFormat = FALSE;
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// file path
|
|
DWORD dwFilePathLength = 0;
|
|
LPWSTR pwszFilePath = NULL;
|
|
|
|
// source FILE_PATH
|
|
DWORD dwSourceFilePathSize = 0;
|
|
PFILE_PATH pfpSourcePath = NULL;
|
|
|
|
// target FILE_PATH
|
|
DWORD dwLength = 0;
|
|
DWORD dwTargetFilePathSize = 0;
|
|
PFILE_PATH pfpTargetPath = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the parameters
|
|
if ( pwszGUID == NULL || pwszPath == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// determine whether we need to choose the extended formatting
|
|
// or normal formatting
|
|
bExtendedFormat = ( (*pwszGUID != L'{') && (*(pwszGUID + StringLengthW( pwszGUID, 0 ) - 1) != L'}') );
|
|
|
|
// default length
|
|
dwFilePathLength = MAX_STRING_LENGTH;
|
|
|
|
try_alloc:
|
|
|
|
//
|
|
// allocate memory for formatting the EFI path
|
|
pwszFilePath = AllocateMemory( (dwFilePathLength + 1) * sizeof( WCHAR ) );
|
|
if ( pwszFilePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// format EFI path
|
|
if ( bExtendedFormat == FALSE )
|
|
{
|
|
hr = StringCchPrintfW( pwszFilePath, dwFilePathLength, FORMAT_FILE_PATH, pwszGUID );
|
|
}
|
|
else
|
|
{
|
|
hr = StringCchPrintfW( pwszFilePath, dwFilePathLength, FORMAT_FILE_PATH_EX, pwszGUID );
|
|
}
|
|
|
|
// check the result -- if failed exit
|
|
if ( HRESULT_CODE( hr ) != S_OK )
|
|
{
|
|
// free the currently allocated block
|
|
FreeMemory( &pwszFilePath );
|
|
|
|
// increase the memory in blocks for MAX_STRING_LENGTH
|
|
// but do this only 4 times the originally allocated
|
|
if ( dwFilePathLength == (MAX_STRING_LENGTH * 4) )
|
|
{
|
|
// cannot afford to give some more tries -- exit
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
dwFilePathLength *= MAX_STRING_LENGTH;
|
|
goto try_alloc;
|
|
}
|
|
}
|
|
|
|
// determine the actual length of the file path
|
|
dwFilePathLength = StringLengthW( pwszFilePath, 0 ) + 1;
|
|
|
|
// now determine the memory size that needs to be allocated for FILE_PATH structure
|
|
// and align up to the even memory bounday
|
|
dwSourceFilePathSize = FIELD_OFFSET( FILE_PATH, FilePath ) + (dwFilePathLength * sizeof(WCHAR));
|
|
|
|
// allocate memory for boot file path -- extra one byte is for safe guarding
|
|
pfpSourcePath = AllocateMemory( dwSourceFilePathSize + 1 );
|
|
if ( pfpSourcePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// initialize the source boot path
|
|
ZeroMemory( pfpSourcePath, dwSourceFilePathSize );
|
|
pfpSourcePath->Type = FILE_PATH_TYPE_ARC_SIGNATURE;
|
|
pfpSourcePath->Version = FILE_PATH_VERSION;
|
|
pfpSourcePath->Length = dwSourceFilePathSize;
|
|
CopyMemory( pfpSourcePath->FilePath, pwszFilePath, dwFilePathLength * sizeof(WCHAR) );
|
|
|
|
//
|
|
// do the translation
|
|
//
|
|
// default size for the target file path is same as the one for source file path
|
|
//
|
|
bSecondChance = FALSE;
|
|
dwTargetFilePathSize = dwSourceFilePathSize;
|
|
|
|
try_translate:
|
|
|
|
// allocate memory -- extra one byte is for safe guarding
|
|
pfpTargetPath = AllocateMemory( dwTargetFilePathSize + 1);
|
|
if ( pfpTargetPath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// attempt to translate the file path
|
|
status = NtTranslateFilePath( pfpSourcePath,
|
|
FILE_PATH_TYPE_NT, pfpTargetPath, &dwTargetFilePathSize );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
// release the memory that is allocated for target path structure
|
|
FreeMemory( &pfpTargetPath );
|
|
|
|
if ( status == STATUS_BUFFER_TOO_SMALL && bSecondChance == FALSE )
|
|
{
|
|
// give a second try
|
|
bSecondChance = TRUE;
|
|
|
|
goto try_translate;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// re-use the memory that is allocated for file path
|
|
// defintely the NT path will be less than the length of ARC Signature
|
|
// NOTE: since we are interested only in the device path, we use StringCopy
|
|
// which stops at the first null character -- otherwise, if we are interestedd in
|
|
// complete path, we need to CopyMemory
|
|
dwLength = StringLengthW( (LPCWSTR) pfpTargetPath->FilePath, 0 );
|
|
if ( dwLength < dwFilePathLength - 1 )
|
|
{
|
|
// copy the string contents
|
|
ZeroMemory( pwszFilePath, (dwFilePathLength - 1) * sizeof( WCHAR ) );
|
|
StringCopyW( pwszFilePath, (LPCWSTR) pfpTargetPath->FilePath, dwFilePathLength );
|
|
}
|
|
else
|
|
{
|
|
// re-allocate memory
|
|
dwLength++;
|
|
if ( ReallocateMemory( (VOID*) &pwszFilePath, (dwLength + 1) * sizeof( WCHAR ) ) == FALSE )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// ...
|
|
ZeroMemory( pwszFilePath, (dwLength + 1) * sizeof( WCHAR ) );
|
|
StringCopyW( pwszFilePath, (LPCWSTR) pfpTargetPath->FilePath, dwLength );
|
|
}
|
|
|
|
// translation is success
|
|
// return the translated file path
|
|
dwResult = ERROR_SUCCESS;
|
|
*pwszPath = pwszFilePath;
|
|
|
|
cleanup:
|
|
// free memory allocated for target path structure
|
|
FreeMemory( &pfpTargetPath );
|
|
|
|
// release the memory allocated for source path structure
|
|
FreeMemory( &pfpSourcePath );
|
|
|
|
// release memory allocated for string
|
|
// NOTE: do this only in case of failure
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// ...
|
|
FreeMemory( &pwszFilePath );
|
|
|
|
// re-init the out params to their default values
|
|
*pwszPath = NULL;
|
|
}
|
|
|
|
// return the result
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
DWORD PrepareCompleteEFIPath( PFILE_PATH pfpSource,
|
|
LPCWSTR pwszDevicePath,
|
|
LPWSTR* pwszEFIPath, DWORD* pdwLength )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwLength = 0;
|
|
LPWSTR pwszBuffer = NULL;
|
|
BOOL bSecondChance = FALSE;
|
|
PFILE_PATH pfpFilePath = NULL;
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
LPCWSTR pwszSourceFilePath = NULL;
|
|
LPCWSTR pwszSourceDevicePath = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the input
|
|
if ( pfpSource == NULL ||
|
|
pwszDevicePath == NULL ||
|
|
pwszEFIPath == NULL || pdwLength == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// we need to translate the source file path into NT file path format
|
|
//
|
|
dwLength = 1024;
|
|
|
|
try_again:
|
|
|
|
// allocate memory for the file path structure
|
|
pfpFilePath = (PFILE_PATH) AllocateMemory( dwLength );
|
|
if ( pfpFilePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// attempt to translate the file path
|
|
status = NtTranslateFilePath( pfpSource, FILE_PATH_TYPE_NT, pfpFilePath, &dwLength );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
// release the memory that is allocated for target path structure
|
|
FreeMemory( &pfpFilePath );
|
|
|
|
if ( status == STATUS_BUFFER_TOO_SMALL && bSecondChance == FALSE )
|
|
{
|
|
// give a second try
|
|
bSecondChance = TRUE;
|
|
goto try_again;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// get the pointer to the source device path
|
|
pwszSourceDevicePath = (LPCWSTR) pfpFilePath->FilePath;
|
|
if ( pwszSourceDevicePath == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// check whether the pwszSourceDevicePath and pwszDevicePath that is
|
|
// passed to the fuction are same or different
|
|
if ( StringCompare( pwszSourceDevicePath, pwszDevicePath, TRUE, 0 ) == 0 )
|
|
{
|
|
dwResult = ERROR_ALREADY_EXISTS;
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the length of the source device path -- +1 for null character
|
|
dwLength = StringLengthW( pwszSourceDevicePath, 0 ) + 1;
|
|
|
|
// check whether the directory path exists or not
|
|
// this can be easily determined based on the length of the device path
|
|
// and total of the structure
|
|
if ( pfpFilePath->Length <= (FIELD_OFFSET( FILE_PATH, FilePath ) + dwLength) )
|
|
{
|
|
// the condition 'less than' will never be true -- but equal might
|
|
// that means there is no directory path associated to this file path
|
|
// so simply return
|
|
//
|
|
// NOTE: this is only for safety sake -- this case will never occur
|
|
//
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// file path exists --
|
|
// this is placed very next to device path seperated by '\0' terminator
|
|
pwszSourceFilePath = pwszSourceDevicePath + dwLength;
|
|
|
|
// sum the lengths of the device path (passed by the caller) and directory path (got from source file path)
|
|
// NOTE: +3 ==> ( one '\0' character for each path )
|
|
dwLength = StringLengthW( pwszDevicePath, 0 ) + StringLengthW( pwszSourceFilePath, 0 ) + 3;
|
|
|
|
// now allocate memory
|
|
// extra one as safety guard
|
|
pwszBuffer = AllocateMemory( (dwLength + 1) * sizeof( WCHAR ) );
|
|
if ( pwszBuffer == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// copy the new device path (which is passed by the caller) to the newly allocated buffer
|
|
StringCopyW( pwszBuffer, pwszDevicePath, dwLength );
|
|
|
|
// increment the pointer leaving one space for UNICODE '\0' character
|
|
StringCopyW( pwszBuffer + (StringLengthW( pwszBuffer, 0 ) + 1),
|
|
pwszSourceFilePath, dwLength - (StringLengthW( pwszBuffer, 0 ) + 1) );
|
|
|
|
// success
|
|
dwResult = ERROR_SUCCESS;
|
|
*pwszEFIPath = pwszBuffer;
|
|
*pdwLength = dwLength + 1; // extra one which we allocated as safe guard
|
|
|
|
cleanup:
|
|
|
|
// free the memory allocated for path translation
|
|
FreeMemory( &pfpFilePath );
|
|
|
|
// free memory allocated for buffer space
|
|
// NOTE: release this memory only in case of error
|
|
if ( dwResult != ERROR_SUCCESS )
|
|
{
|
|
// ...
|
|
FreeMemory( &pwszBuffer );
|
|
|
|
// also, set the 'out' parameters to their default values
|
|
*pdwLength = 0;
|
|
*pwszEFIPath = NULL;
|
|
}
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
BOOL MatchPath( PFILE_PATH pfpSource, LPCWSTR pwszDevicePath, LPCWSTR pwszFilePath )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwLength = 0;
|
|
BOOL bSecondChance = FALSE;
|
|
PFILE_PATH pfpFilePath = NULL;
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
LPCWSTR pwszSourceFilePath = NULL;
|
|
LPCWSTR pwszSourceDevicePath = NULL;
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
// check the input
|
|
if ( pfpSource == NULL || (pwszDevicePath == NULL && pwszFilePath == NULL) )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// we need to translate the source file path into NT file path format
|
|
//
|
|
dwLength = 1024;
|
|
|
|
try_again:
|
|
|
|
// allocate memory for the file path structure
|
|
pfpFilePath = (PFILE_PATH) AllocateMemory( dwLength );
|
|
if ( pfpFilePath == NULL )
|
|
{
|
|
dwResult = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
// attempt to translate the file path
|
|
status = NtTranslateFilePath( pfpSource, FILE_PATH_TYPE_NT, pfpFilePath, &dwLength );
|
|
if ( NOT NT_SUCCESS( status ) )
|
|
{
|
|
// release the memory that is allocated for target path structure
|
|
FreeMemory( &pfpFilePath );
|
|
|
|
if ( status == STATUS_BUFFER_TOO_SMALL && bSecondChance == FALSE )
|
|
{
|
|
// give a second try
|
|
bSecondChance = TRUE;
|
|
goto try_again;
|
|
}
|
|
else
|
|
{
|
|
// error occured -- cannot recover
|
|
dwResult = RtlNtStatusToDosError( status );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// get the pointer to the source device path
|
|
pwszSourceDevicePath = (LPCWSTR) pfpFilePath->FilePath;
|
|
if ( pwszSourceDevicePath == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the length of the source device path -- +1 for null character
|
|
dwLength = StringLengthW( pwszSourceDevicePath, 0 ) + 1;
|
|
|
|
// check whether the file path exists or not
|
|
// this can be easily determined based on the length of the device path
|
|
// and total of the structure
|
|
if ( pfpFilePath->Length <= (FIELD_OFFSET( FILE_PATH, FilePath ) + dwLength) )
|
|
{
|
|
// the condition 'less than' will never be true -- but equal might
|
|
// that means there is no file path associated to this file_path
|
|
// so simply return
|
|
//
|
|
// NOTE: this is only for safety sake -- this case will never occur
|
|
//
|
|
dwResult = (DWORD) STG_E_UNKNOWN;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// file path exists --
|
|
// this is placed very next to device path seperated by '\0' terminator
|
|
pwszSourceFilePath = pwszSourceDevicePath + dwLength;
|
|
|
|
// check whether the pwszSourceDevicePath and pwszDevicePath that
|
|
// is passed to the fuction are same or different
|
|
if ( pwszDevicePath != NULL &&
|
|
StringCompare( pwszSourceDevicePath, pwszDevicePath, TRUE, 0 ) != 0 )
|
|
{
|
|
dwResult = ERROR_NOT_FOUND;
|
|
goto cleanup;
|
|
}
|
|
|
|
// check whether the pwszSourceFilePath and pwszFilePath that
|
|
// is passed to the fuction are same or different
|
|
if ( pwszFilePath != NULL &&
|
|
StringCompare( pwszSourceFilePath, pwszFilePath, TRUE, 0 ) != 0 )
|
|
{
|
|
dwResult = ERROR_NOT_FOUND;
|
|
goto cleanup;
|
|
}
|
|
|
|
// entries matched
|
|
dwResult = ERROR_ALREADY_EXISTS;
|
|
|
|
cleanup:
|
|
|
|
// free the memory allocated for path translation
|
|
FreeMemory( &pfpFilePath );
|
|
|
|
// return
|
|
return (dwResult == ERROR_ALREADY_EXISTS);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DWORD ProcessOptions( DWORD argc,
|
|
LPCWSTR argv[],
|
|
PTCLONE_PARAMS pParams )
|
|
{
|
|
//
|
|
// local variables
|
|
DWORD dwResult = 0;
|
|
BOOL bClone = FALSE;
|
|
PTCMDPARSER2 pcmdOption = NULL;
|
|
TCMDPARSER2 cmdOptions[ OI_CLONE_COUNT ];
|
|
|
|
// check inputs
|
|
if ( argc == 0 || argv == NULL || pParams == NULL )
|
|
{
|
|
dwResult = ERROR_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
// init the entire structure with zero's
|
|
ZeroMemory( cmdOptions, SIZE_OF_ARRAY( cmdOptions )* sizeof( TCMDPARSER2 ) );
|
|
|
|
// -clone
|
|
pcmdOption = &cmdOptions[ OI_CLONE_MAIN ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->pValue = &bClone;
|
|
pcmdOption->dwType = CP_TYPE_BOOLEAN;
|
|
pcmdOption->pwszOptions = OPTION_CLONE;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
|
|
// -?
|
|
pcmdOption = &cmdOptions[ OI_CLONE_HELP ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwFlags = CP2_USAGE;
|
|
pcmdOption->dwType = CP_TYPE_BOOLEAN;
|
|
pcmdOption->pValue = &pParams->bUsage;
|
|
pcmdOption->pwszOptions = OPTION_CLONE_HELP;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
|
|
// -sg
|
|
pcmdOption = &cmdOptions[ OI_CLONE_SOURCE_GUID ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_TEXT;
|
|
pcmdOption->pwszOptions = OPTION_CLONE_SOURCE_GUID;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
pcmdOption->dwFlags = CP2_ALLOCMEMORY | CP2_VALUE_TRIMINPUT | CP2_VALUE_NONULL;
|
|
|
|
// -tg
|
|
pcmdOption = &cmdOptions[ OI_CLONE_TARGET_GUID ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_TEXT;
|
|
pcmdOption->pwszOptions = OPTION_CLONE_TARGET_GUID;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
pcmdOption->dwFlags = CP2_ALLOCMEMORY | CP2_VALUE_TRIMINPUT | CP2_VALUE_NONULL | CP2_MANDATORY;
|
|
|
|
// -d
|
|
pcmdOption = &cmdOptions[ OI_CLONE_FRIENDLY_NAME_REPLACE ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_TEXT;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
pcmdOption->pwszOptions = OPTION_CLONE_FRIENDLY_NAME_REPLACE;
|
|
pcmdOption->dwFlags = CP2_ALLOCMEMORY | CP2_VALUE_TRIMINPUT | CP2_VALUE_NONULL;
|
|
|
|
// -d+
|
|
pcmdOption = &cmdOptions[ OI_CLONE_FRIENDLY_NAME_APPEND ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_TEXT;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
pcmdOption->pwszOptions = OPTION_CLONE_FRIENDLY_NAME_APPEND;
|
|
pcmdOption->dwFlags = CP2_ALLOCMEMORY | CP2_VALUE_TRIMINPUT | CP2_VALUE_NONULL;
|
|
|
|
// -id
|
|
pcmdOption = &cmdOptions[ OI_CLONE_BOOT_ID ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_UNUMERIC;
|
|
pcmdOption->pValue = &pParams->lBootId;
|
|
pcmdOption->pwszOptions = OPTION_CLONE_BOOT_ID;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
|
|
// -upgdrv
|
|
pcmdOption = &cmdOptions[ OI_CLONE_DRIVER_UPDATE ];
|
|
pcmdOption->dwCount = 1;
|
|
pcmdOption->dwType = CP_TYPE_BOOLEAN;
|
|
pcmdOption->pwszOptions = OPTION_CLONE_DRIVER_UPDATE;
|
|
pcmdOption->pValue = &pParams->bDriverUpdate;
|
|
StringCopyA( pcmdOption->szSignature, "PARSER2", 8 );
|
|
|
|
//
|
|
// do the parsing
|
|
pParams->bVerbose = TRUE; // default value -- user need not specify "/v" explicitly
|
|
if ( DoParseParam2( argc, argv, OI_CLONE_MAIN, OI_CLONE_COUNT, cmdOptions, 0 ) == FALSE )
|
|
{
|
|
dwResult = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// validate the input parameters
|
|
//
|
|
|
|
// check the usage option
|
|
if ( pParams->bUsage == TRUE )
|
|
{
|
|
if ( argc > 3 )
|
|
{
|
|
// no other options are accepted along with -? option
|
|
dwResult = (DWORD) MK_E_SYNTAX;
|
|
SetReason( MSG_ERROR_INVALID_USAGE_REQUEST );
|
|
goto cleanup;
|
|
}
|
|
else
|
|
{
|
|
// no need of furthur checking of the values
|
|
dwResult = ERROR_SUCCESS;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
// -d and -d+ are mutually exclusive --
|
|
// that is, -d and -d+ cannot be specified at a time --
|
|
// but it is ok even if both are not specified
|
|
if ( cmdOptions[ OI_CLONE_FRIENDLY_NAME_APPEND ].pValue != NULL &&
|
|
cmdOptions[ OI_CLONE_FRIENDLY_NAME_REPLACE ].pValue != NULL )
|
|
{
|
|
dwResult = (DWORD) MK_E_SYNTAX;
|
|
SetReason( MSG_ERROR_INVALID_DESCRIPTION_COMBINATION );
|
|
goto cleanup;
|
|
}
|
|
|
|
// get the buffer pointers allocated by command line parser
|
|
pParams->dwFriendlyNameType = BOOTENTRY_FRIENDLYNAME_NONE;
|
|
pParams->pwszSourceGuid = cmdOptions[ OI_CLONE_SOURCE_GUID ].pValue;
|
|
pParams->pwszTargetGuid = cmdOptions[ OI_CLONE_TARGET_GUID ].pValue;
|
|
if ( cmdOptions[ OI_CLONE_FRIENDLY_NAME_APPEND ].pValue != NULL )
|
|
{
|
|
pParams->dwFriendlyNameType = BOOTENTRY_FRIENDLYNAME_APPEND;
|
|
pParams->pwszFriendlyName = cmdOptions[ OI_CLONE_FRIENDLY_NAME_APPEND ].pValue;
|
|
}
|
|
else if ( cmdOptions[ OI_CLONE_FRIENDLY_NAME_REPLACE ].pValue != NULL )
|
|
{
|
|
pParams->dwFriendlyNameType = BOOTENTRY_FRIENDLYNAME_REPLACE;
|
|
pParams->pwszFriendlyName = cmdOptions[ OI_CLONE_FRIENDLY_NAME_REPLACE ].pValue;
|
|
}
|
|
|
|
// -id and -sg are mutually exclusive options
|
|
// also, -upddrv also should not be specified when -id is specified
|
|
if ( cmdOptions[ OI_CLONE_BOOT_ID ].dwActuals != 0 )
|
|
{
|
|
if ( pParams->pwszSourceGuid != NULL || pParams->bDriverUpdate == TRUE )
|
|
{
|
|
dwResult = (DWORD) MK_E_SYNTAX;
|
|
SetReason( MSG_ERROR_INVALID_BOOT_ID_COMBINATION );
|
|
goto cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// default value
|
|
pParams->lBootId = -1;
|
|
}
|
|
|
|
// -d or -d+ should not be specified when -upddrv is specified
|
|
if ( pParams->pwszFriendlyName != NULL && pParams->bDriverUpdate == TRUE )
|
|
{
|
|
dwResult = (DWORD) MK_E_SYNTAX;
|
|
SetReason( MSG_ERROR_INVALID_UPDDRV_COMBINATION );
|
|
goto cleanup;
|
|
}
|
|
|
|
// -sg should be specified when -upddrv switch is specified
|
|
if ( pParams->bDriverUpdate == TRUE && pParams->pwszSourceGuid == NULL )
|
|
{
|
|
dwResult = (DWORD) MK_E_SYNTAX;
|
|
SetReason( MSG_ERROR_NO_SGUID_WITH_UPDDRV );
|
|
goto cleanup;
|
|
}
|
|
|
|
// success
|
|
dwResult = ERROR_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
// return
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
DWORD DisplayCloneHelp()
|
|
{
|
|
// local variables
|
|
DWORD dwIndex = IDS_CLONE_BEGIN_IA64 ;
|
|
|
|
// ...
|
|
for(;dwIndex <=IDS_CLONE_END_IA64;dwIndex++)
|
|
{
|
|
ShowMessage( stdout, GetResString(dwIndex) );
|
|
}
|
|
|
|
// return
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|