summaryrefslogtreecommitdiffstats
path: root/ShellPkg/Application/Shell/ShellProtocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'ShellPkg/Application/Shell/ShellProtocol.c')
-rw-r--r--ShellPkg/Application/Shell/ShellProtocol.c3122
1 files changed, 3122 insertions, 0 deletions
diff --git a/ShellPkg/Application/Shell/ShellProtocol.c b/ShellPkg/Application/Shell/ShellProtocol.c
new file mode 100644
index 0000000000..214693f2ae
--- /dev/null
+++ b/ShellPkg/Application/Shell/ShellProtocol.c
@@ -0,0 +1,3122 @@
+/** @file
+ Member functions of EFI_SHELL_PROTOCOL and functions for creation,
+ manipulation, and initialization of EFI_SHELL_PROTOCOL.
+
+ Copyright (c) 2009 - 2010, 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"
+#include <Library/FileHandleLib.h>
+
+/**
+ Close an open file handle.
+
+ This function closes a specified file handle. All "dirty" cached file data is
+ flushed to the device, and the file is closed. In all cases the handle is
+ closed.
+
+ @param[in] FileHandle The file handle to close.
+
+ @retval EFI_SUCCESS The file handle was closed successfully.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellClose (
+ IN SHELL_FILE_HANDLE FileHandle
+ )
+{
+ ShellFileHandleRemove(FileHandle);
+ return (FileHandleClose(FileHandle));
+}
+
+/**
+ Internal worker to determine whether there is a file system somewhere
+ upon the device path specified.
+
+ @param[in] DevicePath The device path to test.
+
+ @retval TRUE gEfiSimpleFileSystemProtocolGuid was installed on a handle with this device path
+ @retval FALSE gEfiSimpleFileSystemProtocolGuid was not found.
+**/
+BOOLEAN
+EFIAPI
+InternalShellProtocolIsSimpleFileSystemPresent(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ Handle = NULL;
+
+ DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)DevicePath;
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &Handle);
+
+ if ((Handle != NULL) && (!EFI_ERROR(Status))) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/**
+ Internal worker debug helper function to print out maps as they are added.
+
+ @param[in] Mapping string mapping that has been added
+ @param[in] DevicePath pointer to device path that has been mapped.
+
+ @retval EFI_SUCCESS the operation was successful.
+ @return other an error ocurred
+
+ @sa LocateHandle
+ @sa OpenProtocol
+**/
+EFI_STATUS
+EFIAPI
+InternalShellProtocolDebugPrintMessage (
+ IN CONST CHAR16 *Mapping,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevicePathToText;
+ EFI_STATUS Status;
+ CHAR16 *Temp;
+
+ Status = EFI_SUCCESS;
+ DEBUG_CODE_BEGIN();
+ DevicePathToText = NULL;
+
+ Status = gBS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid,
+ NULL,
+ (VOID**)&DevicePathToText);
+ if (Mapping != NULL) {
+ DEBUG((EFI_D_INFO, "Added new map item:\"%S\"\r\n", Mapping));
+ }
+ if (!EFI_ERROR(Status)) {
+ if (DevicePath != NULL) {
+ Temp = DevicePathToText->ConvertDevicePathToText(DevicePath, TRUE, TRUE);
+ DEBUG((EFI_D_INFO, "DevicePath: %S\r\n", Temp));
+ FreePool(Temp);
+ }
+ }
+ DEBUG_CODE_END();
+ return (Status);
+}
+
+/**
+ This function creates a mapping for a device path.
+
+ If both DeviecPath and Mapping are NULL, this will reset the mapping to default values.
+
+ @param DevicePath Points to the device path. If this is NULL and Mapping points to a valid mapping,
+ then the mapping will be deleted.
+ @param Mapping Points to the NULL-terminated mapping for the device path. Must end with a ':'
+
+ @retval EFI_SUCCESS Mapping created or deleted successfully.
+ @retval EFI_NO_MAPPING There is no handle that corresponds exactly to DevicePath. See the
+ boot service function LocateDevicePath().
+ @retval EFI_ACCESS_DENIED The mapping is a built-in alias.
+ @retval EFI_INVALID_PARAMETER Mapping was NULL
+ @retval EFI_INVALID_PARAMETER Mapping did not end with a ':'
+ @retval EFI_INVALID_PARAMETER DevicePath was not pointing at a device that had a SIMPLE_FILE_SYSTEM_PROTOCOL installed.
+ @retval EFI_NOT_FOUND There was no mapping found to delete
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetMap(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath OPTIONAL,
+ IN CONST CHAR16 *Mapping
+ )
+{
+ EFI_STATUS Status;
+ SHELL_MAP_LIST *MapListNode;
+
+ if (Mapping == NULL){
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (Mapping[StrLen(Mapping)-1] != ':') {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // Delete the mapping
+ //
+ if (DevicePath == NULL) {
+ if (IsListEmpty(&gShellMapList.Link)) {
+ return (EFI_NOT_FOUND);
+ }
+ for ( MapListNode = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &MapListNode->Link)
+ ; MapListNode = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListNode->Link)
+ ){
+ if (StringNoCaseCompare(&MapListNode->MapName, &Mapping) == 0) {
+ RemoveEntryList(&MapListNode->Link);
+ FreePool(MapListNode);
+ return (EFI_SUCCESS);
+ }
+ } // for loop
+
+ //
+ // We didnt find one to delete
+ //
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // make sure this is a valid to add device path
+ //
+ ///@todo add BlockIo to this test...
+ if (!InternalShellProtocolIsSimpleFileSystemPresent(DevicePath)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ //
+ // First make sure there is no old mapping
+ //
+ Status = EfiShellSetMap(NULL, Mapping);
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_FOUND)) {
+ return (Status);
+ }
+
+ //
+ // now add the new one.
+ //
+ Status = ShellCommandAddMapItemAndUpdatePath(Mapping, DevicePath, 0, FALSE);
+
+ return(Status);
+}
+
+/**
+ Gets the device path from the mapping.
+
+ This function gets the device path associated with a mapping.
+
+ @param Mapping A pointer to the mapping
+
+ @retval !=NULL Pointer to the device path that corresponds to the
+ device mapping. The returned pointer does not need
+ to be freed.
+ @retval NULL There is no device path associated with the
+ specified mapping.
+**/
+CONST EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiShellGetDevicePathFromMap(
+ IN CONST CHAR16 *Mapping
+ )
+{
+ SHELL_MAP_LIST *MapListItem;
+ CHAR16 *NewName;
+ UINTN Size;
+
+ NewName = NULL;
+ Size = 0;
+
+ StrnCatGrow(&NewName, &Size, Mapping, 0);
+ if (Mapping[StrLen(Mapping)-1] != L':') {
+ StrnCatGrow(&NewName, &Size, L":", 0);
+ }
+
+ MapListItem = ShellCommandFindMapItem(NewName);
+
+ FreePool(NewName);
+
+ if (MapListItem != NULL) {
+ return (MapListItem->DevicePath);
+ }
+ return(NULL);
+}
+
+/**
+ Gets the mapping(s) that most closely matches the device path.
+
+ This function gets the mapping which corresponds to the device path *DevicePath. If
+ there is no exact match, then the mapping which most closely matches *DevicePath
+ is returned, and *DevicePath is updated to point to the remaining portion of the
+ device path. If there is an exact match, the mapping is returned and *DevicePath
+ points to the end-of-device-path node.
+
+ If there are multiple map names they will be semi-colon seperated in the
+ NULL-terminated string.
+
+ @param DevicePath On entry, points to a device path pointer. On
+ exit, updates the pointer to point to the
+ portion of the device path after the mapping.
+
+ @retval NULL No mapping was found.
+ @return !=NULL Pointer to NULL-terminated mapping. The buffer
+ is callee allocated and should be freed by the caller.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetMapFromDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ SHELL_MAP_LIST *Node;
+ CHAR16 *PathForReturn;
+ UINTN PathSize;
+// EFI_HANDLE PathHandle;
+// EFI_HANDLE MapHandle;
+// EFI_STATUS Status;
+// EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+// EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
+
+ if (DevicePath == NULL || *DevicePath == NULL) {
+ return (NULL);
+ }
+
+ PathForReturn = NULL;
+ PathSize = 0;
+
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ //
+ // check for exact match
+ //
+ if (DevicePathCompare(DevicePath, &Node->DevicePath) == 0) {
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+ if (PathSize != 0) {
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ }
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ }
+ }
+ if (PathForReturn != NULL) {
+ while (!IsDevicePathEndType (*DevicePath)) {
+ *DevicePath = NextDevicePathNode (*DevicePath);
+ }
+ SetDevicePathEndNode (*DevicePath);
+ }
+/*
+ ///@todo finish code for inexact matches.
+ if (PathForReturn == NULL) {
+ PathSize = 0;
+
+ DevicePathCopy = DuplicateDevicePath(*DevicePath);
+ ASSERT(DevicePathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
+ ASSERT_EFI_ERROR(Status);
+ //
+ // check each of the device paths we have to get the root of the path for consist mappings
+ //
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) == 0) {
+ continue;
+ }
+ MapPathCopy = DuplicateDevicePath(Node->DevicePath);
+ ASSERT(MapPathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+
+ *DevicePath = DevicePathCopy;
+
+ MapPathCopy = NULL;
+ DevicePathCopy = NULL;
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ break;
+ }
+ }
+ //
+ // now add on the non-consistent mappings
+ //
+ for ( Node = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &Node->Link)
+ ; Node = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &Node->Link)
+ ){
+ if ((Node->Flags & SHELL_MAP_FLAGS_CONSIST) != 0) {
+ continue;
+ }
+ MapPathCopy = Node->DevicePath;
+ ASSERT(MapPathCopy != NULL);
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, Node->MapName, 0);
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L";", 0);
+ break;
+ }
+ }
+ }
+*/
+
+ return (AddBufferToFreeList(PathForReturn));
+}
+
+/**
+ Converts a device path to a file system-style path.
+
+ This function converts a device path to a file system path by replacing part, or all, of
+ the device path with the file-system mapping. If there are more than one application
+ file system mappings, the one that most closely matches Path will be used.
+
+ @param Path The pointer to the device path
+
+ @retval NULL the device path could not be found.
+ @return all The pointer of the NULL-terminated file path. The path
+ is callee-allocated and should be freed by the caller.
+**/
+CHAR16 *
+EFIAPI
+EfiShellGetFilePathFromDevicePath(
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *Path
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathCopy;
+ EFI_DEVICE_PATH_PROTOCOL *MapPathCopy;
+ SHELL_MAP_LIST *MapListItem;
+ CHAR16 *PathForReturn;
+ UINTN PathSize;
+ EFI_HANDLE PathHandle;
+ EFI_HANDLE MapHandle;
+ EFI_STATUS Status;
+ FILEPATH_DEVICE_PATH *FilePath;
+
+ PathForReturn = NULL;
+ PathSize = 0;
+
+ DevicePathCopy = (EFI_DEVICE_PATH_PROTOCOL*)Path;
+ ASSERT(DevicePathCopy != NULL);
+ if (DevicePathCopy == NULL) {
+ return (NULL);
+ }
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &DevicePathCopy, &PathHandle);
+
+ if (EFI_ERROR(Status)) {
+ return (NULL);
+ }
+ //
+ // check each of the device paths we have to get the root of the path
+ //
+ for ( MapListItem = (SHELL_MAP_LIST *)GetFirstNode(&gShellMapList.Link)
+ ; !IsNull(&gShellMapList.Link, &MapListItem->Link)
+ ; MapListItem = (SHELL_MAP_LIST *)GetNextNode(&gShellMapList.Link, &MapListItem->Link)
+ ){
+ MapPathCopy = (EFI_DEVICE_PATH_PROTOCOL*)MapListItem->DevicePath;
+ ASSERT(MapPathCopy != NULL);
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid, &MapPathCopy, &MapHandle);
+ if (MapHandle == PathHandle) {
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, MapListItem->MapName, 0);
+ //
+ // go through all the remaining nodes in the device path
+ //
+ for ( FilePath = (FILEPATH_DEVICE_PATH*)DevicePathCopy
+ ; !IsDevicePathEnd (&FilePath->Header)
+ ; FilePath = (FILEPATH_DEVICE_PATH*)NextDevicePathNode (&FilePath->Header)
+ ){
+ //
+ // all the rest should be file path nodes
+ //
+ if ((DevicePathType(&FilePath->Header) != MEDIA_DEVICE_PATH) ||
+ (DevicePathSubType(&FilePath->Header) != MEDIA_FILEPATH_DP)) {
+ FreePool(PathForReturn);
+ PathForReturn = NULL;
+ ASSERT(FALSE);
+ } else {
+ //
+ // append the path part onto the filepath.
+ //
+ ASSERT((PathForReturn == NULL && PathSize == 0) || (PathForReturn != NULL));
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, L"\\", 1);
+ PathForReturn = StrnCatGrow(&PathForReturn, &PathSize, FilePath->PathName, 0);
+ }
+ } // 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 Path the pointer to the path
+
+ @return all The pointer of the file path. The file path is callee
+ allocated and should be freed by the caller.
+**/
+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;
+
+ MapName = NULL;
+ ASSERT(Path != NULL);
+
+ if (StrStr(Path, L":") == NULL) {
+ Cwd = EfiShellGetCurDir(NULL);
+ if (Cwd == NULL) {
+ return (NULL);
+ }
+ Size = StrSize(Cwd);
+ Size += StrSize(Path);
+ NewPath = AllocateZeroPool(Size);
+ ASSERT(NewPath != NULL);
+ StrCpy(NewPath, Cwd);
+ if (NewPath[StrLen(NewPath)-1] == Path[0] == (CHAR16)L'\\') {
+ ((CHAR16*)NewPath)[StrLen(NewPath)-1] = CHAR_NULL;
+ }
+ StrCat(NewPath, Path);
+ DevicePathForReturn = EfiShellGetDevicePathFromFilePath(NewPath);
+ FreePool(NewPath);
+ return (DevicePathForReturn);
+ }
+
+ Size = 0;
+ //
+ // find the part before (but including) the : for the map name
+ //
+ ASSERT((MapName == NULL && Size == 0) || (MapName != NULL));
+ MapName = StrnCatGrow(&MapName, &Size, Path, (StrStr(Path, L":")-Path+1));
+ if (MapName[StrLen(MapName)-1] != L':') {
+ ASSERT(FALSE);
+ 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) {
+ ASSERT(FALSE);
+ 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
+ //
+ DevicePathForReturn = FileDevicePath(Handle, Path+StrLen(MapName)+1);
+
+ 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_TO_TEXT_PROTOCOL *DevicePathToText;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE *HandleList;
+ UINTN HandleCount;
+ UINTN LoopVar;
+ CHAR16 *DeviceNameToReturn;
+ CHAR8 *Lang;
+ CHAR8 *TempChar;
+
+ 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;
+ }
+ if (Language == NULL) {
+ Lang = AllocatePool(AsciiStrSize(CompName2->SupportedLanguages));
+ AsciiStrCpy(Lang, CompName2->SupportedLanguages);
+ TempChar = AsciiStrStr(Lang, ";");
+ if (TempChar != NULL){
+ *TempChar = CHAR_NULL;
+ }
+ } else {
+ Lang = AllocatePool(AsciiStrSize(Language));
+ AsciiStrCpy(Lang, Language);
+ }
+ Status = CompName2->GetControllerName(CompName2, DeviceHandle, NULL, Lang, &DeviceNameToReturn);
+ FreePool(Lang);
+ Lang = NULL;
+ if (!EFI_ERROR(Status) && DeviceNameToReturn != NULL) {
+ break;
+ }
+ }
+ if (HandleList != NULL) {
+ FreePool(HandleList);
+ }
+ if (DeviceNameToReturn != NULL){
+ ASSERT(BestDeviceName == NULL);
+ StrnCatGrow(BestDeviceName, NULL, DeviceNameToReturn, 0);
+ return (EFI_SUCCESS);
+ }
+ //
+ // dont return on fail since we will try device path if that bit is on
+ //
+ }
+ if ((Flags & EFI_DEVICE_NAME_USE_DEVICE_PATH) != 0) {
+ Status = gBS->LocateProtocol(
+ &gEfiDevicePathToTextProtocolGuid,
+ NULL,
+ (VOID**)&DevicePathToText);
+ //
+ // we now have the device path to text protocol
+ //
+ if (!EFI_ERROR(Status)) {
+ 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 = DevicePathToText->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
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenRoot(
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+
+ //
+ // find the handle of the device with that device handle and the file system
+ //
+ ///@todo BlockIo?
+ Status = gBS->LocateDevicePath(&gEfiSimpleFileSystemProtocolGuid,
+ &DevicePath,
+ &Handle);
+ if (EFI_ERROR(Status)) {
+ return (EFI_NOT_FOUND);
+ }
+
+ return (EfiShellOpenRootByHandle(Handle, FileHandle));
+}
+
+/**
+ Returns whether any script files are currently being processed.
+
+ @retval TRUE There is at least one script file active.
+ @retval FALSE No script files are active now.
+
+**/
+BOOLEAN
+EFIAPI
+EfiShellBatchIsActive (
+ VOID
+ )
+{
+ if (ShellCommandGetCurrentScriptFile() == NULL) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/**
+ Worker function to open a file based on a device path. this will open the root
+ of the volume and then traverse down to the file itself.
+
+ @param DevicePath Device Path of the file.
+ @param FileHandle Pointer to the file upon a successful return.
+ @param OpenMode mode to open file in.
+ @param Attributes the File Attributes to use when creating a new file.
+
+ @retval EFI_SUCCESS the file is open and FileHandle is valid
+ @retval EFI_UNSUPPORTED the device path cotained non-path elements
+ @retval other an error ocurred.
+**/
+EFI_STATUS
+EFIAPI
+InternalOpenFileDevicePath(
+ IN OUT EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode,
+ IN UINT64 Attributes OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ FILEPATH_DEVICE_PATH *FilePathNode;
+ EFI_HANDLE Handle;
+ SHELL_FILE_HANDLE ShellHandle;
+ EFI_FILE_PROTOCOL *Handle1;
+ EFI_FILE_PROTOCOL *Handle2;
+ EFI_DEVICE_PATH_PROTOCOL *DpCopy;
+
+ ASSERT(FileHandle != NULL);
+ *FileHandle = NULL;
+ Handle1 = NULL;
+ DpCopy = DevicePath;
+
+ Status = EfiShellOpenRoot(DevicePath, &ShellHandle);
+
+ if (!EFI_ERROR(Status)) {
+ Handle1 = ConvertShellHandleToEfiFileProtocol(ShellHandle);
+ //
+ // 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)
+ ){
+ //
+ // 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,
+ FilePathNode->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,
+ FilePathNode->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,
+ FilePathNode->PathName,
+ OpenMode,
+ Attributes
+ );
+ }
+ }
+ //
+ // Close the last node
+ //
+ Handle2->Close (Handle2);
+
+ //
+ // If there's been an error, stop
+ //
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } // for loop
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ if (Handle1 != NULL) {
+ Handle1->Close(Handle1);
+ }
+ } else {
+ *FileHandle = ConvertEfiFileProtocolToShellHandle(Handle1, ShellFileHandleGetPath(ShellHandle));
+ }
+ return (Status);
+}
+
+/**
+ Creates a file or directory by name.
+
+ This function creates an empty new file or directory with the specified attributes and
+ returns the new file's handle. If the file already exists and is read-only, then
+ EFI_INVALID_PARAMETER will be returned.
+
+ If the file already existed, it is truncated and its attributes updated. If the file is
+ created successfully, the FileHandle is the file's handle, else, the FileHandle is NULL.
+
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ already exists and is non-volatile then EFI_INVALID_PARAMETER is returned.
+
+ @param FileName Pointer to NULL-terminated file path
+ @param FileAttribs The new file's attrbiutes. the different attributes are
+ described in EFI_FILE_PROTOCOL.Open().
+ @param FileHandle On return, points to the created file handle or directory's handle
+
+ @retval EFI_SUCCESS The file was opened. FileHandle points to the new file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.
+ @retval EFI_UNSUPPORTED could not open the file path
+ @retval EFI_NOT_FOUND the specified file could not be found on the devide, or could not
+ file the file system on the device.
+ @retval EFI_NO_MEDIA the device has no medium.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the DirName.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected.
+ @retval EFI_ACCESS_DENIED The service denied access to the file.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
+ @retval EFI_VOLUME_FULL The volume is full.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellCreateFile(
+ IN CONST CHAR16 *FileName,
+ IN UINT64 FileAttribs,
+ OUT SHELL_FILE_HANDLE *FileHandle
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+
+ //
+ // Is this for an environment variable
+ // do we start with >v
+ //
+ if (StrStr(FileName, L">v") == FileName) {
+ if (!IsVolatileEnv(FileName+2)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = CreateFileInterfaceEnv(FileName+2);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // We are opening a regular file.
+ //
+ DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
+ if (DevicePath == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ Status = InternalOpenFileDevicePath(DevicePath, FileHandle, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE|EFI_FILE_MODE_CREATE, FileAttribs); // 0 = no specific file attributes
+ FreePool(DevicePath);
+
+ return(Status);
+}
+
+/**
+ Opens a file or a directory by file name.
+
+ This function opens the specified file in the specified OpenMode and returns a file
+ handle.
+ If the file name begins with >v, then the file handle which is returned refers to the
+ shell environment variable with the specified name. If the shell environment variable
+ exists, is non-volatile and the OpenMode indicates EFI_FILE_MODE_WRITE, then
+ EFI_INVALID_PARAMETER is returned.
+
+ If the file name is >i, then the file handle which is returned refers to the standard
+ input. If the OpenMode indicates EFI_FILE_MODE_WRITE, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >o, then the file handle which is returned refers to the standard
+ output. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is >e, then the file handle which is returned refers to the standard
+ error. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER
+ is returned.
+
+ If the file name is NUL, then the file handle that is returned refers to the standard NUL
+ file. If the OpenMode indicates EFI_FILE_MODE_READ, then EFI_INVALID_PARAMETER is
+ returned.
+
+ If return EFI_SUCCESS, the FileHandle is the opened file's handle, else, the
+ FileHandle is NULL.
+
+ @param FileName Points to the NULL-terminated UCS-2 encoded file name.
+ @param FileHandle On return, points to the file handle.
+ @param OpenMode File open mode. Either EFI_FILE_MODE_READ or
+ EFI_FILE_MODE_WRITE from section 12.4 of the UEFI
+ Specification.
+ @retval EFI_SUCCESS The file was opened. FileHandle has the opened file's handle.
+ @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. FileHandle is NULL.
+ @retval EFI_UNSUPPORTED Could not open the file path. FileHandle is NULL.
+ @retval EFI_NOT_FOUND The specified file could not be found on the device or the file
+ system could not be found on the device. FileHandle is NULL.
+ @retval EFI_NO_MEDIA The device has no medium. FileHandle is NULL.
+ @retval EFI_MEDIA_CHANGED The device has a different medium in it or the medium is no
+ longer supported. FileHandle is NULL.
+ @retval EFI_DEVICE_ERROR The device reported an error or can't get the file path according
+ the FileName. FileHandle is NULL.
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. FileHandle is NULL.
+ @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a file for write
+ when the media is write-protected. FileHandle is NULL.
+ @retval EFI_ACCESS_DENIED The service denied access to the file. FileHandle is NULL.
+ @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. FileHandle
+ is NULL.
+ @retval EFI_VOLUME_FULL The volume is full. FileHandle is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellOpenFileByName(
+ IN CONST CHAR16 *FileName,
+ OUT SHELL_FILE_HANDLE *FileHandle,
+ IN UINT64 OpenMode
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+
+ *FileHandle = NULL;
+
+ //
+ // Is this for StdIn
+ //
+ if (StrCmp(FileName, L">i") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_WRITE) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = ShellInfoObject.NewShellParametersProtocol->StdIn;
+ ASSERT(*FileHandle != NULL);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for StdOut
+ //
+ if (StrCmp(FileName, L">o") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = &FileInterfaceStdOut;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for NUL file
+ //
+ if (StrCmp(FileName, L"NUL") == 0) {
+ *FileHandle = &FileInterfaceNulFile;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for StdErr
+ //
+ if (StrCmp(FileName, L">e") == 0) {
+ //
+ // make sure not writing to StdIn
+ //
+ if ((OpenMode & EFI_FILE_MODE_READ) != 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = &FileInterfaceStdErr;
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // Is this for an environment variable
+ // do we start with >v
+ //
+ if (StrStr(FileName, L">v") == FileName) {
+ if (!IsVolatileEnv(FileName+2) &&
+ ((OpenMode & EFI_FILE_MODE_WRITE) != 0)) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ *FileHandle = CreateFileInterfaceEnv(FileName+2);
+ return (EFI_SUCCESS);
+ }
+
+ //
+ // We are opening a regular file.
+ //
+ DevicePath = EfiShellGetDevicePathFromFilePath(FileName);
+// DEBUG_CODE(InternalShellProtocolDebugPrintMessage (NULL, DevicePath););
+ if (DevicePath == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // Copy the device path, open the file, then free the memory
+ //
+ Status = InternalOpenFileDevicePath(DevicePath, FileHandle, OpenMode, 0); // 0 = no specific file attributes
+ FreePool(DevicePath);
+
+ return(Status);
+}
+
+/**
+ Deletes the file specified by the file name.
+
+ This function deletes a file.
+
+ @param FileName Points to the NULL-terminated file name.
+
+ @retval EFI_SUCCESS The file was closed and deleted, and the handle was closed.
+ @retval EFI_WARN_DELETE_FAILURE The handle was closed but the file was not deleted.
+ @sa EfiShellCreateFile
+**/
+EFI_STATUS
+EFIAPI
+EfiShellDeleteFileByName(
+ IN CONST CHAR16 *FileName
+ )
+{
+ SHELL_FILE_HANDLE FileHandle;
+ EFI_STATUS Status;
+
+ //
+ // get a handle to the file
+ //
+ Status = EfiShellCreateFile(FileName,
+ 0,
+ &FileHandle);
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+ //
+ // now delete the file
+ //
+ return (ShellInfoObject.NewEfiShellProtocol->DeleteFile(FileHandle));
+}
+
+/**
+ Disables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellDisablePageBreak (
+ VOID
+ )
+{
+ ShellInfoObject.PageBreakEnabled = FALSE;
+}
+
+/**
+ Enables the page break output mode.
+**/
+VOID
+EFIAPI
+EfiShellEnablePageBreak (
+ VOID
+ )
+{
+ ShellInfoObject.PageBreakEnabled = TRUE;
+}
+
+/**
+ internal worker function to load and run an image via device path.
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param DevicePath device path of the file to execute
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+ @param StatusCode Points to the status code returned by the command.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+**/
+EFI_STATUS
+EFIAPI
+InternalShellExecuteDevicePath(
+ IN CONST EFI_HANDLE *ParentImageHandle,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN CONST CHAR16 *CommandLine OPTIONAL,
+ IN CONST CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StatusCode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NewHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ LIST_ENTRY OrigEnvs;
+ EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol;
+
+ if (ParentImageHandle == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ InitializeListHead(&OrigEnvs);
+
+ NewHandle = NULL;
+
+ //
+ // Load the image with:
+ // FALSE - not from boot manager and NULL, 0 being not already in memory
+ //
+ Status = gBS->LoadImage(
+ FALSE,
+ *ParentImageHandle,
+ (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
+ NULL,
+ 0,
+ &NewHandle);
+
+ if (EFI_ERROR(Status)) {
+ if (NewHandle != NULL) {
+ gBS->UnloadImage(NewHandle);
+ }
+ return (Status);
+ }
+ Status = gBS->OpenProtocol(
+ NewHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID**)&LoadedImage,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (!EFI_ERROR(Status)) {
+ ASSERT(LoadedImage->LoadOptionsSize == 0);
+ if (CommandLine != NULL) {
+ LoadedImage->LoadOptionsSize = (UINT32)StrSize(CommandLine);
+ LoadedImage->LoadOptions = (VOID*)CommandLine;
+ }
+
+ //
+ // 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, CommandLine, NULL, NULL);
+ ASSERT_EFI_ERROR(Status);
+ Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
+ ASSERT_EFI_ERROR(Status);
+
+ ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols)
+
+ //
+ // now start the image and if the caller wanted the return code pass it to them...
+ //
+ if (!EFI_ERROR(Status)) {
+ if (StatusCode != NULL) {
+ *StatusCode = gBS->StartImage(NewHandle, NULL, NULL);
+ } else {
+ Status = gBS->StartImage(NewHandle, NULL, NULL);
+ }
+ }
+
+ //
+ // Cleanup (and dont overwrite errors)
+ //
+ if (EFI_ERROR(Status)) {
+ gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol);
+ } else {
+ Status = gBS->UninstallProtocolInterface(NewHandle, &gEfiShellParametersProtocolGuid, &ShellParamsProtocol);
+ ASSERT_EFI_ERROR(Status);
+ }
+ }
+
+ if (!IsListEmpty(&OrigEnvs)) {
+ if (EFI_ERROR(Status)) {
+ SetEnvironmentVariableList(&OrigEnvs);
+ } else {
+ Status = SetEnvironmentVariableList(&OrigEnvs);
+ }
+ }
+
+ return(Status);
+}
+/**
+ Execute the command line.
+
+ This function creates a nested instance of the shell and executes the specified
+ command (CommandLine) with the specified environment (Environment). Upon return,
+ the status code returned by the specified command is placed in StatusCode.
+
+ If Environment is NULL, then the current environment is used and all changes made
+ by the commands executed will be reflected in the current environment. If the
+ Environment is non-NULL, then the changes made will be discarded.
+
+ The CommandLine is executed from the current working directory on the current
+ device.
+
+ @param ParentImageHandle A handle of the image that is executing the specified
+ command line.
+ @param CommandLine Points to the NULL-terminated UCS-2 encoded string
+ containing the command line. If NULL then the command-
+ line will be empty.
+ @param Environment Points to a NULL-terminated array of environment
+ variables with the format 'x=y', where x is the
+ environment variable name and y is the value. If this
+ is NULL, then the current shell environment is used.
+ @param StatusCode Points to the status code returned by the command.
+
+ @retval EFI_SUCCESS The command executed successfully. The status code
+ returned by the command is pointed to by StatusCode.
+ @retval EFI_INVALID_PARAMETER The parameters are invalid.
+ @retval EFI_OUT_OF_RESOURCES Out of resources.
+ @retval EFI_UNSUPPORTED Nested shell invocations are not allowed.
+ @retval EFI_UNSUPPORTED The support level required for this function is not present.
+
+ @sa InternalShellExecuteDevicePath
+**/
+EFI_STATUS
+EFIAPI
+EfiShellExecute(
+ IN EFI_HANDLE *ParentImageHandle,
+ IN CHAR16 *CommandLine OPTIONAL,
+ IN CHAR16 **Environment OPTIONAL,
+ OUT EFI_STATUS *StatusCode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Temp;
+ EFI_DEVICE_PATH_PROTOCOL *DevPath;
+ UINTN Size;
+
+ if ((PcdGet8(PcdShellSupportLevel) < 1)) {
+ return (EFI_UNSUPPORTED);
+ }
+
+ DevPath = AppendDevicePath (ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath);
+
+ DEBUG_CODE_BEGIN();
+ Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.FileDevPath, TRUE, TRUE);
+ FreePool(Temp);
+ Temp = gDevPathToText->ConvertDevicePathToText(ShellInfoObject.ImageDevPath, TRUE, TRUE);
+ FreePool(Temp);
+ Temp = gDevPathToText->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 ", 0);
+ StrnCatGrow(&Temp, &Size, CommandLine, 0);
+
+ Status = InternalShellExecuteDevicePath(
+ ParentImageHandle,
+ DevPath,
+ Temp,
+ (CONST CHAR16**)Environment,
+ StatusCode);
+
+ //
+ // de-allocate and return
+ //
+ FreePool(DevPath);
+ FreePool(Temp);
+ return(Status);
+}
+
+/**
+ Utility cleanup function for EFI_SHELL_FILE_INFO objects.
+
+ 1) frees all pointers (non-NULL)
+ 2) Closes the SHELL_FILE_HANDLE
+
+ @param FileListNode pointer to the list node to free
+**/
+VOID
+EFIAPI
+InternalFreeShellFileInfoNode(
+ IN EFI_SHELL_FILE_INFO *FileListNode
+ )
+{
+ if (FileListNode->Info != NULL) {
+ FreePool((VOID*)FileListNode->Info);
+ }
+ if (FileListNode->FileName != NULL) {
+ FreePool((VOID*)FileListNode->FileName);
+ }
+ if (FileListNode->FullName != NULL) {
+ FreePool((VOID*)FileListNode->FullName);
+ }
+ if (FileListNode->Handle != NULL) {
+ ShellInfoObject.NewEfiShellProtocol->CloseFile(FileListNode->Handle);
+ }
+ FreePool(FileListNode);
+}
+/**
+ Frees the file list.
+
+ This function cleans up the file list and any related data structures. It has no
+ impact on the files themselves.
+
+ @param FileList The file list to free. Type EFI_SHELL_FILE_INFO is
+ defined in OpenFileList()
+
+ @retval EFI_SUCCESS Free the file list successfully.
+ @retval EFI_INVALID_PARAMETER FileList was NULL or *FileList was NULL;
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFreeFileList(
+ IN EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_SHELL_FILE_INFO *ShellFileListItem;
+
+ if (FileList == NULL || *FileList == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ for ( ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ; !IsListEmpty(&(*FileList)->Link)
+ ; ShellFileListItem = (EFI_SHELL_FILE_INFO*)GetFirstNode(&(*FileList)->Link)
+ ){
+ RemoveEntryList(&ShellFileListItem->Link);
+ InternalFreeShellFileInfoNode(ShellFileListItem);
+ }
+ 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;
+
+ 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
+ ){
+ RemoveEntryList(&ShellFileListItem2->Link);
+ InternalFreeShellFileInfoNode(ShellFileListItem2);
+ }
+ }
+ }
+ return (EFI_SUCCESS);
+}
+/**
+ Allocates and duplicates a EFI_SHELL_FILE_INFO node.
+
+ @param[in] Node The node to copy from.
+ @param[in] Save TRUE to set Node->Handle to NULL, FALSE otherwise.
+
+ @retval NULL a memory allocation error ocurred
+ @return != NULL a pointer to the new node
+**/
+EFI_SHELL_FILE_INFO*
+EFIAPI
+InternalDuplicateShellFileInfo(
+ IN EFI_SHELL_FILE_INFO *Node,
+ IN BOOLEAN Save
+ )
+{
+ EFI_SHELL_FILE_INFO *NewNode;
+
+ NewNode = AllocatePool(sizeof(EFI_SHELL_FILE_INFO));
+ if (NewNode == NULL) {
+ return (NULL);
+ }
+ NewNode->FullName = AllocateZeroPool(StrSize(Node->FullName));
+
+ NewNode->FileName = AllocateZeroPool(StrSize(Node->FileName));
+ NewNode->Info = AllocatePool((UINTN)Node->Info->Size);
+ if ( NewNode->FullName == NULL
+ || NewNode->FileName == NULL
+ || NewNode->Info == NULL
+ ){
+ return(NULL);
+ }
+ NewNode->Status = Node->Status;
+ NewNode->Handle = Node->Handle;
+ if (!Save) {
+ Node->Handle = NULL;
+ }
+ StrCpy((CHAR16*)NewNode->FullName, Node->FullName);
+ StrCpy((CHAR16*)NewNode->FileName, Node->FileName);
+ CopyMem(NewNode->Info, Node->Info, (UINTN)Node->Info->Size);
+
+ return(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] FullName FullName member initial value.
+ @param[in] FileName FileName member initial value.
+ @param[in] Handle Handle member initial value.
+ @param[in] Info Info struct to copy.
+
+ @retval NULL An error ocurred.
+ @return a pointer to the newly allocated structure.
+**/
+EFI_SHELL_FILE_INFO *
+EFIAPI
+CreateAndPopulateShellFileInfo(
+ IN CONST CHAR16 *BasePath,
+ IN CONST EFI_STATUS Status,
+ IN CONST CHAR16 *FullName,
+ 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) {
+ 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);
+ FreePool(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);
+ }
+ }
+
+ 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;
+
+ 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);
+ TempSpot = StrStr(TempString, L";");
+
+ if (TempSpot != NULL) {
+ *TempSpot = CHAR_NULL;
+ }
+
+ TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
+ 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)
+ ){
+ TempString = NULL;
+ Size = 0;
+ //
+ // allocate a new EFI_SHELL_FILE_INFO and populate it...
+ //
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ TempString = StrnCatGrow(&TempString, &Size, BasePath, 0);
+ TempString = StrnCatGrow(&TempString, &Size, FileInfo->FileName, 0);
+ ShellFileListItem = CreateAndPopulateShellFileInfo(
+ BasePath,
+ EFI_SUCCESS, // success since we didnt fail to open it...
+ TempString,
+ FileInfo->FileName,
+ NULL, // no handle since not open
+ FileInfo);
+
+ if (ShellFileList == NULL) {
+ ShellFileList = (EFI_SHELL_FILE_INFO*)AllocateZeroPool(sizeof(EFI_SHELL_FILE_INFO));
+ ASSERT(ShellFileList != NULL);
+ InitializeListHead(&ShellFileList->Link);
+ }
+ InsertTailList(&ShellFileList->Link, &ShellFileListItem->Link);
+ }
+ if (EFI_ERROR(Status)) {
+ EfiShellFreeFileList(&ShellFileList);
+ *FileList = NULL;
+ } else {
+ *FileList = ShellFileList;
+ }
+ SHELL_FREE_NON_NULL(BasePath);
+ return(Status);
+}
+
+/**
+ Updates a file name to be preceeded by the mapped drive name
+
+ @param[in] BasePath the Mapped drive name to prepend
+ @param[in,out] Path pointer to pointer to the file name to update.
+
+ @retval EFI_SUCCESS
+ @retval EFI_OUT_OF_RESOURCES
+**/
+EFI_STATUS
+EFIAPI
+UpdateFileName(
+ IN CONST CHAR16 *BasePath,
+ IN OUT CHAR16 **Path
+ )
+{
+ CHAR16 *Path2;
+ UINTN Path2Size;
+
+ Path2Size = 0;
+ Path2 = NULL;
+
+ ASSERT(Path != NULL);
+ ASSERT(*Path != NULL);
+ ASSERT(BasePath != NULL);
+
+ //
+ // convert a local path to an absolute path
+ //
+ if (StrStr(*Path, L":") == NULL) {
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, BasePath, 0);
+ if (Path2 == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, (*Path)[0] == L'\\'?(*Path) + 1 :*Path, 0);
+ if (Path2 == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ }
+
+ FreePool(*Path);
+ (*Path) = Path2;
+
+ return (EFI_SUCCESS);
+}
+
+/**
+ If FileHandle is a directory then the function reads from FileHandle and reads in
+ each of the FileInfo structures. If one of them matches the Pattern's first
+ "level" then it opens that handle and calls itself on that handle.
+
+ If FileHandle is a file and matches all of the remaining Pattern (which would be
+ on its last node), then add a EFI_SHELL_FILE_INFO object for this file to fileList.
+
+ if FileList is NULL, then ASSERT
+ if FilePattern is NULL, then ASSERT
+ if UnicodeCollation is NULL, then ASSERT
+ if FileHandle is NULL, then ASSERT
+
+ 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.
+
+ @retval EFI_SUCCESS all files were found and the FileList contains a list.
+ @retval EFI_NOT_FOUND no files were found
+ @retval EFI_OUT_OF_RESOURCES a memory allocation failed
+**/
+EFI_STATUS
+EFIAPI
+ShellSearchHandle(
+ IN CONST CHAR16 *FilePattern,
+ IN EFI_UNICODE_COLLATION_PROTOCOL *UnicodeCollation,
+ IN SHELL_FILE_HANDLE FileHandle,
+ IN OUT EFI_SHELL_FILE_INFO **FileList,
+ IN CONST EFI_SHELL_FILE_INFO *ParentNode OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CONST CHAR16 *NextFilePatternStart;
+ CHAR16 *CurrentFilePattern;
+ EFI_SHELL_FILE_INFO *ShellInfo;
+ EFI_SHELL_FILE_INFO *ShellInfoNode;
+ EFI_SHELL_FILE_INFO *NewShellNode;
+ BOOLEAN Directory;
+
+ if ( FilePattern == NULL
+ || UnicodeCollation == NULL
+ || FileList == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ ShellInfo = NULL;
+ CurrentFilePattern = NULL;
+
+ if (*FilePattern == L'\\') {
+ FilePattern++;
+ }
+
+ for( NextFilePatternStart = FilePattern
+ ; *NextFilePatternStart != CHAR_NULL && *NextFilePatternStart != L'\\'
+ ; NextFilePatternStart++);
+
+ CurrentFilePattern = AllocateZeroPool((NextFilePatternStart-FilePattern+1)*sizeof(CHAR16));
+ ASSERT(CurrentFilePattern != NULL);
+ StrnCpy(CurrentFilePattern, FilePattern, NextFilePatternStart-FilePattern);
+
+ if (CurrentFilePattern[0] == CHAR_NULL
+ &&NextFilePatternStart[0] == CHAR_NULL
+ ){
+ //
+ // Add the current parameter FileHandle to the list, then end...
+ //
+ if (ParentNode == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ NewShellNode = InternalDuplicateShellFileInfo((EFI_SHELL_FILE_INFO*)ParentNode, TRUE);
+ if (NewShellNode == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ } else {
+ NewShellNode->Handle = NULL;
+ if (*FileList == NULL) {
+ *FileList = AllocatePool(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 (Directory){
+ //
+ // should be a directory
+ //
+
+ //
+ // don't open the . and .. directories
+ //
+ if ( (StrCmp(ShellInfoNode->FileName, L".") != 0)
+ && (StrCmp(ShellInfoNode->FileName, L"..") != 0)
+ ){
+ //
+ //
+ //
+ ASSERT_EFI_ERROR(Status);
+ 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);
+ }
+ } else {
+ //
+ // should be a file
+ //
+
+ //
+ // copy the information we need into a new Node
+ //
+ NewShellNode = InternalDuplicateShellFileInfo(ShellInfoNode, FALSE);
+ ASSERT(NewShellNode != NULL);
+ if (NewShellNode == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+ if (*FileList == NULL) {
+ *FileList = AllocatePool(sizeof(EFI_SHELL_FILE_INFO));
+ InitializeListHead(&((*FileList)->Link));
+ }
+
+ //
+ // Add to the returning to use list
+ //
+ InsertTailList(&(*FileList)->Link, &NewShellNode->Link);
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ }
+ if (EFI_ERROR(Status)) {
+ EfiShellFreeFileList(&ShellInfo);
+ } else {
+ Status = EfiShellFreeFileList(&ShellInfo);
+ }
+ }
+ }
+
+ FreePool(CurrentFilePattern);
+ return (Status);
+}
+
+/**
+ Find files that match a specified pattern.
+
+ This function searches for all files and directories that match the specified
+ FilePattern. The FilePattern can contain wild-card characters. The resulting file
+ information is placed in the file list FileList.
+
+ Wildcards are processed
+ according to the rules specified in UEFI Shell 2.0 spec section 3.7.1.
+
+ The files in the file list are not opened. The OpenMode field is set to 0 and the FileInfo
+ field is set to NULL.
+
+ if *FileList is not NULL then it must be a pre-existing and properly initialized list.
+
+ @param FilePattern Points to a NULL-terminated shell file path, including wildcards.
+ @param FileList On return, points to the start of a file list containing the names
+ of all matching files or else points to NULL if no matching files
+ were found. only on a EFI_SUCCESS return will; this be non-NULL.
+
+ @retval EFI_SUCCESS Files found. FileList is a valid list.
+ @retval EFI_NOT_FOUND No files found.
+ @retval EFI_NO_MEDIA The device has no media
+ @retval EFI_DEVICE_ERROR The device reported an error
+ @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted
+**/
+EFI_STATUS
+EFIAPI
+EfiShellFindFiles(
+ IN CONST CHAR16 *FilePattern,
+ OUT EFI_SHELL_FILE_INFO **FileList
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *PatternCopy;
+ CHAR16 *PatternCurrentLocation;
+ EFI_DEVICE_PATH_PROTOCOL *RootDevicePath;
+ SHELL_FILE_HANDLE RootFileHandle;
+ CHAR16 *MapName;
+ UINTN Count;
+
+ if ( FilePattern == NULL
+ || FileList == NULL
+ || StrStr(FilePattern, L":") == NULL
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ Status = EFI_SUCCESS;
+ RootDevicePath = NULL;
+ RootFileHandle = NULL;
+ MapName = NULL;
+ PatternCopy = AllocatePool(StrSize(FilePattern));
+ if (PatternCopy == NULL) {
+ return (EFI_OUT_OF_RESOURCES);
+ }
+ StrCpy(PatternCopy, FilePattern);
+
+ PatternCopy = CleanPath(PatternCopy);
+
+ Count = StrStr(PatternCopy, L":") - PatternCopy;
+ Count += 2;
+
+ ASSERT(MapName == NULL);
+ MapName = StrnCatGrow(&MapName, NULL, PatternCopy, Count);
+
+ if (!EFI_ERROR(Status)) {
+ 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);
+ }
+ FreePool(RootDevicePath);
+ }
+ }
+
+ if (PatternCopy != NULL) {
+ FreePool(PatternCopy);
+ }
+ if (MapName != NULL) {
+ FreePool(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;
+
+ ShellCommandCleanPath(Path);
+
+ Path2Size = 0;
+ Path2 = NULL;
+
+ ASSERT(FileList != NULL);
+ ASSERT(*FileList != NULL);
+
+ if (*Path == L'.' && *(Path+1) == L'\\') {
+ Path++;
+ }
+
+ //
+ // convert a local path to an absolute path
+ //
+ if (StrStr(Path, L":") == NULL) {
+ CurDir = EfiShellGetCurDir(NULL);
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, CurDir, 0);
+ if (*Path == L'\\') {
+ Path++;
+ }
+ ASSERT((Path2 == NULL && Path2Size == 0) || (Path2 != NULL));
+ StrnCatGrow(&Path2, &Path2Size, Path, 0);
+ } else {
+ ASSERT(Path2 == NULL);
+ StrnCatGrow(&Path2, NULL, Path, 0);
+ }
+
+ CleanPath (Path2);
+
+ //
+ // do the search
+ //
+ Status = EfiShellFindFiles(Path2, FileList);
+
+ FreePool(Path2);
+
+ if (EFI_ERROR(Status)) {
+ return (Status);
+ }
+
+ //
+ // 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);
+ }
+ }
+
+ return(EFI_SUCCESS);
+}
+
+/**
+ This function updated with errata.
+
+ 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.
+
+ @return !=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
+ )
+{
+ EFI_STATUS Status;
+ VOID *Buffer;
+ UINTN Size;
+ LIST_ENTRY List;
+ ENV_VAR_LIST *Node;
+ CHAR16 *CurrentWriteLocation;
+
+ Size = 0;
+ Buffer = NULL;
+
+ if (Name == NULL) {
+ //
+ // Get all our environment variables
+ //
+ InitializeListHead(&List);
+ Status = GetEnvironmentVariableList(&List);
+ if (EFI_ERROR(Status)){
+ return (NULL);
+ }
+
+ //
+ // Build the semi-colon delimited list. (2 passes)
+ //
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
+ ; !IsNull(&List, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
+ ){
+ ASSERT(Node->Key != NULL);
+ Size += StrSize(Node->Key);
+ }
+
+ Size += 2*sizeof(CHAR16);
+
+ Buffer = AllocateZeroPool(Size);
+ CurrentWriteLocation = (CHAR16*)Buffer;
+
+ for ( Node = (ENV_VAR_LIST*)GetFirstNode(&List)
+ ; !IsNull(&List, &Node->Link)
+ ; Node = (ENV_VAR_LIST*)GetNextNode(&List, &Node->Link)
+ ){
+ ASSERT(Node->Key != NULL);
+ StrCpy(CurrentWriteLocation, Node->Key);
+ CurrentWriteLocation += StrLen(CurrentWriteLocation) + 1;
+ }
+
+ //
+ // Free the list...
+ //
+ FreeEnvironmentVariableList(&List);
+ } else {
+ //
+ // We are doing a specific environment variable
+ //
+
+ //
+ // get the size we need for this EnvVariable
+ //
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate the space and recall the get function
+ //
+ Buffer = AllocateZeroPool(Size);
+ ASSERT(Buffer != NULL);
+ Status = SHELL_GET_ENVIRONMENT_VARIABLE(Name, &Size, Buffer);
+ }
+ //
+ // we didnt get it (might not exist)
+ // free the memory if we allocated any and return NULL
+ //
+ if (EFI_ERROR(Status)) {
+ if (Buffer != NULL) {
+ FreePool(Buffer);
+ }
+ return (NULL);
+ }
+ }
+
+ //
+ // return the buffer
+ //
+ return (AddBufferToFreeList(Buffer));
+}
+
+/**
+ Internal variable setting function. Allows for setting of the read only variables.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+InternalEfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ )
+{
+ if (Value == NULL || StrLen(Value) == 0) {
+ return (SHELL_DELETE_ENVIRONMENT_VARIABLE(Name));
+ } else {
+ SHELL_DELETE_ENVIRONMENT_VARIABLE(Name);
+ if (Volatile) {
+ return (SHELL_SET_ENVIRONMENT_VARIABLE_V(Name, StrSize(Value), Value));
+ } else {
+ return (SHELL_SET_ENVIRONMENT_VARIABLE_NV(Name, StrSize(Value), Value));
+ }
+ }
+}
+
+/**
+ Sets the environment variable.
+
+ This function changes the current value of the specified environment variable. If the
+ environment variable exists and the Value is an empty string, then the environment
+ variable is deleted. If the environment variable exists and the Value is not an empty
+ string, then the value of the environment variable is changed. If the environment
+ variable does not exist and the Value is an empty string, there is no action. If the
+ environment variable does not exist and the Value is a non-empty string, then the
+ environment variable is created and assigned the specified value.
+
+ For a description of volatile and non-volatile environment variables, see UEFI Shell
+ 2.0 specification section 3.6.1.
+
+ @param Name Points to the NULL-terminated environment variable name.
+ @param Value Points to the NULL-terminated environment variable value. If the value is an
+ empty string then the environment variable is deleted.
+ @param Volatile Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).
+
+ @retval EFI_SUCCESS The environment variable was successfully updated.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetEnv(
+ IN CONST CHAR16 *Name,
+ IN CONST CHAR16 *Value,
+ IN BOOLEAN Volatile
+ )
+{
+ if (Name == NULL || *Name == CHAR_NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ //
+ // Make sure we dont 'set' a predefined read only variable
+ //
+ if (gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"cwd") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"Lasterror") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"profiles") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefishellsupport") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefishellversion") == 0
+ ||gUnicodeCollation->StriColl(
+ gUnicodeCollation,
+ (CHAR16*)Name,
+ L"uefiversion") == 0
+ ){
+ return (EFI_INVALID_PARAMETER);
+ }
+ return (InternalEfiShellSetEnv(Name, Value, Volatile));
+}
+
+/**
+ Returns the current directory on the specified device.
+
+ If FileSystemMapping is NULL, it returns the current working directory. If the
+ FileSystemMapping is not NULL, it returns the current directory associated with the
+ FileSystemMapping. In both cases, the returned name includes the file system
+ mapping (i.e. fs0:\current-dir).
+
+ @param FileSystemMapping A pointer to the file system mapping. If NULL,
+ then the current working directory is returned.
+
+ @retval !=NULL The current directory.
+ @retval NULL Current directory does not exist.
+**/
+CONST CHAR16 *
+EFIAPI
+EfiShellGetCurDir(
+ IN CONST CHAR16 *FileSystemMapping OPTIONAL
+ )
+{
+ CHAR16 *PathToReturn;
+ UINTN Size;
+ SHELL_MAP_LIST *MapListItem;
+ if (!IsListEmpty(&gShellMapList.Link)) {
+ //
+ // if parameter is NULL, use current
+ //
+ if (FileSystemMapping == NULL) {
+ return (EfiShellGetEnv(L"cwd"));
+ } else {
+ Size = 0;
+ PathToReturn = NULL;
+ MapListItem = ShellCommandFindMapItem(FileSystemMapping);
+ if (MapListItem != NULL) {
+ ASSERT((PathToReturn == NULL && Size == 0) || (PathToReturn != NULL));
+ PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->MapName, 0);
+ PathToReturn = StrnCatGrow(&PathToReturn, &Size, MapListItem->CurrentDirectoryPath, 0);
+ }
+ }
+ return (AddBufferToFreeList(PathToReturn));
+ } else {
+ return (NULL);
+ }
+}
+
+/**
+ Changes the current directory on the specified device.
+
+ If the FileSystem is NULL, and the directory Dir does not contain a file system's
+ mapped name, this function changes the current working directory.
+
+ If the FileSystem is NULL and the directory Dir contains a mapped name, then the
+ current file system and the current directory on that file system are changed.
+
+ If FileSystem is NULL, and Dir is not NULL, then this changes the current working file
+ system.
+
+ If FileSystem is not NULL and Dir is not NULL, then this function changes the current
+ directory on the specified file system.
+
+ If the current working directory or the current working file system is changed then the
+ %cwd% environment variable will be updated
+
+ @param FileSystem A pointer to the file system's mapped name. If NULL, then the current working
+ directory is changed.
+ @param Dir Points to the NULL-terminated directory on the device specified by FileSystem.
+
+ @retval EFI_SUCCESS The operation was sucessful
+ @retval EFI_NOT_FOUND The file system could not be found
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetCurDir(
+ IN CONST CHAR16 *FileSystem OPTIONAL,
+ IN CONST CHAR16 *Dir
+ )
+{
+ CHAR16 *MapName;
+ SHELL_MAP_LIST *MapListItem;
+ UINTN Size;
+ EFI_STATUS Status;
+ CHAR16 *TempString;
+ CHAR16 *DirectoryName;
+ UINTN TempLen;
+
+ Size = 0;
+ MapName = NULL;
+ MapListItem = NULL;
+ TempString = NULL;
+ DirectoryName = NULL;
+
+ if (FileSystem == NULL && Dir == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (IsListEmpty(&gShellMapList.Link)){
+ return (EFI_NOT_FOUND);
+ }
+
+ DirectoryName = StrnCatGrow(&DirectoryName, NULL, Dir, 0);
+ ASSERT(DirectoryName != NULL);
+
+ CleanPath(DirectoryName);
+
+ if (FileSystem == NULL) {
+ //
+ // determine the file system mapping to use
+ //
+ if (StrStr(DirectoryName, L":") != NULL) {
+ ASSERT(MapName == NULL);
+ MapName = StrnCatGrow(&MapName, NULL, DirectoryName, (StrStr(DirectoryName, L":")-DirectoryName+1));
+ }
+ //
+ // find the file system mapping's entry in the list
+ // or use current
+ //
+ if (MapName != NULL) {
+ MapListItem = ShellCommandFindMapItem(MapName);
+
+ //
+ // make that the current file system mapping
+ //
+ if (MapListItem != NULL) {
+ gShellCurDir = MapListItem;
+ }
+ } else {
+ MapListItem = gShellCurDir;
+ }
+
+ if (MapListItem == NULL) {
+ return (EFI_NOT_FOUND);
+ }
+
+ //
+ // now update the MapListItem's current directory
+ //
+ if (MapListItem->CurrentDirectoryPath != NULL && DirectoryName[StrLen(DirectoryName) - 1] != L':') {
+ FreePool(MapListItem->CurrentDirectoryPath);
+ MapListItem->CurrentDirectoryPath = NULL;
+ }
+ if (MapName != NULL) {
+ TempLen = StrLen(MapName);
+ if (TempLen != StrLen(DirectoryName)) {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName+StrLen(MapName), 0);
+ }
+ } else {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
+ }
+ if ((MapListItem->CurrentDirectoryPath != NULL && MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') || (MapListItem->CurrentDirectoryPath == NULL)) {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
+ }
+ } else {
+ //
+ // cant have a mapping in the directory...
+ //
+ if (StrStr(DirectoryName, L":") != NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+ //
+ // FileSystem != NULL
+ //
+ MapListItem = ShellCommandFindMapItem(FileSystem);
+ if (MapListItem == NULL) {
+ return (EFI_INVALID_PARAMETER);
+ }
+// gShellCurDir = MapListItem;
+ if (DirectoryName != NULL) {
+ //
+ // change current dir on that file system
+ //
+
+ if (MapListItem->CurrentDirectoryPath != NULL) {
+ FreePool(MapListItem->CurrentDirectoryPath);
+ DEBUG_CODE(MapListItem->CurrentDirectoryPath = NULL;);
+ }
+// ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+// MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, FileSystem, 0);
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, DirectoryName, 0);
+ if (MapListItem->CurrentDirectoryPath[StrLen(MapListItem->CurrentDirectoryPath)-1] != L'\\') {
+ ASSERT((MapListItem->CurrentDirectoryPath == NULL && Size == 0) || (MapListItem->CurrentDirectoryPath != NULL));
+ MapListItem->CurrentDirectoryPath = StrnCatGrow(&MapListItem->CurrentDirectoryPath, &Size, L"\\", 0);
+ }
+ }
+ }
+ //
+ // if updated the current directory then update the environment variable
+ //
+ if (MapListItem == gShellCurDir) {
+ Size = 0;
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ StrnCatGrow(&TempString, &Size, MapListItem->MapName, 0);
+ ASSERT((TempString == NULL && Size == 0) || (TempString != NULL));
+ StrnCatGrow(&TempString, &Size, MapListItem->CurrentDirectoryPath, 0);
+ Status = InternalEfiShellSetEnv(L"cwd", TempString, TRUE);
+ FreePool(TempString);
+ return (Status);
+ }
+ return(EFI_SUCCESS);
+}
+
+/**
+ Return help information about a specific command.
+
+ This function returns the help information for the specified command. The help text
+ can be internal to the shell or can be from a UEFI Shell manual page.
+
+ If Sections is specified, then each section name listed will be compared in a casesensitive
+ manner, to the section names described in Appendix B. If the section exists,
+ it will be appended to the returned help text. If the section does not exist, no
+ information will be returned. If Sections is NULL, then all help text information
+ available will be returned.
+
+ @param Command Points to the NULL-terminated UEFI Shell command name.
+ @param Sections Points to the NULL-terminated comma-delimited
+ section names to return. If NULL, then all
+ sections will be returned.
+ @param HelpText On return, points to a callee-allocated buffer
+ containing all specified help text.
+
+ @retval EFI_SUCCESS The help text was returned.
+ @retval EFI_OUT_OF_RESOURCES The necessary buffer could not be allocated to hold the
+ returned help text.
+ @retval EFI_INVALID_PARAMETER HelpText is NULL
+ @retval EFI_NOT_FOUND There is no help text available for Command.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellGetHelpText(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Sections OPTIONAL,
+ OUT CHAR16 **HelpText
+ )
+{
+ CONST CHAR16 *ManFileName;
+
+ ASSERT(HelpText != NULL);
+
+ ManFileName = ShellCommandGetManFileNameHandler(Command);
+
+ if (ManFileName != NULL) {
+ return (ProcessManFile(ManFileName, Command, Sections, NULL, HelpText));
+ } else {
+ return (ProcessManFile(Command, Command, Sections, NULL, HelpText));
+ }
+}
+
+/**
+ Gets the enable status of the page break output mode.
+
+ User can use this function to determine current page break mode.
+
+ @retval TRUE The page break output mode is enabled.
+ @retval FALSE The page break output mode is disabled.
+**/
+BOOLEAN
+EFIAPI
+EfiShellGetPageBreak(
+ VOID
+ )
+{
+ return(ShellInfoObject.PageBreakEnabled);
+}
+
+/**
+ Judges whether the active shell is the root shell.
+
+ This function makes the user to know that whether the active Shell is the root shell.
+
+ @retval TRUE The active Shell is the root Shell.
+ @retval FALSE The active Shell is NOT the root Shell.
+**/
+BOOLEAN
+EFIAPI
+EfiShellIsRootShell(
+ VOID
+ )
+{
+ return(ShellInfoObject.RootShellInstance);
+}
+
+/**
+ function to return a semi-colon delimeted list of all alias' in the current shell
+
+ up to caller to free the memory.
+
+ @retval NULL No alias' were found
+ @retval NULL An error ocurred getting alias'
+ @return !NULL a list of all alias'
+**/
+CHAR16 *
+EFIAPI
+InternalEfiShellGetListAlias(
+ )
+{
+ UINT64 MaxStorSize;
+ UINT64 RemStorSize;
+ UINT64 MaxVarSize;
+ EFI_STATUS Status;
+ EFI_GUID Guid;
+ CHAR16 *VariableName;
+ UINTN NameSize;
+ CHAR16 *RetVal;
+ UINTN RetSize;
+ CHAR16 *Alias;
+
+ Status = gRT->QueryVariableInfo(EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS, &MaxStorSize, &RemStorSize, &MaxVarSize);
+ ASSERT_EFI_ERROR(Status);
+
+ VariableName = AllocateZeroPool((UINTN)MaxVarSize);
+ RetSize = 0;
+ RetVal = NULL;
+
+ VariableName[0] = CHAR_NULL;
+
+ while (TRUE) {
+ NameSize = (UINTN)MaxVarSize;
+ Status = gRT->GetNextVariableName(&NameSize, VariableName, &Guid);
+ if (Status == EFI_NOT_FOUND){
+ break;
+ }
+ ASSERT_EFI_ERROR(Status);
+ if (EFI_ERROR(Status)) {
+ break;
+ }
+ if (CompareGuid(&Guid, &gShellAliasGuid)){
+ Alias = GetVariable(VariableName, &gShellAliasGuid);
+ ASSERT((RetVal == NULL && RetSize == 0) || (RetVal != NULL));
+ RetVal = StrnCatGrow(&RetVal, &RetSize, VariableName, 0);
+ RetVal = StrnCatGrow(&RetVal, &RetSize, L";", 0);
+ } // compare guid
+ } // while
+ FreePool(VariableName);
+
+ return (RetVal);
+}
+
+/**
+ 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;
+
+ if (Alias != NULL) {
+ if (Volatile == NULL) {
+ return (AddBufferToFreeList(GetVariable((CHAR16*)Alias, &gShellAliasGuid)));
+ }
+ RetSize = 0;
+ RetVal = NULL;
+ Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ RetVal = AllocateZeroPool(RetSize);
+ Status = gRT->GetVariable((CHAR16*)Alias, &gShellAliasGuid, &Attribs, &RetSize, RetVal);
+ }
+ if (EFI_ERROR(Status)) {
+ if (RetVal != NULL) {
+ FreePool(RetVal);
+ }
+ return (NULL);
+ }
+ if ((EFI_VARIABLE_NON_VOLATILE & Attribs) == EFI_VARIABLE_NON_VOLATILE) {
+ *Volatile = FALSE;
+ } else {
+ *Volatile = TRUE;
+ }
+
+ return (AddBufferToFreeList(RetVal));
+ }
+ return (AddBufferToFreeList(InternalEfiShellGetListAlias()));
+}
+
+/**
+ Changes a shell command alias.
+
+ This function creates an alias for a shell command or if Alias is NULL it will delete an existing alias.
+
+ this function does not check for built in alias'.
+
+ @param[in] Command Points to the NULL-terminated shell command or existing alias.
+ @param[in] Alias Points to the NULL-terminated alias for the shell command. If this is NULL, and
+ Command refers to an alias, that alias will be deleted.
+ @param[in] Volatile if TRUE the Alias being set will be stored in a volatile fashion. if FALSE the
+ Alias being set will be stored in a non-volatile fashion.
+
+ @retval EFI_SUCCESS Alias created or deleted successfully.
+ @retval EFI_NOT_FOUND the Alias intended to be deleted was not found
+**/
+EFI_STATUS
+EFIAPI
+InternalSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias,
+ IN BOOLEAN Volatile
+ )
+{
+ //
+ // We must be trying to remove one if Alias is NULL
+ //
+ if (Alias == NULL) {
+ //
+ // remove an alias (but passed in COMMAND parameter)
+ //
+ return (gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL));
+ } else {
+ //
+ // Add and replace are the same
+ //
+
+ // We dont check the error return on purpose since the variable may not exist.
+ gRT->SetVariable((CHAR16*)Command, &gShellAliasGuid, 0, 0, NULL);
+
+ return (gRT->SetVariable((CHAR16*)Alias, &gShellAliasGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS|(Volatile?0:EFI_VARIABLE_NON_VOLATILE), StrSize(Command), (VOID*)Command));
+ }
+}
+
+/**
+ 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.
+**/
+EFI_STATUS
+EFIAPI
+EfiShellSetAlias(
+ IN CONST CHAR16 *Command,
+ IN CONST CHAR16 *Alias,
+ IN BOOLEAN Replace,
+ IN BOOLEAN Volatile
+ )
+{
+ //
+ // cant set over a built in alias
+ //
+ if (ShellCommandIsOnAliasList(Alias==NULL?Command:Alias)) {
+ return (EFI_ACCESS_DENIED);
+ }
+ if (Command == NULL || *Command == CHAR_NULL || StrLen(Command) == 0) {
+ return (EFI_INVALID_PARAMETER);
+ }
+
+ if (EfiShellGetAlias(Command, NULL) != NULL && !Replace) {
+ return (EFI_ACCESS_DENIED);
+ }
+
+ 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
+};
+
+/**
+ Function to create and install on the current handle.
+
+ Will overwrite any existing ShellProtocols in the system to be sure that
+ the current shell is in control.
+
+ This must be removed via calling CleanUpShellProtocol().
+
+ @param[in,out] NewShell The pointer to the pointer to the structure
+ to install.
+
+ @retval EFI_SUCCESS The operation was successful.
+ @return An error from LocateHandle, CreateEvent, or other core function.
+**/
+EFI_STATUS
+EFIAPI
+CreatePopulateInstallShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL **NewShell
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ EFI_HANDLE *Buffer;
+ UINTN HandleCounter;
+ SHELL_PROTOCOL_HANDLE_LIST *OldProtocolNode;
+
+ BufferSize = 0;
+ Buffer = NULL;
+ OldProtocolNode = NULL;
+ InitializeListHead(&ShellInfoObject.OldShellList.Link);
+
+ ASSERT(NewShell != NULL);
+
+ //
+ // Initialize EfiShellProtocol object...
+ //
+ *NewShell = &mShellProtocol;
+ Status = gBS->CreateEvent(0,
+ 0,
+ NULL,
+ NULL,
+ &mShellProtocol.ExecutionBreak);
+ ASSERT_EFI_ERROR(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);
+ ASSERT(Buffer != NULL);
+ Status = gBS->LocateHandle(ByProtocol,
+ &gEfiShellProtocolGuid,
+ NULL,
+ &BufferSize,
+ Buffer);
+ ASSERT_EFI_ERROR(Status);
+ //
+ // now overwrite each of them, but save the info to restore when we end.
+ //
+ for (HandleCounter = 0 ; HandleCounter < (BufferSize/sizeof(EFI_HANDLE)) ; HandleCounter++) {
+ OldProtocolNode = AllocateZeroPool(sizeof(SHELL_PROTOCOL_HANDLE_LIST));
+ ASSERT(OldProtocolNode != NULL);
+ Status = gBS->OpenProtocol(Buffer[HandleCounter],
+ &gEfiShellProtocolGuid,
+ (VOID **) &(OldProtocolNode->Interface),
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (!EFI_ERROR(Status)) {
+ //
+ // reinstall over the old one...
+ //
+ OldProtocolNode->Handle = Buffer[HandleCounter];
+ Status = gBS->ReinstallProtocolInterface(
+ OldProtocolNode->Handle,
+ &gEfiShellProtocolGuid,
+ OldProtocolNode->Interface,
+ (VOID*)(*NewShell));
+ if (!EFI_ERROR(Status)) {
+ //
+ // we reinstalled sucessfully. log this so we can reverse it later.
+ //
+
+ //
+ // add to the list for subsequent...
+ //
+ InsertTailList(&ShellInfoObject.OldShellList.Link, &OldProtocolNode->Link);
+ }
+ }
+ }
+ FreePool(Buffer);
+ } else if (Status == EFI_NOT_FOUND) {
+ ASSERT(IsListEmpty(&ShellInfoObject.OldShellList.Link));
+ //
+ // no one else published yet. just publish it ourselves.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &gImageHandle,
+ &gEfiShellProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ (VOID*)(*NewShell));
+ }
+
+ if (PcdGetBool(PcdShellSupportOldProtocols)){
+ ///@todo support ShellEnvironment2
+ ///@todo do we need to support ShellEnvironment (not ShellEnvironment2) also?
+ }
+
+ return (Status);
+}
+
+/**
+ Opposite of CreatePopulateInstallShellProtocol.
+
+ Free all memory and restore the system to the state it was in before calling
+ CreatePopulateInstallShellProtocol.
+
+ @param[in,out] NewShell The pointer to the new shell protocol structure.
+
+ @retval EFI_SUCCESS The operation was successful.
+**/
+EFI_STATUS
+EFIAPI
+CleanUpShellProtocol (
+ IN OUT EFI_SHELL_PROTOCOL *NewShell
+ )
+{
+ EFI_STATUS Status;
+ SHELL_PROTOCOL_HANDLE_LIST *Node2;
+
+ //
+ // if we need to restore old protocols...
+ //
+ if (!IsListEmpty(&ShellInfoObject.OldShellList.Link)) {
+ for (Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
+ ; !IsListEmpty (&ShellInfoObject.OldShellList.Link)
+ ; Node2 = (SHELL_PROTOCOL_HANDLE_LIST *)GetFirstNode(&ShellInfoObject.OldShellList.Link)
+ ){
+ RemoveEntryList(&Node2->Link);
+ Status = gBS->ReinstallProtocolInterface(Node2->Handle,
+ &gEfiShellProtocolGuid,
+ NewShell,
+ Node2->Interface);
+ ASSERT_EFI_ERROR(Status);
+ FreePool(Node2);
+ }
+ } else {
+ //
+ // no need to restore
+ //
+ Status = gBS->UninstallProtocolInterface(gImageHandle,
+ &gEfiShellProtocolGuid,
+ NewShell);
+ ASSERT_EFI_ERROR(Status);
+ }
+ Status = gBS->CloseEvent(NewShell->ExecutionBreak);
+
+ return (Status);
+}
+
+