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