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