2019-09-03 11:58:42 +02:00
/*
* refit / scan / common . c
*
* Copyright ( c ) 2006 - 2010 Christoph Pfisterer
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are
* met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
*
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the
* distribution .
*
* * Neither the name of Christoph Pfisterer nor the names of the
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "entry_scan.h"
# ifndef DEBUG_ALL
# define DEBUG_COMMON_MENU 1
# else
# define DEBUG_COMMON_MENU DEBUG_ALL
# endif
# if DEBUG_COMMON_MENU == 0
# define DBG(...)
# else
# define DBG(...) DebugLog(DEBUG_COMMON_MENU, __VA_ARGS__)
# endif
2020-02-17 21:41:09 +01:00
static CONST CHAR16 * BuiltinIconNames [ ] = {
2019-09-03 11:58:42 +02:00
/*
L " About " ,
L " Options " ,
L " Clover " ,
L " Reset " ,
L " Shutdown " ,
L " Help " ,
L " Shell " ,
L " Part " ,
L " Rescue " ,
L " Pointer " ,
*/
L " Internal " ,
L " External " ,
L " Optical " ,
L " FireWire " ,
L " Boot " ,
L " HFS " ,
L " APFS " ,
L " NTFS " ,
L " EXT " ,
L " Recovery " ,
} ;
static const UINTN BuiltinIconNamesCount = ( sizeof ( BuiltinIconNames ) / sizeof ( CHAR16 * ) ) ;
2020-02-17 21:41:09 +01:00
EG_IMAGE * LoadBuiltinIcon ( IN CONST CHAR16 * IconName )
2019-09-03 11:58:42 +02:00
{
UINTN Index = 0 ;
if ( IconName = = NULL ) {
return NULL ;
}
while ( Index < BuiltinIconNamesCount ) {
if ( StriCmp ( IconName , BuiltinIconNames [ Index ] ) = = 0 ) {
return BuiltinIcon ( BUILTIN_ICON_VOL_INTERNAL + Index ) ;
}
+ + Index ;
}
return NULL ;
}
EG_IMAGE * ScanVolumeDefaultIcon ( REFIT_VOLUME * Volume , IN UINT8 OSType , IN EFI_DEVICE_PATH_PROTOCOL * DevicePath ) //IN UINT8 DiskKind)
{
UINTN IconNum = 0 ;
// default volume icon based on disk kind
switch ( Volume - > DiskKind ) {
case DISK_KIND_INTERNAL :
switch ( OSType ) {
case OSTYPE_OSX :
case OSTYPE_OSX_INSTALLER :
while ( ! IsDevicePathEndType ( DevicePath ) & &
! ( DevicePathType ( DevicePath ) = = MEDIA_DEVICE_PATH & & DevicePathSubType ( DevicePath ) = = MEDIA_VENDOR_DP ) ) {
DevicePath = NextDevicePathNode ( DevicePath ) ;
}
if ( DevicePathType ( DevicePath ) = = MEDIA_DEVICE_PATH & & DevicePathSubType ( DevicePath ) = = MEDIA_VENDOR_DP ) {
if ( StriCmp ( GuidLEToStr ( ( EFI_GUID * ) ( ( UINT8 * ) DevicePath + 0x04 ) ) , GuidLEToStr ( & APFSSignature ) ) = = 0 ) {
IconNum = BUILTIN_ICON_VOL_INTERNAL_APFS ;
}
} else {
IconNum = BUILTIN_ICON_VOL_INTERNAL_HFS ;
}
break ;
case OSTYPE_RECOVERY :
IconNum = BUILTIN_ICON_VOL_INTERNAL_REC ;
break ;
case OSTYPE_LIN :
case OSTYPE_LINEFI :
IconNum = BUILTIN_ICON_VOL_INTERNAL_EXT3 ;
break ;
case OSTYPE_WIN :
case OSTYPE_WINEFI :
IconNum = BUILTIN_ICON_VOL_INTERNAL_NTFS ;
break ;
default :
IconNum = BUILTIN_ICON_VOL_INTERNAL ;
break ;
}
return BuiltinIcon ( IconNum ) ;
case DISK_KIND_EXTERNAL :
return BuiltinIcon ( BUILTIN_ICON_VOL_EXTERNAL ) ;
case DISK_KIND_OPTICAL :
return BuiltinIcon ( BUILTIN_ICON_VOL_OPTICAL ) ;
case DISK_KIND_FIREWIRE :
return BuiltinIcon ( BUILTIN_ICON_VOL_FIREWIRE ) ;
case DISK_KIND_BOOTER :
return BuiltinIcon ( BUILTIN_ICON_VOL_BOOTER ) ;
default :
break ;
}
return NULL ;
}
extern BOOLEAN CopyKernelAndKextPatches ( IN OUT KERNEL_AND_KEXT_PATCHES * Dst , IN KERNEL_AND_KEXT_PATCHES * Src ) ;
LOADER_ENTRY * DuplicateLoaderEntry ( IN LOADER_ENTRY * Entry )
{
LOADER_ENTRY * DuplicateEntry ;
if ( Entry = = NULL ) {
return NULL ;
}
2020-02-20 13:53:27 +01:00
2019-12-21 01:31:49 +01:00
DuplicateEntry = ( __typeof__ ( DuplicateEntry ) ) AllocateZeroPool ( sizeof ( LOADER_ENTRY ) ) ;
2019-09-03 11:58:42 +02:00
if ( DuplicateEntry ) {
DuplicateEntry - > me . Tag = Entry - > me . Tag ;
DuplicateEntry - > me . AtClick = ActionEnter ;
DuplicateEntry - > Volume = Entry - > Volume ;
DuplicateEntry - > DevicePathString = EfiStrDuplicate ( Entry - > DevicePathString ) ;
DuplicateEntry - > LoadOptions = EfiStrDuplicate ( Entry - > LoadOptions ) ;
DuplicateEntry - > LoaderPath = EfiStrDuplicate ( Entry - > LoaderPath ) ;
DuplicateEntry - > VolName = EfiStrDuplicate ( Entry - > VolName ) ;
DuplicateEntry - > DevicePath = Entry - > DevicePath ;
DuplicateEntry - > Flags = Entry - > Flags ;
DuplicateEntry - > LoaderType = Entry - > LoaderType ;
DuplicateEntry - > OSVersion = Entry - > OSVersion ;
DuplicateEntry - > BuildVersion = Entry - > BuildVersion ;
DuplicateEntry - > KernelAndKextPatches = Entry - > KernelAndKextPatches ;
}
return DuplicateEntry ;
}
2020-02-17 21:41:09 +01:00
CHAR16 * AddLoadOption ( IN CONST CHAR16 * LoadOptions , IN CONST CHAR16 * LoadOption )
2019-09-03 11:58:42 +02:00
{
// If either option strings are null nothing to do
if ( LoadOptions = = NULL )
{
if ( LoadOption = = NULL ) return NULL ;
// Duplicate original options as nothing to add
return EfiStrDuplicate ( LoadOption ) ;
}
// If there is no option or it is already present duplicate original
else if ( ( LoadOption = = NULL ) | | StrStr ( LoadOptions , LoadOption ) ) return EfiStrDuplicate ( LoadOptions ) ;
// Otherwise add option
return PoolPrint ( L " %s %s " , LoadOptions , LoadOption ) ;
}
2020-02-17 21:41:09 +01:00
CHAR16 * RemoveLoadOption ( IN CONST CHAR16 * LoadOptions , IN CONST CHAR16 * LoadOption )
2019-09-03 11:58:42 +02:00
{
2020-02-17 21:41:09 +01:00
CONST CHAR16 * Placement ;
2019-09-03 11:58:42 +02:00
CHAR16 * NewLoadOptions ;
UINTN Length , Offset , OptionLength ;
//DBG("LoadOptions: '%s', remove LoadOption: '%s'\n", LoadOptions, LoadOption);
// If there are no options then nothing to do
if ( LoadOptions = = NULL ) return NULL ;
// If there is no option to remove then duplicate original
if ( LoadOption = = NULL ) return EfiStrDuplicate ( LoadOptions ) ;
// If not present duplicate original
Placement = StrStr ( LoadOptions , LoadOption ) ;
if ( Placement = = NULL ) return EfiStrDuplicate ( LoadOptions ) ;
// Get placement of option in original options
Offset = ( Placement - LoadOptions ) ;
Length = StrLen ( LoadOptions ) ;
OptionLength = StrLen ( LoadOption ) ;
// If this is just part of some larger option (contains non-space at the beginning or end)
if ( ( Offset > 0 & & LoadOptions [ Offset - 1 ] ! = L ' ' ) | |
( ( Offset + OptionLength ) < Length & & LoadOptions [ Offset + OptionLength ] ! = L ' ' ) ) {
return EfiStrDuplicate ( LoadOptions ) ;
}
// Consume preceeding spaces
while ( Offset > 0 & & LoadOptions [ Offset - 1 ] = = L ' ' ) {
OptionLength + + ;
Offset - - ;
}
// Consume following spaces
while ( LoadOptions [ Offset + OptionLength ] = = L ' ' ) {
OptionLength + + ;
}
// If it's the whole string return NULL
if ( OptionLength = = Length ) return NULL ;
if ( Offset = = 0 ) {
// Simple case - we just need substring after OptionLength position
NewLoadOptions = EfiStrDuplicate ( LoadOptions + OptionLength ) ;
} else {
// The rest of LoadOptions is Length - OptionLength, but we may need additional space and ending 0
2019-12-21 01:31:49 +01:00
NewLoadOptions = ( __typeof__ ( NewLoadOptions ) ) AllocateZeroPool ( ( Length - OptionLength + 2 ) * sizeof ( CHAR16 ) ) ;
2019-09-03 11:58:42 +02:00
// Copy preceeding substring
CopyMem ( NewLoadOptions , LoadOptions , Offset * sizeof ( CHAR16 ) ) ;
if ( ( Offset + OptionLength ) < Length ) {
// Copy following substring, but include one space also
OptionLength - - ;
CopyMem ( NewLoadOptions + Offset , LoadOptions + Offset + OptionLength , ( Length - OptionLength - Offset ) * sizeof ( CHAR16 ) ) ;
}
}
return NewLoadOptions ;
}
# define TO_LOWER(ch) (((ch >= L'A') && (ch <= L'Z')) ? ((ch - L'A') + L'a') : ch)
2020-02-17 21:41:09 +01:00
INTN StrniCmp ( IN CONST CHAR16 * Str1 ,
IN CONST CHAR16 * Str2 ,
2019-09-03 11:58:42 +02:00
IN UINTN Count )
{
CHAR16 Ch1 , Ch2 ;
if ( Count = = 0 ) {
return 0 ;
}
if ( Str1 = = NULL ) {
if ( Str2 = = NULL ) {
return 0 ;
} else {
return - 1 ;
}
} else if ( Str2 = = NULL ) {
return 1 ;
}
do {
Ch1 = TO_LOWER ( * Str1 ) ;
Ch2 = TO_LOWER ( * Str2 ) ;
Str1 + + ;
Str2 + + ;
if ( Ch1 ! = Ch2 ) {
return ( Ch1 - Ch2 ) ;
}
if ( Ch1 = = 0 ) {
return 0 ;
}
} while ( - - Count > 0 ) ;
return 0 ;
}
2020-02-17 21:41:09 +01:00
CONST CHAR16 * StriStr ( IN CONST CHAR16 * Str ,
IN CONST CHAR16 * SearchFor )
2019-09-03 11:58:42 +02:00
{
2020-02-17 21:41:09 +01:00
CONST CHAR16 * End ;
2019-09-03 11:58:42 +02:00
UINTN Length ;
UINTN SearchLength ;
if ( ( Str = = NULL ) | | ( SearchFor = = NULL ) ) {
return NULL ;
}
Length = StrLen ( Str ) ;
if ( Length = = 0 ) {
return NULL ;
}
SearchLength = StrLen ( SearchFor ) ;
if ( SearchLength > Length ) {
return NULL ;
}
End = Str + ( Length - SearchLength ) + 1 ;
while ( Str < End ) {
if ( StrniCmp ( Str , SearchFor , SearchLength ) = = 0 ) {
return Str ;
}
+ + Str ;
}
return NULL ;
}
VOID StrToLower ( IN CHAR16 * Str )
{
while ( * Str ) {
* Str = TO_LOWER ( * Str ) ;
+ + Str ;
}
}
2020-02-17 21:41:09 +01:00
STATIC CHAR16 * * CreateInfoLines ( IN CONST CHAR16 * Message , OUT UINTN * Count )
2019-09-03 11:58:42 +02:00
{
2020-02-17 21:41:09 +01:00
CONST CHAR16 * Ptr ;
CHAR16 * * Information ;
2019-09-03 11:58:42 +02:00
UINTN Index = 0 , Total = 0 ;
UINTN Length = ( ( Message = = NULL ) ? 0 : StrLen ( Message ) ) ;
// Check parameters
if ( Length = = 0 ) {
return NULL ;
}
// Count how many new lines
Ptr = Message - 1 ;
while ( Ptr ! = NULL ) {
+ + Total ;
Ptr = StrStr ( + + Ptr , L " \n " ) ;
}
// Create information
Information = ( CHAR16 * * ) AllocatePool ( ( Total * sizeof ( CHAR16 * ) ) + ( ( Length + 1 ) * sizeof ( CHAR16 ) ) ) ;
if ( Information = = NULL ) {
return NULL ;
}
// Copy strings
2020-02-17 21:41:09 +01:00
CHAR16 * Ptr2 = Information [ Index + + ] = ( CHAR16 * ) ( Information + Total ) ;
StrCpyS ( Ptr2 , Length + 1 , Message ) ;
2019-09-03 11:58:42 +02:00
while ( ( Index < Total ) & &
2020-02-17 21:41:09 +01:00
( ( Ptr2 = ( CHAR16 * ) StrStr ( Ptr2 , L " \n " ) ) ! = NULL ) ) { // cast is ok because FilePath is not const, and we know that StrStr returns a pointer in FilePath. Will disappear when using a string object instead of CHAR16*
* Ptr2 + + = 0 ;
Information [ Index + + ] = Ptr2 ;
2019-09-03 11:58:42 +02:00
}
// Return the info lines
if ( Count ! = NULL ) {
* Count = Total ;
}
return Information ;
}
extern REFIT_MENU_ENTRY MenuEntryReturn ;
STATIC REFIT_MENU_ENTRY * AlertMessageEntries [ ] = { & MenuEntryReturn } ;
STATIC REFIT_MENU_SCREEN AlertMessageMenu = { 0 , NULL , NULL , 0 , NULL , 1 , AlertMessageEntries ,
0 , NULL , NULL , FALSE , FALSE , 0 , 0 , 0 , 0 ,
{ 0 , 0 , 0 , 0 } , NULL } ;
// Display an alert message
2020-02-17 21:41:09 +01:00
VOID AlertMessage ( IN CONST CHAR16 * Title , IN CONST CHAR16 * Message )
2019-09-03 11:58:42 +02:00
{
UINTN Count = 0 ;
// Break message into info lines
CHAR16 * * Information = CreateInfoLines ( Message , & Count ) ;
// Check parameters
if ( Information ! = NULL ) {
if ( Count > 0 ) {
// Display the alert message
AlertMessageMenu . InfoLineCount = Count ;
2020-02-17 21:41:09 +01:00
AlertMessageMenu . InfoLines = ( CONST CHAR16 * * ) Information ;
2019-09-03 11:58:42 +02:00
AlertMessageMenu . Title = Title ;
RunMenu ( & AlertMessageMenu , NULL ) ;
}
FreePool ( Information ) ;
}
}
# define TAG_YES 1
# define TAG_NO 2
STATIC REFIT_MENU_ENTRY YesMessageEntry = { L " Yes " , TAG_YES , 0 , 0 , 0 , NULL , NULL , NULL ,
{ 0 , 0 , 0 , 0 } , ActionEnter , ActionNone , ActionNone , ActionNone , NULL } ;
STATIC REFIT_MENU_ENTRY NoMessageEntry = { L " No " , TAG_NO , 0 , 0 , 0 , NULL , NULL , NULL ,
{ 0 , 0 , 0 , 0 } , ActionEnter , ActionNone , ActionNone , ActionNone , NULL } ;
STATIC REFIT_MENU_ENTRY * YesNoMessageEntries [ ] = { & YesMessageEntry , & NoMessageEntry } ;
STATIC REFIT_MENU_SCREEN YesNoMessageMenu = { 0 , NULL , NULL , 0 , NULL , 2 , YesNoMessageEntries ,
0 , NULL , NULL , FALSE , FALSE , 0 , 0 , 0 , 0 ,
{ 0 , 0 , 0 , 0 } , NULL } ;
// Display a yes/no prompt
2020-02-17 21:41:09 +01:00
BOOLEAN YesNoMessage ( IN CHAR16 * Title , IN CONST CHAR16 * Message )
2019-09-03 11:58:42 +02:00
{
BOOLEAN Result = FALSE ;
UINTN Count = 0 , MenuExit ;
// Break message into info lines
CHAR16 * * Information = CreateInfoLines ( Message , & Count ) ;
// Display the yes/no message
YesNoMessageMenu . InfoLineCount = Count ;
2020-02-17 21:41:09 +01:00
YesNoMessageMenu . InfoLines = ( CONST CHAR16 * * ) Information ;
2019-09-03 11:58:42 +02:00
YesNoMessageMenu . Title = Title ;
do
{
REFIT_MENU_ENTRY * ChosenEntry = NULL ;
MenuExit = RunMenu ( & YesNoMessageMenu , & ChosenEntry ) ;
if ( ( ChosenEntry ! = NULL ) & & ( ChosenEntry - > Tag = = TAG_YES ) & &
( ( MenuExit = = MENU_EXIT_ENTER ) | | ( MenuExit = = MENU_EXIT_DETAILS ) ) ) {
Result = TRUE ;
MenuExit = MENU_EXIT_ENTER ;
}
} while ( MenuExit ! = MENU_EXIT_ENTER ) ;
if ( Information ! = NULL ) {
FreePool ( Information ) ;
}
return Result ;
}
// Ask user for file path from directory menu
BOOLEAN AskUserForFilePathFromDir ( IN CHAR16 * Title OPTIONAL , IN REFIT_VOLUME * Volume ,
IN CHAR16 * ParentPath OPTIONAL , IN EFI_FILE * Dir ,
OUT EFI_DEVICE_PATH_PROTOCOL * * Result )
{
//REFIT_MENU_SCREEN Menu = { 0, L"Please Select File...", NULL, 0, NULL, 0, NULL,
// 0, NULL, FALSE, FALSE, 0, 0, 0, 0, { 0, 0, 0, 0 }, NULL};
// Check parameters
if ( ( Volume = = NULL ) | | ( Dir = = NULL ) | | ( Result = = NULL ) ) {
return FALSE ;
}
// TODO: Generate directory menu
return FALSE ;
}
# define TAG_OFFSET 1000
STATIC REFIT_MENU_SCREEN InitialMenu = { 0 , L " Please Select File... " , NULL , 0 , NULL , 0 , NULL ,
0 , NULL , NULL , FALSE , FALSE , 0 , 0 , 0 , 0 ,
{ 0 , 0 , 0 , 0 } , NULL } ;
// Ask user for file path from volumes menu
BOOLEAN AskUserForFilePathFromVolumes ( IN CHAR16 * Title OPTIONAL , OUT EFI_DEVICE_PATH_PROTOCOL * * Result )
{
REFIT_MENU_SCREEN Menu ;
REFIT_MENU_ENTRY * * Entries ;
REFIT_MENU_ENTRY * EntryPtr ;
UINTN Index = 0 , Count = 0 , MenuExit ;
BOOLEAN Responded = FALSE ;
if ( Result = = NULL ) {
return FALSE ;
}
// Allocate entries
Entries = ( REFIT_MENU_ENTRY * * ) AllocateZeroPool ( sizeof ( REFIT_MENU_ENTRY * ) + ( ( sizeof ( REFIT_MENU_ENTRY * ) + sizeof ( REFIT_MENU_ENTRY ) ) * VolumesCount ) ) ;
if ( Entries = = NULL ) {
return FALSE ;
}
EntryPtr = ( REFIT_MENU_ENTRY * ) ( Entries + ( VolumesCount + 1 ) ) ;
// Create volume entries
for ( Index = 0 ; Index < VolumesCount ; + + Index ) {
REFIT_MENU_ENTRY * Entry ;
REFIT_VOLUME * Volume = Volumes [ Index ] ;
if ( ( Volume = = NULL ) | | ( Volume - > RootDir = = NULL ) | |
( ( Volume - > DevicePathString = = NULL ) & & ( Volume - > VolName = = NULL ) ) ) {
continue ;
}
Entry = Entries [ Count + + ] = EntryPtr + + ;
Entry - > Title = ( Volume - > VolName = = NULL ) ? Volume - > DevicePathString : Volume - > VolName ;
Entry - > Tag = TAG_OFFSET + Index ;
Entry - > AtClick = MENU_EXIT_ENTER ;
}
// Setup menu
CopyMem ( & Menu , & InitialMenu , sizeof ( REFIT_MENU_SCREEN ) ) ;
Entries [ Count + + ] = & MenuEntryReturn ;
Menu . EntryCount = Count ;
Menu . Entries = Entries ;
Menu . Title = Title ;
do
{
REFIT_MENU_ENTRY * ChosenEntry = NULL ;
// Run the volume chooser menu
MenuExit = RunMenu ( & Menu , & ChosenEntry ) ;
if ( ( ChosenEntry ! = NULL ) & &
( ( MenuExit = = MENU_EXIT_ENTER ) | | ( MenuExit = = MENU_EXIT_DETAILS ) ) ) {
if ( ChosenEntry - > Tag > = TAG_OFFSET ) {
Index = ( ChosenEntry - > Tag - TAG_OFFSET ) ;
if ( Index < VolumesCount ) {
// Run directory chooser menu
if ( ! AskUserForFilePathFromDir ( Title , Volumes [ Index ] , NULL , Volumes [ Index ] - > RootDir , Result ) ) {
continue ;
}
Responded = TRUE ;
}
}
break ;
}
} while ( MenuExit ! = MENU_EXIT_ESCAPE ) ;
FreePool ( Entries ) ;
return Responded ;
}
// Ask user for file path
BOOLEAN AskUserForFilePath ( IN CHAR16 * Title OPTIONAL , IN EFI_DEVICE_PATH_PROTOCOL * Root OPTIONAL , OUT EFI_DEVICE_PATH_PROTOCOL * * Result )
{
EFI_FILE * Dir = NULL ;
if ( Result = = NULL ) {
return FALSE ;
}
if ( Root ! = NULL ) {
// Get the file path
CHAR16 * DevicePathStr = FileDevicePathToStr ( Root ) ;
if ( DevicePathStr ! = NULL ) {
UINTN Index = 0 ;
// Check the volumes for a match
for ( Index = 0 ; Index < VolumesCount ; + + Index ) {
REFIT_VOLUME * Volume = Volumes [ Index ] ;
UINTN Length ;
if ( ( Volume = = NULL ) | | ( Volume - > RootDir = = NULL ) | |
( Volume - > DevicePathString = = NULL ) ) {
continue ;
}
Length = StrLen ( Volume - > DevicePathString ) ;
if ( Length = = 0 ) {
continue ;
}
// If the path begins with this volumes path it matches
if ( StrniCmp ( DevicePathStr , Volume - > DevicePathString , Length ) ) {
// Need to
CHAR16 * FilePath = DevicePathStr + Length ;
UINTN FileLength = StrLen ( FilePath ) ;
if ( FileLength = = 0 ) {
// If there is no path left then open the root
return AskUserForFilePathFromDir ( Title , Volume , NULL , Volume - > RootDir , Result ) ;
} else {
// Check to make sure this is directory
if ( ! EFI_ERROR ( Volume - > RootDir - > Open ( Volume - > RootDir , & Dir , FilePath , EFI_FILE_MODE_READ , 0 ) ) & &
( Dir ! = NULL ) ) {
// Get file information
EFI_FILE_INFO * Info = EfiLibFileInfo ( Dir ) ;
if ( Info ! = NULL ) {
// Check if the file is a directory
if ( ( Info - > Attribute & EFI_FILE_DIRECTORY ) = = 0 ) {
// Return the passed device path if it is a file
FreePool ( Info ) ;
Dir - > Close ( Dir ) ;
* Result = Root ;
return TRUE ;
} else {
// Ask user other wise
BOOLEAN Success = AskUserForFilePathFromDir ( Title , Volume , FilePath , Dir , Result ) ;
FreePool ( Info ) ;
Dir - > Close ( Dir ) ;
return Success ;
}
//FreePool(Info);
}
Dir - > Close ( Dir ) ;
}
}
}
}
FreePool ( DevicePathStr ) ;
}
}
return AskUserForFilePathFromVolumes ( Title , Result ) ;
}