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