]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/FileExplorerLib/FileExplorer.c
MdeModulePkg/FileExplorer: Remove the codes of searching load file protocol
[mirror_edk2.git] / MdeModulePkg / Library / FileExplorerLib / FileExplorer.c
1 /** @file
2 File explorer related functions.
3
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.
9
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.
12
13 **/
14
15
16 #include "FileExplorer.h"
17
18 EFI_GUID FileExplorerGuid = EFI_FILE_EXPLORE_FORMSET_GUID;
19
20 ///
21 /// File system selection menu
22 ///
23 MENU_OPTION mFsOptionMenu = {
24 MENU_OPTION_SIGNATURE,
25 {NULL},
26 0,
27 FALSE
28 };
29
30 FILE_EXPLORER_CALLBACK_DATA gFileExplorerPrivate = {
31 FILE_EXPLORER_CALLBACK_DATA_SIGNATURE,
32 NULL,
33 NULL,
34 {
35 LibExtractConfig,
36 LibRouteConfig,
37 LibCallback
38 },
39 NULL,
40 &mFsOptionMenu,
41 0
42 };
43
44 HII_VENDOR_DEVICE_PATH *gHiiVendorDevicePath;
45
46 HII_VENDOR_DEVICE_PATH FeHiiVendorDevicePath = {
47 {
48 {
49 HARDWARE_DEVICE_PATH,
50 HW_VENDOR_DP,
51 {
52 (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
53 (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
54 }
55 },
56 //
57 // Will be replace with gEfiCallerIdGuid in code.
58 //
59 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }
60 },
61 {
62 END_DEVICE_PATH_TYPE,
63 END_ENTIRE_DEVICE_PATH_SUBTYPE,
64 {
65 (UINT8) (END_DEVICE_PATH_LENGTH),
66 (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
67 }
68 }
69 };
70
71 VOID *mLibStartOpCodeHandle = NULL;
72 VOID *mLibEndOpCodeHandle = NULL;
73 EFI_IFR_GUID_LABEL *mLibStartLabel = NULL;
74 EFI_IFR_GUID_LABEL *mLibEndLabel = NULL;
75
76 /**
77 This function allows a caller to extract the current configuration for one
78 or more named elements from the target driver.
79
80
81 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
82 @param Request A null-terminated Unicode string in <ConfigRequest> format.
83 @param Progress On return, points to a character in the Request string.
84 Points to the string's null terminator if request was successful.
85 Points to the most recent '&' before the first failing name/value
86 pair (or the beginning of the string if the failure is in the
87 first name/value pair) if the request was not successful.
88 @param Results A null-terminated Unicode string in <ConfigAltResp> format which
89 has all values filled in for the names in the Request string.
90 String to be allocated by the called function.
91
92 @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name.
93 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
94
95 **/
96 EFI_STATUS
97 EFIAPI
98 LibExtractConfig (
99 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
100 IN CONST EFI_STRING Request,
101 OUT EFI_STRING *Progress,
102 OUT EFI_STRING *Results
103 )
104 {
105 if (Progress == NULL || Results == NULL) {
106 return EFI_INVALID_PARAMETER;
107 }
108
109 *Progress = Request;
110 return EFI_NOT_FOUND;
111 }
112
113 /**
114 This function processes the results of changes in configuration.
115
116
117 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
118 @param Configuration A null-terminated Unicode string in <ConfigResp> format.
119 @param Progress A pointer to a string filled in with the offset of the most
120 recent '&' before the first failing name/value pair (or the
121 beginning of the string if the failure is in the first
122 name/value pair) or the terminating NULL if all was successful.
123
124 @retval EFI_INVALID_PARAMETER Configuration is NULL.
125 @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver.
126
127 **/
128 EFI_STATUS
129 EFIAPI
130 LibRouteConfig (
131 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
132 IN CONST EFI_STRING Configuration,
133 OUT EFI_STRING *Progress
134 )
135 {
136 if (Configuration == NULL || Progress == NULL) {
137 return EFI_INVALID_PARAMETER;
138 }
139
140 *Progress = Configuration;
141 return EFI_NOT_FOUND;
142 }
143
144 /**
145 This function processes the results of changes in configuration.
146 When user select a interactive opcode, this callback will be triggered.
147 Based on the Question(QuestionId) that triggers the callback, the corresponding
148 actions is performed. It handles:
149
150 1) Process the axtra action or exit file explorer when user select one file .
151 2) update of file content if a dir is selected.
152
153 @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
154 @param Action Specifies the type of action taken by the browser.
155 @param QuestionId A unique value which is sent to the original exporting driver
156 so that it can identify the type of data to expect.
157 @param Type The type of value for the question.
158 @param Value A pointer to the data being sent to the original exporting driver.
159 @param ActionRequest On return, points to the action requested by the callback function.
160
161 @retval EFI_SUCCESS The callback successfully handled the action.
162 @retval other error Error occur when parse one directory.
163 **/
164 EFI_STATUS
165 EFIAPI
166 LibCallback (
167 IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
168 IN EFI_BROWSER_ACTION Action,
169 IN EFI_QUESTION_ID QuestionId,
170 IN UINT8 Type,
171 IN EFI_IFR_TYPE_VALUE *Value,
172 OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
173 )
174 {
175 EFI_STATUS Status;
176 BOOLEAN NeedExit;
177
178 NeedExit = TRUE;
179
180 if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
181 //
182 // Do nothing for other UEFI Action. Only do call back when data is changed.
183 //
184 return EFI_UNSUPPORTED;
185 }
186
187 if (Action == EFI_BROWSER_ACTION_CHANGED) {
188 if ((Value == NULL) || (ActionRequest == NULL)) {
189 return EFI_INVALID_PARAMETER;
190 }
191
192 if (QuestionId >= FILE_OPTION_OFFSET) {
193 LibGetDevicePath(QuestionId);
194
195 //
196 // Process the extra action.
197 //
198 if (gFileExplorerPrivate.ChooseHandler != NULL) {
199 NeedExit = gFileExplorerPrivate.ChooseHandler (gFileExplorerPrivate.RetDevicePath);
200 }
201
202 if (NeedExit) {
203 *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
204 }
205 }
206 } else if (Action == EFI_BROWSER_ACTION_CHANGING) {
207 if (Value == NULL) {
208 return EFI_INVALID_PARAMETER;
209 }
210
211 if (QuestionId >= FILE_OPTION_OFFSET) {
212 Status = LibUpdateFileExplorer (QuestionId);
213 if (EFI_ERROR (Status)) {
214 return Status;
215 }
216 }
217 }
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Create a menu entry by given menu type.
224
225 @retval NULL If failed to create the menu.
226 @return the new menu entry.
227
228 **/
229 MENU_ENTRY *
230 LibCreateMenuEntry (
231 VOID
232 )
233 {
234 MENU_ENTRY *MenuEntry;
235
236 //
237 // Create new menu entry
238 //
239 MenuEntry = AllocateZeroPool (sizeof (MENU_ENTRY));
240 if (MenuEntry == NULL) {
241 return NULL;
242 }
243
244 MenuEntry->VariableContext = AllocateZeroPool (sizeof (FILE_CONTEXT));
245 if (MenuEntry->VariableContext == NULL) {
246 FreePool (MenuEntry);
247 return NULL;
248 }
249
250 MenuEntry->Signature = MENU_ENTRY_SIGNATURE;
251 return MenuEntry;
252 }
253
254
255 /**
256 Get the Menu Entry from the list in Menu Entry List.
257
258 If MenuNumber is great or equal to the number of Menu
259 Entry in the list, then ASSERT.
260
261 @param MenuOption The Menu Entry List to read the menu entry.
262 @param MenuNumber The index of Menu Entry.
263
264 @return The Menu Entry.
265
266 **/
267 MENU_ENTRY *
268 LibGetMenuEntry (
269 MENU_OPTION *MenuOption,
270 UINTN MenuNumber
271 )
272 {
273 MENU_ENTRY *NewMenuEntry;
274 UINTN Index;
275 LIST_ENTRY *List;
276
277 ASSERT (MenuNumber < MenuOption->MenuNumber);
278
279 List = MenuOption->Head.ForwardLink;
280 for (Index = 0; Index < MenuNumber; Index++) {
281 List = List->ForwardLink;
282 }
283
284 NewMenuEntry = CR (List, MENU_ENTRY, Link, MENU_ENTRY_SIGNATURE);
285
286 return NewMenuEntry;
287 }
288
289 /**
290 Free up all resource allocated for a BM_MENU_ENTRY.
291
292 @param MenuEntry A pointer to BM_MENU_ENTRY.
293
294 **/
295 VOID
296 LibDestroyMenuEntry (
297 MENU_ENTRY *MenuEntry
298 )
299 {
300 FILE_CONTEXT *FileContext;
301
302 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
303
304 if (!FileContext->IsRoot) {
305 if (FileContext->DevicePath != NULL) {
306 FreePool (FileContext->DevicePath);
307 }
308 } else {
309 if (FileContext->FileHandle != NULL) {
310 FileContext->FileHandle->Close (FileContext->FileHandle);
311 }
312 }
313
314 if (FileContext->FileName != NULL) {
315 FreePool (FileContext->FileName);
316 }
317
318 FreePool (FileContext);
319
320 if (MenuEntry->DisplayString != NULL) {
321 FreePool (MenuEntry->DisplayString);
322 }
323 if (MenuEntry->HelpString != NULL) {
324 FreePool (MenuEntry->HelpString);
325 }
326
327 FreePool (MenuEntry);
328 }
329
330
331 /**
332 Free resources allocated in Allocate Rountine.
333
334 @param FreeMenu Menu to be freed
335 **/
336 VOID
337 LibFreeMenu (
338 MENU_OPTION *FreeMenu
339 )
340 {
341 MENU_ENTRY *MenuEntry;
342 while (!IsListEmpty (&FreeMenu->Head)) {
343 MenuEntry = CR (
344 FreeMenu->Head.ForwardLink,
345 MENU_ENTRY,
346 Link,
347 MENU_ENTRY_SIGNATURE
348 );
349 RemoveEntryList (&MenuEntry->Link);
350 LibDestroyMenuEntry (MenuEntry);
351 }
352 FreeMenu->MenuNumber = 0;
353 }
354
355 /**
356
357 Function opens and returns a file handle to the root directory of a volume.
358
359 @param DeviceHandle A handle for a device
360
361 @return A valid file handle or NULL is returned
362
363 **/
364 EFI_FILE_HANDLE
365 LibOpenRoot (
366 IN EFI_HANDLE DeviceHandle
367 )
368 {
369 EFI_STATUS Status;
370 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
371 EFI_FILE_HANDLE File;
372
373 File = NULL;
374
375 //
376 // File the file system interface to the device
377 //
378 Status = gBS->HandleProtocol (
379 DeviceHandle,
380 &gEfiSimpleFileSystemProtocolGuid,
381 (VOID *) &Volume
382 );
383
384 //
385 // Open the root directory of the volume
386 //
387 if (!EFI_ERROR (Status)) {
388 Status = Volume->OpenVolume (
389 Volume,
390 &File
391 );
392 }
393 //
394 // Done
395 //
396 return EFI_ERROR (Status) ? NULL : File;
397 }
398
399 /**
400 This function converts an input device structure to a Unicode string.
401
402 @param DevPath A pointer to the device path structure.
403
404 @return A new allocated Unicode string that represents the device path.
405
406 **/
407 CHAR16 *
408 LibDevicePathToStr (
409 IN EFI_DEVICE_PATH_PROTOCOL *DevPath
410 )
411 {
412 EFI_STATUS Status;
413 CHAR16 *ToText;
414 EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;
415
416 if (DevPath == NULL) {
417 return NULL;
418 }
419
420 Status = gBS->LocateProtocol (
421 &gEfiDevicePathToTextProtocolGuid,
422 NULL,
423 (VOID **) &DevPathToText
424 );
425 ASSERT_EFI_ERROR (Status);
426 ToText = DevPathToText->ConvertDevicePathToText (
427 DevPath,
428 FALSE,
429 TRUE
430 );
431 ASSERT (ToText != NULL);
432
433 return ToText;
434 }
435
436 /**
437 Duplicate a string.
438
439 @param Src The source.
440
441 @return A new string which is duplicated copy of the source.
442 @retval NULL If there is not enough memory.
443
444 **/
445 CHAR16 *
446 LibStrDuplicate (
447 IN CHAR16 *Src
448 )
449 {
450 CHAR16 *Dest;
451 UINTN Size;
452
453 Size = StrSize (Src);
454 Dest = AllocateZeroPool (Size);
455 ASSERT (Dest != NULL);
456 if (Dest != NULL) {
457 CopyMem (Dest, Src, Size);
458 }
459
460 return Dest;
461 }
462
463 /**
464
465 Function gets the file information from an open file descriptor, and stores it
466 in a buffer allocated from pool.
467
468 @param FHand File Handle.
469 @param InfoType Info type need to get.
470
471 @retval A pointer to a buffer with file information or NULL is returned
472
473 **/
474 VOID *
475 LibFileInfo (
476 IN EFI_FILE_HANDLE FHand,
477 IN EFI_GUID *InfoType
478 )
479 {
480 EFI_STATUS Status;
481 EFI_FILE_INFO *Buffer;
482 UINTN BufferSize;
483
484 Buffer = NULL;
485 BufferSize = 0;
486
487 Status = FHand->GetInfo (
488 FHand,
489 InfoType,
490 &BufferSize,
491 Buffer
492 );
493 if (Status == EFI_BUFFER_TOO_SMALL) {
494 Buffer = AllocatePool (BufferSize);
495 ASSERT (Buffer != NULL);
496 }
497
498 Status = FHand->GetInfo (
499 FHand,
500 InfoType,
501 &BufferSize,
502 Buffer
503 );
504
505 return Buffer;
506 }
507
508 /**
509
510 Get file type base on the file name.
511 Just cut the file name, from the ".". eg ".efi"
512
513 @param FileName File need to be checked.
514
515 @retval the file type string.
516
517 **/
518 CHAR16*
519 LibGetTypeFromName (
520 IN CHAR16 *FileName
521 )
522 {
523 UINTN Index;
524
525 Index = StrLen (FileName) - 1;
526 while ((FileName[Index] != L'.') && (Index != 0)) {
527 Index--;
528 }
529
530 return Index == 0 ? NULL : &FileName[Index];
531 }
532
533 /**
534 Converts the unicode character of the string from uppercase to lowercase.
535 This is a internal function.
536
537 @param ConfigString String to be converted
538
539 **/
540 VOID
541 LibToLowerString (
542 IN CHAR16 *String
543 )
544 {
545 CHAR16 *TmpStr;
546
547 for (TmpStr = String; *TmpStr != L'\0'; TmpStr++) {
548 if (*TmpStr >= L'A' && *TmpStr <= L'Z') {
549 *TmpStr = (CHAR16) (*TmpStr - L'A' + L'a');
550 }
551 }
552 }
553
554 /**
555
556 Check whether current FileName point to a valid
557 Efi Image File.
558
559 @param FileName File need to be checked.
560
561 @retval TRUE Is Efi Image
562 @retval FALSE Not a valid Efi Image
563
564 **/
565 BOOLEAN
566 LibIsSupportedFileType (
567 IN UINT16 *FileName
568 )
569 {
570 CHAR16 *InputFileType;
571 CHAR16 *TmpStr;
572 BOOLEAN IsSupported;
573
574 if (gFileExplorerPrivate.FileType == NULL) {
575 return TRUE;
576 }
577
578 InputFileType = LibGetTypeFromName (FileName);
579 //
580 // If the file not has *.* style, always return TRUE.
581 //
582 if (InputFileType == NULL) {
583 return TRUE;
584 }
585
586 TmpStr = AllocateCopyPool (StrSize (InputFileType), InputFileType);
587 ASSERT(TmpStr != NULL);
588 LibToLowerString(TmpStr);
589
590 IsSupported = (StrStr (gFileExplorerPrivate.FileType, TmpStr) == NULL ? FALSE : TRUE);
591
592 FreePool (TmpStr);
593 return IsSupported;
594 }
595
596 /**
597
598 Append file name to existing file name.
599
600 @param Str1 The existing file name
601 @param Str2 The file name to be appended
602
603 @return Allocate a new string to hold the appended result.
604 Caller is responsible to free the returned string.
605
606 **/
607 CHAR16 *
608 LibAppendFileName (
609 IN CHAR16 *Str1,
610 IN CHAR16 *Str2
611 )
612 {
613 UINTN Size1;
614 UINTN Size2;
615 UINTN MaxLen;
616 CHAR16 *Str;
617 CHAR16 *TmpStr;
618 CHAR16 *Ptr;
619 CHAR16 *LastSlash;
620
621 Size1 = StrSize (Str1);
622 Size2 = StrSize (Str2);
623
624 //
625 // Check overflow
626 //
627 if (((MAX_UINTN - Size1) < Size2) || ((MAX_UINTN - Size1 - Size2) < sizeof(CHAR16))) {
628 return NULL;
629 }
630
631 MaxLen = (Size1 + Size2 + sizeof (CHAR16))/ sizeof (CHAR16);
632 Str = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
633 ASSERT (Str != NULL);
634
635 TmpStr = AllocateZeroPool (Size1 + Size2 + sizeof (CHAR16));
636 ASSERT (TmpStr != NULL);
637
638 StrCpyS (Str, MaxLen, Str1);
639 if (!((*Str == '\\') && (*(Str + 1) == 0))) {
640 StrCatS (Str, MaxLen, L"\\");
641 }
642
643 StrCatS (Str, MaxLen, Str2);
644
645 Ptr = Str;
646 LastSlash = Str;
647 while (*Ptr != 0) {
648 if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '.' && *(Ptr + 3) == L'\\') {
649 //
650 // Convert "\Name\..\" to "\"
651 // DO NOT convert the .. if it is at the end of the string. This will
652 // break the .. behavior in changing directories.
653 //
654
655 //
656 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
657 // that overlap.
658 //
659 StrCpyS (TmpStr, MaxLen, Ptr + 3);
660 StrCpyS (LastSlash, MaxLen - (UINTN) (LastSlash - Str), TmpStr);
661 Ptr = LastSlash;
662 } else if (*Ptr == '\\' && *(Ptr + 1) == '.' && *(Ptr + 2) == '\\') {
663 //
664 // Convert a "\.\" to a "\"
665 //
666
667 //
668 // Use TmpStr as a backup, as StrCpyS in BaseLib does not handle copy of two strings
669 // that overlap.
670 //
671 StrCpyS (TmpStr, MaxLen, Ptr + 2);
672 StrCpyS (Ptr, MaxLen - (UINTN) (Ptr - Str), TmpStr);
673 Ptr = LastSlash;
674 } else if (*Ptr == '\\') {
675 LastSlash = Ptr;
676 }
677
678 Ptr++;
679 }
680
681 FreePool (TmpStr);
682
683 return Str;
684 }
685
686 /**
687 This function build the FsOptionMenu list which records all
688 available file system in the system. They includes all instances
689 of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, all instances of EFI_LOAD_FILE_SYSTEM.
690
691
692 @retval EFI_SUCCESS Success find the file system
693 @retval EFI_OUT_OF_RESOURCES Can not create menu entry
694
695 **/
696 EFI_STATUS
697 LibFindFileSystem (
698 VOID
699 )
700 {
701 UINTN NoSimpleFsHandles;
702 EFI_HANDLE *SimpleFsHandle;
703 UINT16 *VolumeLabel;
704 UINTN Index;
705 EFI_STATUS Status;
706 MENU_ENTRY *MenuEntry;
707 FILE_CONTEXT *FileContext;
708 UINTN OptionNumber;
709 EFI_FILE_SYSTEM_VOLUME_LABEL *Info;
710
711 NoSimpleFsHandles = 0;
712 OptionNumber = 0;
713
714 //
715 // Locate Handles that support Simple File System protocol
716 //
717 Status = gBS->LocateHandleBuffer (
718 ByProtocol,
719 &gEfiSimpleFileSystemProtocolGuid,
720 NULL,
721 &NoSimpleFsHandles,
722 &SimpleFsHandle
723 );
724 if (!EFI_ERROR (Status)) {
725 //
726 // Find all the instances of the File System prototocol
727 //
728 for (Index = 0; Index < NoSimpleFsHandles; Index++) {
729 //
730 // Allocate pool for this load option
731 //
732 MenuEntry = LibCreateMenuEntry ();
733 if (NULL == MenuEntry) {
734 FreePool (SimpleFsHandle);
735 return EFI_OUT_OF_RESOURCES;
736 }
737
738 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
739 FileContext->DeviceHandle = SimpleFsHandle[Index];
740 FileContext->FileHandle = LibOpenRoot (FileContext->DeviceHandle);
741 if (FileContext->FileHandle == NULL) {
742 LibDestroyMenuEntry (MenuEntry);
743 continue;
744 }
745
746 MenuEntry->HelpString = LibDevicePathToStr (DevicePathFromHandle (FileContext->DeviceHandle));
747 FileContext->FileName = LibStrDuplicate (L"\\");
748 FileContext->DevicePath = FileDevicePath (FileContext->DeviceHandle, FileContext->FileName);
749 FileContext->IsDir = TRUE;
750 FileContext->IsRoot = TRUE;
751
752 //
753 // Get current file system's Volume Label
754 //
755 Info = (EFI_FILE_SYSTEM_VOLUME_LABEL *) LibFileInfo (FileContext->FileHandle, &gEfiFileSystemVolumeLabelInfoIdGuid);
756 if (Info == NULL) {
757 VolumeLabel = L"NO FILE SYSTEM INFO";
758 } else {
759 if (Info->VolumeLabel == NULL) {
760 VolumeLabel = L"NULL VOLUME LABEL";
761 } else {
762 VolumeLabel = Info->VolumeLabel;
763 if (*VolumeLabel == 0x0000) {
764 VolumeLabel = L"NO VOLUME LABEL";
765 }
766 }
767 }
768 MenuEntry->DisplayString = AllocateZeroPool (MAX_CHAR);
769 ASSERT (MenuEntry->DisplayString != NULL);
770 UnicodeSPrint (
771 MenuEntry->DisplayString,
772 MAX_CHAR,
773 L"%s, [%s]",
774 VolumeLabel,
775 MenuEntry->HelpString
776 );
777 MenuEntry->DisplayStringToken = HiiSetString (
778 gFileExplorerPrivate.FeHiiHandle,
779 0,
780 MenuEntry->DisplayString,
781 NULL
782 );
783
784 if (Info != NULL)
785 FreePool (Info);
786
787 OptionNumber++;
788 InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &MenuEntry->Link);
789 }
790 }
791
792 if (NoSimpleFsHandles != 0) {
793 FreePool (SimpleFsHandle);
794 }
795
796 gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber;
797
798 return EFI_SUCCESS;
799 }
800
801 /**
802 Find the file handle from the input menu info.
803
804 @param MenuEntry Input Menu info.
805 @param RetFileHandle Return the file handle for the input device path.
806
807 @retval EFI_SUCESS Find the file handle success.
808 @retval Other Find the file handle failure.
809 **/
810 EFI_STATUS
811 LibGetFileHandleFromMenu (
812 IN MENU_ENTRY *MenuEntry,
813 OUT EFI_FILE_HANDLE *RetFileHandle
814 )
815 {
816 EFI_FILE_HANDLE Dir;
817 EFI_FILE_HANDLE NewDir;
818 FILE_CONTEXT *FileContext;
819 EFI_STATUS Status;
820
821 FileContext = (FILE_CONTEXT *) MenuEntry->VariableContext;
822 Dir = FileContext->FileHandle;
823
824 //
825 // Open current directory to get files from it
826 //
827 Status = Dir->Open (
828 Dir,
829 &NewDir,
830 FileContext->FileName,
831 EFI_FILE_READ_ONLY,
832 0
833 );
834 if (EFI_ERROR (Status)) {
835 return Status;
836 }
837
838 if (!FileContext->IsRoot) {
839 Dir->Close (Dir);
840 }
841
842 *RetFileHandle = NewDir;
843
844 return EFI_SUCCESS;
845 }
846
847 /**
848 Find the file handle from the input device path info.
849
850 @param RootDirectory Device path info.
851 @param RetFileHandle Return the file handle for the input device path.
852 @param ParentFileName Parent file name.
853 @param DeviceHandle Driver handle for this partition.
854
855 @retval EFI_SUCESS Find the file handle success.
856 @retval Other Find the file handle failure.
857 **/
858 EFI_STATUS
859 LibGetFileHandleFromDevicePath (
860 IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory,
861 OUT EFI_FILE_HANDLE *RetFileHandle,
862 OUT UINT16 **ParentFileName,
863 OUT EFI_HANDLE *DeviceHandle
864 )
865 {
866 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
867 EFI_DEVICE_PATH_PROTOCOL *TempDevicePathNode;
868 EFI_STATUS Status;
869 EFI_HANDLE Handle;
870 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
871 EFI_FILE_HANDLE FileHandle;
872 EFI_FILE_HANDLE LastHandle;
873 CHAR16 *TempPath;
874
875 *ParentFileName = NULL;
876
877 //
878 // Attempt to access the file via a file system interface
879 //
880 DevicePathNode = RootDirectory;
881 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &DevicePathNode, &Handle);
882 if (EFI_ERROR (Status)) {
883 return Status;
884 }
885
886 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Volume);
887 if (EFI_ERROR (Status)) {
888 return Status;
889 }
890
891 //
892 // Open the Volume to get the File System handle
893 //
894 Status = Volume->OpenVolume (Volume, &FileHandle);
895 if (EFI_ERROR (Status)) {
896 return Status;
897 }
898
899 *DeviceHandle = Handle;
900
901 if (IsDevicePathEnd(DevicePathNode)) {
902 *ParentFileName = AllocateCopyPool (StrSize (L"\\"), L"\\");
903 *RetFileHandle = FileHandle;
904 return EFI_SUCCESS;
905 }
906
907 //
908 // Duplicate the device path to avoid the access to unaligned device path node.
909 // Because the device path consists of one or more FILE PATH MEDIA DEVICE PATH
910 // nodes, It assures the fields in device path nodes are 2 byte aligned.
911 //
912 TempDevicePathNode = DuplicateDevicePath (DevicePathNode);
913 if (TempDevicePathNode == NULL) {
914
915 //
916 // Setting Status to an EFI_ERROR value will cause the rest of
917 // the file system support below to be skipped.
918 //
919 Status = EFI_OUT_OF_RESOURCES;
920 goto Done;
921 }
922
923 //
924 // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the
925 // directory information and filename can be seperate. The goal is to inch
926 // our way down each device path node and close the previous node
927 //
928 DevicePathNode = TempDevicePathNode;
929 while (!EFI_ERROR (Status) && !IsDevicePathEnd (DevicePathNode)) {
930 if (DevicePathType (DevicePathNode) != MEDIA_DEVICE_PATH ||
931 DevicePathSubType (DevicePathNode) != MEDIA_FILEPATH_DP) {
932 Status = EFI_UNSUPPORTED;
933 goto Done;
934 }
935
936 LastHandle = FileHandle;
937 FileHandle = NULL;
938
939 Status = LastHandle->Open (
940 LastHandle,
941 &FileHandle,
942 ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName,
943 EFI_FILE_MODE_READ,
944 0
945 );
946 if (*ParentFileName == NULL) {
947 *ParentFileName = AllocateCopyPool (StrSize (((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName), ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName);
948 } else {
949 TempPath = LibAppendFileName (*ParentFileName, ((FILEPATH_DEVICE_PATH *) DevicePathNode)->PathName);
950 if (TempPath == NULL) {
951 LastHandle->Close (LastHandle);
952 Status = EFI_OUT_OF_RESOURCES;
953 goto Done;
954 }
955 FreePool (*ParentFileName);
956 *ParentFileName = TempPath;
957 }
958
959 //
960 // Close the previous node
961 //
962 LastHandle->Close (LastHandle);
963
964 DevicePathNode = NextDevicePathNode (DevicePathNode);
965 }
966
967 if (EFI_ERROR (Status)) {
968 goto Done;
969 }
970
971 *RetFileHandle = FileHandle;
972
973 Status = EFI_SUCCESS;
974
975 Done:
976 if (TempDevicePathNode != NULL) {
977 FreePool (TempDevicePathNode);
978 }
979
980 if ((FileHandle != NULL) && (EFI_ERROR (Status))) {
981 FileHandle->Close (FileHandle);
982 }
983
984 return Status;
985 }
986
987 /**
988 Find files under current directory.
989
990 All files and sub-directories in current directory
991 will be stored in DirectoryMenu for future use.
992
993 @param FileHandle Parent file handle.
994 @param FileName Parent file name.
995 @param DeviceHandle Driver handle for this partition.
996
997 @retval EFI_SUCCESS Get files from current dir successfully.
998 @return Other value if can't get files from current dir.
999
1000 **/
1001 EFI_STATUS
1002 LibFindFiles (
1003 IN EFI_FILE_HANDLE FileHandle,
1004 IN UINT16 *FileName,
1005 IN EFI_HANDLE DeviceHandle
1006 )
1007 {
1008 EFI_FILE_INFO *DirInfo;
1009 UINTN BufferSize;
1010 UINTN DirBufferSize;
1011 MENU_ENTRY *NewMenuEntry;
1012 FILE_CONTEXT *NewFileContext;
1013 UINTN Pass;
1014 EFI_STATUS Status;
1015 UINTN OptionNumber;
1016
1017 OptionNumber = 0;
1018
1019 DirBufferSize = sizeof (EFI_FILE_INFO) + 1024;
1020 DirInfo = AllocateZeroPool (DirBufferSize);
1021 if (DirInfo == NULL) {
1022 return EFI_OUT_OF_RESOURCES;
1023 }
1024
1025 //
1026 // Get all files in current directory
1027 // Pass 1 to get Directories
1028 // Pass 2 to get files that are EFI images
1029 //
1030 Status = EFI_SUCCESS;
1031 for (Pass = 1; Pass <= 2; Pass++) {
1032 FileHandle->SetPosition (FileHandle, 0);
1033 for (;;) {
1034 BufferSize = DirBufferSize;
1035 Status = FileHandle->Read (FileHandle, &BufferSize, DirInfo);
1036 if (EFI_ERROR (Status) || BufferSize == 0) {
1037 Status = EFI_SUCCESS;
1038 break;
1039 }
1040
1041 if (((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 && Pass == 2) ||
1042 ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0 && Pass == 1)
1043 ) {
1044 //
1045 // Pass 1 is for Directories
1046 // Pass 2 is for file names
1047 //
1048 continue;
1049 }
1050
1051 if (!((DirInfo->Attribute & EFI_FILE_DIRECTORY) != 0 || LibIsSupportedFileType (DirInfo->FileName))) {
1052 //
1053 // Slip file unless it is a directory entry or a .EFI file
1054 //
1055 continue;
1056 }
1057
1058 NewMenuEntry = LibCreateMenuEntry ();
1059 if (NULL == NewMenuEntry) {
1060 Status = EFI_OUT_OF_RESOURCES;
1061 goto Done;
1062 }
1063
1064 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1065 NewFileContext->DeviceHandle = DeviceHandle;
1066 NewFileContext->FileName = LibAppendFileName (FileName, DirInfo->FileName);
1067 if (NewFileContext->FileName == NULL) {
1068 LibDestroyMenuEntry (NewMenuEntry);
1069 Status = EFI_OUT_OF_RESOURCES;
1070 goto Done;
1071 }
1072 NewFileContext->FileHandle = FileHandle;
1073 NewFileContext->DevicePath = FileDevicePath (NewFileContext->DeviceHandle, NewFileContext->FileName);
1074 NewMenuEntry->HelpString = NULL;
1075 NewFileContext->IsDir = (BOOLEAN) ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == EFI_FILE_DIRECTORY);
1076
1077 if (NewFileContext->IsDir) {
1078 BufferSize = StrLen (DirInfo->FileName) * 2 + 6;
1079 NewMenuEntry->DisplayString = AllocateZeroPool (BufferSize);
1080 UnicodeSPrint (
1081 NewMenuEntry->DisplayString,
1082 BufferSize,
1083 L"<%s>",
1084 DirInfo->FileName
1085 );
1086 } else {
1087 NewMenuEntry->DisplayString = LibStrDuplicate (DirInfo->FileName);
1088 }
1089
1090 NewMenuEntry->DisplayStringToken = HiiSetString (
1091 gFileExplorerPrivate.FeHiiHandle,
1092 0,
1093 NewMenuEntry->DisplayString,
1094 NULL
1095 );
1096
1097 NewFileContext->IsRoot = FALSE;
1098
1099 OptionNumber++;
1100 InsertTailList (&gFileExplorerPrivate.FsOptionMenu->Head, &NewMenuEntry->Link);
1101 }
1102 }
1103
1104 gFileExplorerPrivate.FsOptionMenu->MenuNumber = OptionNumber;
1105
1106 Done:
1107
1108 FreePool (DirInfo);
1109
1110 return Status;
1111 }
1112
1113 /**
1114 Refresh the global UpdateData structure.
1115
1116 **/
1117 VOID
1118 LibRefreshUpdateData (
1119 VOID
1120 )
1121 {
1122 //
1123 // Free current updated date
1124 //
1125 if (mLibStartOpCodeHandle != NULL) {
1126 HiiFreeOpCodeHandle (mLibStartOpCodeHandle);
1127 }
1128 if (mLibEndOpCodeHandle != NULL) {
1129 HiiFreeOpCodeHandle (mLibEndOpCodeHandle);
1130 }
1131
1132 //
1133 // Create new OpCode Handle
1134 //
1135 mLibStartOpCodeHandle = HiiAllocateOpCodeHandle ();
1136 mLibEndOpCodeHandle = HiiAllocateOpCodeHandle ();
1137
1138 //
1139 // Create Hii Extend Label OpCode as the start opcode
1140 //
1141 mLibStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
1142 mLibStartOpCodeHandle,
1143 &gEfiIfrTianoGuid,
1144 NULL,
1145 sizeof (EFI_IFR_GUID_LABEL)
1146 );
1147 mLibStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
1148
1149 mLibStartLabel->Number = FORM_FILE_EXPLORER_ID;
1150
1151 //
1152 // Create Hii Extend Label OpCode as the start opcode
1153 //
1154 mLibEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
1155 mLibEndOpCodeHandle,
1156 &gEfiIfrTianoGuid,
1157 NULL,
1158 sizeof (EFI_IFR_GUID_LABEL)
1159 );
1160 mLibEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
1161
1162 mLibEndLabel->Number = LABEL_END;
1163 }
1164
1165 /**
1166
1167 Update the File Explore page.
1168
1169 **/
1170 VOID
1171 LibUpdateFileExplorePage (
1172 VOID
1173 )
1174 {
1175 UINTN Index;
1176 MENU_ENTRY *NewMenuEntry;
1177 FILE_CONTEXT *NewFileContext;
1178 MENU_OPTION *MenuOption;
1179
1180 NewMenuEntry = NULL;
1181 NewFileContext = NULL;
1182
1183 LibRefreshUpdateData ();
1184 MenuOption = gFileExplorerPrivate.FsOptionMenu;
1185
1186 for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
1187 NewMenuEntry = LibGetMenuEntry (MenuOption, Index);
1188 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1189
1190 if (!NewFileContext->IsDir) {
1191 //
1192 // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile.
1193 //
1194 HiiCreateActionOpCode (
1195 mLibStartOpCodeHandle,
1196 (UINT16) (FILE_OPTION_OFFSET + Index),
1197 NewMenuEntry->DisplayStringToken,
1198 STRING_TOKEN (STR_NULL_STRING),
1199 EFI_IFR_FLAG_CALLBACK,
1200 0
1201 );
1202 } else {
1203 //
1204 // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState.
1205 //
1206 HiiCreateGotoOpCode (
1207 mLibStartOpCodeHandle,
1208 FORM_FILE_EXPLORER_ID,
1209 NewMenuEntry->DisplayStringToken,
1210 STRING_TOKEN (STR_NULL_STRING),
1211 EFI_IFR_FLAG_CALLBACK,
1212 (UINT16) (FILE_OPTION_OFFSET + Index)
1213 );
1214 }
1215 }
1216
1217 HiiUpdateForm (
1218 gFileExplorerPrivate.FeHiiHandle,
1219 &FileExplorerGuid,
1220 FORM_FILE_EXPLORER_ID,
1221 mLibStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID
1222 mLibEndOpCodeHandle // LABEL_END
1223 );
1224 }
1225
1226 /**
1227 Update the file explower page with the refershed file system.
1228
1229 @param KeyValue Key value to identify the type of data to expect.
1230
1231 @retval EFI_SUCCESS Update the file explorer form success.
1232 @retval other errors Error occur when parse one directory.
1233
1234 **/
1235 EFI_STATUS
1236 LibUpdateFileExplorer (
1237 IN UINT16 KeyValue
1238 )
1239 {
1240 UINT16 FileOptionMask;
1241 MENU_ENTRY *NewMenuEntry;
1242 FILE_CONTEXT *NewFileContext;
1243 EFI_STATUS Status;
1244 EFI_FILE_HANDLE FileHandle;
1245
1246 Status = EFI_SUCCESS;
1247 FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue);
1248 NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask);
1249 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1250
1251 if (NewFileContext->IsDir) {
1252 RemoveEntryList (&NewMenuEntry->Link);
1253 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1254 LibGetFileHandleFromMenu (NewMenuEntry, &FileHandle);
1255 Status = LibFindFiles (FileHandle, NewFileContext->FileName, NewFileContext->DeviceHandle);
1256 if (!EFI_ERROR (Status)) {
1257 LibUpdateFileExplorePage ();
1258 } else {
1259 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1260 }
1261 LibDestroyMenuEntry (NewMenuEntry);
1262 }
1263
1264 return Status;
1265 }
1266
1267 /**
1268 Get the device path info saved in the menu structure.
1269
1270 @param KeyValue Key value to identify the type of data to expect.
1271
1272 **/
1273 VOID
1274 LibGetDevicePath (
1275 IN UINT16 KeyValue
1276 )
1277 {
1278 UINT16 FileOptionMask;
1279 MENU_ENTRY *NewMenuEntry;
1280 FILE_CONTEXT *NewFileContext;
1281
1282 FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue);
1283
1284 NewMenuEntry = LibGetMenuEntry (gFileExplorerPrivate.FsOptionMenu, FileOptionMask);
1285
1286 NewFileContext = (FILE_CONTEXT *) NewMenuEntry->VariableContext;
1287
1288 if (gFileExplorerPrivate.RetDevicePath != NULL) {
1289 FreePool (gFileExplorerPrivate.RetDevicePath);
1290 }
1291 gFileExplorerPrivate.RetDevicePath = DuplicateDevicePath (NewFileContext->DevicePath);
1292 }
1293
1294 /**
1295 Choose a file in the specified directory.
1296
1297 If user input NULL for the RootDirectory, will choose file in the system.
1298
1299 If user input *File != NULL, function will return the allocate device path
1300 info for the choosed file, caller has to free the memory after use it.
1301
1302 @param RootDirectory Pointer to the root directory.
1303 @param FileType The file type need to choose.
1304 @param ChooseHandler Function pointer to the extra task need to do
1305 after choose one file.
1306 @param File Return the device path for the last time chosed file.
1307
1308 @retval EFI_SUCESS Choose file success.
1309 @retval EFI_INVALID_PARAMETER Both ChooseHandler and return device path are NULL
1310 One of them must not NULL.
1311 @retval Other errors Choose file failed.
1312 **/
1313 EFI_STATUS
1314 EFIAPI
1315 ChooseFile (
1316 IN EFI_DEVICE_PATH_PROTOCOL *RootDirectory,
1317 IN CHAR16 *FileType, OPTIONAL
1318 IN CHOOSE_HANDLER ChooseHandler, OPTIONAL
1319 OUT EFI_DEVICE_PATH_PROTOCOL **File OPTIONAL
1320 )
1321 {
1322 EFI_FILE_HANDLE FileHandle;
1323 EFI_STATUS Status;
1324 UINT16 *FileName;
1325 EFI_HANDLE DeviceHandle;
1326
1327 if ((ChooseHandler == NULL) && (File == NULL)) {
1328 return EFI_INVALID_PARAMETER;
1329 }
1330
1331 FileName = NULL;
1332
1333 gFileExplorerPrivate.RetDevicePath = NULL;
1334 gFileExplorerPrivate.ChooseHandler = ChooseHandler;
1335 if (FileType != NULL) {
1336 gFileExplorerPrivate.FileType = AllocateCopyPool (StrSize (FileType), FileType);
1337 ASSERT(gFileExplorerPrivate.FileType != NULL);
1338 LibToLowerString(gFileExplorerPrivate.FileType);
1339 } else {
1340 gFileExplorerPrivate.FileType = NULL;
1341 }
1342
1343 if (RootDirectory == NULL) {
1344 Status = LibFindFileSystem();
1345 } else {
1346 Status = LibGetFileHandleFromDevicePath(RootDirectory, &FileHandle, &FileName, &DeviceHandle);
1347 if (EFI_ERROR (Status)) {
1348 goto Done;
1349 }
1350
1351 Status = LibFindFiles (FileHandle, FileName, DeviceHandle);
1352 }
1353 if (EFI_ERROR (Status)) {
1354 goto Done;
1355 }
1356
1357 LibUpdateFileExplorePage();
1358
1359 gFileExplorerPrivate.FormBrowser2->SendForm (
1360 gFileExplorerPrivate.FormBrowser2,
1361 &gFileExplorerPrivate.FeHiiHandle,
1362 1,
1363 &FileExplorerGuid,
1364 0,
1365 NULL,
1366 NULL
1367 );
1368
1369 Done:
1370 if ((Status == EFI_SUCCESS) && (File != NULL)) {
1371 *File = gFileExplorerPrivate.RetDevicePath;
1372 } else if (gFileExplorerPrivate.RetDevicePath != NULL) {
1373 FreePool (gFileExplorerPrivate.RetDevicePath);
1374 }
1375
1376 if (gFileExplorerPrivate.FileType != NULL) {
1377 FreePool (gFileExplorerPrivate.FileType);
1378 }
1379
1380 LibFreeMenu (gFileExplorerPrivate.FsOptionMenu);
1381
1382 if (FileName != NULL) {
1383 FreePool (FileName);
1384 }
1385
1386 return Status;
1387 }
1388
1389 /**
1390
1391 Install Boot Manager Menu driver.
1392
1393 @param ImageHandle The image handle.
1394 @param SystemTable The system table.
1395
1396 @retval EFI_SUCEESS Install File explorer library success.
1397
1398 **/
1399 EFI_STATUS
1400 EFIAPI
1401 FileExplorerLibConstructor (
1402 IN EFI_HANDLE ImageHandle,
1403 IN EFI_SYSTEM_TABLE *SystemTable
1404 )
1405 {
1406 EFI_STATUS Status;
1407
1408 gHiiVendorDevicePath = (HII_VENDOR_DEVICE_PATH*) DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL*)&FeHiiVendorDevicePath);
1409 ASSERT (gHiiVendorDevicePath != NULL);
1410 CopyGuid (&gHiiVendorDevicePath->VendorDevicePath.Guid, &gEfiCallerIdGuid);
1411
1412 //
1413 // Install Device Path Protocol and Config Access protocol to driver handle
1414 //
1415 Status = gBS->InstallMultipleProtocolInterfaces (
1416 &gFileExplorerPrivate.FeDriverHandle,
1417 &gEfiDevicePathProtocolGuid,
1418 gHiiVendorDevicePath,
1419 &gEfiHiiConfigAccessProtocolGuid,
1420 &gFileExplorerPrivate.FeConfigAccess,
1421 NULL
1422 );
1423 if (Status == EFI_ALREADY_STARTED) {
1424 return EFI_SUCCESS;
1425 }
1426 if (EFI_ERROR (Status)) {
1427 return Status;
1428 }
1429
1430 //
1431 // Post our File Explorer VFR binary to the HII database.
1432 //
1433 gFileExplorerPrivate.FeHiiHandle = HiiAddPackages (
1434 &FileExplorerGuid,
1435 gFileExplorerPrivate.FeDriverHandle,
1436 FileExplorerVfrBin,
1437 FileExplorerLibStrings,
1438 NULL
1439 );
1440 ASSERT (gFileExplorerPrivate.FeHiiHandle != NULL);
1441
1442 //
1443 // Locate Formbrowser2 protocol
1444 //
1445 Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &gFileExplorerPrivate.FormBrowser2);
1446 ASSERT_EFI_ERROR (Status);
1447
1448 InitializeListHead (&gFileExplorerPrivate.FsOptionMenu->Head);
1449
1450 return EFI_SUCCESS;
1451 }
1452
1453 /**
1454 Unloads the application and its installed protocol.
1455
1456 @param[in] ImageHandle Handle that identifies the image to be unloaded.
1457 @param[in] SystemTable The system table.
1458
1459 @retval EFI_SUCCESS The image has been unloaded.
1460 **/
1461 EFI_STATUS
1462 EFIAPI
1463 FileExplorerLibDestructor (
1464 IN EFI_HANDLE ImageHandle,
1465 IN EFI_SYSTEM_TABLE *SystemTable
1466 )
1467 {
1468 EFI_STATUS Status;
1469
1470 ASSERT (gHiiVendorDevicePath != NULL);
1471
1472 if (gFileExplorerPrivate.FeDriverHandle != NULL) {
1473 Status = gBS->UninstallMultipleProtocolInterfaces (
1474 gFileExplorerPrivate.FeDriverHandle,
1475 &gEfiDevicePathProtocolGuid,
1476 gHiiVendorDevicePath,
1477 &gEfiHiiConfigAccessProtocolGuid,
1478 &gFileExplorerPrivate.FeConfigAccess,
1479 NULL
1480 );
1481 ASSERT_EFI_ERROR (Status);
1482
1483 HiiRemovePackages (gFileExplorerPrivate.FeHiiHandle);
1484 }
1485
1486 FreePool (gHiiVendorDevicePath);
1487
1488 return EFI_SUCCESS;
1489 }
1490