2 File explorer related functions.
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include "FileExplorer.h"
12 EFI_GUID FileExplorerGuid
= EFI_FILE_EXPLORE_FORMSET_GUID
;
15 /// File system selection menu
17 MENU_OPTION mFsOptionMenu
= {
18 MENU_OPTION_SIGNATURE
,
24 FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate
= {
25 FILE_EXPLORER_CALLBACK_DATA_SIGNATURE
,
38 HII_VENDOR_DEVICE_PATH
*gHiiVendorDevicePath
;
40 HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath
= {
46 (UINT8
) (sizeof (VENDOR_DEVICE_PATH
)),
47 (UINT8
) ((sizeof (VENDOR_DEVICE_PATH
)) >> 8)
51 // Will be replace with gEfiCallerIdGuid in code.
53 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
57 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
59 (UINT8
) (END_DEVICE_PATH_LENGTH
),
60 (UINT8
) ((END_DEVICE_PATH_LENGTH
) >> 8)
65 VOID
*mLibStartOpCodeHandle
= NULL
;
66 VOID
*mLibEndOpCodeHandle
= NULL
;
67 EFI_IFR_GUID_LABEL
*mLibStartLabel
= NULL
;
68 EFI_IFR_GUID_LABEL
*mLibEndLabel
= NULL
;
69 UINT16 mQuestionIdUpdate
;
70 CHAR16 mNewFileName
[MAX_FILE_NAME_LEN
];
71 CHAR16 mNewFolderName
[MAX_FOLDER_NAME_LEN
];
72 UINTN mNewFileQuestionId
= NEW_FILE_QUESTION_ID_BASE
;
73 UINTN mNewFolderQuestionId
= NEW_FOLDER_QUESTION_ID_BASE
;
76 Create a new file or folder in current directory.
78 @param FileName Point to the fileNmae or folder.
79 @param CreateFile CreateFile== TRUE means create a new file.
80 CreateFile== FALSE means create a new Folder.
90 This function allows a caller to extract the current configuration for one
91 or more named elements from the target driver.
94 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
95 @param Request A null-terminated Unicode string in <ConfigRequest> format.
96 @param Progress On return, points to a character in the Request string.
97 Points to the string's null terminator if request was successful.
98 Points to the most recent '&' before the first failing name/value
99 pair (or the beginning of the string if the failure is in the
100 first name/value pair) if the request was not successful.
101 @param Results A null-terminated Unicode string in <ConfigAltResp> format which
102 has all values filled in for the names in the Request string.
103 String to be allocated by the called function.
105 @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
106 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
112 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
113 IN CONST EFI_STRING Request
,
114 OUT EFI_STRING
*Progress
,
115 OUT EFI_STRING
*Results
118 if (Progress
== NULL
|| Results
== NULL
) {
119 return EFI_INVALID_PARAMETER
;
123 return EFI_NOT_FOUND
;
127 This function processes the results of changes in configuration.
130 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
131 @param Configuration A null-terminated Unicode string in <ConfigResp> format.
132 @param Progress A pointer to a string filled in with the offset of the most
133 recent '&' before the first failing name/value pair (or the
134 beginning of the string if the failure is in the first
135 name/value pair) or the terminating NULL if all was successful.
137 @retval EFI_INVALID_PARAMETER Configuration is NULL.
138 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
144 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
145 IN CONST EFI_STRING Configuration
,
146 OUT EFI_STRING
*Progress
149 if (Configuration
== NULL
|| Progress
== NULL
) {
150 return EFI_INVALID_PARAMETER
;
153 *Progress
= Configuration
;
154 return EFI_NOT_FOUND
;
158 This function processes the results of changes in configuration.
159 When user select a interactive opcode, this callback will be triggered.
160 Based on the Question(QuestionId) that triggers the callback, the corresponding
161 actions is performed. It handles:
163 1) Process the axtra action or exit file explorer when user select one file .
164 2) update of file content if a dir is selected.
166 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
167 @param Action Specifies the type of action taken by the browser.
168 @param QuestionId A unique value which is sent to the original exporting driver
169 so that it can identify the type of data to expect.
170 @param Type The type of value for the question.
171 @param Value A pointer to the data being sent to the original exporting driver.
172 @param ActionRequest On return, points to the action requested by the callback function.
174 @retval EFI_SUCCESS The callback successfully handled the action.
175 @retval other error Error occur when parse one directory.
180 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL
*This
,
181 IN EFI_BROWSER_ACTION Action
,
182 IN EFI_QUESTION_ID QuestionId
,
184 IN EFI_IFR_TYPE_VALUE
*Value
,
185 OUT EFI_BROWSER_ACTION_REQUEST
*ActionRequest
191 CHAR16
*NewFolderName
;
195 NewFolderName
= NULL
;
197 if (Action
!= EFI_BROWSER_ACTION_CHANGING
&& Action
!= EFI_BROWSER_ACTION_CHANGED
) {
199 // Do nothing for other UEFI Action. Only do call back when data is changed.
201 return EFI_UNSUPPORTED
;
204 if (Action
== EFI_BROWSER_ACTION_CHANGED
) {
205 if ((Value
== NULL
) || (ActionRequest
== NULL
)) {
206 return EFI_INVALID_PARAMETER
;
209 if (QuestionId
== KEY_VALUE_CREATE_FILE_AND_EXIT
) {
210 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
211 if (!IsZeroBuffer (mNewFileName
, sizeof (mNewFileName
))) {
212 Status
= LibCreateNewFile (mNewFileName
,TRUE
);
213 ZeroMem (mNewFileName
,sizeof (mNewFileName
));
217 if (QuestionId
== KEY_VALUE_NO_CREATE_FILE_AND_EXIT
) {
218 ZeroMem (mNewFileName
,sizeof (mNewFileName
));
219 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
222 if (QuestionId
== KEY_VALUE_CREATE_FOLDER_AND_EXIT
) {
223 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
224 if (!IsZeroBuffer (mNewFolderName
, sizeof (mNewFolderName
))) {
225 Status
= LibCreateNewFile (mNewFolderName
, FALSE
);
226 ZeroMem (mNewFolderName
,sizeof (mNewFolderName
));
230 if (QuestionId
== KEY_VALUE_NO_CREATE_FOLDER_AND_EXIT
) {
231 ZeroMem (mNewFolderName
,sizeof (mNewFolderName
));
232 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
235 if (QuestionId
== NEW_FILE_NAME_ID
) {
236 NewFileName
= HiiGetString (gFileExplorerPrivate
.FeHiiHandle
, Value
->string
, NULL
);
237 if (NewFileName
!= NULL
) {
238 StrCpyS (mNewFileName
, MAX_FILE_NAME_LEN
, NewFileName
);
239 FreePool (NewFileName
);
242 return EFI_INVALID_PARAMETER
;
246 if (QuestionId
== NEW_FOLDER_NAME_ID
) {
247 NewFolderName
= HiiGetString (gFileExplorerPrivate
.FeHiiHandle
, Value
->string
, NULL
);
248 if (NewFolderName
!= NULL
) {
249 StrCpyS (mNewFolderName
, MAX_FOLDER_NAME_LEN
, NewFolderName
);
250 FreePool (NewFolderName
);
251 NewFolderName
= NULL
;
253 return EFI_INVALID_PARAMETER
;
257 if (QuestionId
>= FILE_OPTION_OFFSET
) {
258 LibGetDevicePath(QuestionId
);
261 // Process the extra action.
263 if (gFileExplorerPrivate
.ChooseHandler
!= NULL
) {
264 NeedExit
= gFileExplorerPrivate
.ChooseHandler (gFileExplorerPrivate
.RetDevicePath
);
268 *ActionRequest
= EFI_BROWSER_ACTION_REQUEST_EXIT
;
271 } else if (Action
== EFI_BROWSER_ACTION_CHANGING
) {
273 return EFI_INVALID_PARAMETER
;
275 if (QuestionId
>= FILE_OPTION_OFFSET
) {
276 LibGetDevicePath(QuestionId
);
277 Status
= LibUpdateFileExplorer (QuestionId
);
278 if (EFI_ERROR (Status
)) {
288 Create a menu entry by given menu type.
290 @retval NULL If failed to create the menu.
291 @return the new menu entry.
299 MENU_ENTRY
*MenuEntry
;
302 // Create new menu entry
304 MenuEntry
= AllocateZeroPool (sizeof (MENU_ENTRY
));
305 if (MenuEntry
== NULL
) {
309 MenuEntry
->VariableContext
= AllocateZeroPool (sizeof (FILE_CONTEXT
));
310 if (MenuEntry
->VariableContext
== NULL
) {
311 FreePool (MenuEntry
);
315 MenuEntry
->Signature
= MENU_ENTRY_SIGNATURE
;
321 Get the Menu Entry from the list in Menu Entry List.
323 If MenuNumber is great or equal to the number of Menu
324 Entry in the list, then ASSERT.
326 @param MenuOption The Menu Entry List to read the menu entry.
327 @param MenuNumber The index of Menu Entry.
329 @return The Menu Entry.
334 MENU_OPTION
*MenuOption
,
338 MENU_ENTRY
*NewMenuEntry
;
342 ASSERT (MenuNumber
< MenuOption
->MenuNumber
);
344 List
= MenuOption
->Head
.ForwardLink
;
345 for (Index
= 0; Index
< MenuNumber
; Index
++) {
346 List
= List
->ForwardLink
;
349 NewMenuEntry
= CR (List
, MENU_ENTRY
, Link
, MENU_ENTRY_SIGNATURE
);
355 Free up all resource allocated for a BM_MENU_ENTRY.
357 @param MenuEntry A pointer to BM_MENU_ENTRY.
361 LibDestroyMenuEntry (
362 MENU_ENTRY
*MenuEntry
365 FILE_CONTEXT
*FileContext
;
367 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
369 if (!FileContext
->IsRoot
) {
370 if (FileContext
->DevicePath
!= NULL
) {
371 FreePool (FileContext
->DevicePath
);
374 if (FileContext
->FileHandle
!= NULL
) {
375 FileContext
->FileHandle
->Close (FileContext
->FileHandle
);
379 if (FileContext
->FileName
!= NULL
) {
380 FreePool (FileContext
->FileName
);
383 FreePool (FileContext
);
385 if (MenuEntry
->DisplayString
!= NULL
) {
386 FreePool (MenuEntry
->DisplayString
);
388 if (MenuEntry
->HelpString
!= NULL
) {
389 FreePool (MenuEntry
->HelpString
);
392 FreePool (MenuEntry
);
397 Free resources allocated in Allocate Rountine.
399 @param FreeMenu Menu to be freed
403 MENU_OPTION
*FreeMenu
406 MENU_ENTRY
*MenuEntry
;
407 while (!IsListEmpty (&FreeMenu
->Head
)) {
409 FreeMenu
->Head
.ForwardLink
,
414 RemoveEntryList (&MenuEntry
->Link
);
415 LibDestroyMenuEntry (MenuEntry
);
417 FreeMenu
->MenuNumber
= 0;
422 Function opens and returns a file handle to the root directory of a volume.
424 @param DeviceHandle A handle for a device
426 @return A valid file handle or NULL is returned
431 IN EFI_HANDLE DeviceHandle
435 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Volume
;
436 EFI_FILE_HANDLE File
;
441 // File the file system interface to the device
443 Status
= gBS
->HandleProtocol (
445 &gEfiSimpleFileSystemProtocolGuid
,
450 // Open the root directory of the volume
452 if (!EFI_ERROR (Status
)) {
453 Status
= Volume
->OpenVolume (
461 return EFI_ERROR (Status
) ? NULL
: File
;
465 This function converts an input device structure to a Unicode string.
467 @param DevPath A pointer to the device path structure.
469 @return A new allocated Unicode string that represents the device path.
474 IN EFI_DEVICE_PATH_PROTOCOL
*DevPath
479 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL
*DevPathToText
;
481 if (DevPath
== NULL
) {
485 Status
= gBS
->LocateProtocol (
486 &gEfiDevicePathToTextProtocolGuid
,
488 (VOID
**) &DevPathToText
490 ASSERT_EFI_ERROR (Status
);
491 ToText
= DevPathToText
->ConvertDevicePathToText (
496 ASSERT (ToText
!= NULL
);
504 @param Src The source.
506 @return A new string which is duplicated copy of the source.
507 @retval NULL If there is not enough memory.
518 Size
= StrSize (Src
);
519 Dest
= AllocateZeroPool (Size
);
520 ASSERT (Dest
!= NULL
);
522 CopyMem (Dest
, Src
, Size
);
530 Function gets the file information from an open file descriptor, and stores it
531 in a buffer allocated from pool.
533 @param FHand File Handle.
534 @param InfoType Info type need to get.
536 @retval A pointer to a buffer with file information or NULL is returned
541 IN EFI_FILE_HANDLE FHand
,
542 IN EFI_GUID
*InfoType
546 EFI_FILE_INFO
*Buffer
;
552 Status
= FHand
->GetInfo (
558 if (Status
== EFI_BUFFER_TOO_SMALL
) {
559 Buffer
= AllocatePool (BufferSize
);
560 ASSERT (Buffer
!= NULL
);
563 Status
= FHand
->GetInfo (
575 Get file type base on the file name.
576 Just cut the file name, from the ".". eg ".efi"
578 @param FileName File need to be checked.
580 @retval the file type string.
590 Index
= StrLen (FileName
) - 1;
591 while ((FileName
[Index
] != L
'.') && (Index
!= 0)) {
595 return Index
== 0 ? NULL
: &FileName
[Index
];
599 Converts the unicode character of the string from uppercase to lowercase.
600 This is a internal function.
602 @param ConfigString String to be converted
612 for (TmpStr
= String
; *TmpStr
!= L
'\0'; TmpStr
++) {
613 if (*TmpStr
>= L
'A' && *TmpStr
<= L
'Z') {
614 *TmpStr
= (CHAR16
) (*TmpStr
- L
'A' + L
'a');
621 Check whether current FileName point to a valid
624 @param FileName File need to be checked.
626 @retval TRUE Is Efi Image
627 @retval FALSE Not a valid Efi Image
631 LibIsSupportedFileType (
635 CHAR16
*InputFileType
;
639 if (gFileExplorerPrivate
.FileType
== NULL
) {
643 InputFileType
= LibGetTypeFromName (FileName
);
645 // If the file not has *.* style, always return TRUE.
647 if (InputFileType
== NULL
) {
651 TmpStr
= AllocateCopyPool (StrSize (InputFileType
), InputFileType
);
652 ASSERT(TmpStr
!= NULL
);
653 LibToLowerString(TmpStr
);
655 IsSupported
= (StrStr (gFileExplorerPrivate
.FileType
, TmpStr
) == NULL
? FALSE
: TRUE
);
663 Append file name to existing file name.
665 @param Str1 The existing file name
666 @param Str2 The file name to be appended
668 @return Allocate a new string to hold the appended result.
669 Caller is responsible to free the returned string.
686 Size1
= StrSize (Str1
);
687 Size2
= StrSize (Str2
);
692 if (((MAX_UINTN
- Size1
) < Size2
) || ((MAX_UINTN
- Size1
- Size2
) < sizeof(CHAR16
))) {
696 MaxLen
= (Size1
+ Size2
+ sizeof (CHAR16
))/ sizeof (CHAR16
);
697 Str
= AllocateZeroPool (Size1
+ Size2
+ sizeof (CHAR16
));
698 ASSERT (Str
!= NULL
);
700 TmpStr
= AllocateZeroPool (Size1
+ Size2
+ sizeof (CHAR16
));
701 ASSERT (TmpStr
!= NULL
);
703 StrCpyS (Str
, MaxLen
, Str1
);
704 if (!((*Str
== '\\') && (*(Str
+ 1) == 0))) {
705 StrCatS (Str
, MaxLen
, L
"\\");
708 StrCatS (Str
, MaxLen
, Str2
);
713 if (*Ptr
== '\\' && *(Ptr
+ 1) == '.' && *(Ptr
+ 2) == '.' && *(Ptr
+ 3) == L
'\\') {
715 // Convert "\Name\..\" to "\"
716 // DO NOT convert the .. if it is at the end of the string. This will
717 // break the .. behavior in changing directories.
721 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
724 StrCpyS (TmpStr
, MaxLen
, Ptr
+ 3);
725 StrCpyS (LastSlash
, MaxLen
- ((UINTN
) LastSlash
- (UINTN
) Str
) / sizeof (CHAR16
), TmpStr
);
727 } else if (*Ptr
== '\\' && *(Ptr
+ 1) == '.' && *(Ptr
+ 2) == '\\') {
729 // Convert a "\.\" to a "\"
733 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
736 StrCpyS (TmpStr
, MaxLen
, Ptr
+ 2);
737 StrCpyS (Ptr
, MaxLen
- ((UINTN
) Ptr
- (UINTN
) Str
) / sizeof (CHAR16
), TmpStr
);
739 } else if (*Ptr
== '\\') {
752 This function build the FsOptionMenu list which records all
753 available file system in the system. They includes all instances
754 of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM.
757 @retval EFI_SUCCESS Success find the file system
758 @retval EFI_OUT_OF_RESOURCES Can not create menu entry
766 UINTN NoSimpleFsHandles
;
767 EFI_HANDLE
*SimpleFsHandle
;
771 MENU_ENTRY
*MenuEntry
;
772 FILE_CONTEXT
*FileContext
;
774 EFI_FILE_SYSTEM_VOLUME_LABEL
*Info
;
776 NoSimpleFsHandles
= 0;
780 // Locate Handles that support Simple File System protocol
782 Status
= gBS
->LocateHandleBuffer (
784 &gEfiSimpleFileSystemProtocolGuid
,
789 if (!EFI_ERROR (Status
)) {
791 // Find all the instances of the File System prototocol
793 for (Index
= 0; Index
< NoSimpleFsHandles
; Index
++) {
795 // Allocate pool for this load option
797 MenuEntry
= LibCreateMenuEntry ();
798 if (NULL
== MenuEntry
) {
799 FreePool (SimpleFsHandle
);
800 return EFI_OUT_OF_RESOURCES
;
803 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
804 FileContext
->DeviceHandle
= SimpleFsHandle
[Index
];
805 FileContext
->FileHandle
= LibOpenRoot (FileContext
->DeviceHandle
);
806 if (FileContext
->FileHandle
== NULL
) {
807 LibDestroyMenuEntry (MenuEntry
);
811 MenuEntry
->HelpString
= LibDevicePathToStr (DevicePathFromHandle (FileContext
->DeviceHandle
));
812 FileContext
->FileName
= LibStrDuplicate (L
"\\");
813 FileContext
->DevicePath
= FileDevicePath (FileContext
->DeviceHandle
, FileContext
->FileName
);
814 FileContext
->IsDir
= TRUE
;
815 FileContext
->IsRoot
= TRUE
;
818 // Get current file system's Volume Label
820 Info
= (EFI_FILE_SYSTEM_VOLUME_LABEL
*) LibFileInfo (FileContext
->FileHandle
, &gEfiFileSystemVolumeLabelInfoIdGuid
);
822 VolumeLabel
= L
"NO FILE SYSTEM INFO";
824 if (Info
->VolumeLabel
== NULL
) {
825 VolumeLabel
= L
"NULL VOLUME LABEL";
827 VolumeLabel
= Info
->VolumeLabel
;
828 if (*VolumeLabel
== 0x0000) {
829 VolumeLabel
= L
"NO VOLUME LABEL";
833 MenuEntry
->DisplayString
= AllocateZeroPool (MAX_CHAR
);
834 ASSERT (MenuEntry
->DisplayString
!= NULL
);
836 MenuEntry
->DisplayString
,
840 MenuEntry
->HelpString
842 MenuEntry
->DisplayStringToken
= HiiSetString (
843 gFileExplorerPrivate
.FeHiiHandle
,
845 MenuEntry
->DisplayString
,
853 InsertTailList (&gFileExplorerPrivate
.FsOptionMenu
->Head
, &MenuEntry
->Link
);
857 if (NoSimpleFsHandles
!= 0) {
858 FreePool (SimpleFsHandle
);
861 gFileExplorerPrivate
.FsOptionMenu
->MenuNumber
= OptionNumber
;
867 Find the file handle from the input menu info.
869 @param MenuEntry Input Menu info.
870 @param RetFileHandle Return the file handle for the input device path.
872 @retval EFI_SUCESS Find the file handle success.
873 @retval Other Find the file handle failure.
876 LibGetFileHandleFromMenu (
877 IN MENU_ENTRY
*MenuEntry
,
878 OUT EFI_FILE_HANDLE
*RetFileHandle
882 EFI_FILE_HANDLE NewDir
;
883 FILE_CONTEXT
*FileContext
;
886 FileContext
= (FILE_CONTEXT
*) MenuEntry
->VariableContext
;
887 Dir
= FileContext
->FileHandle
;
890 // Open current directory to get files from it
895 FileContext
->FileName
,
899 if (EFI_ERROR (Status
)) {
903 if (!FileContext
->IsRoot
) {
907 *RetFileHandle
= NewDir
;
913 Find the file handle from the input device path info.
915 @param RootDirectory Device path info.
916 @param RetFileHandle Return the file handle for the input device path.
917 @param ParentFileName Parent file name.
918 @param DeviceHandle Driver handle for this partition.
920 @retval EFI_SUCESS Find the file handle success.
921 @retval Other Find the file handle failure.
924 LibGetFileHandleFromDevicePath (
925 IN EFI_DEVICE_PATH_PROTOCOL
*RootDirectory
,
926 OUT EFI_FILE_HANDLE
*RetFileHandle
,
927 OUT UINT16
**ParentFileName
,
928 OUT EFI_HANDLE
*DeviceHandle
931 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
932 EFI_DEVICE_PATH_PROTOCOL
*TempDevicePathNode
;
935 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Volume
;
936 EFI_FILE_HANDLE FileHandle
;
937 EFI_FILE_HANDLE LastHandle
;
940 *ParentFileName
= NULL
;
943 // Attempt to access the file via a file system interface
945 DevicePathNode
= RootDirectory
;
946 Status
= gBS
->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid
, &DevicePathNode
, &Handle
);
947 if (EFI_ERROR (Status
)) {
951 Status
= gBS
->HandleProtocol (Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Volume
);
952 if (EFI_ERROR (Status
)) {
957 // Open the Volume to get the File System handle
959 Status
= Volume
->OpenVolume (Volume
, &FileHandle
);
960 if (EFI_ERROR (Status
)) {
964 *DeviceHandle
= Handle
;
966 if (IsDevicePathEnd(DevicePathNode
)) {
967 *ParentFileName
= AllocateCopyPool (StrSize (L
"\\"), L
"\\");
968 *RetFileHandle
= FileHandle
;
973 // Duplicate the device path to avoid the access to unaligned device path node.
974 // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
975 // nodes, It assures the fields in device path nodes are 2 byte aligned.
977 TempDevicePathNode
= DuplicateDevicePath (DevicePathNode
);
978 if (TempDevicePathNode
== NULL
) {
981 // Setting Status to an EFI_ERROR value will cause the rest of
982 // the file system support below to be skipped.
984 Status
= EFI_OUT_OF_RESOURCES
;
989 // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
990 // directory information and filename can be seperate. The goal is to inch
991 // our way down each device path node and close the previous node
993 DevicePathNode
= TempDevicePathNode
;
994 while (!EFI_ERROR (Status
) && !IsDevicePathEnd (DevicePathNode
)) {
995 if (DevicePathType (DevicePathNode
) != MEDIA_DEVICE_PATH
||
996 DevicePathSubType (DevicePathNode
) != MEDIA_FILEPATH_DP
) {
997 Status
= EFI_UNSUPPORTED
;
1001 LastHandle
= FileHandle
;
1004 Status
= LastHandle
->Open (
1007 ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
,
1011 if (*ParentFileName
== NULL
) {
1012 *ParentFileName
= AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
), ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
);
1014 TempPath
= LibAppendFileName (*ParentFileName
, ((FILEPATH_DEVICE_PATH
*) DevicePathNode
)->PathName
);
1015 if (TempPath
== NULL
) {
1016 LastHandle
->Close (LastHandle
);
1017 Status
= EFI_OUT_OF_RESOURCES
;
1020 FreePool (*ParentFileName
);
1021 *ParentFileName
= TempPath
;
1025 // Close the previous node
1027 LastHandle
->Close (LastHandle
);
1029 DevicePathNode
= NextDevicePathNode (DevicePathNode
);
1032 if (EFI_ERROR (Status
)) {
1036 *RetFileHandle
= FileHandle
;
1038 Status
= EFI_SUCCESS
;
1041 if (TempDevicePathNode
!= NULL
) {
1042 FreePool (TempDevicePathNode
);
1045 if ((FileHandle
!= NULL
) && (EFI_ERROR (Status
))) {
1046 FileHandle
->Close (FileHandle
);
1053 Create a new file or folder in current directory.
1055 @param FileName Point to the fileNmae or folder name.
1056 @param CreateFile CreateFile== TRUE means create a new file.
1057 CreateFile== FALSE means create a new Folder.
1062 IN CHAR16
*FileName
,
1063 IN BOOLEAN CreateFile
1066 EFI_FILE_HANDLE FileHandle
;
1067 EFI_FILE_HANDLE NewHandle
;
1068 EFI_HANDLE DeviceHandle
;
1071 CHAR16
*FullFileName
;
1074 FullFileName
= NULL
;
1076 LibGetFileHandleFromDevicePath(gFileExplorerPrivate
.RetDevicePath
, &FileHandle
, &ParentName
, &DeviceHandle
);
1077 FullFileName
= LibAppendFileName (ParentName
, FileName
);
1078 if (FullFileName
== NULL
) {
1079 return EFI_OUT_OF_RESOURCES
;
1082 Status
= FileHandle
->Open(
1086 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1089 if (EFI_ERROR (Status
)) {
1090 FileHandle
->Close (FileHandle
);
1094 Status
= FileHandle
->Open(
1098 EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
,
1101 if (EFI_ERROR (Status
)) {
1102 FileHandle
->Close (FileHandle
);
1107 FileHandle
->Close (FileHandle
);
1110 // Return the DevicePath of the new created file or folder.
1112 gFileExplorerPrivate
.RetDevicePath
= FileDevicePath (DeviceHandle
, FullFileName
);
1119 Find files under current directory.
1121 All files and sub-directories in current directory
1122 will be stored in DirectoryMenu for future use.
1124 @param FileHandle Parent file handle.
1125 @param FileName Parent file name.
1126 @param DeviceHandle Driver handle for this partition.
1128 @retval EFI_SUCCESS Get files from current dir successfully.
1129 @return Other value if can't get files from current dir.
1134 IN EFI_FILE_HANDLE FileHandle
,
1135 IN UINT16
*FileName
,
1136 IN EFI_HANDLE DeviceHandle
1139 EFI_FILE_INFO
*DirInfo
;
1141 UINTN DirBufferSize
;
1142 MENU_ENTRY
*NewMenuEntry
;
1143 FILE_CONTEXT
*NewFileContext
;
1150 DirBufferSize
= sizeof (EFI_FILE_INFO
) + 1024;
1151 DirInfo
= AllocateZeroPool (DirBufferSize
);
1152 if (DirInfo
== NULL
) {
1153 return EFI_OUT_OF_RESOURCES
;
1157 // Get all files in current directory
1158 // Pass 1 to get Directories
1159 // Pass 2 to get files that are EFI images
1161 Status
= EFI_SUCCESS
;
1162 for (Pass
= 1; Pass
<= 2; Pass
++) {
1163 FileHandle
->SetPosition (FileHandle
, 0);
1165 BufferSize
= DirBufferSize
;
1166 Status
= FileHandle
->Read (FileHandle
, &BufferSize
, DirInfo
);
1167 if (EFI_ERROR (Status
) || BufferSize
== 0) {
1168 Status
= EFI_SUCCESS
;
1172 if (((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) != 0 && Pass
== 2) ||
1173 ((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) == 0 && Pass
== 1)
1176 // Pass 1 is for Directories
1177 // Pass 2 is for file names
1182 if (!((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) != 0 || LibIsSupportedFileType (DirInfo
->FileName
))) {
1184 // Slip file unless it is a directory entry or a .EFI file
1189 NewMenuEntry
= LibCreateMenuEntry ();
1190 if (NULL
== NewMenuEntry
) {
1191 Status
= EFI_OUT_OF_RESOURCES
;
1195 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1196 NewFileContext
->DeviceHandle
= DeviceHandle
;
1197 NewFileContext
->FileName
= LibAppendFileName (FileName
, DirInfo
->FileName
);
1198 if (NewFileContext
->FileName
== NULL
) {
1199 LibDestroyMenuEntry (NewMenuEntry
);
1200 Status
= EFI_OUT_OF_RESOURCES
;
1203 NewFileContext
->FileHandle
= FileHandle
;
1204 NewFileContext
->DevicePath
= FileDevicePath (NewFileContext
->DeviceHandle
, NewFileContext
->FileName
);
1205 NewMenuEntry
->HelpString
= NULL
;
1206 NewFileContext
->IsDir
= (BOOLEAN
) ((DirInfo
->Attribute
& EFI_FILE_DIRECTORY
) == EFI_FILE_DIRECTORY
);
1208 if (NewFileContext
->IsDir
) {
1209 BufferSize
= StrLen (DirInfo
->FileName
) * 2 + 6;
1210 NewMenuEntry
->DisplayString
= AllocateZeroPool (BufferSize
);
1212 NewMenuEntry
->DisplayString
,
1218 NewMenuEntry
->DisplayString
= LibStrDuplicate (DirInfo
->FileName
);
1221 NewMenuEntry
->DisplayStringToken
= HiiSetString (
1222 gFileExplorerPrivate
.FeHiiHandle
,
1224 NewMenuEntry
->DisplayString
,
1228 NewFileContext
->IsRoot
= FALSE
;
1231 InsertTailList (&gFileExplorerPrivate
.FsOptionMenu
->Head
, &NewMenuEntry
->Link
);
1235 gFileExplorerPrivate
.FsOptionMenu
->MenuNumber
= OptionNumber
;
1245 Refresh the global UpdateData structure.
1249 LibRefreshUpdateData (
1254 // Free current updated date
1256 if (mLibStartOpCodeHandle
!= NULL
) {
1257 HiiFreeOpCodeHandle (mLibStartOpCodeHandle
);
1259 if (mLibEndOpCodeHandle
!= NULL
) {
1260 HiiFreeOpCodeHandle (mLibEndOpCodeHandle
);
1264 // Create new OpCode Handle
1266 mLibStartOpCodeHandle
= HiiAllocateOpCodeHandle ();
1267 mLibEndOpCodeHandle
= HiiAllocateOpCodeHandle ();
1270 // Create Hii Extend Label OpCode as the start opcode
1272 mLibStartLabel
= (EFI_IFR_GUID_LABEL
*) HiiCreateGuidOpCode (
1273 mLibStartOpCodeHandle
,
1276 sizeof (EFI_IFR_GUID_LABEL
)
1278 mLibStartLabel
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
1280 mLibStartLabel
->Number
= FORM_FILE_EXPLORER_ID
;
1283 // Create Hii Extend Label OpCode as the start opcode
1285 mLibEndLabel
= (EFI_IFR_GUID_LABEL
*) HiiCreateGuidOpCode (
1286 mLibEndOpCodeHandle
,
1289 sizeof (EFI_IFR_GUID_LABEL
)
1291 mLibEndLabel
->ExtendOpCode
= EFI_IFR_EXTEND_OP_LABEL
;
1293 mLibEndLabel
->Number
= LABEL_END
;
1298 Update the File Explore page.
1302 LibUpdateFileExplorePage (
1307 MENU_ENTRY
*NewMenuEntry
;
1308 FILE_CONTEXT
*NewFileContext
;
1309 MENU_OPTION
*MenuOption
;
1310 BOOLEAN CreateNewFile
;
1312 NewMenuEntry
= NULL
;
1313 NewFileContext
= NULL
;
1314 CreateNewFile
= FALSE
;
1316 LibRefreshUpdateData ();
1317 MenuOption
= gFileExplorerPrivate
.FsOptionMenu
;
1319 mQuestionIdUpdate
+= QUESTION_ID_UPDATE_STEP
;
1321 for (Index
= 0; Index
< MenuOption
->MenuNumber
; Index
++) {
1322 NewMenuEntry
= LibGetMenuEntry (MenuOption
, Index
);
1323 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1325 if (!NewFileContext
->IsRoot
&& !CreateNewFile
) {
1326 HiiCreateGotoOpCode (
1327 mLibStartOpCodeHandle
,
1328 FORM_ADD_NEW_FILE_ID
,
1329 STRING_TOKEN (STR_NEW_FILE
),
1330 STRING_TOKEN (STR_NEW_FILE_HELP
),
1331 EFI_IFR_FLAG_CALLBACK
,
1332 (UINT16
) (mNewFileQuestionId
++)
1334 HiiCreateGotoOpCode (
1335 mLibStartOpCodeHandle
,
1336 FORM_ADD_NEW_FOLDER_ID
,
1337 STRING_TOKEN (STR_NEW_FOLDER
),
1338 STRING_TOKEN (STR_NEW_FOLDER_HELP
),
1339 EFI_IFR_FLAG_CALLBACK
,
1340 (UINT16
) (mNewFolderQuestionId
++)
1342 HiiCreateTextOpCode(
1343 mLibStartOpCodeHandle
,
1344 STRING_TOKEN (STR_NULL_STRING
),
1345 STRING_TOKEN (STR_NULL_STRING
),
1348 CreateNewFile
= TRUE
;
1351 if (!NewFileContext
->IsDir
) {
1353 // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile.
1355 HiiCreateActionOpCode (
1356 mLibStartOpCodeHandle
,
1357 (UINT16
) (FILE_OPTION_OFFSET
+ Index
+ mQuestionIdUpdate
),
1358 NewMenuEntry
->DisplayStringToken
,
1359 STRING_TOKEN (STR_NULL_STRING
),
1360 EFI_IFR_FLAG_CALLBACK
,
1365 // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState.
1367 HiiCreateGotoOpCode (
1368 mLibStartOpCodeHandle
,
1369 FORM_FILE_EXPLORER_ID
,
1370 NewMenuEntry
->DisplayStringToken
,
1371 STRING_TOKEN (STR_NULL_STRING
),
1372 EFI_IFR_FLAG_CALLBACK
,
1373 (UINT16
) (FILE_OPTION_OFFSET
+ Index
+ mQuestionIdUpdate
)
1379 gFileExplorerPrivate
.FeHiiHandle
,
1381 FORM_FILE_EXPLORER_ID
,
1382 mLibStartOpCodeHandle
, // Label FORM_FILE_EXPLORER_ID
1383 mLibEndOpCodeHandle
// LABEL_END
1388 Update the file explower page with the refershed file system.
1390 @param KeyValue Key value to identify the type of data to expect.
1392 @retval EFI_SUCCESS Update the file explorer form success.
1393 @retval other errors Error occur when parse one directory.
1397 LibUpdateFileExplorer (
1401 UINT16 FileOptionMask
;
1402 MENU_ENTRY
*NewMenuEntry
;
1403 FILE_CONTEXT
*NewFileContext
;
1405 EFI_FILE_HANDLE FileHandle
;
1407 Status
= EFI_SUCCESS
;
1408 FileOptionMask
= (UINT16
) (FILE_OPTION_MASK
& KeyValue
) - mQuestionIdUpdate
;
1409 NewMenuEntry
= LibGetMenuEntry (gFileExplorerPrivate
.FsOptionMenu
, FileOptionMask
);
1410 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1412 if (NewFileContext
->IsDir
) {
1413 RemoveEntryList (&NewMenuEntry
->Link
);
1414 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1415 LibGetFileHandleFromMenu (NewMenuEntry
, &FileHandle
);
1416 Status
= LibFindFiles (FileHandle
, NewFileContext
->FileName
, NewFileContext
->DeviceHandle
);
1417 if (!EFI_ERROR (Status
)) {
1418 LibUpdateFileExplorePage ();
1420 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1422 LibDestroyMenuEntry (NewMenuEntry
);
1429 Get the device path info saved in the menu structure.
1431 @param KeyValue Key value to identify the type of data to expect.
1439 UINT16 FileOptionMask
;
1440 MENU_ENTRY
*NewMenuEntry
;
1441 FILE_CONTEXT
*NewFileContext
;
1443 FileOptionMask
= (UINT16
) (FILE_OPTION_MASK
& KeyValue
) - mQuestionIdUpdate
;
1445 NewMenuEntry
= LibGetMenuEntry (gFileExplorerPrivate
.FsOptionMenu
, FileOptionMask
);
1447 NewFileContext
= (FILE_CONTEXT
*) NewMenuEntry
->VariableContext
;
1449 if (gFileExplorerPrivate
.RetDevicePath
!= NULL
) {
1450 FreePool (gFileExplorerPrivate
.RetDevicePath
);
1452 gFileExplorerPrivate
.RetDevicePath
= DuplicateDevicePath (NewFileContext
->DevicePath
);
1456 Choose a file in the specified directory.
1458 If user input NULL for the RootDirectory, will choose file in the system.
1460 If user input *File != NULL, function will return the allocate device path
1461 info for the choosed file, caller has to free the memory after use it.
1463 @param RootDirectory Pointer to the root directory.
1464 @param FileType The file type need to choose.
1465 @param ChooseHandler Function pointer to the extra task need to do
1466 after choose one file.
1467 @param File Return the device path for the last time chosed file.
1469 @retval EFI_SUCESS Choose file success.
1470 @retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL
1471 One of them must not NULL.
1472 @retval Other errors Choose file failed.
1477 IN EFI_DEVICE_PATH_PROTOCOL
*RootDirectory
,
1478 IN CHAR16
*FileType
, OPTIONAL
1479 IN CHOOSE_HANDLER ChooseHandler
, OPTIONAL
1480 OUT EFI_DEVICE_PATH_PROTOCOL
**File OPTIONAL
1483 EFI_FILE_HANDLE FileHandle
;
1486 EFI_HANDLE DeviceHandle
;
1488 if ((ChooseHandler
== NULL
) && (File
== NULL
)) {
1489 return EFI_INVALID_PARAMETER
;
1492 mQuestionIdUpdate
= 0;
1495 gFileExplorerPrivate
.RetDevicePath
= NULL
;
1496 gFileExplorerPrivate
.ChooseHandler
= ChooseHandler
;
1497 if (FileType
!= NULL
) {
1498 gFileExplorerPrivate
.FileType
= AllocateCopyPool (StrSize (FileType
), FileType
);
1499 ASSERT(gFileExplorerPrivate
.FileType
!= NULL
);
1500 LibToLowerString(gFileExplorerPrivate
.FileType
);
1502 gFileExplorerPrivate
.FileType
= NULL
;
1505 if (RootDirectory
== NULL
) {
1506 Status
= LibFindFileSystem();
1508 Status
= LibGetFileHandleFromDevicePath(RootDirectory
, &FileHandle
, &FileName
, &DeviceHandle
);
1509 if (EFI_ERROR (Status
)) {
1513 Status
= LibFindFiles (FileHandle
, FileName
, DeviceHandle
);
1515 if (EFI_ERROR (Status
)) {
1519 LibUpdateFileExplorePage();
1521 gFileExplorerPrivate
.FormBrowser2
->SendForm (
1522 gFileExplorerPrivate
.FormBrowser2
,
1523 &gFileExplorerPrivate
.FeHiiHandle
,
1532 if ((Status
== EFI_SUCCESS
) && (File
!= NULL
)) {
1533 *File
= gFileExplorerPrivate
.RetDevicePath
;
1534 } else if (gFileExplorerPrivate
.RetDevicePath
!= NULL
) {
1535 FreePool (gFileExplorerPrivate
.RetDevicePath
);
1538 if (gFileExplorerPrivate
.FileType
!= NULL
) {
1539 FreePool (gFileExplorerPrivate
.FileType
);
1542 LibFreeMenu (gFileExplorerPrivate
.FsOptionMenu
);
1544 if (FileName
!= NULL
) {
1545 FreePool (FileName
);
1553 Install Boot Manager Menu driver.
1555 @param ImageHandle The image handle.
1556 @param SystemTable The system table.
1558 @retval EFI_SUCEESS Install File explorer library success.
1563 FileExplorerLibConstructor (
1564 IN EFI_HANDLE ImageHandle
,
1565 IN EFI_SYSTEM_TABLE
*SystemTable
1570 gHiiVendorDevicePath
= (HII_VENDOR_DEVICE_PATH
*) DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL
*)&FeHiiVendorDevicePath
);
1571 ASSERT (gHiiVendorDevicePath
!= NULL
);
1572 CopyGuid (&gHiiVendorDevicePath
->VendorDevicePath
.Guid
, &gEfiCallerIdGuid
);
1575 // Install Device Path Protocol and Config Access protocol to driver handle
1577 Status
= gBS
->InstallMultipleProtocolInterfaces (
1578 &gFileExplorerPrivate
.FeDriverHandle
,
1579 &gEfiDevicePathProtocolGuid
,
1580 gHiiVendorDevicePath
,
1581 &gEfiHiiConfigAccessProtocolGuid
,
1582 &gFileExplorerPrivate
.FeConfigAccess
,
1585 if (Status
== EFI_ALREADY_STARTED
) {
1588 if (EFI_ERROR (Status
)) {
1593 // Post our File Explorer VFR binary to the HII database.
1595 gFileExplorerPrivate
.FeHiiHandle
= HiiAddPackages (
1597 gFileExplorerPrivate
.FeDriverHandle
,
1599 FileExplorerLibStrings
,
1602 ASSERT (gFileExplorerPrivate
.FeHiiHandle
!= NULL
);
1605 // Locate Formbrowser2 protocol
1607 Status
= gBS
->LocateProtocol (&gEfiFormBrowser2ProtocolGuid
, NULL
, (VOID
**) &gFileExplorerPrivate
.FormBrowser2
);
1608 ASSERT_EFI_ERROR (Status
);
1610 InitializeListHead (&gFileExplorerPrivate
.FsOptionMenu
->Head
);
1616 Unloads the application and its installed protocol.
1618 @param[in] ImageHandle Handle that identifies the image to be unloaded.
1619 @param[in] SystemTable The system table.
1621 @retval EFI_SUCCESS The image has been unloaded.
1625 FileExplorerLibDestructor (
1626 IN EFI_HANDLE ImageHandle
,
1627 IN EFI_SYSTEM_TABLE
*SystemTable
1632 ASSERT (gHiiVendorDevicePath
!= NULL
);
1634 if (gFileExplorerPrivate
.FeDriverHandle
!= NULL
) {
1635 Status
= gBS
->UninstallMultipleProtocolInterfaces (
1636 gFileExplorerPrivate
.FeDriverHandle
,
1637 &gEfiDevicePathProtocolGuid
,
1638 gHiiVendorDevicePath
,
1639 &gEfiHiiConfigAccessProtocolGuid
,
1640 &gFileExplorerPrivate
.FeConfigAccess
,
1643 ASSERT_EFI_ERROR (Status
);
1645 HiiRemovePackages (gFileExplorerPrivate
.FeHiiHandle
);
1648 FreePool (gHiiVendorDevicePath
);