Home | History | Annotate | Download | only in EfiFileLib
      1 /** @file
      2 File IO routines inspired by Streams with an EFI flavor
      3 
      4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
      5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
      6 
      7 This program and the accompanying materials
      8 are licensed and made available under the terms and conditions of the BSD License
      9 which accompanies this distribution.  The full text of the license may be found at
     10 http://opensource.org/licenses/bsd-license.php
     11 
     12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     14 
     15 Basic support for opening files on different device types. The device string
     16 is in the form of DevType:Path. Current DevType is required as there is no
     17 current mounted device concept of current working directory concept implement
     18 by this library.
     19 
     20 Device names are case insensitive and only check the leading characters for
     21 unique matches. Thus the following are all the same:
     22 LoadFile0:
     23 l0:
     24 L0:
     25 Lo0:
     26 
     27 Supported Device Names:
     28 A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
     29 l1:          - EFI LoadFile device one.
     30 B0:          - EFI BlockIo zero.
     31 fs3:         - EFI Simple File System device 3
     32 Fv2:         - EFI Firmware VOlume device 2
     33 10.0.1.102:  - TFTP service IP followed by the file name
     34 **/
     35 
     36 #include <PiDxe.h>
     37 #include <Protocol/BlockIo.h>
     38 #include <Protocol/DiskIo.h>
     39 #include <Protocol/SimpleFileSystem.h>
     40 #include <Protocol/FirmwareVolume2.h>
     41 #include <Protocol/LoadFile.h>
     42 #include <Protocol/FirmwareVolumeBlock.h>
     43 
     44 #include <Guid/FileInfo.h>
     45 #include <Guid/ZeroGuid.h>
     46 
     47 #include <Library/BaseLib.h>
     48 #include <Library/MemoryAllocationLib.h>
     49 #include <Library/DevicePathLib.h>
     50 #include <Library/PrintLib.h>
     51 #include <Library/BaseMemoryLib.h>
     52 #include <Library/UefiLib.h>
     53 #include <Library/UefiBootServicesTableLib.h>
     54 #include <Library/UefiRuntimeServicesTableLib.h>
     55 #include <Library/DebugLib.h>
     56 #include <Library/EfiFileLib.h>
     57 #include <Library/PcdLib.h>
     58 #include <Library/EblNetworkLib.h>
     59 
     60 
     61 CHAR8 *gCwd = NULL;
     62 
     63 #define EFI_OPEN_FILE_GUARD_HEADER  0x4B4D4641
     64 #define EFI_OPEN_FILE_GUARD_FOOTER  0x444D5A56
     65 
     66 // Need to defend against this overflowing
     67 #define MAX_CMD_LINE  0x200
     68 
     69 typedef struct {
     70   UINT32            Header;
     71   EFI_OPEN_FILE     File;
     72   UINT32            Footer;
     73 } EFI_OPEN_FILE_GUARD;
     74 
     75 
     76 // globals to store current open device info
     77 EFI_HANDLE            *mBlkIo = NULL;
     78 UINTN                 mBlkIoCount = 0;
     79 
     80 EFI_HANDLE            *mFs = NULL;
     81 UINTN                 mFsCount = 0;
     82 // mFsInfo[] array entries must match mFs[] handles
     83 EFI_FILE_SYSTEM_INFO  **mFsInfo = NULL;
     84 
     85 EFI_HANDLE            *mFv = NULL;
     86 UINTN                 mFvCount = 0;
     87 EFI_HANDLE            *mLoadFile = NULL;
     88 UINTN                 mLoadFileCount = 0;
     89 
     90 
     91 
     92 /**
     93 Internal worker function to validate a File handle.
     94 
     95 @param  File    Open File Handle
     96 
     97 @return TRUE    File is valid
     98 @return FALSE   File is not valid
     99 
    100 
    101 **/
    102 BOOLEAN
    103 FileHandleValid (
    104   IN EFI_OPEN_FILE  *File
    105   )
    106 {
    107   EFI_OPEN_FILE_GUARD  *GuardFile;
    108 
    109   // Look right before and after file structure for the correct signatures
    110   GuardFile = BASE_CR (File, EFI_OPEN_FILE_GUARD, File);
    111   if ((GuardFile->Header != EFI_OPEN_FILE_GUARD_HEADER) ||
    112     (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
    113       return FALSE;
    114     }
    115 
    116     return TRUE;
    117 }
    118 
    119 /**
    120 Internal worker function. If Buffer is not NULL free it.
    121 
    122 @param  Buffer    Buffer to FreePool()
    123 
    124 **/
    125 VOID
    126 EblFreePool (
    127   IN  VOID  *Buffer
    128   )
    129 {
    130   if (Buffer != NULL) {
    131     FreePool (Buffer);
    132   }
    133 }
    134 
    135 /**
    136 Update Device List Global Variables
    137 
    138 **/
    139 VOID
    140 EblUpdateDeviceLists (
    141   VOID
    142   )
    143 {
    144   EFI_STATUS                        Status;
    145   UINTN                             Size;
    146   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Fs;
    147   EFI_FILE_HANDLE                   Root;
    148   UINTN                             Index;
    149 
    150   if (mBlkIo != NULL) {
    151     FreePool (mBlkIo);
    152   }
    153   gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
    154 
    155 
    156 
    157   if (mFv != NULL) {
    158     FreePool (mFv);
    159   }
    160   gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
    161 
    162   if (mLoadFile != NULL) {
    163     FreePool (mLoadFile);
    164   }
    165   gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);
    166 
    167   if (mFs != NULL) {
    168     FreePool (mFs);
    169   }
    170 
    171   if (&mFsInfo[0] != NULL) {
    172     // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code
    173     for (Index = 0; Index < mFsCount; Index++) {
    174       if (mFsInfo[Index] != NULL) {
    175         FreePool (mFsInfo[Index]);
    176       }
    177     }
    178     FreePool (mFsInfo);
    179   }
    180 
    181   gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);
    182 
    183 
    184   mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));
    185   if (mFsInfo == NULL) {
    186     // If we can't do this then we can't support file system entries
    187     mFsCount = 0;
    188   } else {
    189     // Loop through all the file system structures and cache the file system info data
    190     for (Index =0; Index < mFsCount; Index++) {
    191       Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
    192       if (!EFI_ERROR (Status)) {
    193         Status = Fs->OpenVolume (Fs, &Root);
    194         if (!EFI_ERROR (Status)) {
    195           // Get information about the volume
    196           Size = 0;
    197           Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
    198           if (Status == EFI_BUFFER_TOO_SMALL) {
    199             mFsInfo[Index] = AllocatePool (Size);
    200             Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
    201           }
    202 
    203           Root->Close (Root);
    204         }
    205       }
    206     }
    207   }
    208 }
    209 
    210 
    211 /**
    212 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
    213 Return TRUE if the <devce name> prefix of PathName matches a file system
    214 Volume Name. MatchIndex is the array  index in mFsInfo[] of the match,
    215 and it can be used with mFs[] to find the handle that needs to be opened
    216 
    217 @param  PathName      PathName to check
    218 @param  FileStart     Index of the first character of the <path>
    219 @param  MatchIndex    Index in mFsInfo[] that matches
    220 
    221 @return TRUE      PathName matches a Volume Label and MatchIndex is valid
    222 @return FALSE     PathName does not match a Volume Label MatchIndex undefined
    223 
    224 **/
    225 BOOLEAN
    226 EblMatchVolumeName (
    227   IN  CHAR8   *PathName,
    228   IN  UINTN   FileStart,
    229   OUT UINTN   *MatchIndex
    230   )
    231 {
    232   UINTN   Index;
    233   UINTN   Compare;
    234   UINTN   VolStrLen;
    235   BOOLEAN Match;
    236 
    237   for (Index =0; Index < mFsCount; Index++) {
    238     if (mFsInfo[Index] == NULL) {
    239       // FsInfo is not valid so skip it
    240       continue;
    241     }
    242     VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);
    243     for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {
    244       if (Compare > VolStrLen) {
    245         Match = FALSE;
    246         break;
    247       }
    248       if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {
    249         // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
    250         if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {
    251           Match = FALSE;
    252           break;
    253         }
    254       }
    255     }
    256     if (Match) {
    257       *MatchIndex = Index;
    258       return TRUE;
    259     }
    260   }
    261 
    262   return FALSE;
    263 }
    264 
    265 
    266 /**
    267 Return the number of devices of the current type active in the system
    268 
    269 @param  Type      Device type to check
    270 
    271 @return 0         Invalid type
    272 
    273 **/
    274 UINTN
    275 EfiGetDeviceCounts (
    276   IN  EFI_OPEN_FILE_TYPE     DeviceType
    277   )
    278 {
    279   switch (DeviceType) {
    280   case EfiOpenLoadFile:
    281     return mLoadFileCount;
    282   case EfiOpenFirmwareVolume:
    283     return mFvCount;
    284   case EfiOpenFileSystem:
    285     return mFsCount;
    286   case EfiOpenBlockIo:
    287     return mBlkIoCount;
    288   default:
    289     return 0;
    290   }
    291 }
    292 
    293 EFI_STATUS
    294 ConvertIpStringToEfiIp (
    295   IN  CHAR8           *PathName,
    296   OUT EFI_IP_ADDRESS  *ServerIp
    297   )
    298 {
    299   CHAR8     *Str;
    300 
    301   Str = PathName;
    302   ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);
    303 
    304   Str = AsciiStrStr (Str, ".");
    305   if (Str == NULL) {
    306     return EFI_DEVICE_ERROR;
    307   }
    308 
    309   ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);
    310 
    311   Str = AsciiStrStr (Str, ".");
    312   if (Str == NULL) {
    313     return EFI_DEVICE_ERROR;
    314   }
    315 
    316   ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);
    317 
    318   Str = AsciiStrStr (Str, ".");
    319   if (Str == NULL) {
    320     return EFI_DEVICE_ERROR;
    321   }
    322 
    323   ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);
    324 
    325   return EFI_SUCCESS;
    326 }
    327 
    328 
    329 /**
    330 Internal work function to extract a device number from a string skipping
    331 text. Easy way to extract numbers from strings like blk7:.
    332 
    333 @param  Str   String to extract device number form
    334 
    335 @return -1    Device string is not valid
    336 @return       Device #
    337 
    338 **/
    339 UINTN
    340 EblConvertDevStringToNumber (
    341   IN  CHAR8   *Str
    342   )
    343 {
    344   UINTN   Max;
    345   UINTN   Index;
    346 
    347 
    348   // Find the first digit
    349   Max = AsciiStrLen (Str);
    350   for  (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {
    351     Str++;
    352   }
    353   if (Index == Max) {
    354     return (UINTN)-1;
    355   }
    356 
    357   return AsciiStrDecimalToUintn (Str);
    358 }
    359 
    360 
    361 /**
    362 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
    363 
    364 @param  File        Open file handle
    365 @param  FileName    Name of file after device stripped off
    366 
    367 
    368 **/
    369 EFI_STATUS
    370 EblFileDevicePath (
    371   IN OUT EFI_OPEN_FILE  *File,
    372   IN  CHAR8             *FileName,
    373   IN  CONST UINT64      OpenMode
    374   )
    375 {
    376   EFI_STATUS                        Status;
    377   UINTN                             Size;
    378   FILEPATH_DEVICE_PATH              *FilePath;
    379   EFI_DEVICE_PATH_PROTOCOL          *FileDevicePath;
    380   CHAR16                            UnicodeFileName[MAX_PATHNAME];
    381   EFI_BLOCK_IO_PROTOCOL             *BlkIo;
    382   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Fs;
    383   EFI_FILE_HANDLE                   Root;
    384 
    385 
    386   if ( *FileName != 0 ) {
    387     AsciiStrToUnicodeStrS (FileName, UnicodeFileName,
    388       ARRAY_SIZE (UnicodeFileName));
    389   } else {
    390     AsciiStrToUnicodeStrS ("\\", UnicodeFileName, ARRAY_SIZE (UnicodeFileName));
    391   }
    392 
    393   Size = StrSize (UnicodeFileName);
    394   FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
    395   if (FileDevicePath != NULL) {
    396     FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
    397     FilePath->Header.Type    = MEDIA_DEVICE_PATH;
    398     FilePath->Header.SubType = MEDIA_FILEPATH_DP;
    399     CopyMem (&FilePath->PathName, UnicodeFileName, Size);
    400     SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
    401     SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
    402 
    403     if (File->EfiHandle != NULL) {
    404       File->DevicePath = DevicePathFromHandle (File->EfiHandle);
    405     }
    406 
    407     File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
    408     FreePool (FileDevicePath);
    409   }
    410 
    411   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
    412   if (!EFI_ERROR (Status)) {
    413     File->FsBlockIoMedia = BlkIo->Media;
    414     File->FsBlockIo = BlkIo;
    415 
    416     // If we are not opening the device this will get over written with file info
    417     File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
    418   }
    419 
    420   if (File->Type == EfiOpenFileSystem) {
    421     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
    422     if (!EFI_ERROR (Status)) {
    423       Status = Fs->OpenVolume (Fs, &Root);
    424       if (!EFI_ERROR (Status)) {
    425         // Get information about the volume
    426         Size = 0;
    427         Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
    428         if (Status == EFI_BUFFER_TOO_SMALL) {
    429           File->FsInfo = AllocatePool (Size);
    430           Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
    431         }
    432 
    433         // Get information about the file
    434         Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
    435         if (!EFI_ERROR (Status)) {
    436           Size = 0;
    437           Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
    438           if (Status == EFI_BUFFER_TOO_SMALL) {
    439             File->FsFileInfo = AllocatePool (Size);
    440             Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
    441             if (!EFI_ERROR (Status)) {
    442               File->Size = (UINTN)File->FsFileInfo->FileSize;
    443               File->MaxPosition = (UINT64)File->Size;
    444             }
    445           }
    446         }
    447 
    448         Root->Close (Root);
    449       }
    450     }
    451   } else if (File->Type == EfiOpenBlockIo) {
    452     File->Size = (UINTN)File->MaxPosition;
    453   }
    454 
    455   return Status;
    456 }
    457 
    458 #define ToUpper(a)  ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
    459 
    460 EFI_STATUS
    461 CompareGuidToString (
    462   IN  EFI_GUID    *Guid,
    463   IN  CHAR8       *String
    464   )
    465 {
    466   CHAR8       AsciiGuid[64];
    467   CHAR8       *StringPtr;
    468   CHAR8       *GuidPtr;
    469 
    470   AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
    471 
    472   StringPtr = String;
    473   GuidPtr   = AsciiGuid;
    474 
    475   while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
    476     // Skip dashes
    477     if (*StringPtr == '-') {
    478       StringPtr++;
    479       continue;
    480     }
    481 
    482     if (*GuidPtr == '-') {
    483       GuidPtr++;
    484       continue;
    485     }
    486 
    487     if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
    488       return EFI_NOT_FOUND;
    489     }
    490 
    491     StringPtr++;
    492     GuidPtr++;
    493   }
    494 
    495   return EFI_SUCCESS;
    496 }
    497 
    498 
    499 /**
    500 Internal work function to fill in EFI_OPEN_FILE information for the FV
    501 
    502 @param  File        Open file handle
    503 @param  FileName    Name of file after device stripped off
    504 
    505 
    506 **/
    507 EFI_STATUS
    508 EblFvFileDevicePath (
    509   IN OUT EFI_OPEN_FILE  *File,
    510   IN  CHAR8             *FileName,
    511   IN  CONST UINT64      OpenMode
    512   )
    513 {
    514   EFI_STATUS                          Status;
    515   EFI_STATUS                          GetNextFileStatus;
    516   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH   DevicePathNode;
    517   EFI_DEVICE_PATH_PROTOCOL            *DevicePath;
    518   UINTN                               Key;
    519   UINT32                              AuthenticationStatus;
    520   CHAR8                               AsciiSection[MAX_PATHNAME];
    521   VOID                                *Section;
    522   UINTN                               SectionSize;
    523   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
    524   EFI_LBA                             Lba;
    525   UINTN                               BlockSize;
    526   UINTN                               NumberOfBlocks;
    527   EFI_FIRMWARE_VOLUME_HEADER          *FvHeader = NULL;
    528   UINTN                               Index;
    529 
    530 
    531   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
    532   if (EFI_ERROR (Status)) {
    533     return Status;
    534   }
    535 
    536   // Get FVB Info about the handle
    537   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
    538   if (!EFI_ERROR (Status)) {
    539     Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
    540     if (!EFI_ERROR (Status)) {
    541       FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
    542       File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
    543       for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
    544         File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
    545       }
    546 
    547       for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
    548         Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
    549         if (EFI_ERROR (Status)) {
    550           break;
    551         }
    552       }
    553     }
    554   }
    555 
    556 
    557   DevicePath = DevicePathFromHandle (File->EfiHandle);
    558 
    559   if (*FileName == '\0') {
    560     File->DevicePath = DuplicateDevicePath (DevicePath);
    561     File->Size = File->FvSize;
    562     File->MaxPosition = File->Size;
    563   } else {
    564     Key = 0;
    565     do {
    566       File->FvType = EFI_FV_FILETYPE_ALL;
    567       GetNextFileStatus = File->Fv->GetNextFile (
    568         File->Fv,
    569         &Key,
    570         &File->FvType,
    571         &File->FvNameGuid,
    572         &File->FvAttributes,
    573         &File->Size
    574         );
    575       if (!EFI_ERROR (GetNextFileStatus)) {
    576         // Compare GUID first
    577         Status = CompareGuidToString (&File->FvNameGuid, FileName);
    578         if (!EFI_ERROR(Status)) {
    579           break;
    580         }
    581 
    582         Section = NULL;
    583         Status = File->Fv->ReadSection (
    584           File->Fv,
    585           &File->FvNameGuid,
    586           EFI_SECTION_USER_INTERFACE,
    587           0,
    588           &Section,
    589           &SectionSize,
    590           &AuthenticationStatus
    591           );
    592         if (!EFI_ERROR (Status)) {
    593           UnicodeStrToAsciiStrS (Section, AsciiSection, MAX_PATHNAME);
    594           if (AsciiStriCmp (FileName, AsciiSection) == 0) {
    595             FreePool (Section);
    596             break;
    597           }
    598           FreePool (Section);
    599         }
    600       }
    601     } while (!EFI_ERROR (GetNextFileStatus));
    602 
    603     if (EFI_ERROR (GetNextFileStatus)) {
    604       return GetNextFileStatus;
    605     }
    606 
    607     if (OpenMode != EFI_SECTION_ALL) {
    608       // Calculate the size of the section we are targeting
    609       Section = NULL;
    610       File->Size = 0;
    611       Status = File->Fv->ReadSection (
    612         File->Fv,
    613         &File->FvNameGuid,
    614         (EFI_SECTION_TYPE)OpenMode,
    615         0,
    616         &Section,
    617         &File->Size,
    618         &AuthenticationStatus
    619         );
    620       if (EFI_ERROR (Status)) {
    621         return Status;
    622       }
    623     }
    624 
    625     File->MaxPosition = File->Size;
    626     EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
    627     File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
    628   }
    629 
    630 
    631   // FVB not required if FV was soft loaded...
    632   return EFI_SUCCESS;
    633 }
    634 
    635 
    636 
    637 
    638 /**
    639 Open a device named by PathName. The PathName includes a device name and
    640 path separated by a :. See file header for more details on the PathName
    641 syntax. There is no checking to prevent a file from being opened more than
    642 one type.
    643 
    644 SectionType is only used to open an FV. Each file in an FV contains multiple
    645 sections and only the SectionType section is opened.
    646 
    647 For any file that is opened with EfiOpen() must be closed with EfiClose().
    648 
    649 @param  PathName    Path to parse to open
    650 @param  OpenMode    Same as EFI_FILE.Open()
    651 @param  SectionType Section in FV to open.
    652 
    653 @return NULL  Open failed
    654 @return Valid EFI_OPEN_FILE handle
    655 
    656 **/
    657 EFI_OPEN_FILE *
    658 EfiOpen (
    659   IN        CHAR8               *PathName,
    660   IN  CONST UINT64              OpenMode,
    661   IN  CONST EFI_SECTION_TYPE    SectionType
    662   )
    663 {
    664   EFI_STATUS                Status;
    665   EFI_OPEN_FILE             *File;
    666   EFI_OPEN_FILE             FileData;
    667   UINTN                     StrLen;
    668   UINTN                     FileStart;
    669   UINTN                     DevNumber = 0;
    670   EFI_OPEN_FILE_GUARD       *GuardFile;
    671   BOOLEAN                   VolumeNameMatch;
    672   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    673   UINTN                     Size;
    674   EFI_IP_ADDRESS            Ip;
    675   CHAR8                     *CwdPlusPathName;
    676   UINTN                     Index;
    677   EFI_SECTION_TYPE          ModifiedSectionType;
    678   UINTN                     AsciiLength;
    679 
    680   EblUpdateDeviceLists ();
    681 
    682   File = &FileData;
    683   ZeroMem (File, sizeof (EFI_OPEN_FILE));
    684 
    685   StrLen = AsciiStrSize (PathName);
    686   if (StrLen <= 1) {
    687     // Smallest valid path is 1 char and a null
    688     return NULL;
    689   }
    690 
    691   for (FileStart = 0; FileStart < StrLen; FileStart++) {
    692     if (PathName[FileStart] == ':') {
    693       FileStart++;
    694       break;
    695     }
    696   }
    697 
    698   //
    699   // Matching volume name has precedence over handle based names
    700   //
    701   VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
    702   if (!VolumeNameMatch) {
    703     if (FileStart == StrLen) {
    704       // No Volume name or device name, so try Current Working Directory
    705       if (gCwd == NULL) {
    706         // No CWD
    707         return NULL;
    708       }
    709 
    710       // We could add a current working directory concept
    711       AsciiLength = AsciiStrSize (gCwd) + AsciiStrSize (PathName);
    712       CwdPlusPathName = AllocatePool (AsciiLength);
    713       if (CwdPlusPathName == NULL) {
    714         return NULL;
    715       }
    716 
    717       if ((PathName[0] == '/') || (PathName[0] == '\\')) {
    718         // PathName starts in / so this means we go to the root of the device in the CWD.
    719         CwdPlusPathName[0] = '\0';
    720         for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
    721           CwdPlusPathName[FileStart] = gCwd[FileStart];
    722           if (gCwd[FileStart] == ':') {
    723             FileStart++;
    724             CwdPlusPathName[FileStart] = '\0';
    725             break;
    726           }
    727         }
    728       } else {
    729         AsciiStrCpyS (CwdPlusPathName, AsciiLength, gCwd);
    730         StrLen = AsciiStrLen (gCwd);
    731         if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
    732           AsciiStrCatS (CwdPlusPathName, AsciiLength, "\\");
    733         }
    734       }
    735 
    736       AsciiStrCatS (CwdPlusPathName, AsciiLength, PathName);
    737       if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
    738         // Extra error check to make sure we don't recurse and blow stack
    739         return NULL;
    740       }
    741 
    742       File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
    743       FreePool (CwdPlusPathName);
    744       return File;
    745     }
    746 
    747     DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
    748   }
    749 
    750   File->DeviceName = AllocatePool (StrLen);
    751   AsciiStrCpyS (File->DeviceName, StrLen, PathName);
    752   File->DeviceName[FileStart - 1] = '\0';
    753   File->FileName = &File->DeviceName[FileStart];
    754   if (File->FileName[0] == '\0') {
    755     // if it is just a file name use / as root
    756     File->FileName = "\\";
    757   }
    758 
    759   //
    760   // Use best match algorithm on the dev names so we only need to look at the
    761   // first few charters to match the full device name. Short name forms are
    762   // legal from the caller.
    763   //
    764   Status = EFI_SUCCESS;
    765   if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
    766     if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
    767       if (DevNumber >= mFsCount) {
    768         goto ErrorExit;
    769       }
    770       File->Type = EfiOpenFileSystem;
    771       File->EfiHandle = mFs[DevNumber];
    772       Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
    773 
    774     } else if (PathName[1] == 'v' || PathName[1] == 'V') {
    775       if (DevNumber >= mFvCount) {
    776         goto ErrorExit;
    777       }
    778       File->Type = EfiOpenFirmwareVolume;
    779       File->EfiHandle = mFv[DevNumber];
    780 
    781       if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
    782         // Skip leading / as its not really needed for the FV since no directories are supported
    783         FileStart++;
    784       }
    785 
    786       // Check for 2nd :
    787       ModifiedSectionType = SectionType;
    788       for (Index = FileStart; PathName[Index] != '\0'; Index++) {
    789         if (PathName[Index] == ':') {
    790           // Support fv0:\DxeCore:0x10
    791           // This means open the PE32 Section of the file
    792           ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
    793           PathName[Index] = '\0';
    794         }
    795       }
    796       File->FvSectionType = ModifiedSectionType;
    797       Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
    798     }
    799   } else if ((*PathName == 'A') || (*PathName == 'a')) {
    800     // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
    801     File->Type = EfiOpenMemoryBuffer;
    802     // 1st colon is at PathName[FileStart - 1]
    803     File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
    804 
    805     // Find 2nd colon
    806     while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
    807       FileStart++;
    808     }
    809 
    810     // If we ran out of string, there's no extra data
    811     if (PathName[FileStart] == '\0') {
    812       File->Size = 0;
    813     } else {
    814       File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
    815     }
    816 
    817     // if there's no number after the second colon, default
    818     // the end of memory
    819     if (File->Size == 0) {
    820       File->Size =  (UINTN)(0 - (UINTN)File->Buffer);
    821     }
    822 
    823     File->MaxPosition = File->Size;
    824     File->BaseOffset = (UINTN)File->Buffer;
    825 
    826   } else if (*PathName== 'l' || *PathName == 'L') {
    827     if (DevNumber >= mLoadFileCount) {
    828       goto ErrorExit;
    829     }
    830     File->Type = EfiOpenLoadFile;
    831     File->EfiHandle = mLoadFile[DevNumber];
    832 
    833     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
    834     if (EFI_ERROR (Status)) {
    835       goto ErrorExit;
    836     }
    837 
    838     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
    839     if (EFI_ERROR (Status)) {
    840       goto ErrorExit;
    841     }
    842     File->DevicePath = DuplicateDevicePath (DevicePath);
    843 
    844   } else if (*PathName == 'b' || *PathName == 'B') {
    845     // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
    846     if (DevNumber >= mBlkIoCount) {
    847       goto ErrorExit;
    848     }
    849     File->Type = EfiOpenBlockIo;
    850     File->EfiHandle = mBlkIo[DevNumber];
    851     EblFileDevicePath (File, "", OpenMode);
    852 
    853     // 1st colon is at PathName[FileStart - 1]
    854     File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
    855 
    856     // Find 2nd colon
    857     while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
    858       FileStart++;
    859     }
    860 
    861     // If we ran out of string, there's no extra data
    862     if (PathName[FileStart] == '\0') {
    863       Size = 0;
    864     } else {
    865       Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
    866     }
    867 
    868     // if a zero size is passed in (or the size is left out entirely),
    869     // go to the end of the device.
    870     if (Size == 0) {
    871       File->Size = File->Size - File->DiskOffset;
    872     } else {
    873       File->Size = Size;
    874     }
    875 
    876     File->MaxPosition = File->Size;
    877     File->BaseOffset = File->DiskOffset;
    878   } else if ((*PathName) >= '0' && (*PathName <= '9')) {
    879 
    880     // Get current IP address
    881     Status = EblGetCurrentIpAddress (&Ip);
    882     if (EFI_ERROR(Status)) {
    883       AsciiPrint("Device IP Address is not configured.\n");
    884       goto ErrorExit;
    885     }
    886 
    887 
    888     // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
    889     File->Type = EfiOpenTftp;
    890     File->IsDirty = FALSE;
    891     File->IsBufferValid = FALSE;
    892 
    893     Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
    894   }
    895 
    896   if (EFI_ERROR (Status)) {
    897     goto ErrorExit;
    898   }
    899 
    900   GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
    901   if (GuardFile == NULL) {
    902     goto ErrorExit;
    903   }
    904 
    905   GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
    906   CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
    907   GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
    908 
    909   return &(GuardFile->File);
    910 
    911 ErrorExit:
    912   FreePool (File->DeviceName);
    913   return NULL;
    914 }
    915 
    916 #define FILE_COPY_CHUNK 0x01000000
    917 
    918 EFI_STATUS
    919 EfiCopyFile (
    920   IN        CHAR8               *DestinationFile,
    921   IN        CHAR8               *SourceFile
    922   )
    923 {
    924   EFI_OPEN_FILE *Source      = NULL;
    925   EFI_OPEN_FILE *Destination = NULL;
    926   EFI_STATUS    Status       = EFI_SUCCESS;
    927   VOID          *Buffer      = NULL;
    928   UINTN         Size;
    929   UINTN         Offset;
    930   UINTN         Chunk = FILE_COPY_CHUNK;
    931 
    932   Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
    933   if (Source == NULL) {
    934     AsciiPrint("Source file open error.\n");
    935     Status = EFI_NOT_FOUND;
    936     goto Exit;
    937   }
    938 
    939   Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
    940   if (Destination == NULL) {
    941     AsciiPrint("Destination file open error.\n");
    942     Status = EFI_NOT_FOUND;
    943     goto Exit;
    944   }
    945 
    946   Buffer = AllocatePool(FILE_COPY_CHUNK);
    947   if (Buffer == NULL) {
    948     Status = EFI_OUT_OF_RESOURCES;
    949     goto Exit;
    950   }
    951 
    952   Size = EfiTell(Source, NULL);
    953 
    954   for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
    955     Chunk = FILE_COPY_CHUNK;
    956 
    957     Status = EfiRead(Source, Buffer, &Chunk);
    958     if (EFI_ERROR(Status)) {
    959       AsciiPrint("Read file error %r\n", Status);
    960       goto Exit;
    961     }
    962 
    963     Status = EfiWrite(Destination, Buffer, &Chunk);
    964     if (EFI_ERROR(Status)) {
    965       AsciiPrint("Write file error %r\n", Status);
    966       goto Exit;
    967     }
    968   }
    969 
    970   // Any left over?
    971   if (Offset < Size) {
    972     Chunk = Size - Offset;
    973 
    974     Status = EfiRead(Source, Buffer, &Chunk);
    975     if (EFI_ERROR(Status)) {
    976       AsciiPrint("Read file error\n");
    977       goto Exit;
    978     }
    979 
    980     Status = EfiWrite(Destination, Buffer, &Chunk);
    981     if (EFI_ERROR(Status)) {
    982       AsciiPrint("Write file error\n");
    983       goto Exit;
    984     }
    985   }
    986 
    987 Exit:
    988   if (Source != NULL) {
    989     Status = EfiClose(Source);
    990     if (EFI_ERROR(Status)) {
    991       AsciiPrint("Source close error");
    992     }
    993   }
    994 
    995   if (Destination != NULL) {
    996     Status = EfiClose(Destination);
    997     if (EFI_ERROR(Status)) {
    998       AsciiPrint("Destination close error");
    999     }
   1000   }
   1001 
   1002   if (Buffer != NULL) {
   1003     FreePool(Buffer);
   1004   }
   1005 
   1006   return Status;
   1007 }
   1008 
   1009 /**
   1010 Use DeviceType and Index to form a valid PathName and try and open it.
   1011 
   1012 @param  DeviceType  Device type to open
   1013 @param  Index       Device Index to use. Zero relative.
   1014 
   1015 @return NULL  Open failed
   1016 @return Valid EFI_OPEN_FILE handle
   1017 
   1018 **/
   1019 EFI_OPEN_FILE  *
   1020 EfiDeviceOpenByType (
   1021   IN  EFI_OPEN_FILE_TYPE    DeviceType,
   1022   IN  UINTN                 Index
   1023   )
   1024 {
   1025   CHAR8   *DevStr;
   1026   CHAR8   Path[MAX_CMD_LINE];
   1027 
   1028   switch (DeviceType) {
   1029   case EfiOpenLoadFile:
   1030     DevStr = "loadfile%d:";
   1031     break;
   1032   case EfiOpenFirmwareVolume:
   1033     DevStr = "fv%d:";
   1034     break;
   1035   case EfiOpenFileSystem:
   1036     DevStr = "fs%d:";
   1037     break;
   1038   case EfiOpenBlockIo:
   1039     DevStr = "blk%d:";
   1040     break;
   1041   case EfiOpenMemoryBuffer:
   1042     DevStr = "a%d:";
   1043     break;
   1044   default:
   1045     return NULL;
   1046   }
   1047 
   1048   AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
   1049 
   1050   return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
   1051 }
   1052 
   1053 
   1054 /**
   1055 Close a file handle opened by EfiOpen() and free all resources allocated by
   1056 EfiOpen().
   1057 
   1058 @param  Stream    Open File Handle
   1059 
   1060 @return EFI_INVALID_PARAMETER  Stream is not an Open File
   1061 @return EFI_SUCCESS            Steam closed
   1062 
   1063 **/
   1064 EFI_STATUS
   1065 EfiClose (
   1066   IN  EFI_OPEN_FILE     *File
   1067   )
   1068 {
   1069   EFI_STATUS          Status;
   1070   UINT64              TftpBufferSize;
   1071 
   1072   if (!FileHandleValid (File)) {
   1073     return EFI_INVALID_PARAMETER;
   1074   }
   1075 
   1076   //Write the buffer contents to TFTP file.
   1077   if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
   1078 
   1079     TftpBufferSize = File->Size;
   1080     Status = EblMtftp (
   1081       EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
   1082       File->Buffer,
   1083       TRUE,
   1084       &TftpBufferSize,
   1085       NULL,
   1086       &File->ServerIp,
   1087       (UINT8 *)File->FileName,
   1088       NULL,
   1089       FALSE
   1090       );
   1091     if (EFI_ERROR(Status)) {
   1092       AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
   1093       return Status;
   1094     }
   1095   }
   1096 
   1097   if ((File->Type == EfiOpenLoadFile) ||
   1098     ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
   1099     ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
   1100     EblFreePool(File->Buffer);
   1101   }
   1102 
   1103   EblFreePool (File->DevicePath);
   1104   EblFreePool (File->DeviceName);
   1105   EblFreePool (File->FsFileInfo);
   1106   EblFreePool (File->FsInfo);
   1107 
   1108   if (File->FsFileHandle != NULL) {
   1109     File->FsFileHandle->Close (File->FsFileHandle);
   1110   }
   1111 
   1112   // Need to free File and it's Guard structures
   1113   EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
   1114   return EFI_SUCCESS;
   1115 }
   1116 
   1117 
   1118 /**
   1119 Return the size of the file represented by Stream. Also return the current
   1120 Seek position. Opening a file will enable a valid file size to be returned.
   1121 LoadFile is an exception as a load file size is set to zero.
   1122 
   1123 @param  Stream    Open File Handle
   1124 
   1125 @return 0         Stream is not an Open File or a valid LoadFile handle
   1126 
   1127 **/
   1128 UINTN
   1129 EfiTell (
   1130   IN  EFI_OPEN_FILE     *File,
   1131   OUT EFI_LBA           *CurrentPosition    OPTIONAL
   1132   )
   1133 {
   1134   EFI_STATUS Status;
   1135   UINT64     BufferSize = 0;
   1136 
   1137   if (!FileHandleValid (File)) {
   1138     return 0;
   1139   }
   1140 
   1141   if (CurrentPosition != NULL) {
   1142     *CurrentPosition = File->CurrentPosition;
   1143   }
   1144 
   1145   if (File->Type == EfiOpenLoadFile) {
   1146     // Figure out the File->Size
   1147     File->Buffer = NULL;
   1148     File->Size   = 0;
   1149     Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
   1150     if (Status != EFI_BUFFER_TOO_SMALL) {
   1151       return 0;
   1152     }
   1153 
   1154     File->MaxPosition = (UINT64)File->Size;
   1155   } else if (File->Type == EfiOpenTftp) {
   1156 
   1157     Status = EblMtftp (
   1158       EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
   1159       NULL,
   1160       FALSE,
   1161       &BufferSize,
   1162       NULL,
   1163       &File->ServerIp,
   1164       (UINT8 *)File->FileName,
   1165       NULL,
   1166       TRUE
   1167       );
   1168     if (EFI_ERROR(Status)) {
   1169       AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
   1170       return 0;
   1171     }
   1172 
   1173     File->Size        = (UINTN)BufferSize;
   1174     File->MaxPosition = File->Size;
   1175   }
   1176 
   1177   return File->Size;
   1178 }
   1179 
   1180 
   1181 /**
   1182 Seek to the Offset location in the file. LoadFile and FV device types do
   1183 not support EfiSeek(). It is not possible to grow the file size using
   1184 EfiSeek().
   1185 
   1186 SeekType defines how use Offset to calculate the new file position:
   1187 EfiSeekStart  : Position = Offset
   1188 EfiSeekCurrent: Position is Offset bytes from the current position
   1189 EfiSeekEnd    : Only supported if Offset is zero to seek to end of file.
   1190 
   1191 @param  Stream    Open File Handle
   1192 @param  Offset    Offset to seek too.
   1193 @param  SeekType  Type of seek to perform
   1194 
   1195 
   1196 @return EFI_INVALID_PARAMETER  Stream is not an Open File
   1197 @return EFI_UNSUPPORTED        LoadFile and FV do not support Seek
   1198 @return EFI_NOT_FOUND          Seek past the end of the file.
   1199 @return EFI_SUCCESS            Steam closed
   1200 
   1201 **/
   1202 EFI_STATUS
   1203 EfiSeek (
   1204   IN  EFI_OPEN_FILE     *File,
   1205   IN  EFI_LBA           Offset,
   1206   IN  EFI_SEEK_TYPE     SeekType
   1207   )
   1208 {
   1209   EFI_STATUS    Status;
   1210   UINT64        CurrentPosition;
   1211 
   1212   if (!FileHandleValid (File)) {
   1213     return EFI_INVALID_PARAMETER;
   1214   }
   1215 
   1216   if (File->Type == EfiOpenLoadFile) {
   1217     // LoadFile does not support Seek
   1218     return EFI_UNSUPPORTED;
   1219   }
   1220 
   1221   CurrentPosition = File->CurrentPosition;
   1222   switch (SeekType) {
   1223   case EfiSeekStart:
   1224     if (Offset > File->MaxPosition) {
   1225       return EFI_NOT_FOUND;
   1226     }
   1227     CurrentPosition = Offset;
   1228     break;
   1229 
   1230   case EfiSeekCurrent:
   1231     if ((File->CurrentPosition + Offset) > File->MaxPosition) {
   1232       return EFI_NOT_FOUND;
   1233     }
   1234     CurrentPosition += Offset;
   1235     break;
   1236 
   1237   case EfiSeekEnd:
   1238     if (Offset != 0) {
   1239       // We don't support growing file size via seeking past end of file
   1240       return EFI_UNSUPPORTED;
   1241     }
   1242     CurrentPosition = File->MaxPosition;
   1243     break;
   1244 
   1245   default:
   1246     return EFI_NOT_FOUND;
   1247   }
   1248 
   1249   Status = EFI_SUCCESS;
   1250   if (File->FsFileHandle != NULL) {
   1251     Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
   1252   }
   1253 
   1254   if (!EFI_ERROR (Status)) {
   1255     File->CurrentPosition = CurrentPosition;
   1256   }
   1257 
   1258   return Status;
   1259 }
   1260 
   1261 EFI_STATUS
   1262 CacheTftpFile (
   1263   IN OUT  EFI_OPEN_FILE *File
   1264   )
   1265 {
   1266   EFI_STATUS          Status;
   1267   UINT64              TftpBufferSize;
   1268 
   1269   if (File->IsBufferValid) {
   1270     return EFI_SUCCESS;
   1271   }
   1272 
   1273   // Make sure the file size is set.
   1274   EfiTell (File, NULL);
   1275 
   1276   //Allocate a buffer to hold the whole file.
   1277   File->Buffer = AllocatePool(File->Size);
   1278   if (File->Buffer == NULL) {
   1279     return EFI_OUT_OF_RESOURCES;
   1280   }
   1281 
   1282   TftpBufferSize = File->Size;
   1283 
   1284   Status = EblMtftp (
   1285     EFI_PXE_BASE_CODE_TFTP_READ_FILE,
   1286     File->Buffer,
   1287     FALSE,
   1288     &TftpBufferSize,
   1289     NULL,
   1290     &File->ServerIp,
   1291     (UINT8 *)File->FileName,
   1292     NULL,
   1293     FALSE);
   1294   if (EFI_ERROR(Status)) {
   1295     AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
   1296     FreePool(File->Buffer);
   1297     return Status;
   1298   }
   1299 
   1300   // Set the buffer valid flag.
   1301   File->IsBufferValid = TRUE;
   1302 
   1303   return Status;
   1304 }
   1305 
   1306 /**
   1307 Read BufferSize bytes from the current location in the file. For load file,
   1308 FV, and TFTP case you must read the entire file.
   1309 
   1310 @param  Stream      Open File Handle
   1311 @param  Buffer      Caller allocated buffer.
   1312 @param  BufferSize  Size of buffer in bytes.
   1313 
   1314 
   1315 @return EFI_SUCCESS           Stream is not an Open File
   1316 @return EFI_END_OF_FILE Tried to read past the end of the file
   1317 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1318 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1319 @return "other"               Error returned from device read
   1320 
   1321 **/
   1322 EFI_STATUS
   1323 EfiRead (
   1324   IN  EFI_OPEN_FILE       *File,
   1325   OUT VOID                *Buffer,
   1326   OUT UINTN               *BufferSize
   1327   )
   1328 {
   1329   EFI_STATUS            Status;
   1330   UINT32                AuthenticationStatus;
   1331   EFI_DISK_IO_PROTOCOL  *DiskIo;
   1332 
   1333   if (!FileHandleValid (File)) {
   1334     return EFI_INVALID_PARAMETER;
   1335   }
   1336 
   1337   // Don't read past the end of the file.
   1338   if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1339     return EFI_END_OF_FILE;
   1340   }
   1341 
   1342   switch (File->Type) {
   1343   case EfiOpenLoadFile:
   1344     // Figure out the File->Size
   1345     EfiTell (File, NULL);
   1346 
   1347     Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
   1348     break;
   1349 
   1350   case EfiOpenFirmwareVolume:
   1351     if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
   1352       // This is the entire FV device, so treat like a memory buffer
   1353       CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
   1354       File->CurrentPosition += *BufferSize;
   1355       Status = EFI_SUCCESS;
   1356     } else {
   1357       if (File->Buffer == NULL) {
   1358         if (File->FvSectionType == EFI_SECTION_ALL) {
   1359           Status = File->Fv->ReadFile (
   1360             File->Fv,
   1361             &File->FvNameGuid,
   1362             (VOID **)&File->Buffer,
   1363             &File->Size,
   1364             &File->FvType,
   1365             &File->FvAttributes,
   1366             &AuthenticationStatus
   1367             );
   1368         } else {
   1369           Status = File->Fv->ReadSection (
   1370             File->Fv,
   1371             &File->FvNameGuid,
   1372             File->FvSectionType,
   1373             0,
   1374             (VOID **)&File->Buffer,
   1375             &File->Size,
   1376             &AuthenticationStatus
   1377             );
   1378         }
   1379         if (EFI_ERROR (Status)) {
   1380           return Status;
   1381         }
   1382         File->IsBufferValid = TRUE;
   1383       }
   1384       // Operate on the cached buffer so Seek will work
   1385       CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1386       File->CurrentPosition += *BufferSize;
   1387       Status = EFI_SUCCESS;
   1388     }
   1389     break;
   1390 
   1391   case EfiOpenMemoryBuffer:
   1392     CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1393     File->CurrentPosition += *BufferSize;
   1394     Status = EFI_SUCCESS;
   1395     break;
   1396 
   1397   case EfiOpenFileSystem:
   1398     Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
   1399     File->CurrentPosition += *BufferSize;
   1400     break;
   1401 
   1402   case EfiOpenBlockIo:
   1403     Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
   1404     if (!EFI_ERROR(Status)) {
   1405       Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
   1406     }
   1407     File->CurrentPosition += *BufferSize;
   1408     break;
   1409 
   1410   case EfiOpenTftp:
   1411     // Cache the file if it hasn't been cached yet.
   1412     if (File->IsBufferValid == FALSE) {
   1413       Status = CacheTftpFile (File);
   1414       if (EFI_ERROR (Status)) {
   1415         return Status;
   1416       }
   1417     }
   1418 
   1419     // Copy out the requested data
   1420     CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1421     File->CurrentPosition += *BufferSize;
   1422 
   1423     Status = EFI_SUCCESS;
   1424     break;
   1425 
   1426   default:
   1427     return EFI_INVALID_PARAMETER;
   1428   };
   1429 
   1430   return Status;
   1431 }
   1432 
   1433 
   1434 /**
   1435 Read the entire file into a buffer. This routine allocates the buffer and
   1436 returns it to the user full of the read data.
   1437 
   1438 This is very useful for load file where it's hard to know how big the buffer
   1439 must be.
   1440 
   1441 @param  Stream      Open File Handle
   1442 @param  Buffer      Pointer to buffer to return.
   1443 @param  BufferSize  Pointer to Size of buffer return..
   1444 
   1445 
   1446 @return EFI_SUCCESS           Stream is not an Open File
   1447 @return EFI_END_OF_FILE       Tried to read past the end of the file
   1448 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1449 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1450 @return "other"               Error returned from device read
   1451 
   1452 **/
   1453 EFI_STATUS
   1454 EfiReadAllocatePool (
   1455   IN  EFI_OPEN_FILE     *File,
   1456   OUT VOID              **Buffer,
   1457   OUT UINTN             *BufferSize
   1458   )
   1459 {
   1460   if (!FileHandleValid (File)) {
   1461     return EFI_INVALID_PARAMETER;
   1462   }
   1463 
   1464   // Loadfile defers file size determination on Open so use tell to find it
   1465   EfiTell (File, NULL);
   1466 
   1467   *BufferSize = File->Size;
   1468   *Buffer = AllocatePool (*BufferSize);
   1469   if (*Buffer == NULL) {
   1470     return EFI_NOT_FOUND;
   1471   }
   1472 
   1473   return EfiRead (File, *Buffer, BufferSize);
   1474 }
   1475 
   1476 
   1477 /**
   1478 Write data back to the file. For TFTP case you must write the entire file.
   1479 
   1480 @param  Stream      Open File Handle
   1481 @param  Buffer      Pointer to buffer to return.
   1482 @param  BufferSize  Pointer to Size of buffer return..
   1483 
   1484 
   1485 @return EFI_SUCCESS           Stream is not an Open File
   1486 @return EFI_END_OF_FILE       Tried to read past the end of the file
   1487 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1488 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1489 @return "other"               Error returned from device write
   1490 
   1491 **/
   1492 EFI_STATUS
   1493 EfiWrite (
   1494   IN  EFI_OPEN_FILE   *File,
   1495   OUT VOID            *Buffer,
   1496   OUT UINTN           *BufferSize
   1497   )
   1498 {
   1499   EFI_STATUS              Status;
   1500   EFI_FV_WRITE_FILE_DATA  FileData;
   1501   EFI_DISK_IO_PROTOCOL    *DiskIo;
   1502 
   1503   if (!FileHandleValid (File)) {
   1504     return EFI_INVALID_PARAMETER;
   1505   }
   1506 
   1507   switch (File->Type) {
   1508   case EfiOpenMemoryBuffer:
   1509     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1510       return EFI_END_OF_FILE;
   1511     }
   1512 
   1513     CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
   1514     File->CurrentPosition += *BufferSize;
   1515     Status = EFI_SUCCESS;
   1516 
   1517   case EfiOpenLoadFile:
   1518     // LoadFile device is read only be definition
   1519     Status = EFI_UNSUPPORTED;
   1520 
   1521   case EfiOpenFirmwareVolume:
   1522     if (File->FvSectionType != EFI_SECTION_ALL) {
   1523       // Writes not support to a specific section. You have to update entire file
   1524       return EFI_UNSUPPORTED;
   1525     }
   1526 
   1527     FileData.NameGuid       = &(File->FvNameGuid);
   1528     FileData.Type           = File->FvType;
   1529     FileData.FileAttributes = File->FvAttributes;
   1530     FileData.Buffer         = Buffer;
   1531     FileData.BufferSize     = (UINT32)*BufferSize;
   1532     Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
   1533     break;
   1534 
   1535   case EfiOpenFileSystem:
   1536     Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
   1537     File->CurrentPosition += *BufferSize;
   1538     break;
   1539 
   1540   case EfiOpenBlockIo:
   1541     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1542       return EFI_END_OF_FILE;
   1543     }
   1544 
   1545     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
   1546     if (!EFI_ERROR(Status)) {
   1547       Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
   1548     }
   1549     File->CurrentPosition += *BufferSize;
   1550     break;
   1551 
   1552   case EfiOpenTftp:
   1553     // Cache the file if it hasn't been cached yet.
   1554     if (File->IsBufferValid == FALSE) {
   1555       Status = CacheTftpFile(File);
   1556       if (EFI_ERROR(Status)) {
   1557         return Status;
   1558       }
   1559     }
   1560 
   1561     // Don't overwrite the buffer
   1562     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1563       UINT8 *TempBuffer;
   1564 
   1565       TempBuffer = File->Buffer;
   1566 
   1567       File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
   1568       if (File->Buffer == NULL) {
   1569         return EFI_OUT_OF_RESOURCES;
   1570       }
   1571 
   1572       CopyMem (File->Buffer, TempBuffer, File->Size);
   1573 
   1574       FreePool (TempBuffer);
   1575 
   1576       File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
   1577       File->MaxPosition = (UINT64)File->Size;
   1578     }
   1579 
   1580     // Copy in the requested data
   1581     CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
   1582     File->CurrentPosition += *BufferSize;
   1583 
   1584     // Mark the file dirty
   1585     File->IsDirty = TRUE;
   1586 
   1587     Status = EFI_SUCCESS;
   1588     break;
   1589 
   1590   default:
   1591     Status = EFI_INVALID_PARAMETER;
   1592   };
   1593 
   1594   return Status;
   1595 }
   1596 
   1597 
   1598 /**
   1599 Given Cwd expand Path to remove .. and replace them with real
   1600 directory names.
   1601 
   1602 @param  Cwd     Current Working Directory
   1603 @param  Path    Path to expand
   1604 
   1605 @return NULL     Cwd or Path are not valid
   1606 @return 'other'  Path with .. expanded
   1607 
   1608 **/
   1609 CHAR8 *
   1610 ExpandPath (
   1611   IN CHAR8    *Cwd,
   1612   IN CHAR8    *Path
   1613   )
   1614 {
   1615   CHAR8   *NewPath;
   1616   CHAR8   *Work, *Start, *End;
   1617   UINTN   StrLen, AllocLen;
   1618   INTN    i;
   1619 
   1620   if (Cwd == NULL || Path == NULL) {
   1621     return NULL;
   1622   }
   1623 
   1624   StrLen = AsciiStrSize (Cwd);
   1625   if (StrLen <= 2) {
   1626     // Smallest valid path is 1 char and a null
   1627     return NULL;
   1628   }
   1629 
   1630   StrLen = AsciiStrSize (Path);
   1631   AllocLen = AsciiStrSize (Cwd) + StrLen + 1;
   1632   NewPath = AllocatePool (AllocLen);
   1633   if (NewPath == NULL) {
   1634     return NULL;
   1635   }
   1636   AsciiStrCpyS (NewPath, AllocLen, Cwd);
   1637 
   1638   End = Path + StrLen;
   1639   for (Start = Path ;;) {
   1640     Work = AsciiStrStr (Start, "..") ;
   1641     if (Work == NULL) {
   1642       // Remaining part of Path contains no more ..
   1643       break;
   1644     }
   1645 
   1646     // append path prior to ..
   1647     AsciiStrnCatS (NewPath, AllocLen, Start, Work - Start);
   1648     StrLen = AsciiStrLen (NewPath);
   1649     for (i = StrLen; i >= 0; i--) {
   1650       if (NewPath[i] == ':') {
   1651         // too many ..
   1652         return NULL;
   1653       }
   1654       if (NewPath[i] == '/' || NewPath[i] == '\\') {
   1655         if ((i > 0) && (NewPath[i-1] == ':')) {
   1656           // leave the / before a :
   1657           NewPath[i+1] = '\0';
   1658         } else {
   1659           // replace / will Null to remove trailing file/dir reference
   1660           NewPath[i] = '\0';
   1661         }
   1662         break;
   1663       }
   1664     }
   1665 
   1666     Start = Work + 3;
   1667   }
   1668 
   1669   // Handle the path that remains after the ..
   1670   AsciiStrnCatS (NewPath, AllocLen, Start, End - Start);
   1671 
   1672   return NewPath;
   1673 }
   1674 
   1675 
   1676 /**
   1677 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
   1678 the path does not contain a device name, The CWD is prepended to the path.
   1679 
   1680 @param  Cwd     Current Working Directory to set
   1681 
   1682 
   1683 @return EFI_SUCCESS           CWD is set
   1684 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
   1685 
   1686 **/
   1687 EFI_STATUS
   1688 EfiSetCwd (
   1689   IN  CHAR8   *Cwd
   1690   )
   1691 {
   1692   EFI_OPEN_FILE *File;
   1693   UINTN         Len, AllocLen;
   1694   CHAR8         *Path;
   1695 
   1696   if (Cwd == NULL) {
   1697     return EFI_INVALID_PARAMETER;
   1698   }
   1699 
   1700   if (AsciiStrCmp (Cwd, ".") == 0) {
   1701     // cd . is a no-op
   1702     return EFI_SUCCESS;
   1703   }
   1704 
   1705   Path = Cwd;
   1706   if (AsciiStrStr (Cwd, "..") != NULL) {
   1707     if (gCwd == NULL) {
   1708       // no parent
   1709       return EFI_SUCCESS;
   1710     }
   1711 
   1712     Len = AsciiStrLen (gCwd);
   1713     if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
   1714       // parent is device so nothing to do
   1715       return EFI_SUCCESS;
   1716     }
   1717 
   1718     // Expand .. in Cwd, given we know current working directory
   1719     Path = ExpandPath (gCwd, Cwd);
   1720     if (Path == NULL) {
   1721       return EFI_NOT_FOUND;
   1722     }
   1723   }
   1724 
   1725   File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
   1726   if (File == NULL) {
   1727     return EFI_INVALID_PARAMETER;
   1728   }
   1729 
   1730   if (gCwd != NULL) {
   1731     FreePool (gCwd);
   1732   }
   1733 
   1734   // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
   1735   // relative to the current gCwd or not.
   1736   AllocLen = AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10;
   1737   gCwd = AllocatePool (AllocLen);
   1738   if (gCwd == NULL) {
   1739     return EFI_INVALID_PARAMETER;
   1740   }
   1741 
   1742   AsciiStrCpyS (gCwd, AllocLen, File->DeviceName);
   1743   if (File->FileName == NULL) {
   1744     AsciiStrCatS (gCwd, AllocLen, ":\\");
   1745   } else {
   1746     AsciiStrCatS (gCwd, AllocLen, ":");
   1747     AsciiStrCatS (gCwd, AllocLen, File->FileName);
   1748   }
   1749 
   1750 
   1751   EfiClose (File);
   1752   if (Path != Cwd) {
   1753     FreePool (Path);
   1754   }
   1755   return EFI_SUCCESS;
   1756 }
   1757 
   1758 
   1759 /**
   1760 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
   1761 the path does not contain a device name, The CWD is prepended to the path.
   1762 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
   1763 a call to EfiSetCwd() it is not legal to use the pointer returned by
   1764 this function.
   1765 
   1766 @param  Cwd     Current Working Directory
   1767 
   1768 
   1769 @return ""      No CWD set
   1770 @return 'other' Returns buffer that contains CWD.
   1771 
   1772 **/
   1773 CHAR8 *
   1774 EfiGetCwd (
   1775   VOID
   1776   )
   1777 {
   1778   if (gCwd == NULL) {
   1779     return "";
   1780   }
   1781   return gCwd;
   1782 }
   1783 
   1784 
   1785