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