2 File explorer related functions.
4 Copyright (c) 2004 - 2017, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available under
6 the terms and conditions of the BSD License that accompanies this distribution.
7 The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
16 #include "FileExplorer.h"
18 EFI_GUID FileExplorerGuid
= EFI_FILE_EXPLORE_FORMSET_GUID
;
21 /// File system selection menu
23 MENU_OPTION mFsOptionMenu
= {
24 MENU_OPTION_SIGNATURE
,
30 FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate
= {
31 FILE_EXPLORER_CALLBACK_DATA_SIGNATURE
,
44 HII_VENDOR_DEVICE_PATH
*gHiiVendorDevicePath
;
46 HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath
= {
52 (UINT8
) (sizeof (VENDOR_DEVICE_PATH
)),
53 (UINT8
) ((sizeof (VENDOR_DEVICE_PATH
)) >> 8)
57 // Will be replace with gEfiCallerIdGuid in code.
59 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
63 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
65 (UINT8
) (END_DEVICE_PATH_LENGTH
),
66 (UINT8
) ((END_DEVICE_PATH_LENGTH
) >> 8)
71 VOID
*mLibStartOpCodeHandle
= NULL
;
72 VOID
*mLibEndOpCodeHandle
= NULL
;
73 EFI_IFR_GUID_LABEL
*mLibStartLabel
= NULL
;
74 EFI_IFR_GUID_LABEL
*mLibEndLabel
= NULL
;
75 UINT16 mQuestionIdUpdate
;
76 CHAR16 mNewFileName
[MAX_FILE_NAME_LEN
];
77 CHAR16 mNewFolderName
[MAX_FOLDER_NAME_LEN
];
78 UINTN mNewFileQuestionId
= NEW_FILE_QUESTION_ID_BASE
;
79 UINTN mNewFolderQuestionId
= NEW_FOLDER_QUESTION_ID_BASE
;
82 Create a new file or folder in current directory.
84 @param FileName Point to the fileNmae or folder.
85 @param CreateFile CreateFile== TRUE means create a new file.
86 CreateFile== FALSE means create a new Folder.
96 This function allows a caller to extract the current configuration for one
97 or more named elements from the target driver.
100 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
101 @param Request A null-terminated Unicode string in <ConfigRequest> format.
102 @param Progress On return, points to a character in the Request string.
103 Points to the string's null terminator if request was successful.
104 Points to the most recent '&' before the first failing name/value
105 pair (or the beginning of the string if the failure is in the
106 first name/value pair) if the request was not successful.
107 @param Results A null-terminated Unicode string in <ConfigAltResp> format which
108 has all values filled in for the names in the Request string.
109 String to be allocated by the called function.
111 @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
112 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
118 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
119 IN CONST EFI_STRING Request
,
120 OUT EFI_STRING
*Progress
,
121 OUT EFI_STRING
*Results
124 if (Progress
== NULL
|| Results
== NULL
) {
125 return EFI_INVALID_PARAMETER
;
129 return EFI_NOT_FOUND
;
133 This function processes the results of changes in configuration.
136 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
137 @param Configuration A null-terminated Unicode string in <ConfigResp> format.
138 @param Progress A pointer to a string filled in with the offset of the most
139 recent '&' before the first failing name/value pair (or the
140 beginning of the string if the failure is in the first
141 name/value pair) or the terminating NULL if all was successful.
143 @retval EFI_INVALID_PARAMETER Configuration is NULL.
144 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
150 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
151 IN CONST EFI_STRING Configuration
,
152 OUT EFI_STRING
*Progress
155 if (Configuration
== NULL
|| Progress
== NULL
) {
156 return EFI_INVALID_PARAMETER
;
159 *Progress
= Configuration
;
160 return EFI_NOT_FOUND
;
164 This function processes the results of changes in configuration.
165 When user select a interactive opcode, this callback will be triggered.
166 Based on the Question(QuestionId) that triggers the callback, the corresponding
167 actions is performed. It handles:
169 1) Process the axtra action or exit file explorer when user select one file .
170 2) update of file content if a dir is selected.
172 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
173 @param Action Specifies the type of action taken by the browser.
174 @param QuestionId A unique value which is sent to the original exporting driver
175 so that it can identify the type of data to expect.
176 @param Type The type of value for the question.
177 @param Value A pointer to the data being sent to the original exporting driver.
178 @param ActionRequest On return, points to the action requested by the callback function.
180 @retval EFI_SUCCESS The callback successfully handled the action.
181 @retval other error Error occur when parse one directory.
186 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
187 IN EFI_BROWSER_ACTION Action
,
188 IN EFI_QUESTION_ID QuestionId
,
190 IN EFI_IFR_TYPE_VALUE
*Value
,
191 OUT EFI_BROWSER_ACTION_REQUEST
*ActionRequest
197 CHAR16
*NewFolderName
;
201 NewFolderName
= NULL
;
203 if (Action
!= EFI_BROWSER_ACTION_CHANGING
&& Action
!= EFI_BROWSER_ACTION_CHANGED
) {
205 // Do nothing for other UEFI Action. Only do call back when data is changed.
207 return EFI_UNSUPPORTED
;
210 if (Action
== EFI_BROWSER_ACTION_CHANGED
) {
211 if ((Value
== NULL
) || (ActionRequest
== NULL
)) {
212 return EFI_INVALID_PARAMETER
;
215 if (QuestionId
== KEY_VALUE_CREATE_FILE_AND_EXIT
) {
216 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
217 if (!IsZeroBuffer (mNewFileName
, sizeof (mNewFileName
))) {
218 Status
= LibCreateNewFile (mNewFileName
,TRUE
);
219 ZeroMem (mNewFileName
,sizeof (mNewFileName
));
223 if (QuestionId
== KEY_VALUE_NO_CREATE_FILE_AND_EXIT
) {
224 ZeroMem (mNewFileName
,sizeof (mNewFileName
));
225 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
228 if (QuestionId
== KEY_VALUE_CREATE_FOLDER_AND_EXIT
) {
229 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
230 if (!IsZeroBuffer (mNewFolderName
, sizeof (mNewFolderName
))) {
231 Status
= LibCreateNewFile (mNewFolderName
, FALSE
);
232 ZeroMem (mNewFolderName
,sizeof (mNewFolderName
));
236 if (QuestionId
== KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT
) {
237 ZeroMem (mNewFolderName
,sizeof (mNewFolderName
));
238 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
241 if (QuestionId
== NEW_FILE_NAME_ID
) {
242 NewFileName
= HiiGetString (gFileExplorerPrivate
.FeHiiHandle
, Value
->string
, NULL
);
243 if (NewFileName
!= NULL
) {
244 StrCpyS (mNewFileName
, MAX_FILE_NAME_LEN
, NewFileName
);
245 FreePool (NewFileName
);
248 return EFI_INVALID_PARAMETER
;
252 if (QuestionId
== NEW_FOLDER_NAME_ID
) {
253 NewFolderName
= HiiGetString (gFileExplorerPrivate
.FeHiiHandle
, Value
->string
, NULL
);
254 if (NewFolderName
!= NULL
) {
255 StrCpyS (mNewFolderName
, MAX_FOLDER_NAME_LEN
, NewFolderName
);
256 FreePool (NewFolderName
);
257 NewFolderName
= NULL
;
259 return EFI_INVALID_PARAMETER
;
263 if (QuestionId
>= FILE_OPTION_OFFSET
) {
264 LibGetDevicePath(QuestionId
);
267 // Process the extra action.
269 if (gFileExplorerPrivate
.ChooseHandler
!= NULL
) {
270 NeedExit
= gFileExplorerPrivate
.ChooseHandler (gFileExplorerPrivate
.RetDevicePath
);
274 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
277 } else if (Action
== EFI_BROWSER_ACTION_CHANGING
) {
279 return EFI_INVALID_PARAMETER
;
281 if (QuestionId
>= FILE_OPTION_OFFSET
) {
282 LibGetDevicePath(QuestionId
);
283 Status
= LibUpdateFileExplorer (QuestionId
);
284 if (EFI_ERROR (Status
)) {
294 Create a menu entry by given menu type.
296 @retval NULL If failed to create the menu.
297 @return the new menu entry.
305 MENU_ENTRY
*MenuEntry
;
308 // Create new menu entry
310 MenuEntry
= AllocateZeroPool (sizeof (MENU_ENTRY
));
311 if (MenuEntry
== NULL
) {
315 MenuEntry
->VariableContext
= AllocateZeroPool (sizeof (FILE_CONTEXT
));
316 if (MenuEntry
->VariableContext
== NULL
) {
317 FreePool (MenuEntry
);
321 MenuEntry
->Signature
= MENU_ENTRY_SIGNATURE
;
327 Get the Menu Entry from the list in Menu Entry List.
329 If MenuNumber is great or equal to the number of Menu
330 Entry in the list, then ASSERT.
332 @param MenuOption The Menu Entry List to read the menu entry.
333 @param MenuNumber The index of Menu Entry.
335 @return The Menu Entry.
340 MENU_OPTION
*MenuOption
,
344 MENU_ENTRY
*NewMenuEntry
;
348 ASSERT (MenuNumber
< MenuOption
->MenuNumber
);
350 List
= MenuOption
->Head
.ForwardLink
;
351 for (Index
= 0; Index
< MenuNumber
; Index
++) {
352 List
= List
->ForwardLink
;
355 NewMenuEntry
= CR (List
, MENU_ENTRY
, Link
, MENU_ENTRY_SIGNATURE
);
361 Free up all resource allocated for a BM_MENU_ENTRY.
363 @param MenuEntry A pointer to BM_MENU_ENTRY.
367 LibDestroyMenuEntry (
368 MENU_ENTRY
*MenuEntry
371 FILE_CONTEXT
*FileContext
;
373 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
375 if (!FileContext
->IsRoot
) {
376 if (FileContext
->DevicePath
!= NULL
) {
377 FreePool (FileContext
->DevicePath
);
380 if (FileContext
->FileHandle
!= NULL
) {
381 FileContext
->FileHandle
->Close (FileContext
->FileHandle
);
385 if (FileContext
->FileName
!= NULL
) {
386 FreePool (FileContext
->FileName
);
389 FreePool (FileContext
);
391 if (MenuEntry
->DisplayString
!= NULL
) {
392 FreePool (MenuEntry
->DisplayString
);
394 if (MenuEntry
->HelpString
!= NULL
) {
395 FreePool (MenuEntry
->HelpString
);
398 FreePool (MenuEntry
);
403 Free resources allocated in Allocate Rountine.
405 @param FreeMenu Menu to be freed
409 MENU_OPTION
*FreeMenu
412 MENU_ENTRY
*MenuEntry
;
413 while (!IsListEmpty (&FreeMenu
->Head
)) {
415 FreeMenu
->Head
.ForwardLink
,
420 RemoveEntryList (&MenuEntry
->Link
);
421 LibDestroyMenuEntry (MenuEntry
);
423 FreeMenu
->MenuNumber
= 0;
428 Function opens and returns a file handle to the root directory of a volume.
430 @param DeviceHandle A handle for a device
432 @return A valid file handle or NULL is returned
437 IN EFI_HANDLE DeviceHandle
441 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Volume
;
442 EFI_FILE_HANDLE File
;
447 // File the file system interface to the device
449 Status
= gBS
->HandleProtocol (
451 &gEfiSimpleFileSystemProtocolGuid
,
456 // Open the root directory of the volume
458 if (!EFI_ERROR (Status
)) {
459 Status
= Volume
->OpenVolume (
467 return EFI_ERROR (Status
) ? NULL
: File
;
471 This function converts an input device structure to a Unicode string.
473 @param DevPath A pointer to the device path structure.
475 @return A new allocated Unicode string that represents the device path.
480 IN EFI_DEVICE_PATH_PROTOCOL
*DevPath
485 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
*DevPathToText
;
487 if (DevPath
== NULL
) {
491 Status
= gBS
->LocateProtocol (
492 &gEfiDevicePathToTextProtocolGuid
,
494 (VOID
**) &DevPathToText
496 ASSERT_EFI_ERROR (Status
);
497 ToText
= DevPathToText
->ConvertDevicePathToText (
502 ASSERT (ToText
!= NULL
);
510 @param Src The source.
512 @return A new string which is duplicated copy of the source.
513 @retval NULL If there is not enough memory.
524 Size
= StrSize (Src
);
525 Dest
= AllocateZeroPool (Size
);
526 ASSERT (Dest
!= NULL
);
528 CopyMem (Dest
, Src
, Size
);
536 Function gets the file information from an open file descriptor, and stores it
537 in a buffer allocated from pool.
539 @param FHand File Handle.
540 @param InfoType Info type need to get.
542 @retval A pointer to a buffer with file information or NULL is returned
547 IN EFI_FILE_HANDLE FHand
,
548 IN EFI_GUID
*InfoType
552 EFI_FILE_INFO
*Buffer
;
558 Status
= FHand
->GetInfo (
564 if (Status
== EFI_BUFFER_TOO_SMALL
) {
565 Buffer
= AllocatePool (BufferSize
);
566 ASSERT (Buffer
!= NULL
);
569 Status
= FHand
->GetInfo (
581 Get file type base on the file name.
582 Just cut the file name, from the ".". eg ".efi"
584 @param FileName File need to be checked.
586 @retval the file type string.
596 Index
= StrLen (FileName
) - 1;
597 while ((FileName
[Index
] != L
'.') && (Index
!= 0)) {
601 return Index
== 0 ? NULL
: &FileName
[Index
];
605 Converts the unicode character of the string from uppercase to lowercase.
606 This is a internal function.
608 @param ConfigString String to be converted
618 for (TmpStr
= String
; *TmpStr
!= L
'\0'; TmpStr
++) {
619 if (*TmpStr
>= L
'A' && *TmpStr
<= L
'Z') {
620 *TmpStr
= (CHAR16
) (*TmpStr
- L
'A' + L
'a');
627 Check whether current FileName point to a valid
630 @param FileName File need to be checked.
632 @retval TRUE Is Efi Image
633 @retval FALSE Not a valid Efi Image
637 LibIsSupportedFileType (
641 CHAR16
*InputFileType
;
645 if (gFileExplorerPrivate
.FileType
== NULL
) {
649 InputFileType
= LibGetTypeFromName (FileName
);
651 // If the file not has *.* style, always return TRUE.
653 if (InputFileType
== NULL
) {
657 TmpStr
= AllocateCopyPool (StrSize (InputFileType
), InputFileType
);
658 ASSERT(TmpStr
!= NULL
);
659 LibToLowerString(TmpStr
);
661 IsSupported
= (StrStr (gFileExplorerPrivate
.FileType
, TmpStr
) == NULL
? FALSE
: TRUE
);
669 Append file name to existing file name.
671 @param Str1 The existing file name
672 @param Str2 The file name to be appended
674 @return Allocate a new string to hold the appended result.
675 Caller is responsible to free the returned string.
692 Size1
= StrSize (Str1
);
693 Size2
= StrSize (Str2
);
698 if (((MAX_UINTN
- Size1
) < Size2
) || ((MAX_UINTN
- Size1
- Size2
) < sizeof(CHAR16
))) {
702 MaxLen
= (Size1
+ Size2
+ sizeof (CHAR16
))/ sizeof (CHAR16
);
703 Str
= AllocateZeroPool (Size1
+ Size2
+ sizeof (CHAR16
));
704 ASSERT (Str
!= NULL
);
706 TmpStr
= AllocateZeroPool (Size1
+ Size2
+ sizeof (CHAR16
));
707 ASSERT (TmpStr
!= NULL
);
709 StrCpyS (Str
, MaxLen
, Str1
);
710 if (!((*Str
== '\\') && (*(Str
+ 1) == 0))) {
711 StrCatS (Str
, MaxLen
, L
"\\");
714 StrCatS (Str
, MaxLen
, Str2
);
719 if (*Ptr
== '\\' && *(Ptr
+ 1) == '.' && *(Ptr
+ 2) == '.' && *(Ptr
+ 3) == L
'\\') {
721 // Convert "\Name\..\" to "\"
722 // DO NOT convert the .. if it is at the end of the string. This will
723 // break the .. behavior in changing directories.
727 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
730 StrCpyS (TmpStr
, MaxLen
, Ptr
+ 3);
731 StrCpyS (LastSlash
, MaxLen
- (UINTN
) (LastSlash
- Str
), TmpStr
);
733 } else if (*Ptr
== '\\' && *(Ptr
+ 1) == '.' && *(Ptr
+ 2) == '\\') {
735 // Convert a "\.\" to a "\"
739 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
742 StrCpyS (TmpStr
, MaxLen
, Ptr
+ 2);
743 StrCpyS (Ptr
, MaxLen
- (UINTN
) (Ptr
- Str
), TmpStr
);
745 } else if (*Ptr
== '\\') {
758 This function build the FsOptionMenu list which records all
759 available file system in the system. They includes all instances
760 of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM.
763 @retval EFI_SUCCESS Success find the file system
764 @retval EFI_OUT_OF_RESOURCES Can not create menu entry
772 UINTN NoSimpleFsHandles
;
773 EFI_HANDLE
*SimpleFsHandle
;
777 MENU_ENTRY
*MenuEntry
;
778 FILE_CONTEXT
*FileContext
;
780 EFI_FILE_SYSTEM_VOLUME_LABEL
*Info
;
782 NoSimpleFsHandles
= 0;
786 // Locate Handles that support Simple File System protocol
788 Status
= gBS
->LocateHandleBuffer (
790 &gEfiSimpleFileSystemProtocolGuid
,
795 if (!EFI_ERROR (Status
)) {
797 // Find all the instances of the File System prototocol
799 for (Index
= 0; Index
< NoSimpleFsHandles
; Index
++) {
801 // Allocate pool for this load option
803 MenuEntry
= LibCreateMenuEntry ();
804 if (NULL
== MenuEntry
) {
805 FreePool (SimpleFsHandle
);
806 return EFI_OUT_OF_RESOURCES
;
809 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
810 FileContext
->DeviceHandle
= SimpleFsHandle
[Index
];
811 FileContext
->FileHandle
= LibOpenRoot (FileContext
->DeviceHandle
);
812 if (FileContext
->FileHandle
== NULL
) {
813 LibDestroyMenuEntry (MenuEntry
);
817 MenuEntry
->HelpString
= LibDevicePathToStr (DevicePathFromHandle (FileContext
->DeviceHandle
));
818 FileContext
->FileName
= LibStrDuplicate (L
"\\");
819 FileContext
->DevicePath
= FileDevicePath (FileContext
->DeviceHandle
, FileContext
->FileName
);
820 FileContext
->IsDir
= TRUE
;
821 FileContext
->IsRoot
= TRUE
;
824 // Get current file system's Volume Label
826 Info
= (EFI_FILE_SYSTEM_VOLUME_LABEL
*) LibFileInfo (FileContext
->FileHandle
, &gEfiFileSystemVolumeLabelInfoIdGuid
);
828 VolumeLabel
= L
"NO FILE SYSTEM INFO";
830 if (Info
->VolumeLabel
== NULL
) {
831 VolumeLabel
= L
"NULL VOLUME LABEL";
833 VolumeLabel
= Info
->VolumeLabel
;
834 if (*VolumeLabel
== 0x0000) {
835 VolumeLabel
= L
"NO VOLUME LABEL";
839 MenuEntry
->DisplayString
= AllocateZeroPool (MAX_CHAR
);
840 ASSERT (MenuEntry
->DisplayString
!= NULL
);
842 MenuEntry
->DisplayString
,
846 MenuEntry
->HelpString
848 MenuEntry
->DisplayStringToken
= HiiSetString (
849 gFileExplorerPrivate
.FeHiiHandle
,
851 MenuEntry
->DisplayString
,
859 InsertTailList (&gFileExplorerPrivate
.FsOptionMenu
->Head
, &MenuEntry
->Link
);
863 if (NoSimpleFsHandles
!= 0) {
864 FreePool (SimpleFsHandle
);
867 gFileExplorerPrivate
.FsOptionMenu
->MenuNumber
= OptionNumber
;
873 Find the file handle from the input menu info.
875 @param MenuEntry Input Menu info.
876 @param RetFileHandle Return the file handle for the input device path.
878 @retval EFI_SUCESS Find the file handle success.
879 @retval Other Find the file handle failure.
882 LibGetFileHandleFromMenu (
883 IN MENU_ENTRY
*MenuEntry
,
884 OUT EFI_FILE_HANDLE
*RetFileHandle
888 EFI_FILE_HANDLE NewDir
;
889 FILE_CONTEXT
*FileContext
;
892 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
893 Dir
= FileContext
->FileHandle
;
896 // Open current directory to get files from it
901 FileContext
->FileName
,
905 if (EFI_ERROR (Status
)) {
909 if (!FileContext
->IsRoot
) {
913 *RetFileHandle
= NewDir
;
919 Find the file handle from the input device path info.
921 @param RootDirectory Device path info.
922 @param RetFileHandle Return the file handle for the input device path.
923 @param ParentFileName Parent file name.
924 @param DeviceHandle Driver handle for this partition.
926 @retval EFI_SUCESS Find the file handle success.
927 @retval Other Find the file handle failure.
930 LibGetFileHandleFromDevicePath (
931 IN EFI_DEVICE_PATH_PROTOCOL
*RootDirectory
,
932 OUT EFI_FILE_HANDLE
*RetFileHandle
,
933 OUT UINT16
**ParentFileName
,
934 OUT EFI_HANDLE
*DeviceHandle
937 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
938 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePathNode
;
941 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Volume
;
942 EFI_FILE_HANDLE FileHandle
;
943 EFI_FILE_HANDLE LastHandle
;
946 *ParentFileName
= NULL
;
949 // Attempt to access the file via a file system interface
951 DevicePathNode
= RootDirectory
;
952 Status
= gBS
->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid
, &DevicePathNode
, &Handle
);
953 if (EFI_ERROR (Status
)) {
957 Status
= gBS
->HandleProtocol (Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Volume
);
958 if (EFI_ERROR (Status
)) {
963 // Open the Volume to get the File System handle
965 Status
= Volume
->OpenVolume (Volume
, &FileHandle
);
966 if (EFI_ERROR (Status
)) {
970 *DeviceHandle
= Handle
;
972 if (IsDevicePathEnd(DevicePathNode
)) {
973 *ParentFileName
= AllocateCopyPool (StrSize (L
"\\"), L
"\\");
974 *RetFileHandle
= FileHandle
;
979 // Duplicate the device path to avoid the access to unaligned device path node.
980 // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
981 // nodes, It assures the fields in device path nodes are 2 byte aligned.
983 TempDevicePathNode
= DuplicateDevicePath (DevicePathNode
);
984 if (TempDevicePathNode
== NULL
) {
987 // Setting Status to an EFI_ERROR value will cause the rest of
988 // the file system support below to be skipped.
990 Status
= EFI_OUT_OF_RESOURCES
;
995 // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
996 // directory information and filename can be seperate. The goal is to inch
997 // our way down each device path node and close the previous node
999 DevicePathNode
= TempDevicePathNode
;
1000 while (!EFI_ERROR (Status
) && !IsDevicePathEnd (DevicePathNode
)) {
1001 if (DevicePathType (DevicePathNode
) != MEDIA_DEVICE_PATH
||
1002 DevicePathSubType (DevicePathNode
) != MEDIA_FILEPATH_DP
) {
1003 Status
= EFI_UNSUPPORTED
;
1007 LastHandle
= FileHandle
;
1010 Status
= LastHandle
->Open (
1013 ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
,
1017 if (*ParentFileName
== NULL
) {
1018 *ParentFileName
= AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
), ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
);
1020 TempPath
= LibAppendFileName (*ParentFileName
, ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
);
1021 if (TempPath
== NULL
) {
1022 LastHandle
->Close (LastHandle
);
1023 Status
= EFI_OUT_OF_RESOURCES
;
1026 FreePool (*ParentFileName
);
1027 *ParentFileName
= TempPath
;
1031 // Close the previous node
1033 LastHandle
->Close (LastHandle
);
1035 DevicePathNode
= NextDevicePathNode (DevicePathNode
);
1038 if (EFI_ERROR (Status
)) {
1042 *RetFileHandle
= FileHandle
;
1044 Status
= EFI_SUCCESS
;
1047 if (TempDevicePathNode
!= NULL
) {
1048 FreePool (TempDevicePathNode
);
1051 if ((FileHandle
!= NULL
) && (EFI_ERROR (Status
))) {
1052 FileHandle
->Close (FileHandle
);
1059 Create a new file or folder in current directory.
1061 @param FileName Point to the fileNmae or folder name.
1062 @param CreateFile CreateFile== TRUE means create a new file.
1063 CreateFile== FALSE means create a new Folder.
1068 IN CHAR16
*FileName
,
1069 IN BOOLEAN CreateFile
1072 EFI_FILE_HANDLE FileHandle
;
1073 EFI_FILE_HANDLE NewHandle
;
1074 EFI_HANDLE DeviceHandle
;
1077 CHAR16
*FullFileName
;
1080 FullFileName
= NULL
;
1082 LibGetFileHandleFromDevicePath(gFileExplorerPrivate
.RetDevicePath
, &FileHandle
, &ParentName
, &DeviceHandle
);
1083 FullFileName
= LibAppendFileName (ParentName
, FileName
);
1084 if (FullFileName
== NULL
) {
1085 return EFI_OUT_OF_RESOURCES
;
1088 Status
= FileHandle
->Open(
1092 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1095 if (EFI_ERROR (Status
)) {
1096 FileHandle
->Close (FileHandle
);
1100 Status
= FileHandle
->Open(
1104 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1107 if (EFI_ERROR (Status
)) {
1108 FileHandle
->Close (FileHandle
);
1113 FileHandle
->Close (FileHandle
);
1116 // Return the DevicePath of the new created file or folder.
1118 gFileExplorerPrivate
.RetDevicePath
= FileDevicePath (DeviceHandle
, FullFileName
);
1125 Find files under current directory.
1127 All files and sub-directories in current directory
1128 will be stored in DirectoryMenu for future use.
1130 @param FileHandle Parent file handle.
1131 @param FileName Parent file name.
1132 @param DeviceHandle Driver handle for this partition.
1134 @retval EFI_SUCCESS Get files from current dir successfully.
1135 @return Other value if can't get files from current dir.
1140 IN EFI_FILE_HANDLE FileHandle
,
1141 IN UINT16
*FileName
,
1142 IN EFI_HANDLE DeviceHandle
1145 EFI_FILE_INFO
*DirInfo
;
1147 UINTN DirBufferSize
;
1148 MENU_ENTRY
*NewMenuEntry
;
1149 FILE_CONTEXT
*NewFileContext
;
1156 DirBufferSize
= sizeof (EFI_FILE_INFO
) + 1024;
1157 DirInfo
= AllocateZeroPool (DirBufferSize
);
1158 if (DirInfo
== NULL
) {
1159 return EFI_OUT_OF_RESOURCES
;
1163 // Get all files in current directory
1164 // Pass 1 to get Directories
1165 // Pass 2 to get files that are EFI images
1167 Status
= EFI_SUCCESS
;
1168 for (Pass
= 1; Pass
<= 2; Pass
++) {
1169 FileHandle
->SetPosition (FileHandle
, 0);
1171 BufferSize
= DirBufferSize
;
1172 Status
= FileHandle
->Read (FileHandle
, &BufferSize
, DirInfo
);
1173 if (EFI_ERROR (Status
) || BufferSize
== 0) {
1174 Status
= EFI_SUCCESS
;
1178 if (((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) != 0 && Pass
== 2) ||
1179 ((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) == 0 && Pass
== 1)
1182 // Pass 1 is for Directories
1183 // Pass 2 is for file names
1188 if (!((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) != 0 || LibIsSupportedFileType (DirInfo
->FileName
))) {
1190 // Slip file unless it is a directory entry or a .EFI file
1195 NewMenuEntry
= LibCreateMenuEntry ();
1196 if (NULL
== NewMenuEntry
) {
1197 Status
= EFI_OUT_OF_RESOURCES
;
1201 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1202 NewFileContext
->DeviceHandle
= DeviceHandle
;
1203 NewFileContext
->FileName
= LibAppendFileName (FileName
, DirInfo
->FileName
);
1204 if (NewFileContext
->FileName
== NULL
) {
1205 LibDestroyMenuEntry (NewMenuEntry
);
1206 Status
= EFI_OUT_OF_RESOURCES
;
1209 NewFileContext
->FileHandle
= FileHandle
;
1210 NewFileContext
->DevicePath
= FileDevicePath (NewFileContext
->DeviceHandle
, NewFileContext
->FileName
);
1211 NewMenuEntry
->HelpString
= NULL
;
1212 NewFileContext
->IsDir
= (BOOLEAN
) ((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) == EFI_FILE_DIRECTORY
);
1214 if (NewFileContext
->IsDir
) {
1215 BufferSize
= StrLen (DirInfo
->FileName
) * 2 + 6;
1216 NewMenuEntry
->DisplayString
= AllocateZeroPool (BufferSize
);
1218 NewMenuEntry
->DisplayString
,
1224 NewMenuEntry
->DisplayString
= LibStrDuplicate (DirInfo
->FileName
);
1227 NewMenuEntry
->DisplayStringToken
= HiiSetString (
1228 gFileExplorerPrivate
.FeHiiHandle
,
1230 NewMenuEntry
->DisplayString
,
1234 NewFileContext
->IsRoot
= FALSE
;
1237 InsertTailList (&gFileExplorerPrivate
.FsOptionMenu
->Head
, &NewMenuEntry
->Link
);
1241 gFileExplorerPrivate
.FsOptionMenu
->MenuNumber
= OptionNumber
;
1251 Refresh the global UpdateData structure.
1255 LibRefreshUpdateData (
1260 // Free current updated date
1262 if (mLibStartOpCodeHandle
!= NULL
) {
1263 HiiFreeOpCodeHandle (mLibStartOpCodeHandle
);
1265 if (mLibEndOpCodeHandle
!= NULL
) {
1266 HiiFreeOpCodeHandle (mLibEndOpCodeHandle
);
1270 // Create new OpCode Handle
1272 mLibStartOpCodeHandle
= HiiAllocateOpCodeHandle ();
1273 mLibEndOpCodeHandle
= HiiAllocateOpCodeHandle ();
1276 // Create Hii Extend Label OpCode as the start opcode
1278 mLibStartLabel
= (EFI_IFR_GUID_LABEL
*) HiiCreateGuidOpCode (
1279 mLibStartOpCodeHandle
,
1282 sizeof (EFI_IFR_GUID_LABEL
)
1284 mLibStartLabel
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
1286 mLibStartLabel
->Number
= FORM_FILE_EXPLORER_ID
;
1289 // Create Hii Extend Label OpCode as the start opcode
1291 mLibEndLabel
= (EFI_IFR_GUID_LABEL
*) HiiCreateGuidOpCode (
1292 mLibEndOpCodeHandle
,
1295 sizeof (EFI_IFR_GUID_LABEL
)
1297 mLibEndLabel
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
1299 mLibEndLabel
->Number
= LABEL_END
;
1304 Update the File Explore page.
1308 LibUpdateFileExplorePage (
1313 MENU_ENTRY
*NewMenuEntry
;
1314 FILE_CONTEXT
*NewFileContext
;
1315 MENU_OPTION
*MenuOption
;
1316 BOOLEAN CreateNewFile
;
1318 NewMenuEntry
= NULL
;
1319 NewFileContext
= NULL
;
1320 CreateNewFile
= FALSE
;
1322 LibRefreshUpdateData ();
1323 MenuOption
= gFileExplorerPrivate
.FsOptionMenu
;
1325 mQuestionIdUpdate
+= QUESTION_ID_UPDATE_STEP
;
1327 for (Index
= 0; Index
< MenuOption
->MenuNumber
; Index
++) {
1328 NewMenuEntry
= LibGetMenuEntry (MenuOption
, Index
);
1329 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1331 if (!NewFileContext
->IsRoot
&& !CreateNewFile
) {
1332 HiiCreateGotoOpCode (
1333 mLibStartOpCodeHandle
,
1334 FORM_ADD_NEW_FILE_ID
,
1335 STRING_TOKEN (STR_NEW_FILE
),
1336 STRING_TOKEN (STR_NEW_FILE_HELP
),
1337 EFI_IFR_FLAG_CALLBACK
,
1338 (UINT16
) (mNewFileQuestionId
++)
1340 HiiCreateGotoOpCode (
1341 mLibStartOpCodeHandle
,
1342 FORM_ADD_NEW_FOLDER_ID
,
1343 STRING_TOKEN (STR_NEW_FOLDER
),
1344 STRING_TOKEN (STR_NEW_FOLDER_HELP
),
1345 EFI_IFR_FLAG_CALLBACK
,
1346 (UINT16
) (mNewFolderQuestionId
++)
1348 HiiCreateTextOpCode(
1349 mLibStartOpCodeHandle
,
1350 STRING_TOKEN (STR_NULL_STRING
),
1351 STRING_TOKEN (STR_NULL_STRING
),
1354 CreateNewFile
= TRUE
;
1357 if (!NewFileContext
->IsDir
) {
1359 // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile.
1361 HiiCreateActionOpCode (
1362 mLibStartOpCodeHandle
,
1363 (UINT16
) (FILE_OPTION_OFFSET
+ Index
+ mQuestionIdUpdate
),
1364 NewMenuEntry
->DisplayStringToken
,
1365 STRING_TOKEN (STR_NULL_STRING
),
1366 EFI_IFR_FLAG_CALLBACK
,
1371 // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState.
1373 HiiCreateGotoOpCode (
1374 mLibStartOpCodeHandle
,
1375 FORM_FILE_EXPLORER_ID
,
1376 NewMenuEntry
->DisplayStringToken
,
1377 STRING_TOKEN (STR_NULL_STRING
),
1378 EFI_IFR_FLAG_CALLBACK
,
1379 (UINT16
) (FILE_OPTION_OFFSET
+ Index
+ mQuestionIdUpdate
)
1385 gFileExplorerPrivate
.FeHiiHandle
,
1387 FORM_FILE_EXPLORER_ID
,
1388 mLibStartOpCodeHandle
, // Label FORM_FILE_EXPLORER_ID
1389 mLibEndOpCodeHandle
// LABEL_END
1394 Update the file explower page with the refershed file system.
1396 @param KeyValue Key value to identify the type of data to expect.
1398 @retval EFI_SUCCESS Update the file explorer form success.
1399 @retval other errors Error occur when parse one directory.
1403 LibUpdateFileExplorer (
1407 UINT16 FileOptionMask
;
1408 MENU_ENTRY
*NewMenuEntry
;
1409 FILE_CONTEXT
*NewFileContext
;
1411 EFI_FILE_HANDLE FileHandle
;
1413 Status
= EFI_SUCCESS
;
1414 FileOptionMask
= (UINT16
) (FILE_OPTION_MASK
& KeyValue
) - mQuestionIdUpdate
;
1415 NewMenuEntry
= LibGetMenuEntry (gFileExplorerPrivate
.FsOptionMenu
, FileOptionMask
);
1416 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1418 if (NewFileContext
->IsDir
) {
1419 RemoveEntryList (&NewMenuEntry
->Link
);
1420 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1421 LibGetFileHandleFromMenu (NewMenuEntry
, &FileHandle
);
1422 Status
= LibFindFiles (FileHandle
, NewFileContext
->FileName
, NewFileContext
->DeviceHandle
);
1423 if (!EFI_ERROR (Status
)) {
1424 LibUpdateFileExplorePage ();
1426 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1428 LibDestroyMenuEntry (NewMenuEntry
);
1435 Get the device path info saved in the menu structure.
1437 @param KeyValue Key value to identify the type of data to expect.
1445 UINT16 FileOptionMask
;
1446 MENU_ENTRY
*NewMenuEntry
;
1447 FILE_CONTEXT
*NewFileContext
;
1449 FileOptionMask
= (UINT16
) (FILE_OPTION_MASK
& KeyValue
) - mQuestionIdUpdate
;
1451 NewMenuEntry
= LibGetMenuEntry (gFileExplorerPrivate
.FsOptionMenu
, FileOptionMask
);
1453 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1455 if (gFileExplorerPrivate
.RetDevicePath
!= NULL
) {
1456 FreePool (gFileExplorerPrivate
.RetDevicePath
);
1458 gFileExplorerPrivate
.RetDevicePath
= DuplicateDevicePath (NewFileContext
->DevicePath
);
1462 Choose a file in the specified directory.
1464 If user input NULL for the RootDirectory, will choose file in the system.
1466 If user input *File != NULL, function will return the allocate device path
1467 info for the choosed file, caller has to free the memory after use it.
1469 @param RootDirectory Pointer to the root directory.
1470 @param FileType The file type need to choose.
1471 @param ChooseHandler Function pointer to the extra task need to do
1472 after choose one file.
1473 @param File Return the device path for the last time chosed file.
1475 @retval EFI_SUCESS Choose file success.
1476 @retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL
1477 One of them must not NULL.
1478 @retval Other errors Choose file failed.
1483 IN EFI_DEVICE_PATH_PROTOCOL
*RootDirectory
,
1484 IN CHAR16
*FileType
, OPTIONAL
1485 IN CHOOSE_HANDLER ChooseHandler
, OPTIONAL
1486 OUT EFI_DEVICE_PATH_PROTOCOL
**File OPTIONAL
1489 EFI_FILE_HANDLE FileHandle
;
1492 EFI_HANDLE DeviceHandle
;
1494 if ((ChooseHandler
== NULL
) && (File
== NULL
)) {
1495 return EFI_INVALID_PARAMETER
;
1498 mQuestionIdUpdate
= 0;
1501 gFileExplorerPrivate
.RetDevicePath
= NULL
;
1502 gFileExplorerPrivate
.ChooseHandler
= ChooseHandler
;
1503 if (FileType
!= NULL
) {
1504 gFileExplorerPrivate
.FileType
= AllocateCopyPool (StrSize (FileType
), FileType
);
1505 ASSERT(gFileExplorerPrivate
.FileType
!= NULL
);
1506 LibToLowerString(gFileExplorerPrivate
.FileType
);
1508 gFileExplorerPrivate
.FileType
= NULL
;
1511 if (RootDirectory
== NULL
) {
1512 Status
= LibFindFileSystem();
1514 Status
= LibGetFileHandleFromDevicePath(RootDirectory
, &FileHandle
, &FileName
, &DeviceHandle
);
1515 if (EFI_ERROR (Status
)) {
1519 Status
= LibFindFiles (FileHandle
, FileName
, DeviceHandle
);
1521 if (EFI_ERROR (Status
)) {
1525 LibUpdateFileExplorePage();
1527 gFileExplorerPrivate
.FormBrowser2
->SendForm (
1528 gFileExplorerPrivate
.FormBrowser2
,
1529 &gFileExplorerPrivate
.FeHiiHandle
,
1538 if ((Status
== EFI_SUCCESS
) && (File
!= NULL
)) {
1539 *File
= gFileExplorerPrivate
.RetDevicePath
;
1540 } else if (gFileExplorerPrivate
.RetDevicePath
!= NULL
) {
1541 FreePool (gFileExplorerPrivate
.RetDevicePath
);
1544 if (gFileExplorerPrivate
.FileType
!= NULL
) {
1545 FreePool (gFileExplorerPrivate
.FileType
);
1548 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1550 if (FileName
!= NULL
) {
1551 FreePool (FileName
);
1559 Install Boot Manager Menu driver.
1561 @param ImageHandle The image handle.
1562 @param SystemTable The system table.
1564 @retval EFI_SUCEESS Install File explorer library success.
1569 FileExplorerLibConstructor (
1570 IN EFI_HANDLE ImageHandle
,
1571 IN EFI_SYSTEM_TABLE
*SystemTable
1576 gHiiVendorDevicePath
= (HII_VENDOR_DEVICE_PATH
*) DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL
*)&FeHiiVendorDevicePath
);
1577 ASSERT (gHiiVendorDevicePath
!= NULL
);
1578 CopyGuid (&gHiiVendorDevicePath
->VendorDevicePath
.Guid
, &gEfiCallerIdGuid
);
1581 // Install Device Path Protocol and Config Access protocol to driver handle
1583 Status
= gBS
->InstallMultipleProtocolInterfaces (
1584 &gFileExplorerPrivate
.FeDriverHandle
,
1585 &gEfiDevicePathProtocolGuid
,
1586 gHiiVendorDevicePath
,
1587 &gEfiHiiConfigAccessProtocolGuid
,
1588 &gFileExplorerPrivate
.FeConfigAccess
,
1591 if (Status
== EFI_ALREADY_STARTED
) {
1594 if (EFI_ERROR (Status
)) {
1599 // Post our File Explorer VFR binary to the HII database.
1601 gFileExplorerPrivate
.FeHiiHandle
= HiiAddPackages (
1603 gFileExplorerPrivate
.FeDriverHandle
,
1605 FileExplorerLibStrings
,
1608 ASSERT (gFileExplorerPrivate
.FeHiiHandle
!= NULL
);
1611 // Locate Formbrowser2 protocol
1613 Status
= gBS
->LocateProtocol (&gEfiFormBrowser2ProtocolGuid
, NULL
, (VOID
**) &gFileExplorerPrivate
.FormBrowser2
);
1614 ASSERT_EFI_ERROR (Status
);
1616 InitializeListHead (&gFileExplorerPrivate
.FsOptionMenu
->Head
);
1622 Unloads the application and its installed protocol.
1624 @param[in] ImageHandle Handle that identifies the image to be unloaded.
1625 @param[in] SystemTable The system table.
1627 @retval EFI_SUCCESS The image has been unloaded.
1631 FileExplorerLibDestructor (
1632 IN EFI_HANDLE ImageHandle
,
1633 IN EFI_SYSTEM_TABLE
*SystemTable
1638 ASSERT (gHiiVendorDevicePath
!= NULL
);
1640 if (gFileExplorerPrivate
.FeDriverHandle
!= NULL
) {
1641 Status
= gBS
->UninstallMultipleProtocolInterfaces (
1642 gFileExplorerPrivate
.FeDriverHandle
,
1643 &gEfiDevicePathProtocolGuid
,
1644 gHiiVendorDevicePath
,
1645 &gEfiHiiConfigAccessProtocolGuid
,
1646 &gFileExplorerPrivate
.FeConfigAccess
,
1649 ASSERT_EFI_ERROR (Status
);
1651 HiiRemovePackages (gFileExplorerPrivate
.FeHiiHandle
);
1654 FreePool (gHiiVendorDevicePath
);