mirror of https://github.com/acidanthera/audk.git
1780 lines
48 KiB
C
1780 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 ) {
|
|
AsciiStrToUnicodeStr (FileName, UnicodeFileName);
|
|
} else {
|
|
AsciiStrToUnicodeStr ("\\", 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)) {
|
|
UnicodeStrToAsciiStr (Section, AsciiSection);
|
|
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;
|
|
|
|
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
|
|
CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
|
|
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 {
|
|
AsciiStrCpy (CwdPlusPathName, gCwd);
|
|
StrLen = AsciiStrLen (gCwd);
|
|
if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
|
|
AsciiStrCat (CwdPlusPathName, "\\");
|
|
}
|
|
}
|
|
|
|
AsciiStrCat (CwdPlusPathName, 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);
|
|
AsciiStrCpy (File->DeviceName, 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;
|
|
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);
|
|
NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
|
|
if (NewPath == NULL) {
|
|
return NULL;
|
|
}
|
|
AsciiStrCpy (NewPath, 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 ..
|
|
AsciiStrnCat (NewPath, 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 ..
|
|
AsciiStrnCat (NewPath, 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;
|
|
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.
|
|
gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
|
|
if (gCwd == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
AsciiStrCpy (gCwd, File->DeviceName);
|
|
if (File->FileName == NULL) {
|
|
AsciiStrCat (gCwd, ":\\");
|
|
} else {
|
|
AsciiStrCat (gCwd, ":");
|
|
AsciiStrCat (gCwd, 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;
|
|
}
|
|
|
|
|