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