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