https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Ray Ni <ray.ni@intel.com> Reviewed-by: Jaben Carsey <jaben.carsey@intel.com>
		
			
				
	
	
		
			3830 lines
		
	
	
		
			128 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3830 lines
		
	
	
		
			128 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Member functions of EFI_SHELL_PROTOCOL and functions for creation,
 | |
|   manipulation, and initialization of EFI_SHELL_PROTOCOL.
 | |
| 
 | |
|   (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
 | |
|   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
 | |
|   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
 | |
|   SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "Shell.h"
 | |
| 
 | |
| #define INIT_NAME_BUFFER_SIZE  128
 | |
| 
 | |
| /**
 | |
|   Close an open file handle.
 | |
| 
 | |
|   This function closes a specified file handle. All "dirty" cached file data is
 | |
|   flushed to the device, and the file is closed. In all cases the handle is
 | |
|   closed.
 | |
| 
 | |
|   @param[in] FileHandle           The file handle to close.
 | |
| 
 | |
|   @retval EFI_SUCCESS             The file handle was closed successfully.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellClose (
 | |
|   IN SHELL_FILE_HANDLE            FileHandle
 | |
|   )
 | |
| {
 | |
|   ShellFileHandleRemove(FileHandle);
 | |
|   return (FileHandleClose(ConvertShellHandleToEfiFileProtocol(FileHandle)));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Internal worker to determine whether there is a BlockIo somewhere
 | |
|   upon the device path specified.
 | |
| 
 | |
|   @param[in] DevicePath    The device path to test.
 | |
| 
 | |
|   @retval TRUE      gEfiBlockIoProtocolGuid was installed on a handle with this device path
 | |
|   @retval FALSE     gEfiBlockIoProtocolGuid was not found.
 | |
| **/
 | |
| BOOLEAN
 | |
| InternalShellProtocolIsBlockIoPresent(
 | |
|   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
 | |
|   )
 | |
| {
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_HANDLE                Handle;
 | |
| 
 | |
|   Handle = NULL;
 | |
| 
 | |
|   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
 | |
|   Status = gBS->LocateDevicePath(&gEfiBlockIoProtocolGuid, &DevicePathCopy, &Handle);
 | |
| 
 | |
|   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
 | |
|     return (TRUE);
 | |
|   }
 | |
|   return (FALSE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Internal worker to determine whether there is a file system somewhere
 | |
|   upon the device path specified.
 | |
| 
 | |
|   @param[in] DevicePath    The device path to test.
 | |
| 
 | |
|   @retval TRUE      gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
 | |
|   @retval FALSE     gEfiSimpleFileSystemProtocolGuid was not found.
 | |
| **/
 | |
| BOOLEAN
 | |
| InternalShellProtocolIsSimpleFileSystemPresent(
 | |
|   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
 | |
|   )
 | |
| {
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
 | |
|   EFI_STATUS                Status;
 | |
|   EFI_HANDLE                Handle;
 | |
| 
 | |
|   Handle = NULL;
 | |
| 
 | |
|   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
 | |
|   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
 | |
| 
 | |
|   if ((Handle != NULL) && (!EFI_ERROR(Status))) {
 | |
|     return (TRUE);
 | |
|   }
 | |
|   return (FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   This function creates a mapping for a device path.
 | |
| 
 | |
|   If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
 | |
| 
 | |
|   @param DevicePath             Points to the device path. If this is NULL and Mapping points to a valid mapping,
 | |
|                                 then the mapping will be deleted.
 | |
|   @param Mapping                Points to the NULL-terminated mapping for the device path.  Must end with a ':'
 | |
| 
 | |
|   @retval EFI_SUCCESS           Mapping created or deleted successfully.
 | |
|   @retval EFI_NO_MAPPING        There is no handle that corresponds exactly to DevicePath. See the
 | |
|                                 boot service function LocateDevicePath().
 | |
|   @retval EFI_ACCESS_DENIED     The mapping is a built-in alias.
 | |
|   @retval EFI_INVALID_PARAMETER Mapping was NULL
 | |
|   @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
 | |
|   @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
 | |
|   @retval EFI_NOT_FOUND         There was no mapping found to delete
 | |
|   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellSetMap(
 | |
|   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
 | |
|   IN CONST CHAR16 *Mapping
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
|   SHELL_MAP_LIST  *MapListNode;
 | |
| 
 | |
|   if (Mapping == NULL){
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   if (Mapping[StrLen(Mapping)-1] != ':') {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Delete the mapping
 | |
|   //
 | |
|   if (DevicePath == NULL) {
 | |
|     if (IsListEmpty(&gShellMapList.Link)) {
 | |
|       return (EFI_NOT_FOUND);
 | |
|     }
 | |
|     for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
 | |
|         ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
 | |
|         ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
 | |
|        ){
 | |
|           if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
 | |
|             RemoveEntryList(&MapListNode->Link);
 | |
|             SHELL_FREE_NON_NULL(MapListNode->DevicePath);
 | |
|             SHELL_FREE_NON_NULL(MapListNode->MapName);
 | |
|             SHELL_FREE_NON_NULL(MapListNode->CurrentDirectoryPath);
 | |
|             FreePool(MapListNode);
 | |
|             return (EFI_SUCCESS);
 | |
|           }
 | |
|     } // for loop
 | |
| 
 | |
|     //
 | |
|     // We didnt find one to delete
 | |
|     //
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // make sure this is a valid to add device path
 | |
|   //
 | |
|   ///@todo add BlockIo to this test...
 | |
|   if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)
 | |
|     && !InternalShellProtocolIsBlockIoPresent(DevicePath)) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // First make sure there is no old mapping
 | |
|   //
 | |
|   Status = EfiShellSetMap(NULL, Mapping);
 | |
|   if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
 | |
|     return (Status);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // now add the new one.
 | |
|   //
 | |
|   Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the device path from the mapping.
 | |
| 
 | |
|   This function gets the device path associated with a mapping.
 | |
| 
 | |
|   @param Mapping                A pointer to the mapping
 | |
| 
 | |
|   @retval !=NULL                Pointer to the device path that corresponds to the
 | |
|                                 device mapping. The returned pointer does not need
 | |
|                                 to be freed.
 | |
|   @retval NULL                  There is no device path associated with the
 | |
|                                 specified mapping.
 | |
| **/
 | |
| CONST EFI_DEVICE_PATH_PROTOCOL *
 | |
| EFIAPI
 | |
| EfiShellGetDevicePathFromMap(
 | |
|   IN CONST CHAR16 *Mapping
 | |
|   )
 | |
| {
 | |
|   SHELL_MAP_LIST  *MapListItem;
 | |
|   CHAR16          *NewName;
 | |
|   UINTN           Size;
 | |
| 
 | |
|   NewName = NULL;
 | |
|   Size    = 0;
 | |
| 
 | |
|   StrnCatGrow(&NewName, &Size, Mapping, 0);
 | |
|   if (Mapping[StrLen(Mapping)-1] != L':') {
 | |
|     StrnCatGrow(&NewName, &Size, L":", 0);
 | |
|   }
 | |
| 
 | |
|   MapListItem = ShellCommandFindMapItem(NewName);
 | |
| 
 | |
|   FreePool(NewName);
 | |
| 
 | |
|   if (MapListItem != NULL) {
 | |
|     return (MapListItem->DevicePath);
 | |
|   }
 | |
|   return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the mapping(s) that most closely matches the device path.
 | |
| 
 | |
|   This function gets the mapping which corresponds to the device path *DevicePath. If
 | |
|   there is no exact match, then the mapping which most closely matches *DevicePath
 | |
|   is returned, and *DevicePath is updated to point to the remaining portion of the
 | |
|   device path. If there is an exact match, the mapping is returned and *DevicePath
 | |
|   points to the end-of-device-path node.
 | |
| 
 | |
|   If there are multiple map names they will be semi-colon seperated in the
 | |
|   NULL-terminated string.
 | |
| 
 | |
|   @param DevicePath             On entry, points to a device path pointer. On
 | |
|                                 exit, updates the pointer to point to the
 | |
|                                 portion of the device path after the mapping.
 | |
| 
 | |
|   @retval NULL                  No mapping was found.
 | |
|   @return !=NULL                Pointer to NULL-terminated mapping. The buffer
 | |
|                                 is callee allocated and should be freed by the caller.
 | |
| **/
 | |
| CONST CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetMapFromDevicePath(
 | |
|   IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
 | |
|   )
 | |
| {
 | |
|   SHELL_MAP_LIST              *Node;
 | |
|   CHAR16                      *PathForReturn;
 | |
|   UINTN                       PathSize;
 | |
| //  EFI_HANDLE                  PathHandle;
 | |
| //  EFI_HANDLE                  MapHandle;
 | |
| //  EFI_STATUS                  Status;
 | |
| //  EFI_DEVICE_PATH_PROTOCOL    *DevicePathCopy;
 | |
| //  EFI_DEVICE_PATH_PROTOCOL    *MapPathCopy;
 | |
| 
 | |
|   if (DevicePath == NULL || *DevicePath == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   PathForReturn = NULL;
 | |
|   PathSize      = 0;
 | |
| 
 | |
|   for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
 | |
|       ; !IsNull(&gShellMapList.Link, &Node->Link)
 | |
|       ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
 | |
|      ){
 | |
|     //
 | |
|     // check for exact match
 | |
|     //
 | |
|     if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
 | |
|       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
 | |
|       if (PathSize != 0) {
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
 | |
|       }
 | |
|       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
 | |
|     }
 | |
|   }
 | |
|   if (PathForReturn != NULL) {
 | |
|     while (!IsDevicePathEndType (*DevicePath)) {
 | |
|       *DevicePath = NextDevicePathNode (*DevicePath);
 | |
|     }
 | |
|     SetDevicePathEndNode (*DevicePath);
 | |
|   }
 | |
| /*
 | |
|   ///@todo finish code for inexact matches.
 | |
|   if (PathForReturn == NULL) {
 | |
|     PathSize = 0;
 | |
| 
 | |
|     DevicePathCopy = DuplicateDevicePath(*DevicePath);
 | |
|     ASSERT(DevicePathCopy != NULL);
 | |
|     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
 | |
|     ASSERT_EFI_ERROR(Status);
 | |
|     //
 | |
|     //  check each of the device paths we have to get the root of the path for consist mappings
 | |
|     //
 | |
|     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
 | |
|         ; !IsNull(&gShellMapList.Link, &Node->Link)
 | |
|         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
 | |
|        ){
 | |
|       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
 | |
|         continue;
 | |
|       }
 | |
|       MapPathCopy = DuplicateDevicePath(Node->DevicePath);
 | |
|       ASSERT(MapPathCopy != NULL);
 | |
|       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
 | |
|       if (MapHandle == PathHandle) {
 | |
| 
 | |
|         *DevicePath = DevicePathCopy;
 | |
| 
 | |
|         MapPathCopy = NULL;
 | |
|         DevicePathCopy = NULL;
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     //
 | |
|     // now add on the non-consistent mappings
 | |
|     //
 | |
|     for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
 | |
|         ; !IsNull(&gShellMapList.Link, &Node->Link)
 | |
|         ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
 | |
|        ){
 | |
|       if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
 | |
|         continue;
 | |
|       }
 | |
|       MapPathCopy = Node->DevicePath;
 | |
|       ASSERT(MapPathCopy != NULL);
 | |
|       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
 | |
|       if (MapHandle == PathHandle) {
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| */
 | |
| 
 | |
|   return (AddBufferToFreeList(PathForReturn));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Converts a device path to a file system-style path.
 | |
| 
 | |
|   This function converts a device path to a file system path by replacing part, or all, of
 | |
|   the device path with the file-system mapping. If there are more than one application
 | |
|   file system mappings, the one that most closely matches Path will be used.
 | |
| 
 | |
|   @param Path                   The pointer to the device path
 | |
| 
 | |
|   @retval NULL                  the device path could not be found.
 | |
|   @return all                   The pointer of the NULL-terminated file path. The path
 | |
|                                 is callee-allocated and should be freed by the caller.
 | |
| **/
 | |
| CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetFilePathFromDevicePath(
 | |
|   IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
 | |
|   )
 | |
| {
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevicePathCopy;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *MapPathCopy;
 | |
|   SHELL_MAP_LIST                  *MapListItem;
 | |
|   CHAR16                          *PathForReturn;
 | |
|   UINTN                           PathSize;
 | |
|   EFI_HANDLE                      PathHandle;
 | |
|   EFI_HANDLE                      MapHandle;
 | |
|   EFI_STATUS                      Status;
 | |
|   FILEPATH_DEVICE_PATH            *FilePath;
 | |
|   FILEPATH_DEVICE_PATH            *AlignedNode;
 | |
| 
 | |
|   PathForReturn = NULL;
 | |
|   PathSize = 0;
 | |
| 
 | |
|   DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
 | |
|   ASSERT(DevicePathCopy != NULL);
 | |
|   if (DevicePathCopy == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
|   ///@todo BlockIo?
 | |
|   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
 | |
| 
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (NULL);
 | |
|   }
 | |
|   //
 | |
|   //  check each of the device paths we have to get the root of the path
 | |
|   //
 | |
|   for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
 | |
|       ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
 | |
|       ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
 | |
|      ){
 | |
|     MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
 | |
|     ASSERT(MapPathCopy != NULL);
 | |
|     ///@todo BlockIo?
 | |
|     Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
 | |
|     if (MapHandle == PathHandle) {
 | |
|       ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
 | |
|       PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
 | |
|       //
 | |
|       // go through all the remaining nodes in the device path
 | |
|       //
 | |
|       for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
 | |
|           ; !IsDevicePathEnd (&FilePath->Header)
 | |
|           ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
 | |
|          ){
 | |
|         //
 | |
|         // If any node is not a file path node, then the conversion can not be completed
 | |
|         //
 | |
|         if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
 | |
|             (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
 | |
|           FreePool(PathForReturn);
 | |
|           return NULL;
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // append the path part onto the filepath.
 | |
|         //
 | |
|         ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
 | |
| 
 | |
|         AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath);
 | |
|         if (AlignedNode == NULL) {
 | |
|           FreePool (PathForReturn);
 | |
|           return NULL;
 | |
|         }
 | |
| 
 | |
|         // File Path Device Path Nodes 'can optionally add a "\" separator to
 | |
|         //  the beginning and/or the end of the Path Name string.'
 | |
|         // (UEFI Spec 2.4 section 9.3.6.4).
 | |
|         // If necessary, add a "\", but otherwise don't
 | |
|         // (This is specified in the above section, and also implied by the
 | |
|         //  UEFI Shell spec section 3.7)
 | |
|         if ((PathSize != 0)                        &&
 | |
|             (PathForReturn != NULL)                &&
 | |
|             (PathForReturn[PathSize / sizeof (CHAR16) - 1] != L'\\') &&
 | |
|             (AlignedNode->PathName[0]    != L'\\')) {
 | |
|           PathForReturn = StrnCatGrow (&PathForReturn, &PathSize, L"\\", 1);
 | |
|         }
 | |
| 
 | |
|         PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, AlignedNode->PathName, 0);
 | |
|         FreePool(AlignedNode);
 | |
|       } // for loop of remaining nodes
 | |
|     }
 | |
|     if (PathForReturn != NULL) {
 | |
|       break;
 | |
|     }
 | |
|   } // for loop of paths to check
 | |
|   return(PathForReturn);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Converts a file system style name to a device path.
 | |
| 
 | |
|   This function converts a file system style name to a device path, by replacing any
 | |
|   mapping references to the associated device path.
 | |
| 
 | |
|   @param[in] Path               The pointer to the path.
 | |
| 
 | |
|   @return                       The pointer of the file path. The file path is callee
 | |
|                                 allocated and should be freed by the caller.
 | |
|   @retval NULL                  The path could not be found.
 | |
|   @retval NULL                  There was not enough available memory.
 | |
| **/
 | |
| EFI_DEVICE_PATH_PROTOCOL *
 | |
| EFIAPI
 | |
| EfiShellGetDevicePathFromFilePath(
 | |
|   IN CONST CHAR16 *Path
 | |
|   )
 | |
| {
 | |
|   CHAR16                          *MapName;
 | |
|   CHAR16                          *NewPath;
 | |
|   CONST CHAR16                    *Cwd;
 | |
|   UINTN                           Size;
 | |
|   CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopy;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *DevicePathCopyForFree;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *DevicePathForReturn;
 | |
|   EFI_HANDLE                      Handle;
 | |
|   EFI_STATUS                      Status;
 | |
| 
 | |
|   if (Path == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   MapName = NULL;
 | |
|   NewPath = NULL;
 | |
| 
 | |
|   if (StrStr(Path, L":") == NULL) {
 | |
|     Cwd = EfiShellGetCurDir(NULL);
 | |
|     if (Cwd == NULL) {
 | |
|       return (NULL);
 | |
|     }
 | |
|     Size = StrSize(Cwd) + StrSize(Path);
 | |
|     NewPath = AllocateZeroPool(Size);
 | |
|     if (NewPath == NULL) {
 | |
|       return (NULL);
 | |
|     }
 | |
|     StrCpyS(NewPath, Size/sizeof(CHAR16), Cwd);
 | |
|     StrCatS(NewPath, Size/sizeof(CHAR16), L"\\");
 | |
|     if (*Path == L'\\') {
 | |
|       Path++;
 | |
|       while (PathRemoveLastItem(NewPath)) ;
 | |
|     }
 | |
|     StrCatS(NewPath, Size/sizeof(CHAR16), Path);
 | |
|     DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
 | |
|     FreePool(NewPath);
 | |
|     return (DevicePathForReturn);
 | |
|   }
 | |
| 
 | |
|   Size = 0;
 | |
|   //
 | |
|   // find the part before (but including) the : for the map name
 | |
|   //
 | |
|   ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
 | |
|   MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
 | |
|   if (MapName == NULL || MapName[StrLen(MapName)-1] != L':') {
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // look up the device path in the map
 | |
|   //
 | |
|   DevicePath = EfiShellGetDevicePathFromMap(MapName);
 | |
|   if (DevicePath == NULL) {
 | |
|     //
 | |
|     // Must have been a bad Mapname
 | |
|     //
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // make a copy for LocateDevicePath to modify (also save a pointer to call FreePool with)
 | |
|   //
 | |
|   DevicePathCopyForFree = DevicePathCopy = DuplicateDevicePath(DevicePath);
 | |
|   if (DevicePathCopy == NULL) {
 | |
|     FreePool(MapName);
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // get the handle
 | |
|   //
 | |
|   ///@todo BlockIo?
 | |
|   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     if (DevicePathCopyForFree != NULL) {
 | |
|       FreePool(DevicePathCopyForFree);
 | |
|     }
 | |
|     FreePool(MapName);
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // build the full device path
 | |
|   //
 | |
|   if ((*(Path+StrLen(MapName)) != CHAR_NULL) &&
 | |
|       (*(Path+StrLen(MapName)+1) == CHAR_NULL)) {
 | |
|     DevicePathForReturn = FileDevicePath(Handle, L"\\");
 | |
|   } else {
 | |
|     DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName));
 | |
|   }
 | |
| 
 | |
|   FreePool(MapName);
 | |
|   if (DevicePathCopyForFree != NULL) {
 | |
|     FreePool(DevicePathCopyForFree);
 | |
|   }
 | |
| 
 | |
|   return (DevicePathForReturn);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the name of the device specified by the device handle.
 | |
| 
 | |
|   This function gets the user-readable name of the device specified by the device
 | |
|   handle. If no user-readable name could be generated, then *BestDeviceName will be
 | |
|   NULL and EFI_NOT_FOUND will be returned.
 | |
| 
 | |
|   If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the
 | |
|   device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on
 | |
|   DeviceHandle.
 | |
| 
 | |
|   If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the
 | |
|   device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle.
 | |
|   If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and
 | |
|   EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then
 | |
|   EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority.
 | |
| 
 | |
|   @param DeviceHandle           The handle of the device.
 | |
|   @param Flags                  Determines the possible sources of component names.
 | |
|                                 Valid bits are:
 | |
|                                   EFI_DEVICE_NAME_USE_COMPONENT_NAME
 | |
|                                   EFI_DEVICE_NAME_USE_DEVICE_PATH
 | |
|   @param Language               A pointer to the language specified for the device
 | |
|                                 name, in the same format as described in the UEFI
 | |
|                                 specification, Appendix M
 | |
|   @param BestDeviceName         On return, points to the callee-allocated NULL-
 | |
|                                 terminated name of the device. If no device name
 | |
|                                 could be found, points to NULL. The name must be
 | |
|                                 freed by the caller...
 | |
| 
 | |
|   @retval EFI_SUCCESS           Get the name successfully.
 | |
|   @retval EFI_NOT_FOUND         Fail to get the device name.
 | |
|   @retval EFI_INVALID_PARAMETER Flags did not have a valid bit set.
 | |
|   @retval EFI_INVALID_PARAMETER BestDeviceName was NULL
 | |
|   @retval EFI_INVALID_PARAMETER DeviceHandle was NULL
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellGetDeviceName(
 | |
|   IN EFI_HANDLE DeviceHandle,
 | |
|   IN EFI_SHELL_DEVICE_NAME_FLAGS Flags,
 | |
|   IN CHAR8 *Language,
 | |
|   OUT CHAR16 **BestDeviceName
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                        Status;
 | |
|   EFI_COMPONENT_NAME2_PROTOCOL      *CompName2;
 | |
|   EFI_DEVICE_PATH_PROTOCOL          *DevicePath;
 | |
|   EFI_HANDLE                        *HandleList;
 | |
|   UINTN                             HandleCount;
 | |
|   UINTN                             LoopVar;
 | |
|   CHAR16                            *DeviceNameToReturn;
 | |
|   CHAR8                             *Lang;
 | |
|   UINTN                             ParentControllerCount;
 | |
|   EFI_HANDLE                        *ParentControllerBuffer;
 | |
|   UINTN                             ParentDriverCount;
 | |
|   EFI_HANDLE                        *ParentDriverBuffer;
 | |
| 
 | |
|   if (BestDeviceName == NULL ||
 | |
|       DeviceHandle   == NULL
 | |
|      ){
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // make sure one of the 2 supported bits is on
 | |
|   //
 | |
|   if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) &&
 | |
|       ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   DeviceNameToReturn  = NULL;
 | |
|   *BestDeviceName     = NULL;
 | |
|   HandleList          = NULL;
 | |
|   HandleCount         = 0;
 | |
|   Lang                = NULL;
 | |
| 
 | |
|   if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) {
 | |
|     Status = ParseHandleDatabaseByRelationship(
 | |
|       NULL,
 | |
|       DeviceHandle,
 | |
|       HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER,
 | |
|       &HandleCount,
 | |
|       &HandleList);
 | |
|     for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){
 | |
|       //
 | |
|       // Go through those handles until we get one that passes for GetComponentName
 | |
|       //
 | |
|       Status = gBS->OpenProtocol(
 | |
|         HandleList[LoopVar],
 | |
|         &gEfiComponentName2ProtocolGuid,
 | |
|         (VOID**)&CompName2,
 | |
|         gImageHandle,
 | |
|         NULL,
 | |
|         EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         Status = gBS->OpenProtocol(
 | |
|           HandleList[LoopVar],
 | |
|           &gEfiComponentNameProtocolGuid,
 | |
|           (VOID**)&CompName2,
 | |
|           gImageHandle,
 | |
|           NULL,
 | |
|           EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|       }
 | |
| 
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         continue;
 | |
|       }
 | |
|       Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
 | |
|       Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
 | |
|       FreePool(Lang);
 | |
|       Lang = NULL;
 | |
|       if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     if (HandleList != NULL) {
 | |
|       FreePool(HandleList);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Now check the parent controller using this as the child.
 | |
|     //
 | |
|     if (DeviceNameToReturn == NULL){
 | |
|       PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer);
 | |
|       for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) {
 | |
|         PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer);
 | |
|         for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) {
 | |
|           //
 | |
|           // try using that driver's component name with controller and our driver as the child.
 | |
|           //
 | |
|           Status = gBS->OpenProtocol(
 | |
|             ParentDriverBuffer[HandleCount],
 | |
|             &gEfiComponentName2ProtocolGuid,
 | |
|             (VOID**)&CompName2,
 | |
|             gImageHandle,
 | |
|             NULL,
 | |
|             EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|           if (EFI_ERROR(Status)) {
 | |
|             Status = gBS->OpenProtocol(
 | |
|               ParentDriverBuffer[HandleCount],
 | |
|               &gEfiComponentNameProtocolGuid,
 | |
|               (VOID**)&CompName2,
 | |
|               gImageHandle,
 | |
|               NULL,
 | |
|               EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|           }
 | |
| 
 | |
|           if (EFI_ERROR(Status)) {
 | |
|             continue;
 | |
|           }
 | |
|           Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE);
 | |
|           Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn);
 | |
|           FreePool(Lang);
 | |
|           Lang = NULL;
 | |
|           if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
 | |
|             break;
 | |
|           }
 | |
| 
 | |
| 
 | |
| 
 | |
|         }
 | |
|         SHELL_FREE_NON_NULL(ParentDriverBuffer);
 | |
|         if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       SHELL_FREE_NON_NULL(ParentControllerBuffer);
 | |
|     }
 | |
|     //
 | |
|     // dont return on fail since we will try device path if that bit is on
 | |
|     //
 | |
|     if (DeviceNameToReturn != NULL){
 | |
|       ASSERT(BestDeviceName != NULL);
 | |
|       StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
 | |
|       return (EFI_SUCCESS);
 | |
|     }
 | |
|   }
 | |
|   if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
 | |
|     Status = gBS->OpenProtocol(
 | |
|       DeviceHandle,
 | |
|       &gEfiDevicePathProtocolGuid,
 | |
|       (VOID**)&DevicePath,
 | |
|       gImageHandle,
 | |
|       NULL,
 | |
|       EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|     if (!EFI_ERROR(Status)) {
 | |
|       //
 | |
|       // use device path to text on the device path
 | |
|       //
 | |
|       *BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE);
 | |
|       return (EFI_SUCCESS);
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // none of the selected bits worked.
 | |
|   //
 | |
|   return (EFI_NOT_FOUND);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Opens the root directory of a device on a handle
 | |
| 
 | |
|   This function opens the root directory of a device and returns a file handle to it.
 | |
| 
 | |
|   @param DeviceHandle           The handle of the device that contains the volume.
 | |
|   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
 | |
|                                 device.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Root opened successfully.
 | |
|   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
 | |
|                                 could not be opened.
 | |
|   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
 | |
|   @retval EFI_DEVICE_ERROR      The device had an error.
 | |
|   @retval Others                Error status returned from EFI_SIMPLE_FILE_SYSTEM_PROTOCOL->OpenVolume().
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellOpenRootByHandle(
 | |
|   IN EFI_HANDLE DeviceHandle,
 | |
|   OUT SHELL_FILE_HANDLE *FileHandle
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
 | |
|   EFI_FILE_PROTOCOL               *RealFileHandle;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *DevPath;
 | |
| 
 | |
|   //
 | |
|   // get the simple file system interface
 | |
|   //
 | |
|   Status = gBS->OpenProtocol(DeviceHandle,
 | |
|                              &gEfiSimpleFileSystemProtocolGuid,
 | |
|                              (VOID**)&SimpleFileSystem,
 | |
|                              gImageHandle,
 | |
|                              NULL,
 | |
|                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   Status = gBS->OpenProtocol(DeviceHandle,
 | |
|                              &gEfiDevicePathProtocolGuid,
 | |
|                              (VOID**)&DevPath,
 | |
|                              gImageHandle,
 | |
|                              NULL,
 | |
|                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
|   //
 | |
|   // Open the root volume now...
 | |
|   //
 | |
|   Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &RealFileHandle);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return Status;
 | |
|   }
 | |
| 
 | |
|   *FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath));
 | |
|   return (EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Opens the root directory of a device.
 | |
| 
 | |
|   This function opens the root directory of a device and returns a file handle to it.
 | |
| 
 | |
|   @param DevicePath             Points to the device path corresponding to the device where the
 | |
|                                 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed.
 | |
|   @param FileHandle             On exit, points to the file handle corresponding to the root directory on the
 | |
|                                 device.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Root opened successfully.
 | |
|   @retval EFI_NOT_FOUND         EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory
 | |
|                                 could not be opened.
 | |
|   @retval EFI_VOLUME_CORRUPTED  The data structures in the volume were corrupted.
 | |
|   @retval EFI_DEVICE_ERROR      The device had an error
 | |
|   @retval EFI_INVALID_PARAMETER FileHandle is NULL.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellOpenRoot(
 | |
|   IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
 | |
|   OUT SHELL_FILE_HANDLE *FileHandle
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS Status;
 | |
|   EFI_HANDLE Handle;
 | |
| 
 | |
|   if (FileHandle == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // find the handle of the device with that device handle and the file system
 | |
|   //
 | |
|   ///@todo BlockIo?
 | |
|   Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
 | |
|                                  &DevicePath,
 | |
|                                  &Handle);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   return (EfiShellOpenRootByHandle(Handle, FileHandle));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns whether any script files are currently being processed.
 | |
| 
 | |
|   @retval TRUE                 There is at least one script file active.
 | |
|   @retval FALSE                No script files are active now.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| EfiShellBatchIsActive (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   if (ShellCommandGetCurrentScriptFile() == NULL) {
 | |
|     return (FALSE);
 | |
|   }
 | |
|   return (TRUE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Worker function to open a file based on a device path.  this will open the root
 | |
|   of the volume and then traverse down to the file itself.
 | |
| 
 | |
|   @param DevicePath               Device Path of the file.
 | |
|   @param FileHandle               Pointer to the file upon a successful return.
 | |
|   @param OpenMode                 mode to open file in.
 | |
|   @param Attributes               the File Attributes to use when creating a new file.
 | |
| 
 | |
|   @retval EFI_SUCCESS             the file is open and FileHandle is valid
 | |
|   @retval EFI_UNSUPPORTED         the device path cotained non-path elements
 | |
|   @retval other                   an error ocurred.
 | |
| **/
 | |
| EFI_STATUS
 | |
| InternalOpenFileDevicePath(
 | |
|   IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
 | |
|   OUT SHELL_FILE_HANDLE           *FileHandle,
 | |
|   IN UINT64                       OpenMode,
 | |
|   IN UINT64                       Attributes OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   FILEPATH_DEVICE_PATH            *FilePathNode;
 | |
|   EFI_HANDLE                      Handle;
 | |
|   SHELL_FILE_HANDLE               ShellHandle;
 | |
|   EFI_FILE_PROTOCOL               *Handle1;
 | |
|   EFI_FILE_PROTOCOL               *Handle2;
 | |
|   FILEPATH_DEVICE_PATH            *AlignedNode;
 | |
| 
 | |
|   if (FileHandle == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   *FileHandle   = NULL;
 | |
|   Handle1       = NULL;
 | |
|   Handle2       = NULL;
 | |
|   Handle        = NULL;
 | |
|   ShellHandle   = NULL;
 | |
|   FilePathNode  = NULL;
 | |
|   AlignedNode   = NULL;
 | |
| 
 | |
|   Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
 | |
| 
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
 | |
|     if (Handle1 != NULL) {
 | |
|       //
 | |
|       // chop off the begining part before the file system part...
 | |
|       //
 | |
|       ///@todo BlockIo?
 | |
|       Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
 | |
|                                      &DevicePath,
 | |
|                                      &Handle);
 | |
|         if (!EFI_ERROR(Status)) {
 | |
|         //
 | |
|         // To access as a file system, the file path should only
 | |
|         // contain file path components.  Follow the file path nodes
 | |
|         // and find the target file
 | |
|         //
 | |
|         for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath
 | |
|             ; !IsDevicePathEnd (&FilePathNode->Header)
 | |
|             ; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header)
 | |
|            ){
 | |
|           SHELL_FREE_NON_NULL(AlignedNode);
 | |
|           AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode);
 | |
|           //
 | |
|           // For file system access each node should be a file path component
 | |
|           //
 | |
|           if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||
 | |
|               DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP
 | |
|              ) {
 | |
|             Status = EFI_UNSUPPORTED;
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           //
 | |
|           // Open this file path node
 | |
|           //
 | |
|           Handle2 = Handle1;
 | |
|           Handle1 = NULL;
 | |
| 
 | |
|           //
 | |
|           // if this is the last node in the DevicePath always create (if that was requested).
 | |
|           //
 | |
|           if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) {
 | |
|             Status = Handle2->Open (
 | |
|                                   Handle2,
 | |
|                                   &Handle1,
 | |
|                                   AlignedNode->PathName,
 | |
|                                   OpenMode,
 | |
|                                   Attributes
 | |
|                                  );
 | |
|           } else {
 | |
| 
 | |
|             //
 | |
|             //  This is not the last node and we dont want to 'create' existing
 | |
|             //  directory entries...
 | |
|             //
 | |
| 
 | |
|             //
 | |
|             // open without letting it create
 | |
|             // prevents error on existing files/directories
 | |
|             //
 | |
|             Status = Handle2->Open (
 | |
|                                   Handle2,
 | |
|                                   &Handle1,
 | |
|                                   AlignedNode->PathName,
 | |
|                                   OpenMode &~EFI_FILE_MODE_CREATE,
 | |
|                                   Attributes
 | |
|                                  );
 | |
|             //
 | |
|             // if above failed now open and create the 'item'
 | |
|             // if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above)
 | |
|             //
 | |
|             if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
 | |
|               Status = Handle2->Open (
 | |
|                                     Handle2,
 | |
|                                     &Handle1,
 | |
|                                     AlignedNode->PathName,
 | |
|                                     OpenMode,
 | |
|                                     Attributes
 | |
|                                    );
 | |
|             }
 | |
|           }
 | |
|           //
 | |
|           // Close the last node
 | |
|           //
 | |
|           ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2);
 | |
| 
 | |
|           //
 | |
|           // If there's been an error, stop
 | |
|           //
 | |
|           if (EFI_ERROR (Status)) {
 | |
|             break;
 | |
|           }
 | |
|         } // for loop
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   SHELL_FREE_NON_NULL(AlignedNode);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     if (Handle1 != NULL) {
 | |
|       ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1);
 | |
|     }
 | |
|   } else {
 | |
|     *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
 | |
|   }
 | |
|   return (Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Creates a file or directory by name.
 | |
| 
 | |
|   This function creates an empty new file or directory with the specified attributes and
 | |
|   returns the new file's handle. If the file already exists and is read-only, then
 | |
|   EFI_INVALID_PARAMETER will be returned.
 | |
| 
 | |
|   If the file already existed, it is truncated and its attributes updated. If the file is
 | |
|   created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
 | |
| 
 | |
|   If the file name begins with >v, then the file handle which is returned refers to the
 | |
|   shell environment variable with the specified name. If the shell environment variable
 | |
|   already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
 | |
| 
 | |
|   @param FileName           Pointer to NULL-terminated file path
 | |
|   @param FileAttribs        The new file's attrbiutes.  the different attributes are
 | |
|                             described in EFI_FILE_PROTOCOL.Open().
 | |
|   @param FileHandle         On return, points to the created file handle or directory's handle
 | |
| 
 | |
|   @retval EFI_SUCCESS       The file was opened.  FileHandle points to the new file's handle.
 | |
|   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
 | |
|   @retval EFI_UNSUPPORTED   could not open the file path
 | |
|   @retval EFI_NOT_FOUND     the specified file could not be found on the devide, or could not
 | |
|                             file the file system on the device.
 | |
|   @retval EFI_NO_MEDIA      the device has no medium.
 | |
|   @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
 | |
|                             longer supported.
 | |
|   @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
 | |
|                             the DirName.
 | |
|   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
 | |
|   @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
 | |
|                             when the media is write-protected.
 | |
|   @retval EFI_ACCESS_DENIED The service denied access to the file.
 | |
|   @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
 | |
|   @retval EFI_VOLUME_FULL   The volume is full.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellCreateFile(
 | |
|   IN CONST CHAR16       *FileName,
 | |
|   IN UINT64             FileAttribs,
 | |
|   OUT SHELL_FILE_HANDLE *FileHandle
 | |
|   )
 | |
| {
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | |
|   EFI_STATUS                Status;
 | |
|   BOOLEAN                   Volatile;
 | |
| 
 | |
|   //
 | |
|   // Is this for an environment variable
 | |
|   // do we start with >v
 | |
|   //
 | |
|   if (StrStr(FileName, L">v") == FileName) {
 | |
|     Status = IsVolatileEnv (FileName + 2, &Volatile);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|     if (!Volatile) {
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     *FileHandle = CreateFileInterfaceEnv(FileName+2);
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // We are opening a regular file.
 | |
|   //
 | |
|   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
 | |
|   if (DevicePath == NULL) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs);
 | |
|   FreePool(DevicePath);
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Register a GUID and a localized human readable name for it.
 | |
| 
 | |
|   If Guid is not assigned a name, then assign GuidName to Guid.  This list of GUID
 | |
|   names must be used whenever a shell command outputs GUID information.
 | |
| 
 | |
|   This function is only available when the major and minor versions in the
 | |
|   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
 | |
| 
 | |
|   @param[in] Guid       A pointer to the GUID being registered.
 | |
|   @param[in] GuidName   A pointer to the localized name for the GUID being registered.
 | |
| 
 | |
|   @retval EFI_SUCCESS             The operation was successful.
 | |
|   @retval EFI_INVALID_PARAMETER   Guid was NULL.
 | |
|   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
 | |
|   @retval EFI_ACCESS_DENIED       Guid already is assigned a name.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellRegisterGuidName(
 | |
|   IN CONST EFI_GUID *Guid,
 | |
|   IN CONST CHAR16   *GuidName
 | |
|   )
 | |
| {
 | |
|   return (AddNewGuidNameMapping(Guid, GuidName, NULL));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Opens a file or a directory by file name.
 | |
| 
 | |
|   This function opens the specified file in the specified OpenMode and returns a file
 | |
|   handle.
 | |
|   If the file name begins with >v, then the file handle which is returned refers to the
 | |
|   shell environment variable with the specified name. If the shell environment variable
 | |
|   exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
 | |
|   EFI_INVALID_PARAMETER is returned.
 | |
| 
 | |
|   If the file name is >i, then the file handle which is returned refers to the standard
 | |
|   input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
 | |
|   is returned.
 | |
| 
 | |
|   If the file name is >o, then the file handle which is returned refers to the standard
 | |
|   output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
 | |
|   is returned.
 | |
| 
 | |
|   If the file name is >e, then the file handle which is returned refers to the standard
 | |
|   error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
 | |
|   is returned.
 | |
| 
 | |
|   If the file name is NUL, then the file handle that is returned refers to the standard NUL
 | |
|   file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
 | |
|   returned.
 | |
| 
 | |
|   If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
 | |
|   FileHandle is NULL.
 | |
| 
 | |
|   @param FileName               Points to the NULL-terminated UCS-2 encoded file name.
 | |
|   @param FileHandle             On return, points to the file handle.
 | |
|   @param OpenMode               File open mode. Either EFI_FILE_MODE_READ or
 | |
|                                 EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
 | |
|                                 Specification.
 | |
|   @retval EFI_SUCCESS           The file was opened. FileHandle has the opened file's handle.
 | |
|   @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
 | |
|   @retval EFI_UNSUPPORTED       Could not open the file path. FileHandle is NULL.
 | |
|   @retval EFI_NOT_FOUND         The specified file could not be found on the device or the file
 | |
|                                 system could not be found on the device. FileHandle is NULL.
 | |
|   @retval EFI_NO_MEDIA          The device has no medium. FileHandle is NULL.
 | |
|   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the medium is no
 | |
|                                 longer supported. FileHandle is NULL.
 | |
|   @retval EFI_DEVICE_ERROR      The device reported an error or can't get the file path according
 | |
|                                 the FileName. FileHandle is NULL.
 | |
|   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted. FileHandle is NULL.
 | |
|   @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a file for write
 | |
|                                 when the media is write-protected. FileHandle is NULL.
 | |
|   @retval EFI_ACCESS_DENIED     The service denied access to the file. FileHandle is NULL.
 | |
|   @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the file. FileHandle
 | |
|                                 is NULL.
 | |
|   @retval EFI_VOLUME_FULL       The volume is full. FileHandle is NULL.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellOpenFileByName(
 | |
|   IN CONST CHAR16       *FileName,
 | |
|   OUT SHELL_FILE_HANDLE *FileHandle,
 | |
|   IN UINT64             OpenMode
 | |
|   )
 | |
| {
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *DevicePath;
 | |
|   EFI_STATUS                      Status;
 | |
|   BOOLEAN                         Volatile;
 | |
| 
 | |
|   *FileHandle = NULL;
 | |
| 
 | |
|   //
 | |
|   // Is this for StdIn
 | |
|   //
 | |
|   if (StrCmp(FileName, L">i") == 0) {
 | |
|     //
 | |
|     // make sure not writing to StdIn
 | |
|     //
 | |
|     if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
 | |
|     ASSERT(*FileHandle != NULL);
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Is this for StdOut
 | |
|   //
 | |
|   if (StrCmp(FileName, L">o") == 0) {
 | |
|     //
 | |
|     // make sure not writing to StdIn
 | |
|     //
 | |
|     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     *FileHandle = &FileInterfaceStdOut;
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Is this for NUL / NULL file
 | |
|   //
 | |
|   if ((gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16*)FileName, L"NUL") == 0) ||
 | |
|       (gUnicodeCollation->StriColl (gUnicodeCollation, (CHAR16*)FileName, L"NULL") == 0)) {
 | |
|     *FileHandle = &FileInterfaceNulFile;
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Is this for StdErr
 | |
|   //
 | |
|   if (StrCmp(FileName, L">e") == 0) {
 | |
|     //
 | |
|     // make sure not writing to StdIn
 | |
|     //
 | |
|     if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     *FileHandle = &FileInterfaceStdErr;
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Is this for an environment variable
 | |
|   // do we start with >v
 | |
|   //
 | |
|   if (StrStr(FileName, L">v") == FileName) {
 | |
|     Status = IsVolatileEnv (FileName + 2, &Volatile);
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       return Status;
 | |
|     }
 | |
|     if (!Volatile &&
 | |
|         ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     *FileHandle = CreateFileInterfaceEnv(FileName+2);
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // We are opening a regular file.
 | |
|   //
 | |
|   DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
 | |
| 
 | |
|   if (DevicePath == NULL) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Copy the device path, open the file, then free the memory
 | |
|   //
 | |
|   Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
 | |
|   FreePool(DevicePath);
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deletes the file specified by the file name.
 | |
| 
 | |
|   This function deletes a file.
 | |
| 
 | |
|   @param FileName                 Points to the NULL-terminated file name.
 | |
| 
 | |
|   @retval EFI_SUCCESS             The file was closed and deleted, and the handle was closed.
 | |
|   @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
 | |
|   @sa EfiShellCreateFile
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellDeleteFileByName(
 | |
|   IN CONST CHAR16 *FileName
 | |
|   )
 | |
| {
 | |
|   SHELL_FILE_HANDLE FileHandle;
 | |
|   EFI_STATUS        Status;
 | |
| 
 | |
|   FileHandle = NULL;
 | |
| 
 | |
|   //
 | |
|   // get a handle to the file
 | |
|   //
 | |
|   Status = EfiShellCreateFile(FileName,
 | |
|                               0,
 | |
|                               &FileHandle);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (Status);
 | |
|   }
 | |
|   //
 | |
|   // now delete the file
 | |
|   //
 | |
|   ShellFileHandleRemove(FileHandle);
 | |
|   return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Disables the page break output mode.
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| EfiShellDisablePageBreak (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   ShellInfoObject.PageBreakEnabled = FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enables the page break output mode.
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| EfiShellEnablePageBreak (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   ShellInfoObject.PageBreakEnabled = TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   internal worker function to load and run an image via device path.
 | |
| 
 | |
|   @param ParentImageHandle      A handle of the image that is executing the specified
 | |
|                                 command line.
 | |
|   @param DevicePath             device path of the file to execute
 | |
|   @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
 | |
|                                 containing the command line. If NULL then the command-
 | |
|                                 line will be empty.
 | |
|   @param Environment            Points to a NULL-terminated array of environment
 | |
|                                 variables with the format 'x=y', where x is the
 | |
|                                 environment variable name and y is the value. If this
 | |
|                                 is NULL, then the current shell environment is used.
 | |
| 
 | |
|   @param[out] StartImageStatus  Returned status from gBS->StartImage.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The command executed successfully. The  status code
 | |
|                             returned by the command is pointed to by StatusCode.
 | |
|   @retval EFI_INVALID_PARAMETER The parameters are invalid.
 | |
|   @retval EFI_OUT_OF_RESOURCES Out of resources.
 | |
|   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| InternalShellExecuteDevicePath(
 | |
|   IN CONST EFI_HANDLE               *ParentImageHandle,
 | |
|   IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
 | |
|   IN CONST CHAR16                   *CommandLine OPTIONAL,
 | |
|   IN CONST CHAR16                   **Environment OPTIONAL,
 | |
|   OUT EFI_STATUS                    *StartImageStatus OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   EFI_STATUS                    StartStatus;
 | |
|   EFI_STATUS                    CleanupStatus;
 | |
|   EFI_HANDLE                    NewHandle;
 | |
|   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
 | |
|   LIST_ENTRY                    OrigEnvs;
 | |
|   EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
 | |
|   CHAR16                        *ImagePath;
 | |
|   UINTN                         Index;
 | |
|   CHAR16                        *Walker;
 | |
|   CHAR16                        *NewCmdLine;
 | |
| 
 | |
|   if (ParentImageHandle == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   InitializeListHead(&OrigEnvs);
 | |
|   ZeroMem(&ShellParamsProtocol, sizeof(EFI_SHELL_PARAMETERS_PROTOCOL));
 | |
| 
 | |
|   NewHandle = NULL;
 | |
| 
 | |
|   NewCmdLine = AllocateCopyPool (StrSize (CommandLine), CommandLine);
 | |
|   if (NewCmdLine == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   for (Walker = NewCmdLine; Walker != NULL && *Walker != CHAR_NULL ; Walker++) {
 | |
|     if (*Walker == L'^' && *(Walker+1) == L'#') {
 | |
|       CopyMem(Walker, Walker+1, StrSize(Walker) - sizeof(Walker[0]));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Load the image with:
 | |
|   // FALSE - not from boot manager and NULL, 0 being not already in memory
 | |
|   //
 | |
|   Status = gBS->LoadImage(
 | |
|     FALSE,
 | |
|     *ParentImageHandle,
 | |
|     (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
 | |
|     NULL,
 | |
|     0,
 | |
|     &NewHandle);
 | |
| 
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     if (NewHandle != NULL) {
 | |
|       gBS->UnloadImage(NewHandle);
 | |
|     }
 | |
|     FreePool (NewCmdLine);
 | |
|     return (Status);
 | |
|   }
 | |
|   Status = gBS->OpenProtocol(
 | |
|     NewHandle,
 | |
|     &gEfiLoadedImageProtocolGuid,
 | |
|     (VOID**)&LoadedImage,
 | |
|     gImageHandle,
 | |
|     NULL,
 | |
|     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
| 
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     //
 | |
|     // If the image is not an app abort it.
 | |
|     //
 | |
|     if (LoadedImage->ImageCodeType != EfiLoaderCode){
 | |
|       ShellPrintHiiEx(
 | |
|         -1,
 | |
|         -1,
 | |
|         NULL,
 | |
|         STRING_TOKEN (STR_SHELL_IMAGE_NOT_APP),
 | |
|         ShellInfoObject.HiiHandle
 | |
|       );
 | |
|       goto UnloadImage;
 | |
|     }
 | |
| 
 | |
|     ASSERT(LoadedImage->LoadOptionsSize == 0);
 | |
|     if (NewCmdLine != NULL) {
 | |
|       LoadedImage->LoadOptionsSize  = (UINT32)StrSize(NewCmdLine);
 | |
|       LoadedImage->LoadOptions      = (VOID*)NewCmdLine;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Save our current environment settings for later restoration if necessary
 | |
|     //
 | |
|     if (Environment != NULL) {
 | |
|       Status = GetEnvironmentVariableList(&OrigEnvs);
 | |
|       if (!EFI_ERROR(Status)) {
 | |
|         Status = SetEnvironmentVariables(Environment);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Initialize and install a shell parameters protocol on the image.
 | |
|     //
 | |
|     ShellParamsProtocol.StdIn   = ShellInfoObject.NewShellParametersProtocol->StdIn;
 | |
|     ShellParamsProtocol.StdOut  = ShellInfoObject.NewShellParametersProtocol->StdOut;
 | |
|     ShellParamsProtocol.StdErr  = ShellInfoObject.NewShellParametersProtocol->StdErr;
 | |
|     Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, Efi_Application, NULL, NULL);
 | |
|     ASSERT_EFI_ERROR(Status);
 | |
|     //
 | |
|     // Replace Argv[0] with the full path of the binary we're executing:
 | |
|     // If the command line was "foo", the binary might be called "foo.efi".
 | |
|     // "The first entry in [Argv] is always the full file path of the
 | |
|     //  executable" - UEFI Shell Spec section 2.3
 | |
|     //
 | |
|     ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
 | |
|     // The image we're executing isn't necessarily in a filesystem - it might
 | |
|     // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
 | |
|     // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
 | |
|     if (ImagePath != NULL) {
 | |
|       if (ShellParamsProtocol.Argv == NULL) {
 | |
|         // Command line was empty or null.
 | |
|         // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
 | |
|         ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
 | |
|         if (ShellParamsProtocol.Argv == NULL) {
 | |
|           Status = EFI_OUT_OF_RESOURCES;
 | |
|           goto UnloadImage;
 | |
|         }
 | |
|         ShellParamsProtocol.Argc = 1;
 | |
|       } else {
 | |
|         // Free the string UpdateArgcArgv put in Argv[0];
 | |
|         FreePool (ShellParamsProtocol.Argv[0]);
 | |
|       }
 | |
|       ShellParamsProtocol.Argv[0] = ImagePath;
 | |
|     }
 | |
| 
 | |
|     Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
 | |
|     ASSERT_EFI_ERROR(Status);
 | |
| 
 | |
|     ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
 | |
| 
 | |
|     //
 | |
|     // now start the image and if the caller wanted the return code pass it to them...
 | |
|     //
 | |
|     if (!EFI_ERROR(Status)) {
 | |
|       StartStatus      = gBS->StartImage(
 | |
|                           NewHandle,
 | |
|                           0,
 | |
|                           NULL
 | |
|                           );
 | |
|       if (StartImageStatus != NULL) {
 | |
|         *StartImageStatus = StartStatus;
 | |
|       }
 | |
| 
 | |
|       CleanupStatus = gBS->UninstallProtocolInterface(
 | |
|                             NewHandle,
 | |
|                             &gEfiShellParametersProtocolGuid,
 | |
|                             &ShellParamsProtocol
 | |
|                             );
 | |
|       ASSERT_EFI_ERROR(CleanupStatus);
 | |
| 
 | |
|       goto FreeAlloc;
 | |
|     }
 | |
| 
 | |
| UnloadImage:
 | |
|     // Unload image - We should only get here if we didn't call StartImage
 | |
|     gBS->UnloadImage (NewHandle);
 | |
| 
 | |
| FreeAlloc:
 | |
|     // Free Argv (Allocated in UpdateArgcArgv)
 | |
|     if (ShellParamsProtocol.Argv != NULL) {
 | |
|       for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) {
 | |
|         if (ShellParamsProtocol.Argv[Index] != NULL) {
 | |
|           FreePool (ShellParamsProtocol.Argv[Index]);
 | |
|         }
 | |
|       }
 | |
|       FreePool (ShellParamsProtocol.Argv);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Restore environment variables
 | |
|   if (!IsListEmpty(&OrigEnvs)) {
 | |
|     CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
 | |
|     ASSERT_EFI_ERROR (CleanupStatus);
 | |
|   }
 | |
| 
 | |
|   FreePool (NewCmdLine);
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   internal worker function to load and run an image in the current shell.
 | |
| 
 | |
|   @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
 | |
|                                 containing the command line. If NULL then the command-
 | |
|                                 line will be empty.
 | |
|   @param Environment            Points to a NULL-terminated array of environment
 | |
|                                 variables with the format 'x=y', where x is the
 | |
|                                 environment variable name and y is the value. If this
 | |
|                                 is NULL, then the current shell environment is used.
 | |
| 
 | |
|   @param[out] StartImageStatus  Returned status from the command line.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The command executed successfully. The  status code
 | |
|                             returned by the command is pointed to by StatusCode.
 | |
|   @retval EFI_INVALID_PARAMETER The parameters are invalid.
 | |
|   @retval EFI_OUT_OF_RESOURCES Out of resources.
 | |
|   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
 | |
| **/
 | |
| EFI_STATUS
 | |
| InternalShellExecute(
 | |
|   IN CONST CHAR16                   *CommandLine OPTIONAL,
 | |
|   IN CONST CHAR16                   **Environment OPTIONAL,
 | |
|   OUT EFI_STATUS                    *StartImageStatus OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                    Status;
 | |
|   EFI_STATUS                    CleanupStatus;
 | |
|   LIST_ENTRY                    OrigEnvs;
 | |
| 
 | |
|   InitializeListHead(&OrigEnvs);
 | |
| 
 | |
|   //
 | |
|   // Save our current environment settings for later restoration if necessary
 | |
|   //
 | |
|   if (Environment != NULL) {
 | |
|     Status = GetEnvironmentVariableList(&OrigEnvs);
 | |
|     if (!EFI_ERROR(Status)) {
 | |
|       Status = SetEnvironmentVariables(Environment);
 | |
|     } else {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   Status = RunShellCommand(CommandLine, StartImageStatus);
 | |
| 
 | |
|   // Restore environment variables
 | |
|   if (!IsListEmpty(&OrigEnvs)) {
 | |
|     CleanupStatus = SetEnvironmentVariableList(&OrigEnvs);
 | |
|     ASSERT_EFI_ERROR (CleanupStatus);
 | |
|   }
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Determine if the UEFI Shell is currently running with nesting enabled or disabled.
 | |
| 
 | |
|   @retval FALSE   nesting is required
 | |
|   @retval other   nesting is enabled
 | |
| **/
 | |
| STATIC
 | |
| BOOLEAN
 | |
| NestingEnabled(
 | |
|   VOID
 | |
| )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   CHAR16      *Temp;
 | |
|   CHAR16      *Temp2;
 | |
|   UINTN       TempSize;
 | |
|   BOOLEAN     RetVal;
 | |
| 
 | |
|   RetVal = TRUE;
 | |
|   Temp   = NULL;
 | |
|   Temp2  = NULL;
 | |
| 
 | |
|   if (ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest) {
 | |
|     TempSize = 0;
 | |
|     Temp     = NULL;
 | |
|     Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
 | |
|     if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|       Temp = AllocateZeroPool(TempSize + sizeof(CHAR16));
 | |
|       if (Temp != NULL) {
 | |
|         Status = SHELL_GET_ENVIRONMENT_VARIABLE(mNoNestingEnvVarName, &TempSize, Temp);
 | |
|       }
 | |
|     }
 | |
|     Temp2 = StrnCatGrow(&Temp2, NULL, mNoNestingTrue, 0);
 | |
|     if (Temp != NULL && Temp2 != NULL && StringNoCaseCompare(&Temp, &Temp2) == 0) {
 | |
|       //
 | |
|       // Use the no nesting method.
 | |
|       //
 | |
|       RetVal = FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SHELL_FREE_NON_NULL(Temp);
 | |
|   SHELL_FREE_NON_NULL(Temp2);
 | |
|   return (RetVal);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Execute the command line.
 | |
| 
 | |
|   This function creates a nested instance of the shell and executes the specified
 | |
|   command (CommandLine) with the specified environment (Environment). Upon return,
 | |
|   the status code returned by the specified command is placed in StatusCode.
 | |
| 
 | |
|   If Environment is NULL, then the current environment is used and all changes made
 | |
|   by the commands executed will be reflected in the current environment. If the
 | |
|   Environment is non-NULL, then the changes made will be discarded.
 | |
| 
 | |
|   The CommandLine is executed from the current working directory on the current
 | |
|   device.
 | |
| 
 | |
|   @param ParentImageHandle  A handle of the image that is executing the specified
 | |
|                             command line.
 | |
|   @param CommandLine        Points to the NULL-terminated UCS-2 encoded string
 | |
|                             containing the command line. If NULL then the command-
 | |
|                             line will be empty.
 | |
|   @param Environment        Points to a NULL-terminated array of environment
 | |
|                             variables with the format 'x=y', where x is the
 | |
|                             environment variable name and y is the value. If this
 | |
|                             is NULL, then the current shell environment is used.
 | |
|   @param StatusCode         Points to the status code returned by the CommandLine.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The command executed successfully. The  status code
 | |
|                             returned by the command is pointed to by StatusCode.
 | |
|   @retval EFI_INVALID_PARAMETER The parameters are invalid.
 | |
|   @retval EFI_OUT_OF_RESOURCES Out of resources.
 | |
|   @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
 | |
|   @retval EFI_UNSUPPORTED   The support level required for this function is not present.
 | |
| 
 | |
|   @sa InternalShellExecuteDevicePath
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellExecute(
 | |
|   IN EFI_HANDLE *ParentImageHandle,
 | |
|   IN CHAR16 *CommandLine OPTIONAL,
 | |
|   IN CHAR16 **Environment OPTIONAL,
 | |
|   OUT EFI_STATUS *StatusCode OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                Status;
 | |
|   CHAR16                    *Temp;
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *DevPath;
 | |
|   UINTN                     Size;
 | |
| 
 | |
|   if ((PcdGet8(PcdShellSupportLevel) < 1)) {
 | |
|     return (EFI_UNSUPPORTED);
 | |
|   }
 | |
| 
 | |
|   if (NestingEnabled()) {
 | |
|     DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
 | |
| 
 | |
|     DEBUG_CODE_BEGIN();
 | |
|     Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
 | |
|     FreePool(Temp);
 | |
|     Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
 | |
|     FreePool(Temp);
 | |
|     Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE);
 | |
|     FreePool(Temp);
 | |
|     DEBUG_CODE_END();
 | |
| 
 | |
|     Temp = NULL;
 | |
|     Size = 0;
 | |
|     ASSERT((Temp == NULL && Size == 0) || (Temp != NULL));
 | |
|     StrnCatGrow(&Temp, &Size, L"Shell.efi -exit ", 0);
 | |
|     StrnCatGrow(&Temp, &Size, CommandLine, 0);
 | |
| 
 | |
|     Status = InternalShellExecuteDevicePath(
 | |
|       ParentImageHandle,
 | |
|       DevPath,
 | |
|       Temp,
 | |
|       (CONST CHAR16**)Environment,
 | |
|       StatusCode);
 | |
| 
 | |
|     //
 | |
|     // de-allocate and return
 | |
|     //
 | |
|     FreePool(DevPath);
 | |
|     FreePool(Temp);
 | |
|   } else {
 | |
|     Status = InternalShellExecute(
 | |
|       (CONST CHAR16*)CommandLine,
 | |
|       (CONST CHAR16**)Environment,
 | |
|       StatusCode);
 | |
|   }
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Utility cleanup function for EFI_SHELL_FILE_INFO objects.
 | |
| 
 | |
|   1) frees all pointers (non-NULL)
 | |
|   2) Closes the SHELL_FILE_HANDLE
 | |
| 
 | |
|   @param FileListNode     pointer to the list node to free
 | |
| **/
 | |
| VOID
 | |
| InternalFreeShellFileInfoNode(
 | |
|   IN EFI_SHELL_FILE_INFO *FileListNode
 | |
|   )
 | |
| {
 | |
|   if (FileListNode->Info != NULL) {
 | |
|     FreePool((VOID*)FileListNode->Info);
 | |
|   }
 | |
|   if (FileListNode->FileName != NULL) {
 | |
|     FreePool((VOID*)FileListNode->FileName);
 | |
|   }
 | |
|   if (FileListNode->FullName != NULL) {
 | |
|     FreePool((VOID*)FileListNode->FullName);
 | |
|   }
 | |
|   if (FileListNode->Handle != NULL) {
 | |
|     ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
 | |
|   }
 | |
|   FreePool(FileListNode);
 | |
| }
 | |
| /**
 | |
|   Frees the file list.
 | |
| 
 | |
|   This function cleans up the file list and any related data structures. It has no
 | |
|   impact on the files themselves.
 | |
| 
 | |
|   @param FileList               The file list to free. Type EFI_SHELL_FILE_INFO is
 | |
|                                 defined in OpenFileList()
 | |
| 
 | |
|   @retval EFI_SUCCESS           Free the file list successfully.
 | |
|   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellFreeFileList(
 | |
|   IN EFI_SHELL_FILE_INFO **FileList
 | |
|   )
 | |
| {
 | |
|   EFI_SHELL_FILE_INFO *ShellFileListItem;
 | |
| 
 | |
|   if (FileList == NULL || *FileList == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
 | |
|       ; !IsListEmpty(&(*FileList)->Link)
 | |
|       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
 | |
|      ){
 | |
|     RemoveEntryList(&ShellFileListItem->Link);
 | |
|     InternalFreeShellFileInfoNode(ShellFileListItem);
 | |
|   }
 | |
|   InternalFreeShellFileInfoNode(*FileList);
 | |
|   *FileList = NULL;
 | |
|   return(EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Deletes the duplicate file names files in the given file list.
 | |
| 
 | |
|   This function deletes the reduplicate files in the given file list.
 | |
| 
 | |
|   @param FileList               A pointer to the first entry in the file list.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Always success.
 | |
|   @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellRemoveDupInFileList(
 | |
|   IN EFI_SHELL_FILE_INFO **FileList
 | |
|   )
 | |
| {
 | |
|   EFI_SHELL_FILE_INFO *ShellFileListItem;
 | |
|   EFI_SHELL_FILE_INFO *ShellFileListItem2;
 | |
|   EFI_SHELL_FILE_INFO *TempNode;
 | |
| 
 | |
|   if (FileList == NULL || *FileList == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
 | |
|       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
 | |
|       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
 | |
|      ){
 | |
|     for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
 | |
|         ; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link)
 | |
|         ; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link)
 | |
|        ){
 | |
|       if (gUnicodeCollation->StriColl(
 | |
|             gUnicodeCollation,
 | |
|             (CHAR16*)ShellFileListItem->FullName,
 | |
|             (CHAR16*)ShellFileListItem2->FullName) == 0
 | |
|          ){
 | |
|         TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode(
 | |
|                                             &(*FileList)->Link,
 | |
|                                             &ShellFileListItem2->Link
 | |
|                                             );
 | |
|         RemoveEntryList(&ShellFileListItem2->Link);
 | |
|         InternalFreeShellFileInfoNode(ShellFileListItem2);
 | |
|         // Set ShellFileListItem2 to PreviousNode so we don't access Freed
 | |
|         // memory in GetNextNode in the loop expression above.
 | |
|         ShellFileListItem2 = TempNode;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return (EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| //
 | |
| // This is the same structure as the external version, but it has no CONST qualifiers.
 | |
| //
 | |
| typedef struct {
 | |
|   LIST_ENTRY        Link;       ///< Linked list members.
 | |
|   EFI_STATUS        Status;     ///< Status of opening the file.  Valid only if Handle != NULL.
 | |
|         CHAR16      *FullName;  ///< Fully qualified filename.
 | |
|         CHAR16      *FileName;  ///< name of this file.
 | |
|   SHELL_FILE_HANDLE Handle;     ///< Handle for interacting with the opened file or NULL if closed.
 | |
|   EFI_FILE_INFO     *Info;      ///< Pointer to the FileInfo struct for this file or NULL.
 | |
| } EFI_SHELL_FILE_INFO_NO_CONST;
 | |
| 
 | |
| /**
 | |
|   Allocates and duplicates a EFI_SHELL_FILE_INFO node.
 | |
| 
 | |
|   @param[in] Node     The node to copy from.
 | |
|   @param[in] Save     TRUE to set Node->Handle to NULL, FALSE otherwise.
 | |
| 
 | |
|   @retval NULL        a memory allocation error ocurred
 | |
|   @return != NULL     a pointer to the new node
 | |
| **/
 | |
| EFI_SHELL_FILE_INFO*
 | |
| InternalDuplicateShellFileInfo(
 | |
|   IN       EFI_SHELL_FILE_INFO *Node,
 | |
|   IN BOOLEAN                   Save
 | |
|   )
 | |
| {
 | |
|   EFI_SHELL_FILE_INFO_NO_CONST *NewNode;
 | |
| 
 | |
|   //
 | |
|   // try to confirm that the objects are in sync
 | |
|   //
 | |
|   ASSERT(sizeof(EFI_SHELL_FILE_INFO_NO_CONST) == sizeof(EFI_SHELL_FILE_INFO));
 | |
| 
 | |
|   NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
 | |
|   if (NewNode == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
|   NewNode->FullName = AllocateCopyPool(StrSize(Node->FullName), Node->FullName);
 | |
|   NewNode->FileName = AllocateCopyPool(StrSize(Node->FileName), Node->FileName);
 | |
|   NewNode->Info     = AllocateCopyPool((UINTN)Node->Info->Size, Node->Info);
 | |
|   if ( NewNode->FullName == NULL
 | |
|     || NewNode->FileName == NULL
 | |
|     || NewNode->Info == NULL
 | |
|   ){
 | |
|     SHELL_FREE_NON_NULL(NewNode->FullName);
 | |
|     SHELL_FREE_NON_NULL(NewNode->FileName);
 | |
|     SHELL_FREE_NON_NULL(NewNode->Info);
 | |
|     SHELL_FREE_NON_NULL(NewNode);
 | |
|     return(NULL);
 | |
|   }
 | |
|   NewNode->Status = Node->Status;
 | |
|   NewNode->Handle = Node->Handle;
 | |
|   if (!Save) {
 | |
|     Node->Handle = NULL;
 | |
|   }
 | |
| 
 | |
|   return((EFI_SHELL_FILE_INFO*)NewNode);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Allocates and populates a EFI_SHELL_FILE_INFO structure.  if any memory operation
 | |
|   failed it will return NULL.
 | |
| 
 | |
|   @param[in] BasePath         the Path to prepend onto filename for FullPath
 | |
|   @param[in] Status           Status member initial value.
 | |
|   @param[in] FileName         FileName member initial value.
 | |
|   @param[in] Handle           Handle member initial value.
 | |
|   @param[in] Info             Info struct to copy.
 | |
| 
 | |
|   @retval NULL                An error ocurred.
 | |
|   @return                     a pointer to the newly allocated structure.
 | |
| **/
 | |
| EFI_SHELL_FILE_INFO *
 | |
| CreateAndPopulateShellFileInfo(
 | |
|   IN CONST CHAR16 *BasePath,
 | |
|   IN CONST EFI_STATUS Status,
 | |
|   IN CONST CHAR16 *FileName,
 | |
|   IN CONST SHELL_FILE_HANDLE Handle,
 | |
|   IN CONST EFI_FILE_INFO *Info
 | |
|   )
 | |
| {
 | |
|   EFI_SHELL_FILE_INFO *ShellFileListItem;
 | |
|   CHAR16              *TempString;
 | |
|   UINTN               Size;
 | |
| 
 | |
|   TempString = NULL;
 | |
|   Size = 0;
 | |
| 
 | |
|   ShellFileListItem = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
 | |
|   if (ShellFileListItem == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
|   if (Info != NULL && Info->Size != 0) {
 | |
|     ShellFileListItem->Info = AllocateZeroPool((UINTN)Info->Size);
 | |
|     if (ShellFileListItem->Info == NULL) {
 | |
|       FreePool(ShellFileListItem);
 | |
|       return (NULL);
 | |
|     }
 | |
|     CopyMem(ShellFileListItem->Info, Info, (UINTN)Info->Size);
 | |
|   } else {
 | |
|     ShellFileListItem->Info = NULL;
 | |
|   }
 | |
|   if (FileName != NULL) {
 | |
|     ASSERT(TempString == NULL);
 | |
|     ShellFileListItem->FileName = StrnCatGrow(&TempString, 0, FileName, 0);
 | |
|     if (ShellFileListItem->FileName == NULL) {
 | |
|       FreePool(ShellFileListItem->Info);
 | |
|       FreePool(ShellFileListItem);
 | |
|       return (NULL);
 | |
|     }
 | |
|   } else {
 | |
|     ShellFileListItem->FileName = NULL;
 | |
|   }
 | |
|   Size = 0;
 | |
|   TempString = NULL;
 | |
|   if (BasePath != NULL) {
 | |
|     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
 | |
|     TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
 | |
|     if (TempString == NULL) {
 | |
|       FreePool((VOID*)ShellFileListItem->FileName);
 | |
|       SHELL_FREE_NON_NULL(ShellFileListItem->Info);
 | |
|       FreePool(ShellFileListItem);
 | |
|       return (NULL);
 | |
|     }
 | |
|   }
 | |
|   if (ShellFileListItem->FileName != NULL) {
 | |
|     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
 | |
|     TempString = StrnCatGrow(&TempString, &Size, ShellFileListItem->FileName, 0);
 | |
|     if (TempString == NULL) {
 | |
|       FreePool((VOID*)ShellFileListItem->FileName);
 | |
|       FreePool(ShellFileListItem->Info);
 | |
|       FreePool(ShellFileListItem);
 | |
|       return (NULL);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   TempString = PathCleanUpDirectories(TempString);
 | |
| 
 | |
|   ShellFileListItem->FullName = TempString;
 | |
|   ShellFileListItem->Status   = Status;
 | |
|   ShellFileListItem->Handle   = Handle;
 | |
| 
 | |
|   return (ShellFileListItem);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Find all files in a specified directory.
 | |
| 
 | |
|   @param FileDirHandle          Handle of the directory to search.
 | |
|   @param FileList               On return, points to the list of files in the directory
 | |
|                                 or NULL if there are no files in the directory.
 | |
| 
 | |
|   @retval EFI_SUCCESS           File information was returned successfully.
 | |
|   @retval EFI_VOLUME_CORRUPTED  The file system structures have been corrupted.
 | |
|   @retval EFI_DEVICE_ERROR      The device reported an error.
 | |
|   @retval EFI_NO_MEDIA          The device media is not present.
 | |
|   @retval EFI_INVALID_PARAMETER The FileDirHandle was not a directory.
 | |
|   @return                       An error from FileHandleGetFileName().
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellFindFilesInDir(
 | |
|   IN SHELL_FILE_HANDLE FileDirHandle,
 | |
|   OUT EFI_SHELL_FILE_INFO **FileList
 | |
|   )
 | |
| {
 | |
|   EFI_SHELL_FILE_INFO       *ShellFileList;
 | |
|   EFI_SHELL_FILE_INFO       *ShellFileListItem;
 | |
|   EFI_FILE_INFO             *FileInfo;
 | |
|   EFI_STATUS                Status;
 | |
|   BOOLEAN                   NoFile;
 | |
|   CHAR16                    *TempString;
 | |
|   CHAR16                    *BasePath;
 | |
|   UINTN                     Size;
 | |
|   CHAR16                    *TempSpot;
 | |
| 
 | |
|   BasePath = NULL;
 | |
|   Status = FileHandleGetFileName(FileDirHandle, &BasePath);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (Status);
 | |
|   }
 | |
| 
 | |
|   if (ShellFileHandleGetPath(FileDirHandle) != NULL) {
 | |
|     TempString        = NULL;
 | |
|     Size              = 0;
 | |
|     TempString        = StrnCatGrow(&TempString, &Size, ShellFileHandleGetPath(FileDirHandle), 0);
 | |
|     if (TempString == NULL) {
 | |
|       SHELL_FREE_NON_NULL(BasePath);
 | |
|       return (EFI_OUT_OF_RESOURCES);
 | |
|     }
 | |
|     TempSpot          = StrStr(TempString, L";");
 | |
| 
 | |
|     if (TempSpot != NULL) {
 | |
|       *TempSpot = CHAR_NULL;
 | |
|     }
 | |
| 
 | |
|     TempString        = StrnCatGrow(&TempString, &Size, BasePath, 0);
 | |
|     if (TempString == NULL) {
 | |
|       SHELL_FREE_NON_NULL(BasePath);
 | |
|       return (EFI_OUT_OF_RESOURCES);
 | |
|     }
 | |
|     SHELL_FREE_NON_NULL(BasePath);
 | |
|     BasePath          = TempString;
 | |
|   }
 | |
| 
 | |
|   NoFile            = FALSE;
 | |
|   ShellFileList     = NULL;
 | |
|   ShellFileListItem = NULL;
 | |
|   FileInfo          = NULL;
 | |
|   Status            = EFI_SUCCESS;
 | |
| 
 | |
| 
 | |
|   for ( Status = FileHandleFindFirstFile(FileDirHandle, &FileInfo)
 | |
|       ; !EFI_ERROR(Status) && !NoFile
 | |
|       ; Status = FileHandleFindNextFile(FileDirHandle, FileInfo, &NoFile)
 | |
|      ){
 | |
|     if (ShellFileList == NULL) {
 | |
|       ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
 | |
|       if (ShellFileList == NULL) {
 | |
|         SHELL_FREE_NON_NULL (BasePath);
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
|       InitializeListHead(&ShellFileList->Link);
 | |
|     }
 | |
|     //
 | |
|     // allocate a new EFI_SHELL_FILE_INFO and populate it...
 | |
|     //
 | |
|     ShellFileListItem = CreateAndPopulateShellFileInfo(
 | |
|       BasePath,
 | |
|       EFI_SUCCESS,  // success since we didnt fail to open it...
 | |
|       FileInfo->FileName,
 | |
|       NULL,         // no handle since not open
 | |
|       FileInfo);
 | |
|     if (ShellFileListItem == NULL) {
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|       //
 | |
|       // Free resources outside the loop.
 | |
|       //
 | |
|       break;
 | |
|     }
 | |
|     InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
 | |
|   }
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     EfiShellFreeFileList(&ShellFileList);
 | |
|     *FileList = NULL;
 | |
|   } else {
 | |
|     *FileList = ShellFileList;
 | |
|   }
 | |
|   SHELL_FREE_NON_NULL(BasePath);
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the GUID value from a human readable name.
 | |
| 
 | |
|   If GuidName is a known GUID name, then update Guid to have the correct value for
 | |
|   that GUID.
 | |
| 
 | |
|   This function is only available when the major and minor versions in the
 | |
|   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
 | |
| 
 | |
|   @param[in]  GuidName   A pointer to the localized name for the GUID being queried.
 | |
|   @param[out] Guid       A pointer to the GUID structure to be filled in.
 | |
| 
 | |
|   @retval EFI_SUCCESS             The operation was successful.
 | |
|   @retval EFI_INVALID_PARAMETER   Guid was NULL.
 | |
|   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
 | |
|   @retval EFI_NOT_FOUND           GuidName is not a known GUID Name.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellGetGuidFromName(
 | |
|   IN  CONST CHAR16   *GuidName,
 | |
|   OUT       EFI_GUID *Guid
 | |
|   )
 | |
| {
 | |
|   EFI_GUID    *NewGuid;
 | |
|   EFI_STATUS  Status;
 | |
| 
 | |
|   if (Guid == NULL || GuidName == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   Status = GetGuidFromStringName(GuidName, NULL, &NewGuid);
 | |
| 
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     CopyGuid(Guid, NewGuid);
 | |
|   }
 | |
| 
 | |
|   return (Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the human readable name for a GUID from the value.
 | |
| 
 | |
|   If Guid is assigned a name, then update *GuidName to point to the name. The callee
 | |
|   should not modify the value.
 | |
| 
 | |
|   This function is only available when the major and minor versions in the
 | |
|   EfiShellProtocol are greater than or equal to 2 and 1, respectively.
 | |
| 
 | |
|   @param[in]  Guid       A pointer to the GUID being queried.
 | |
|   @param[out] GuidName   A pointer to a pointer the localized to name for the GUID being requested
 | |
| 
 | |
|   @retval EFI_SUCCESS             The operation was successful.
 | |
|   @retval EFI_INVALID_PARAMETER   Guid was NULL.
 | |
|   @retval EFI_INVALID_PARAMETER   GuidName was NULL.
 | |
|   @retval EFI_NOT_FOUND           Guid is not assigned a name.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellGetGuidName(
 | |
|   IN  CONST EFI_GUID *Guid,
 | |
|   OUT CONST CHAR16   **GuidName
 | |
|   )
 | |
| {
 | |
|   CHAR16   *Name;
 | |
| 
 | |
|   if (Guid == NULL || GuidName == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   Name = GetStringNameFromGuid(Guid, NULL);
 | |
|   if (Name == NULL || StrLen(Name) == 0) {
 | |
|     SHELL_FREE_NON_NULL(Name);
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   *GuidName = AddBufferToFreeList(Name);
 | |
| 
 | |
|   return (EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|   If FileHandle is a directory then the function reads from FileHandle and reads in
 | |
|   each of the FileInfo structures.  If one of them matches the Pattern's first
 | |
|   "level" then it opens that handle and calls itself on that handle.
 | |
| 
 | |
|   If FileHandle is a file and matches all of the remaining Pattern (which would be
 | |
|   on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
 | |
| 
 | |
|   Upon a EFI_SUCCESS return fromt he function any the caller is responsible to call
 | |
|   FreeFileList with FileList.
 | |
| 
 | |
|   @param[in] FilePattern         The FilePattern to check against.
 | |
|   @param[in] UnicodeCollation    The pointer to EFI_UNICODE_COLLATION_PROTOCOL structure
 | |
|   @param[in] FileHandle          The FileHandle to start with
 | |
|   @param[in, out] FileList       pointer to pointer to list of found files.
 | |
|   @param[in] ParentNode          The node for the parent. Same file as identified by HANDLE.
 | |
|   @param[in] MapName             The file system name this file is on.
 | |
| 
 | |
|   @retval EFI_SUCCESS           all files were found and the FileList contains a list.
 | |
|   @retval EFI_NOT_FOUND         no files were found
 | |
|   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed
 | |
| **/
 | |
| EFI_STATUS
 | |
| ShellSearchHandle(
 | |
|   IN     CONST CHAR16                         *FilePattern,
 | |
|   IN           EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
 | |
|   IN           SHELL_FILE_HANDLE              FileHandle,
 | |
|   IN OUT       EFI_SHELL_FILE_INFO            **FileList,
 | |
|   IN     CONST EFI_SHELL_FILE_INFO            *ParentNode OPTIONAL,
 | |
|   IN     CONST CHAR16                         *MapName
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS          Status;
 | |
|   CONST CHAR16        *NextFilePatternStart;
 | |
|   CHAR16              *CurrentFilePattern;
 | |
|   EFI_SHELL_FILE_INFO *ShellInfo;
 | |
|   EFI_SHELL_FILE_INFO *ShellInfoNode;
 | |
|   EFI_SHELL_FILE_INFO *NewShellNode;
 | |
|   EFI_FILE_INFO       *FileInfo;
 | |
|   BOOLEAN             Directory;
 | |
|   CHAR16              *NewFullName;
 | |
|   UINTN               Size;
 | |
| 
 | |
|   if ( FilePattern      == NULL
 | |
|     || UnicodeCollation == NULL
 | |
|     || FileList         == NULL
 | |
|    ){
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   ShellInfo = NULL;
 | |
|   CurrentFilePattern = NULL;
 | |
| 
 | |
|   if (*FilePattern == L'\\') {
 | |
|     FilePattern++;
 | |
|   }
 | |
| 
 | |
|   for( NextFilePatternStart = FilePattern
 | |
|      ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
 | |
|      ; NextFilePatternStart++);
 | |
| 
 | |
|   CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
 | |
|   if (CurrentFilePattern == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
| 
 | |
|   StrnCpyS(CurrentFilePattern, NextFilePatternStart-FilePattern+1, FilePattern, NextFilePatternStart-FilePattern);
 | |
| 
 | |
|   if (CurrentFilePattern[0]   == CHAR_NULL
 | |
|     &&NextFilePatternStart[0] == CHAR_NULL
 | |
|     ){
 | |
|     //
 | |
|     // we want the parent or root node (if no parent)
 | |
|     //
 | |
|     if (ParentNode == NULL) {
 | |
|       //
 | |
|       // We want the root node.  create the node.
 | |
|       //
 | |
|       FileInfo = FileHandleGetInfo(FileHandle);
 | |
|       NewShellNode = CreateAndPopulateShellFileInfo(
 | |
|         MapName,
 | |
|         EFI_SUCCESS,
 | |
|         L"\\",
 | |
|         FileHandle,
 | |
|         FileInfo
 | |
|         );
 | |
|       SHELL_FREE_NON_NULL(FileInfo);
 | |
|     } else {
 | |
|       //
 | |
|       // Add the current parameter FileHandle to the list, then end...
 | |
|       //
 | |
|       NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
 | |
|     }
 | |
|     if (NewShellNode == NULL) {
 | |
|       Status = EFI_OUT_OF_RESOURCES;
 | |
|     } else {
 | |
|       NewShellNode->Handle = NULL;
 | |
|       if (*FileList == NULL) {
 | |
|         *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
 | |
|         InitializeListHead(&((*FileList)->Link));
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Add to the returning to use list
 | |
|       //
 | |
|       InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
 | |
| 
 | |
|       Status = EFI_SUCCESS;
 | |
|     }
 | |
|   } else {
 | |
|     Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo);
 | |
| 
 | |
|     if (!EFI_ERROR(Status)){
 | |
|       if (StrStr(NextFilePatternStart, L"\\") != NULL){
 | |
|         Directory = TRUE;
 | |
|       } else {
 | |
|         Directory = FALSE;
 | |
|       }
 | |
|       for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link)
 | |
|           ; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link)
 | |
|           ; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link)
 | |
|          ){
 | |
|         if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){
 | |
|           if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) {
 | |
|             Size = StrSize (ShellInfoNode->FullName) + StrSize (MapName);
 | |
|             NewFullName = AllocateZeroPool(Size);
 | |
|             if (NewFullName == NULL) {
 | |
|               Status = EFI_OUT_OF_RESOURCES;
 | |
|             } else {
 | |
|               StrCpyS(NewFullName, Size / sizeof(CHAR16), MapName);
 | |
|               StrCatS(NewFullName, Size / sizeof(CHAR16), ShellInfoNode->FullName);
 | |
|               FreePool ((VOID *) ShellInfoNode->FullName);
 | |
|               ShellInfoNode->FullName = NewFullName;
 | |
|             }
 | |
|           }
 | |
|           if (Directory && !EFI_ERROR(Status) && ShellInfoNode->FullName != NULL && ShellInfoNode->FileName != NULL){
 | |
|             //
 | |
|             // should be a directory
 | |
|             //
 | |
| 
 | |
|             //
 | |
|             // don't open the . and .. directories
 | |
|             //
 | |
|             if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
 | |
|               && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
 | |
|              ){
 | |
|               //
 | |
|               //
 | |
|               //
 | |
|               if (EFI_ERROR(Status)) {
 | |
|                 break;
 | |
|               }
 | |
|               //
 | |
|               // Open the directory since we need that handle in the next recursion.
 | |
|               //
 | |
|               ShellInfoNode->Status = EfiShellOpenFileByName (ShellInfoNode->FullName, &ShellInfoNode->Handle, EFI_FILE_MODE_READ);
 | |
| 
 | |
|               //
 | |
|               // recurse with the next part of the pattern
 | |
|               //
 | |
|               Status = ShellSearchHandle(NextFilePatternStart, UnicodeCollation, ShellInfoNode->Handle, FileList, ShellInfoNode, MapName);
 | |
|               EfiShellClose(ShellInfoNode->Handle);
 | |
|               ShellInfoNode->Handle = NULL;
 | |
|             }
 | |
|           } else if (!EFI_ERROR(Status)) {
 | |
|             //
 | |
|             // should be a file
 | |
|             //
 | |
| 
 | |
|             //
 | |
|             // copy the information we need into a new Node
 | |
|             //
 | |
|             NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
 | |
|             if (NewShellNode == NULL) {
 | |
|               Status = EFI_OUT_OF_RESOURCES;
 | |
|             }
 | |
|             if (*FileList == NULL) {
 | |
|               *FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
 | |
|               InitializeListHead(&((*FileList)->Link));
 | |
|             }
 | |
| 
 | |
|             //
 | |
|             // Add to the returning to use list
 | |
|             //
 | |
|             InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
 | |
|           }
 | |
|         }
 | |
|         if (EFI_ERROR(Status)) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         EfiShellFreeFileList(&ShellInfo);
 | |
|       } else {
 | |
|         Status = EfiShellFreeFileList(&ShellInfo);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (*FileList == NULL || (*FileList != NULL && IsListEmpty(&(*FileList)->Link))) {
 | |
|     Status = EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   FreePool(CurrentFilePattern);
 | |
|   return (Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Find files that match a specified pattern.
 | |
| 
 | |
|   This function searches for all files and directories that match the specified
 | |
|   FilePattern. The FilePattern can contain wild-card characters. The resulting file
 | |
|   information is placed in the file list FileList.
 | |
| 
 | |
|   Wildcards are processed
 | |
|   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
 | |
| 
 | |
|   The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
 | |
|   field is set to NULL.
 | |
| 
 | |
|   if *FileList is not NULL then it must be a pre-existing and properly initialized list.
 | |
| 
 | |
|   @param FilePattern      Points to a NULL-terminated shell file path, including wildcards.
 | |
|   @param FileList         On return, points to the start of a file list containing the names
 | |
|                           of all matching files or else points to NULL if no matching files
 | |
|                           were found.  only on a EFI_SUCCESS return will; this be non-NULL.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Files found.  FileList is a valid list.
 | |
|   @retval EFI_NOT_FOUND         No files found.
 | |
|   @retval EFI_NO_MEDIA          The device has no media
 | |
|   @retval EFI_DEVICE_ERROR      The device reported an error
 | |
|   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellFindFiles(
 | |
|   IN CONST CHAR16 *FilePattern,
 | |
|   OUT EFI_SHELL_FILE_INFO **FileList
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                      Status;
 | |
|   CHAR16                          *PatternCopy;
 | |
|   CHAR16                          *PatternCurrentLocation;
 | |
|   EFI_DEVICE_PATH_PROTOCOL        *RootDevicePath;
 | |
|   SHELL_FILE_HANDLE               RootFileHandle;
 | |
|   CHAR16                          *MapName;
 | |
|   UINTN                           Count;
 | |
| 
 | |
|   if ( FilePattern      == NULL
 | |
|     || FileList         == NULL
 | |
|     || StrStr(FilePattern, L":") == NULL
 | |
|    ){
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   Status = EFI_SUCCESS;
 | |
|   RootDevicePath = NULL;
 | |
|   RootFileHandle = NULL;
 | |
|   MapName        = NULL;
 | |
|   PatternCopy = AllocateCopyPool(StrSize(FilePattern), FilePattern);
 | |
|   if (PatternCopy == NULL) {
 | |
|     return (EFI_OUT_OF_RESOURCES);
 | |
|   }
 | |
| 
 | |
|   PatternCopy = PathCleanUpDirectories(PatternCopy);
 | |
| 
 | |
|   Count = StrStr(PatternCopy, L":") - PatternCopy + 1;
 | |
|   ASSERT (Count <= StrLen (PatternCopy));
 | |
| 
 | |
|   ASSERT(MapName == NULL);
 | |
|   MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
 | |
|   if (MapName == NULL) {
 | |
|     Status = EFI_OUT_OF_RESOURCES;
 | |
|   } else {
 | |
|     RootDevicePath = EfiShellGetDevicePathFromFilePath(PatternCopy);
 | |
|     if (RootDevicePath == NULL) {
 | |
|       Status = EFI_INVALID_PARAMETER;
 | |
|     } else {
 | |
|       Status = EfiShellOpenRoot(RootDevicePath, &RootFileHandle);
 | |
|       if (!EFI_ERROR(Status)) {
 | |
|         for ( PatternCurrentLocation = PatternCopy
 | |
|             ; *PatternCurrentLocation != ':'
 | |
|             ; PatternCurrentLocation++);
 | |
|         PatternCurrentLocation++;
 | |
|         Status = ShellSearchHandle(PatternCurrentLocation, gUnicodeCollation, RootFileHandle, FileList, NULL, MapName);
 | |
|         EfiShellClose(RootFileHandle);
 | |
|       }
 | |
|       FreePool(RootDevicePath);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   SHELL_FREE_NON_NULL(PatternCopy);
 | |
|   SHELL_FREE_NON_NULL(MapName);
 | |
| 
 | |
|   return(Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Opens the files that match the path specified.
 | |
| 
 | |
|   This function opens all of the files specified by Path. Wildcards are processed
 | |
|   according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each
 | |
|   matching file has an EFI_SHELL_FILE_INFO structure created in a linked list.
 | |
| 
 | |
|   @param Path                   A pointer to the path string.
 | |
|   @param OpenMode               Specifies the mode used to open each file, EFI_FILE_MODE_READ or
 | |
|                                 EFI_FILE_MODE_WRITE.
 | |
|   @param FileList               Points to the start of a list of files opened.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Create the file list successfully.
 | |
|   @return Others                Can't create the file list.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellOpenFileList(
 | |
|   IN CHAR16 *Path,
 | |
|   IN UINT64 OpenMode,
 | |
|   IN OUT EFI_SHELL_FILE_INFO **FileList
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS Status;
 | |
|   EFI_SHELL_FILE_INFO *ShellFileListItem;
 | |
|   CHAR16              *Path2;
 | |
|   UINTN               Path2Size;
 | |
|   CONST CHAR16        *CurDir;
 | |
|   BOOLEAN             Found;
 | |
| 
 | |
|   PathCleanUpDirectories(Path);
 | |
| 
 | |
|   Path2Size     = 0;
 | |
|   Path2         = NULL;
 | |
| 
 | |
|   if (FileList == NULL || *FileList == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   if (*Path == L'.' && *(Path+1) == L'\\') {
 | |
|     Path+=2;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // convert a local path to an absolute path
 | |
|   //
 | |
|   if (StrStr(Path, L":") == NULL) {
 | |
|     CurDir = EfiShellGetCurDir(NULL);
 | |
|     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
 | |
|     StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
 | |
|     StrnCatGrow(&Path2, &Path2Size, L"\\", 0);
 | |
|     if (*Path == L'\\') {
 | |
|       Path++;
 | |
|       while (PathRemoveLastItem(Path2)) ;
 | |
|     }
 | |
|     ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
 | |
|     StrnCatGrow(&Path2, &Path2Size, Path, 0);
 | |
|   } else {
 | |
|     ASSERT(Path2 == NULL);
 | |
|     StrnCatGrow(&Path2, NULL, Path, 0);
 | |
|   }
 | |
| 
 | |
|   PathCleanUpDirectories (Path2);
 | |
| 
 | |
|   //
 | |
|   // do the search
 | |
|   //
 | |
|   Status = EfiShellFindFiles(Path2, FileList);
 | |
| 
 | |
|   FreePool(Path2);
 | |
| 
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (Status);
 | |
|   }
 | |
| 
 | |
|   Found = FALSE;
 | |
|   //
 | |
|   // We had no errors so open all the files (that are not already opened...)
 | |
|   //
 | |
|   for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
 | |
|       ; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link)
 | |
|       ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link)
 | |
|      ){
 | |
|     if (ShellFileListItem->Status == 0 && ShellFileListItem->Handle == NULL) {
 | |
|       ShellFileListItem->Status = EfiShellOpenFileByName (ShellFileListItem->FullName, &ShellFileListItem->Handle, OpenMode);
 | |
|       Found = TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (!Found) {
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
|   return(EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the environment variable and Attributes, or list of environment variables.  Can be
 | |
|   used instead of GetEnv().
 | |
| 
 | |
|   This function returns the current value of the specified environment variable and
 | |
|   the Attributes. If no variable name was specified, then all of the known
 | |
|   variables will be returned.
 | |
| 
 | |
|   @param[in] Name               A pointer to the environment variable name. If Name is NULL,
 | |
|                                 then the function will return all of the defined shell
 | |
|                                 environment variables. In the case where multiple environment
 | |
|                                 variables are being returned, each variable will be terminated
 | |
|                                 by a NULL, and the list will be terminated by a double NULL.
 | |
|   @param[out] Attributes        If not NULL, a pointer to the returned attributes bitmask for
 | |
|                                 the environment variable. In the case where Name is NULL, and
 | |
|                                 multiple environment variables are being returned, Attributes
 | |
|                                 is undefined.
 | |
| 
 | |
|   @retval NULL                  The environment variable doesn't exist.
 | |
|   @return                       A non-NULL value points to the variable's value. The returned
 | |
|                                 pointer does not need to be freed by the caller.
 | |
| **/
 | |
| CONST CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetEnvEx(
 | |
|   IN  CONST CHAR16 *Name,
 | |
|   OUT       UINT32 *Attributes OPTIONAL
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   VOID        *Buffer;
 | |
|   UINTN       Size;
 | |
|   ENV_VAR_LIST *Node;
 | |
|   CHAR16      *CurrentWriteLocation;
 | |
| 
 | |
|   Size = 0;
 | |
|   Buffer = NULL;
 | |
| 
 | |
|   if (Name == NULL) {
 | |
| 
 | |
|     //
 | |
|     // Build the semi-colon delimited list. (2 passes)
 | |
|     //
 | |
|     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
 | |
|       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
 | |
|       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
 | |
|      ){
 | |
|       ASSERT(Node->Key != NULL);
 | |
|       Size += StrSize(Node->Key);
 | |
|     }
 | |
| 
 | |
|     Size += 2*sizeof(CHAR16);
 | |
| 
 | |
|     Buffer = AllocateZeroPool(Size);
 | |
|     if (Buffer == NULL) {
 | |
|       return (NULL);
 | |
|     }
 | |
|     CurrentWriteLocation = (CHAR16*)Buffer;
 | |
| 
 | |
|     for ( Node = (ENV_VAR_LIST*)GetFirstNode(&gShellEnvVarList.Link)
 | |
|       ; !IsNull(&gShellEnvVarList.Link, &Node->Link)
 | |
|       ; Node = (ENV_VAR_LIST*)GetNextNode(&gShellEnvVarList.Link, &Node->Link)
 | |
|      ){
 | |
|       ASSERT(Node->Key != NULL);
 | |
|       StrCpyS( CurrentWriteLocation,
 | |
|                 (Size)/sizeof(CHAR16) - (CurrentWriteLocation - ((CHAR16*)Buffer)),
 | |
|                 Node->Key
 | |
|                 );
 | |
|       CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
 | |
|     }
 | |
| 
 | |
|   } else {
 | |
|     //
 | |
|     // We are doing a specific environment variable
 | |
|     //
 | |
|     Status = ShellFindEnvVarInList(Name, (CHAR16**)&Buffer, &Size, Attributes);
 | |
| 
 | |
|     if (EFI_ERROR(Status)){
 | |
|       //
 | |
|       // get the size we need for this EnvVariable
 | |
|       //
 | |
|       Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
 | |
|       if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|         //
 | |
|         // Allocate the space and recall the get function
 | |
|         //
 | |
|         Buffer = AllocateZeroPool(Size);
 | |
|         Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer);
 | |
|       }
 | |
|       //
 | |
|       // we didnt get it (might not exist)
 | |
|       // free the memory if we allocated any and return NULL
 | |
|       //
 | |
|       if (EFI_ERROR(Status)) {
 | |
|         if (Buffer != NULL) {
 | |
|           FreePool(Buffer);
 | |
|         }
 | |
|         return (NULL);
 | |
|       } else {
 | |
|         //
 | |
|         // If we did not find the environment variable in the gShellEnvVarList
 | |
|         // but get it from UEFI variable storage successfully then we need update
 | |
|         // the gShellEnvVarList.
 | |
|         //
 | |
|         ShellFreeEnvVarList ();
 | |
|         Status = ShellInitEnvVarList ();
 | |
|         ASSERT (Status == EFI_SUCCESS);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // return the buffer
 | |
|   //
 | |
|   return (AddBufferToFreeList(Buffer));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets either a single or list of environment variables.
 | |
| 
 | |
|   If name is not NULL then this function returns the current value of the specified
 | |
|   environment variable.
 | |
| 
 | |
|   If Name is NULL, then a list of all environment variable names is returned.  Each is a
 | |
|   NULL terminated string with a double NULL terminating the list.
 | |
| 
 | |
|   @param Name                   A pointer to the environment variable name.  If
 | |
|                                 Name is NULL, then the function will return all
 | |
|                                 of the defined shell environment variables.  In
 | |
|                                 the case where multiple environment variables are
 | |
|                                 being returned, each variable will be terminated by
 | |
|                                 a NULL, and the list will be terminated by a double
 | |
|                                 NULL.
 | |
| 
 | |
|   @retval !=NULL                A pointer to the returned string.
 | |
|                                 The returned pointer does not need to be freed by the caller.
 | |
| 
 | |
|   @retval NULL                  The environment variable doesn't exist or there are
 | |
|                                 no environment variables.
 | |
| **/
 | |
| CONST CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetEnv(
 | |
|   IN CONST CHAR16 *Name
 | |
|   )
 | |
| {
 | |
|   return (EfiShellGetEnvEx(Name, NULL));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Internal variable setting function.  Allows for setting of the read only variables.
 | |
| 
 | |
|   @param Name                   Points to the NULL-terminated environment variable name.
 | |
|   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
 | |
|                                 empty string then the environment variable is deleted.
 | |
|   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
 | |
| 
 | |
|   @retval EFI_SUCCESS           The environment variable was successfully updated.
 | |
| **/
 | |
| EFI_STATUS
 | |
| InternalEfiShellSetEnv(
 | |
|   IN CONST CHAR16 *Name,
 | |
|   IN CONST CHAR16 *Value,
 | |
|   IN BOOLEAN Volatile
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS      Status;
 | |
| 
 | |
|   if (Value == NULL || StrLen(Value) == 0) {
 | |
|     Status = SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
 | |
|     if (!EFI_ERROR(Status)) {
 | |
|       ShellRemvoeEnvVarFromList(Name);
 | |
|     }
 | |
|   } else {
 | |
|     SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
 | |
|     Status = ShellAddEnvVarToList(
 | |
|                Name, Value, StrSize(Value),
 | |
|                EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE)
 | |
|                );
 | |
|     if (!EFI_ERROR (Status)) {
 | |
|       Status = Volatile
 | |
|              ? SHELL_SET_ENVIRONMENT_VARIABLE_V (Name, StrSize (Value) - sizeof (CHAR16), Value)
 | |
|              : SHELL_SET_ENVIRONMENT_VARIABLE_NV (Name, StrSize (Value) - sizeof (CHAR16), Value);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         ShellRemvoeEnvVarFromList(Name);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Sets the environment variable.
 | |
| 
 | |
|   This function changes the current value of the specified environment variable. If the
 | |
|   environment variable exists and the Value is an empty string, then the environment
 | |
|   variable is deleted. If the environment variable exists and the Value is not an empty
 | |
|   string, then the value of the environment variable is changed. If the environment
 | |
|   variable does not exist and the Value is an empty string, there is no action. If the
 | |
|   environment variable does not exist and the Value is a non-empty string, then the
 | |
|   environment variable is created and assigned the specified value.
 | |
| 
 | |
|   For a description of volatile and non-volatile environment variables, see UEFI Shell
 | |
|   2.0 specification section 3.6.1.
 | |
| 
 | |
|   @param Name                   Points to the NULL-terminated environment variable name.
 | |
|   @param Value                  Points to the NULL-terminated environment variable value. If the value is an
 | |
|                                 empty string then the environment variable is deleted.
 | |
|   @param Volatile               Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
 | |
| 
 | |
|   @retval EFI_SUCCESS           The environment variable was successfully updated.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellSetEnv(
 | |
|   IN CONST CHAR16 *Name,
 | |
|   IN CONST CHAR16 *Value,
 | |
|   IN BOOLEAN Volatile
 | |
|   )
 | |
| {
 | |
|   if (Name == NULL || *Name == CHAR_NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   //
 | |
|   // Make sure we dont 'set' a predefined read only variable
 | |
|   //
 | |
|   if ((StrCmp (Name, L"cwd") == 0) ||
 | |
|       (StrCmp (Name, L"lasterror") == 0) ||
 | |
|       (StrCmp (Name, L"profiles") == 0) ||
 | |
|       (StrCmp (Name, L"uefishellsupport") == 0) ||
 | |
|       (StrCmp (Name, L"uefishellversion") == 0) ||
 | |
|       (StrCmp (Name, L"uefiversion") == 0) ||
 | |
|       (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoNest &&
 | |
|        StrCmp (Name, mNoNestingEnvVarName) == 0)
 | |
|      ) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
|   return (InternalEfiShellSetEnv(Name, Value, Volatile));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Returns the current directory on the specified device.
 | |
| 
 | |
|   If FileSystemMapping is NULL, it returns the current working directory. If the
 | |
|   FileSystemMapping is not NULL, it returns the current directory associated with the
 | |
|   FileSystemMapping. In both cases, the returned name includes the file system
 | |
|   mapping (i.e. fs0:\current-dir).
 | |
| 
 | |
|   Note that the current directory string should exclude the tailing backslash character.
 | |
| 
 | |
|   @param FileSystemMapping      A pointer to the file system mapping. If NULL,
 | |
|                                 then the current working directory is returned.
 | |
| 
 | |
|   @retval !=NULL                The current directory.
 | |
|   @retval NULL                  Current directory does not exist.
 | |
| **/
 | |
| CONST CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetCurDir(
 | |
|   IN CONST CHAR16 *FileSystemMapping OPTIONAL
 | |
|   )
 | |
| {
 | |
|   CHAR16  *PathToReturn;
 | |
|   UINTN   Size;
 | |
|   SHELL_MAP_LIST *MapListItem;
 | |
|   if (!IsListEmpty(&gShellMapList.Link)) {
 | |
|     //
 | |
|     // if parameter is NULL, use current
 | |
|     //
 | |
|     if (FileSystemMapping == NULL) {
 | |
|       return (EfiShellGetEnv(L"cwd"));
 | |
|     } else {
 | |
|       Size = 0;
 | |
|       PathToReturn = NULL;
 | |
|       MapListItem = ShellCommandFindMapItem(FileSystemMapping);
 | |
|       if (MapListItem != NULL) {
 | |
|         ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
 | |
|         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
 | |
|         PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
 | |
|       }
 | |
|     }
 | |
|     return (AddBufferToFreeList(PathToReturn));
 | |
|   } else {
 | |
|     return (NULL);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Changes the current directory on the specified device.
 | |
| 
 | |
|   If the FileSystem is NULL, and the directory Dir does not contain a file system's
 | |
|   mapped name, this function changes the current working directory.
 | |
| 
 | |
|   If the FileSystem is NULL and the directory Dir contains a mapped name, then the
 | |
|   current file system and the current directory on that file system are changed.
 | |
| 
 | |
|   If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
 | |
|   system.
 | |
| 
 | |
|   If FileSystem is not NULL and Dir is not NULL, then this function changes the current
 | |
|   directory on the specified file system.
 | |
| 
 | |
|   If the current working directory or the current working file system is changed then the
 | |
|   %cwd% environment variable will be updated
 | |
| 
 | |
|   Note that the current directory string should exclude the tailing backslash character.
 | |
| 
 | |
|   @param FileSystem             A pointer to the file system's mapped name. If NULL, then the current working
 | |
|                                 directory is changed.
 | |
|   @param Dir                    Points to the NULL-terminated directory on the device specified by FileSystem.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The operation was sucessful
 | |
|   @retval EFI_NOT_FOUND         The file system could not be found
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellSetCurDir(
 | |
|   IN CONST CHAR16 *FileSystem OPTIONAL,
 | |
|   IN CONST CHAR16 *Dir
 | |
|   )
 | |
| {
 | |
|   CHAR16          *MapName;
 | |
|   SHELL_MAP_LIST  *MapListItem;
 | |
|   UINTN           Size;
 | |
|   EFI_STATUS      Status;
 | |
|   CHAR16          *TempString;
 | |
|   CHAR16          *DirectoryName;
 | |
|   UINTN           TempLen;
 | |
| 
 | |
|   Size          = 0;
 | |
|   MapName       = NULL;
 | |
|   MapListItem   = NULL;
 | |
|   TempString    = NULL;
 | |
|   DirectoryName = NULL;
 | |
| 
 | |
|   if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   if (IsListEmpty(&gShellMapList.Link)){
 | |
|     return (EFI_NOT_FOUND);
 | |
|   }
 | |
| 
 | |
|   DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
 | |
|   ASSERT(DirectoryName != NULL);
 | |
| 
 | |
|   PathCleanUpDirectories(DirectoryName);
 | |
| 
 | |
|   if (FileSystem == NULL) {
 | |
|     //
 | |
|     // determine the file system mapping to use
 | |
|     //
 | |
|     if (StrStr(DirectoryName, L":") != NULL) {
 | |
|       ASSERT(MapName == NULL);
 | |
|       MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
 | |
|     }
 | |
|     //
 | |
|     // find the file system mapping's entry in the list
 | |
|     // or use current
 | |
|     //
 | |
|     if (MapName != NULL) {
 | |
|       MapListItem = ShellCommandFindMapItem(MapName);
 | |
| 
 | |
|       //
 | |
|       // make that the current file system mapping
 | |
|       //
 | |
|       if (MapListItem != NULL) {
 | |
|         gShellCurMapping = MapListItem;
 | |
|       }
 | |
|     } else {
 | |
|       MapListItem = gShellCurMapping;
 | |
|     }
 | |
| 
 | |
|     if (MapListItem == NULL) {
 | |
|       FreePool (DirectoryName);
 | |
|       SHELL_FREE_NON_NULL(MapName);
 | |
|       return (EFI_NOT_FOUND);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // now update the MapListItem's current directory
 | |
|     //
 | |
|     if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
 | |
|       FreePool(MapListItem->CurrentDirectoryPath);
 | |
|       MapListItem->CurrentDirectoryPath = NULL;
 | |
|     }
 | |
|     if (MapName != NULL) {
 | |
|       TempLen = StrLen(MapName);
 | |
|       if (TempLen != StrLen(DirectoryName)) {
 | |
|         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|         MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
 | |
|       }
 | |
|       FreePool (MapName);
 | |
|     } else {
 | |
|       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
 | |
|     }
 | |
|     if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
 | |
|       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|       if (MapListItem->CurrentDirectoryPath != NULL) {
 | |
|         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
 | |
|     }
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // cant have a mapping in the directory...
 | |
|     //
 | |
|     if (StrStr(DirectoryName, L":") != NULL) {
 | |
|       FreePool (DirectoryName);
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
|     //
 | |
|     // FileSystem != NULL
 | |
|     //
 | |
|     MapListItem = ShellCommandFindMapItem(FileSystem);
 | |
|     if (MapListItem == NULL) {
 | |
|       FreePool (DirectoryName);
 | |
|       return (EFI_INVALID_PARAMETER);
 | |
|     }
 | |
| //    gShellCurMapping = MapListItem;
 | |
|     if (DirectoryName != NULL) {
 | |
|       //
 | |
|       // change current dir on that file system
 | |
|       //
 | |
| 
 | |
|       if (MapListItem->CurrentDirectoryPath != NULL) {
 | |
|         FreePool(MapListItem->CurrentDirectoryPath);
 | |
|         DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
 | |
|       }
 | |
| //      ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
| //      MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
 | |
|       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
 | |
|       ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|       MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
 | |
|       if (MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] == L'\\') {
 | |
|         ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
 | |
|         MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] = CHAR_NULL;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   FreePool (DirectoryName);
 | |
|   //
 | |
|   // if updated the current directory then update the environment variable
 | |
|   //
 | |
|   if (MapListItem == gShellCurMapping) {
 | |
|     Size = 0;
 | |
|     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
 | |
|     StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
 | |
|     ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
 | |
|     StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
 | |
|     Status =  InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
 | |
|     FreePool(TempString);
 | |
|     return (Status);
 | |
|   }
 | |
|   return(EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Return help information about a specific command.
 | |
| 
 | |
|   This function returns the help information for the specified command. The help text
 | |
|   can be internal to the shell or can be from a UEFI Shell manual page.
 | |
| 
 | |
|   If Sections is specified, then each section name listed will be compared in a casesensitive
 | |
|   manner, to the section names described in Appendix B. If the section exists,
 | |
|   it will be appended to the returned help text. If the section does not exist, no
 | |
|   information will be returned. If Sections is NULL, then all help text information
 | |
|   available will be returned.
 | |
| 
 | |
|   @param Command                Points to the NULL-terminated UEFI Shell command name.
 | |
|   @param Sections               Points to the NULL-terminated comma-delimited
 | |
|                                 section names to return. If NULL, then all
 | |
|                                 sections will be returned.
 | |
|   @param HelpText               On return, points to a callee-allocated buffer
 | |
|                                 containing all specified help text.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The help text was returned.
 | |
|   @retval EFI_OUT_OF_RESOURCES  The necessary buffer could not be allocated to hold the
 | |
|                                 returned help text.
 | |
|   @retval EFI_INVALID_PARAMETER HelpText is NULL
 | |
|   @retval EFI_NOT_FOUND         There is no help text available for Command.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellGetHelpText(
 | |
|   IN CONST CHAR16 *Command,
 | |
|   IN CONST CHAR16 *Sections OPTIONAL,
 | |
|   OUT CHAR16 **HelpText
 | |
|   )
 | |
| {
 | |
|   CONST CHAR16  *ManFileName;
 | |
|   CHAR16        *FixCommand;
 | |
|   EFI_STATUS    Status;
 | |
| 
 | |
|   ASSERT(HelpText != NULL);
 | |
|   FixCommand = NULL;
 | |
| 
 | |
|   ManFileName = ShellCommandGetManFileNameHandler(Command);
 | |
| 
 | |
|   if (ManFileName != NULL) {
 | |
|     return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
 | |
|   } else {
 | |
|     if ((StrLen(Command)> 4)
 | |
|     && (Command[StrLen(Command)-1] == L'i' || Command[StrLen(Command)-1] == L'I')
 | |
|     && (Command[StrLen(Command)-2] == L'f' || Command[StrLen(Command)-2] == L'F')
 | |
|     && (Command[StrLen(Command)-3] == L'e' || Command[StrLen(Command)-3] == L'E')
 | |
|     && (Command[StrLen(Command)-4] == L'.')
 | |
|     ) {
 | |
|       FixCommand = AllocateZeroPool(StrSize(Command) - 4 * sizeof (CHAR16));
 | |
|       if (FixCommand == NULL) {
 | |
|         return EFI_OUT_OF_RESOURCES;
 | |
|       }
 | |
| 
 | |
|       StrnCpyS( FixCommand,
 | |
|                 (StrSize(Command) - 4 * sizeof (CHAR16))/sizeof(CHAR16),
 | |
|                 Command,
 | |
|                 StrLen(Command)-4
 | |
|                 );
 | |
|       Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText);
 | |
|       FreePool(FixCommand);
 | |
|       return Status;
 | |
|     } else {
 | |
|       return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Gets the enable status of the page break output mode.
 | |
| 
 | |
|   User can use this function to determine current page break mode.
 | |
| 
 | |
|   @retval TRUE                  The page break output mode is enabled.
 | |
|   @retval FALSE                 The page break output mode is disabled.
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| EfiShellGetPageBreak(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   return(ShellInfoObject.PageBreakEnabled);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Judges whether the active shell is the root shell.
 | |
| 
 | |
|   This function makes the user to know that whether the active Shell is the root shell.
 | |
| 
 | |
|   @retval TRUE                  The active Shell is the root Shell.
 | |
|   @retval FALSE                 The active Shell is NOT the root Shell.
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| EfiShellIsRootShell(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   return(ShellInfoObject.RootShellInstance);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   function to return a semi-colon delimeted list of all alias' in the current shell
 | |
| 
 | |
|   up to caller to free the memory.
 | |
| 
 | |
|   @retval NULL    No alias' were found
 | |
|   @retval NULL    An error ocurred getting alias'
 | |
|   @return !NULL   a list of all alias'
 | |
| **/
 | |
| CHAR16 *
 | |
| InternalEfiShellGetListAlias(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
| 
 | |
|   EFI_STATUS        Status;
 | |
|   EFI_GUID          Guid;
 | |
|   CHAR16            *VariableName;
 | |
|   UINTN             NameSize;
 | |
|   UINTN             NameBufferSize;
 | |
|   CHAR16            *RetVal;
 | |
|   UINTN             RetSize;
 | |
| 
 | |
|   NameBufferSize = INIT_NAME_BUFFER_SIZE;
 | |
|   VariableName  = AllocateZeroPool(NameBufferSize);
 | |
|   RetSize       = 0;
 | |
|   RetVal        = NULL;
 | |
| 
 | |
|   if (VariableName == NULL) {
 | |
|     return (NULL);
 | |
|   }
 | |
| 
 | |
|   VariableName[0] = CHAR_NULL;
 | |
| 
 | |
|   while (TRUE) {
 | |
|     NameSize = NameBufferSize;
 | |
|     Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
 | |
|     if (Status == EFI_NOT_FOUND){
 | |
|       break;
 | |
|     } else if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|       NameBufferSize = NameSize > NameBufferSize * 2 ? NameSize : NameBufferSize * 2;
 | |
|       SHELL_FREE_NON_NULL(VariableName);
 | |
|       VariableName = AllocateZeroPool(NameBufferSize);
 | |
|       if (VariableName == NULL) {
 | |
|         Status = EFI_OUT_OF_RESOURCES;
 | |
|         SHELL_FREE_NON_NULL(RetVal);
 | |
|         RetVal = NULL;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       NameSize = NameBufferSize;
 | |
|       Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
 | |
|     }
 | |
| 
 | |
|     if (EFI_ERROR (Status)) {
 | |
|       SHELL_FREE_NON_NULL(RetVal);
 | |
|       RetVal = NULL;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     if (CompareGuid(&Guid, &gShellAliasGuid)){
 | |
|       ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
 | |
|       RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
 | |
|       RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
 | |
|     } // compare guid
 | |
|   } // while
 | |
|   SHELL_FREE_NON_NULL(VariableName);
 | |
| 
 | |
|   return (RetVal);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Convert a null-terminated unicode string, in-place, to all lowercase.
 | |
|   Then return it.
 | |
| 
 | |
|   @param  Str    The null-terminated string to be converted to all lowercase.
 | |
| 
 | |
|   @return        The null-terminated string converted into all lowercase.
 | |
| **/
 | |
| CHAR16 *
 | |
| ToLower (
 | |
|   CHAR16 *Str
 | |
|   )
 | |
| {
 | |
|   UINTN Index;
 | |
| 
 | |
|   for (Index = 0; Str[Index] != L'\0'; Index++) {
 | |
|     if (Str[Index] >= L'A' && Str[Index] <= L'Z') {
 | |
|       Str[Index] -= (CHAR16)(L'A' - L'a');
 | |
|     }
 | |
|   }
 | |
|   return Str;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This function returns the command associated with a alias or a list of all
 | |
|   alias'.
 | |
| 
 | |
|   @param[in] Alias              Points to the NULL-terminated shell alias.
 | |
|                                 If this parameter is NULL, then all
 | |
|                                 aliases will be returned in ReturnedData.
 | |
|   @param[out] Volatile          upon return of a single command if TRUE indicates
 | |
|                                 this is stored in a volatile fashion.  FALSE otherwise.
 | |
| 
 | |
|   @return                        If Alias is not NULL, it will return a pointer to
 | |
|                                 the NULL-terminated command for that alias.
 | |
|                                 If Alias is NULL, ReturnedData points to a ';'
 | |
|                                 delimited list of alias (e.g.
 | |
|                                 ReturnedData = "dir;del;copy;mfp") that is NULL-terminated.
 | |
|   @retval NULL                  an error ocurred
 | |
|   @retval NULL                  Alias was not a valid Alias
 | |
| **/
 | |
| CONST CHAR16 *
 | |
| EFIAPI
 | |
| EfiShellGetAlias(
 | |
|   IN  CONST CHAR16 *Alias,
 | |
|   OUT BOOLEAN      *Volatile OPTIONAL
 | |
|   )
 | |
| {
 | |
|   CHAR16      *RetVal;
 | |
|   UINTN       RetSize;
 | |
|   UINT32      Attribs;
 | |
|   EFI_STATUS  Status;
 | |
|   CHAR16      *AliasLower;
 | |
|   CHAR16      *AliasVal;
 | |
| 
 | |
|   // Convert to lowercase to make aliases case-insensitive
 | |
|   if (Alias != NULL) {
 | |
|     AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
 | |
|     if (AliasLower == NULL) {
 | |
|       return NULL;
 | |
|     }
 | |
|     ToLower (AliasLower);
 | |
| 
 | |
|     if (Volatile == NULL) {
 | |
|       GetVariable2 (AliasLower, &gShellAliasGuid, (VOID **)&AliasVal, NULL);
 | |
|       FreePool(AliasLower);
 | |
|       return (AddBufferToFreeList(AliasVal));
 | |
|     }
 | |
|     RetSize = 0;
 | |
|     RetVal = NULL;
 | |
|     Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
 | |
|     if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|       RetVal = AllocateZeroPool(RetSize);
 | |
|       Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
 | |
|     }
 | |
|     if (EFI_ERROR(Status)) {
 | |
|       if (RetVal != NULL) {
 | |
|         FreePool(RetVal);
 | |
|       }
 | |
|       FreePool(AliasLower);
 | |
|       return (NULL);
 | |
|     }
 | |
|     if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
 | |
|       *Volatile = FALSE;
 | |
|     } else {
 | |
|       *Volatile = TRUE;
 | |
|     }
 | |
| 
 | |
|     FreePool (AliasLower);
 | |
|     return (AddBufferToFreeList(RetVal));
 | |
|   }
 | |
|   return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Changes a shell command alias.
 | |
| 
 | |
|   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
 | |
| 
 | |
|   this function does not check for built in alias'.
 | |
| 
 | |
|   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
 | |
|   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
 | |
|                                 Command refers to an alias, that alias will be deleted.
 | |
|   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
 | |
|                                 Alias being set will be stored in a non-volatile fashion.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Alias created or deleted successfully.
 | |
|   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
 | |
| **/
 | |
| EFI_STATUS
 | |
| InternalSetAlias(
 | |
|   IN CONST CHAR16 *Command,
 | |
|   IN CONST CHAR16 *Alias,
 | |
|   IN BOOLEAN Volatile
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  Status;
 | |
|   CHAR16      *AliasLower;
 | |
|   BOOLEAN     DeleteAlias;
 | |
| 
 | |
|   DeleteAlias = FALSE;
 | |
|   if (Alias == NULL) {
 | |
|     //
 | |
|     // We must be trying to remove one if Alias is NULL
 | |
|     // remove an alias (but passed in COMMAND parameter)
 | |
|     //
 | |
|     Alias       = Command;
 | |
|     DeleteAlias = TRUE;
 | |
|   }
 | |
|   ASSERT (Alias != NULL);
 | |
| 
 | |
|   //
 | |
|   // Convert to lowercase to make aliases case-insensitive
 | |
|   //
 | |
|   AliasLower = AllocateCopyPool (StrSize (Alias), Alias);
 | |
|   if (AliasLower == NULL) {
 | |
|     return EFI_OUT_OF_RESOURCES;
 | |
|   }
 | |
|   ToLower (AliasLower);
 | |
| 
 | |
|   if (DeleteAlias) {
 | |
|     Status = gRT->SetVariable (AliasLower, &gShellAliasGuid, 0, 0, NULL);
 | |
|   } else {
 | |
|     Status = gRT->SetVariable (
 | |
|                     AliasLower, &gShellAliasGuid,
 | |
|                     EFI_VARIABLE_BOOTSERVICE_ACCESS | (Volatile ? 0 : EFI_VARIABLE_NON_VOLATILE),
 | |
|                     StrSize (Command), (VOID *) Command
 | |
|                     );
 | |
|   }
 | |
| 
 | |
|   FreePool (AliasLower);
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Changes a shell command alias.
 | |
| 
 | |
|   This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
 | |
| 
 | |
| 
 | |
|   @param[in] Command            Points to the NULL-terminated shell command or existing alias.
 | |
|   @param[in] Alias              Points to the NULL-terminated alias for the shell command. If this is NULL, and
 | |
|                                 Command refers to an alias, that alias will be deleted.
 | |
|   @param[in] Replace            If TRUE and the alias already exists, then the existing alias will be replaced. If
 | |
|                                 FALSE and the alias already exists, then the existing alias is unchanged and
 | |
|                                 EFI_ACCESS_DENIED is returned.
 | |
|   @param[in] Volatile           if TRUE the Alias being set will be stored in a volatile fashion.  if FALSE the
 | |
|                                 Alias being set will be stored in a non-volatile fashion.
 | |
| 
 | |
|   @retval EFI_SUCCESS           Alias created or deleted successfully.
 | |
|   @retval EFI_NOT_FOUND         the Alias intended to be deleted was not found
 | |
|   @retval EFI_ACCESS_DENIED     The alias is a built-in alias or already existed and Replace was set to
 | |
|                                 FALSE.
 | |
|   @retval EFI_INVALID_PARAMETER Command is null or the empty string.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| EfiShellSetAlias(
 | |
|   IN CONST CHAR16 *Command,
 | |
|   IN CONST CHAR16 *Alias,
 | |
|   IN BOOLEAN Replace,
 | |
|   IN BOOLEAN Volatile
 | |
|   )
 | |
| {
 | |
|   if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
 | |
|     //
 | |
|     // cant set over a built in alias
 | |
|     //
 | |
|     return (EFI_ACCESS_DENIED);
 | |
|   } else if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
 | |
|     //
 | |
|     // Command is null or empty
 | |
|     //
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   } else if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
 | |
|     //
 | |
|     // Alias already exists, Replace not set
 | |
|     //
 | |
|     return (EFI_ACCESS_DENIED);
 | |
|   } else {
 | |
|     return (InternalSetAlias(Command, Alias, Volatile));
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Pure FILE_HANDLE operations are passed to FileHandleLib
 | |
| // these functions are indicated by the *
 | |
| EFI_SHELL_PROTOCOL         mShellProtocol = {
 | |
|   EfiShellExecute,
 | |
|   EfiShellGetEnv,
 | |
|   EfiShellSetEnv,
 | |
|   EfiShellGetAlias,
 | |
|   EfiShellSetAlias,
 | |
|   EfiShellGetHelpText,
 | |
|   EfiShellGetDevicePathFromMap,
 | |
|   EfiShellGetMapFromDevicePath,
 | |
|   EfiShellGetDevicePathFromFilePath,
 | |
|   EfiShellGetFilePathFromDevicePath,
 | |
|   EfiShellSetMap,
 | |
|   EfiShellGetCurDir,
 | |
|   EfiShellSetCurDir,
 | |
|   EfiShellOpenFileList,
 | |
|   EfiShellFreeFileList,
 | |
|   EfiShellRemoveDupInFileList,
 | |
|   EfiShellBatchIsActive,
 | |
|   EfiShellIsRootShell,
 | |
|   EfiShellEnablePageBreak,
 | |
|   EfiShellDisablePageBreak,
 | |
|   EfiShellGetPageBreak,
 | |
|   EfiShellGetDeviceName,
 | |
|   (EFI_SHELL_GET_FILE_INFO)FileHandleGetInfo,         //*
 | |
|   (EFI_SHELL_SET_FILE_INFO)FileHandleSetInfo,         //*
 | |
|   EfiShellOpenFileByName,
 | |
|   EfiShellClose,
 | |
|   EfiShellCreateFile,
 | |
|   (EFI_SHELL_READ_FILE)FileHandleRead,                //*
 | |
|   (EFI_SHELL_WRITE_FILE)FileHandleWrite,              //*
 | |
|   (EFI_SHELL_DELETE_FILE)FileHandleDelete,            //*
 | |
|   EfiShellDeleteFileByName,
 | |
|   (EFI_SHELL_GET_FILE_POSITION)FileHandleGetPosition, //*
 | |
|   (EFI_SHELL_SET_FILE_POSITION)FileHandleSetPosition, //*
 | |
|   (EFI_SHELL_FLUSH_FILE)FileHandleFlush,              //*
 | |
|   EfiShellFindFiles,
 | |
|   EfiShellFindFilesInDir,
 | |
|   (EFI_SHELL_GET_FILE_SIZE)FileHandleGetSize,         //*
 | |
|   EfiShellOpenRoot,
 | |
|   EfiShellOpenRootByHandle,
 | |
|   NULL,
 | |
|   SHELL_MAJOR_VERSION,
 | |
|   SHELL_MINOR_VERSION,
 | |
| 
 | |
|   // New for UEFI Shell 2.1
 | |
|   EfiShellRegisterGuidName,
 | |
|   EfiShellGetGuidName,
 | |
|   EfiShellGetGuidFromName,
 | |
|   EfiShellGetEnvEx
 | |
| };
 | |
| 
 | |
| /**
 | |
|   Function to create and install on the current handle.
 | |
| 
 | |
|   Will overwrite any existing ShellProtocols in the system to be sure that
 | |
|   the current shell is in control.
 | |
| 
 | |
|   This must be removed via calling CleanUpShellProtocol().
 | |
| 
 | |
|   @param[in, out] NewShell   The pointer to the pointer to the structure
 | |
|   to install.
 | |
| 
 | |
|   @retval EFI_SUCCESS     The operation was successful.
 | |
|   @return                 An error from LocateHandle, CreateEvent, or other core function.
 | |
| **/
 | |
| EFI_STATUS
 | |
| CreatePopulateInstallShellProtocol (
 | |
|   IN OUT EFI_SHELL_PROTOCOL  **NewShell
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                  Status;
 | |
|   UINTN                       BufferSize;
 | |
|   EFI_HANDLE                  *Buffer;
 | |
|   UINTN                       HandleCounter;
 | |
|   SHELL_PROTOCOL_HANDLE_LIST  *OldProtocolNode;
 | |
|   EFI_SHELL_PROTOCOL          *OldShell;
 | |
| 
 | |
|   if (NewShell == NULL) {
 | |
|     return (EFI_INVALID_PARAMETER);
 | |
|   }
 | |
| 
 | |
|   BufferSize = 0;
 | |
|   Buffer = NULL;
 | |
|   OldProtocolNode = NULL;
 | |
|   InitializeListHead(&ShellInfoObject.OldShellList.Link);
 | |
| 
 | |
|   //
 | |
|   // Initialize EfiShellProtocol object...
 | |
|   //
 | |
|   Status = gBS->CreateEvent(0,
 | |
|                             0,
 | |
|                             NULL,
 | |
|                             NULL,
 | |
|                             &mShellProtocol.ExecutionBreak);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     return (Status);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the size of the buffer we need.
 | |
|   //
 | |
|   Status = gBS->LocateHandle(ByProtocol,
 | |
|                              &gEfiShellProtocolGuid,
 | |
|                              NULL,
 | |
|                              &BufferSize,
 | |
|                              Buffer);
 | |
|   if (Status == EFI_BUFFER_TOO_SMALL) {
 | |
|     //
 | |
|     // Allocate and recall with buffer of correct size
 | |
|     //
 | |
|     Buffer = AllocateZeroPool(BufferSize);
 | |
|     if (Buffer == NULL) {
 | |
|       return (EFI_OUT_OF_RESOURCES);
 | |
|     }
 | |
|     Status = gBS->LocateHandle(ByProtocol,
 | |
|                                &gEfiShellProtocolGuid,
 | |
|                                NULL,
 | |
|                                &BufferSize,
 | |
|                                Buffer);
 | |
|     if (EFI_ERROR(Status)) {
 | |
|       FreePool(Buffer);
 | |
|       return (Status);
 | |
|     }
 | |
|     //
 | |
|     // now overwrite each of them, but save the info to restore when we end.
 | |
|     //
 | |
|     for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
 | |
|       Status = gBS->OpenProtocol(Buffer[HandleCounter],
 | |
|                                 &gEfiShellProtocolGuid,
 | |
|                                 (VOID **) &OldShell,
 | |
|                                 gImageHandle,
 | |
|                                 NULL,
 | |
|                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | |
|                                );
 | |
|       if (!EFI_ERROR(Status)) {
 | |
|         OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
 | |
|         if (OldProtocolNode == NULL) {
 | |
|           if (!IsListEmpty (&ShellInfoObject.OldShellList.Link)) {
 | |
|             CleanUpShellProtocol (&mShellProtocol);
 | |
|           }
 | |
|           Status = EFI_OUT_OF_RESOURCES;
 | |
|           break;
 | |
|         }
 | |
|         //
 | |
|         // reinstall over the old one...
 | |
|         //
 | |
|         OldProtocolNode->Handle    = Buffer[HandleCounter];
 | |
|         OldProtocolNode->Interface = OldShell;
 | |
|         Status = gBS->ReinstallProtocolInterface(
 | |
|                             OldProtocolNode->Handle,
 | |
|                             &gEfiShellProtocolGuid,
 | |
|                             OldProtocolNode->Interface,
 | |
|                             (VOID*)(&mShellProtocol));
 | |
|         if (!EFI_ERROR(Status)) {
 | |
|           //
 | |
|           // we reinstalled sucessfully.  log this so we can reverse it later.
 | |
|           //
 | |
| 
 | |
|           //
 | |
|           // add to the list for subsequent...
 | |
|           //
 | |
|           InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     FreePool(Buffer);
 | |
|   } else if (Status == EFI_NOT_FOUND) {
 | |
|     ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
 | |
|     //
 | |
|     // no one else published yet.  just publish it ourselves.
 | |
|     //
 | |
|     Status = gBS->InstallProtocolInterface (
 | |
|                       &gImageHandle,
 | |
|                       &gEfiShellProtocolGuid,
 | |
|                       EFI_NATIVE_INTERFACE,
 | |
|                       (VOID*)(&mShellProtocol));
 | |
|   }
 | |
| 
 | |
|   if (PcdGetBool(PcdShellSupportOldProtocols)){
 | |
|     ///@todo support ShellEnvironment2
 | |
|     ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
 | |
|   }
 | |
| 
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     *NewShell = &mShellProtocol;
 | |
|   }
 | |
|   return (Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Opposite of CreatePopulateInstallShellProtocol.
 | |
| 
 | |
|   Free all memory and restore the system to the state it was in before calling
 | |
|   CreatePopulateInstallShellProtocol.
 | |
| 
 | |
|   @param[in, out] NewShell   The pointer to the new shell protocol structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation was successful.
 | |
| **/
 | |
| EFI_STATUS
 | |
| CleanUpShellProtocol (
 | |
|   IN OUT EFI_SHELL_PROTOCOL  *NewShell
 | |
|   )
 | |
| {
 | |
|   SHELL_PROTOCOL_HANDLE_LIST        *Node2;
 | |
| 
 | |
|   //
 | |
|   // if we need to restore old protocols...
 | |
|   //
 | |
|   if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
 | |
|     for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
 | |
|          ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
 | |
|          ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *) GetFirstNode (&ShellInfoObject.OldShellList.Link)
 | |
|          ) {
 | |
|       RemoveEntryList (&Node2->Link);
 | |
|       gBS->ReinstallProtocolInterface (Node2->Handle, &gEfiShellProtocolGuid, NewShell, Node2->Interface);
 | |
|       FreePool (Node2);
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // no need to restore
 | |
|     //
 | |
|     gBS->UninstallProtocolInterface (gImageHandle, &gEfiShellProtocolGuid, NewShell);
 | |
|   }
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Cleanup the shell environment.
 | |
| 
 | |
|   @param[in, out] NewShell   The pointer to the new shell protocol structure.
 | |
| 
 | |
|   @retval EFI_SUCCESS       The operation was successful.
 | |
| **/
 | |
| EFI_STATUS
 | |
| CleanUpShellEnvironment (
 | |
|   IN OUT EFI_SHELL_PROTOCOL  *NewShell
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                        Status;
 | |
|   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
 | |
| 
 | |
|   CleanUpShellProtocol (NewShell);
 | |
| 
 | |
|   Status = gBS->CloseEvent(NewShell->ExecutionBreak);
 | |
|   NewShell->ExecutionBreak = NULL;
 | |
| 
 | |
|   Status = gBS->OpenProtocol(
 | |
|     gST->ConsoleInHandle,
 | |
|     &gEfiSimpleTextInputExProtocolGuid,
 | |
|     (VOID**)&SimpleEx,
 | |
|     gImageHandle,
 | |
|     NULL,
 | |
|     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
| 
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3);
 | |
|     Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4);
 | |
|   }
 | |
|   return (Status);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Notification function for keystrokes.
 | |
| 
 | |
|   @param[in] KeyData    The key that was pressed.
 | |
| 
 | |
|   @retval EFI_SUCCESS   The operation was successful.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| NotificationFunction(
 | |
|   IN EFI_KEY_DATA *KeyData
 | |
|   )
 | |
| {
 | |
|   if ( ((KeyData->Key.UnicodeChar == L'c') &&
 | |
|         (KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) ||
 | |
|       (KeyData->Key.UnicodeChar == 3)
 | |
|       ){
 | |
|     if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
 | |
|       return (EFI_UNSUPPORTED);
 | |
|     }
 | |
|     return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak));
 | |
|   } else if  ((KeyData->Key.UnicodeChar == L's') &&
 | |
|               (KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState  == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))
 | |
|               ){
 | |
|     ShellInfoObject.HaltOutput = TRUE;
 | |
|   }
 | |
|   return (EFI_SUCCESS);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Function to start monitoring for CTRL-C using SimpleTextInputEx.  This
 | |
|   feature's enabled state was not known when the shell initially launched.
 | |
| 
 | |
|   @retval EFI_SUCCESS           The feature is enabled.
 | |
|   @retval EFI_OUT_OF_RESOURCES  There is not enough mnemory available.
 | |
| **/
 | |
| EFI_STATUS
 | |
| InernalEfiShellStartMonitor(
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
 | |
|   EFI_KEY_DATA                      KeyData;
 | |
|   EFI_STATUS                        Status;
 | |
| 
 | |
|   Status = gBS->OpenProtocol(
 | |
|     gST->ConsoleInHandle,
 | |
|     &gEfiSimpleTextInputExProtocolGuid,
 | |
|     (VOID**)&SimpleEx,
 | |
|     gImageHandle,
 | |
|     NULL,
 | |
|     EFI_OPEN_PROTOCOL_GET_PROTOCOL);
 | |
|   if (EFI_ERROR(Status)) {
 | |
|     ShellPrintHiiEx(
 | |
|       -1,
 | |
|       -1,
 | |
|       NULL,
 | |
|       STRING_TOKEN (STR_SHELL_NO_IN_EX),
 | |
|       ShellInfoObject.HiiHandle);
 | |
|     return (EFI_SUCCESS);
 | |
|   }
 | |
| 
 | |
|   if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) {
 | |
|     return (EFI_UNSUPPORTED);
 | |
|   }
 | |
| 
 | |
|   KeyData.KeyState.KeyToggleState = 0;
 | |
|   KeyData.Key.ScanCode            = 0;
 | |
|   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
 | |
|   KeyData.Key.UnicodeChar         = L'c';
 | |
| 
 | |
|   Status = SimpleEx->RegisterKeyNotify(
 | |
|     SimpleEx,
 | |
|     &KeyData,
 | |
|     NotificationFunction,
 | |
|     &ShellInfoObject.CtrlCNotifyHandle1);
 | |
| 
 | |
|   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     Status = SimpleEx->RegisterKeyNotify(
 | |
|       SimpleEx,
 | |
|       &KeyData,
 | |
|       NotificationFunction,
 | |
|       &ShellInfoObject.CtrlCNotifyHandle2);
 | |
|   }
 | |
|   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
 | |
|   KeyData.Key.UnicodeChar         = 3;
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     Status = SimpleEx->RegisterKeyNotify(
 | |
|       SimpleEx,
 | |
|       &KeyData,
 | |
|       NotificationFunction,
 | |
|       &ShellInfoObject.CtrlCNotifyHandle3);
 | |
|   }
 | |
|   KeyData.KeyState.KeyShiftState  = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED;
 | |
|   if (!EFI_ERROR(Status)) {
 | |
|     Status = SimpleEx->RegisterKeyNotify(
 | |
|       SimpleEx,
 | |
|       &KeyData,
 | |
|       NotificationFunction,
 | |
|       &ShellInfoObject.CtrlCNotifyHandle4);
 | |
|   }
 | |
|   return (Status);
 | |
| }
 | |
| 
 |