| /** @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 DevicePath 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 didn't 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 separated 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); | |
| } | |
| // | |
| // Do not call SetDevicePathEndNode() if the device path node is already the | |
| // end of an entire device path. | |
| // | |
| if (!IsDevicePathEnd (*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)) | |
| { | |
| if (PathForReturn != NULL) { | |
| 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) { | |
| if (PathForReturn != 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); | |
| if (Lang == NULL) { | |
| continue; | |
| } | |
| 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. | |
| // | |
| Status = PARSE_HANDLE_DATABASE_PARENTS (DeviceHandle, &ParentControllerCount, &ParentControllerBuffer); | |
| if ((DeviceNameToReturn == NULL) && !EFI_ERROR (Status)) { | |
| for (LoopVar = 0; LoopVar < ParentControllerCount; LoopVar++) { | |
| Status = PARSE_HANDLE_DATABASE_UEFI_DRIVERS (ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer); | |
| if (!EFI_ERROR (Status)) { | |
| 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); | |
| if (Lang == NULL) { | |
| continue; | |
| } | |
| 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 contained non-path elements | |
| @retval other an error occurred. | |
| **/ | |
| 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 beginning 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 attributes. 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 device, 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) { | |
| ShellPrintHiiDefaultEx ( | |
| 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); | |
| if (EFI_ERROR (Status)) { | |
| goto UnloadImage; | |
| } | |
| // | |
| // 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); | |
| // | |
| // 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); | |
| } | |
| Temp = NULL; | |
| if (NestingEnabled ()) { | |
| DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); | |
| if (DevPath == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| DEBUG_CODE_BEGIN (); | |
| Temp = ConvertDevicePathToText (ShellInfoObject.FileDevPath, TRUE, TRUE); | |
| if (Temp != NULL) { | |
| FreePool (Temp); | |
| } | |
| Temp = ConvertDevicePathToText (ShellInfoObject.ImageDevPath, TRUE, TRUE); | |
| if (Temp != NULL) { | |
| FreePool (Temp); | |
| } | |
| if (DevPath != NULL) { | |
| Temp = ConvertDevicePathToText (DevPath, TRUE, TRUE); | |
| } | |
| if (Temp != NULL) { | |
| 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_STATUS Status; | |
| EFI_SHELL_FILE_INFO *Duplicates; | |
| EFI_SHELL_FILE_INFO *ShellFileListItem; | |
| EFI_SHELL_FILE_INFO *ShellFileListItem2; | |
| EFI_SHELL_FILE_INFO *TempNode; | |
| if ((FileList == NULL) || (*FileList == NULL)) { | |
| return (EFI_INVALID_PARAMETER); | |
| } | |
| Status = ShellSortFileList ( | |
| FileList, | |
| &Duplicates, | |
| ShellSortFileListByFullName | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| EfiShellFreeFileList (&Duplicates); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Fall back to the slow method that needs no extra memory, and so cannot | |
| // fail. | |
| // | |
| 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 occurred | |
| @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 occurred. | |
| @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 didn't 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; | |
| NewShellNode = NULL; | |
| FileInfo = NULL; | |
| 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); | |
| if (FileInfo != NULL) { | |
| 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); | |
| if (PatternCopy == NULL) { | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| 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); | |
| if (CurDir == NULL) { | |
| return EFI_NOT_FOUND; | |
| } | |
| 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) | |
| ) | |
| { | |
| if (Node->Key == NULL) { | |
| ASSERT (FALSE); | |
| continue; | |
| } | |
| 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); | |
| if (Buffer == NULL) { | |
| return NULL; | |
| } | |
| Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES (Name, Attributes, &Size, Buffer); | |
| } | |
| // | |
| // we didn't 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 successful | |
| @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); | |
| if (DirectoryName == NULL) { | |
| ASSERT (DirectoryName != NULL); | |
| return (EFI_OUT_OF_RESOURCES); | |
| } | |
| 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 delimited 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 occurred 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 occurred | |
| @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); | |
| if (RetVal == NULL) { | |
| FreePool (AliasLower); | |
| return NULL; | |
| } | |
| 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 successfully. 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 (!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 memory 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)) { | |
| ShellPrintHiiDefaultEx ( | |
| 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); | |
| } |