/** @file | |
File explorer related functions. | |
Copyright (c) 2004 - 2019, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "FileExplorer.h" | |
EFI_GUID FileExplorerGuid = EFI_FILE_EXPLORE_FORMSET_GUID; | |
/// | |
/// File system selection menu | |
/// | |
MENU_OPTION mFsOptionMenu = { | |
MENU_OPTION_SIGNATURE, | |
{ NULL }, | |
0, | |
FALSE | |
}; | |
FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate = { | |
FILE_EXPLORER_CALLBACK_DATA_SIGNATURE, | |
NULL, | |
NULL, | |
{ | |
LibExtractConfig, | |
LibRouteConfig, | |
LibCallback | |
}, | |
NULL, | |
&mFsOptionMenu, | |
0 | |
}; | |
HII_VENDOR_DEVICE_PATH *gHiiVendorDevicePath; | |
HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath = { | |
{ | |
{ | |
HARDWARE_DEVICE_PATH, | |
HW_VENDOR_DP, | |
{ | |
(UINT8)(sizeof (VENDOR_DEVICE_PATH)), | |
(UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8) | |
} | |
}, | |
// | |
// Will be replace with gEfiCallerIdGuid in code. | |
// | |
{ 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } | |
} | |
}, | |
{ | |
END_DEVICE_PATH_TYPE, | |
END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
{ | |
(UINT8)(END_DEVICE_PATH_LENGTH), | |
(UINT8)((END_DEVICE_PATH_LENGTH) >> 8) | |
} | |
} | |
}; | |
VOID *mLibStartOpCodeHandle = NULL; | |
VOID *mLibEndOpCodeHandle = NULL; | |
EFI_IFR_GUID_LABEL *mLibStartLabel = NULL; | |
EFI_IFR_GUID_LABEL *mLibEndLabel = NULL; | |
UINT16 mQuestionIdUpdate; | |
CHAR16 mNewFileName[MAX_FILE_NAME_LEN]; | |
CHAR16 mNewFolderName[MAX_FOLDER_NAME_LEN]; | |
UINTN mNewFileQuestionId = NEW_FILE_QUESTION_ID_BASE; | |
UINTN mNewFolderQuestionId = NEW_FOLDER_QUESTION_ID_BASE; | |
/** | |
Create a new file or folder in current directory. | |
@param FileName Point to the fileNmae or folder. | |
@param CreateFile CreateFile== TRUE means create a new file. | |
CreateFile== FALSE means create a new Folder. | |
**/ | |
EFI_STATUS | |
LibCreateNewFile ( | |
IN CHAR16 *FileName, | |
IN BOOLEAN CreateFile | |
); | |
/** | |
This function allows a caller to extract the current configuration for one | |
or more named elements from the target driver. | |
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param Request A null-terminated Unicode string in <ConfigRequest> format. | |
@param Progress On return, points to a character in the Request string. | |
Points to the string's null terminator if request was successful. | |
Points to the most recent '&' before the first failing name/value | |
pair (or the beginning of the string if the failure is in the | |
first name/value pair) if the request was not successful. | |
@param Results A null-terminated Unicode string in <ConfigAltResp> format which | |
has all values filled in for the names in the Request string. | |
String to be allocated by the called function. | |
@retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. | |
@retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
LibExtractConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Request, | |
OUT EFI_STRING *Progress, | |
OUT EFI_STRING *Results | |
) | |
{ | |
if ((Progress == NULL) || (Results == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Progress = Request; | |
return EFI_NOT_FOUND; | |
} | |
/** | |
This function processes the results of changes in configuration. | |
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param Configuration A null-terminated Unicode string in <ConfigResp> format. | |
@param Progress A pointer to a string filled in with the offset of the most | |
recent '&' before the first failing name/value pair (or the | |
beginning of the string if the failure is in the first | |
name/value pair) or the terminating NULL if all was successful. | |
@retval EFI_INVALID_PARAMETER Configuration is NULL. | |
@retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
LibRouteConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Configuration, | |
OUT EFI_STRING *Progress | |
) | |
{ | |
if ((Configuration == NULL) || (Progress == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Progress = Configuration; | |
return EFI_NOT_FOUND; | |
} | |
/** | |
This function processes the results of changes in configuration. | |
When user select a interactive opcode, this callback will be triggered. | |
Based on the Question(QuestionId) that triggers the callback, the corresponding | |
actions is performed. It handles: | |
1) Process the axtra action or exit file explorer when user select one file . | |
2) update of file content if a dir is selected. | |
@param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param Action Specifies the type of action taken by the browser. | |
@param QuestionId A unique value which is sent to the original exporting driver | |
so that it can identify the type of data to expect. | |
@param Type The type of value for the question. | |
@param Value A pointer to the data being sent to the original exporting driver. | |
@param ActionRequest On return, points to the action requested by the callback function. | |
@retval EFI_SUCCESS The callback successfully handled the action. | |
@retval other error Error occur when parse one directory. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
LibCallback ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN EFI_BROWSER_ACTION Action, | |
IN EFI_QUESTION_ID QuestionId, | |
IN UINT8 Type, | |
IN EFI_IFR_TYPE_VALUE *Value, | |
OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN NeedExit; | |
CHAR16 *NewFileName; | |
CHAR16 *NewFolderName; | |
NeedExit = TRUE; | |
NewFileName = NULL; | |
NewFolderName = NULL; | |
if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) { | |
// | |
// Do nothing for other UEFI Action. Only do call back when data is changed. | |
// | |
return EFI_UNSUPPORTED; | |
} | |
if (Action == EFI_BROWSER_ACTION_CHANGED) { | |
if ((Value == NULL) || (ActionRequest == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (QuestionId == KEY_VALUE_CREATE_FILE_AND_EXIT) { | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; | |
if (!IsZeroBuffer (mNewFileName, sizeof (mNewFileName))) { | |
Status = LibCreateNewFile (mNewFileName, TRUE); | |
ZeroMem (mNewFileName, sizeof (mNewFileName)); | |
} | |
} | |
if (QuestionId == KEY_VALUE_NO_CREATE_FILE_AND_EXIT) { | |
ZeroMem (mNewFileName, sizeof (mNewFileName)); | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; | |
} | |
if (QuestionId == KEY_VALUE_CREATE_FOLDER_AND_EXIT) { | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; | |
if (!IsZeroBuffer (mNewFolderName, sizeof (mNewFolderName))) { | |
Status = LibCreateNewFile (mNewFolderName, FALSE); | |
ZeroMem (mNewFolderName, sizeof (mNewFolderName)); | |
} | |
} | |
if (QuestionId == KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT) { | |
ZeroMem (mNewFolderName, sizeof (mNewFolderName)); | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; | |
} | |
if (QuestionId == NEW_FILE_NAME_ID) { | |
NewFileName = HiiGetString (gFileExplorerPrivate.FeHiiHandle, Value->string, NULL); | |
if (NewFileName != NULL) { | |
StrCpyS (mNewFileName, MAX_FILE_NAME_LEN, NewFileName); | |
FreePool (NewFileName); | |
NewFileName = NULL; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
if (QuestionId == NEW_FOLDER_NAME_ID) { | |
NewFolderName = HiiGetString (gFileExplorerPrivate.FeHiiHandle, Value->string, NULL); | |
if (NewFolderName != NULL) { | |
StrCpyS (mNewFolderName, MAX_FOLDER_NAME_LEN, NewFolderName); | |
FreePool (NewFolderName); | |
NewFolderName = NULL; | |
} else { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
if (QuestionId >= FILE_OPTION_OFFSET) { | |
LibGetDevicePath (QuestionId); | |
// | |
// Process the extra action. | |
// | |
if (gFileExplorerPrivate.ChooseHandler != NULL) { | |
NeedExit = gFileExplorerPrivate.ChooseHandler (gFileExplorerPrivate.RetDevicePath); | |
} | |
if (NeedExit) { | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; | |
} | |
} | |
} else if (Action == EFI_BROWSER_ACTION_CHANGING) { | |
if (Value == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (QuestionId >= FILE_OPTION_OFFSET) { | |
LibGetDevicePath (QuestionId); | |
Status = LibUpdateFileExplorer (QuestionId); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Create a menu entry by given menu type. | |
@retval NULL If failed to create the menu. | |
@return the new menu entry. | |
**/ | |
MENU_ENTRY * | |
LibCreateMenuEntry ( | |
VOID | |
) | |
{ | |
MENU_ENTRY *MenuEntry; | |
// | |
// Create new menu entry | |
// | |
MenuEntry = AllocateZeroPool (sizeof (MENU_ENTRY)); | |
if (MenuEntry == NULL) { | |
return NULL; | |
} | |
MenuEntry->VariableContext = AllocateZeroPool (sizeof (FILE_CONTEXT)); | |
if (MenuEntry->VariableContext == NULL) { | |
FreePool (MenuEntry); | |
return NULL; | |
} | |
MenuEntry->Signature = MENU_ENTRY_SIGNATURE; | |
return MenuEntry; | |
} | |
/** | |
Get the Menu Entry from the list in Menu Entry List. | |
If MenuNumber is great or equal to the number of Menu | |
Entry in the list, then ASSERT. | |
@param MenuOption The Menu Entry List to read the menu entry. | |
@param MenuNumber The index of Menu Entry. | |
@return The Menu Entry. | |
**/ | |
MENU_ENTRY * | |
LibGetMenuEntry ( | |
MENU_OPTION *MenuOption, | |
UINTN MenuNumber | |
) | |
{ | |
MENU_ENTRY *NewMenuEntry; | |
UINTN Index; | |
LIST_ENTRY *List; | |
ASSERT (MenuNumber < MenuOption->MenuNumber); | |
List = MenuOption->Head.ForwardLink; | |
for (Index = 0; Index < MenuNumber; Index++) { | |
List = List->ForwardLink; | |
} | |
NewMenuEntry = CR (List, MENU_ENTRY, Link, MENU_ENTRY_SIGNATURE); | |
return NewMenuEntry; | |
} | |
/** | |
Free up all resource allocated for a BM_MENU_ENTRY. | |
@param MenuEntry A pointer to BM_MENU_ENTRY. | |
**/ | |
VOID | |
LibDestroyMenuEntry ( | |
MENU_ENTRY *MenuEntry | |
) | |
{ | |
FILE_CONTEXT *FileContext; | |
FileContext = (FILE_CONTEXT *)MenuEntry->VariableContext; | |
if (!FileContext->IsRoot) { | |
if (FileContext->DevicePath != NULL) { | |
FreePool (FileContext->DevicePath); | |
} | |
} else { | |
if (FileContext->FileHandle != NULL) { | |
FileContext->FileHandle->Close (FileContext->FileHandle); | |
} | |
} | |
if (FileContext->FileName != NULL) { | |
FreePool (FileContext->FileName); | |
} | |
FreePool (FileContext); | |
if (MenuEntry->DisplayString != NULL) { | |
FreePool (MenuEntry->DisplayString); | |
} | |
if (MenuEntry->HelpString != NULL) { | |
FreePool (MenuEntry->HelpString); | |
} | |
FreePool (MenuEntry); | |
} | |
/** | |
Free resources allocated in Allocate Rountine. | |
@param FreeMenu Menu to be freed | |
**/ | |
VOID | |
LibFreeMenu ( | |
MENU_OPTION *FreeMenu | |
) | |
{ | |
MENU_ENTRY *MenuEntry; | |
while (!IsListEmpty (&FreeMenu->Head)) { | |
MenuEntry = CR ( | |
FreeMenu->Head.ForwardLink, | |
MENU_ENTRY, | |
Link, | |
MENU_ENTRY_SIGNATURE | |
); | |
RemoveEntryList (&MenuEntry->Link); | |
LibDestroyMenuEntry (MenuEntry); | |
} | |
FreeMenu->MenuNumber = 0; | |
} | |
/** | |
Function opens and returns a file handle to the root directory of a volume. | |
@param DeviceHandle A handle for a device | |
@return A valid file handle or NULL is returned | |
**/ | |
EFI_FILE_HANDLE | |
LibOpenRoot ( | |
IN EFI_HANDLE DeviceHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; | |
EFI_FILE_HANDLE File; | |
File = NULL; | |
// | |
// File the file system interface to the device | |
// | |
Status = gBS->HandleProtocol ( | |
DeviceHandle, | |
&gEfiSimpleFileSystemProtocolGuid, | |
(VOID *)&Volume | |
); | |
// | |
// Open the root directory of the volume | |
// | |
if (!EFI_ERROR (Status)) { | |
Status = Volume->OpenVolume ( | |
Volume, | |
&File | |
); | |
} | |
// | |
// Done | |
// | |
return EFI_ERROR (Status) ? NULL : File; | |
} | |
/** | |
This function converts an input device structure to a Unicode string. | |
@param DevPath A pointer to the device path structure. | |
@return A new allocated Unicode string that represents the device path. | |
**/ | |
CHAR16 * | |
LibDevicePathToStr ( | |
IN EFI_DEVICE_PATH_PROTOCOL *DevPath | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *ToText; | |
EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText; | |
if (DevPath == NULL) { | |
return NULL; | |
} | |
Status = gBS->LocateProtocol ( | |
&gEfiDevicePathToTextProtocolGuid, | |
NULL, | |
(VOID **)&DevPathToText | |
); | |
ASSERT_EFI_ERROR (Status); | |
ToText = DevPathToText->ConvertDevicePathToText ( | |
DevPath, | |
FALSE, | |
TRUE | |
); | |
ASSERT (ToText != NULL); | |
return ToText; | |
} | |
/** | |
Duplicate a string. | |
@param Src The source. | |
@return A new string which is duplicated copy of the source. | |
@retval NULL If there is not enough memory. | |
**/ | |
CHAR16 * | |
LibStrDuplicate ( | |
IN CHAR16 *Src | |
) | |
{ | |
CHAR16 *Dest; | |
UINTN Size; | |
Size = StrSize (Src); | |
Dest = AllocateZeroPool (Size); | |
ASSERT (Dest != NULL); | |
if (Dest != NULL) { | |
CopyMem (Dest, Src, Size); | |
} | |
return Dest; | |
} | |
/** | |
Function gets the file information from an open file descriptor, and stores it | |
in a buffer allocated from pool. | |
@param FHand File Handle. | |
@param InfoType Info type need to get. | |
@retval A pointer to a buffer with file information or NULL is returned | |
**/ | |
VOID * | |
LibFileInfo ( | |
IN EFI_FILE_HANDLE FHand, | |
IN EFI_GUID *InfoType | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_FILE_INFO *Buffer; | |
UINTN BufferSize; | |
Buffer = NULL; | |
BufferSize = 0; | |
Status = FHand->GetInfo ( | |
FHand, | |
InfoType, | |
&BufferSize, | |
Buffer | |
); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
Buffer = AllocatePool (BufferSize); | |
ASSERT (Buffer != NULL); | |
} | |
Status = FHand->GetInfo ( | |
FHand, | |
InfoType, | |
&BufferSize, | |
Buffer | |
); | |
return Buffer; | |
} | |
/** | |
Get file type base on the file name. | |
Just cut the file name, from the ".". eg ".efi" | |
@param FileName File need to be checked. | |
@retval the file type string. | |
**/ | |
CHAR16 * | |
LibGetTypeFromName ( | |
IN CHAR16 *FileName | |
) | |
{ | |
UINTN Index; | |
Index = StrLen (FileName) - 1; | |
while ((FileName[Index] != L'.') && (Index != 0)) { | |
Index--; | |
} | |
return Index == 0 ? NULL : &FileName[Index]; | |
} | |
/** | |
Converts the unicode character of the string from uppercase to lowercase. | |
This is a internal function. | |
@param ConfigString String to be converted | |
**/ | |
VOID | |
LibToLowerString ( | |
IN CHAR16 *String | |
) | |
{ | |
CHAR16 *TmpStr; | |
for (TmpStr = String; *TmpStr != L'\0'; TmpStr++) { | |
if ((*TmpStr >= L'A') && (*TmpStr <= L'Z')) { | |
*TmpStr = (CHAR16)(*TmpStr - L'A' + L'a'); | |
} | |
} | |
} | |
/** | |
Check whether current FileName point to a valid | |
Efi Image File. | |
@param FileName File need to be checked. | |
@retval TRUE Is Efi Image | |
@retval FALSE Not a valid Efi Image | |
**/ | |
BOOLEAN | |
LibIsSupportedFileType ( | |
IN UINT16 *FileName | |
) | |
{ | |
CHAR16 *InputFileType; | |
CHAR16 *TmpStr; | |
BOOLEAN IsSupported; | |
if (gFileExplorerPrivate.FileType == NULL) { | |
return TRUE; | |
} | |
InputFileType = LibGetTypeFromName (FileName); | |
// | |
// If the file not has *.* style, always return TRUE. | |
// | |
if (InputFileType == NULL) { | |
return TRUE; | |
} | |
TmpStr = AllocateCopyPool (StrSize (InputFileType), InputFileType); | |
ASSERT (TmpStr != NULL); | |
LibToLowerString (TmpStr); | |
IsSupported = (StrStr (gFileExplorerPrivate.FileType, TmpStr) == NULL ? FALSE : TRUE); | |
FreePool (TmpStr); | |
return IsSupported; | |
} | |
/** | |
Append file name to existing file name. | |
@param Str1 The existing file name | |
@param Str2 The file name to be appended | |
@return Allocate a new string to hold the appended result. | |
Caller is responsible to free the returned string. | |
**/ | |
CHAR16 * | |
LibAppendFileName ( | |
IN CHAR16 *Str1, | |
IN CHAR16 *Str2 | |
) | |
{ | |
UINTN Size1; | |
UINTN Size2; | |
UINTN MaxLen; | |
CHAR16 *Str; | |
CHAR16 *TmpStr; | |
CHAR16 *Ptr; | |
CHAR16 *LastSlash; | |
Size1 = StrSize (Str1); | |
Size2 = StrSize (Str2); | |
// | |
// Check overflow | |
// | |
if (((MAX_UINTN - Size1) < Size2) || ((MAX_UINTN - Size1 - Size2) < sizeof (CHAR16))) { | |
return NULL; | |
} | |
MaxLen = (Size1 + Size2 + sizeof (CHAR16))/ sizeof (CHAR16); | |
Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16)); | |
ASSERT (Str != NULL); | |
TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16)); | |
ASSERT (TmpStr != NULL); | |
StrCpyS (Str, MaxLen, Str1); | |
if (!((*Str == '\\') && (*(Str + 1) == 0))) { | |
StrCatS (Str, MaxLen, L"\\"); | |
} | |
StrCatS (Str, MaxLen, Str2); | |
Ptr = Str; | |
LastSlash = Str; | |
while (*Ptr != 0) { | |
if ((*Ptr == '\\') && (*(Ptr + 1) == '.') && (*(Ptr + 2) == '.') && (*(Ptr + 3) == L'\\')) { | |
// | |
// Convert "\Name\..\" to "\" | |
// DO NOT convert the .. if it is at the end of the string. This will | |
// break the .. behavior in changing directories. | |
// | |
// | |
// Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings | |
// that overlap. | |
// | |
StrCpyS (TmpStr, MaxLen, Ptr + 3); | |
StrCpyS (LastSlash, MaxLen - ((UINTN)LastSlash - (UINTN)Str) / sizeof (CHAR16), TmpStr); | |
Ptr = LastSlash; | |
} else if ((*Ptr == '\\') && (*(Ptr + 1) == '.') && (*(Ptr + 2) == '\\')) { | |
// | |
// Convert a "\.\" to a "\" | |
// | |
// | |
// Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings | |
// that overlap. | |
// | |
StrCpyS (TmpStr, MaxLen, Ptr + 2); | |
StrCpyS (Ptr, MaxLen - ((UINTN)Ptr - (UINTN)Str) / sizeof (CHAR16), TmpStr); | |
Ptr = LastSlash; | |
} else if (*Ptr == '\\') { | |
LastSlash = Ptr; | |
} | |
Ptr++; | |
} | |
FreePool (TmpStr); | |
return Str; | |
} | |
/** | |
This function build the FsOptionMenu list which records all | |
available file system in the system. They includes all instances | |
of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM. | |
@retval EFI_SUCCESS Success find the file system | |
@retval EFI_OUT_OF_RESOURCES Can not create menu entry | |
**/ | |
EFI_STATUS | |
LibFindFileSystem ( | |
VOID | |
) | |
{ | |
UINTN NoSimpleFsHandles; | |
EFI_HANDLE *SimpleFsHandle; | |
UINT16 *VolumeLabel; | |
UINTN Index; | |
EFI_STATUS Status; | |
MENU_ENTRY *MenuEntry; | |
FILE_CONTEXT *FileContext; | |
UINTN OptionNumber; | |
EFI_FILE_SYSTEM_VOLUME_LABEL *Info; | |
NoSimpleFsHandles = 0; | |
OptionNumber = 0; | |
// | |
// Locate Handles that support Simple File System protocol | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiSimpleFileSystemProtocolGuid, | |
NULL, | |
&NoSimpleFsHandles, | |
&SimpleFsHandle | |
); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Find all the instances of the File System prototocol | |
// | |
for (Index = 0; Index < NoSimpleFsHandles; Index++) { | |
// | |
// Allocate pool for this load option | |
// | |
MenuEntry = LibCreateMenuEntry (); | |
if (NULL == MenuEntry) { | |
FreePool (SimpleFsHandle); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
FileContext = (FILE_CONTEXT *)MenuEntry->VariableContext; | |
FileContext->DeviceHandle = SimpleFsHandle[Index]; | |
FileContext->FileHandle = LibOpenRoot (FileContext->DeviceHandle); | |
if (FileContext->FileHandle == NULL) { | |
LibDestroyMenuEntry (MenuEntry); | |
continue; | |
} | |
MenuEntry->HelpString = LibDevicePathToStr (DevicePathFromHandle (FileContext->DeviceHandle)); | |
FileContext->FileName = LibStrDuplicate (L"\\"); | |
FileContext->DevicePath = FileDevicePath (FileContext->DeviceHandle, FileContext->FileName); | |
FileContext->IsDir = TRUE; | |
FileContext->IsRoot = TRUE; | |
// | |
// Get current file system's Volume Label | |
// | |
Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *)LibFileInfo (FileContext->FileHandle, &gEfiFileSystemVolumeLabelInfoIdGuid); | |
if (Info == NULL) { | |
VolumeLabel = L"NO FILE SYSTEM INFO"; | |
} else { | |
VolumeLabel = Info->VolumeLabel; | |
if (*VolumeLabel == 0x0000) { | |
VolumeLabel = L"NO VOLUME LABEL"; | |
} | |
} | |
MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR); | |
ASSERT (MenuEntry->DisplayString != NULL); | |
UnicodeSPrint ( | |
MenuEntry->DisplayString, | |
MAX_CHAR, | |
L"%s, [%s]", | |
VolumeLabel, | |
MenuEntry->HelpString | |
); | |
MenuEntry->DisplayStringToken = HiiSetString ( | |
gFileExplorerPrivate.FeHiiHandle, | |
0, | |
MenuEntry->DisplayString, | |
NULL | |
); | |
if (Info != NULL) { | |
FreePool (Info); | |
} | |
OptionNumber++; | |
InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &MenuEntry->Link); | |
} | |
} | |
if (NoSimpleFsHandles != 0) { | |
FreePool (SimpleFsHandle); | |
} | |
gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber; | |
return EFI_SUCCESS; | |
} | |
/** | |
Find the file handle from the input menu info. | |
@param MenuEntry Input Menu info. | |
@param RetFileHandle Return the file handle for the input device path. | |
@retval EFI_SUCESS Find the file handle success. | |
@retval Other Find the file handle failure. | |
**/ | |
EFI_STATUS | |
LibGetFileHandleFromMenu ( | |
IN MENU_ENTRY *MenuEntry, | |
OUT EFI_FILE_HANDLE *RetFileHandle | |
) | |
{ | |
EFI_FILE_HANDLE Dir; | |
EFI_FILE_HANDLE NewDir; | |
FILE_CONTEXT *FileContext; | |
EFI_STATUS Status; | |
FileContext = (FILE_CONTEXT *)MenuEntry->VariableContext; | |
Dir = FileContext->FileHandle; | |
// | |
// Open current directory to get files from it | |
// | |
Status = Dir->Open ( | |
Dir, | |
&NewDir, | |
FileContext->FileName, | |
EFI_FILE_READ_ONLY, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
if (!FileContext->IsRoot) { | |
Dir->Close (Dir); | |
} | |
*RetFileHandle = NewDir; | |
return EFI_SUCCESS; | |
} | |
/** | |
Find the file handle from the input device path info. | |
@param RootDirectory Device path info. | |
@param RetFileHandle Return the file handle for the input device path. | |
@param ParentFileName Parent file name. | |
@param DeviceHandle Driver handle for this partition. | |
@retval EFI_SUCESS Find the file handle success. | |
@retval Other Find the file handle failure. | |
**/ | |
EFI_STATUS | |
LibGetFileHandleFromDevicePath ( | |
IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory, | |
OUT EFI_FILE_HANDLE *RetFileHandle, | |
OUT UINT16 **ParentFileName, | |
OUT EFI_HANDLE *DeviceHandle | |
) | |
{ | |
EFI_DEVICE_PATH_PROTOCOL *DevicePathNode; | |
EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode; | |
EFI_STATUS Status; | |
EFI_HANDLE Handle; | |
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; | |
EFI_FILE_HANDLE FileHandle; | |
EFI_FILE_HANDLE LastHandle; | |
CHAR16 *TempPath; | |
*ParentFileName = NULL; | |
// | |
// Attempt to access the file via a file system interface | |
// | |
DevicePathNode = RootDirectory; | |
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathNode, &Handle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Volume); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Open the Volume to get the File System handle | |
// | |
Status = Volume->OpenVolume (Volume, &FileHandle); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*DeviceHandle = Handle; | |
if (IsDevicePathEnd (DevicePathNode)) { | |
*ParentFileName = AllocateCopyPool (StrSize (L"\\"), L"\\"); | |
*RetFileHandle = FileHandle; | |
return EFI_SUCCESS; | |
} | |
// | |
// Duplicate the device path to avoid the access to unaligned device path node. | |
// Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH | |
// nodes, It assures the fields in device path nodes are 2 byte aligned. | |
// | |
TempDevicePathNode = DuplicateDevicePath (DevicePathNode); | |
if (TempDevicePathNode == NULL) { | |
// | |
// Setting Status to an EFI_ERROR value will cause the rest of | |
// the file system support below to be skipped. | |
// | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
// | |
// Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the | |
// directory information and filename can be seperate. The goal is to inch | |
// our way down each device path node and close the previous node | |
// | |
DevicePathNode = TempDevicePathNode; | |
while (!EFI_ERROR (Status) && !IsDevicePathEnd (DevicePathNode)) { | |
if ((DevicePathType (DevicePathNode) != MEDIA_DEVICE_PATH) || | |
(DevicePathSubType (DevicePathNode) != MEDIA_FILEPATH_DP)) | |
{ | |
Status = EFI_UNSUPPORTED; | |
goto Done; | |
} | |
LastHandle = FileHandle; | |
FileHandle = NULL; | |
Status = LastHandle->Open ( | |
LastHandle, | |
&FileHandle, | |
((FILEPATH_DEVICE_PATH *)DevicePathNode)->PathName, | |
EFI_FILE_MODE_READ, | |
0 | |
); | |
if (*ParentFileName == NULL) { | |
*ParentFileName = AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH *)DevicePathNode)->PathName), ((FILEPATH_DEVICE_PATH *)DevicePathNode)->PathName); | |
} else { | |
TempPath = LibAppendFileName (*ParentFileName, ((FILEPATH_DEVICE_PATH *)DevicePathNode)->PathName); | |
if (TempPath == NULL) { | |
LastHandle->Close (LastHandle); | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
FreePool (*ParentFileName); | |
*ParentFileName = TempPath; | |
} | |
// | |
// Close the previous node | |
// | |
LastHandle->Close (LastHandle); | |
DevicePathNode = NextDevicePathNode (DevicePathNode); | |
} | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
*RetFileHandle = FileHandle; | |
Status = EFI_SUCCESS; | |
Done: | |
if (TempDevicePathNode != NULL) { | |
FreePool (TempDevicePathNode); | |
} | |
if ((FileHandle != NULL) && (EFI_ERROR (Status))) { | |
FileHandle->Close (FileHandle); | |
} | |
return Status; | |
} | |
/** | |
Create a new file or folder in current directory. | |
@param FileName Point to the fileNmae or folder name. | |
@param CreateFile CreateFile== TRUE means create a new file. | |
CreateFile== FALSE means create a new Folder. | |
**/ | |
EFI_STATUS | |
LibCreateNewFile ( | |
IN CHAR16 *FileName, | |
IN BOOLEAN CreateFile | |
) | |
{ | |
EFI_FILE_HANDLE FileHandle; | |
EFI_FILE_HANDLE NewHandle; | |
EFI_HANDLE DeviceHandle; | |
EFI_STATUS Status; | |
CHAR16 *ParentName; | |
CHAR16 *FullFileName; | |
NewHandle = NULL; | |
FullFileName = NULL; | |
if (EFI_ERROR (LibGetFileHandleFromDevicePath (gFileExplorerPrivate.RetDevicePath, &FileHandle, &ParentName, &DeviceHandle))) { | |
return EFI_DEVICE_ERROR; | |
} | |
FullFileName = LibAppendFileName (ParentName, FileName); | |
if (FullFileName == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
if (CreateFile) { | |
Status = FileHandle->Open ( | |
FileHandle, | |
&NewHandle, | |
FullFileName, | |
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE| EFI_FILE_MODE_CREATE, | |
0 | |
); | |
if (EFI_ERROR (Status)) { | |
FileHandle->Close (FileHandle); | |
return Status; | |
} | |
} else { | |
Status = FileHandle->Open ( | |
FileHandle, | |
&NewHandle, | |
FullFileName, | |
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE| EFI_FILE_MODE_CREATE, | |
EFI_FILE_DIRECTORY | |
); | |
if (EFI_ERROR (Status)) { | |
FileHandle->Close (FileHandle); | |
return Status; | |
} | |
} | |
FileHandle->Close (FileHandle); | |
// | |
// Return the DevicePath of the new created file or folder. | |
// | |
gFileExplorerPrivate.RetDevicePath = FileDevicePath (DeviceHandle, FullFileName); | |
return EFI_SUCCESS; | |
} | |
/** | |
Find files under current directory. | |
All files and sub-directories in current directory | |
will be stored in DirectoryMenu for future use. | |
@param FileHandle Parent file handle. | |
@param FileName Parent file name. | |
@param DeviceHandle Driver handle for this partition. | |
@retval EFI_SUCCESS Get files from current dir successfully. | |
@return Other value if can't get files from current dir. | |
**/ | |
EFI_STATUS | |
LibFindFiles ( | |
IN EFI_FILE_HANDLE FileHandle, | |
IN UINT16 *FileName, | |
IN EFI_HANDLE DeviceHandle | |
) | |
{ | |
EFI_FILE_INFO *DirInfo; | |
UINTN BufferSize; | |
UINTN DirBufferSize; | |
MENU_ENTRY *NewMenuEntry; | |
FILE_CONTEXT *NewFileContext; | |
UINTN Pass; | |
EFI_STATUS Status; | |
UINTN OptionNumber; | |
OptionNumber = 0; | |
DirBufferSize = sizeof (EFI_FILE_INFO) + 1024; | |
DirInfo = AllocateZeroPool (DirBufferSize); | |
if (DirInfo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Get all files in current directory | |
// Pass 1 to get Directories | |
// Pass 2 to get files that are EFI images | |
// | |
Status = EFI_SUCCESS; | |
for (Pass = 1; Pass <= 2; Pass++) { | |
FileHandle->SetPosition (FileHandle, 0); | |
for ( ; ;) { | |
BufferSize = DirBufferSize; | |
Status = FileHandle->Read (FileHandle, &BufferSize, DirInfo); | |
if (EFI_ERROR (Status) || (BufferSize == 0)) { | |
Status = EFI_SUCCESS; | |
break; | |
} | |
if ((((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0) && (Pass == 2)) || | |
(((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0) && (Pass == 1)) | |
) | |
{ | |
// | |
// Pass 1 is for Directories | |
// Pass 2 is for file names | |
// | |
continue; | |
} | |
if (!(((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0) || LibIsSupportedFileType (DirInfo->FileName))) { | |
// | |
// Slip file unless it is a directory entry or a .EFI file | |
// | |
continue; | |
} | |
NewMenuEntry = LibCreateMenuEntry (); | |
if (NULL == NewMenuEntry) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
NewFileContext = (FILE_CONTEXT *)NewMenuEntry->VariableContext; | |
NewFileContext->DeviceHandle = DeviceHandle; | |
NewFileContext->FileName = LibAppendFileName (FileName, DirInfo->FileName); | |
if (NewFileContext->FileName == NULL) { | |
LibDestroyMenuEntry (NewMenuEntry); | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
NewFileContext->FileHandle = FileHandle; | |
NewFileContext->DevicePath = FileDevicePath (NewFileContext->DeviceHandle, NewFileContext->FileName); | |
NewMenuEntry->HelpString = NULL; | |
NewFileContext->IsDir = (BOOLEAN)((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY); | |
if (NewFileContext->IsDir) { | |
BufferSize = StrLen (DirInfo->FileName) * 2 + 6; | |
NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize); | |
UnicodeSPrint ( | |
NewMenuEntry->DisplayString, | |
BufferSize, | |
L"<%s>", | |
DirInfo->FileName | |
); | |
} else { | |
NewMenuEntry->DisplayString = LibStrDuplicate (DirInfo->FileName); | |
} | |
NewMenuEntry->DisplayStringToken = HiiSetString ( | |
gFileExplorerPrivate.FeHiiHandle, | |
0, | |
NewMenuEntry->DisplayString, | |
NULL | |
); | |
NewFileContext->IsRoot = FALSE; | |
OptionNumber++; | |
InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &NewMenuEntry->Link); | |
} | |
} | |
gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber; | |
Done: | |
FreePool (DirInfo); | |
return Status; | |
} | |
/** | |
Refresh the global UpdateData structure. | |
**/ | |
VOID | |
LibRefreshUpdateData ( | |
VOID | |
) | |
{ | |
// | |
// Free current updated date | |
// | |
if (mLibStartOpCodeHandle != NULL) { | |
HiiFreeOpCodeHandle (mLibStartOpCodeHandle); | |
} | |
if (mLibEndOpCodeHandle != NULL) { | |
HiiFreeOpCodeHandle (mLibEndOpCodeHandle); | |
} | |
// | |
// Create new OpCode Handle | |
// | |
mLibStartOpCodeHandle = HiiAllocateOpCodeHandle (); | |
mLibEndOpCodeHandle = HiiAllocateOpCodeHandle (); | |
// | |
// Create Hii Extend Label OpCode as the start opcode | |
// | |
mLibStartLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( | |
mLibStartOpCodeHandle, | |
&gEfiIfrTianoGuid, | |
NULL, | |
sizeof (EFI_IFR_GUID_LABEL) | |
); | |
mLibStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
mLibStartLabel->Number = FORM_FILE_EXPLORER_ID; | |
// | |
// Create Hii Extend Label OpCode as the start opcode | |
// | |
mLibEndLabel = (EFI_IFR_GUID_LABEL *)HiiCreateGuidOpCode ( | |
mLibEndOpCodeHandle, | |
&gEfiIfrTianoGuid, | |
NULL, | |
sizeof (EFI_IFR_GUID_LABEL) | |
); | |
mLibEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
mLibEndLabel->Number = LABEL_END; | |
} | |
/** | |
Update the File Explore page. | |
**/ | |
VOID | |
LibUpdateFileExplorePage ( | |
VOID | |
) | |
{ | |
UINTN Index; | |
MENU_ENTRY *NewMenuEntry; | |
FILE_CONTEXT *NewFileContext; | |
MENU_OPTION *MenuOption; | |
BOOLEAN CreateNewFile; | |
NewMenuEntry = NULL; | |
NewFileContext = NULL; | |
CreateNewFile = FALSE; | |
LibRefreshUpdateData (); | |
MenuOption = gFileExplorerPrivate.FsOptionMenu; | |
mQuestionIdUpdate += QUESTION_ID_UPDATE_STEP; | |
for (Index = 0; Index < MenuOption->MenuNumber; Index++) { | |
NewMenuEntry = LibGetMenuEntry (MenuOption, Index); | |
NewFileContext = (FILE_CONTEXT *)NewMenuEntry->VariableContext; | |
if (!NewFileContext->IsRoot && !CreateNewFile) { | |
HiiCreateGotoOpCode ( | |
mLibStartOpCodeHandle, | |
FORM_ADD_NEW_FILE_ID, | |
STRING_TOKEN (STR_NEW_FILE), | |
STRING_TOKEN (STR_NEW_FILE_HELP), | |
EFI_IFR_FLAG_CALLBACK, | |
(UINT16)(mNewFileQuestionId++) | |
); | |
HiiCreateGotoOpCode ( | |
mLibStartOpCodeHandle, | |
FORM_ADD_NEW_FOLDER_ID, | |
STRING_TOKEN (STR_NEW_FOLDER), | |
STRING_TOKEN (STR_NEW_FOLDER_HELP), | |
EFI_IFR_FLAG_CALLBACK, | |
(UINT16)(mNewFolderQuestionId++) | |
); | |
HiiCreateTextOpCode ( | |
mLibStartOpCodeHandle, | |
STRING_TOKEN (STR_NULL_STRING), | |
STRING_TOKEN (STR_NULL_STRING), | |
0 | |
); | |
CreateNewFile = TRUE; | |
} | |
if (!NewFileContext->IsDir) { | |
// | |
// Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile. | |
// | |
HiiCreateActionOpCode ( | |
mLibStartOpCodeHandle, | |
(UINT16)(FILE_OPTION_OFFSET + Index + mQuestionIdUpdate), | |
NewMenuEntry->DisplayStringToken, | |
STRING_TOKEN (STR_NULL_STRING), | |
EFI_IFR_FLAG_CALLBACK, | |
0 | |
); | |
} else { | |
// | |
// Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState. | |
// | |
HiiCreateGotoOpCode ( | |
mLibStartOpCodeHandle, | |
FORM_FILE_EXPLORER_ID, | |
NewMenuEntry->DisplayStringToken, | |
STRING_TOKEN (STR_NULL_STRING), | |
EFI_IFR_FLAG_CALLBACK, | |
(UINT16)(FILE_OPTION_OFFSET + Index + mQuestionIdUpdate) | |
); | |
} | |
} | |
HiiUpdateForm ( | |
gFileExplorerPrivate.FeHiiHandle, | |
&FileExplorerGuid, | |
FORM_FILE_EXPLORER_ID, | |
mLibStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID | |
mLibEndOpCodeHandle // LABEL_END | |
); | |
} | |
/** | |
Update the file explower page with the refershed file system. | |
@param KeyValue Key value to identify the type of data to expect. | |
@retval EFI_SUCCESS Update the file explorer form success. | |
@retval other errors Error occur when parse one directory. | |
**/ | |
EFI_STATUS | |
LibUpdateFileExplorer ( | |
IN UINT16 KeyValue | |
) | |
{ | |
UINT16 FileOptionMask; | |
MENU_ENTRY *NewMenuEntry; | |
FILE_CONTEXT *NewFileContext; | |
EFI_STATUS Status; | |
EFI_FILE_HANDLE FileHandle; | |
Status = EFI_SUCCESS; | |
FileOptionMask = (UINT16)(FILE_OPTION_MASK & KeyValue) - mQuestionIdUpdate; | |
NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask); | |
NewFileContext = (FILE_CONTEXT *)NewMenuEntry->VariableContext; | |
if (NewFileContext->IsDir) { | |
RemoveEntryList (&NewMenuEntry->Link); | |
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); | |
Status = LibGetFileHandleFromMenu (NewMenuEntry, &FileHandle); | |
if (!EFI_ERROR (Status)) { | |
Status = LibFindFiles (FileHandle, NewFileContext->FileName, NewFileContext->DeviceHandle); | |
if (!EFI_ERROR (Status)) { | |
LibUpdateFileExplorePage (); | |
} else { | |
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); | |
} | |
} | |
LibDestroyMenuEntry (NewMenuEntry); | |
} | |
return Status; | |
} | |
/** | |
Get the device path info saved in the menu structure. | |
@param KeyValue Key value to identify the type of data to expect. | |
**/ | |
VOID | |
LibGetDevicePath ( | |
IN UINT16 KeyValue | |
) | |
{ | |
UINT16 FileOptionMask; | |
MENU_ENTRY *NewMenuEntry; | |
FILE_CONTEXT *NewFileContext; | |
FileOptionMask = (UINT16)(FILE_OPTION_MASK & KeyValue) - mQuestionIdUpdate; | |
NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask); | |
NewFileContext = (FILE_CONTEXT *)NewMenuEntry->VariableContext; | |
if (gFileExplorerPrivate.RetDevicePath != NULL) { | |
FreePool (gFileExplorerPrivate.RetDevicePath); | |
} | |
gFileExplorerPrivate.RetDevicePath = DuplicateDevicePath (NewFileContext->DevicePath); | |
} | |
/** | |
Choose a file in the specified directory. | |
If user input NULL for the RootDirectory, will choose file in the system. | |
If user input *File != NULL, function will return the allocate device path | |
info for the choosed file, caller has to free the memory after use it. | |
@param RootDirectory Pointer to the root directory. | |
@param FileType The file type need to choose. | |
@param ChooseHandler Function pointer to the extra task need to do | |
after choose one file. | |
@param File Return the device path for the last time chosed file. | |
@retval EFI_SUCESS Choose file success. | |
@retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL | |
One of them must not NULL. | |
@retval Other errors Choose file failed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
ChooseFile ( | |
IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory, | |
IN CHAR16 *FileType OPTIONAL, | |
IN CHOOSE_HANDLER ChooseHandler OPTIONAL, | |
OUT EFI_DEVICE_PATH_PROTOCOL **File OPTIONAL | |
) | |
{ | |
EFI_FILE_HANDLE FileHandle; | |
EFI_STATUS Status; | |
UINT16 *FileName; | |
EFI_HANDLE DeviceHandle; | |
if ((ChooseHandler == NULL) && (File == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
mQuestionIdUpdate = 0; | |
FileName = NULL; | |
gFileExplorerPrivate.RetDevicePath = NULL; | |
gFileExplorerPrivate.ChooseHandler = ChooseHandler; | |
if (FileType != NULL) { | |
gFileExplorerPrivate.FileType = AllocateCopyPool (StrSize (FileType), FileType); | |
ASSERT (gFileExplorerPrivate.FileType != NULL); | |
LibToLowerString (gFileExplorerPrivate.FileType); | |
} else { | |
gFileExplorerPrivate.FileType = NULL; | |
} | |
if (RootDirectory == NULL) { | |
Status = LibFindFileSystem (); | |
} else { | |
Status = LibGetFileHandleFromDevicePath (RootDirectory, &FileHandle, &FileName, &DeviceHandle); | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
Status = LibFindFiles (FileHandle, FileName, DeviceHandle); | |
} | |
if (EFI_ERROR (Status)) { | |
goto Done; | |
} | |
LibUpdateFileExplorePage (); | |
gFileExplorerPrivate.FormBrowser2->SendForm ( | |
gFileExplorerPrivate.FormBrowser2, | |
&gFileExplorerPrivate.FeHiiHandle, | |
1, | |
&FileExplorerGuid, | |
0, | |
NULL, | |
NULL | |
); | |
Done: | |
if ((Status == EFI_SUCCESS) && (File != NULL)) { | |
*File = gFileExplorerPrivate.RetDevicePath; | |
} else if (gFileExplorerPrivate.RetDevicePath != NULL) { | |
FreePool (gFileExplorerPrivate.RetDevicePath); | |
} | |
if (gFileExplorerPrivate.FileType != NULL) { | |
FreePool (gFileExplorerPrivate.FileType); | |
} | |
LibFreeMenu (gFileExplorerPrivate.FsOptionMenu); | |
if (FileName != NULL) { | |
FreePool (FileName); | |
} | |
return Status; | |
} | |
/** | |
Install Boot Manager Menu driver. | |
@param ImageHandle The image handle. | |
@param SystemTable The system table. | |
@retval EFI_SUCEESS Install File explorer library success. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FileExplorerLibConstructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
gHiiVendorDevicePath = (HII_VENDOR_DEVICE_PATH *)DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL *)&FeHiiVendorDevicePath); | |
ASSERT (gHiiVendorDevicePath != NULL); | |
CopyGuid (&gHiiVendorDevicePath->VendorDevicePath.Guid, &gEfiCallerIdGuid); | |
// | |
// Install Device Path Protocol and Config Access protocol to driver handle | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&gFileExplorerPrivate.FeDriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
gHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&gFileExplorerPrivate.FeConfigAccess, | |
NULL | |
); | |
if (Status == EFI_ALREADY_STARTED) { | |
return EFI_SUCCESS; | |
} | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
// | |
// Post our File Explorer VFR binary to the HII database. | |
// | |
gFileExplorerPrivate.FeHiiHandle = HiiAddPackages ( | |
&FileExplorerGuid, | |
gFileExplorerPrivate.FeDriverHandle, | |
FileExplorerVfrBin, | |
FileExplorerLibStrings, | |
NULL | |
); | |
ASSERT (gFileExplorerPrivate.FeHiiHandle != NULL); | |
// | |
// Locate Formbrowser2 protocol | |
// | |
Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **)&gFileExplorerPrivate.FormBrowser2); | |
ASSERT_EFI_ERROR (Status); | |
InitializeListHead (&gFileExplorerPrivate.FsOptionMenu->Head); | |
return EFI_SUCCESS; | |
} | |
/** | |
Unloads the application and its installed protocol. | |
@param[in] ImageHandle Handle that identifies the image to be unloaded. | |
@param[in] SystemTable The system table. | |
@retval EFI_SUCCESS The image has been unloaded. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
FileExplorerLibDestructor ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
ASSERT (gHiiVendorDevicePath != NULL); | |
if (gFileExplorerPrivate.FeDriverHandle != NULL) { | |
Status = gBS->UninstallMultipleProtocolInterfaces ( | |
gFileExplorerPrivate.FeDriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
gHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&gFileExplorerPrivate.FeConfigAccess, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
HiiRemovePackages (gFileExplorerPrivate.FeHiiHandle); | |
gFileExplorerPrivate.FeDriverHandle = NULL; | |
} | |
FreePool (gHiiVendorDevicePath); | |
return EFI_SUCCESS; | |
} |