mirror of https://github.com/acidanthera/audk.git
1652 lines
46 KiB
C
1652 lines
46 KiB
C
/** @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;
|
|
|
|
LibGetFileHandleFromDevicePath(gFileExplorerPrivate.RetDevicePath, &FileHandle, &ParentName, &DeviceHandle);
|
|
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;
|
|
}
|
|
|