Home | History | Annotate | Download | only in Shell
      1 /** @file
      2   Member functions of EFI_SHELL_PROTOCOL and functions for creation,
      3   manipulation, and initialization of EFI_SHELL_PROTOCOL.
      4 
      5   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
      6   Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
      7   This program and the accompanying materials
      8   are licensed and made available under the terms and conditions of the BSD License
      9   which accompanies this distribution.  The full text of the license may be found at
     10   http://opensource.org/licenses/bsd-license.php
     11 
     12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 **/
     16 
     17 #include "Shell.h"
     18 
     19 #define INIT_NAME_BUFFER_SIZE  128
     20 
     21 /**
     22   Close an open file handle.
     23 
     24   This function closes a specified file handle. All "dirty" cached file data is
     25   flushed to the device, and the file is closed. In all cases the handle is
     26   closed.
     27 
     28   @param[in] FileHandle           The file handle to close.
     29 
     30   @retval EFI_SUCCESS             The file handle was closed successfully.
     31 **/
     32 EFI_STATUS
     33 EFIAPI
     34 EfiShellClose (
     35   IN SHELL_FILE_HANDLE            FileHandle
     36   )
     37 {
     38   ShellFileHandleRemove(FileHandle);
     39   return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));
     40 }
     41 
     42 /**
     43   Internal worker to determine whether there is a BlockIo somewhere
     44   upon the device path specified.
     45 
     46   @param[in] DevicePath    The device path to test.
     47 
     48   @retval TRUE      gEfiBlockIoProtocolGuid was installed on a handle with this device path
     49   @retval FALSE     gEfiBlockIoProtocolGuid was not found.
     50 **/
     51 BOOLEAN
     52 EFIAPI
     53 InternalShellProtocolIsBlockIoPresent(
     54   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
     55   )
     56 {
     57   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
     58   EFI_STATUS                Status;
     59   EFI_HANDLE                Handle;
     60 
     61   Handle = NULL;
     62 
     63   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
     64   Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
     65 
     66   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
     67     return (TRUE);
     68   }
     69   return (FALSE);
     70 }
     71 
     72 /**
     73   Internal worker to determine whether there is a file system somewhere
     74   upon the device path specified.
     75 
     76   @param[in] DevicePath    The device path to test.
     77 
     78   @retval TRUE      gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
     79   @retval FALSE     gEfiSimpleFileSystemProtocolGuid was not found.
     80 **/
     81 BOOLEAN
     82 EFIAPI
     83 InternalShellProtocolIsSimpleFileSystemPresent(
     84   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
     85   )
     86 {
     87   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
     88   EFI_STATUS                Status;
     89   EFI_HANDLE                Handle;
     90 
     91   Handle = NULL;
     92 
     93   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
     94   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
     95 
     96   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
     97     return (TRUE);
     98   }
     99   return (FALSE);
    100 }
    101 
    102 /**
    103   Internal worker debug helper function to print out maps as they are added.
    104 
    105   @param[in] Mapping        string mapping that has been added
    106   @param[in] DevicePath     pointer to device path that has been mapped.
    107 
    108   @retval EFI_SUCCESS   the operation was successful.
    109   @return other         an error ocurred
    110 
    111   @sa LocateHandle
    112   @sa OpenProtocol
    113 **/
    114 EFI_STATUS
    115 EFIAPI
    116 InternalShellProtocolDebugPrintMessage (
    117   IN CONST CHAR16                   *Mapping,
    118   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
    119   )
    120 {
    121   EFI_STATUS                        Status;
    122   CHAR16                            *Temp;
    123 
    124   Status = EFI_SUCCESS;
    125   DEBUG_CODE_BEGIN();
    126 
    127   if (Mapping != NULL) {
    128     DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));
    129   }
    130   Temp = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
    131   DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));
    132   FreePool(Temp);
    133 
    134   DEBUG_CODE_END();
    135   return (Status);
    136 }
    137 
    138 /**
    139   This function creates a mapping for a device path.
    140 
    141   If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
    142 
    143   @param DevicePath             Points to the device path. If this is NULL and Mapping points to a valid mapping,
    144                                 then the mapping will be deleted.
    145   @param Mapping                Points to the NULL-terminated mapping for the device path.  Must end with a ':'
    146 
    147   @retval EFI_SUCCESS           Mapping created or deleted successfully.
    148   @retval EFI_NO_MAPPING        There is no handle that corresponds exactly to DevicePath. See the
    149                                 boot service function LocateDevicePath().
    150   @retval EFI_ACCESS_DENIED     The mapping is a built-in alias.
    151   @retval EFI_INVALID_PARAMETER Mapping was NULL
    152   @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
    153   @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
    154   @retval EFI_NOT_FOUND         There was no mapping found to delete
    155   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed
    156 **/
    157 EFI_STATUS
    158 EFIAPI
    159 EfiShellSetMap(
    160   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
    161   IN CONST CHAR16 *Mapping
    162   )
    163 {
    164   EFI_STATUS      Status;
    165   SHELL_MAP_LIST  *MapListNode;
    166 
    167   if (Mapping == NULL){
    168     return (EFI_INVALID_PARAMETER);
    169   }
    170 
    171   if (Mapping[StrLen(Mapping)-1] != ':') {
    172     return (EFI_INVALID_PARAMETER);
    173   }
    174 
    175   //
    176   // Delete the mapping
    177   //
    178   if (DevicePath == NULL) {
    179     if (IsListEmpty(&gShellMapList.Link)) {
    180       return (EFI_NOT_FOUND);
    181     }
    182     for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    183         ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
    184         ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
    185        ){
    186           if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
    187             RemoveEntryList(&MapListNode->Link);
    188             SHELL_FREE_NON_NULL(MapListNode->DevicePath);
    189             SHELL_FREE_NON_NULL(MapListNode->MapName);
    190             SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
    191             FreePool(MapListNode);
    192             return (EFI_SUCCESS);
    193           }
    194     } // for loop
    195 
    196     //
    197     // We didnt find one to delete
    198     //
    199     return (EFI_NOT_FOUND);
    200   }
    201 
    202   //
    203   // make sure this is a valid to add device path
    204   //
    205   ///@todo add BlockIo to this test...
    206   if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)
    207     && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {
    208     return (EFI_INVALID_PARAMETER);
    209   }
    210 
    211   //
    212   // First make sure there is no old mapping
    213   //
    214   Status = EfiShellSetMap(NULL, Mapping);
    215   if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
    216     return (Status);
    217   }
    218 
    219   //
    220   // now add the new one.
    221   //
    222   Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
    223 
    224   return(Status);
    225 }
    226 
    227 /**
    228   Gets the device path from the mapping.
    229 
    230   This function gets the device path associated with a mapping.
    231 
    232   @param Mapping                A pointer to the mapping
    233 
    234   @retval !=NULL                Pointer to the device path that corresponds to the
    235                                 device mapping. The returned pointer does not need
    236                                 to be freed.
    237   @retval NULL                  There is no device path associated with the
    238                                 specified mapping.
    239 **/
    240 CONST EFI_DEVICE_PATH_PROTOCOL *
    241 EFIAPI
    242 EfiShellGetDevicePathFromMap(
    243   IN CONST CHAR16 *Mapping
    244   )
    245 {
    246   SHELL_MAP_LIST  *MapListItem;
    247   CHAR16          *NewName;
    248   UINTN           Size;
    249 
    250   NewName = NULL;
    251   Size    = 0;
    252 
    253   StrnCatGrow(&NewName, &Size, Mapping, 0);
    254   if (Mapping[StrLen(Mapping)-1] != L':') {
    255     StrnCatGrow(&NewName, &Size, L":", 0);
    256   }
    257 
    258   MapListItem = ShellCommandFindMapItem(NewName);
    259 
    260   FreePool(NewName);
    261 
    262   if (MapListItem != NULL) {
    263     return (MapListItem->DevicePath);
    264   }
    265   return(NULL);
    266 }
    267 
    268 /**
    269   Gets the mapping(s) that most closely matches the device path.
    270 
    271   This function gets the mapping which corresponds to the device path *DevicePath. If
    272   there is no exact match, then the mapping which most closely matches *DevicePath
    273   is returned, and *DevicePath is updated to point to the remaining portion of the
    274   device path. If there is an exact match, the mapping is returned and *DevicePath
    275   points to the end-of-device-path node.
    276 
    277   If there are multiple map names they will be semi-colon seperated in the
    278   NULL-terminated string.
    279 
    280   @param DevicePath             On entry, points to a device path pointer. On
    281                                 exit, updates the pointer to point to the
    282                                 portion of the device path after the mapping.
    283 
    284   @retval NULL                  No mapping was found.
    285   @return !=NULL                Pointer to NULL-terminated mapping. The buffer
    286                                 is callee allocated and should be freed by the caller.
    287 **/
    288 CONST CHAR16 *
    289 EFIAPI
    290 EfiShellGetMapFromDevicePath(
    291   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
    292   )
    293 {
    294   SHELL_MAP_LIST              *Node;
    295   CHAR16                      *PathForReturn;
    296   UINTN                       PathSize;
    297 //  EFI_HANDLE                  PathHandle;
    298 //  EFI_HANDLE                  MapHandle;
    299 //  EFI_STATUS                  Status;
    300 //  EFI_DEVICE_PATH_PROTOCOL    *DevicePathCopy;
    301 //  EFI_DEVICE_PATH_PROTOCOL    *MapPathCopy;
    302 
    303   if (DevicePath == NULL || *DevicePath == NULL) {
    304     return (NULL);
    305   }
    306 
    307   PathForReturn = NULL;
    308   PathSize      = 0;
    309 
    310   for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    311       ; !IsNull(&gShellMapList.Link, &Node->Link)
    312       ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    313      ){
    314     //
    315     // check for exact match
    316     //
    317     if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
    318       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    319       if (PathSize != 0) {
    320         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    321       }
    322       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    323     }
    324   }
    325   if (PathForReturn != NULL) {
    326     while (!IsDevicePathEndType (*DevicePath)) {
    327       *DevicePath = NextDevicePathNode (*DevicePath);
    328     }
    329     SetDevicePathEndNode (*DevicePath);
    330   }
    331 /*
    332   ///@todo finish code for inexact matches.
    333   if (PathForReturn == NULL) {
    334     PathSize = 0;
    335 
    336     DevicePathCopy = DuplicateDevicePath(*DevicePath);
    337     ASSERT(DevicePathCopy != NULL);
    338     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
    339     ASSERT_EFI_ERROR(Status);
    340     //
    341     //  check each of the device paths we have to get the root of the path for consist mappings
    342     //
    343     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    344         ; !IsNull(&gShellMapList.Link, &Node->Link)
    345         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    346        ){
    347       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
    348         continue;
    349       }
    350       MapPathCopy = DuplicateDevicePath(Node->DevicePath);
    351       ASSERT(MapPathCopy != NULL);
    352       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    353       if (MapHandle == PathHandle) {
    354 
    355         *DevicePath = DevicePathCopy;
    356 
    357         MapPathCopy = NULL;
    358         DevicePathCopy = NULL;
    359         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    360         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    361         break;
    362       }
    363     }
    364     //
    365     // now add on the non-consistent mappings
    366     //
    367     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    368         ; !IsNull(&gShellMapList.Link, &Node->Link)
    369         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
    370        ){
    371       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
    372         continue;
    373       }
    374       MapPathCopy = Node->DevicePath;
    375       ASSERT(MapPathCopy != NULL);
    376       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    377       if (MapHandle == PathHandle) {
    378         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
    379         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
    380         break;
    381       }
    382     }
    383   }
    384 */
    385 
    386   return (AddBufferToFreeList(PathForReturn));
    387 }
    388 
    389 /**
    390   Converts a device path to a file system-style path.
    391 
    392   This function converts a device path to a file system path by replacing part, or all, of
    393   the device path with the file-system mapping. If there are more than one application
    394   file system mappings, the one that most closely matches Path will be used.
    395 
    396   @param Path                   The pointer to the device path
    397 
    398   @retval NULL                  the device path could not be found.
    399   @return all                   The pointer of the NULL-terminated file path. The path
    400                                 is callee-allocated and should be freed by the caller.
    401 **/
    402 CHAR16 *
    403 EFIAPI
    404 EfiShellGetFilePathFromDevicePath(
    405   IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
    406   )
    407 {
    408   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
    409   EFI_DEVICE_PATH_PROTOCOL        *MapPathCopy;
    410   SHELL_MAP_LIST                  *MapListItem;
    411   CHAR16                          *PathForReturn;
    412   UINTN                           PathSize;
    413   EFI_HANDLE                      PathHandle;
    414   EFI_HANDLE                      MapHandle;
    415   EFI_STATUS                      Status;
    416   FILEPATH_DEVICE_PATH            *FilePath;
    417   FILEPATH_DEVICE_PATH            *AlignedNode;
    418 
    419   PathForReturn = NULL;
    420   PathSize = 0;
    421 
    422   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
    423   ASSERT(DevicePathCopy != NULL);
    424   if (DevicePathCopy == NULL) {
    425     return (NULL);
    426   }
    427   ///@todo BlockIo?
    428   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
    429 
    430   if (EFI_ERROR(Status)) {
    431     return (NULL);
    432   }
    433   //
    434   //  check each of the device paths we have to get the root of the path
    435   //
    436   for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
    437       ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
    438       ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
    439      ){
    440     MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
    441     ASSERT(MapPathCopy != NULL);
    442     ///@todo BlockIo?
    443     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
    444     if (MapHandle == PathHandle) {
    445       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    446       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
    447       //
    448       // go through all the remaining nodes in the device path
    449       //
    450       for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
    451           ; !IsDevicePathEnd (&FilePath->Header)
    452           ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
    453          ){
    454         //
    455         // If any node is not a file path node, then the conversion can not be completed
    456         //
    457         if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
    458             (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
    459           FreePool(PathForReturn);
    460           return NULL;
    461         }
    462 
    463         //
    464         // append the path part onto the filepath.
    465         //
    466         ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
    467 
    468         AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);
    469         ASSERT (AlignedNode != NULL);
    470 
    471         // File Path Device Path Nodes 'can optionally add a "\" separator to
    472         //  the beginning and/or the end of the Path Name string.'
    473         // (UEFI Spec 2.4 section 9.3.6.4).
    474         // If necessary, add a "\", but otherwise don't
    475         // (This is specified in the above section, and also implied by the
    476         //  UEFI Shell spec section 3.7)
    477         if ((PathSize != 0)                        &&
    478             (PathForReturn != NULL)                &&
    479             (PathForReturn[PathSize - 1] != L'\\') &&
    480             (AlignedNode->PathName[0]    != L'\\')) {
    481           PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
    482         }
    483 
    484         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);
    485         FreePool(AlignedNode);
    486       } // for loop of remaining nodes
    487     }
    488     if (PathForReturn != NULL) {
    489       break;
    490     }
    491   } // for loop of paths to check
    492   return(PathForReturn);
    493 }
    494 
    495 /**
    496   Converts a file system style name to a device path.
    497 
    498   This function converts a file system style name to a device path, by replacing any
    499   mapping references to the associated device path.
    500 
    501   @param[in] Path               The pointer to the path.
    502 
    503   @return                       The pointer of the file path. The file path is callee
    504                                 allocated and should be freed by the caller.
    505   @retval NULL                  The path could not be found.
    506   @retval NULL                  There was not enough available memory.
    507 **/
    508 EFI_DEVICE_PATH_PROTOCOL *
    509 EFIAPI
    510 EfiShellGetDevicePathFromFilePath(
    511   IN CONST CHAR16 *Path
    512   )
    513 {
    514   CHAR16                          *MapName;
    515   CHAR16                          *NewPath;
    516   CONST CHAR16                    *Cwd;
    517   UINTN                           Size;
    518   CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    519   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopy;
    520   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopyForFree;
    521   EFI_DEVICE_PATH_PROTOCOL        *DevicePathForReturn;
    522   EFI_HANDLE                      Handle;
    523   EFI_STATUS                      Status;
    524 
    525   if (Path == NULL) {
    526     return (NULL);
    527   }
    528 
    529   MapName = NULL;
    530   NewPath = NULL;
    531 
    532   if (StrStr(Path, L":") == NULL) {
    533     Cwd = EfiShellGetCurDir(NULL);
    534     if (Cwd == NULL) {
    535       return (NULL);
    536     }
    537     Size = StrSize(Cwd) + StrSize(Path);
    538     NewPath = AllocateZeroPool(Size);
    539     if (NewPath == NULL) {
    540       return (NULL);
    541     }
    542     StrCpyS(NewPath, Size/sizeof(CHAR16), Cwd);
    543     StrCatS(NewPath, Size/sizeof(CHAR16), L"\\");
    544     if (*Path == L'\\') {
    545       Path++;
    546       while (PathRemoveLastItem(NewPath)) ;
    547     }
    548     StrCatS(NewPath, Size/sizeof(CHAR16), Path);
    549     DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
    550     FreePool(NewPath);
    551     return (DevicePathForReturn);
    552   }
    553 
    554   Size = 0;
    555   //
    556   // find the part before (but including) the : for the map name
    557   //
    558   ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
    559   MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
    560   if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {
    561     return (NULL);
    562   }
    563 
    564   //
    565   // look up the device path in the map
    566   //
    567   DevicePath = EfiShellGetDevicePathFromMap(MapName);
    568   if (DevicePath == NULL) {
    569     //
    570     // Must have been a bad Mapname
    571     //
    572     return (NULL);
    573   }
    574 
    575   //
    576   // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
    577   //
    578   DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);
    579   if (DevicePathCopy == NULL) {
    580     FreePool(MapName);
    581     return (NULL);
    582   }
    583 
    584   //
    585   // get the handle
    586   //
    587   ///@todo BlockIo?
    588   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
    589   if (EFI_ERROR(Status)) {
    590     if (DevicePathCopyForFree != NULL) {
    591       FreePool(DevicePathCopyForFree);
    592     }
    593     FreePool(MapName);
    594     return (NULL);
    595   }
    596 
    597   //
    598   // build the full device path
    599   //
    600   if (*(Path+StrLen(MapName)+1) == CHAR_NULL) {
    601     DevicePathForReturn = FileDevicePath(Handle, L"\\");
    602   } else {
    603     DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));
    604   }
    605 
    606   FreePool(MapName);
    607   if (DevicePathCopyForFree != NULL) {
    608     FreePool(DevicePathCopyForFree);
    609   }
    610 
    611   return (DevicePathForReturn);
    612 }
    613 
    614 /**
    615   Gets the name of the device specified by the device handle.
    616 
    617   This function gets the user-readable name of the device specified by the device
    618   handle. If no user-readable name could be generated, then *BestDeviceName will be
    619   NULL and EFI_NOT_FOUND will be returned.
    620 
    621   If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
    622   device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
    623   DeviceHandle.
    624 
    625   If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
    626   device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
    627   If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
    628   EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
    629   EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
    630 
    631   @param DeviceHandle           The handle of the device.
    632   @param Flags                  Determines the possible sources of component names.
    633                                 Valid bits are:
    634                                   EFI_DEVICE_NAME_USE_COMPONENT_NAME
    635                                   EFI_DEVICE_NAME_USE_DEVICE_PATH
    636   @param Language               A pointer to the language specified for the device
    637                                 name, in the same format as described in the UEFI
    638                                 specification, Appendix M
    639   @param BestDeviceName         On return, points to the callee-allocated NULL-
    640                                 terminated name of the device. If no device name
    641                                 could be found, points to NULL. The name must be
    642                                 freed by the caller...
    643 
    644   @retval EFI_SUCCESS           Get the name successfully.
    645   @retval EFI_NOT_FOUND         Fail to get the device name.
    646   @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
    647   @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
    648   @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
    649 **/
    650 EFI_STATUS
    651 EFIAPI
    652 EfiShellGetDeviceName(
    653   IN EFI_HANDLE DeviceHandle,
    654   IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
    655   IN CHAR8 *Language,
    656   OUT CHAR16 **BestDeviceName
    657   )
    658 {
    659   EFI_STATUS                        Status;
    660   EFI_COMPONENT_NAME2_PROTOCOL      *CompName2;
    661   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
    662   EFI_HANDLE                        *HandleList;
    663   UINTN                             HandleCount;
    664   UINTN                             LoopVar;
    665   CHAR16                            *DeviceNameToReturn;
    666   CHAR8                             *Lang;
    667   UINTN                             ParentControllerCount;
    668   EFI_HANDLE                        *ParentControllerBuffer;
    669   UINTN                             ParentDriverCount;
    670   EFI_HANDLE                        *ParentDriverBuffer;
    671 
    672   if (BestDeviceName == NULL ||
    673       DeviceHandle   == NULL
    674      ){
    675     return (EFI_INVALID_PARAMETER);
    676   }
    677 
    678   //
    679   // make sure one of the 2 supported bits is on
    680   //
    681   if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
    682       ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {
    683     return (EFI_INVALID_PARAMETER);
    684   }
    685 
    686   DeviceNameToReturn  = NULL;
    687   *BestDeviceName     = NULL;
    688   HandleList          = NULL;
    689   HandleCount         = 0;
    690   Lang                = NULL;
    691 
    692   if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
    693     Status = ParseHandleDatabaseByRelationship(
    694       NULL,
    695       DeviceHandle,
    696       HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
    697       &HandleCount,
    698       &HandleList);
    699     for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){
    700       //
    701       // Go through those handles until we get one that passes for GetComponentName
    702       //
    703       Status = gBS->OpenProtocol(
    704         HandleList[LoopVar],
    705         &gEfiComponentName2ProtocolGuid,
    706         (VOID**)&CompName2,
    707         gImageHandle,
    708         NULL,
    709         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    710       if (EFI_ERROR(Status)) {
    711         Status = gBS->OpenProtocol(
    712           HandleList[LoopVar],
    713           &gEfiComponentNameProtocolGuid,
    714           (VOID**)&CompName2,
    715           gImageHandle,
    716           NULL,
    717           EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    718       }
    719 
    720       if (EFI_ERROR(Status)) {
    721         continue;
    722       }
    723       Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
    724       Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
    725       FreePool(Lang);
    726       Lang = NULL;
    727       if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    728         break;
    729       }
    730     }
    731     if (HandleList != NULL) {
    732       FreePool(HandleList);
    733     }
    734 
    735     //
    736     // Now check the parent controller using this as the child.
    737     //
    738     if (DeviceNameToReturn == NULL){
    739       PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
    740       for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {
    741         PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
    742         for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {
    743           //
    744           // try using that driver's component name with controller and our driver as the child.
    745           //
    746           Status = gBS->OpenProtocol(
    747             ParentDriverBuffer[HandleCount],
    748             &gEfiComponentName2ProtocolGuid,
    749             (VOID**)&CompName2,
    750             gImageHandle,
    751             NULL,
    752             EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    753           if (EFI_ERROR(Status)) {
    754             Status = gBS->OpenProtocol(
    755               ParentDriverBuffer[HandleCount],
    756               &gEfiComponentNameProtocolGuid,
    757               (VOID**)&CompName2,
    758               gImageHandle,
    759               NULL,
    760               EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    761           }
    762 
    763           if (EFI_ERROR(Status)) {
    764             continue;
    765           }
    766           Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
    767           Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
    768           FreePool(Lang);
    769           Lang = NULL;
    770           if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    771             break;
    772           }
    773 
    774 
    775 
    776         }
    777         SHELL_FREE_NON_NULL(ParentDriverBuffer);
    778         if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
    779           break;
    780         }
    781       }
    782       SHELL_FREE_NON_NULL(ParentControllerBuffer);
    783     }
    784     //
    785     // dont return on fail since we will try device path if that bit is on
    786     //
    787     if (DeviceNameToReturn != NULL){
    788       ASSERT(BestDeviceName != NULL);
    789       StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
    790       return (EFI_SUCCESS);
    791     }
    792   }
    793   if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
    794     Status = gBS->OpenProtocol(
    795       DeviceHandle,
    796       &gEfiDevicePathProtocolGuid,
    797       (VOID**)&DevicePath,
    798       gImageHandle,
    799       NULL,
    800       EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    801     if (!EFI_ERROR(Status)) {
    802       //
    803       // use device path to text on the device path
    804       //
    805       *BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
    806       return (EFI_SUCCESS);
    807     }
    808   }
    809   //
    810   // none of the selected bits worked.
    811   //
    812   return (EFI_NOT_FOUND);
    813 }
    814 
    815 /**
    816   Opens the root directory of a device on a handle
    817 
    818   This function opens the root directory of a device and returns a file handle to it.
    819 
    820   @param DeviceHandle           The handle of the device that contains the volume.
    821   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
    822                                 device.
    823 
    824   @retval EFI_SUCCESS           Root opened successfully.
    825   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
    826                                 could not be opened.
    827   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
    828   @retval EFI_DEVICE_ERROR      The device had an error
    829 **/
    830 EFI_STATUS
    831 EFIAPI
    832 EfiShellOpenRootByHandle(
    833   IN EFI_HANDLE DeviceHandle,
    834   OUT SHELL_FILE_HANDLE *FileHandle
    835   )
    836 {
    837   EFI_STATUS                      Status;
    838   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
    839   EFI_FILE_PROTOCOL               *RealFileHandle;
    840   EFI_DEVICE_PATH_PROTOCOL        *DevPath;
    841 
    842   //
    843   // get the simple file system interface
    844   //
    845   Status = gBS->OpenProtocol(DeviceHandle,
    846                              &gEfiSimpleFileSystemProtocolGuid,
    847                              (VOID**)&SimpleFileSystem,
    848                              gImageHandle,
    849                              NULL,
    850                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    851   if (EFI_ERROR(Status)) {
    852     return (EFI_NOT_FOUND);
    853   }
    854 
    855   Status = gBS->OpenProtocol(DeviceHandle,
    856                              &gEfiDevicePathProtocolGuid,
    857                              (VOID**)&DevPath,
    858                              gImageHandle,
    859                              NULL,
    860                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
    861   if (EFI_ERROR(Status)) {
    862     return (EFI_NOT_FOUND);
    863   }
    864   //
    865   // Open the root volume now...
    866   //
    867   Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);
    868   *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));
    869   return (Status);
    870 }
    871 
    872 /**
    873   Opens the root directory of a device.
    874 
    875   This function opens the root directory of a device and returns a file handle to it.
    876 
    877   @param DevicePath             Points to the device path corresponding to the device where the
    878                                 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
    879   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
    880                                 device.
    881 
    882   @retval EFI_SUCCESS           Root opened successfully.
    883   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
    884                                 could not be opened.
    885   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
    886   @retval EFI_DEVICE_ERROR      The device had an error
    887   @retval EFI_INVALID_PARAMETER FileHandle is NULL.
    888 **/
    889 EFI_STATUS
    890 EFIAPI
    891 EfiShellOpenRoot(
    892   IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
    893   OUT SHELL_FILE_HANDLE *FileHandle
    894   )
    895 {
    896   EFI_STATUS Status;
    897   EFI_HANDLE Handle;
    898 
    899   if (FileHandle == NULL) {
    900     return (EFI_INVALID_PARAMETER);
    901   }
    902 
    903   //
    904   // find the handle of the device with that device handle and the file system
    905   //
    906   ///@todo BlockIo?
    907   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
    908                                  &DevicePath,
    909                                  &Handle);
    910   if (EFI_ERROR(Status)) {
    911     return (EFI_NOT_FOUND);
    912   }
    913 
    914   return (EfiShellOpenRootByHandle(Handle, FileHandle));
    915 }
    916 
    917 /**
    918   Returns whether any script files are currently being processed.
    919 
    920   @retval TRUE                 There is at least one script file active.
    921   @retval FALSE                No script files are active now.
    922 
    923 **/
    924 BOOLEAN
    925 EFIAPI
    926 EfiShellBatchIsActive (
    927   VOID
    928   )
    929 {
    930   if (ShellCommandGetCurrentScriptFile() == NULL) {
    931     return (FALSE);
    932   }
    933   return (TRUE);
    934 }
    935 
    936 /**
    937   Worker function to open a file based on a device path.  this will open the root
    938   of the volume and then traverse down to the file itself.
    939 
    940   @param DevicePath               Device Path of the file.
    941   @param FileHandle               Pointer to the file upon a successful return.
    942   @param OpenMode                 mode to open file in.
    943   @param Attributes               the File Attributes to use when creating a new file.
    944 
    945   @retval EFI_SUCCESS             the file is open and FileHandle is valid
    946   @retval EFI_UNSUPPORTED         the device path cotained non-path elements
    947   @retval other                   an error ocurred.
    948 **/
    949 EFI_STATUS
    950 EFIAPI
    951 InternalOpenFileDevicePath(
    952   IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
    953   OUT SHELL_FILE_HANDLE           *FileHandle,
    954   IN UINT64                       OpenMode,
    955   IN UINT64                       Attributes OPTIONAL
    956   )
    957 {
    958   EFI_STATUS                      Status;
    959   FILEPATH_DEVICE_PATH            *FilePathNode;
    960   EFI_HANDLE                      Handle;
    961   SHELL_FILE_HANDLE               ShellHandle;
    962   EFI_FILE_PROTOCOL               *Handle1;
    963   EFI_FILE_PROTOCOL               *Handle2;
    964   FILEPATH_DEVICE_PATH            *AlignedNode;
    965 
    966   if (FileHandle == NULL) {
    967     return (EFI_INVALID_PARAMETER);
    968   }
    969   *FileHandle   = NULL;
    970   Handle1       = NULL;
    971   Handle2       = NULL;
    972   Handle        = NULL;
    973   ShellHandle   = NULL;
    974   FilePathNode  = NULL;
    975   AlignedNode   = NULL;
    976 
    977   Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
    978 
    979   if (!EFI_ERROR(Status)) {
    980     Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
    981     if (Handle1 != NULL) {
    982       //
    983       // chop off the begining part before the file system part...
    984       //
    985       ///@todo BlockIo?
    986       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
    987                                      &DevicePath,
    988                                      &Handle);
    989         if (!EFI_ERROR(Status)) {
    990         //
    991         // To access as a file system, the file path should only
    992         // contain file path components.  Follow the file path nodes
    993         // and find the target file
    994         //
    995         for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
    996             ; !IsDevicePathEnd (&FilePathNode->Header)
    997             ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)
    998            ){
    999           SHELL_FREE_NON_NULL(AlignedNode);
   1000           AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);
   1001           //
   1002           // For file system access each node should be a file path component
   1003           //
   1004           if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
   1005               DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP
   1006              ) {
   1007             Status = EFI_UNSUPPORTED;
   1008             break;
   1009           }
   1010 
   1011           //
   1012           // Open this file path node
   1013           //
   1014           Handle2 = Handle1;
   1015           Handle1 = NULL;
   1016 
   1017           //
   1018           // if this is the last node in the DevicePath always create (if that was requested).
   1019           //
   1020           if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
   1021             Status = Handle2->Open (
   1022                                   Handle2,
   1023                                   &Handle1,
   1024                                   AlignedNode->PathName,
   1025                                   OpenMode,
   1026                                   Attributes
   1027                                  );
   1028           } else {
   1029 
   1030             //
   1031             //  This is not the last node and we dont want to 'create' existing
   1032             //  directory entries...
   1033             //
   1034 
   1035             //
   1036             // open without letting it create
   1037             // prevents error on existing files/directories
   1038             //
   1039             Status = Handle2->Open (
   1040                                   Handle2,
   1041                                   &Handle1,
   1042                                   AlignedNode->PathName,
   1043                                   OpenMode &~EFI_FILE_MODE_CREATE,
   1044                                   Attributes
   1045                                  );
   1046             //
   1047             // if above failed now open and create the 'item'
   1048             // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
   1049             //
   1050             if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
   1051               Status = Handle2->Open (
   1052                                     Handle2,
   1053                                     &Handle1,
   1054                                     AlignedNode->PathName,
   1055                                     OpenMode,
   1056                                     Attributes
   1057                                    );
   1058             }
   1059           }
   1060           //
   1061           // Close the last node
   1062           //
   1063           ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
   1064 
   1065           //
   1066           // If there's been an error, stop
   1067           //
   1068           if (EFI_ERROR (Status)) {
   1069             break;
   1070           }
   1071         } // for loop
   1072       }
   1073     }
   1074   }
   1075   SHELL_FREE_NON_NULL(AlignedNode);
   1076   if (EFI_ERROR(Status)) {
   1077     if (Handle1 != NULL) {
   1078       ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);
   1079     }
   1080   } else {
   1081     *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
   1082   }
   1083   return (Status);
   1084 }
   1085 
   1086 /**
   1087   Creates a file or directory by name.
   1088 
   1089   This function creates an empty new file or directory with the specified attributes and
   1090   returns the new file's handle. If the file already exists and is read-only, then
   1091   EFI_INVALID_PARAMETER will be returned.
   1092 
   1093   If the file already existed, it is truncated and its attributes updated. If the file is
   1094   created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
   1095 
   1096   If the file name begins with >v, then the file handle which is returned refers to the
   1097   shell environment variable with the specified name. If the shell environment variable
   1098   already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
   1099 
   1100   @param FileName           Pointer to NULL-terminated file path
   1101   @param FileAttribs        The new file's attrbiutes.  the different attributes are
   1102                             described in EFI_FILE_PROTOCOL.Open().
   1103   @param FileHandle         On return, points to the created file handle or directory's handle
   1104 
   1105   @retval EFI_SUCCESS       The file was opened.  FileHandle points to the new file's handle.
   1106   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
   1107   @retval EFI_UNSUPPORTED   could not open the file path
   1108   @retval EFI_NOT_FOUND     the specified file could not be found on the devide, or could not
   1109                             file the file system on the device.
   1110   @retval EFI_NO_MEDIA      the device has no medium.
   1111   @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
   1112                             longer supported.
   1113   @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
   1114                             the DirName.
   1115   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
   1116   @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
   1117                             when the media is write-protected.
   1118   @retval EFI_ACCESS_DENIED The service denied access to the file.
   1119   @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
   1120   @retval EFI_VOLUME_FULL   The volume is full.
   1121 **/
   1122 EFI_STATUS
   1123 EFIAPI
   1124 EfiShellCreateFile(
   1125   IN CONST CHAR16       *FileName,
   1126   IN UINT64             FileAttribs,
   1127   OUT SHELL_FILE_HANDLE *FileHandle
   1128   )
   1129 {
   1130   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
   1131   EFI_STATUS                Status;
   1132 
   1133   //
   1134   // Is this for an environment variable
   1135   // do we start with >v
   1136   //
   1137   if (StrStr(FileName, L">v") == FileName) {
   1138     if (!IsVolatileEnv(FileName+2)) {
   1139       return (EFI_INVALID_PARAMETER);
   1140     }
   1141     *FileHandle = CreateFileInterfaceEnv(FileName+2);
   1142     return (EFI_SUCCESS);
   1143   }
   1144 
   1145   //
   1146   // We are opening a regular file.
   1147   //
   1148   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
   1149   if (DevicePath == NULL) {
   1150     return (EFI_NOT_FOUND);
   1151   }
   1152 
   1153   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs);
   1154   FreePool(DevicePath);
   1155 
   1156   return(Status);
   1157 }
   1158 
   1159 /**
   1160   Register a GUID and a localized human readable name for it.
   1161 
   1162   If Guid is not assigned a name, then assign GuidName to Guid.  This list of GUID
   1163   names must be used whenever a shell command outputs GUID information.
   1164 
   1165   This function is only available when the major and minor versions in the
   1166   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   1167 
   1168   @param[in] Guid       A pointer to the GUID being registered.
   1169   @param[in] GuidName   A pointer to the localized name for the GUID being registered.
   1170 
   1171   @retval EFI_SUCCESS             The operation was successful.
   1172   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   1173   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   1174   @retval EFI_ACCESS_DENIED       Guid already is assigned a name.
   1175 **/
   1176 EFI_STATUS
   1177 EFIAPI
   1178 EfiShellRegisterGuidName(
   1179   IN CONST EFI_GUID *Guid,
   1180   IN CONST CHAR16   *GuidName
   1181   )
   1182 {
   1183   return (AddNewGuidNameMapping(Guid, GuidName, NULL));
   1184 }
   1185 
   1186 /**
   1187   Opens a file or a directory by file name.
   1188 
   1189   This function opens the specified file in the specified OpenMode and returns a file
   1190   handle.
   1191   If the file name begins with >v, then the file handle which is returned refers to the
   1192   shell environment variable with the specified name. If the shell environment variable
   1193   exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
   1194   EFI_INVALID_PARAMETER is returned.
   1195 
   1196   If the file name is >i, then the file handle which is returned refers to the standard
   1197   input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
   1198   is returned.
   1199 
   1200   If the file name is >o, then the file handle which is returned refers to the standard
   1201   output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
   1202   is returned.
   1203 
   1204   If the file name is >e, then the file handle which is returned refers to the standard
   1205   error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
   1206   is returned.
   1207 
   1208   If the file name is NUL, then the file handle that is returned refers to the standard NUL
   1209   file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
   1210   returned.
   1211 
   1212   If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
   1213   FileHandle is NULL.
   1214 
   1215   @param FileName               Points to the NULL-terminated UCS-2 encoded file name.
   1216   @param FileHandle             On return, points to the file handle.
   1217   @param OpenMode               File open mode. Either EFI_FILE_MODE_READ or
   1218                                 EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
   1219                                 Specification.
   1220   @retval EFI_SUCCESS           The file was opened. FileHandle has the opened file's handle.
   1221   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
   1222   @retval EFI_UNSUPPORTED       Could not open the file path. FileHandle is NULL.
   1223   @retval EFI_NOT_FOUND         The specified file could not be found on the device or the file
   1224                                 system could not be found on the device. FileHandle is NULL.
   1225   @retval EFI_NO_MEDIA          The device has no medium. FileHandle is NULL.
   1226   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the medium is no
   1227                                 longer supported. FileHandle is NULL.
   1228   @retval EFI_DEVICE_ERROR      The device reported an error or can't get the file path according
   1229                                 the FileName. FileHandle is NULL.
   1230   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted. FileHandle is NULL.
   1231   @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a file for write
   1232                                 when the media is write-protected. FileHandle is NULL.
   1233   @retval EFI_ACCESS_DENIED     The service denied access to the file. FileHandle is NULL.
   1234   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the file. FileHandle
   1235                                 is NULL.
   1236   @retval EFI_VOLUME_FULL       The volume is full. FileHandle is NULL.
   1237 **/
   1238 EFI_STATUS
   1239 EFIAPI
   1240 EfiShellOpenFileByName(
   1241   IN CONST CHAR16       *FileName,
   1242   OUT SHELL_FILE_HANDLE *FileHandle,
   1243   IN UINT64             OpenMode
   1244   )
   1245 {
   1246   EFI_DEVICE_PATH_PROTOCOL        *DevicePath;
   1247   EFI_STATUS                      Status;
   1248 
   1249   *FileHandle = NULL;
   1250 
   1251   //
   1252   // Is this for StdIn
   1253   //
   1254   if (StrCmp(FileName, L">i") == 0) {
   1255     //
   1256     // make sure not writing to StdIn
   1257     //
   1258     if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
   1259       return (EFI_INVALID_PARAMETER);
   1260     }
   1261     *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
   1262     ASSERT(*FileHandle != NULL);
   1263     return (EFI_SUCCESS);
   1264   }
   1265 
   1266   //
   1267   // Is this for StdOut
   1268   //
   1269   if (StrCmp(FileName, L">o") == 0) {
   1270     //
   1271     // make sure not writing to StdIn
   1272     //
   1273     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
   1274       return (EFI_INVALID_PARAMETER);
   1275     }
   1276     *FileHandle = &FileInterfaceStdOut;
   1277     return (EFI_SUCCESS);
   1278   }
   1279 
   1280   //
   1281   // Is this for NUL file
   1282   //
   1283   if (StrCmp(FileName, L"NUL") == 0) {
   1284     *FileHandle = &FileInterfaceNulFile;
   1285     return (EFI_SUCCESS);
   1286   }
   1287 
   1288   //
   1289   // Is this for StdErr
   1290   //
   1291   if (StrCmp(FileName, L">e") == 0) {
   1292     //
   1293     // make sure not writing to StdIn
   1294     //
   1295     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
   1296       return (EFI_INVALID_PARAMETER);
   1297     }
   1298     *FileHandle = &FileInterfaceStdErr;
   1299     return (EFI_SUCCESS);
   1300   }
   1301 
   1302   //
   1303   // Is this for an environment variable
   1304   // do we start with >v
   1305   //
   1306   if (StrStr(FileName, L">v") == FileName) {
   1307     if (!IsVolatileEnv(FileName+2) &&
   1308         ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
   1309       return (EFI_INVALID_PARAMETER);
   1310     }
   1311     *FileHandle = CreateFileInterfaceEnv(FileName+2);
   1312     return (EFI_SUCCESS);
   1313   }
   1314 
   1315   //
   1316   // We are opening a regular file.
   1317   //
   1318   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
   1319 //  DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););
   1320   if (DevicePath == NULL) {
   1321     return (EFI_NOT_FOUND);
   1322   }
   1323 
   1324   //
   1325   // Copy the device path, open the file, then free the memory
   1326   //
   1327   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
   1328   FreePool(DevicePath);
   1329 
   1330   return(Status);
   1331 }
   1332 
   1333 /**
   1334   Deletes the file specified by the file name.
   1335 
   1336   This function deletes a file.
   1337 
   1338   @param FileName                 Points to the NULL-terminated file name.
   1339 
   1340   @retval EFI_SUCCESS             The file was closed and deleted, and the handle was closed.
   1341   @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
   1342   @sa EfiShellCreateFile
   1343 **/
   1344 EFI_STATUS
   1345 EFIAPI
   1346 EfiShellDeleteFileByName(
   1347   IN CONST CHAR16 *FileName
   1348   )
   1349 {
   1350   SHELL_FILE_HANDLE FileHandle;
   1351   EFI_STATUS        Status;
   1352 
   1353   FileHandle = NULL;
   1354 
   1355   //
   1356   // get a handle to the file
   1357   //
   1358   Status = EfiShellCreateFile(FileName,
   1359                               0,
   1360                               &FileHandle);
   1361   if (EFI_ERROR(Status)) {
   1362     return (Status);
   1363   }
   1364   //
   1365   // now delete the file
   1366   //
   1367   ShellFileHandleRemove(FileHandle);
   1368   return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
   1369 }
   1370 
   1371 /**
   1372   Disables the page break output mode.
   1373 **/
   1374 VOID
   1375 EFIAPI
   1376 EfiShellDisablePageBreak (
   1377   VOID
   1378   )
   1379 {
   1380   ShellInfoObject.PageBreakEnabled = FALSE;
   1381 }
   1382 
   1383 /**
   1384   Enables the page break output mode.
   1385 **/
   1386 VOID
   1387 EFIAPI
   1388 EfiShellEnablePageBreak (
   1389   VOID
   1390   )
   1391 {
   1392   ShellInfoObject.PageBreakEnabled = TRUE;
   1393 }
   1394 
   1395 /**
   1396   internal worker function to load and run an image via device path.
   1397 
   1398   @param ParentImageHandle      A handle of the image that is executing the specified
   1399                                 command line.
   1400   @param DevicePath             device path of the file to execute
   1401   @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
   1402                                 containing the command line. If NULL then the command-
   1403                                 line will be empty.
   1404   @param Environment            Points to a NULL-terminated array of environment
   1405                                 variables with the format 'x=y', where x is the
   1406                                 environment variable name and y is the value. If this
   1407                                 is NULL, then the current shell environment is used.
   1408 
   1409   @param[out] StartImageStatus  Returned status from gBS->StartImage.
   1410 
   1411   @retval EFI_SUCCESS       The command executed successfully. The  status code
   1412                             returned by the command is pointed to by StatusCode.
   1413   @retval EFI_INVALID_PARAMETER The parameters are invalid.
   1414   @retval EFI_OUT_OF_RESOURCES Out of resources.
   1415   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
   1416 **/
   1417 EFI_STATUS
   1418 EFIAPI
   1419 InternalShellExecuteDevicePath(
   1420   IN CONST EFI_HANDLE               *ParentImageHandle,
   1421   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
   1422   IN CONST CHAR16                   *CommandLine OPTIONAL,
   1423   IN CONST CHAR16                   **Environment OPTIONAL,
   1424   OUT EFI_STATUS                    *StartImageStatus OPTIONAL
   1425   )
   1426 {
   1427   EFI_STATUS                    Status;
   1428   EFI_STATUS                    StartStatus;
   1429   EFI_STATUS                    CleanupStatus;
   1430   EFI_HANDLE                    NewHandle;
   1431   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
   1432   LIST_ENTRY                    OrigEnvs;
   1433   EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
   1434   CHAR16                        *ImagePath;
   1435   UINTN                         Index;
   1436   CHAR16                        *Walker;
   1437   CHAR16                        *NewCmdLine;
   1438 
   1439   if (ParentImageHandle == NULL) {
   1440     return (EFI_INVALID_PARAMETER);
   1441   }
   1442 
   1443   InitializeListHead(&OrigEnvs);
   1444 
   1445   NewHandle = NULL;
   1446 
   1447   NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
   1448   if (NewCmdLine == NULL) {
   1449     return EFI_OUT_OF_RESOURCES;
   1450   }
   1451 
   1452   for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
   1453     if (*Walker == L'^' && *(Walker+1) == L'#') {
   1454       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
   1455     }
   1456   }
   1457 
   1458   //
   1459   // Load the image with:
   1460   // FALSE - not from boot manager and NULL, 0 being not already in memory
   1461   //
   1462   Status = gBS->LoadImage(
   1463     FALSE,
   1464     *ParentImageHandle,
   1465     (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
   1466     NULL,
   1467     0,
   1468     &NewHandle);
   1469 
   1470   if (EFI_ERROR(Status)) {
   1471     if (NewHandle != NULL) {
   1472       gBS->UnloadImage(NewHandle);
   1473     }
   1474     FreePool (NewCmdLine);
   1475     return (Status);
   1476   }
   1477   Status = gBS->OpenProtocol(
   1478     NewHandle,
   1479     &gEfiLoadedImageProtocolGuid,
   1480     (VOID**)&LoadedImage,
   1481     gImageHandle,
   1482     NULL,
   1483     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   1484 
   1485   if (!EFI_ERROR(Status)) {
   1486     ASSERT(LoadedImage->LoadOptionsSize == 0);
   1487     if (NewCmdLine != NULL) {
   1488       LoadedImage->LoadOptionsSize  = (UINT32)StrSize(NewCmdLine);
   1489       LoadedImage->LoadOptions      = (VOID*)NewCmdLine;
   1490     }
   1491 
   1492     //
   1493     // Save our current environment settings for later restoration if necessary
   1494     //
   1495     if (Environment != NULL) {
   1496       Status = GetEnvironmentVariableList(&OrigEnvs);
   1497       if (!EFI_ERROR(Status)) {
   1498         Status = SetEnvironmentVariables(Environment);
   1499       }
   1500     }
   1501 
   1502     //
   1503     // Initialize and install a shell parameters protocol on the image.
   1504     //
   1505     ShellParamsProtocol.StdIn   = ShellInfoObject.NewShellParametersProtocol->StdIn;
   1506     ShellParamsProtocol.StdOut  = ShellInfoObject.NewShellParametersProtocol->StdOut;
   1507     ShellParamsProtocol.StdErr  = ShellInfoObject.NewShellParametersProtocol->StdErr;
   1508     Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL);
   1509     ASSERT_EFI_ERROR(Status);
   1510     //
   1511     // Replace Argv[0] with the full path of the binary we're executing:
   1512     // If the command line was "foo", the binary might be called "foo.efi".
   1513     // "The first entry in [Argv] is always the full file path of the
   1514     //  executable" - UEFI Shell Spec section 2.3
   1515     //
   1516     ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
   1517     // The image we're executing isn't necessarily in a filesystem - it might
   1518     // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
   1519     // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
   1520     if (ImagePath != NULL) {
   1521       if (ShellParamsProtocol.Argv == NULL) {
   1522         // Command line was empty or null.
   1523         // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
   1524         ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
   1525         if (ShellParamsProtocol.Argv == NULL) {
   1526           Status = EFI_OUT_OF_RESOURCES;
   1527           goto UnloadImage;
   1528         }
   1529         ShellParamsProtocol.Argc = 1;
   1530       } else {
   1531         // Free the string UpdateArgcArgv put in Argv[0];
   1532         FreePool (ShellParamsProtocol.Argv[0]);
   1533       }
   1534       ShellParamsProtocol.Argv[0] = ImagePath;
   1535     }
   1536 
   1537     Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
   1538     ASSERT_EFI_ERROR(Status);
   1539 
   1540     ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
   1541 
   1542     //
   1543     // now start the image and if the caller wanted the return code pass it to them...
   1544     //
   1545     if (!EFI_ERROR(Status)) {
   1546       StartStatus      = gBS->StartImage(
   1547                           NewHandle,
   1548                           0,
   1549                           NULL
   1550                           );
   1551       if (StartImageStatus != NULL) {
   1552         *StartImageStatus = StartStatus;
   1553       }
   1554 
   1555       CleanupStatus = gBS->UninstallProtocolInterface(
   1556                             NewHandle,
   1557                             &gEfiShellParametersProtocolGuid,
   1558                             &ShellParamsProtocol
   1559                             );
   1560       ASSERT_EFI_ERROR(CleanupStatus);
   1561 
   1562       goto FreeAlloc;
   1563     }
   1564 
   1565 UnloadImage:
   1566     // Unload image - We should only get here if we didn't call StartImage
   1567     gBS->UnloadImage (NewHandle);
   1568 
   1569 FreeAlloc:
   1570     // Free Argv (Allocated in UpdateArgcArgv)
   1571     if (ShellParamsProtocol.Argv != NULL) {
   1572       for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) {
   1573         if (ShellParamsProtocol.Argv[Index] != NULL) {
   1574           FreePool (ShellParamsProtocol.Argv[Index]);
   1575         }
   1576       }
   1577       FreePool (ShellParamsProtocol.Argv);
   1578     }
   1579   }
   1580 
   1581   // Restore environment variables
   1582   if (!IsListEmpty(&OrigEnvs)) {
   1583     CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
   1584     ASSERT_EFI_ERROR (CleanupStatus);
   1585   }
   1586 
   1587   FreePool (NewCmdLine);
   1588 
   1589   return(Status);
   1590 }
   1591 /**
   1592   Execute the command line.
   1593 
   1594   This function creates a nested instance of the shell and executes the specified
   1595   command (CommandLine) with the specified environment (Environment). Upon return,
   1596   the status code returned by the specified command is placed in StatusCode.
   1597 
   1598   If Environment is NULL, then the current environment is used and all changes made
   1599   by the commands executed will be reflected in the current environment. If the
   1600   Environment is non-NULL, then the changes made will be discarded.
   1601 
   1602   The CommandLine is executed from the current working directory on the current
   1603   device.
   1604 
   1605   @param ParentImageHandle  A handle of the image that is executing the specified
   1606                             command line.
   1607   @param CommandLine        Points to the NULL-terminated UCS-2 encoded string
   1608                             containing the command line. If NULL then the command-
   1609                             line will be empty.
   1610   @param Environment        Points to a NULL-terminated array of environment
   1611                             variables with the format 'x=y', where x is the
   1612                             environment variable name and y is the value. If this
   1613                             is NULL, then the current shell environment is used.
   1614   @param StatusCode         Points to the status code returned by the command.
   1615 
   1616   @retval EFI_SUCCESS       The command executed successfully. The  status code
   1617                             returned by the command is pointed to by StatusCode.
   1618   @retval EFI_INVALID_PARAMETER The parameters are invalid.
   1619   @retval EFI_OUT_OF_RESOURCES Out of resources.
   1620   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
   1621   @retval EFI_UNSUPPORTED   The support level required for this function is not present.
   1622 
   1623   @sa InternalShellExecuteDevicePath
   1624 **/
   1625 EFI_STATUS
   1626 EFIAPI
   1627 EfiShellExecute(
   1628   IN EFI_HANDLE *ParentImageHandle,
   1629   IN CHAR16 *CommandLine OPTIONAL,
   1630   IN CHAR16 **Environment OPTIONAL,
   1631   OUT EFI_STATUS *StatusCode OPTIONAL
   1632   )
   1633 {
   1634   EFI_STATUS                Status;
   1635   CHAR16                    *Temp;
   1636   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
   1637   UINTN                     Size;
   1638   EFI_STATUS                CalleeStatusCode;
   1639 
   1640   CalleeStatusCode = EFI_SUCCESS;
   1641 
   1642   if ((PcdGet8(PcdShellSupportLevel) < 1)) {
   1643     return (EFI_UNSUPPORTED);
   1644   }
   1645 
   1646   if (Environment != NULL) {
   1647     // If Environment isn't null, load a new image of the shell with its own
   1648     // environment
   1649     DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
   1650 
   1651     DEBUG_CODE_BEGIN();
   1652     Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
   1653     FreePool(Temp);
   1654     Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
   1655     FreePool(Temp);
   1656     Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE);
   1657     FreePool(Temp);
   1658     DEBUG_CODE_END();
   1659 
   1660     Temp = NULL;
   1661     Size = 0;
   1662     ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));
   1663     StrnCatGrow(&Temp, &Size, L"Shell.efi -_exit ", 0);
   1664     StrnCatGrow(&Temp, &Size, CommandLine, 0);
   1665 
   1666     Status = InternalShellExecuteDevicePath(
   1667       ParentImageHandle,
   1668       DevPath,
   1669       Temp,
   1670       (CONST CHAR16**)Environment,
   1671       StatusCode);
   1672 
   1673     //
   1674     // de-allocate and return
   1675     //
   1676     FreePool(DevPath);
   1677     FreePool(Temp);
   1678   } else {
   1679     // If Environment is NULL, we are free to use and mutate the current shell
   1680     // environment. This is much faster as uses much less memory.
   1681 
   1682     if (CommandLine == NULL) {
   1683       CommandLine = L"";
   1684     }
   1685 
   1686     Status = RunShellCommand (CommandLine, &CalleeStatusCode);
   1687 
   1688     // Pass up the command's exit code if the caller wants it
   1689     if (StatusCode != NULL) {
   1690       *StatusCode = (EFI_STATUS) CalleeStatusCode;
   1691     }
   1692   }
   1693 
   1694   return(Status);
   1695 }
   1696 
   1697 /**
   1698   Utility cleanup function for EFI_SHELL_FILE_INFO objects.
   1699 
   1700   1) frees all pointers (non-NULL)
   1701   2) Closes the SHELL_FILE_HANDLE
   1702 
   1703   @param FileListNode     pointer to the list node to free
   1704 **/
   1705 VOID
   1706 EFIAPI
   1707 InternalFreeShellFileInfoNode(
   1708   IN EFI_SHELL_FILE_INFO *FileListNode
   1709   )
   1710 {
   1711   if (FileListNode->Info != NULL) {
   1712     FreePool((VOID*)FileListNode->Info);
   1713   }
   1714   if (FileListNode->FileName != NULL) {
   1715     FreePool((VOID*)FileListNode->FileName);
   1716   }
   1717   if (FileListNode->FullName != NULL) {
   1718     FreePool((VOID*)FileListNode->FullName);
   1719   }
   1720   if (FileListNode->Handle != NULL) {
   1721     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
   1722   }
   1723   FreePool(FileListNode);
   1724 }
   1725 /**
   1726   Frees the file list.
   1727 
   1728   This function cleans up the file list and any related data structures. It has no
   1729   impact on the files themselves.
   1730 
   1731   @param FileList               The file list to free. Type EFI_SHELL_FILE_INFO is
   1732                                 defined in OpenFileList()
   1733 
   1734   @retval EFI_SUCCESS           Free the file list successfully.
   1735   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
   1736 **/
   1737 EFI_STATUS
   1738 EFIAPI
   1739 EfiShellFreeFileList(
   1740   IN EFI_SHELL_FILE_INFO **FileList
   1741   )
   1742 {
   1743   EFI_SHELL_FILE_INFO *ShellFileListItem;
   1744 
   1745   if (FileList == NULL || *FileList == NULL) {
   1746     return (EFI_INVALID_PARAMETER);
   1747   }
   1748 
   1749   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1750       ; !IsListEmpty(&(*FileList)->Link)
   1751       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1752      ){
   1753     RemoveEntryList(&ShellFileListItem->Link);
   1754     InternalFreeShellFileInfoNode(ShellFileListItem);
   1755   }
   1756   InternalFreeShellFileInfoNode(*FileList);
   1757   *FileList = NULL;
   1758   return(EFI_SUCCESS);
   1759 }
   1760 
   1761 /**
   1762   Deletes the duplicate file names files in the given file list.
   1763 
   1764   This function deletes the reduplicate files in the given file list.
   1765 
   1766   @param FileList               A pointer to the first entry in the file list.
   1767 
   1768   @retval EFI_SUCCESS           Always success.
   1769   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
   1770 **/
   1771 EFI_STATUS
   1772 EFIAPI
   1773 EfiShellRemoveDupInFileList(
   1774   IN EFI_SHELL_FILE_INFO **FileList
   1775   )
   1776 {
   1777   EFI_SHELL_FILE_INFO *ShellFileListItem;
   1778   EFI_SHELL_FILE_INFO *ShellFileListItem2;
   1779   EFI_SHELL_FILE_INFO *TempNode;
   1780 
   1781   if (FileList == NULL || *FileList == NULL) {
   1782     return (EFI_INVALID_PARAMETER);
   1783   }
   1784   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   1785       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
   1786       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   1787      ){
   1788     for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   1789         ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)
   1790         ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)
   1791        ){
   1792       if (gUnicodeCollation->StriColl(
   1793             gUnicodeCollation,
   1794             (CHAR16*)ShellFileListItem->FullName,
   1795             (CHAR16*)ShellFileListItem2->FullName) == 0
   1796          ){
   1797         TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode(
   1798                                             &(*FileList)->Link,
   1799                                             &ShellFileListItem2->Link
   1800                                             );
   1801         RemoveEntryList(&ShellFileListItem2->Link);
   1802         InternalFreeShellFileInfoNode(ShellFileListItem2);
   1803         // Set ShellFileListItem2 to PreviousNode so we don't access Freed
   1804         // memory in GetNextNode in the loop expression above.
   1805         ShellFileListItem2 = TempNode;
   1806       }
   1807     }
   1808   }
   1809   return (EFI_SUCCESS);
   1810 }
   1811 
   1812 //
   1813 // This is the same structure as the external version, but it has no CONST qualifiers.
   1814 //
   1815 typedef struct {
   1816   LIST_ENTRY        Link;       ///< Linked list members.
   1817   EFI_STATUS        Status;     ///< Status of opening the file.  Valid only if Handle != NULL.
   1818         CHAR16      *FullName;  ///< Fully qualified filename.
   1819         CHAR16      *FileName;  ///< name of this file.
   1820   SHELL_FILE_HANDLE Handle;     ///< Handle for interacting with the opened file or NULL if closed.
   1821   EFI_FILE_INFO     *Info;      ///< Pointer to the FileInfo struct for this file or NULL.
   1822 } EFI_SHELL_FILE_INFO_NO_CONST;
   1823 
   1824 /**
   1825   Allocates and duplicates a EFI_SHELL_FILE_INFO node.
   1826 
   1827   @param[in] Node     The node to copy from.
   1828   @param[in] Save     TRUE to set Node->Handle to NULL, FALSE otherwise.
   1829 
   1830   @retval NULL        a memory allocation error ocurred
   1831   @return != NULL     a pointer to the new node
   1832 **/
   1833 EFI_SHELL_FILE_INFO*
   1834 EFIAPI
   1835 InternalDuplicateShellFileInfo(
   1836   IN       EFI_SHELL_FILE_INFO *Node,
   1837   IN BOOLEAN                   Save
   1838   )
   1839 {
   1840   EFI_SHELL_FILE_INFO_NO_CONST *NewNode;
   1841 
   1842   //
   1843   // try to confirm that the objects are in sync
   1844   //
   1845   ASSERT(sizeof(EFI_SHELL_FILE_INFO_NO_CONST) == sizeof(EFI_SHELL_FILE_INFO));
   1846 
   1847   NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1848   if (NewNode == NULL) {
   1849     return (NULL);
   1850   }
   1851   NewNode->FullName = AllocateCopyPool(StrSize(Node->FullName), Node->FullName);
   1852   NewNode->FileName = AllocateCopyPool(StrSize(Node->FileName), Node->FileName);
   1853   NewNode->Info     = AllocateCopyPool((UINTN)Node->Info->Size, Node->Info);
   1854   if ( NewNode->FullName == NULL
   1855     || NewNode->FileName == NULL
   1856     || NewNode->Info == NULL
   1857   ){
   1858     SHELL_FREE_NON_NULL(NewNode->FullName);
   1859     SHELL_FREE_NON_NULL(NewNode->FileName);
   1860     SHELL_FREE_NON_NULL(NewNode->Info);
   1861     SHELL_FREE_NON_NULL(NewNode);
   1862     return(NULL);
   1863   }
   1864   NewNode->Status = Node->Status;
   1865   NewNode->Handle = Node->Handle;
   1866   if (!Save) {
   1867     Node->Handle = NULL;
   1868   }
   1869 
   1870   return((EFI_SHELL_FILE_INFO*)NewNode);
   1871 }
   1872 
   1873 /**
   1874   Allocates and populates a EFI_SHELL_FILE_INFO structure.  if any memory operation
   1875   failed it will return NULL.
   1876 
   1877   @param[in] BasePath         the Path to prepend onto filename for FullPath
   1878   @param[in] Status           Status member initial value.
   1879   @param[in] FileName         FileName member initial value.
   1880   @param[in] Handle           Handle member initial value.
   1881   @param[in] Info             Info struct to copy.
   1882 
   1883   @retval NULL                An error ocurred.
   1884   @return                     a pointer to the newly allocated structure.
   1885 **/
   1886 EFI_SHELL_FILE_INFO *
   1887 EFIAPI
   1888 CreateAndPopulateShellFileInfo(
   1889   IN CONST CHAR16 *BasePath,
   1890   IN CONST EFI_STATUS Status,
   1891   IN CONST CHAR16 *FileName,
   1892   IN CONST SHELL_FILE_HANDLE Handle,
   1893   IN CONST EFI_FILE_INFO *Info
   1894   )
   1895 {
   1896   EFI_SHELL_FILE_INFO *ShellFileListItem;
   1897   CHAR16              *TempString;
   1898   UINTN               Size;
   1899 
   1900   TempString = NULL;
   1901   Size = 0;
   1902 
   1903   ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   1904   if (ShellFileListItem == NULL) {
   1905     return (NULL);
   1906   }
   1907   if (Info != NULL && Info->Size != 0) {
   1908     ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);
   1909     if (ShellFileListItem->Info == NULL) {
   1910       FreePool(ShellFileListItem);
   1911       return (NULL);
   1912     }
   1913     CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);
   1914   } else {
   1915     ShellFileListItem->Info = NULL;
   1916   }
   1917   if (FileName != NULL) {
   1918     ASSERT(TempString == NULL);
   1919     ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);
   1920     if (ShellFileListItem->FileName == NULL) {
   1921       FreePool(ShellFileListItem->Info);
   1922       FreePool(ShellFileListItem);
   1923       return (NULL);
   1924     }
   1925   } else {
   1926     ShellFileListItem->FileName = NULL;
   1927   }
   1928   Size = 0;
   1929   TempString = NULL;
   1930   if (BasePath != NULL) {
   1931     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   1932     TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
   1933     if (TempString == NULL) {
   1934       FreePool((VOID*)ShellFileListItem->FileName);
   1935       SHELL_FREE_NON_NULL(ShellFileListItem->Info);
   1936       FreePool(ShellFileListItem);
   1937       return (NULL);
   1938     }
   1939   }
   1940   if (ShellFileListItem->FileName != NULL) {
   1941     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   1942     TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);
   1943     if (TempString == NULL) {
   1944       FreePool((VOID*)ShellFileListItem->FileName);
   1945       FreePool(ShellFileListItem->Info);
   1946       FreePool(ShellFileListItem);
   1947       return (NULL);
   1948     }
   1949   }
   1950 
   1951   TempString = PathCleanUpDirectories(TempString);
   1952 
   1953   ShellFileListItem->FullName = TempString;
   1954   ShellFileListItem->Status   = Status;
   1955   ShellFileListItem->Handle   = Handle;
   1956 
   1957   return (ShellFileListItem);
   1958 }
   1959 
   1960 /**
   1961   Find all files in a specified directory.
   1962 
   1963   @param FileDirHandle          Handle of the directory to search.
   1964   @param FileList               On return, points to the list of files in the directory
   1965                                 or NULL if there are no files in the directory.
   1966 
   1967   @retval EFI_SUCCESS           File information was returned successfully.
   1968   @retval EFI_VOLUME_CORRUPTED  The file system structures have been corrupted.
   1969   @retval EFI_DEVICE_ERROR      The device reported an error.
   1970   @retval EFI_NO_MEDIA          The device media is not present.
   1971   @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
   1972   @return                       An error from FileHandleGetFileName().
   1973 **/
   1974 EFI_STATUS
   1975 EFIAPI
   1976 EfiShellFindFilesInDir(
   1977   IN SHELL_FILE_HANDLE FileDirHandle,
   1978   OUT EFI_SHELL_FILE_INFO **FileList
   1979   )
   1980 {
   1981   EFI_SHELL_FILE_INFO       *ShellFileList;
   1982   EFI_SHELL_FILE_INFO       *ShellFileListItem;
   1983   EFI_FILE_INFO             *FileInfo;
   1984   EFI_STATUS                Status;
   1985   BOOLEAN                   NoFile;
   1986   CHAR16                    *TempString;
   1987   CHAR16                    *BasePath;
   1988   UINTN                     Size;
   1989   CHAR16                    *TempSpot;
   1990 
   1991   BasePath = NULL;
   1992   Status = FileHandleGetFileName(FileDirHandle, &BasePath);
   1993   if (EFI_ERROR(Status)) {
   1994     return (Status);
   1995   }
   1996 
   1997   if (ShellFileHandleGetPath(FileDirHandle) != NULL) {
   1998     TempString        = NULL;
   1999     Size              = 0;
   2000     TempString        = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);
   2001     if (TempString == NULL) {
   2002       SHELL_FREE_NON_NULL(BasePath);
   2003       return (EFI_OUT_OF_RESOURCES);
   2004     }
   2005     TempSpot          = StrStr(TempString, L";");
   2006 
   2007     if (TempSpot != NULL) {
   2008       *TempSpot = CHAR_NULL;
   2009     }
   2010 
   2011     TempString        = StrnCatGrow(&TempString, &Size, BasePath, 0);
   2012     if (TempString == NULL) {
   2013       SHELL_FREE_NON_NULL(BasePath);
   2014       return (EFI_OUT_OF_RESOURCES);
   2015     }
   2016     SHELL_FREE_NON_NULL(BasePath);
   2017     BasePath          = TempString;
   2018   }
   2019 
   2020   NoFile            = FALSE;
   2021   ShellFileList     = NULL;
   2022   ShellFileListItem = NULL;
   2023   FileInfo          = NULL;
   2024   Status            = EFI_SUCCESS;
   2025 
   2026 
   2027   for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)
   2028       ; !EFI_ERROR(Status) && !NoFile
   2029       ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)
   2030      ){
   2031     //
   2032     // allocate a new EFI_SHELL_FILE_INFO and populate it...
   2033     //
   2034     ShellFileListItem = CreateAndPopulateShellFileInfo(
   2035       BasePath,
   2036       EFI_SUCCESS,  // success since we didnt fail to open it...
   2037       FileInfo->FileName,
   2038       NULL,         // no handle since not open
   2039       FileInfo);
   2040 
   2041     if (ShellFileList == NULL) {
   2042       ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2043       ASSERT(ShellFileList != NULL);
   2044       InitializeListHead(&ShellFileList->Link);
   2045     }
   2046     InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
   2047   }
   2048   if (EFI_ERROR(Status)) {
   2049     EfiShellFreeFileList(&ShellFileList);
   2050     *FileList = NULL;
   2051   } else {
   2052     *FileList = ShellFileList;
   2053   }
   2054   SHELL_FREE_NON_NULL(BasePath);
   2055   return(Status);
   2056 }
   2057 
   2058 /**
   2059   Get the GUID value from a human readable name.
   2060 
   2061   If GuidName is a known GUID name, then update Guid to have the correct value for
   2062   that GUID.
   2063 
   2064   This function is only available when the major and minor versions in the
   2065   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   2066 
   2067   @param[in]  GuidName   A pointer to the localized name for the GUID being queried.
   2068   @param[out] Guid       A pointer to the GUID structure to be filled in.
   2069 
   2070   @retval EFI_SUCCESS             The operation was successful.
   2071   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   2072   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   2073   @retval EFI_NOT_FOUND           GuidName is not a known GUID Name.
   2074 **/
   2075 EFI_STATUS
   2076 EFIAPI
   2077 EfiShellGetGuidFromName(
   2078   IN  CONST CHAR16   *GuidName,
   2079   OUT       EFI_GUID *Guid
   2080   )
   2081 {
   2082   EFI_GUID    *NewGuid;
   2083   EFI_STATUS  Status;
   2084 
   2085   if (Guid == NULL || GuidName == NULL) {
   2086     return (EFI_INVALID_PARAMETER);
   2087   }
   2088 
   2089   Status = GetGuidFromStringName(GuidName, NULL, &NewGuid);
   2090 
   2091   if (!EFI_ERROR(Status)) {
   2092     CopyGuid(NewGuid, Guid);
   2093   }
   2094 
   2095   return (Status);
   2096 }
   2097 
   2098 /**
   2099   Get the human readable name for a GUID from the value.
   2100 
   2101   If Guid is assigned a name, then update *GuidName to point to the name. The callee
   2102   should not modify the value.
   2103 
   2104   This function is only available when the major and minor versions in the
   2105   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
   2106 
   2107   @param[in]  Guid       A pointer to the GUID being queried.
   2108   @param[out] GuidName   A pointer to a pointer the localized to name for the GUID being requested
   2109 
   2110   @retval EFI_SUCCESS             The operation was successful.
   2111   @retval EFI_INVALID_PARAMETER   Guid was NULL.
   2112   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
   2113   @retval EFI_NOT_FOUND           Guid is not assigned a name.
   2114 **/
   2115 EFI_STATUS
   2116 EFIAPI
   2117 EfiShellGetGuidName(
   2118   IN  CONST EFI_GUID *Guid,
   2119   OUT CONST CHAR16   **GuidName
   2120   )
   2121 {
   2122   CHAR16   *Name;
   2123 
   2124   if (Guid == NULL || GuidName == NULL) {
   2125     return (EFI_INVALID_PARAMETER);
   2126   }
   2127 
   2128   Name = GetStringNameFromGuid(Guid, NULL);
   2129   if (Name == NULL || StrLen(Name) == 0) {
   2130     SHELL_FREE_NON_NULL(Name);
   2131     return (EFI_NOT_FOUND);
   2132   }
   2133 
   2134   *GuidName = AddBufferToFreeList(Name);
   2135 
   2136   return (EFI_SUCCESS);
   2137 }
   2138 
   2139 /**
   2140   Updates a file name to be preceeded by the mapped drive name
   2141 
   2142   @param[in] BasePath      the Mapped drive name to prepend
   2143   @param[in, out] Path     pointer to pointer to the file name to update.
   2144 
   2145   @retval EFI_SUCCESS
   2146   @retval EFI_OUT_OF_RESOURCES
   2147 **/
   2148 EFI_STATUS
   2149 EFIAPI
   2150 UpdateFileName(
   2151   IN CONST CHAR16 *BasePath,
   2152   IN OUT CHAR16   **Path
   2153   )
   2154 {
   2155   CHAR16              *Path2;
   2156   UINTN               Path2Size;
   2157 
   2158   Path2Size = 0;
   2159   Path2 = NULL;
   2160 
   2161   ASSERT(Path      != NULL);
   2162   ASSERT(*Path     != NULL);
   2163   ASSERT(BasePath  != NULL);
   2164 
   2165   //
   2166   // convert a local path to an absolute path
   2167   //
   2168   if (StrStr(*Path, L":") == NULL) {
   2169     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2170     StrnCatGrow(&Path2, &Path2Size, BasePath, 0);
   2171     if (Path2 == NULL) {
   2172       return (EFI_OUT_OF_RESOURCES);
   2173     }
   2174     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2175     StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);
   2176     if (Path2 == NULL) {
   2177       return (EFI_OUT_OF_RESOURCES);
   2178     }
   2179   }
   2180 
   2181   FreePool(*Path);
   2182   (*Path) = Path2;
   2183 
   2184   return (EFI_SUCCESS);
   2185 }
   2186 
   2187 /**
   2188   If FileHandle is a directory then the function reads from FileHandle and reads in
   2189   each of the FileInfo structures.  If one of them matches the Pattern's first
   2190   "level" then it opens that handle and calls itself on that handle.
   2191 
   2192   If FileHandle is a file and matches all of the remaining Pattern (which would be
   2193   on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
   2194 
   2195   Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
   2196   FreeFileList with FileList.
   2197 
   2198   @param[in] FilePattern         The FilePattern to check against.
   2199   @param[in] UnicodeCollation    The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
   2200   @param[in] FileHandle          The FileHandle to start with
   2201   @param[in, out] FileList       pointer to pointer to list of found files.
   2202   @param[in] ParentNode          The node for the parent. Same file as identified by HANDLE.
   2203   @param[in] MapName             The file system name this file is on.
   2204 
   2205   @retval EFI_SUCCESS           all files were found and the FileList contains a list.
   2206   @retval EFI_NOT_FOUND         no files were found
   2207   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed
   2208 **/
   2209 EFI_STATUS
   2210 EFIAPI
   2211 ShellSearchHandle(
   2212   IN     CONST CHAR16                         *FilePattern,
   2213   IN           EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
   2214   IN           SHELL_FILE_HANDLE              FileHandle,
   2215   IN OUT       EFI_SHELL_FILE_INFO            **FileList,
   2216   IN     CONST EFI_SHELL_FILE_INFO            *ParentNode OPTIONAL,
   2217   IN     CONST CHAR16                         *MapName
   2218   )
   2219 {
   2220   EFI_STATUS          Status;
   2221   CONST CHAR16        *NextFilePatternStart;
   2222   CHAR16              *CurrentFilePattern;
   2223   EFI_SHELL_FILE_INFO *ShellInfo;
   2224   EFI_SHELL_FILE_INFO *ShellInfoNode;
   2225   EFI_SHELL_FILE_INFO *NewShellNode;
   2226   EFI_FILE_INFO       *FileInfo;
   2227   BOOLEAN             Directory;
   2228   CHAR16              *NewFullName;
   2229   UINTN               Size;
   2230 
   2231   if ( FilePattern      == NULL
   2232     || UnicodeCollation == NULL
   2233     || FileList         == NULL
   2234    ){
   2235     return (EFI_INVALID_PARAMETER);
   2236   }
   2237   ShellInfo = NULL;
   2238   CurrentFilePattern = NULL;
   2239 
   2240   if (*FilePattern == L'\\') {
   2241     FilePattern++;
   2242   }
   2243 
   2244   for( NextFilePatternStart = FilePattern
   2245      ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
   2246      ; NextFilePatternStart++);
   2247 
   2248   CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
   2249   ASSERT(CurrentFilePattern != NULL);
   2250   StrnCpyS(CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern);
   2251 
   2252   if (CurrentFilePattern[0]   == CHAR_NULL
   2253     &&NextFilePatternStart[0] == CHAR_NULL
   2254     ){
   2255     //
   2256     // we want the parent or root node (if no parent)
   2257     //
   2258     if (ParentNode == NULL) {
   2259       //
   2260       // We want the root node.  create the node.
   2261       //
   2262       FileInfo = FileHandleGetInfo(FileHandle);
   2263       NewShellNode = CreateAndPopulateShellFileInfo(
   2264         MapName,
   2265         EFI_SUCCESS,
   2266         L"\\",
   2267         FileHandle,
   2268         FileInfo
   2269         );
   2270       SHELL_FREE_NON_NULL(FileInfo);
   2271     } else {
   2272       //
   2273       // Add the current parameter FileHandle to the list, then end...
   2274       //
   2275       NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
   2276     }
   2277     if (NewShellNode == NULL) {
   2278       Status = EFI_OUT_OF_RESOURCES;
   2279     } else {
   2280       NewShellNode->Handle = NULL;
   2281       if (*FileList == NULL) {
   2282         *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2283         InitializeListHead(&((*FileList)->Link));
   2284       }
   2285 
   2286       //
   2287       // Add to the returning to use list
   2288       //
   2289       InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
   2290 
   2291       Status = EFI_SUCCESS;
   2292     }
   2293   } else {
   2294     Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);
   2295 
   2296     if (!EFI_ERROR(Status)){
   2297       if (StrStr(NextFilePatternStart, L"\\") != NULL){
   2298         Directory = TRUE;
   2299       } else {
   2300         Directory = FALSE;
   2301       }
   2302       for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)
   2303           ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
   2304           ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)
   2305          ){
   2306         if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){
   2307           if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {
   2308             Size = StrSize(ShellInfoNode->FullName);
   2309             Size += StrSize(MapName) + sizeof(CHAR16);
   2310             NewFullName = AllocateZeroPool(Size);
   2311             if (NewFullName == NULL) {
   2312               Status = EFI_OUT_OF_RESOURCES;
   2313             } else {
   2314               StrCpyS(NewFullName, Size/sizeof(CHAR16), MapName);
   2315               StrCatS(NewFullName, Size/sizeof(CHAR16), ShellInfoNode->FullName+1);
   2316               FreePool((VOID*)ShellInfoNode->FullName);
   2317               ShellInfoNode->FullName = NewFullName;
   2318             }
   2319           }
   2320           if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){
   2321             //
   2322             // should be a directory
   2323             //
   2324 
   2325             //
   2326             // don't open the . and .. directories
   2327             //
   2328             if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
   2329               && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
   2330              ){
   2331               //
   2332               //
   2333               //
   2334               if (EFI_ERROR(Status)) {
   2335                 break;
   2336               }
   2337               //
   2338               // Open the directory since we need that handle in the next recursion.
   2339               //
   2340               ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
   2341 
   2342               //
   2343               // recurse with the next part of the pattern
   2344               //
   2345               Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
   2346               EfiShellClose(ShellInfoNode->Handle);
   2347               ShellInfoNode->Handle = NULL;
   2348             }
   2349           } else if (!EFI_ERROR(Status)) {
   2350             //
   2351             // should be a file
   2352             //
   2353 
   2354             //
   2355             // copy the information we need into a new Node
   2356             //
   2357             NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
   2358             ASSERT(NewShellNode != NULL);
   2359             if (NewShellNode == NULL) {
   2360               Status = EFI_OUT_OF_RESOURCES;
   2361             }
   2362             if (*FileList == NULL) {
   2363               *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
   2364               InitializeListHead(&((*FileList)->Link));
   2365             }
   2366 
   2367             //
   2368             // Add to the returning to use list
   2369             //
   2370             InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
   2371           }
   2372         }
   2373         if (EFI_ERROR(Status)) {
   2374           break;
   2375         }
   2376       }
   2377       if (EFI_ERROR(Status)) {
   2378         EfiShellFreeFileList(&ShellInfo);
   2379       } else {
   2380         Status = EfiShellFreeFileList(&ShellInfo);
   2381       }
   2382     }
   2383   }
   2384 
   2385   FreePool(CurrentFilePattern);
   2386   return (Status);
   2387 }
   2388 
   2389 /**
   2390   Find files that match a specified pattern.
   2391 
   2392   This function searches for all files and directories that match the specified
   2393   FilePattern. The FilePattern can contain wild-card characters. The resulting file
   2394   information is placed in the file list FileList.
   2395 
   2396   Wildcards are processed
   2397   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
   2398 
   2399   The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
   2400   field is set to NULL.
   2401 
   2402   if *FileList is not NULL then it must be a pre-existing and properly initialized list.
   2403 
   2404   @param FilePattern      Points to a NULL-terminated shell file path, including wildcards.
   2405   @param FileList         On return, points to the start of a file list containing the names
   2406                           of all matching files or else points to NULL if no matching files
   2407                           were found.  only on a EFI_SUCCESS return will; this be non-NULL.
   2408 
   2409   @retval EFI_SUCCESS           Files found.  FileList is a valid list.
   2410   @retval EFI_NOT_FOUND         No files found.
   2411   @retval EFI_NO_MEDIA          The device has no media
   2412   @retval EFI_DEVICE_ERROR      The device reported an error
   2413   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted
   2414 **/
   2415 EFI_STATUS
   2416 EFIAPI
   2417 EfiShellFindFiles(
   2418   IN CONST CHAR16 *FilePattern,
   2419   OUT EFI_SHELL_FILE_INFO **FileList
   2420   )
   2421 {
   2422   EFI_STATUS                      Status;
   2423   CHAR16                          *PatternCopy;
   2424   CHAR16                          *PatternCurrentLocation;
   2425   EFI_DEVICE_PATH_PROTOCOL        *RootDevicePath;
   2426   SHELL_FILE_HANDLE               RootFileHandle;
   2427   CHAR16                          *MapName;
   2428   UINTN                           Count;
   2429 
   2430   if ( FilePattern      == NULL
   2431     || FileList         == NULL
   2432     || StrStr(FilePattern, L":") == NULL
   2433    ){
   2434     return (EFI_INVALID_PARAMETER);
   2435   }
   2436   Status = EFI_SUCCESS;
   2437   RootDevicePath = NULL;
   2438   RootFileHandle = NULL;
   2439   MapName        = NULL;
   2440   PatternCopy = AllocateCopyPool(StrSize(FilePattern), FilePattern);
   2441   if (PatternCopy == NULL) {
   2442     return (EFI_OUT_OF_RESOURCES);
   2443   }
   2444 
   2445   PatternCopy = PathCleanUpDirectories(PatternCopy);
   2446 
   2447   Count = StrStr(PatternCopy, L":") - PatternCopy;
   2448   Count += 2;
   2449 
   2450   ASSERT(MapName == NULL);
   2451   MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
   2452   if (MapName == NULL) {
   2453     Status = EFI_OUT_OF_RESOURCES;
   2454   } else {
   2455     RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);
   2456     if (RootDevicePath == NULL) {
   2457       Status = EFI_INVALID_PARAMETER;
   2458     } else {
   2459       Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);
   2460       if (!EFI_ERROR(Status)) {
   2461         for ( PatternCurrentLocation = PatternCopy
   2462             ; *PatternCurrentLocation != ':'
   2463             ; PatternCurrentLocation++);
   2464         PatternCurrentLocation++;
   2465         Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
   2466         EfiShellClose(RootFileHandle);
   2467       }
   2468       FreePool(RootDevicePath);
   2469     }
   2470   }
   2471 
   2472   SHELL_FREE_NON_NULL(PatternCopy);
   2473   SHELL_FREE_NON_NULL(MapName);
   2474 
   2475   return(Status);
   2476 }
   2477 
   2478 /**
   2479   Opens the files that match the path specified.
   2480 
   2481   This function opens all of the files specified by Path. Wildcards are processed
   2482   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
   2483   matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
   2484 
   2485   @param Path                   A pointer to the path string.
   2486   @param OpenMode               Specifies the mode used to open each file, EFI_FILE_MODE_READ or
   2487                                 EFI_FILE_MODE_WRITE.
   2488   @param FileList               Points to the start of a list of files opened.
   2489 
   2490   @retval EFI_SUCCESS           Create the file list successfully.
   2491   @return Others                Can't create the file list.
   2492 **/
   2493 EFI_STATUS
   2494 EFIAPI
   2495 EfiShellOpenFileList(
   2496   IN CHAR16 *Path,
   2497   IN UINT64 OpenMode,
   2498   IN OUT EFI_SHELL_FILE_INFO **FileList
   2499   )
   2500 {
   2501   EFI_STATUS Status;
   2502   EFI_SHELL_FILE_INFO *ShellFileListItem;
   2503   CHAR16              *Path2;
   2504   UINTN               Path2Size;
   2505   CONST CHAR16        *CurDir;
   2506   BOOLEAN             Found;
   2507 
   2508   PathCleanUpDirectories(Path);
   2509 
   2510   Path2Size     = 0;
   2511   Path2         = NULL;
   2512 
   2513   if (FileList == NULL || *FileList == NULL) {
   2514     return (EFI_INVALID_PARAMETER);
   2515   }
   2516 
   2517   if (*Path == L'.' && *(Path+1) == L'\\') {
   2518     Path+=2;
   2519   }
   2520 
   2521   //
   2522   // convert a local path to an absolute path
   2523   //
   2524   if (StrStr(Path, L":") == NULL) {
   2525     CurDir = EfiShellGetCurDir(NULL);
   2526     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2527     StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
   2528     StrnCatGrow(&Path2, &Path2Size, L"\\", 0);
   2529     if (*Path == L'\\') {
   2530       Path++;
   2531       while (PathRemoveLastItem(Path2)) ;
   2532     }
   2533     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
   2534     StrnCatGrow(&Path2, &Path2Size, Path, 0);
   2535   } else {
   2536     ASSERT(Path2 == NULL);
   2537     StrnCatGrow(&Path2, NULL, Path, 0);
   2538   }
   2539 
   2540   PathCleanUpDirectories (Path2);
   2541 
   2542   //
   2543   // do the search
   2544   //
   2545   Status = EfiShellFindFiles(Path2, FileList);
   2546 
   2547   FreePool(Path2);
   2548 
   2549   if (EFI_ERROR(Status)) {
   2550     return (Status);
   2551   }
   2552 
   2553   Found = FALSE;
   2554   //
   2555   // We had no errors so open all the files (that are not already opened...)
   2556   //
   2557   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
   2558       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
   2559       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
   2560      ){
   2561     if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {
   2562       ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
   2563       Found = TRUE;
   2564     }
   2565   }
   2566 
   2567   if (!Found) {
   2568     return (EFI_NOT_FOUND);
   2569   }
   2570   return(EFI_SUCCESS);
   2571 }
   2572 
   2573 /**
   2574   Gets the environment variable and Attributes, or list of environment variables.  Can be
   2575   used instead of GetEnv().
   2576 
   2577   This function returns the current value of the specified environment variable and
   2578   the Attributes. If no variable name was specified, then all of the known
   2579   variables will be returned.
   2580 
   2581   @param[in] Name               A pointer to the environment variable name. If Name is NULL,
   2582                                 then the function will return all of the defined shell
   2583                                 environment variables. In the case where multiple environment
   2584                                 variables are being returned, each variable will be terminated
   2585                                 by a NULL, and the list will be terminated by a double NULL.
   2586   @param[out] Attributes        If not NULL, a pointer to the returned attributes bitmask for
   2587                                 the environment variable. In the case where Name is NULL, and
   2588                                 multiple environment variables are being returned, Attributes
   2589                                 is undefined.
   2590 
   2591   @retval NULL                  The environment variable doesn't exist.
   2592   @return                       A non-NULL value points to the variable's value. The returned
   2593                                 pointer does not need to be freed by the caller.
   2594 **/
   2595 CONST CHAR16 *
   2596 EFIAPI
   2597 EfiShellGetEnvEx(
   2598   IN  CONST CHAR16 *Name,
   2599   OUT       UINT32 *Attributes OPTIONAL
   2600   )
   2601 {
   2602   EFI_STATUS  Status;
   2603   VOID        *Buffer;
   2604   UINTN       Size;
   2605   LIST_ENTRY  List;
   2606   ENV_VAR_LIST *Node;
   2607   CHAR16      *CurrentWriteLocation;
   2608 
   2609   Size = 0;
   2610   Buffer = NULL;
   2611 
   2612   if (Name == NULL) {
   2613     //
   2614     // Get all our environment variables
   2615     //
   2616     InitializeListHead(&List);
   2617     Status = GetEnvironmentVariableList(&List);
   2618     if (EFI_ERROR(Status)){
   2619       return (NULL);
   2620     }
   2621 
   2622     //
   2623     // Build the semi-colon delimited list. (2 passes)
   2624     //
   2625     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
   2626       ; !IsNull(&List, &Node->Link)
   2627       ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
   2628      ){
   2629       ASSERT(Node->Key != NULL);
   2630       Size += StrSize(Node->Key);
   2631     }
   2632 
   2633     Size += 2*sizeof(CHAR16);
   2634 
   2635     Buffer = AllocateZeroPool(Size);
   2636     if (Buffer == NULL) {
   2637       if (!IsListEmpty (&List)) {
   2638         FreeEnvironmentVariableList(&List);
   2639       }
   2640       return (NULL);
   2641     }
   2642     CurrentWriteLocation = (CHAR16*)Buffer;
   2643 
   2644     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
   2645       ; !IsNull(&List, &Node->Link)
   2646       ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
   2647      ){
   2648       ASSERT(Node->Key != NULL);
   2649       StrCpyS( CurrentWriteLocation,
   2650                 (Size)/sizeof(CHAR16) - (CurrentWriteLocation - ((CHAR16*)Buffer)),
   2651                 Node->Key
   2652                 );
   2653       CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
   2654     }
   2655 
   2656     //
   2657     // Free the list...
   2658     //
   2659     if (!IsListEmpty (&List)) {
   2660       FreeEnvironmentVariableList(&List);
   2661     }
   2662   } else {
   2663     //
   2664     // We are doing a specific environment variable
   2665     //
   2666 
   2667     //
   2668     // get the size we need for this EnvVariable
   2669     //
   2670     Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
   2671     if (Status == EFI_BUFFER_TOO_SMALL) {
   2672       //
   2673       // Allocate the space and recall the get function
   2674       //
   2675       Buffer = AllocateZeroPool(Size);
   2676       Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
   2677     }
   2678     //
   2679     // we didnt get it (might not exist)
   2680     // free the memory if we allocated any and return NULL
   2681     //
   2682     if (EFI_ERROR(Status)) {
   2683       if (Buffer != NULL) {
   2684         FreePool(Buffer);
   2685       }
   2686       return (NULL);
   2687     }
   2688   }
   2689 
   2690   //
   2691   // return the buffer
   2692   //
   2693   return (AddBufferToFreeList(Buffer));
   2694 }
   2695 
   2696 /**
   2697   Gets either a single or list of environment variables.
   2698 
   2699   If name is not NULL then this function returns the current value of the specified
   2700   environment variable.
   2701 
   2702   If Name is NULL, then a list of all environment variable names is returned.  Each is a
   2703   NULL terminated string with a double NULL terminating the list.
   2704 
   2705   @param Name                   A pointer to the environment variable name.  If
   2706                                 Name is NULL, then the function will return all
   2707                                 of the defined shell environment variables.  In
   2708                                 the case where multiple environment variables are
   2709                                 being returned, each variable will be terminated by
   2710                                 a NULL, and the list will be terminated by a double
   2711                                 NULL.
   2712 
   2713   @retval !=NULL                A pointer to the returned string.
   2714                                 The returned pointer does not need to be freed by the caller.
   2715 
   2716   @retval NULL                  The environment variable doesn't exist or there are
   2717                                 no environment variables.
   2718 **/
   2719 CONST CHAR16 *
   2720 EFIAPI
   2721 EfiShellGetEnv(
   2722   IN CONST CHAR16 *Name
   2723   )
   2724 {
   2725   return (EfiShellGetEnvEx(Name, NULL));
   2726 }
   2727 
   2728 /**
   2729   Internal variable setting function.  Allows for setting of the read only variables.
   2730 
   2731   @param Name                   Points to the NULL-terminated environment variable name.
   2732   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
   2733                                 empty string then the environment variable is deleted.
   2734   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
   2735 
   2736   @retval EFI_SUCCESS           The environment variable was successfully updated.
   2737 **/
   2738 EFI_STATUS
   2739 EFIAPI
   2740 InternalEfiShellSetEnv(
   2741   IN CONST CHAR16 *Name,
   2742   IN CONST CHAR16 *Value,
   2743   IN BOOLEAN Volatile
   2744   )
   2745 {
   2746   if (Value == NULL || StrLen(Value) == 0) {
   2747     return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name));
   2748   } else {
   2749     SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
   2750     if (Volatile) {
   2751       return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value));
   2752     } else {
   2753       return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value));
   2754     }
   2755   }
   2756 }
   2757 
   2758 /**
   2759   Sets the environment variable.
   2760 
   2761   This function changes the current value of the specified environment variable. If the
   2762   environment variable exists and the Value is an empty string, then the environment
   2763   variable is deleted. If the environment variable exists and the Value is not an empty
   2764   string, then the value of the environment variable is changed. If the environment
   2765   variable does not exist and the Value is an empty string, there is no action. If the
   2766   environment variable does not exist and the Value is a non-empty string, then the
   2767   environment variable is created and assigned the specified value.
   2768 
   2769   For a description of volatile and non-volatile environment variables, see UEFI Shell
   2770   2.0 specification section 3.6.1.
   2771 
   2772   @param Name                   Points to the NULL-terminated environment variable name.
   2773   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
   2774                                 empty string then the environment variable is deleted.
   2775   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
   2776 
   2777   @retval EFI_SUCCESS           The environment variable was successfully updated.
   2778 **/
   2779 EFI_STATUS
   2780 EFIAPI
   2781 EfiShellSetEnv(
   2782   IN CONST CHAR16 *Name,
   2783   IN CONST CHAR16 *Value,
   2784   IN BOOLEAN Volatile
   2785   )
   2786 {
   2787   if (Name == NULL || *Name == CHAR_NULL) {
   2788     return (EFI_INVALID_PARAMETER);
   2789   }
   2790   //
   2791   // Make sure we dont 'set' a predefined read only variable
   2792   //
   2793   if (gUnicodeCollation->StriColl(
   2794         gUnicodeCollation,
   2795         (CHAR16*)Name,
   2796         L"cwd") == 0
   2797     ||gUnicodeCollation->StriColl(
   2798         gUnicodeCollation,
   2799         (CHAR16*)Name,
   2800         L"Lasterror") == 0
   2801     ||gUnicodeCollation->StriColl(
   2802         gUnicodeCollation,
   2803         (CHAR16*)Name,
   2804         L"profiles") == 0
   2805     ||gUnicodeCollation->StriColl(
   2806         gUnicodeCollation,
   2807         (CHAR16*)Name,
   2808         L"uefishellsupport") == 0
   2809     ||gUnicodeCollation->StriColl(
   2810         gUnicodeCollation,
   2811         (CHAR16*)Name,
   2812         L"uefishellversion") == 0
   2813     ||gUnicodeCollation->StriColl(
   2814         gUnicodeCollation,
   2815         (CHAR16*)Name,
   2816         L"uefiversion") == 0
   2817        ){
   2818     return (EFI_INVALID_PARAMETER);
   2819   }
   2820   return (InternalEfiShellSetEnv(Name, Value, Volatile));
   2821 }
   2822 
   2823 /**
   2824   Returns the current directory on the specified device.
   2825 
   2826   If FileSystemMapping is NULL, it returns the current working directory. If the
   2827   FileSystemMapping is not NULL, it returns the current directory associated with the
   2828   FileSystemMapping. In both cases, the returned name includes the file system
   2829   mapping (i.e. fs0:\current-dir).
   2830 
   2831   Note that the current directory string should exclude the tailing backslash character.
   2832 
   2833   @param FileSystemMapping      A pointer to the file system mapping. If NULL,
   2834                                 then the current working directory is returned.
   2835 
   2836   @retval !=NULL                The current directory.
   2837   @retval NULL                  Current directory does not exist.
   2838 **/
   2839 CONST CHAR16 *
   2840 EFIAPI
   2841 EfiShellGetCurDir(
   2842   IN CONST CHAR16 *FileSystemMapping OPTIONAL
   2843   )
   2844 {
   2845   CHAR16  *PathToReturn;
   2846   UINTN   Size;
   2847   SHELL_MAP_LIST *MapListItem;
   2848   if (!IsListEmpty(&gShellMapList.Link)) {
   2849     //
   2850     // if parameter is NULL, use current
   2851     //
   2852     if (FileSystemMapping == NULL) {
   2853       return (EfiShellGetEnv(L"cwd"));
   2854     } else {
   2855       Size = 0;
   2856       PathToReturn = NULL;
   2857       MapListItem = ShellCommandFindMapItem(FileSystemMapping);
   2858       if (MapListItem != NULL) {
   2859         ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
   2860         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
   2861         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
   2862       }
   2863     }
   2864     return (AddBufferToFreeList(PathToReturn));
   2865   } else {
   2866     return (NULL);
   2867   }
   2868 }
   2869 
   2870 /**
   2871   Changes the current directory on the specified device.
   2872 
   2873   If the FileSystem is NULL, and the directory Dir does not contain a file system's
   2874   mapped name, this function changes the current working directory.
   2875 
   2876   If the FileSystem is NULL and the directory Dir contains a mapped name, then the
   2877   current file system and the current directory on that file system are changed.
   2878 
   2879   If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
   2880   system.
   2881 
   2882   If FileSystem is not NULL and Dir is not NULL, then this function changes the current
   2883   directory on the specified file system.
   2884 
   2885   If the current working directory or the current working file system is changed then the
   2886   %cwd% environment variable will be updated
   2887 
   2888   Note that the current directory string should exclude the tailing backslash character.
   2889 
   2890   @param FileSystem             A pointer to the file system's mapped name. If NULL, then the current working
   2891                                 directory is changed.
   2892   @param Dir                    Points to the NULL-terminated directory on the device specified by FileSystem.
   2893 
   2894   @retval EFI_SUCCESS           The operation was sucessful
   2895   @retval EFI_NOT_FOUND         The file system could not be found
   2896 **/
   2897 EFI_STATUS
   2898 EFIAPI
   2899 EfiShellSetCurDir(
   2900   IN CONST CHAR16 *FileSystem OPTIONAL,
   2901   IN CONST CHAR16 *Dir
   2902   )
   2903 {
   2904   CHAR16          *MapName;
   2905   SHELL_MAP_LIST  *MapListItem;
   2906   UINTN           Size;
   2907   EFI_STATUS      Status;
   2908   CHAR16          *TempString;
   2909   CHAR16          *DirectoryName;
   2910   UINTN           TempLen;
   2911 
   2912   Size          = 0;
   2913   MapName       = NULL;
   2914   MapListItem   = NULL;
   2915   TempString    = NULL;
   2916   DirectoryName = NULL;
   2917 
   2918   if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {
   2919     return (EFI_INVALID_PARAMETER);
   2920   }
   2921 
   2922   if (IsListEmpty(&gShellMapList.Link)){
   2923     return (EFI_NOT_FOUND);
   2924   }
   2925 
   2926   DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
   2927   ASSERT(DirectoryName != NULL);
   2928 
   2929   PathCleanUpDirectories(DirectoryName);
   2930 
   2931   if (FileSystem == NULL) {
   2932     //
   2933     // determine the file system mapping to use
   2934     //
   2935     if (StrStr(DirectoryName, L":") != NULL) {
   2936       ASSERT(MapName == NULL);
   2937       MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
   2938     }
   2939     //
   2940     // find the file system mapping's entry in the list
   2941     // or use current
   2942     //
   2943     if (MapName != NULL) {
   2944       MapListItem = ShellCommandFindMapItem(MapName);
   2945 
   2946       //
   2947       // make that the current file system mapping
   2948       //
   2949       if (MapListItem != NULL) {
   2950         gShellCurDir = MapListItem;
   2951       }
   2952     } else {
   2953       MapListItem = gShellCurDir;
   2954     }
   2955 
   2956     if (MapListItem == NULL) {
   2957       FreePool (DirectoryName);
   2958       SHELL_FREE_NON_NULL(MapName);
   2959       return (EFI_NOT_FOUND);
   2960     }
   2961 
   2962     //
   2963     // now update the MapListItem's current directory
   2964     //
   2965     if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
   2966       FreePool(MapListItem->CurrentDirectoryPath);
   2967       MapListItem->CurrentDirectoryPath = NULL;
   2968     }
   2969     if (MapName != NULL) {
   2970       TempLen = StrLen(MapName);
   2971       if (TempLen != StrLen(DirectoryName)) {
   2972         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   2973         MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
   2974       }
   2975       FreePool (MapName);
   2976     } else {
   2977       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   2978       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
   2979     }
   2980     if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
   2981       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   2982       if (MapListItem->CurrentDirectoryPath != NULL) {
   2983         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
   2984       }
   2985     }
   2986   } else {
   2987     //
   2988     // cant have a mapping in the directory...
   2989     //
   2990     if (StrStr(DirectoryName, L":") != NULL) {
   2991       FreePool (DirectoryName);
   2992       return (EFI_INVALID_PARAMETER);
   2993     }
   2994     //
   2995     // FileSystem != NULL
   2996     //
   2997     MapListItem = ShellCommandFindMapItem(FileSystem);
   2998     if (MapListItem == NULL) {
   2999       FreePool (DirectoryName);
   3000       return (EFI_INVALID_PARAMETER);
   3001     }
   3002 //    gShellCurDir = MapListItem;
   3003     if (DirectoryName != NULL) {
   3004       //
   3005       // change current dir on that file system
   3006       //
   3007 
   3008       if (MapListItem->CurrentDirectoryPath != NULL) {
   3009         FreePool(MapListItem->CurrentDirectoryPath);
   3010         DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
   3011       }
   3012 //      ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3013 //      MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
   3014       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3015       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
   3016       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3017       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
   3018       if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') {
   3019         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
   3020         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
   3021       }
   3022     }
   3023   }
   3024   FreePool (DirectoryName);
   3025   //
   3026   // if updated the current directory then update the environment variable
   3027   //
   3028   if (MapListItem == gShellCurDir) {
   3029     Size = 0;
   3030     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   3031     StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
   3032     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
   3033     StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
   3034     Status =  InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
   3035     FreePool(TempString);
   3036     return (Status);
   3037   }
   3038   return(EFI_SUCCESS);
   3039 }
   3040 
   3041 /**
   3042   Return help information about a specific command.
   3043 
   3044   This function returns the help information for the specified command. The help text
   3045   can be internal to the shell or can be from a UEFI Shell manual page.
   3046 
   3047   If Sections is specified, then each section name listed will be compared in a casesensitive
   3048   manner, to the section names described in Appendix B. If the section exists,
   3049   it will be appended to the returned help text. If the section does not exist, no
   3050   information will be returned. If Sections is NULL, then all help text information
   3051   available will be returned.
   3052 
   3053   @param Command                Points to the NULL-terminated UEFI Shell command name.
   3054   @param Sections               Points to the NULL-terminated comma-delimited
   3055                                 section names to return. If NULL, then all
   3056                                 sections will be returned.
   3057   @param HelpText               On return, points to a callee-allocated buffer
   3058                                 containing all specified help text.
   3059 
   3060   @retval EFI_SUCCESS           The help text was returned.
   3061   @retval EFI_OUT_OF_RESOURCES  The necessary buffer could not be allocated to hold the
   3062                                 returned help text.
   3063   @retval EFI_INVALID_PARAMETER HelpText is NULL
   3064   @retval EFI_NOT_FOUND         There is no help text available for Command.
   3065 **/
   3066 EFI_STATUS
   3067 EFIAPI
   3068 EfiShellGetHelpText(
   3069   IN CONST CHAR16 *Command,
   3070   IN CONST CHAR16 *Sections OPTIONAL,
   3071   OUT CHAR16 **HelpText
   3072   )
   3073 {
   3074   CONST CHAR16  *ManFileName;
   3075   CHAR16        *FixCommand;
   3076   EFI_STATUS    Status;
   3077 
   3078   ASSERT(HelpText != NULL);
   3079   FixCommand = NULL;
   3080 
   3081   ManFileName = ShellCommandGetManFileNameHandler(Command);
   3082 
   3083   if (ManFileName != NULL) {
   3084     return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
   3085   } else {
   3086     if ((StrLen(Command)> 4)
   3087     && (Command[StrLen(Command)-1] == L'i' || Command[StrLen(Command)-1] == L'I')
   3088     && (Command[StrLen(Command)-2] == L'f' || Command[StrLen(Command)-2] == L'F')
   3089     && (Command[StrLen(Command)-3] == L'e' || Command[StrLen(Command)-3] == L'E')
   3090     && (Command[StrLen(Command)-4] == L'.')
   3091     ) {
   3092       FixCommand = AllocateZeroPool(StrSize(Command) - 4 * sizeof (CHAR16));
   3093       ASSERT(FixCommand != NULL);
   3094 
   3095       StrnCpyS( FixCommand,
   3096                 (StrSize(Command) - 4 * sizeof (CHAR16))/sizeof(CHAR16),
   3097                 Command,
   3098                 StrLen(Command)-4
   3099                 );
   3100       Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText);
   3101       FreePool(FixCommand);
   3102       return Status;
   3103     } else {
   3104       return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
   3105     }
   3106   }
   3107 }
   3108 
   3109 /**
   3110   Gets the enable status of the page break output mode.
   3111 
   3112   User can use this function to determine current page break mode.
   3113 
   3114   @retval TRUE                  The page break output mode is enabled.
   3115   @retval FALSE                 The page break output mode is disabled.
   3116 **/
   3117 BOOLEAN
   3118 EFIAPI
   3119 EfiShellGetPageBreak(
   3120   VOID
   3121   )
   3122 {
   3123   return(ShellInfoObject.PageBreakEnabled);
   3124 }
   3125 
   3126 /**
   3127   Judges whether the active shell is the root shell.
   3128 
   3129   This function makes the user to know that whether the active Shell is the root shell.
   3130 
   3131   @retval TRUE                  The active Shell is the root Shell.
   3132   @retval FALSE                 The active Shell is NOT the root Shell.
   3133 **/
   3134 BOOLEAN
   3135 EFIAPI
   3136 EfiShellIsRootShell(
   3137   VOID
   3138   )
   3139 {
   3140   return(ShellInfoObject.RootShellInstance);
   3141 }
   3142 
   3143 /**
   3144   function to return a semi-colon delimeted list of all alias' in the current shell
   3145 
   3146   up to caller to free the memory.
   3147 
   3148   @retval NULL    No alias' were found
   3149   @retval NULL    An error ocurred getting alias'
   3150   @return !NULL   a list of all alias'
   3151 **/
   3152 CHAR16 *
   3153 EFIAPI
   3154 InternalEfiShellGetListAlias(
   3155   )
   3156 {
   3157 
   3158   EFI_STATUS        Status;
   3159   EFI_GUID          Guid;
   3160   CHAR16            *VariableName;
   3161   UINTN             NameSize;
   3162   UINTN             NameBufferSize;
   3163   CHAR16            *RetVal;
   3164   UINTN             RetSize;
   3165 
   3166   NameBufferSize = INIT_NAME_BUFFER_SIZE;
   3167   VariableName  = AllocateZeroPool(NameBufferSize);
   3168   RetSize       = 0;
   3169   RetVal        = NULL;
   3170 
   3171   if (VariableName == NULL) {
   3172     return (NULL);
   3173   }
   3174 
   3175   VariableName[0] = CHAR_NULL;
   3176 
   3177   while (TRUE) {
   3178     NameSize = NameBufferSize;
   3179     Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
   3180     if (Status == EFI_NOT_FOUND){
   3181       break;
   3182     } else if (Status == EFI_BUFFER_TOO_SMALL) {
   3183       NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
   3184       SHELL_FREE_NON_NULL(VariableName);
   3185       VariableName = AllocateZeroPool(NameBufferSize);
   3186       if (VariableName == NULL) {
   3187         Status = EFI_OUT_OF_RESOURCES;
   3188         SHELL_FREE_NON_NULL(RetVal);
   3189         RetVal = NULL;
   3190         break;
   3191       }
   3192 
   3193       NameSize = NameBufferSize;
   3194       Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
   3195     }
   3196 
   3197     if (EFI_ERROR (Status)) {
   3198       SHELL_FREE_NON_NULL(RetVal);
   3199       RetVal = NULL;
   3200       break;
   3201     }
   3202 
   3203     if (CompareGuid(&Guid, &gShellAliasGuid)){
   3204       ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
   3205       RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
   3206       RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
   3207     } // compare guid
   3208   } // while
   3209   SHELL_FREE_NON_NULL(VariableName);
   3210 
   3211   return (RetVal);
   3212 }
   3213 
   3214 /**
   3215   Convert a null-terminated unicode string, in-place, to all lowercase.
   3216   Then return it.
   3217 
   3218   @param  Str    The null-terminated string to be converted to all lowercase.
   3219 
   3220   @return        The null-terminated string converted into all lowercase.
   3221 **/
   3222 CHAR16 *
   3223 ToLower (
   3224   CHAR16 *Str
   3225   )
   3226 {
   3227   UINTN Index;
   3228 
   3229   for (Index = 0; Str[Index] != L'\0'; Index++) {
   3230     if (Str[Index] >= L'A' && Str[Index] <= L'Z') {
   3231       Str[Index] -= (CHAR16)(L'A' - L'a');
   3232     }
   3233   }
   3234   return Str;
   3235 }
   3236 
   3237 /**
   3238   This function returns the command associated with a alias or a list of all
   3239   alias'.
   3240 
   3241   @param[in] Alias              Points to the NULL-terminated shell alias.
   3242                                 If this parameter is NULL, then all
   3243                                 aliases will be returned in ReturnedData.
   3244   @param[out] Volatile          upon return of a single command if TRUE indicates
   3245                                 this is stored in a volatile fashion.  FALSE otherwise.
   3246 
   3247   @return                      	If Alias is not NULL, it will return a pointer to
   3248                                 the NULL-terminated command for that alias.
   3249                                 If Alias is NULL, ReturnedData points to a ';'
   3250                                 delimited list of alias (e.g.
   3251                                 ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
   3252   @retval NULL                  an error ocurred
   3253   @retval NULL                  Alias was not a valid Alias
   3254 **/
   3255 CONST CHAR16 *
   3256 EFIAPI
   3257 EfiShellGetAlias(
   3258   IN  CONST CHAR16 *Alias,
   3259   OUT BOOLEAN      *Volatile OPTIONAL
   3260   )
   3261 {
   3262   CHAR16      *RetVal;
   3263   UINTN       RetSize;
   3264   UINT32      Attribs;
   3265   EFI_STATUS  Status;
   3266   CHAR16      *AliasLower;
   3267   CHAR16      *AliasVal;
   3268 
   3269   // Convert to lowercase to make aliases case-insensitive
   3270   if (Alias != NULL) {
   3271     AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
   3272     ASSERT (AliasLower != NULL);
   3273     ToLower (AliasLower);
   3274 
   3275     if (Volatile == NULL) {
   3276       GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL);
   3277       FreePool(AliasLower);
   3278       return (AddBufferToFreeList(AliasVal));
   3279     }
   3280     RetSize = 0;
   3281     RetVal = NULL;
   3282     Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
   3283     if (Status == EFI_BUFFER_TOO_SMALL) {
   3284       RetVal = AllocateZeroPool(RetSize);
   3285       Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
   3286     }
   3287     if (EFI_ERROR(Status)) {
   3288       if (RetVal != NULL) {
   3289         FreePool(RetVal);
   3290       }
   3291       FreePool(AliasLower);
   3292       return (NULL);
   3293     }
   3294     if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
   3295       *Volatile = FALSE;
   3296     } else {
   3297       *Volatile = TRUE;
   3298     }
   3299 
   3300     FreePool (AliasLower);
   3301     return (AddBufferToFreeList(RetVal));
   3302   }
   3303   return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
   3304 }
   3305 
   3306 /**
   3307   Changes a shell command alias.
   3308 
   3309   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
   3310 
   3311   this function does not check for built in alias'.
   3312 
   3313   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
   3314   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
   3315                                 Command refers to an alias, that alias will be deleted.
   3316   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
   3317                                 Alias being set will be stored in a non-volatile fashion.
   3318 
   3319   @retval EFI_SUCCESS           Alias created or deleted successfully.
   3320   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
   3321 **/
   3322 EFI_STATUS
   3323 EFIAPI
   3324 InternalSetAlias(
   3325   IN CONST CHAR16 *Command,
   3326   IN CONST CHAR16 *Alias,
   3327   IN BOOLEAN Volatile
   3328   )
   3329 {
   3330   EFI_STATUS  Status;
   3331   CHAR16      *AliasLower;
   3332 
   3333   // Convert to lowercase to make aliases case-insensitive
   3334   if (Alias != NULL) {
   3335     AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
   3336     ASSERT (AliasLower != NULL);
   3337     ToLower (AliasLower);
   3338   } else {
   3339     AliasLower = NULL;
   3340   }
   3341 
   3342   //
   3343   // We must be trying to remove one if Alias is NULL
   3344   //
   3345   if (Alias == NULL) {
   3346     //
   3347     // remove an alias (but passed in COMMAND parameter)
   3348     //
   3349     Status = (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));
   3350   } else {
   3351     //
   3352     // Add and replace are the same
   3353     //
   3354 
   3355     // We dont check the error return on purpose since the variable may not exist.
   3356     gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);
   3357 
   3358     Status = (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));
   3359   }
   3360 
   3361   if (Alias != NULL) {
   3362     FreePool (AliasLower);
   3363   }
   3364   return Status;
   3365 }
   3366 
   3367 /**
   3368   Changes a shell command alias.
   3369 
   3370   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
   3371 
   3372 
   3373   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
   3374   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
   3375                                 Command refers to an alias, that alias will be deleted.
   3376   @param[in] Replace            If TRUE and the alias already exists, then the existing alias will be replaced. If
   3377                                 FALSE and the alias already exists, then the existing alias is unchanged and
   3378                                 EFI_ACCESS_DENIED is returned.
   3379   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
   3380                                 Alias being set will be stored in a non-volatile fashion.
   3381 
   3382   @retval EFI_SUCCESS           Alias created or deleted successfully.
   3383   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
   3384   @retval EFI_ACCESS_DENIED     The alias is a built-in alias or already existed and Replace was set to
   3385                                 FALSE.
   3386   @retval EFI_INVALID_PARAMETER Command is null or the empty string.
   3387 **/
   3388 EFI_STATUS
   3389 EFIAPI
   3390 EfiShellSetAlias(
   3391   IN CONST CHAR16 *Command,
   3392   IN CONST CHAR16 *Alias,
   3393   IN BOOLEAN Replace,
   3394   IN BOOLEAN Volatile
   3395   )
   3396 {
   3397   if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
   3398     //
   3399     // cant set over a built in alias
   3400     //
   3401     return (EFI_ACCESS_DENIED);
   3402   } else if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
   3403     //
   3404     // Command is null or empty
   3405     //
   3406     return (EFI_INVALID_PARAMETER);
   3407   } else if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
   3408     //
   3409     // Alias already exists, Replace not set
   3410     //
   3411     return (EFI_ACCESS_DENIED);
   3412   } else {
   3413     return (InternalSetAlias(Command, Alias, Volatile));
   3414   }
   3415 }
   3416 
   3417 // Pure FILE_HANDLE operations are passed to FileHandleLib
   3418 // these functions are indicated by the *
   3419 EFI_SHELL_PROTOCOL         mShellProtocol = {
   3420   EfiShellExecute,
   3421   EfiShellGetEnv,
   3422   EfiShellSetEnv,
   3423   EfiShellGetAlias,
   3424   EfiShellSetAlias,
   3425   EfiShellGetHelpText,
   3426   EfiShellGetDevicePathFromMap,
   3427   EfiShellGetMapFromDevicePath,
   3428   EfiShellGetDevicePathFromFilePath,
   3429   EfiShellGetFilePathFromDevicePath,
   3430   EfiShellSetMap,
   3431   EfiShellGetCurDir,
   3432   EfiShellSetCurDir,
   3433   EfiShellOpenFileList,
   3434   EfiShellFreeFileList,
   3435   EfiShellRemoveDupInFileList,
   3436   EfiShellBatchIsActive,
   3437   EfiShellIsRootShell,
   3438   EfiShellEnablePageBreak,
   3439   EfiShellDisablePageBreak,
   3440   EfiShellGetPageBreak,
   3441   EfiShellGetDeviceName,
   3442   (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo,         //*
   3443   (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo,         //*
   3444   EfiShellOpenFileByName,
   3445   EfiShellClose,
   3446   EfiShellCreateFile,
   3447   (EFI_SHELL_READ_FILE)FileHandleRead,                //*
   3448   (EFI_SHELL_WRITE_FILE)FileHandleWrite,              //*
   3449   (EFI_SHELL_DELETE_FILE)FileHandleDelete,            //*
   3450   EfiShellDeleteFileByName,
   3451   (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, //*
   3452   (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, //*
   3453   (EFI_SHELL_FLUSH_FILE)FileHandleFlush,              //*
   3454   EfiShellFindFiles,
   3455   EfiShellFindFilesInDir,
   3456   (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize,         //*
   3457   EfiShellOpenRoot,
   3458   EfiShellOpenRootByHandle,
   3459   NULL,
   3460   SHELL_MAJOR_VERSION,
   3461   SHELL_MINOR_VERSION,
   3462 
   3463   // New for UEFI Shell 2.1
   3464   EfiShellRegisterGuidName,
   3465   EfiShellGetGuidName,
   3466   EfiShellGetGuidFromName,
   3467   EfiShellGetEnvEx
   3468 };
   3469 
   3470 /**
   3471   Function to create and install on the current handle.
   3472 
   3473   Will overwrite any existing ShellProtocols in the system to be sure that
   3474   the current shell is in control.
   3475 
   3476   This must be removed via calling CleanUpShellProtocol().
   3477 
   3478   @param[in, out] NewShell   The pointer to the pointer to the structure
   3479   to install.
   3480 
   3481   @retval EFI_SUCCESS     The operation was successful.
   3482   @return                 An error from LocateHandle, CreateEvent, or other core function.
   3483 **/
   3484 EFI_STATUS
   3485 EFIAPI
   3486 CreatePopulateInstallShellProtocol (
   3487   IN OUT EFI_SHELL_PROTOCOL  **NewShell
   3488   )
   3489 {
   3490   EFI_STATUS                  Status;
   3491   UINTN                       BufferSize;
   3492   EFI_HANDLE                  *Buffer;
   3493   UINTN                       HandleCounter;
   3494   SHELL_PROTOCOL_HANDLE_LIST  *OldProtocolNode;
   3495 
   3496   if (NewShell == NULL) {
   3497     return (EFI_INVALID_PARAMETER);
   3498   }
   3499 
   3500   BufferSize = 0;
   3501   Buffer = NULL;
   3502   OldProtocolNode = NULL;
   3503   InitializeListHead(&ShellInfoObject.OldShellList.Link);
   3504 
   3505   //
   3506   // Initialize EfiShellProtocol object...
   3507   //
   3508   Status = gBS->CreateEvent(0,
   3509                             0,
   3510                             NULL,
   3511                             NULL,
   3512                             &mShellProtocol.ExecutionBreak);
   3513   if (EFI_ERROR(Status)) {
   3514     return (Status);
   3515   }
   3516 
   3517   //
   3518   // Get the size of the buffer we need.
   3519   //
   3520   Status = gBS->LocateHandle(ByProtocol,
   3521                              &gEfiShellProtocolGuid,
   3522                              NULL,
   3523                              &BufferSize,
   3524                              Buffer);
   3525   if (Status == EFI_BUFFER_TOO_SMALL) {
   3526     //
   3527     // Allocate and recall with buffer of correct size
   3528     //
   3529     Buffer = AllocateZeroPool(BufferSize);
   3530     if (Buffer == NULL) {
   3531       return (EFI_OUT_OF_RESOURCES);
   3532     }
   3533     Status = gBS->LocateHandle(ByProtocol,
   3534                                &gEfiShellProtocolGuid,
   3535                                NULL,
   3536                                &BufferSize,
   3537                                Buffer);
   3538     if (EFI_ERROR(Status)) {
   3539       FreePool(Buffer);
   3540       return (Status);
   3541     }
   3542     //
   3543     // now overwrite each of them, but save the info to restore when we end.
   3544     //
   3545     for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
   3546       OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
   3547       ASSERT(OldProtocolNode != NULL);
   3548       Status = gBS->OpenProtocol(Buffer[HandleCounter],
   3549                                 &gEfiShellProtocolGuid,
   3550                                 (VOID **) &(OldProtocolNode->Interface),
   3551                                 gImageHandle,
   3552                                 NULL,
   3553                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL
   3554                                );
   3555       if (!EFI_ERROR(Status)) {
   3556         //
   3557         // reinstall over the old one...
   3558         //
   3559         OldProtocolNode->Handle = Buffer[HandleCounter];
   3560         Status = gBS->ReinstallProtocolInterface(
   3561                             OldProtocolNode->Handle,
   3562                             &gEfiShellProtocolGuid,
   3563                             OldProtocolNode->Interface,
   3564                             (VOID*)(&mShellProtocol));
   3565         if (!EFI_ERROR(Status)) {
   3566           //
   3567           // we reinstalled sucessfully.  log this so we can reverse it later.
   3568           //
   3569 
   3570           //
   3571           // add to the list for subsequent...
   3572           //
   3573           InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
   3574         }
   3575       }
   3576     }
   3577     FreePool(Buffer);
   3578   } else if (Status == EFI_NOT_FOUND) {
   3579     ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
   3580     //
   3581     // no one else published yet.  just publish it ourselves.
   3582     //
   3583     Status = gBS->InstallProtocolInterface (
   3584                       &gImageHandle,
   3585                       &gEfiShellProtocolGuid,
   3586                       EFI_NATIVE_INTERFACE,
   3587                       (VOID*)(&mShellProtocol));
   3588   }
   3589 
   3590   if (PcdGetBool(PcdShellSupportOldProtocols)){
   3591     ///@todo support ShellEnvironment2
   3592     ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
   3593   }
   3594 
   3595   if (!EFI_ERROR(Status)) {
   3596     *NewShell = &mShellProtocol;
   3597   }
   3598   return (Status);
   3599 }
   3600 
   3601 /**
   3602   Opposite of CreatePopulateInstallShellProtocol.
   3603 
   3604   Free all memory and restore the system to the state it was in before calling
   3605   CreatePopulateInstallShellProtocol.
   3606 
   3607   @param[in, out] NewShell   The pointer to the new shell protocol structure.
   3608 
   3609   @retval EFI_SUCCESS       The operation was successful.
   3610 **/
   3611 EFI_STATUS
   3612 EFIAPI
   3613 CleanUpShellProtocol (
   3614   IN OUT EFI_SHELL_PROTOCOL  *NewShell
   3615   )
   3616 {
   3617   EFI_STATUS                        Status;
   3618   SHELL_PROTOCOL_HANDLE_LIST        *Node2;
   3619   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
   3620 
   3621   //
   3622   // if we need to restore old protocols...
   3623   //
   3624   if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
   3625     for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
   3626          ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
   3627          ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
   3628         ){
   3629       RemoveEntryList(&Node2->Link);
   3630       Status = gBS->ReinstallProtocolInterface(Node2->Handle,
   3631                                                &gEfiShellProtocolGuid,
   3632                                                NewShell,
   3633                                                Node2->Interface);
   3634       FreePool(Node2);
   3635     }
   3636   } else {
   3637     //
   3638     // no need to restore
   3639     //
   3640     Status = gBS->UninstallProtocolInterface(gImageHandle,
   3641                                              &gEfiShellProtocolGuid,
   3642                                              NewShell);
   3643   }
   3644   Status = gBS->CloseEvent(NewShell->ExecutionBreak);
   3645   NewShell->ExecutionBreak = NULL;
   3646 
   3647   Status = gBS->OpenProtocol(
   3648     gST->ConsoleInHandle,
   3649     &gEfiSimpleTextInputExProtocolGuid,
   3650     (VOID**)&SimpleEx,
   3651     gImageHandle,
   3652     NULL,
   3653     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   3654 
   3655   if (!EFI_ERROR (Status)) {
   3656     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
   3657     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
   3658     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
   3659     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
   3660     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
   3661     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
   3662     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
   3663     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
   3664   }
   3665   return (Status);
   3666 }
   3667 
   3668 /**
   3669   Notification function for keystrokes.
   3670 
   3671   @param[in] KeyData    The key that was pressed.
   3672 
   3673   @retval EFI_SUCCESS   The operation was successful.
   3674 **/
   3675 EFI_STATUS
   3676 EFIAPI
   3677 NotificationFunction(
   3678   IN EFI_KEY_DATA *KeyData
   3679   )
   3680 {
   3681   if ( ((KeyData->Key.UnicodeChar == L'c') &&
   3682         (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) ||
   3683       (KeyData->Key.UnicodeChar == 3)
   3684       ){
   3685     if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
   3686       return (EFI_UNSUPPORTED);
   3687     }
   3688     return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
   3689   } else if  ((KeyData->Key.UnicodeChar == L's') &&
   3690               (KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))
   3691               ){
   3692     ShellInfoObject.HaltOutput = TRUE;
   3693   }
   3694   return (EFI_SUCCESS);
   3695 }
   3696 
   3697 /**
   3698   Function to start monitoring for CTRL-C using SimpleTextInputEx.  This
   3699   feature's enabled state was not known when the shell initially launched.
   3700 
   3701   @retval EFI_SUCCESS           The feature is enabled.
   3702   @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.
   3703 **/
   3704 EFI_STATUS
   3705 EFIAPI
   3706 InernalEfiShellStartMonitor(
   3707   VOID
   3708   )
   3709 {
   3710   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
   3711   EFI_KEY_DATA                      KeyData;
   3712   EFI_STATUS                        Status;
   3713 
   3714   Status = gBS->OpenProtocol(
   3715     gST->ConsoleInHandle,
   3716     &gEfiSimpleTextInputExProtocolGuid,
   3717     (VOID**)&SimpleEx,
   3718     gImageHandle,
   3719     NULL,
   3720     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
   3721   if (EFI_ERROR(Status)) {
   3722     ShellPrintHiiEx(
   3723       -1,
   3724       -1,
   3725       NULL,
   3726       STRING_TOKEN (STR_SHELL_NO_IN_EX),
   3727       ShellInfoObject.HiiHandle);
   3728     return (EFI_SUCCESS);
   3729   }
   3730 
   3731   if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
   3732     return (EFI_UNSUPPORTED);
   3733   }
   3734 
   3735   KeyData.KeyState.KeyToggleState = 0;
   3736   KeyData.Key.ScanCode            = 0;
   3737   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
   3738   KeyData.Key.UnicodeChar         = L'c';
   3739 
   3740   Status = SimpleEx->RegisterKeyNotify(
   3741     SimpleEx,
   3742     &KeyData,
   3743     NotificationFunction,
   3744     &ShellInfoObject.CtrlCNotifyHandle1);
   3745 
   3746   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
   3747   if (!EFI_ERROR(Status)) {
   3748     Status = SimpleEx->RegisterKeyNotify(
   3749       SimpleEx,
   3750       &KeyData,
   3751       NotificationFunction,
   3752       &ShellInfoObject.CtrlCNotifyHandle2);
   3753   }
   3754   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
   3755   KeyData.Key.UnicodeChar         = 3;
   3756   if (!EFI_ERROR(Status)) {
   3757     Status = SimpleEx->RegisterKeyNotify(
   3758       SimpleEx,
   3759       &KeyData,
   3760       NotificationFunction,
   3761       &ShellInfoObject.CtrlCNotifyHandle3);
   3762   }
   3763   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
   3764   if (!EFI_ERROR(Status)) {
   3765     Status = SimpleEx->RegisterKeyNotify(
   3766       SimpleEx,
   3767       &KeyData,
   3768       NotificationFunction,
   3769       &ShellInfoObject.CtrlCNotifyHandle4);
   3770   }
   3771   return (Status);
   3772 }
   3773 
   3774