Get rid of calls to unsafe string functions. These are deprecated and may be removed in the future. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Leif Lindholm <leif.lindholm@linaro.org>
		
			
				
	
	
		
			1785 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1785 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
File IO routines inspired by Streams with an EFI flavor
 | 
						|
 | 
						|
Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
 | 
						|
Portions copyright (c) 2008 - 2009, Apple Inc. 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.
 | 
						|
 | 
						|
Basic support for opening files on different device types. The device string
 | 
						|
is in the form of DevType:Path. Current DevType is required as there is no
 | 
						|
current mounted device concept of current working directory concept implement
 | 
						|
by this library.
 | 
						|
 | 
						|
Device names are case insensitive and only check the leading characters for
 | 
						|
unique matches. Thus the following are all the same:
 | 
						|
LoadFile0:
 | 
						|
l0:
 | 
						|
L0:
 | 
						|
Lo0:
 | 
						|
 | 
						|
Supported Device Names:
 | 
						|
A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
 | 
						|
l1:          - EFI LoadFile device one.
 | 
						|
B0:          - EFI BlockIo zero.
 | 
						|
fs3:         - EFI Simple File System device 3
 | 
						|
Fv2:         - EFI Firmware VOlume device 2
 | 
						|
10.0.1.102:  - TFTP service IP followed by the file name
 | 
						|
**/
 | 
						|
 | 
						|
#include <PiDxe.h>
 | 
						|
#include <Protocol/BlockIo.h>
 | 
						|
#include <Protocol/DiskIo.h>
 | 
						|
#include <Protocol/SimpleFileSystem.h>
 | 
						|
#include <Protocol/FirmwareVolume2.h>
 | 
						|
#include <Protocol/LoadFile.h>
 | 
						|
#include <Protocol/FirmwareVolumeBlock.h>
 | 
						|
 | 
						|
#include <Guid/FileInfo.h>
 | 
						|
#include <Guid/ZeroGuid.h>
 | 
						|
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/DevicePathLib.h>
 | 
						|
#include <Library/PrintLib.h>
 | 
						|
#include <Library/BaseMemoryLib.h>
 | 
						|
#include <Library/UefiLib.h>
 | 
						|
#include <Library/UefiBootServicesTableLib.h>
 | 
						|
#include <Library/UefiRuntimeServicesTableLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/EfiFileLib.h>
 | 
						|
#include <Library/PcdLib.h>
 | 
						|
#include <Library/EblNetworkLib.h>
 | 
						|
 | 
						|
 | 
						|
CHAR8 *gCwd = NULL;
 | 
						|
 | 
						|
#define EFI_OPEN_FILE_GUARD_HEADER  0x4B4D4641
 | 
						|
#define EFI_OPEN_FILE_GUARD_FOOTER  0x444D5A56
 | 
						|
 | 
						|
// Need to defend against this overflowing
 | 
						|
#define MAX_CMD_LINE  0x200
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  UINT32            Header;
 | 
						|
  EFI_OPEN_FILE     File;
 | 
						|
  UINT32            Footer;
 | 
						|
} EFI_OPEN_FILE_GUARD;
 | 
						|
 | 
						|
 | 
						|
// globals to store current open device info
 | 
						|
EFI_HANDLE            *mBlkIo = NULL;
 | 
						|
UINTN                 mBlkIoCount = 0;
 | 
						|
 | 
						|
EFI_HANDLE            *mFs = NULL;
 | 
						|
UINTN                 mFsCount = 0;
 | 
						|
// mFsInfo[] array entries must match mFs[] handles
 | 
						|
EFI_FILE_SYSTEM_INFO  **mFsInfo = NULL;
 | 
						|
 | 
						|
EFI_HANDLE            *mFv = NULL;
 | 
						|
UINTN                 mFvCount = 0;
 | 
						|
EFI_HANDLE            *mLoadFile = NULL;
 | 
						|
UINTN                 mLoadFileCount = 0;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Internal worker function to validate a File handle.
 | 
						|
 | 
						|
@param  File    Open File Handle
 | 
						|
 | 
						|
@return TRUE    File is valid
 | 
						|
@return FALSE   File is not valid
 | 
						|
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
FileHandleValid (
 | 
						|
  IN EFI_OPEN_FILE  *File
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_OPEN_FILE_GUARD  *GuardFile;
 | 
						|
 | 
						|
  // Look right before and after file structure for the correct signatures
 | 
						|
  GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File);
 | 
						|
  if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) ||
 | 
						|
    (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
 | 
						|
      return FALSE;
 | 
						|
    }
 | 
						|
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
Internal worker function. If Buffer is not NULL free it.
 | 
						|
 | 
						|
@param  Buffer    Buffer to FreePool()
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EblFreePool (
 | 
						|
  IN  VOID  *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (Buffer != NULL) {
 | 
						|
    FreePool (Buffer);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
Update Device List Global Variables
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
EblUpdateDeviceLists (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                        Status;
 | 
						|
  UINTN                             Size;
 | 
						|
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Fs;
 | 
						|
  EFI_FILE_HANDLE                   Root;
 | 
						|
  UINTN                             Index;
 | 
						|
 | 
						|
  if (mBlkIo != NULL) {
 | 
						|
    FreePool (mBlkIo);
 | 
						|
  }
 | 
						|
  gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  if (mFv != NULL) {
 | 
						|
    FreePool (mFv);
 | 
						|
  }
 | 
						|
  gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
 | 
						|
 | 
						|
  if (mLoadFile != NULL) {
 | 
						|
    FreePool (mLoadFile);
 | 
						|
  }
 | 
						|
  gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);
 | 
						|
 | 
						|
  if (mFs != NULL) {
 | 
						|
    FreePool (mFs);
 | 
						|
  }
 | 
						|
 | 
						|
  if (&mFsInfo[0] != NULL) {
 | 
						|
    // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code
 | 
						|
    for (Index = 0; Index < mFsCount; Index++) {
 | 
						|
      if (mFsInfo[Index] != NULL) {
 | 
						|
        FreePool (mFsInfo[Index]);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    FreePool (mFsInfo);
 | 
						|
  }
 | 
						|
 | 
						|
  gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);
 | 
						|
 | 
						|
 | 
						|
  mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));
 | 
						|
  if (mFsInfo == NULL) {
 | 
						|
    // If we can't do this then we can't support file system entries
 | 
						|
    mFsCount = 0;
 | 
						|
  } else {
 | 
						|
    // Loop through all the file system structures and cache the file system info data
 | 
						|
    for (Index =0; Index < mFsCount; Index++) {
 | 
						|
      Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
 | 
						|
      if (!EFI_ERROR (Status)) {
 | 
						|
        Status = Fs->OpenVolume (Fs, &Root);
 | 
						|
        if (!EFI_ERROR (Status)) {
 | 
						|
          // Get information about the volume
 | 
						|
          Size = 0;
 | 
						|
          Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
 | 
						|
          if (Status == EFI_BUFFER_TOO_SMALL) {
 | 
						|
            mFsInfo[Index] = AllocatePool (Size);
 | 
						|
            Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
 | 
						|
          }
 | 
						|
 | 
						|
          Root->Close (Root);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
 | 
						|
Return TRUE if the <devce name> prefix of PathName matches a file system
 | 
						|
Volume Name. MatchIndex is the array  index in mFsInfo[] of the match,
 | 
						|
and it can be used with mFs[] to find the handle that needs to be opened
 | 
						|
 | 
						|
@param  PathName      PathName to check
 | 
						|
@param  FileStart     Index of the first character of the <path>
 | 
						|
@param  MatchIndex    Index in mFsInfo[] that matches
 | 
						|
 | 
						|
@return TRUE      PathName matches a Volume Label and MatchIndex is valid
 | 
						|
@return FALSE     PathName does not match a Volume Label MatchIndex undefined
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
EblMatchVolumeName (
 | 
						|
  IN  CHAR8   *PathName,
 | 
						|
  IN  UINTN   FileStart,
 | 
						|
  OUT UINTN   *MatchIndex
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN   Index;
 | 
						|
  UINTN   Compare;
 | 
						|
  UINTN   VolStrLen;
 | 
						|
  BOOLEAN Match;
 | 
						|
 | 
						|
  for (Index =0; Index < mFsCount; Index++) {
 | 
						|
    if (mFsInfo[Index] == NULL) {
 | 
						|
      // FsInfo is not valid so skip it
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);
 | 
						|
    for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {
 | 
						|
      if (Compare > VolStrLen) {
 | 
						|
        Match = FALSE;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {
 | 
						|
        // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
 | 
						|
        if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {
 | 
						|
          Match = FALSE;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (Match) {
 | 
						|
      *MatchIndex = Index;
 | 
						|
      return TRUE;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Return the number of devices of the current type active in the system
 | 
						|
 | 
						|
@param  Type      Device type to check
 | 
						|
 | 
						|
@return 0         Invalid type
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
EfiGetDeviceCounts (
 | 
						|
  IN  EFI_OPEN_FILE_TYPE     DeviceType
 | 
						|
  )
 | 
						|
{
 | 
						|
  switch (DeviceType) {
 | 
						|
  case EfiOpenLoadFile:
 | 
						|
    return mLoadFileCount;
 | 
						|
  case EfiOpenFirmwareVolume:
 | 
						|
    return mFvCount;
 | 
						|
  case EfiOpenFileSystem:
 | 
						|
    return mFsCount;
 | 
						|
  case EfiOpenBlockIo:
 | 
						|
    return mBlkIoCount;
 | 
						|
  default:
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
ConvertIpStringToEfiIp (
 | 
						|
  IN  CHAR8           *PathName,
 | 
						|
  OUT EFI_IP_ADDRESS  *ServerIp
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8     *Str;
 | 
						|
 | 
						|
  Str = PathName;
 | 
						|
  ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);
 | 
						|
 | 
						|
  Str = AsciiStrStr (Str, ".");
 | 
						|
  if (Str == NULL) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);
 | 
						|
 | 
						|
  Str = AsciiStrStr (Str, ".");
 | 
						|
  if (Str == NULL) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);
 | 
						|
 | 
						|
  Str = AsciiStrStr (Str, ".");
 | 
						|
  if (Str == NULL) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Internal work function to extract a device number from a string skipping
 | 
						|
text. Easy way to extract numbers from strings like blk7:.
 | 
						|
 | 
						|
@param  Str   String to extract device number form
 | 
						|
 | 
						|
@return -1    Device string is not valid
 | 
						|
@return       Device #
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
EblConvertDevStringToNumber (
 | 
						|
  IN  CHAR8   *Str
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN   Max;
 | 
						|
  UINTN   Index;
 | 
						|
 | 
						|
 | 
						|
  // Find the first digit
 | 
						|
  Max = AsciiStrLen (Str);
 | 
						|
  for  (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {
 | 
						|
    Str++;
 | 
						|
  }
 | 
						|
  if (Index == Max) {
 | 
						|
    return (UINTN)-1;
 | 
						|
  }
 | 
						|
 | 
						|
  return AsciiStrDecimalToUintn (Str);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
 | 
						|
 | 
						|
@param  File        Open file handle
 | 
						|
@param  FileName    Name of file after device stripped off
 | 
						|
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EblFileDevicePath (
 | 
						|
  IN OUT EFI_OPEN_FILE  *File,
 | 
						|
  IN  CHAR8             *FileName,
 | 
						|
  IN  CONST UINT64      OpenMode
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                        Status;
 | 
						|
  UINTN                             Size;
 | 
						|
  FILEPATH_DEVICE_PATH              *FilePath;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL          *FileDevicePath;
 | 
						|
  CHAR16                            UnicodeFileName[MAX_PATHNAME];
 | 
						|
  EFI_BLOCK_IO_PROTOCOL             *BlkIo;
 | 
						|
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Fs;
 | 
						|
  EFI_FILE_HANDLE                   Root;
 | 
						|
 | 
						|
 | 
						|
  if ( *FileName != 0 ) {
 | 
						|
    AsciiStrToUnicodeStrS (FileName, UnicodeFileName,
 | 
						|
      ARRAY_SIZE (UnicodeFileName));
 | 
						|
  } else {
 | 
						|
    AsciiStrToUnicodeStrS ("\\", UnicodeFileName, ARRAY_SIZE (UnicodeFileName));
 | 
						|
  }
 | 
						|
 | 
						|
  Size = StrSize (UnicodeFileName);
 | 
						|
  FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
 | 
						|
  if (FileDevicePath != NULL) {
 | 
						|
    FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
 | 
						|
    FilePath->Header.Type    = MEDIA_DEVICE_PATH;
 | 
						|
    FilePath->Header.SubType = MEDIA_FILEPATH_DP;
 | 
						|
    CopyMem (&FilePath->PathName, UnicodeFileName, Size);
 | 
						|
    SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
 | 
						|
    SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
 | 
						|
 | 
						|
    if (File->EfiHandle != NULL) {
 | 
						|
      File->DevicePath = DevicePathFromHandle (File->EfiHandle);
 | 
						|
    }
 | 
						|
 | 
						|
    File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
 | 
						|
    FreePool (FileDevicePath);
 | 
						|
  }
 | 
						|
 | 
						|
  Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    File->FsBlockIoMedia = BlkIo->Media;
 | 
						|
    File->FsBlockIo = BlkIo;
 | 
						|
 | 
						|
    // If we are not opening the device this will get over written with file info
 | 
						|
    File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
 | 
						|
  }
 | 
						|
 | 
						|
  if (File->Type == EfiOpenFileSystem) {
 | 
						|
    Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      Status = Fs->OpenVolume (Fs, &Root);
 | 
						|
      if (!EFI_ERROR (Status)) {
 | 
						|
        // Get information about the volume
 | 
						|
        Size = 0;
 | 
						|
        Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
 | 
						|
        if (Status == EFI_BUFFER_TOO_SMALL) {
 | 
						|
          File->FsInfo = AllocatePool (Size);
 | 
						|
          Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
 | 
						|
        }
 | 
						|
 | 
						|
        // Get information about the file
 | 
						|
        Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
 | 
						|
        if (!EFI_ERROR (Status)) {
 | 
						|
          Size = 0;
 | 
						|
          Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
 | 
						|
          if (Status == EFI_BUFFER_TOO_SMALL) {
 | 
						|
            File->FsFileInfo = AllocatePool (Size);
 | 
						|
            Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
 | 
						|
            if (!EFI_ERROR (Status)) {
 | 
						|
              File->Size = (UINTN)File->FsFileInfo->FileSize;
 | 
						|
              File->MaxPosition = (UINT64)File->Size;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        Root->Close (Root);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else if (File->Type == EfiOpenBlockIo) {
 | 
						|
    File->Size = (UINTN)File->MaxPosition;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
#define ToUpper(a)  ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
CompareGuidToString (
 | 
						|
  IN  EFI_GUID    *Guid,
 | 
						|
  IN  CHAR8       *String
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8       AsciiGuid[64];
 | 
						|
  CHAR8       *StringPtr;
 | 
						|
  CHAR8       *GuidPtr;
 | 
						|
 | 
						|
  AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
 | 
						|
 | 
						|
  StringPtr = String;
 | 
						|
  GuidPtr   = AsciiGuid;
 | 
						|
 | 
						|
  while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
 | 
						|
    // Skip dashes
 | 
						|
    if (*StringPtr == '-') {
 | 
						|
      StringPtr++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (*GuidPtr == '-') {
 | 
						|
      GuidPtr++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
 | 
						|
      return EFI_NOT_FOUND;
 | 
						|
    }
 | 
						|
 | 
						|
    StringPtr++;
 | 
						|
    GuidPtr++;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Internal work function to fill in EFI_OPEN_FILE information for the FV
 | 
						|
 | 
						|
@param  File        Open file handle
 | 
						|
@param  FileName    Name of file after device stripped off
 | 
						|
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EblFvFileDevicePath (
 | 
						|
  IN OUT EFI_OPEN_FILE  *File,
 | 
						|
  IN  CHAR8             *FileName,
 | 
						|
  IN  CONST UINT64      OpenMode
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                          Status;
 | 
						|
  EFI_STATUS                          GetNextFileStatus;
 | 
						|
  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH   DevicePathNode;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL            *DevicePath;
 | 
						|
  UINTN                               Key;
 | 
						|
  UINT32                              AuthenticationStatus;
 | 
						|
  CHAR8                               AsciiSection[MAX_PATHNAME];
 | 
						|
  VOID                                *Section;
 | 
						|
  UINTN                               SectionSize;
 | 
						|
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
 | 
						|
  EFI_LBA                             Lba;
 | 
						|
  UINTN                               BlockSize;
 | 
						|
  UINTN                               NumberOfBlocks;
 | 
						|
  EFI_FIRMWARE_VOLUME_HEADER          *FvHeader = NULL;
 | 
						|
  UINTN                               Index;
 | 
						|
 | 
						|
 | 
						|
  Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  // Get FVB Info about the handle
 | 
						|
  Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
 | 
						|
    if (!EFI_ERROR (Status)) {
 | 
						|
      FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
 | 
						|
      File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
 | 
						|
      for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
 | 
						|
        File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
 | 
						|
      }
 | 
						|
 | 
						|
      for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
 | 
						|
        Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  DevicePath = DevicePathFromHandle (File->EfiHandle);
 | 
						|
 | 
						|
  if (*FileName == '\0') {
 | 
						|
    File->DevicePath = DuplicateDevicePath (DevicePath);
 | 
						|
    File->Size = File->FvSize;
 | 
						|
    File->MaxPosition = File->Size;
 | 
						|
  } else {
 | 
						|
    Key = 0;
 | 
						|
    do {
 | 
						|
      File->FvType = EFI_FV_FILETYPE_ALL;
 | 
						|
      GetNextFileStatus = File->Fv->GetNextFile (
 | 
						|
        File->Fv,
 | 
						|
        &Key,
 | 
						|
        &File->FvType,
 | 
						|
        &File->FvNameGuid,
 | 
						|
        &File->FvAttributes,
 | 
						|
        &File->Size
 | 
						|
        );
 | 
						|
      if (!EFI_ERROR (GetNextFileStatus)) {
 | 
						|
        // Compare GUID first
 | 
						|
        Status = CompareGuidToString (&File->FvNameGuid, FileName);
 | 
						|
        if (!EFI_ERROR(Status)) {
 | 
						|
          break;
 | 
						|
        }
 | 
						|
 | 
						|
        Section = NULL;
 | 
						|
        Status = File->Fv->ReadSection (
 | 
						|
          File->Fv,
 | 
						|
          &File->FvNameGuid,
 | 
						|
          EFI_SECTION_USER_INTERFACE,
 | 
						|
          0,
 | 
						|
          &Section,
 | 
						|
          &SectionSize,
 | 
						|
          &AuthenticationStatus
 | 
						|
          );
 | 
						|
        if (!EFI_ERROR (Status)) {
 | 
						|
          UnicodeStrToAsciiStrS (Section, AsciiSection, MAX_PATHNAME);
 | 
						|
          if (AsciiStriCmp (FileName, AsciiSection) == 0) {
 | 
						|
            FreePool (Section);
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          FreePool (Section);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    } while (!EFI_ERROR (GetNextFileStatus));
 | 
						|
 | 
						|
    if (EFI_ERROR (GetNextFileStatus)) {
 | 
						|
      return GetNextFileStatus;
 | 
						|
    }
 | 
						|
 | 
						|
    if (OpenMode != EFI_SECTION_ALL) {
 | 
						|
      // Calculate the size of the section we are targeting
 | 
						|
      Section = NULL;
 | 
						|
      File->Size = 0;
 | 
						|
      Status = File->Fv->ReadSection (
 | 
						|
        File->Fv,
 | 
						|
        &File->FvNameGuid,
 | 
						|
        (EFI_SECTION_TYPE)OpenMode,
 | 
						|
        0,
 | 
						|
        &Section,
 | 
						|
        &File->Size,
 | 
						|
        &AuthenticationStatus
 | 
						|
        );
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    File->MaxPosition = File->Size;
 | 
						|
    EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
 | 
						|
    File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  // FVB not required if FV was soft loaded...
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Open a device named by PathName. The PathName includes a device name and
 | 
						|
path separated by a :. See file header for more details on the PathName
 | 
						|
syntax. There is no checking to prevent a file from being opened more than
 | 
						|
one type.
 | 
						|
 | 
						|
SectionType is only used to open an FV. Each file in an FV contains multiple
 | 
						|
sections and only the SectionType section is opened.
 | 
						|
 | 
						|
For any file that is opened with EfiOpen() must be closed with EfiClose().
 | 
						|
 | 
						|
@param  PathName    Path to parse to open
 | 
						|
@param  OpenMode    Same as EFI_FILE.Open()
 | 
						|
@param  SectionType Section in FV to open.
 | 
						|
 | 
						|
@return NULL  Open failed
 | 
						|
@return Valid EFI_OPEN_FILE handle
 | 
						|
 | 
						|
**/
 | 
						|
EFI_OPEN_FILE *
 | 
						|
EfiOpen (
 | 
						|
  IN        CHAR8               *PathName,
 | 
						|
  IN  CONST UINT64              OpenMode,
 | 
						|
  IN  CONST EFI_SECTION_TYPE    SectionType
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  EFI_OPEN_FILE             *File;
 | 
						|
  EFI_OPEN_FILE             FileData;
 | 
						|
  UINTN                     StrLen;
 | 
						|
  UINTN                     FileStart;
 | 
						|
  UINTN                     DevNumber = 0;
 | 
						|
  EFI_OPEN_FILE_GUARD       *GuardFile;
 | 
						|
  BOOLEAN                   VolumeNameMatch;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
 | 
						|
  UINTN                     Size;
 | 
						|
  EFI_IP_ADDRESS            Ip;
 | 
						|
  CHAR8                     *CwdPlusPathName;
 | 
						|
  UINTN                     Index;
 | 
						|
  EFI_SECTION_TYPE          ModifiedSectionType;
 | 
						|
  UINTN                     AsciiLength;
 | 
						|
 | 
						|
  EblUpdateDeviceLists ();
 | 
						|
 | 
						|
  File = &FileData;
 | 
						|
  ZeroMem (File, sizeof (EFI_OPEN_FILE));
 | 
						|
 | 
						|
  StrLen = AsciiStrSize (PathName);
 | 
						|
  if (StrLen <= 1) {
 | 
						|
    // Smallest valid path is 1 char and a null
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  for (FileStart = 0; FileStart < StrLen; FileStart++) {
 | 
						|
    if (PathName[FileStart] == ':') {
 | 
						|
      FileStart++;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Matching volume name has precedence over handle based names
 | 
						|
  //
 | 
						|
  VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
 | 
						|
  if (!VolumeNameMatch) {
 | 
						|
    if (FileStart == StrLen) {
 | 
						|
      // No Volume name or device name, so try Current Working Directory
 | 
						|
      if (gCwd == NULL) {
 | 
						|
        // No CWD
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      // We could add a current working directory concept
 | 
						|
      AsciiLength = AsciiStrSize (gCwd) + AsciiStrSize (PathName);
 | 
						|
      CwdPlusPathName = AllocatePool (AsciiLength);
 | 
						|
      if (CwdPlusPathName == NULL) {
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      if ((PathName[0] == '/') || (PathName[0] == '\\')) {
 | 
						|
        // PathName starts in / so this means we go to the root of the device in the CWD.
 | 
						|
        CwdPlusPathName[0] = '\0';
 | 
						|
        for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
 | 
						|
          CwdPlusPathName[FileStart] = gCwd[FileStart];
 | 
						|
          if (gCwd[FileStart] == ':') {
 | 
						|
            FileStart++;
 | 
						|
            CwdPlusPathName[FileStart] = '\0';
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      } else {
 | 
						|
        AsciiStrCpyS (CwdPlusPathName, AsciiLength, gCwd);
 | 
						|
        StrLen = AsciiStrLen (gCwd);
 | 
						|
        if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
 | 
						|
          AsciiStrCatS (CwdPlusPathName, AsciiLength, "\\");
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      AsciiStrCatS (CwdPlusPathName, AsciiLength, PathName);
 | 
						|
      if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
 | 
						|
        // Extra error check to make sure we don't recurse and blow stack
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
 | 
						|
      File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
 | 
						|
      FreePool (CwdPlusPathName);
 | 
						|
      return File;
 | 
						|
    }
 | 
						|
 | 
						|
    DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
 | 
						|
  }
 | 
						|
 | 
						|
  File->DeviceName = AllocatePool (StrLen);
 | 
						|
  AsciiStrCpyS (File->DeviceName, StrLen, PathName);
 | 
						|
  File->DeviceName[FileStart - 1] = '\0';
 | 
						|
  File->FileName = &File->DeviceName[FileStart];
 | 
						|
  if (File->FileName[0] == '\0') {
 | 
						|
    // if it is just a file name use / as root
 | 
						|
    File->FileName = "\\";
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Use best match algorithm on the dev names so we only need to look at the
 | 
						|
  // first few charters to match the full device name. Short name forms are
 | 
						|
  // legal from the caller.
 | 
						|
  //
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
 | 
						|
    if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
 | 
						|
      if (DevNumber >= mFsCount) {
 | 
						|
        goto ErrorExit;
 | 
						|
      }
 | 
						|
      File->Type = EfiOpenFileSystem;
 | 
						|
      File->EfiHandle = mFs[DevNumber];
 | 
						|
      Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
 | 
						|
 | 
						|
    } else if (PathName[1] == 'v' || PathName[1] == 'V') {
 | 
						|
      if (DevNumber >= mFvCount) {
 | 
						|
        goto ErrorExit;
 | 
						|
      }
 | 
						|
      File->Type = EfiOpenFirmwareVolume;
 | 
						|
      File->EfiHandle = mFv[DevNumber];
 | 
						|
 | 
						|
      if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
 | 
						|
        // Skip leading / as its not really needed for the FV since no directories are supported
 | 
						|
        FileStart++;
 | 
						|
      }
 | 
						|
 | 
						|
      // Check for 2nd :
 | 
						|
      ModifiedSectionType = SectionType;
 | 
						|
      for (Index = FileStart; PathName[Index] != '\0'; Index++) {
 | 
						|
        if (PathName[Index] == ':') {
 | 
						|
          // Support fv0:\DxeCore:0x10
 | 
						|
          // This means open the PE32 Section of the file
 | 
						|
          ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
 | 
						|
          PathName[Index] = '\0';
 | 
						|
        }
 | 
						|
      }
 | 
						|
      File->FvSectionType = ModifiedSectionType;
 | 
						|
      Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
 | 
						|
    }
 | 
						|
  } else if ((*PathName == 'A') || (*PathName == 'a')) {
 | 
						|
    // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
 | 
						|
    File->Type = EfiOpenMemoryBuffer;
 | 
						|
    // 1st colon is at PathName[FileStart - 1]
 | 
						|
    File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
 | 
						|
 | 
						|
    // Find 2nd colon
 | 
						|
    while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
 | 
						|
      FileStart++;
 | 
						|
    }
 | 
						|
 | 
						|
    // If we ran out of string, there's no extra data
 | 
						|
    if (PathName[FileStart] == '\0') {
 | 
						|
      File->Size = 0;
 | 
						|
    } else {
 | 
						|
      File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
 | 
						|
    }
 | 
						|
 | 
						|
    // if there's no number after the second colon, default
 | 
						|
    // the end of memory
 | 
						|
    if (File->Size == 0) {
 | 
						|
      File->Size =  (UINTN)(0 - (UINTN)File->Buffer);
 | 
						|
    }
 | 
						|
 | 
						|
    File->MaxPosition = File->Size;
 | 
						|
    File->BaseOffset = (UINTN)File->Buffer;
 | 
						|
 | 
						|
  } else if (*PathName== 'l' || *PathName == 'L') {
 | 
						|
    if (DevNumber >= mLoadFileCount) {
 | 
						|
      goto ErrorExit;
 | 
						|
    }
 | 
						|
    File->Type = EfiOpenLoadFile;
 | 
						|
    File->EfiHandle = mLoadFile[DevNumber];
 | 
						|
 | 
						|
    Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ErrorExit;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto ErrorExit;
 | 
						|
    }
 | 
						|
    File->DevicePath = DuplicateDevicePath (DevicePath);
 | 
						|
 | 
						|
  } else if (*PathName == 'b' || *PathName == 'B') {
 | 
						|
    // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
 | 
						|
    if (DevNumber >= mBlkIoCount) {
 | 
						|
      goto ErrorExit;
 | 
						|
    }
 | 
						|
    File->Type = EfiOpenBlockIo;
 | 
						|
    File->EfiHandle = mBlkIo[DevNumber];
 | 
						|
    EblFileDevicePath (File, "", OpenMode);
 | 
						|
 | 
						|
    // 1st colon is at PathName[FileStart - 1]
 | 
						|
    File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
 | 
						|
 | 
						|
    // Find 2nd colon
 | 
						|
    while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
 | 
						|
      FileStart++;
 | 
						|
    }
 | 
						|
 | 
						|
    // If we ran out of string, there's no extra data
 | 
						|
    if (PathName[FileStart] == '\0') {
 | 
						|
      Size = 0;
 | 
						|
    } else {
 | 
						|
      Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
 | 
						|
    }
 | 
						|
 | 
						|
    // if a zero size is passed in (or the size is left out entirely),
 | 
						|
    // go to the end of the device.
 | 
						|
    if (Size == 0) {
 | 
						|
      File->Size = File->Size - File->DiskOffset;
 | 
						|
    } else {
 | 
						|
      File->Size = Size;
 | 
						|
    }
 | 
						|
 | 
						|
    File->MaxPosition = File->Size;
 | 
						|
    File->BaseOffset = File->DiskOffset;
 | 
						|
  } else if ((*PathName) >= '0' && (*PathName <= '9')) {
 | 
						|
 | 
						|
    // Get current IP address
 | 
						|
    Status = EblGetCurrentIpAddress (&Ip);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Device IP Address is not configured.\n");
 | 
						|
      goto ErrorExit;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
 | 
						|
    File->Type = EfiOpenTftp;
 | 
						|
    File->IsDirty = FALSE;
 | 
						|
    File->IsBufferValid = FALSE;
 | 
						|
 | 
						|
    Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
 | 
						|
  }
 | 
						|
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
 | 
						|
  GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
 | 
						|
  if (GuardFile == NULL) {
 | 
						|
    goto ErrorExit;
 | 
						|
  }
 | 
						|
 | 
						|
  GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
 | 
						|
  CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
 | 
						|
  GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
 | 
						|
 | 
						|
  return &(GuardFile->File);
 | 
						|
 | 
						|
ErrorExit:
 | 
						|
  FreePool (File->DeviceName);
 | 
						|
  return NULL;
 | 
						|
}
 | 
						|
 | 
						|
#define FILE_COPY_CHUNK 0x01000000
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
EfiCopyFile (
 | 
						|
  IN        CHAR8               *DestinationFile,
 | 
						|
  IN        CHAR8               *SourceFile
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_OPEN_FILE *Source      = NULL;
 | 
						|
  EFI_OPEN_FILE *Destination = NULL;
 | 
						|
  EFI_STATUS    Status       = EFI_SUCCESS;
 | 
						|
  VOID          *Buffer      = NULL;
 | 
						|
  UINTN         Size;
 | 
						|
  UINTN         Offset;
 | 
						|
  UINTN         Chunk = FILE_COPY_CHUNK;
 | 
						|
 | 
						|
  Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
 | 
						|
  if (Source == NULL) {
 | 
						|
    AsciiPrint("Source file open error.\n");
 | 
						|
    Status = EFI_NOT_FOUND;
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
 | 
						|
  if (Destination == NULL) {
 | 
						|
    AsciiPrint("Destination file open error.\n");
 | 
						|
    Status = EFI_NOT_FOUND;
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  Buffer = AllocatePool(FILE_COPY_CHUNK);
 | 
						|
  if (Buffer == NULL) {
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto Exit;
 | 
						|
  }
 | 
						|
 | 
						|
  Size = EfiTell(Source, NULL);
 | 
						|
 | 
						|
  for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
 | 
						|
    Chunk = FILE_COPY_CHUNK;
 | 
						|
 | 
						|
    Status = EfiRead(Source, Buffer, &Chunk);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Read file error %r\n", Status);
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = EfiWrite(Destination, Buffer, &Chunk);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Write file error %r\n", Status);
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // Any left over?
 | 
						|
  if (Offset < Size) {
 | 
						|
    Chunk = Size - Offset;
 | 
						|
 | 
						|
    Status = EfiRead(Source, Buffer, &Chunk);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Read file error\n");
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = EfiWrite(Destination, Buffer, &Chunk);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Write file error\n");
 | 
						|
      goto Exit;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
Exit:
 | 
						|
  if (Source != NULL) {
 | 
						|
    Status = EfiClose(Source);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Source close error");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (Destination != NULL) {
 | 
						|
    Status = EfiClose(Destination);
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("Destination close error");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (Buffer != NULL) {
 | 
						|
    FreePool(Buffer);
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
Use DeviceType and Index to form a valid PathName and try and open it.
 | 
						|
 | 
						|
@param  DeviceType  Device type to open
 | 
						|
@param  Index       Device Index to use. Zero relative.
 | 
						|
 | 
						|
@return NULL  Open failed
 | 
						|
@return Valid EFI_OPEN_FILE handle
 | 
						|
 | 
						|
**/
 | 
						|
EFI_OPEN_FILE  *
 | 
						|
EfiDeviceOpenByType (
 | 
						|
  IN  EFI_OPEN_FILE_TYPE    DeviceType,
 | 
						|
  IN  UINTN                 Index
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8   *DevStr;
 | 
						|
  CHAR8   Path[MAX_CMD_LINE];
 | 
						|
 | 
						|
  switch (DeviceType) {
 | 
						|
  case EfiOpenLoadFile:
 | 
						|
    DevStr = "loadfile%d:";
 | 
						|
    break;
 | 
						|
  case EfiOpenFirmwareVolume:
 | 
						|
    DevStr = "fv%d:";
 | 
						|
    break;
 | 
						|
  case EfiOpenFileSystem:
 | 
						|
    DevStr = "fs%d:";
 | 
						|
    break;
 | 
						|
  case EfiOpenBlockIo:
 | 
						|
    DevStr = "blk%d:";
 | 
						|
    break;
 | 
						|
  case EfiOpenMemoryBuffer:
 | 
						|
    DevStr = "a%d:";
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
 | 
						|
 | 
						|
  return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Close a file handle opened by EfiOpen() and free all resources allocated by
 | 
						|
EfiOpen().
 | 
						|
 | 
						|
@param  Stream    Open File Handle
 | 
						|
 | 
						|
@return EFI_INVALID_PARAMETER  Stream is not an Open File
 | 
						|
@return EFI_SUCCESS            Steam closed
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiClose (
 | 
						|
  IN  EFI_OPEN_FILE     *File
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS          Status;
 | 
						|
  UINT64              TftpBufferSize;
 | 
						|
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //Write the buffer contents to TFTP file.
 | 
						|
  if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
 | 
						|
 | 
						|
    TftpBufferSize = File->Size;
 | 
						|
    Status = EblMtftp (
 | 
						|
      EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
 | 
						|
      File->Buffer,
 | 
						|
      TRUE,
 | 
						|
      &TftpBufferSize,
 | 
						|
      NULL,
 | 
						|
      &File->ServerIp,
 | 
						|
      (UINT8 *)File->FileName,
 | 
						|
      NULL,
 | 
						|
      FALSE
 | 
						|
      );
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if ((File->Type == EfiOpenLoadFile) ||
 | 
						|
    ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
 | 
						|
    ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
 | 
						|
    EblFreePool(File->Buffer);
 | 
						|
  }
 | 
						|
 | 
						|
  EblFreePool (File->DevicePath);
 | 
						|
  EblFreePool (File->DeviceName);
 | 
						|
  EblFreePool (File->FsFileInfo);
 | 
						|
  EblFreePool (File->FsInfo);
 | 
						|
 | 
						|
  if (File->FsFileHandle != NULL) {
 | 
						|
    File->FsFileHandle->Close (File->FsFileHandle);
 | 
						|
  }
 | 
						|
 | 
						|
  // Need to free File and it's Guard structures
 | 
						|
  EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Return the size of the file represented by Stream. Also return the current
 | 
						|
Seek position. Opening a file will enable a valid file size to be returned.
 | 
						|
LoadFile is an exception as a load file size is set to zero.
 | 
						|
 | 
						|
@param  Stream    Open File Handle
 | 
						|
 | 
						|
@return 0         Stream is not an Open File or a valid LoadFile handle
 | 
						|
 | 
						|
**/
 | 
						|
UINTN
 | 
						|
EfiTell (
 | 
						|
  IN  EFI_OPEN_FILE     *File,
 | 
						|
  OUT EFI_LBA           *CurrentPosition    OPTIONAL
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS Status;
 | 
						|
  UINT64     BufferSize = 0;
 | 
						|
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  if (CurrentPosition != NULL) {
 | 
						|
    *CurrentPosition = File->CurrentPosition;
 | 
						|
  }
 | 
						|
 | 
						|
  if (File->Type == EfiOpenLoadFile) {
 | 
						|
    // Figure out the File->Size
 | 
						|
    File->Buffer = NULL;
 | 
						|
    File->Size   = 0;
 | 
						|
    Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
 | 
						|
    if (Status != EFI_BUFFER_TOO_SMALL) {
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    File->MaxPosition = (UINT64)File->Size;
 | 
						|
  } else if (File->Type == EfiOpenTftp) {
 | 
						|
 | 
						|
    Status = EblMtftp (
 | 
						|
      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
 | 
						|
      NULL,
 | 
						|
      FALSE,
 | 
						|
      &BufferSize,
 | 
						|
      NULL,
 | 
						|
      &File->ServerIp,
 | 
						|
      (UINT8 *)File->FileName,
 | 
						|
      NULL,
 | 
						|
      TRUE
 | 
						|
      );
 | 
						|
    if (EFI_ERROR(Status)) {
 | 
						|
      AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
 | 
						|
      return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    File->Size        = (UINTN)BufferSize;
 | 
						|
    File->MaxPosition = File->Size;
 | 
						|
  }
 | 
						|
 | 
						|
  return File->Size;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Seek to the Offset location in the file. LoadFile and FV device types do
 | 
						|
not support EfiSeek(). It is not possible to grow the file size using
 | 
						|
EfiSeek().
 | 
						|
 | 
						|
SeekType defines how use Offset to calculate the new file position:
 | 
						|
EfiSeekStart  : Position = Offset
 | 
						|
EfiSeekCurrent: Position is Offset bytes from the current position
 | 
						|
EfiSeekEnd    : Only supported if Offset is zero to seek to end of file.
 | 
						|
 | 
						|
@param  Stream    Open File Handle
 | 
						|
@param  Offset    Offset to seek too.
 | 
						|
@param  SeekType  Type of seek to perform
 | 
						|
 | 
						|
 | 
						|
@return EFI_INVALID_PARAMETER  Stream is not an Open File
 | 
						|
@return EFI_UNSUPPORTED        LoadFile and FV do not support Seek
 | 
						|
@return EFI_NOT_FOUND          Seek past the end of the file.
 | 
						|
@return EFI_SUCCESS            Steam closed
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiSeek (
 | 
						|
  IN  EFI_OPEN_FILE     *File,
 | 
						|
  IN  EFI_LBA           Offset,
 | 
						|
  IN  EFI_SEEK_TYPE     SeekType
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS    Status;
 | 
						|
  UINT64        CurrentPosition;
 | 
						|
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (File->Type == EfiOpenLoadFile) {
 | 
						|
    // LoadFile does not support Seek
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  CurrentPosition = File->CurrentPosition;
 | 
						|
  switch (SeekType) {
 | 
						|
  case EfiSeekStart:
 | 
						|
    if (Offset > File->MaxPosition) {
 | 
						|
      return EFI_NOT_FOUND;
 | 
						|
    }
 | 
						|
    CurrentPosition = Offset;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiSeekCurrent:
 | 
						|
    if ((File->CurrentPosition + Offset) > File->MaxPosition) {
 | 
						|
      return EFI_NOT_FOUND;
 | 
						|
    }
 | 
						|
    CurrentPosition += Offset;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiSeekEnd:
 | 
						|
    if (Offset != 0) {
 | 
						|
      // We don't support growing file size via seeking past end of file
 | 
						|
      return EFI_UNSUPPORTED;
 | 
						|
    }
 | 
						|
    CurrentPosition = File->MaxPosition;
 | 
						|
    break;
 | 
						|
 | 
						|
  default:
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
  if (File->FsFileHandle != NULL) {
 | 
						|
    Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
 | 
						|
  }
 | 
						|
 | 
						|
  if (!EFI_ERROR (Status)) {
 | 
						|
    File->CurrentPosition = CurrentPosition;
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
EFI_STATUS
 | 
						|
CacheTftpFile (
 | 
						|
  IN OUT  EFI_OPEN_FILE *File
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS          Status;
 | 
						|
  UINT64              TftpBufferSize;
 | 
						|
 | 
						|
  if (File->IsBufferValid) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  // Make sure the file size is set.
 | 
						|
  EfiTell (File, NULL);
 | 
						|
 | 
						|
  //Allocate a buffer to hold the whole file.
 | 
						|
  File->Buffer = AllocatePool(File->Size);
 | 
						|
  if (File->Buffer == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  TftpBufferSize = File->Size;
 | 
						|
 | 
						|
  Status = EblMtftp (
 | 
						|
    EFI_PXE_BASE_CODE_TFTP_READ_FILE,
 | 
						|
    File->Buffer,
 | 
						|
    FALSE,
 | 
						|
    &TftpBufferSize,
 | 
						|
    NULL,
 | 
						|
    &File->ServerIp,
 | 
						|
    (UINT8 *)File->FileName,
 | 
						|
    NULL,
 | 
						|
    FALSE);
 | 
						|
  if (EFI_ERROR(Status)) {
 | 
						|
    AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
 | 
						|
    FreePool(File->Buffer);
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  // Set the buffer valid flag.
 | 
						|
  File->IsBufferValid = TRUE;
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
Read BufferSize bytes from the current location in the file. For load file,
 | 
						|
FV, and TFTP case you must read the entire file.
 | 
						|
 | 
						|
@param  Stream      Open File Handle
 | 
						|
@param  Buffer      Caller allocated buffer.
 | 
						|
@param  BufferSize  Size of buffer in bytes.
 | 
						|
 | 
						|
 | 
						|
@return EFI_SUCCESS           Stream is not an Open File
 | 
						|
@return EFI_END_OF_FILE Tried to read past the end of the file
 | 
						|
@return EFI_INVALID_PARAMETER Stream is not an open file handle
 | 
						|
@return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
 | 
						|
@return "other"               Error returned from device read
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiRead (
 | 
						|
  IN  EFI_OPEN_FILE       *File,
 | 
						|
  OUT VOID                *Buffer,
 | 
						|
  OUT UINTN               *BufferSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS            Status;
 | 
						|
  UINT32                AuthenticationStatus;
 | 
						|
  EFI_DISK_IO_PROTOCOL  *DiskIo;
 | 
						|
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  // Don't read past the end of the file.
 | 
						|
  if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
 | 
						|
    return EFI_END_OF_FILE;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (File->Type) {
 | 
						|
  case EfiOpenLoadFile:
 | 
						|
    // Figure out the File->Size
 | 
						|
    EfiTell (File, NULL);
 | 
						|
 | 
						|
    Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenFirmwareVolume:
 | 
						|
    if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
 | 
						|
      // This is the entire FV device, so treat like a memory buffer
 | 
						|
      CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
 | 
						|
      File->CurrentPosition += *BufferSize;
 | 
						|
      Status = EFI_SUCCESS;
 | 
						|
    } else {
 | 
						|
      if (File->Buffer == NULL) {
 | 
						|
        if (File->FvSectionType == EFI_SECTION_ALL) {
 | 
						|
          Status = File->Fv->ReadFile (
 | 
						|
            File->Fv,
 | 
						|
            &File->FvNameGuid,
 | 
						|
            (VOID **)&File->Buffer,
 | 
						|
            &File->Size,
 | 
						|
            &File->FvType,
 | 
						|
            &File->FvAttributes,
 | 
						|
            &AuthenticationStatus
 | 
						|
            );
 | 
						|
        } else {
 | 
						|
          Status = File->Fv->ReadSection (
 | 
						|
            File->Fv,
 | 
						|
            &File->FvNameGuid,
 | 
						|
            File->FvSectionType,
 | 
						|
            0,
 | 
						|
            (VOID **)&File->Buffer,
 | 
						|
            &File->Size,
 | 
						|
            &AuthenticationStatus
 | 
						|
            );
 | 
						|
        }
 | 
						|
        if (EFI_ERROR (Status)) {
 | 
						|
          return Status;
 | 
						|
        }
 | 
						|
        File->IsBufferValid = TRUE;
 | 
						|
      }
 | 
						|
      // Operate on the cached buffer so Seek will work
 | 
						|
      CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
 | 
						|
      File->CurrentPosition += *BufferSize;
 | 
						|
      Status = EFI_SUCCESS;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenMemoryBuffer:
 | 
						|
    CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenFileSystem:
 | 
						|
    Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenBlockIo:
 | 
						|
    Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
 | 
						|
    if (!EFI_ERROR(Status)) {
 | 
						|
      Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
 | 
						|
    }
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenTftp:
 | 
						|
    // Cache the file if it hasn't been cached yet.
 | 
						|
    if (File->IsBufferValid == FALSE) {
 | 
						|
      Status = CacheTftpFile (File);
 | 
						|
      if (EFI_ERROR (Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Copy out the requested data
 | 
						|
    CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
    break;
 | 
						|
 | 
						|
  default:
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  };
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Read the entire file into a buffer. This routine allocates the buffer and
 | 
						|
returns it to the user full of the read data.
 | 
						|
 | 
						|
This is very useful for load file where it's hard to know how big the buffer
 | 
						|
must be.
 | 
						|
 | 
						|
@param  Stream      Open File Handle
 | 
						|
@param  Buffer      Pointer to buffer to return.
 | 
						|
@param  BufferSize  Pointer to Size of buffer return..
 | 
						|
 | 
						|
 | 
						|
@return EFI_SUCCESS           Stream is not an Open File
 | 
						|
@return EFI_END_OF_FILE       Tried to read past the end of the file
 | 
						|
@return EFI_INVALID_PARAMETER Stream is not an open file handle
 | 
						|
@return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
 | 
						|
@return "other"               Error returned from device read
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiReadAllocatePool (
 | 
						|
  IN  EFI_OPEN_FILE     *File,
 | 
						|
  OUT VOID              **Buffer,
 | 
						|
  OUT UINTN             *BufferSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  // Loadfile defers file size determination on Open so use tell to find it
 | 
						|
  EfiTell (File, NULL);
 | 
						|
 | 
						|
  *BufferSize = File->Size;
 | 
						|
  *Buffer = AllocatePool (*BufferSize);
 | 
						|
  if (*Buffer == NULL) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  return EfiRead (File, *Buffer, BufferSize);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Write data back to the file. For TFTP case you must write the entire file.
 | 
						|
 | 
						|
@param  Stream      Open File Handle
 | 
						|
@param  Buffer      Pointer to buffer to return.
 | 
						|
@param  BufferSize  Pointer to Size of buffer return..
 | 
						|
 | 
						|
 | 
						|
@return EFI_SUCCESS           Stream is not an Open File
 | 
						|
@return EFI_END_OF_FILE       Tried to read past the end of the file
 | 
						|
@return EFI_INVALID_PARAMETER Stream is not an open file handle
 | 
						|
@return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
 | 
						|
@return "other"               Error returned from device write
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiWrite (
 | 
						|
  IN  EFI_OPEN_FILE   *File,
 | 
						|
  OUT VOID            *Buffer,
 | 
						|
  OUT UINTN           *BufferSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_STATUS              Status;
 | 
						|
  EFI_FV_WRITE_FILE_DATA  FileData;
 | 
						|
  EFI_DISK_IO_PROTOCOL    *DiskIo;
 | 
						|
 | 
						|
  if (!FileHandleValid (File)) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (File->Type) {
 | 
						|
  case EfiOpenMemoryBuffer:
 | 
						|
    if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
 | 
						|
      return EFI_END_OF_FILE;
 | 
						|
    }
 | 
						|
 | 
						|
    CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  case EfiOpenLoadFile:
 | 
						|
    // LoadFile device is read only be definition
 | 
						|
    Status = EFI_UNSUPPORTED;
 | 
						|
 | 
						|
  case EfiOpenFirmwareVolume:
 | 
						|
    if (File->FvSectionType != EFI_SECTION_ALL) {
 | 
						|
      // Writes not support to a specific section. You have to update entire file
 | 
						|
      return EFI_UNSUPPORTED;
 | 
						|
    }
 | 
						|
 | 
						|
    FileData.NameGuid       = &(File->FvNameGuid);
 | 
						|
    FileData.Type           = File->FvType;
 | 
						|
    FileData.FileAttributes = File->FvAttributes;
 | 
						|
    FileData.Buffer         = Buffer;
 | 
						|
    FileData.BufferSize     = (UINT32)*BufferSize;
 | 
						|
    Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenFileSystem:
 | 
						|
    Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenBlockIo:
 | 
						|
    if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
 | 
						|
      return EFI_END_OF_FILE;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
 | 
						|
    if (!EFI_ERROR(Status)) {
 | 
						|
      Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
 | 
						|
    }
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
    break;
 | 
						|
 | 
						|
  case EfiOpenTftp:
 | 
						|
    // Cache the file if it hasn't been cached yet.
 | 
						|
    if (File->IsBufferValid == FALSE) {
 | 
						|
      Status = CacheTftpFile(File);
 | 
						|
      if (EFI_ERROR(Status)) {
 | 
						|
        return Status;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Don't overwrite the buffer
 | 
						|
    if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
 | 
						|
      UINT8 *TempBuffer;
 | 
						|
 | 
						|
      TempBuffer = File->Buffer;
 | 
						|
 | 
						|
      File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
 | 
						|
      if (File->Buffer == NULL) {
 | 
						|
        return EFI_OUT_OF_RESOURCES;
 | 
						|
      }
 | 
						|
 | 
						|
      CopyMem (File->Buffer, TempBuffer, File->Size);
 | 
						|
 | 
						|
      FreePool (TempBuffer);
 | 
						|
 | 
						|
      File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
 | 
						|
      File->MaxPosition = (UINT64)File->Size;
 | 
						|
    }
 | 
						|
 | 
						|
    // Copy in the requested data
 | 
						|
    CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
 | 
						|
    File->CurrentPosition += *BufferSize;
 | 
						|
 | 
						|
    // Mark the file dirty
 | 
						|
    File->IsDirty = TRUE;
 | 
						|
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
    break;
 | 
						|
 | 
						|
  default:
 | 
						|
    Status = EFI_INVALID_PARAMETER;
 | 
						|
  };
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Given Cwd expand Path to remove .. and replace them with real
 | 
						|
directory names.
 | 
						|
 | 
						|
@param  Cwd     Current Working Directory
 | 
						|
@param  Path    Path to expand
 | 
						|
 | 
						|
@return NULL     Cwd or Path are not valid
 | 
						|
@return 'other'  Path with .. expanded
 | 
						|
 | 
						|
**/
 | 
						|
CHAR8 *
 | 
						|
ExpandPath (
 | 
						|
  IN CHAR8    *Cwd,
 | 
						|
  IN CHAR8    *Path
 | 
						|
  )
 | 
						|
{
 | 
						|
  CHAR8   *NewPath;
 | 
						|
  CHAR8   *Work, *Start, *End;
 | 
						|
  UINTN   StrLen, AllocLen;
 | 
						|
  INTN    i;
 | 
						|
 | 
						|
  if (Cwd == NULL || Path == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  StrLen = AsciiStrSize (Cwd);
 | 
						|
  if (StrLen <= 2) {
 | 
						|
    // Smallest valid path is 1 char and a null
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  StrLen = AsciiStrSize (Path);
 | 
						|
  AllocLen = AsciiStrSize (Cwd) + StrLen + 1;
 | 
						|
  NewPath = AllocatePool (AllocLen);
 | 
						|
  if (NewPath == NULL) {
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
  AsciiStrCpyS (NewPath, AllocLen, Cwd);
 | 
						|
 | 
						|
  End = Path + StrLen;
 | 
						|
  for (Start = Path ;;) {
 | 
						|
    Work = AsciiStrStr (Start, "..") ;
 | 
						|
    if (Work == NULL) {
 | 
						|
      // Remaining part of Path contains no more ..
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    // append path prior to ..
 | 
						|
    AsciiStrnCatS (NewPath, AllocLen, Start, Work - Start);
 | 
						|
    StrLen = AsciiStrLen (NewPath);
 | 
						|
    for (i = StrLen; i >= 0; i--) {
 | 
						|
      if (NewPath[i] == ':') {
 | 
						|
        // too many ..
 | 
						|
        return NULL;
 | 
						|
      }
 | 
						|
      if (NewPath[i] == '/' || NewPath[i] == '\\') {
 | 
						|
        if ((i > 0) && (NewPath[i-1] == ':')) {
 | 
						|
          // leave the / before a :
 | 
						|
          NewPath[i+1] = '\0';
 | 
						|
        } else {
 | 
						|
          // replace / will Null to remove trailing file/dir reference
 | 
						|
          NewPath[i] = '\0';
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    Start = Work + 3;
 | 
						|
  }
 | 
						|
 | 
						|
  // Handle the path that remains after the ..
 | 
						|
  AsciiStrnCatS (NewPath, AllocLen, Start, End - Start);
 | 
						|
 | 
						|
  return NewPath;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
 | 
						|
the path does not contain a device name, The CWD is prepended to the path.
 | 
						|
 | 
						|
@param  Cwd     Current Working Directory to set
 | 
						|
 | 
						|
 | 
						|
@return EFI_SUCCESS           CWD is set
 | 
						|
@return EFI_INVALID_PARAMETER Cwd is not a valid device:path
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EfiSetCwd (
 | 
						|
  IN  CHAR8   *Cwd
 | 
						|
  )
 | 
						|
{
 | 
						|
  EFI_OPEN_FILE *File;
 | 
						|
  UINTN         Len, AllocLen;
 | 
						|
  CHAR8         *Path;
 | 
						|
 | 
						|
  if (Cwd == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (AsciiStrCmp (Cwd, ".") == 0) {
 | 
						|
    // cd . is a no-op
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  Path = Cwd;
 | 
						|
  if (AsciiStrStr (Cwd, "..") != NULL) {
 | 
						|
    if (gCwd == NULL) {
 | 
						|
      // no parent
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
 | 
						|
    Len = AsciiStrLen (gCwd);
 | 
						|
    if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
 | 
						|
      // parent is device so nothing to do
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
 | 
						|
    // Expand .. in Cwd, given we know current working directory
 | 
						|
    Path = ExpandPath (gCwd, Cwd);
 | 
						|
    if (Path == NULL) {
 | 
						|
      return EFI_NOT_FOUND;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
 | 
						|
  if (File == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  if (gCwd != NULL) {
 | 
						|
    FreePool (gCwd);
 | 
						|
  }
 | 
						|
 | 
						|
  // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
 | 
						|
  // relative to the current gCwd or not.
 | 
						|
  AllocLen = AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10;
 | 
						|
  gCwd = AllocatePool (AllocLen);
 | 
						|
  if (gCwd == NULL) {
 | 
						|
    return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  AsciiStrCpyS (gCwd, AllocLen, File->DeviceName);
 | 
						|
  if (File->FileName == NULL) {
 | 
						|
    AsciiStrCatS (gCwd, AllocLen, ":\\");
 | 
						|
  } else {
 | 
						|
    AsciiStrCatS (gCwd, AllocLen, ":");
 | 
						|
    AsciiStrCatS (gCwd, AllocLen, File->FileName);
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  EfiClose (File);
 | 
						|
  if (Path != Cwd) {
 | 
						|
    FreePool (Path);
 | 
						|
  }
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
 | 
						|
the path does not contain a device name, The CWD is prepended to the path.
 | 
						|
The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
 | 
						|
a call to EfiSetCwd() it is not legal to use the pointer returned by
 | 
						|
this function.
 | 
						|
 | 
						|
@param  Cwd     Current Working Directory
 | 
						|
 | 
						|
 | 
						|
@return ""      No CWD set
 | 
						|
@return 'other' Returns buffer that contains CWD.
 | 
						|
 | 
						|
**/
 | 
						|
CHAR8 *
 | 
						|
EfiGetCwd (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  if (gCwd == NULL) {
 | 
						|
    return "";
 | 
						|
  }
 | 
						|
  return gCwd;
 | 
						|
}
 | 
						|
 | 
						|
 |