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