/** @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> | |
Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> | |
This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
**/ | |
#include "Shell.h" | |
/** | |
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 | |
EFIAPI | |
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 | |
EFIAPI | |
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); | |
} | |
/** | |
Internal worker debug helper function to print out maps as they are added. | |
@param[in] Mapping string mapping that has been added | |
@param[in] DevicePath pointer to device path that has been mapped. | |
@retval EFI_SUCCESS the operation was successful. | |
@return other an error ocurred | |
@sa LocateHandle | |
@sa OpenProtocol | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InternalShellProtocolDebugPrintMessage ( | |
IN CONST CHAR16 *Mapping, | |
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *Temp; | |
Status = EFI_SUCCESS; | |
DEBUG_CODE_BEGIN(); | |
if (Mapping != NULL) { | |
DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping)); | |
} | |
Temp = ConvertDevicePathToText(DevicePath, TRUE, TRUE); | |
DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp)); | |
FreePool(Temp); | |
DEBUG_CODE_END(); | |
return (Status); | |
} | |
/** | |
This function creates a mapping for a device path. | |
If both DeviecPath and Mapping are NULL, this will reset the mapping to default values. | |
@param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping, | |
then the mapping will be deleted. | |
@param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':' | |
@retval EFI_SUCCESS Mapping created or deleted successfully. | |
@retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the | |
boot service function LocateDevicePath(). | |
@retval EFI_ACCESS_DENIED The mapping is a built-in alias. | |
@retval EFI_INVALID_PARAMETER Mapping was NULL | |
@retval EFI_INVALID_PARAMETER Mapping did not end with a ':' | |
@retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed. | |
@retval EFI_NOT_FOUND There was no mapping found to delete | |
@retval EFI_OUT_OF_RESOURCES Memory allocation failed | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellSetMap( | |
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL, | |
IN CONST CHAR16 *Mapping | |
) | |
{ | |
EFI_STATUS Status; | |
SHELL_MAP_LIST *MapListNode; | |
if (Mapping == NULL){ | |
return (EFI_INVALID_PARAMETER); | |
} | |
if (Mapping[StrLen(Mapping)-1] != ':') { | |
return (EFI_INVALID_PARAMETER); | |
} | |
// | |
// Delete the mapping | |
// | |
if (DevicePath == NULL) { | |
if (IsListEmpty(&gShellMapList.Link)) { | |
return (EFI_NOT_FOUND); | |
} | |
for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) | |
; !IsNull(&gShellMapList.Link, &MapListNode->Link) | |
; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link) | |
){ | |
if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) { | |
RemoveEntryList(&MapListNode->Link); | |
FreePool(MapListNode); | |
return (EFI_SUCCESS); | |
} | |
} // for loop | |
// | |
// We didnt find one to delete | |
// | |
return (EFI_NOT_FOUND); | |
} | |
// | |
// make sure this is a valid to add device path | |
// | |
///@todo add BlockIo to this test... | |
if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath) | |
&& !InternalShellProtocolIsBlockIoPresent(DevicePath)) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
// | |
// First make sure there is no old mapping | |
// | |
Status = EfiShellSetMap(NULL, Mapping); | |
if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) { | |
return (Status); | |
} | |
// | |
// now add the new one. | |
// | |
Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE); | |
return(Status); | |
} | |
/** | |
Gets the device path from the mapping. | |
This function gets the device path associated with a mapping. | |
@param Mapping A pointer to the mapping | |
@retval !=NULL Pointer to the device path that corresponds to the | |
device mapping. The returned pointer does not need | |
to be freed. | |
@retval NULL There is no device path associated with the | |
specified mapping. | |
**/ | |
CONST EFI_DEVICE_PATH_PROTOCOL * | |
EFIAPI | |
EfiShellGetDevicePathFromMap( | |
IN CONST CHAR16 *Mapping | |
) | |
{ | |
SHELL_MAP_LIST *MapListItem; | |
CHAR16 *NewName; | |
UINTN Size; | |
NewName = NULL; | |
Size = 0; | |
StrnCatGrow(&NewName, &Size, Mapping, 0); | |
if (Mapping[StrLen(Mapping)-1] != L':') { | |
StrnCatGrow(&NewName, &Size, L":", 0); | |
} | |
MapListItem = ShellCommandFindMapItem(NewName); | |
FreePool(NewName); | |
if (MapListItem != NULL) { | |
return (MapListItem->DevicePath); | |
} | |
return(NULL); | |
} | |
/** | |
Gets the mapping(s) that most closely matches the device path. | |
This function gets the mapping which corresponds to the device path *DevicePath. If | |
there is no exact match, then the mapping which most closely matches *DevicePath | |
is returned, and *DevicePath is updated to point to the remaining portion of the | |
device path. If there is an exact match, the mapping is returned and *DevicePath | |
points to the end-of-device-path node. | |
If there are multiple map names they will be semi-colon seperated in the | |
NULL-terminated string. | |
@param DevicePath On entry, points to a device path pointer. On | |
exit, updates the pointer to point to the | |
portion of the device path after the mapping. | |
@retval NULL No mapping was found. | |
@return !=NULL Pointer to NULL-terminated mapping. The buffer | |
is callee allocated and should be freed by the caller. | |
**/ | |
CONST CHAR16 * | |
EFIAPI | |
EfiShellGetMapFromDevicePath( | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath | |
) | |
{ | |
SHELL_MAP_LIST *Node; | |
CHAR16 *PathForReturn; | |
UINTN PathSize; | |
// EFI_HANDLE PathHandle; | |
// EFI_HANDLE MapHandle; | |
// EFI_STATUS Status; | |
// EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; | |
// EFI_DEVICE_PATH_PROTOCOL *MapPathCopy; | |
if (DevicePath == NULL || *DevicePath == NULL) { | |
return (NULL); | |
} | |
PathForReturn = NULL; | |
PathSize = 0; | |
for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) | |
; !IsNull(&gShellMapList.Link, &Node->Link) | |
; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link) | |
){ | |
// | |
// check for exact match | |
// | |
if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) { | |
ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); | |
if (PathSize != 0) { | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0); | |
} | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0); | |
} | |
} | |
if (PathForReturn != NULL) { | |
while (!IsDevicePathEndType (*DevicePath)) { | |
*DevicePath = NextDevicePathNode (*DevicePath); | |
} | |
SetDevicePathEndNode (*DevicePath); | |
} | |
/* | |
///@todo finish code for inexact matches. | |
if (PathForReturn == NULL) { | |
PathSize = 0; | |
DevicePathCopy = DuplicateDevicePath(*DevicePath); | |
ASSERT(DevicePathCopy != NULL); | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle); | |
ASSERT_EFI_ERROR(Status); | |
// | |
// check each of the device paths we have to get the root of the path for consist mappings | |
// | |
for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) | |
; !IsNull(&gShellMapList.Link, &Node->Link) | |
; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link) | |
){ | |
if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) { | |
continue; | |
} | |
MapPathCopy = DuplicateDevicePath(Node->DevicePath); | |
ASSERT(MapPathCopy != NULL); | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); | |
if (MapHandle == PathHandle) { | |
*DevicePath = DevicePathCopy; | |
MapPathCopy = NULL; | |
DevicePathCopy = NULL; | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0); | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0); | |
break; | |
} | |
} | |
// | |
// now add on the non-consistent mappings | |
// | |
for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) | |
; !IsNull(&gShellMapList.Link, &Node->Link) | |
; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link) | |
){ | |
if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) { | |
continue; | |
} | |
MapPathCopy = Node->DevicePath; | |
ASSERT(MapPathCopy != NULL); | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); | |
if (MapHandle == PathHandle) { | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0); | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0); | |
break; | |
} | |
} | |
} | |
*/ | |
return (AddBufferToFreeList(PathForReturn)); | |
} | |
/** | |
Converts a device path to a file system-style path. | |
This function converts a device path to a file system path by replacing part, or all, of | |
the device path with the file-system mapping. If there are more than one application | |
file system mappings, the one that most closely matches Path will be used. | |
@param Path The pointer to the device path | |
@retval NULL the device path could not be found. | |
@return all The pointer of the NULL-terminated file path. The path | |
is callee-allocated and should be freed by the caller. | |
**/ | |
CHAR16 * | |
EFIAPI | |
EfiShellGetFilePathFromDevicePath( | |
IN CONST EFI_DEVICE_PATH_PROTOCOL *Path | |
) | |
{ | |
EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy; | |
EFI_DEVICE_PATH_PROTOCOL *MapPathCopy; | |
SHELL_MAP_LIST *MapListItem; | |
CHAR16 *PathForReturn; | |
UINTN PathSize; | |
EFI_HANDLE PathHandle; | |
EFI_HANDLE MapHandle; | |
EFI_STATUS Status; | |
FILEPATH_DEVICE_PATH *FilePath; | |
FILEPATH_DEVICE_PATH *AlignedNode; | |
PathForReturn = NULL; | |
PathSize = 0; | |
DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path; | |
ASSERT(DevicePathCopy != NULL); | |
if (DevicePathCopy == NULL) { | |
return (NULL); | |
} | |
///@todo BlockIo? | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle); | |
if (EFI_ERROR(Status)) { | |
return (NULL); | |
} | |
// | |
// check each of the device paths we have to get the root of the path | |
// | |
for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link) | |
; !IsNull(&gShellMapList.Link, &MapListItem->Link) | |
; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link) | |
){ | |
MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath; | |
ASSERT(MapPathCopy != NULL); | |
///@todo BlockIo? | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle); | |
if (MapHandle == PathHandle) { | |
ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); | |
PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0); | |
// | |
// go through all the remaining nodes in the device path | |
// | |
for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy | |
; !IsDevicePathEnd (&FilePath->Header) | |
; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header) | |
){ | |
// | |
// all the rest should be file path nodes | |
// | |
if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) || | |
(DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) { | |
FreePool(PathForReturn); | |
PathForReturn = NULL; | |
ASSERT(FALSE); | |
} else { | |
// | |
// append the path part onto the filepath. | |
// | |
ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL)); | |
AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePath), FilePath); | |
ASSERT (AlignedNode != 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 - 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) - sizeof(CHAR16); | |
NewPath = AllocateZeroPool(Size); | |
if (NewPath == NULL) { | |
return (NULL); | |
} | |
StrnCpy(NewPath, Cwd, Size/sizeof(CHAR16)-1); | |
if (*Path == L'\\') { | |
Path++; | |
while (PathRemoveLastItem(NewPath)) ; | |
} | |
StrnCat(NewPath, Path, Size/sizeof(CHAR16) - 1 - StrLen(NewPath)); | |
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)+1) == CHAR_NULL) { | |
DevicePathForReturn = FileDevicePath(Handle, L"\\"); | |
} else { | |
DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName)); | |
} | |
FreePool(MapName); | |
if (DevicePathCopyForFree != NULL) { | |
FreePool(DevicePathCopyForFree); | |
} | |
return (DevicePathForReturn); | |
} | |
/** | |
Gets the name of the device specified by the device handle. | |
This function gets the user-readable name of the device specified by the device | |
handle. If no user-readable name could be generated, then *BestDeviceName will be | |
NULL and EFI_NOT_FOUND will be returned. | |
If EFI_DEVICE_NAME_USE_COMPONENT_NAME is set, then the function will return the | |
device's name using the EFI_COMPONENT_NAME2_PROTOCOL, if present on | |
DeviceHandle. | |
If EFI_DEVICE_NAME_USE_DEVICE_PATH is set, then the function will return the | |
device's name using the EFI_DEVICE_PATH_PROTOCOL, if present on DeviceHandle. | |
If both EFI_DEVICE_NAME_USE_COMPONENT_NAME and | |
EFI_DEVICE_NAME_USE_DEVICE_PATH are set, then | |
EFI_DEVICE_NAME_USE_COMPONENT_NAME will have higher priority. | |
@param DeviceHandle The handle of the device. | |
@param Flags Determines the possible sources of component names. | |
Valid bits are: | |
EFI_DEVICE_NAME_USE_COMPONENT_NAME | |
EFI_DEVICE_NAME_USE_DEVICE_PATH | |
@param Language A pointer to the language specified for the device | |
name, in the same format as described in the UEFI | |
specification, Appendix M | |
@param BestDeviceName On return, points to the callee-allocated NULL- | |
terminated name of the device. If no device name | |
could be found, points to NULL. The name must be | |
freed by the caller... | |
@retval EFI_SUCCESS Get the name successfully. | |
@retval EFI_NOT_FOUND Fail to get the device name. | |
@retval EFI_INVALID_PARAMETER Flags did not have a valid bit set. | |
@retval EFI_INVALID_PARAMETER BestDeviceName was NULL | |
@retval EFI_INVALID_PARAMETER DeviceHandle was NULL | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellGetDeviceName( | |
IN EFI_HANDLE DeviceHandle, | |
IN EFI_SHELL_DEVICE_NAME_FLAGS Flags, | |
IN CHAR8 *Language, | |
OUT CHAR16 **BestDeviceName | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_COMPONENT_NAME2_PROTOCOL *CompName2; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_HANDLE *HandleList; | |
UINTN HandleCount; | |
UINTN LoopVar; | |
CHAR16 *DeviceNameToReturn; | |
CHAR8 *Lang; | |
UINTN ParentControllerCount; | |
EFI_HANDLE *ParentControllerBuffer; | |
UINTN ParentDriverCount; | |
EFI_HANDLE *ParentDriverBuffer; | |
if (BestDeviceName == NULL || | |
DeviceHandle == NULL | |
){ | |
return (EFI_INVALID_PARAMETER); | |
} | |
// | |
// make sure one of the 2 supported bits is on | |
// | |
if (((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) == 0) && | |
((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) == 0)) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
DeviceNameToReturn = NULL; | |
*BestDeviceName = NULL; | |
HandleList = NULL; | |
HandleCount = 0; | |
Lang = NULL; | |
if ((Flags & EFI_DEVICE_NAME_USE_COMPONENT_NAME) != 0) { | |
Status = ParseHandleDatabaseByRelationship( | |
NULL, | |
DeviceHandle, | |
HR_DRIVER_BINDING_HANDLE|HR_DEVICE_DRIVER, | |
&HandleCount, | |
&HandleList); | |
for (LoopVar = 0; LoopVar < HandleCount ; LoopVar++){ | |
// | |
// Go through those handles until we get one that passes for GetComponentName | |
// | |
Status = gBS->OpenProtocol( | |
HandleList[LoopVar], | |
&gEfiComponentName2ProtocolGuid, | |
(VOID**)&CompName2, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (EFI_ERROR(Status)) { | |
Status = gBS->OpenProtocol( | |
HandleList[LoopVar], | |
&gEfiComponentNameProtocolGuid, | |
(VOID**)&CompName2, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
} | |
if (EFI_ERROR(Status)) { | |
continue; | |
} | |
Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE); | |
Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn); | |
FreePool(Lang); | |
Lang = NULL; | |
if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) { | |
break; | |
} | |
} | |
if (HandleList != NULL) { | |
FreePool(HandleList); | |
} | |
// | |
// Now check the parent controller using this as the child. | |
// | |
if (DeviceNameToReturn == NULL){ | |
PARSE_HANDLE_DATABASE_PARENTS(DeviceHandle, &ParentControllerCount, &ParentControllerBuffer); | |
for (LoopVar = 0 ; LoopVar < ParentControllerCount ; LoopVar++) { | |
PARSE_HANDLE_DATABASE_UEFI_DRIVERS(ParentControllerBuffer[LoopVar], &ParentDriverCount, &ParentDriverBuffer); | |
for (HandleCount = 0 ; HandleCount < ParentDriverCount ; HandleCount++) { | |
// | |
// try using that driver's component name with controller and our driver as the child. | |
// | |
Status = gBS->OpenProtocol( | |
ParentDriverBuffer[HandleCount], | |
&gEfiComponentName2ProtocolGuid, | |
(VOID**)&CompName2, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (EFI_ERROR(Status)) { | |
Status = gBS->OpenProtocol( | |
ParentDriverBuffer[HandleCount], | |
&gEfiComponentNameProtocolGuid, | |
(VOID**)&CompName2, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
} | |
if (EFI_ERROR(Status)) { | |
continue; | |
} | |
Lang = GetBestLanguageForDriver(CompName2->SupportedLanguages, Language, FALSE); | |
Status = CompName2->GetControllerName(CompName2, ParentControllerBuffer[LoopVar], DeviceHandle, Lang, &DeviceNameToReturn); | |
FreePool(Lang); | |
Lang = NULL; | |
if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) { | |
break; | |
} | |
} | |
SHELL_FREE_NON_NULL(ParentDriverBuffer); | |
if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) { | |
break; | |
} | |
} | |
SHELL_FREE_NON_NULL(ParentControllerBuffer); | |
} | |
// | |
// dont return on fail since we will try device path if that bit is on | |
// | |
if (DeviceNameToReturn != NULL){ | |
ASSERT(BestDeviceName != NULL); | |
StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0); | |
return (EFI_SUCCESS); | |
} | |
} | |
if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) { | |
Status = gBS->OpenProtocol( | |
DeviceHandle, | |
&gEfiDevicePathProtocolGuid, | |
(VOID**)&DevicePath, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (!EFI_ERROR(Status)) { | |
// | |
// use device path to text on the device path | |
// | |
*BestDeviceName = ConvertDevicePathToText(DevicePath, TRUE, TRUE); | |
return (EFI_SUCCESS); | |
} | |
} | |
// | |
// none of the selected bits worked. | |
// | |
return (EFI_NOT_FOUND); | |
} | |
/** | |
Opens the root directory of a device on a handle | |
This function opens the root directory of a device and returns a file handle to it. | |
@param DeviceHandle The handle of the device that contains the volume. | |
@param FileHandle On exit, points to the file handle corresponding to the root directory on the | |
device. | |
@retval EFI_SUCCESS Root opened successfully. | |
@retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory | |
could not be opened. | |
@retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. | |
@retval EFI_DEVICE_ERROR The device had an error | |
**/ | |
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); | |
*FileHandle = ConvertEfiFileProtocolToShellHandle(RealFileHandle, EfiShellGetMapFromDevicePath(&DevPath)); | |
return (Status); | |
} | |
/** | |
Opens the root directory of a device. | |
This function opens the root directory of a device and returns a file handle to it. | |
@param DevicePath Points to the device path corresponding to the device where the | |
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL is installed. | |
@param FileHandle On exit, points to the file handle corresponding to the root directory on the | |
device. | |
@retval EFI_SUCCESS Root opened successfully. | |
@retval EFI_NOT_FOUND EFI_SIMPLE_FILE_SYSTEM could not be found or the root directory | |
could not be opened. | |
@retval EFI_VOLUME_CORRUPTED The data structures in the volume were corrupted. | |
@retval EFI_DEVICE_ERROR The device had an error | |
@retval EFI_INVALID_PARAMETER FileHandle is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellOpenRoot( | |
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT SHELL_FILE_HANDLE *FileHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE Handle; | |
if (FileHandle == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
// | |
// find the handle of the device with that device handle and the file system | |
// | |
///@todo BlockIo? | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, | |
&DevicePath, | |
&Handle); | |
if (EFI_ERROR(Status)) { | |
return (EFI_NOT_FOUND); | |
} | |
return (EfiShellOpenRootByHandle(Handle, FileHandle)); | |
} | |
/** | |
Returns whether any script files are currently being processed. | |
@retval TRUE There is at least one script file active. | |
@retval FALSE No script files are active now. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EfiShellBatchIsActive ( | |
VOID | |
) | |
{ | |
if (ShellCommandGetCurrentScriptFile() == NULL) { | |
return (FALSE); | |
} | |
return (TRUE); | |
} | |
/** | |
Worker function to open a file based on a device path. this will open the root | |
of the volume and then traverse down to the file itself. | |
@param DevicePath Device Path of the file. | |
@param FileHandle Pointer to the file upon a successful return. | |
@param OpenMode mode to open file in. | |
@param Attributes the File Attributes to use when creating a new file. | |
@retval EFI_SUCCESS the file is open and FileHandle is valid | |
@retval EFI_UNSUPPORTED the device path cotained non-path elements | |
@retval other an error ocurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InternalOpenFileDevicePath( | |
IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
OUT SHELL_FILE_HANDLE *FileHandle, | |
IN UINT64 OpenMode, | |
IN UINT64 Attributes OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
FILEPATH_DEVICE_PATH *FilePathNode; | |
EFI_HANDLE Handle; | |
SHELL_FILE_HANDLE ShellHandle; | |
EFI_FILE_PROTOCOL *Handle1; | |
EFI_FILE_PROTOCOL *Handle2; | |
FILEPATH_DEVICE_PATH *AlignedNode; | |
if (FileHandle == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
*FileHandle = NULL; | |
Handle1 = NULL; | |
Handle2 = NULL; | |
Handle = NULL; | |
ShellHandle = NULL; | |
FilePathNode = NULL; | |
AlignedNode = NULL; | |
Status = EfiShellOpenRoot(DevicePath, &ShellHandle); | |
if (!EFI_ERROR(Status)) { | |
Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle); | |
if (Handle1 != NULL) { | |
// | |
// chop off the begining part before the file system part... | |
// | |
///@todo BlockIo? | |
Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, | |
&DevicePath, | |
&Handle); | |
if (!EFI_ERROR(Status)) { | |
// | |
// To access as a file system, the file path should only | |
// contain file path components. Follow the file path nodes | |
// and find the target file | |
// | |
for ( FilePathNode = (FILEPATH_DEVICE_PATH *)DevicePath | |
; !IsDevicePathEnd (&FilePathNode->Header) | |
; FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header) | |
){ | |
SHELL_FREE_NON_NULL(AlignedNode); | |
AlignedNode = AllocateCopyPool (DevicePathNodeLength(FilePathNode), FilePathNode); | |
// | |
// For file system access each node should be a file path component | |
// | |
if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH || | |
DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP | |
) { | |
Status = EFI_UNSUPPORTED; | |
break; | |
} | |
// | |
// Open this file path node | |
// | |
Handle2 = Handle1; | |
Handle1 = NULL; | |
// | |
// if this is the last node in the DevicePath always create (if that was requested). | |
// | |
if (IsDevicePathEnd ((NextDevicePathNode (&FilePathNode->Header)))) { | |
Status = Handle2->Open ( | |
Handle2, | |
&Handle1, | |
AlignedNode->PathName, | |
OpenMode, | |
Attributes | |
); | |
} else { | |
// | |
// This is not the last node and we dont want to 'create' existing | |
// directory entries... | |
// | |
// | |
// open without letting it create | |
// prevents error on existing files/directories | |
// | |
Status = Handle2->Open ( | |
Handle2, | |
&Handle1, | |
AlignedNode->PathName, | |
OpenMode &~EFI_FILE_MODE_CREATE, | |
Attributes | |
); | |
// | |
// if above failed now open and create the 'item' | |
// if OpenMode EFI_FILE_MODE_CREATE bit was on (but disabled above) | |
// | |
if ((EFI_ERROR (Status)) && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) { | |
Status = Handle2->Open ( | |
Handle2, | |
&Handle1, | |
AlignedNode->PathName, | |
OpenMode, | |
Attributes | |
); | |
} | |
} | |
// | |
// Close the last node | |
// | |
ShellInfoObject.NewEfiShellProtocol->CloseFile (Handle2); | |
// | |
// If there's been an error, stop | |
// | |
if (EFI_ERROR (Status)) { | |
break; | |
} | |
} // for loop | |
} | |
} | |
} | |
SHELL_FREE_NON_NULL(AlignedNode); | |
if (EFI_ERROR(Status)) { | |
if (Handle1 != NULL) { | |
ShellInfoObject.NewEfiShellProtocol->CloseFile(Handle1); | |
} | |
} else { | |
*FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle)); | |
} | |
return (Status); | |
} | |
/** | |
Creates a file or directory by name. | |
This function creates an empty new file or directory with the specified attributes and | |
returns the new file's handle. If the file already exists and is read-only, then | |
EFI_INVALID_PARAMETER will be returned. | |
If the file already existed, it is truncated and its attributes updated. If the file is | |
created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL. | |
If the file name begins with >v, then the file handle which is returned refers to the | |
shell environment variable with the specified name. If the shell environment variable | |
already exists and is non-volatile then EFI_INVALID_PARAMETER is returned. | |
@param FileName Pointer to NULL-terminated file path | |
@param FileAttribs The new file's attrbiutes. the different attributes are | |
described in EFI_FILE_PROTOCOL.Open(). | |
@param FileHandle On return, points to the created file handle or directory's handle | |
@retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle. | |
@retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. | |
@retval EFI_UNSUPPORTED could not open the file path | |
@retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not | |
file the file system on the device. | |
@retval EFI_NO_MEDIA the device has no medium. | |
@retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no | |
longer supported. | |
@retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according | |
the DirName. | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. | |
@retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write | |
when the media is write-protected. | |
@retval EFI_ACCESS_DENIED The service denied access to the file. | |
@retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. | |
@retval EFI_VOLUME_FULL The volume is full. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellCreateFile( | |
IN CONST CHAR16 *FileName, | |
IN UINT64 FileAttribs, | |
OUT SHELL_FILE_HANDLE *FileHandle | |
) | |
{ | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
EFI_STATUS Status; | |
// | |
// Is this for an environment variable | |
// do we start with >v | |
// | |
if (StrStr(FileName, L">v") == FileName) { | |
if (!IsVolatileEnv(FileName+2)) { | |
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; | |
*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 file | |
// | |
if (StrCmp(FileName, L"NUL") == 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) { | |
if (!IsVolatileEnv(FileName+2) && | |
((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); | |
// DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath);); | |
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 | |
// | |
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 | |
EFIAPI | |
InternalShellExecuteDevicePath( | |
IN CONST EFI_HANDLE *ParentImageHandle, | |
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
IN CONST CHAR16 *CommandLine OPTIONAL, | |
IN CONST CHAR16 **Environment OPTIONAL, | |
OUT EFI_STATUS *StartImageStatus OPTIONAL | |
) | |
{ | |
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); | |
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); | |
} | |
return (Status); | |
} | |
Status = gBS->OpenProtocol( | |
NewHandle, | |
&gEfiLoadedImageProtocolGuid, | |
(VOID**)&LoadedImage, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (!EFI_ERROR(Status)) { | |
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, NULL, NULL); | |
ASSERT_EFI_ERROR(Status); | |
// | |
// Replace Argv[0] with the full path of the binary we're executing: | |
// If the command line was "foo", the binary might be called "foo.efi". | |
// "The first entry in [Argv] is always the full file path of the | |
// executable" - UEFI Shell Spec section 2.3 | |
// | |
ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath); | |
// The image we're executing isn't necessarily in a filesystem - it might | |
// be memory mapped. In this case EfiShellGetFilePathFromDevicePath will | |
// return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it. | |
if (ImagePath != NULL) { | |
if (ShellParamsProtocol.Argv == NULL) { | |
// Command line was empty or null. | |
// (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL) | |
ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *)); | |
if (ShellParamsProtocol.Argv == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto UnloadImage; | |
} | |
ShellParamsProtocol.Argc = 1; | |
} else { | |
// Free the string UpdateArgcArgv put in Argv[0]; | |
FreePool (ShellParamsProtocol.Argv[0]); | |
} | |
ShellParamsProtocol.Argv[0] = ImagePath; | |
} | |
Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol); | |
ASSERT_EFI_ERROR(Status); | |
///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols) | |
// | |
// now start the image and if the caller wanted the return code pass it to them... | |
// | |
if (!EFI_ERROR(Status)) { | |
StartStatus = gBS->StartImage( | |
NewHandle, | |
0, | |
NULL | |
); | |
if (StartImageStatus != NULL) { | |
*StartImageStatus = StartStatus; | |
} | |
CleanupStatus = gBS->UninstallProtocolInterface( | |
NewHandle, | |
&gEfiShellParametersProtocolGuid, | |
&ShellParamsProtocol | |
); | |
ASSERT_EFI_ERROR(CleanupStatus); | |
goto FreeAlloc; | |
} | |
UnloadImage: | |
// Unload image - We should only get here if we didn't call StartImage | |
gBS->UnloadImage (NewHandle); | |
FreeAlloc: | |
// Free Argv (Allocated in UpdateArgcArgv) | |
if (ShellParamsProtocol.Argv != NULL) { | |
for (Index = 0; Index < ShellParamsProtocol.Argc; Index++) { | |
if (ShellParamsProtocol.Argv[Index] != NULL) { | |
FreePool (ShellParamsProtocol.Argv[Index]); | |
} | |
} | |
FreePool (ShellParamsProtocol.Argv); | |
} | |
} | |
// Restore environment variables | |
if (!IsListEmpty(&OrigEnvs)) { | |
CleanupStatus = SetEnvironmentVariableList(&OrigEnvs); | |
ASSERT_EFI_ERROR (CleanupStatus); | |
} | |
FreePool (NewCmdLine); | |
return(Status); | |
} | |
/** | |
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 command. | |
@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); | |
} | |
DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); | |
DEBUG_CODE_BEGIN(); | |
Temp = ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE); | |
FreePool(Temp); | |
Temp = ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE); | |
FreePool(Temp); | |
Temp = ConvertDevicePathToText(DevPath, TRUE, TRUE); | |
FreePool(Temp); | |
DEBUG_CODE_END(); | |
Temp = NULL; | |
Size = 0; | |
ASSERT((Temp == NULL && Size == 0) || (Temp != NULL)); | |
StrnCatGrow(&Temp, &Size, L"Shell.efi -_exit ", 0); | |
StrnCatGrow(&Temp, &Size, CommandLine, 0); | |
Status = InternalShellExecuteDevicePath( | |
ParentImageHandle, | |
DevPath, | |
Temp, | |
(CONST CHAR16**)Environment, | |
StatusCode); | |
// | |
// de-allocate and return | |
// | |
FreePool(DevPath); | |
FreePool(Temp); | |
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 | |
EFIAPI | |
InternalFreeShellFileInfoNode( | |
IN EFI_SHELL_FILE_INFO *FileListNode | |
) | |
{ | |
if (FileListNode->Info != NULL) { | |
FreePool((VOID*)FileListNode->Info); | |
} | |
if (FileListNode->FileName != NULL) { | |
FreePool((VOID*)FileListNode->FileName); | |
} | |
if (FileListNode->FullName != NULL) { | |
FreePool((VOID*)FileListNode->FullName); | |
} | |
if (FileListNode->Handle != NULL) { | |
ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle); | |
} | |
FreePool(FileListNode); | |
} | |
/** | |
Frees the file list. | |
This function cleans up the file list and any related data structures. It has no | |
impact on the files themselves. | |
@param FileList The file list to free. Type EFI_SHELL_FILE_INFO is | |
defined in OpenFileList() | |
@retval EFI_SUCCESS Free the file list successfully. | |
@retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellFreeFileList( | |
IN EFI_SHELL_FILE_INFO **FileList | |
) | |
{ | |
EFI_SHELL_FILE_INFO *ShellFileListItem; | |
if (FileList == NULL || *FileList == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link) | |
; !IsListEmpty(&(*FileList)->Link) | |
; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link) | |
){ | |
RemoveEntryList(&ShellFileListItem->Link); | |
InternalFreeShellFileInfoNode(ShellFileListItem); | |
} | |
InternalFreeShellFileInfoNode(*FileList); | |
*FileList = NULL; | |
return(EFI_SUCCESS); | |
} | |
/** | |
Deletes the duplicate file names files in the given file list. | |
This function deletes the reduplicate files in the given file list. | |
@param FileList A pointer to the first entry in the file list. | |
@retval EFI_SUCCESS Always success. | |
@retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL; | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellRemoveDupInFileList( | |
IN EFI_SHELL_FILE_INFO **FileList | |
) | |
{ | |
EFI_SHELL_FILE_INFO *ShellFileListItem; | |
EFI_SHELL_FILE_INFO *ShellFileListItem2; | |
EFI_SHELL_FILE_INFO *TempNode; | |
if (FileList == NULL || *FileList == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link) | |
; !IsNull(&(*FileList)->Link, &ShellFileListItem->Link) | |
; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link) | |
){ | |
for ( ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem->Link) | |
; !IsNull(&(*FileList)->Link, &ShellFileListItem2->Link) | |
; ShellFileListItem2 = (EFI_SHELL_FILE_INFO*)GetNextNode(&(*FileList)->Link, &ShellFileListItem2->Link) | |
){ | |
if (gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)ShellFileListItem->FullName, | |
(CHAR16*)ShellFileListItem2->FullName) == 0 | |
){ | |
TempNode = (EFI_SHELL_FILE_INFO *)GetPreviousNode( | |
&(*FileList)->Link, | |
&ShellFileListItem2->Link | |
); | |
RemoveEntryList(&ShellFileListItem2->Link); | |
InternalFreeShellFileInfoNode(ShellFileListItem2); | |
// Set ShellFileListItem2 to PreviousNode so we don't access Freed | |
// memory in GetNextNode in the loop expression above. | |
ShellFileListItem2 = TempNode; | |
} | |
} | |
} | |
return (EFI_SUCCESS); | |
} | |
// | |
// This is the same structure as the external version, but it has no CONST qualifiers. | |
// | |
typedef struct { | |
LIST_ENTRY Link; ///< Linked list members. | |
EFI_STATUS Status; ///< Status of opening the file. Valid only if Handle != NULL. | |
CHAR16 *FullName; ///< Fully qualified filename. | |
CHAR16 *FileName; ///< name of this file. | |
SHELL_FILE_HANDLE Handle; ///< Handle for interacting with the opened file or NULL if closed. | |
EFI_FILE_INFO *Info; ///< Pointer to the FileInfo struct for this file or NULL. | |
} EFI_SHELL_FILE_INFO_NO_CONST; | |
/** | |
Allocates and duplicates a EFI_SHELL_FILE_INFO node. | |
@param[in] Node The node to copy from. | |
@param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise. | |
@retval NULL a memory allocation error ocurred | |
@return != NULL a pointer to the new node | |
**/ | |
EFI_SHELL_FILE_INFO* | |
EFIAPI | |
InternalDuplicateShellFileInfo( | |
IN EFI_SHELL_FILE_INFO *Node, | |
IN BOOLEAN Save | |
) | |
{ | |
EFI_SHELL_FILE_INFO_NO_CONST *NewNode; | |
// | |
// try to confirm that the objects are in sync | |
// | |
ASSERT(sizeof(EFI_SHELL_FILE_INFO_NO_CONST) == sizeof(EFI_SHELL_FILE_INFO)); | |
NewNode = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); | |
if (NewNode == NULL) { | |
return (NULL); | |
} | |
NewNode->FullName = AllocateCopyPool(StrSize(Node->FullName), Node->FullName); | |
NewNode->FileName = AllocateCopyPool(StrSize(Node->FileName), Node->FileName); | |
NewNode->Info = AllocateCopyPool((UINTN)Node->Info->Size, Node->Info); | |
if ( NewNode->FullName == NULL | |
|| NewNode->FileName == NULL | |
|| NewNode->Info == NULL | |
){ | |
SHELL_FREE_NON_NULL(NewNode->FullName); | |
SHELL_FREE_NON_NULL(NewNode->FileName); | |
SHELL_FREE_NON_NULL(NewNode->Info); | |
SHELL_FREE_NON_NULL(NewNode); | |
return(NULL); | |
} | |
NewNode->Status = Node->Status; | |
NewNode->Handle = Node->Handle; | |
if (!Save) { | |
Node->Handle = NULL; | |
} | |
return((EFI_SHELL_FILE_INFO*)NewNode); | |
} | |
/** | |
Allocates and populates a EFI_SHELL_FILE_INFO structure. if any memory operation | |
failed it will return NULL. | |
@param[in] BasePath the Path to prepend onto filename for FullPath | |
@param[in] Status Status member initial value. | |
@param[in] FileName FileName member initial value. | |
@param[in] Handle Handle member initial value. | |
@param[in] Info Info struct to copy. | |
@retval NULL An error ocurred. | |
@return a pointer to the newly allocated structure. | |
**/ | |
EFI_SHELL_FILE_INFO * | |
EFIAPI | |
CreateAndPopulateShellFileInfo( | |
IN CONST CHAR16 *BasePath, | |
IN CONST EFI_STATUS Status, | |
IN CONST CHAR16 *FileName, | |
IN CONST SHELL_FILE_HANDLE Handle, | |
IN CONST EFI_FILE_INFO *Info | |
) | |
{ | |
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) | |
){ | |
// | |
// allocate a new EFI_SHELL_FILE_INFO and populate it... | |
// | |
ShellFileListItem = CreateAndPopulateShellFileInfo( | |
BasePath, | |
EFI_SUCCESS, // success since we didnt fail to open it... | |
FileInfo->FileName, | |
NULL, // no handle since not open | |
FileInfo); | |
if (ShellFileList == NULL) { | |
ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); | |
ASSERT(ShellFileList != NULL); | |
InitializeListHead(&ShellFileList->Link); | |
} | |
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(NewGuid, Guid); | |
} | |
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); | |
} | |
/** | |
Updates a file name to be preceeded by the mapped drive name | |
@param[in] BasePath the Mapped drive name to prepend | |
@param[in, out] Path pointer to pointer to the file name to update. | |
@retval EFI_SUCCESS | |
@retval EFI_OUT_OF_RESOURCES | |
**/ | |
EFI_STATUS | |
EFIAPI | |
UpdateFileName( | |
IN CONST CHAR16 *BasePath, | |
IN OUT CHAR16 **Path | |
) | |
{ | |
CHAR16 *Path2; | |
UINTN Path2Size; | |
Path2Size = 0; | |
Path2 = NULL; | |
ASSERT(Path != NULL); | |
ASSERT(*Path != NULL); | |
ASSERT(BasePath != NULL); | |
// | |
// convert a local path to an absolute path | |
// | |
if (StrStr(*Path, L":") == NULL) { | |
ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); | |
StrnCatGrow(&Path2, &Path2Size, BasePath, 0); | |
if (Path2 == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); | |
StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0); | |
if (Path2 == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
} | |
FreePool(*Path); | |
(*Path) = Path2; | |
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 | |
EFIAPI | |
ShellSearchHandle( | |
IN CONST CHAR16 *FilePattern, | |
IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation, | |
IN SHELL_FILE_HANDLE FileHandle, | |
IN OUT EFI_SHELL_FILE_INFO **FileList, | |
IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL, | |
IN CONST CHAR16 *MapName | |
) | |
{ | |
EFI_STATUS Status; | |
CONST CHAR16 *NextFilePatternStart; | |
CHAR16 *CurrentFilePattern; | |
EFI_SHELL_FILE_INFO *ShellInfo; | |
EFI_SHELL_FILE_INFO *ShellInfoNode; | |
EFI_SHELL_FILE_INFO *NewShellNode; | |
EFI_FILE_INFO *FileInfo; | |
BOOLEAN Directory; | |
CHAR16 *NewFullName; | |
UINTN Size; | |
if ( FilePattern == NULL | |
|| UnicodeCollation == NULL | |
|| FileList == NULL | |
){ | |
return (EFI_INVALID_PARAMETER); | |
} | |
ShellInfo = NULL; | |
CurrentFilePattern = NULL; | |
if (*FilePattern == L'\\') { | |
FilePattern++; | |
} | |
for( NextFilePatternStart = FilePattern | |
; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\' | |
; NextFilePatternStart++); | |
CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16)); | |
ASSERT(CurrentFilePattern != NULL); | |
StrnCpy(CurrentFilePattern, FilePattern, NextFilePatternStart-FilePattern); | |
if (CurrentFilePattern[0] == CHAR_NULL | |
&&NextFilePatternStart[0] == CHAR_NULL | |
){ | |
// | |
// we want the parent or root node (if no parent) | |
// | |
if (ParentNode == NULL) { | |
// | |
// We want the root node. create the node. | |
// | |
FileInfo = FileHandleGetInfo(FileHandle); | |
NewShellNode = CreateAndPopulateShellFileInfo( | |
MapName, | |
EFI_SUCCESS, | |
L"\\", | |
FileHandle, | |
FileInfo | |
); | |
SHELL_FREE_NON_NULL(FileInfo); | |
} else { | |
// | |
// Add the current parameter FileHandle to the list, then end... | |
// | |
NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE); | |
} | |
if (NewShellNode == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
} else { | |
NewShellNode->Handle = NULL; | |
if (*FileList == NULL) { | |
*FileList = AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO)); | |
InitializeListHead(&((*FileList)->Link)); | |
} | |
// | |
// Add to the returning to use list | |
// | |
InsertTailList(&(*FileList)->Link, &NewShellNode->Link); | |
Status = EFI_SUCCESS; | |
} | |
} else { | |
Status = EfiShellFindFilesInDir(FileHandle, &ShellInfo); | |
if (!EFI_ERROR(Status)){ | |
if (StrStr(NextFilePatternStart, L"\\") != NULL){ | |
Directory = TRUE; | |
} else { | |
Directory = FALSE; | |
} | |
for ( ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetFirstNode(&ShellInfo->Link) | |
; !IsNull (&ShellInfo->Link, &ShellInfoNode->Link) | |
; ShellInfoNode = (EFI_SHELL_FILE_INFO*)GetNextNode(&ShellInfo->Link, &ShellInfoNode->Link) | |
){ | |
if (UnicodeCollation->MetaiMatch(UnicodeCollation, (CHAR16*)ShellInfoNode->FileName, CurrentFilePattern)){ | |
if (ShellInfoNode->FullName != NULL && StrStr(ShellInfoNode->FullName, L":") == NULL) { | |
Size = StrSize(ShellInfoNode->FullName); | |
Size += StrSize(MapName) + sizeof(CHAR16); | |
NewFullName = AllocateZeroPool(Size); | |
if (NewFullName == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
} else { | |
StrnCpy(NewFullName, MapName, Size/sizeof(CHAR16)-1); | |
StrnCat(NewFullName, ShellInfoNode->FullName+1, (Size/sizeof(CHAR16))-StrLen(NewFullName)-1); | |
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); | |
} | |
} else if (!EFI_ERROR(Status)) { | |
// | |
// should be a file | |
// | |
// | |
// copy the information we need into a new Node | |
// | |
NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE); | |
ASSERT(NewShellNode != NULL); | |
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); | |
} | |
} | |
} | |
FreePool(CurrentFilePattern); | |
return (Status); | |
} | |
/** | |
Find files that match a specified pattern. | |
This function searches for all files and directories that match the specified | |
FilePattern. The FilePattern can contain wild-card characters. The resulting file | |
information is placed in the file list FileList. | |
Wildcards are processed | |
according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. | |
The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo | |
field is set to NULL. | |
if *FileList is not NULL then it must be a pre-existing and properly initialized list. | |
@param FilePattern Points to a NULL-terminated shell file path, including wildcards. | |
@param FileList On return, points to the start of a file list containing the names | |
of all matching files or else points to NULL if no matching files | |
were found. only on a EFI_SUCCESS return will; this be non-NULL. | |
@retval EFI_SUCCESS Files found. FileList is a valid list. | |
@retval EFI_NOT_FOUND No files found. | |
@retval EFI_NO_MEDIA The device has no media | |
@retval EFI_DEVICE_ERROR The device reported an error | |
@retval EFI_VOLUME_CORRUPTED The file system structures are corrupted | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellFindFiles( | |
IN CONST CHAR16 *FilePattern, | |
OUT EFI_SHELL_FILE_INFO **FileList | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *PatternCopy; | |
CHAR16 *PatternCurrentLocation; | |
EFI_DEVICE_PATH_PROTOCOL *RootDevicePath; | |
SHELL_FILE_HANDLE RootFileHandle; | |
CHAR16 *MapName; | |
UINTN Count; | |
if ( FilePattern == NULL | |
|| FileList == NULL | |
|| StrStr(FilePattern, L":") == NULL | |
){ | |
return (EFI_INVALID_PARAMETER); | |
} | |
Status = EFI_SUCCESS; | |
RootDevicePath = NULL; | |
RootFileHandle = NULL; | |
MapName = NULL; | |
PatternCopy = AllocateCopyPool(StrSize(FilePattern), FilePattern); | |
if (PatternCopy == NULL) { | |
return (EFI_OUT_OF_RESOURCES); | |
} | |
PatternCopy = PathCleanUpDirectories(PatternCopy); | |
Count = StrStr(PatternCopy, L":") - PatternCopy; | |
Count += 2; | |
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); | |
} | |
FreePool(RootDevicePath); | |
} | |
} | |
SHELL_FREE_NON_NULL(PatternCopy); | |
SHELL_FREE_NON_NULL(MapName); | |
return(Status); | |
} | |
/** | |
Opens the files that match the path specified. | |
This function opens all of the files specified by Path. Wildcards are processed | |
according to the rules specified in UEFI Shell 2.0 spec section 3.7.1. Each | |
matching file has an EFI_SHELL_FILE_INFO structure created in a linked list. | |
@param Path A pointer to the path string. | |
@param OpenMode Specifies the mode used to open each file, EFI_FILE_MODE_READ or | |
EFI_FILE_MODE_WRITE. | |
@param FileList Points to the start of a list of files opened. | |
@retval EFI_SUCCESS Create the file list successfully. | |
@return Others Can't create the file list. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellOpenFileList( | |
IN CHAR16 *Path, | |
IN UINT64 OpenMode, | |
IN OUT EFI_SHELL_FILE_INFO **FileList | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_SHELL_FILE_INFO *ShellFileListItem; | |
CHAR16 *Path2; | |
UINTN Path2Size; | |
CONST CHAR16 *CurDir; | |
BOOLEAN Found; | |
PathCleanUpDirectories(Path); | |
Path2Size = 0; | |
Path2 = NULL; | |
if (FileList == NULL || *FileList == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
if (*Path == L'.' && *(Path+1) == L'\\') { | |
Path+=2; | |
} | |
// | |
// convert a local path to an absolute path | |
// | |
if (StrStr(Path, L":") == NULL) { | |
CurDir = EfiShellGetCurDir(NULL); | |
ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL)); | |
StrnCatGrow(&Path2, &Path2Size, CurDir, 0); | |
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; | |
LIST_ENTRY List; | |
ENV_VAR_LIST *Node; | |
CHAR16 *CurrentWriteLocation; | |
Size = 0; | |
Buffer = NULL; | |
if (Name == NULL) { | |
// | |
// Get all our environment variables | |
// | |
InitializeListHead(&List); | |
Status = GetEnvironmentVariableList(&List); | |
if (EFI_ERROR(Status)){ | |
return (NULL); | |
} | |
// | |
// Build the semi-colon delimited list. (2 passes) | |
// | |
for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List) | |
; !IsNull(&List, &Node->Link) | |
; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link) | |
){ | |
ASSERT(Node->Key != NULL); | |
Size += StrSize(Node->Key); | |
} | |
Size += 2*sizeof(CHAR16); | |
Buffer = AllocateZeroPool(Size); | |
if (Buffer == NULL) { | |
if (!IsListEmpty (&List)) { | |
FreeEnvironmentVariableList(&List); | |
} | |
return (NULL); | |
} | |
CurrentWriteLocation = (CHAR16*)Buffer; | |
for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List) | |
; !IsNull(&List, &Node->Link) | |
; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link) | |
){ | |
ASSERT(Node->Key != NULL); | |
StrnCpy(CurrentWriteLocation, Node->Key, (Size)/sizeof(CHAR16) - (CurrentWriteLocation - ((CHAR16*)Buffer)) - 1); | |
CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1; | |
} | |
// | |
// Free the list... | |
// | |
if (!IsListEmpty (&List)) { | |
FreeEnvironmentVariableList(&List); | |
} | |
} else { | |
// | |
// We are doing a specific environment variable | |
// | |
// | |
// get the size we need for this EnvVariable | |
// | |
Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
// | |
// Allocate the space and recall the get function | |
// | |
Buffer = AllocateZeroPool(Size); | |
Status = SHELL_GET_ENVIRONMENT_VARIABLE_AND_ATTRIBUTES(Name, Attributes, &Size, Buffer); | |
} | |
// | |
// we didnt get it (might not exist) | |
// free the memory if we allocated any and return NULL | |
// | |
if (EFI_ERROR(Status)) { | |
if (Buffer != NULL) { | |
FreePool(Buffer); | |
} | |
return (NULL); | |
} | |
} | |
// | |
// 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 | |
EFIAPI | |
InternalEfiShellSetEnv( | |
IN CONST CHAR16 *Name, | |
IN CONST CHAR16 *Value, | |
IN BOOLEAN Volatile | |
) | |
{ | |
if (Value == NULL || StrLen(Value) == 0) { | |
return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name)); | |
} else { | |
SHELL_DELETE_ENVIRONMENT_VARIABLE(Name); | |
if (Volatile) { | |
return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value)); | |
} else { | |
return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value)); | |
} | |
} | |
} | |
/** | |
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 (gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"cwd") == 0 | |
||gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"Lasterror") == 0 | |
||gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"profiles") == 0 | |
||gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"uefishellsupport") == 0 | |
||gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"uefishellversion") == 0 | |
||gUnicodeCollation->StriColl( | |
gUnicodeCollation, | |
(CHAR16*)Name, | |
L"uefiversion") == 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). | |
@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 | |
@param FileSystem A pointer to the file system's mapped name. If NULL, then the current working | |
directory is changed. | |
@param Dir Points to the NULL-terminated directory on the device specified by FileSystem. | |
@retval EFI_SUCCESS The operation was sucessful | |
@retval EFI_NOT_FOUND The file system could not be found | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiShellSetCurDir( | |
IN CONST CHAR16 *FileSystem OPTIONAL, | |
IN CONST CHAR16 *Dir | |
) | |
{ | |
CHAR16 *MapName; | |
SHELL_MAP_LIST *MapListItem; | |
UINTN Size; | |
EFI_STATUS Status; | |
CHAR16 *TempString; | |
CHAR16 *DirectoryName; | |
UINTN TempLen; | |
Size = 0; | |
MapName = NULL; | |
MapListItem = NULL; | |
TempString = NULL; | |
DirectoryName = NULL; | |
if ((FileSystem == NULL && Dir == NULL) || Dir == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
if (IsListEmpty(&gShellMapList.Link)){ | |
return (EFI_NOT_FOUND); | |
} | |
DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0); | |
ASSERT(DirectoryName != NULL); | |
PathCleanUpDirectories(DirectoryName); | |
if (FileSystem == NULL) { | |
// | |
// determine the file system mapping to use | |
// | |
if (StrStr(DirectoryName, L":") != NULL) { | |
ASSERT(MapName == NULL); | |
MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1)); | |
} | |
// | |
// find the file system mapping's entry in the list | |
// or use current | |
// | |
if (MapName != NULL) { | |
MapListItem = ShellCommandFindMapItem(MapName); | |
// | |
// make that the current file system mapping | |
// | |
if (MapListItem != NULL) { | |
gShellCurDir = MapListItem; | |
} | |
} else { | |
MapListItem = gShellCurDir; | |
} | |
if (MapListItem == NULL) { | |
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); | |
} | |
} 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)); | |
MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0); | |
} | |
} else { | |
// | |
// cant have a mapping in the directory... | |
// | |
if (StrStr(DirectoryName, L":") != NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
// | |
// FileSystem != NULL | |
// | |
MapListItem = ShellCommandFindMapItem(FileSystem); | |
if (MapListItem == NULL) { | |
return (EFI_INVALID_PARAMETER); | |
} | |
// gShellCurDir = 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 = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0); | |
} | |
} | |
} | |
// | |
// if updated the current directory then update the environment variable | |
// | |
if (MapListItem == gShellCurDir) { | |
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)); | |
ASSERT(FixCommand != NULL); | |
StrnCpy(FixCommand, Command, StrLen(Command)-4); | |
Status = ProcessManFile(FixCommand, FixCommand, Sections, NULL, HelpText); | |
FreePool(FixCommand); | |
return Status; | |
} else { | |
return (ProcessManFile(Command, Command, Sections, NULL, HelpText)); | |
} | |
} | |
} | |
/** | |
Gets the enable status of the page break output mode. | |
User can use this function to determine current page break mode. | |
@retval TRUE The page break output mode is enabled. | |
@retval FALSE The page break output mode is disabled. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EfiShellGetPageBreak( | |
VOID | |
) | |
{ | |
return(ShellInfoObject.PageBreakEnabled); | |
} | |
/** | |
Judges whether the active shell is the root shell. | |
This function makes the user to know that whether the active Shell is the root shell. | |
@retval TRUE The active Shell is the root Shell. | |
@retval FALSE The active Shell is NOT the root Shell. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EfiShellIsRootShell( | |
VOID | |
) | |
{ | |
return(ShellInfoObject.RootShellInstance); | |
} | |
/** | |
function to return a semi-colon delimeted list of all alias' in the current shell | |
up to caller to free the memory. | |
@retval NULL No alias' were found | |
@retval NULL An error ocurred getting alias' | |
@return !NULL a list of all alias' | |
**/ | |
CHAR16 * | |
EFIAPI | |
InternalEfiShellGetListAlias( | |
) | |
{ | |
UINT64 MaxStorSize; | |
UINT64 RemStorSize; | |
UINT64 MaxVarSize; | |
EFI_STATUS Status; | |
EFI_GUID Guid; | |
CHAR16 *VariableName; | |
UINTN NameSize; | |
CHAR16 *RetVal; | |
UINTN RetSize; | |
Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize); | |
ASSERT_EFI_ERROR(Status); | |
VariableName = AllocateZeroPool((UINTN)MaxVarSize); | |
RetSize = 0; | |
RetVal = NULL; | |
if (VariableName == NULL) { | |
return (NULL); | |
} | |
VariableName[0] = CHAR_NULL; | |
while (TRUE) { | |
NameSize = (UINTN)MaxVarSize; | |
Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid); | |
if (Status == EFI_NOT_FOUND){ | |
break; | |
} | |
ASSERT_EFI_ERROR(Status); | |
if (EFI_ERROR(Status)) { | |
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 | |
FreePool(VariableName); | |
return (RetVal); | |
} | |
/** | |
Convert a null-terminated unicode string, in-place, to all lowercase. | |
Then return it. | |
@param Str The null-terminated string to be converted to all lowercase. | |
@return The null-terminated string converted into all lowercase. | |
**/ | |
CHAR16 * | |
ToLower ( | |
CHAR16 *Str | |
) | |
{ | |
UINTN Index; | |
for (Index = 0; Str[Index] != L'\0'; Index++) { | |
if (Str[Index] >= L'A' && Str[Index] <= L'Z') { | |
Str[Index] -= (CHAR16)(L'A' - L'a'); | |
} | |
} | |
return Str; | |
} | |
/** | |
This function returns the command associated with a alias or a list of all | |
alias'. | |
@param[in] Alias Points to the NULL-terminated shell alias. | |
If this parameter is NULL, then all | |
aliases will be returned in ReturnedData. | |
@param[out] Volatile upon return of a single command if TRUE indicates | |
this is stored in a volatile fashion. FALSE otherwise. | |
@return If Alias is not NULL, it will return a pointer to | |
the NULL-terminated command for that alias. | |
If Alias is NULL, ReturnedData points to a ';' | |
delimited list of alias (e.g. | |
ReturnedData = "dir;del;copy;mfp") that is NULL-terminated. | |
@retval NULL an error ocurred | |
@retval NULL Alias was not a valid Alias | |
**/ | |
CONST CHAR16 * | |
EFIAPI | |
EfiShellGetAlias( | |
IN CONST CHAR16 *Alias, | |
OUT BOOLEAN *Volatile OPTIONAL | |
) | |
{ | |
CHAR16 *RetVal; | |
UINTN RetSize; | |
UINT32 Attribs; | |
EFI_STATUS Status; | |
CHAR16 *AliasLower; | |
// Convert to lowercase to make aliases case-insensitive | |
if (Alias != NULL) { | |
AliasLower = AllocateCopyPool (StrSize (Alias), Alias); | |
ASSERT (AliasLower != NULL); | |
ToLower (AliasLower); | |
if (Volatile == NULL) { | |
return (AddBufferToFreeList(GetVariable(AliasLower, &gShellAliasGuid))); | |
} | |
RetSize = 0; | |
RetVal = NULL; | |
Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
RetVal = AllocateZeroPool(RetSize); | |
Status = gRT->GetVariable(AliasLower, &gShellAliasGuid, &Attribs, &RetSize, RetVal); | |
} | |
if (EFI_ERROR(Status)) { | |
if (RetVal != NULL) { | |
FreePool(RetVal); | |
} | |
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 | |
EFIAPI | |
InternalSetAlias( | |
IN CONST CHAR16 *Command, | |
IN CONST CHAR16 *Alias, | |
IN BOOLEAN Volatile | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *AliasLower; | |
// Convert to lowercase to make aliases case-insensitive | |
if (Alias != NULL) { | |
AliasLower = AllocateCopyPool (StrSize (Alias), Alias); | |
ASSERT (AliasLower != NULL); | |
ToLower (AliasLower); | |
} else { | |
AliasLower = NULL; | |
} | |
// | |
// We must be trying to remove one if Alias is NULL | |
// | |
if (Alias == NULL) { | |
// | |
// remove an alias (but passed in COMMAND parameter) | |
// | |
Status = (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL)); | |
} else { | |
// | |
// Add and replace are the same | |
// | |
// We dont check the error return on purpose since the variable may not exist. | |
gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL); | |
Status = (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command)); | |
} | |
if (Alias != NULL) { | |
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 | |
EFIAPI | |
CreatePopulateInstallShellProtocol ( | |
IN OUT EFI_SHELL_PROTOCOL **NewShell | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN BufferSize; | |
EFI_HANDLE *Buffer; | |
UINTN HandleCounter; | |
SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode; | |
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++) { | |
OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST)); | |
ASSERT(OldProtocolNode != NULL); | |
Status = gBS->OpenProtocol(Buffer[HandleCounter], | |
&gEfiShellProtocolGuid, | |
(VOID **) &(OldProtocolNode->Interface), | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (!EFI_ERROR(Status)) { | |
// | |
// reinstall over the old one... | |
// | |
OldProtocolNode->Handle = Buffer[HandleCounter]; | |
Status = gBS->ReinstallProtocolInterface( | |
OldProtocolNode->Handle, | |
&gEfiShellProtocolGuid, | |
OldProtocolNode->Interface, | |
(VOID*)(&mShellProtocol)); | |
if (!EFI_ERROR(Status)) { | |
// | |
// we reinstalled sucessfully. log this so we can reverse it later. | |
// | |
// | |
// add to the list for subsequent... | |
// | |
InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link); | |
} | |
} | |
} | |
FreePool(Buffer); | |
} else if (Status == EFI_NOT_FOUND) { | |
ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link)); | |
// | |
// no one else published yet. just publish it ourselves. | |
// | |
Status = gBS->InstallProtocolInterface ( | |
&gImageHandle, | |
&gEfiShellProtocolGuid, | |
EFI_NATIVE_INTERFACE, | |
(VOID*)(&mShellProtocol)); | |
} | |
if (PcdGetBool(PcdShellSupportOldProtocols)){ | |
///@todo support ShellEnvironment2 | |
///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also? | |
} | |
if (!EFI_ERROR(Status)) { | |
*NewShell = &mShellProtocol; | |
} | |
return (Status); | |
} | |
/** | |
Opposite of CreatePopulateInstallShellProtocol. | |
Free all memory and restore the system to the state it was in before calling | |
CreatePopulateInstallShellProtocol. | |
@param[in, out] NewShell The pointer to the new shell protocol structure. | |
@retval EFI_SUCCESS The operation was successful. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CleanUpShellProtocol ( | |
IN OUT EFI_SHELL_PROTOCOL *NewShell | |
) | |
{ | |
EFI_STATUS Status; | |
SHELL_PROTOCOL_HANDLE_LIST *Node2; | |
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; | |
// | |
// 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); | |
Status = gBS->ReinstallProtocolInterface(Node2->Handle, | |
&gEfiShellProtocolGuid, | |
NewShell, | |
Node2->Interface); | |
FreePool(Node2); | |
} | |
} else { | |
// | |
// no need to restore | |
// | |
Status = gBS->UninstallProtocolInterface(gImageHandle, | |
&gEfiShellProtocolGuid, | |
NewShell); | |
} | |
Status = gBS->CloseEvent(NewShell->ExecutionBreak); | |
NewShell->ExecutionBreak = NULL; | |
Status = gBS->OpenProtocol( | |
gST->ConsoleInHandle, | |
&gEfiSimpleTextInputExProtocolGuid, | |
(VOID**)&SimpleEx, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (!EFI_ERROR (Status)) { | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle1); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle2); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle3); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlCNotifyHandle4); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle1); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle2); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle3); | |
Status = SimpleEx->UnregisterKeyNotify(SimpleEx, ShellInfoObject.CtrlSNotifyHandle4); | |
} | |
return (Status); | |
} | |
/** | |
Notification function for keystrokes. | |
@param[in] KeyData The key that was pressed. | |
@retval EFI_SUCCESS The operation was successful. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
NotificationFunction( | |
IN EFI_KEY_DATA *KeyData | |
) | |
{ | |
if ( ((KeyData->Key.UnicodeChar == L'c') && | |
(KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED))) || | |
(KeyData->Key.UnicodeChar == 3) | |
){ | |
if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) { | |
return (EFI_UNSUPPORTED); | |
} | |
return (gBS->SignalEvent(ShellInfoObject.NewEfiShellProtocol->ExecutionBreak)); | |
} else if ((KeyData->Key.UnicodeChar == L's') && | |
(KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED) || KeyData->KeyState.KeyShiftState == (EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED)) | |
){ | |
ShellInfoObject.HaltOutput = TRUE; | |
} | |
return (EFI_SUCCESS); | |
} | |
/** | |
Function to start monitoring for CTRL-C using SimpleTextInputEx. This | |
feature's enabled state was not known when the shell initially launched. | |
@retval EFI_SUCCESS The feature is enabled. | |
@retval EFI_OUT_OF_RESOURCES There is not enough mnemory available. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
InernalEfiShellStartMonitor( | |
VOID | |
) | |
{ | |
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx; | |
EFI_KEY_DATA KeyData; | |
EFI_STATUS Status; | |
Status = gBS->OpenProtocol( | |
gST->ConsoleInHandle, | |
&gEfiSimpleTextInputExProtocolGuid, | |
(VOID**)&SimpleEx, | |
gImageHandle, | |
NULL, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL); | |
if (EFI_ERROR(Status)) { | |
ShellPrintHiiEx( | |
-1, | |
-1, | |
NULL, | |
STRING_TOKEN (STR_SHELL_NO_IN_EX), | |
ShellInfoObject.HiiHandle); | |
return (EFI_SUCCESS); | |
} | |
if (ShellInfoObject.NewEfiShellProtocol->ExecutionBreak == NULL) { | |
return (EFI_UNSUPPORTED); | |
} | |
KeyData.KeyState.KeyToggleState = 0; | |
KeyData.Key.ScanCode = 0; | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; | |
KeyData.Key.UnicodeChar = L'c'; | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlCNotifyHandle1); | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlCNotifyHandle2); | |
} | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED; | |
KeyData.Key.UnicodeChar = 3; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlCNotifyHandle3); | |
} | |
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_RIGHT_CONTROL_PRESSED; | |
if (!EFI_ERROR(Status)) { | |
Status = SimpleEx->RegisterKeyNotify( | |
SimpleEx, | |
&KeyData, | |
NotificationFunction, | |
&ShellInfoObject.CtrlCNotifyHandle4); | |
} | |
return (Status); | |
} | |