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     AsciiStrToUnicodeStr (FileName, UnicodeFileName);
    388   } else {
    389     AsciiStrToUnicodeStr ("\\", UnicodeFileName);
    390   }
    391 
    392   Size = StrSize (UnicodeFileName);
    393   FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
    394   if (FileDevicePath != NULL) {
    395     FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
    396     FilePath->Header.Type    = MEDIA_DEVICE_PATH;
    397     FilePath->Header.SubType = MEDIA_FILEPATH_DP;
    398     CopyMem (&FilePath->PathName, UnicodeFileName, Size);
    399     SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
    400     SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
    401 
    402     if (File->EfiHandle != NULL) {
    403       File->DevicePath = DevicePathFromHandle (File->EfiHandle);
    404     }
    405 
    406     File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
    407     FreePool (FileDevicePath);
    408   }
    409 
    410   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
    411   if (!EFI_ERROR (Status)) {
    412     File->FsBlockIoMedia = BlkIo->Media;
    413     File->FsBlockIo = BlkIo;
    414 
    415     // If we are not opening the device this will get over written with file info
    416     File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
    417   }
    418 
    419   if (File->Type == EfiOpenFileSystem) {
    420     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
    421     if (!EFI_ERROR (Status)) {
    422       Status = Fs->OpenVolume (Fs, &Root);
    423       if (!EFI_ERROR (Status)) {
    424         // Get information about the volume
    425         Size = 0;
    426         Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
    427         if (Status == EFI_BUFFER_TOO_SMALL) {
    428           File->FsInfo = AllocatePool (Size);
    429           Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
    430         }
    431 
    432         // Get information about the file
    433         Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
    434         if (!EFI_ERROR (Status)) {
    435           Size = 0;
    436           Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
    437           if (Status == EFI_BUFFER_TOO_SMALL) {
    438             File->FsFileInfo = AllocatePool (Size);
    439             Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
    440             if (!EFI_ERROR (Status)) {
    441               File->Size = (UINTN)File->FsFileInfo->FileSize;
    442               File->MaxPosition = (UINT64)File->Size;
    443             }
    444           }
    445         }
    446 
    447         Root->Close (Root);
    448       }
    449     }
    450   } else if (File->Type == EfiOpenBlockIo) {
    451     File->Size = (UINTN)File->MaxPosition;
    452   }
    453 
    454   return Status;
    455 }
    456 
    457 #define ToUpper(a)  ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
    458 
    459 EFI_STATUS
    460 CompareGuidToString (
    461   IN  EFI_GUID    *Guid,
    462   IN  CHAR8       *String
    463   )
    464 {
    465   CHAR8       AsciiGuid[64];
    466   CHAR8       *StringPtr;
    467   CHAR8       *GuidPtr;
    468 
    469   AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
    470 
    471   StringPtr = String;
    472   GuidPtr   = AsciiGuid;
    473 
    474   while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
    475     // Skip dashes
    476     if (*StringPtr == '-') {
    477       StringPtr++;
    478       continue;
    479     }
    480 
    481     if (*GuidPtr == '-') {
    482       GuidPtr++;
    483       continue;
    484     }
    485 
    486     if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
    487       return EFI_NOT_FOUND;
    488     }
    489 
    490     StringPtr++;
    491     GuidPtr++;
    492   }
    493 
    494   return EFI_SUCCESS;
    495 }
    496 
    497 
    498 /**
    499 Internal work function to fill in EFI_OPEN_FILE information for the FV
    500 
    501 @param  File        Open file handle
    502 @param  FileName    Name of file after device stripped off
    503 
    504 
    505 **/
    506 EFI_STATUS
    507 EblFvFileDevicePath (
    508   IN OUT EFI_OPEN_FILE  *File,
    509   IN  CHAR8             *FileName,
    510   IN  CONST UINT64      OpenMode
    511   )
    512 {
    513   EFI_STATUS                          Status;
    514   EFI_STATUS                          GetNextFileStatus;
    515   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH   DevicePathNode;
    516   EFI_DEVICE_PATH_PROTOCOL            *DevicePath;
    517   UINTN                               Key;
    518   UINT32                              AuthenticationStatus;
    519   CHAR8                               AsciiSection[MAX_PATHNAME];
    520   VOID                                *Section;
    521   UINTN                               SectionSize;
    522   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
    523   EFI_LBA                             Lba;
    524   UINTN                               BlockSize;
    525   UINTN                               NumberOfBlocks;
    526   EFI_FIRMWARE_VOLUME_HEADER          *FvHeader = NULL;
    527   UINTN                               Index;
    528 
    529 
    530   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
    531   if (EFI_ERROR (Status)) {
    532     return Status;
    533   }
    534 
    535   // Get FVB Info about the handle
    536   Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
    537   if (!EFI_ERROR (Status)) {
    538     Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
    539     if (!EFI_ERROR (Status)) {
    540       FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
    541       File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
    542       for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
    543         File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
    544       }
    545 
    546       for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
    547         Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
    548         if (EFI_ERROR (Status)) {
    549           break;
    550         }
    551       }
    552     }
    553   }
    554 
    555 
    556   DevicePath = DevicePathFromHandle (File->EfiHandle);
    557 
    558   if (*FileName == '\0') {
    559     File->DevicePath = DuplicateDevicePath (DevicePath);
    560     File->Size = File->FvSize;
    561     File->MaxPosition = File->Size;
    562   } else {
    563     Key = 0;
    564     do {
    565       File->FvType = EFI_FV_FILETYPE_ALL;
    566       GetNextFileStatus = File->Fv->GetNextFile (
    567         File->Fv,
    568         &Key,
    569         &File->FvType,
    570         &File->FvNameGuid,
    571         &File->FvAttributes,
    572         &File->Size
    573         );
    574       if (!EFI_ERROR (GetNextFileStatus)) {
    575         // Compare GUID first
    576         Status = CompareGuidToString (&File->FvNameGuid, FileName);
    577         if (!EFI_ERROR(Status)) {
    578           break;
    579         }
    580 
    581         Section = NULL;
    582         Status = File->Fv->ReadSection (
    583           File->Fv,
    584           &File->FvNameGuid,
    585           EFI_SECTION_USER_INTERFACE,
    586           0,
    587           &Section,
    588           &SectionSize,
    589           &AuthenticationStatus
    590           );
    591         if (!EFI_ERROR (Status)) {
    592           UnicodeStrToAsciiStr (Section, AsciiSection);
    593           if (AsciiStriCmp (FileName, AsciiSection) == 0) {
    594             FreePool (Section);
    595             break;
    596           }
    597           FreePool (Section);
    598         }
    599       }
    600     } while (!EFI_ERROR (GetNextFileStatus));
    601 
    602     if (EFI_ERROR (GetNextFileStatus)) {
    603       return GetNextFileStatus;
    604     }
    605 
    606     if (OpenMode != EFI_SECTION_ALL) {
    607       // Calculate the size of the section we are targeting
    608       Section = NULL;
    609       File->Size = 0;
    610       Status = File->Fv->ReadSection (
    611         File->Fv,
    612         &File->FvNameGuid,
    613         (EFI_SECTION_TYPE)OpenMode,
    614         0,
    615         &Section,
    616         &File->Size,
    617         &AuthenticationStatus
    618         );
    619       if (EFI_ERROR (Status)) {
    620         return Status;
    621       }
    622     }
    623 
    624     File->MaxPosition = File->Size;
    625     EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
    626     File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
    627   }
    628 
    629 
    630   // FVB not required if FV was soft loaded...
    631   return EFI_SUCCESS;
    632 }
    633 
    634 
    635 
    636 
    637 /**
    638 Open a device named by PathName. The PathName includes a device name and
    639 path separated by a :. See file header for more details on the PathName
    640 syntax. There is no checking to prevent a file from being opened more than
    641 one type.
    642 
    643 SectionType is only used to open an FV. Each file in an FV contains multiple
    644 sections and only the SectionType section is opened.
    645 
    646 For any file that is opened with EfiOpen() must be closed with EfiClose().
    647 
    648 @param  PathName    Path to parse to open
    649 @param  OpenMode    Same as EFI_FILE.Open()
    650 @param  SectionType Section in FV to open.
    651 
    652 @return NULL  Open failed
    653 @return Valid EFI_OPEN_FILE handle
    654 
    655 **/
    656 EFI_OPEN_FILE *
    657 EfiOpen (
    658   IN        CHAR8               *PathName,
    659   IN  CONST UINT64              OpenMode,
    660   IN  CONST EFI_SECTION_TYPE    SectionType
    661   )
    662 {
    663   EFI_STATUS                Status;
    664   EFI_OPEN_FILE             *File;
    665   EFI_OPEN_FILE             FileData;
    666   UINTN                     StrLen;
    667   UINTN                     FileStart;
    668   UINTN                     DevNumber = 0;
    669   EFI_OPEN_FILE_GUARD       *GuardFile;
    670   BOOLEAN                   VolumeNameMatch;
    671   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    672   UINTN                     Size;
    673   EFI_IP_ADDRESS            Ip;
    674   CHAR8                     *CwdPlusPathName;
    675   UINTN                     Index;
    676   EFI_SECTION_TYPE          ModifiedSectionType;
    677 
    678   EblUpdateDeviceLists ();
    679 
    680   File = &FileData;
    681   ZeroMem (File, sizeof (EFI_OPEN_FILE));
    682 
    683   StrLen = AsciiStrSize (PathName);
    684   if (StrLen <= 1) {
    685     // Smallest valid path is 1 char and a null
    686     return NULL;
    687   }
    688 
    689   for (FileStart = 0; FileStart < StrLen; FileStart++) {
    690     if (PathName[FileStart] == ':') {
    691       FileStart++;
    692       break;
    693     }
    694   }
    695 
    696   //
    697   // Matching volume name has precedence over handle based names
    698   //
    699   VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
    700   if (!VolumeNameMatch) {
    701     if (FileStart == StrLen) {
    702       // No Volume name or device name, so try Current Working Directory
    703       if (gCwd == NULL) {
    704         // No CWD
    705         return NULL;
    706       }
    707 
    708       // We could add a current working directory concept
    709       CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
    710       if (CwdPlusPathName == NULL) {
    711         return NULL;
    712       }
    713 
    714       if ((PathName[0] == '/') || (PathName[0] == '\\')) {
    715         // PathName starts in / so this means we go to the root of the device in the CWD.
    716         CwdPlusPathName[0] = '\0';
    717         for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
    718           CwdPlusPathName[FileStart] = gCwd[FileStart];
    719           if (gCwd[FileStart] == ':') {
    720             FileStart++;
    721             CwdPlusPathName[FileStart] = '\0';
    722             break;
    723           }
    724         }
    725       } else {
    726         AsciiStrCpy (CwdPlusPathName, gCwd);
    727         StrLen = AsciiStrLen (gCwd);
    728         if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
    729           AsciiStrCat (CwdPlusPathName, "\\");
    730         }
    731       }
    732 
    733       AsciiStrCat (CwdPlusPathName, PathName);
    734       if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
    735         // Extra error check to make sure we don't recurse and blow stack
    736         return NULL;
    737       }
    738 
    739       File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
    740       FreePool (CwdPlusPathName);
    741       return File;
    742     }
    743 
    744     DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
    745   }
    746 
    747   File->DeviceName = AllocatePool (StrLen);
    748   AsciiStrCpy (File->DeviceName, PathName);
    749   File->DeviceName[FileStart - 1] = '\0';
    750   File->FileName = &File->DeviceName[FileStart];
    751   if (File->FileName[0] == '\0') {
    752     // if it is just a file name use / as root
    753     File->FileName = "\\";
    754   }
    755 
    756   //
    757   // Use best match algorithm on the dev names so we only need to look at the
    758   // first few charters to match the full device name. Short name forms are
    759   // legal from the caller.
    760   //
    761   Status = EFI_SUCCESS;
    762   if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
    763     if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
    764       if (DevNumber >= mFsCount) {
    765         goto ErrorExit;
    766       }
    767       File->Type = EfiOpenFileSystem;
    768       File->EfiHandle = mFs[DevNumber];
    769       Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
    770 
    771     } else if (PathName[1] == 'v' || PathName[1] == 'V') {
    772       if (DevNumber >= mFvCount) {
    773         goto ErrorExit;
    774       }
    775       File->Type = EfiOpenFirmwareVolume;
    776       File->EfiHandle = mFv[DevNumber];
    777 
    778       if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
    779         // Skip leading / as its not really needed for the FV since no directories are supported
    780         FileStart++;
    781       }
    782 
    783       // Check for 2nd :
    784       ModifiedSectionType = SectionType;
    785       for (Index = FileStart; PathName[Index] != '\0'; Index++) {
    786         if (PathName[Index] == ':') {
    787           // Support fv0:\DxeCore:0x10
    788           // This means open the PE32 Section of the file
    789           ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
    790           PathName[Index] = '\0';
    791         }
    792       }
    793       File->FvSectionType = ModifiedSectionType;
    794       Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
    795     }
    796   } else if ((*PathName == 'A') || (*PathName == 'a')) {
    797     // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
    798     File->Type = EfiOpenMemoryBuffer;
    799     // 1st colon is at PathName[FileStart - 1]
    800     File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
    801 
    802     // Find 2nd colon
    803     while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
    804       FileStart++;
    805     }
    806 
    807     // If we ran out of string, there's no extra data
    808     if (PathName[FileStart] == '\0') {
    809       File->Size = 0;
    810     } else {
    811       File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
    812     }
    813 
    814     // if there's no number after the second colon, default
    815     // the end of memory
    816     if (File->Size == 0) {
    817       File->Size =  (UINTN)(0 - (UINTN)File->Buffer);
    818     }
    819 
    820     File->MaxPosition = File->Size;
    821     File->BaseOffset = (UINTN)File->Buffer;
    822 
    823   } else if (*PathName== 'l' || *PathName == 'L') {
    824     if (DevNumber >= mLoadFileCount) {
    825       goto ErrorExit;
    826     }
    827     File->Type = EfiOpenLoadFile;
    828     File->EfiHandle = mLoadFile[DevNumber];
    829 
    830     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
    831     if (EFI_ERROR (Status)) {
    832       goto ErrorExit;
    833     }
    834 
    835     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
    836     if (EFI_ERROR (Status)) {
    837       goto ErrorExit;
    838     }
    839     File->DevicePath = DuplicateDevicePath (DevicePath);
    840 
    841   } else if (*PathName == 'b' || *PathName == 'B') {
    842     // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
    843     if (DevNumber >= mBlkIoCount) {
    844       goto ErrorExit;
    845     }
    846     File->Type = EfiOpenBlockIo;
    847     File->EfiHandle = mBlkIo[DevNumber];
    848     EblFileDevicePath (File, "", OpenMode);
    849 
    850     // 1st colon is at PathName[FileStart - 1]
    851     File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
    852 
    853     // Find 2nd colon
    854     while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
    855       FileStart++;
    856     }
    857 
    858     // If we ran out of string, there's no extra data
    859     if (PathName[FileStart] == '\0') {
    860       Size = 0;
    861     } else {
    862       Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
    863     }
    864 
    865     // if a zero size is passed in (or the size is left out entirely),
    866     // go to the end of the device.
    867     if (Size == 0) {
    868       File->Size = File->Size - File->DiskOffset;
    869     } else {
    870       File->Size = Size;
    871     }
    872 
    873     File->MaxPosition = File->Size;
    874     File->BaseOffset = File->DiskOffset;
    875   } else if ((*PathName) >= '0' && (*PathName <= '9')) {
    876 
    877     // Get current IP address
    878     Status = EblGetCurrentIpAddress (&Ip);
    879     if (EFI_ERROR(Status)) {
    880       AsciiPrint("Device IP Address is not configured.\n");
    881       goto ErrorExit;
    882     }
    883 
    884 
    885     // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
    886     File->Type = EfiOpenTftp;
    887     File->IsDirty = FALSE;
    888     File->IsBufferValid = FALSE;
    889 
    890     Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
    891   }
    892 
    893   if (EFI_ERROR (Status)) {
    894     goto ErrorExit;
    895   }
    896 
    897   GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
    898   if (GuardFile == NULL) {
    899     goto ErrorExit;
    900   }
    901 
    902   GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
    903   CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
    904   GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
    905 
    906   return &(GuardFile->File);
    907 
    908 ErrorExit:
    909   FreePool (File->DeviceName);
    910   return NULL;
    911 }
    912 
    913 #define FILE_COPY_CHUNK 0x01000000
    914 
    915 EFI_STATUS
    916 EfiCopyFile (
    917   IN        CHAR8               *DestinationFile,
    918   IN        CHAR8               *SourceFile
    919   )
    920 {
    921   EFI_OPEN_FILE *Source      = NULL;
    922   EFI_OPEN_FILE *Destination = NULL;
    923   EFI_STATUS    Status       = EFI_SUCCESS;
    924   VOID          *Buffer      = NULL;
    925   UINTN         Size;
    926   UINTN         Offset;
    927   UINTN         Chunk = FILE_COPY_CHUNK;
    928 
    929   Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
    930   if (Source == NULL) {
    931     AsciiPrint("Source file open error.\n");
    932     Status = EFI_NOT_FOUND;
    933     goto Exit;
    934   }
    935 
    936   Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
    937   if (Destination == NULL) {
    938     AsciiPrint("Destination file open error.\n");
    939     Status = EFI_NOT_FOUND;
    940     goto Exit;
    941   }
    942 
    943   Buffer = AllocatePool(FILE_COPY_CHUNK);
    944   if (Buffer == NULL) {
    945     Status = EFI_OUT_OF_RESOURCES;
    946     goto Exit;
    947   }
    948 
    949   Size = EfiTell(Source, NULL);
    950 
    951   for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
    952     Chunk = FILE_COPY_CHUNK;
    953 
    954     Status = EfiRead(Source, Buffer, &Chunk);
    955     if (EFI_ERROR(Status)) {
    956       AsciiPrint("Read file error %r\n", Status);
    957       goto Exit;
    958     }
    959 
    960     Status = EfiWrite(Destination, Buffer, &Chunk);
    961     if (EFI_ERROR(Status)) {
    962       AsciiPrint("Write file error %r\n", Status);
    963       goto Exit;
    964     }
    965   }
    966 
    967   // Any left over?
    968   if (Offset < Size) {
    969     Chunk = Size - Offset;
    970 
    971     Status = EfiRead(Source, Buffer, &Chunk);
    972     if (EFI_ERROR(Status)) {
    973       AsciiPrint("Read file error\n");
    974       goto Exit;
    975     }
    976 
    977     Status = EfiWrite(Destination, Buffer, &Chunk);
    978     if (EFI_ERROR(Status)) {
    979       AsciiPrint("Write file error\n");
    980       goto Exit;
    981     }
    982   }
    983 
    984 Exit:
    985   if (Source != NULL) {
    986     Status = EfiClose(Source);
    987     if (EFI_ERROR(Status)) {
    988       AsciiPrint("Source close error");
    989     }
    990   }
    991 
    992   if (Destination != NULL) {
    993     Status = EfiClose(Destination);
    994     if (EFI_ERROR(Status)) {
    995       AsciiPrint("Destination close error");
    996     }
    997   }
    998 
    999   if (Buffer != NULL) {
   1000     FreePool(Buffer);
   1001   }
   1002 
   1003   return Status;
   1004 }
   1005 
   1006 /**
   1007 Use DeviceType and Index to form a valid PathName and try and open it.
   1008 
   1009 @param  DeviceType  Device type to open
   1010 @param  Index       Device Index to use. Zero relative.
   1011 
   1012 @return NULL  Open failed
   1013 @return Valid EFI_OPEN_FILE handle
   1014 
   1015 **/
   1016 EFI_OPEN_FILE  *
   1017 EfiDeviceOpenByType (
   1018   IN  EFI_OPEN_FILE_TYPE    DeviceType,
   1019   IN  UINTN                 Index
   1020   )
   1021 {
   1022   CHAR8   *DevStr;
   1023   CHAR8   Path[MAX_CMD_LINE];
   1024 
   1025   switch (DeviceType) {
   1026   case EfiOpenLoadFile:
   1027     DevStr = "loadfile%d:";
   1028     break;
   1029   case EfiOpenFirmwareVolume:
   1030     DevStr = "fv%d:";
   1031     break;
   1032   case EfiOpenFileSystem:
   1033     DevStr = "fs%d:";
   1034     break;
   1035   case EfiOpenBlockIo:
   1036     DevStr = "blk%d:";
   1037     break;
   1038   case EfiOpenMemoryBuffer:
   1039     DevStr = "a%d:";
   1040     break;
   1041   default:
   1042     return NULL;
   1043   }
   1044 
   1045   AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
   1046 
   1047   return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
   1048 }
   1049 
   1050 
   1051 /**
   1052 Close a file handle opened by EfiOpen() and free all resources allocated by
   1053 EfiOpen().
   1054 
   1055 @param  Stream    Open File Handle
   1056 
   1057 @return EFI_INVALID_PARAMETER  Stream is not an Open File
   1058 @return EFI_SUCCESS            Steam closed
   1059 
   1060 **/
   1061 EFI_STATUS
   1062 EfiClose (
   1063   IN  EFI_OPEN_FILE     *File
   1064   )
   1065 {
   1066   EFI_STATUS          Status;
   1067   UINT64              TftpBufferSize;
   1068 
   1069   if (!FileHandleValid (File)) {
   1070     return EFI_INVALID_PARAMETER;
   1071   }
   1072 
   1073   //Write the buffer contents to TFTP file.
   1074   if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
   1075 
   1076     TftpBufferSize = File->Size;
   1077     Status = EblMtftp (
   1078       EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
   1079       File->Buffer,
   1080       TRUE,
   1081       &TftpBufferSize,
   1082       NULL,
   1083       &File->ServerIp,
   1084       (UINT8 *)File->FileName,
   1085       NULL,
   1086       FALSE
   1087       );
   1088     if (EFI_ERROR(Status)) {
   1089       AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
   1090       return Status;
   1091     }
   1092   }
   1093 
   1094   if ((File->Type == EfiOpenLoadFile) ||
   1095     ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
   1096     ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
   1097     EblFreePool(File->Buffer);
   1098   }
   1099 
   1100   EblFreePool (File->DevicePath);
   1101   EblFreePool (File->DeviceName);
   1102   EblFreePool (File->FsFileInfo);
   1103   EblFreePool (File->FsInfo);
   1104 
   1105   if (File->FsFileHandle != NULL) {
   1106     File->FsFileHandle->Close (File->FsFileHandle);
   1107   }
   1108 
   1109   // Need to free File and it's Guard structures
   1110   EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
   1111   return EFI_SUCCESS;
   1112 }
   1113 
   1114 
   1115 /**
   1116 Return the size of the file represented by Stream. Also return the current
   1117 Seek position. Opening a file will enable a valid file size to be returned.
   1118 LoadFile is an exception as a load file size is set to zero.
   1119 
   1120 @param  Stream    Open File Handle
   1121 
   1122 @return 0         Stream is not an Open File or a valid LoadFile handle
   1123 
   1124 **/
   1125 UINTN
   1126 EfiTell (
   1127   IN  EFI_OPEN_FILE     *File,
   1128   OUT EFI_LBA           *CurrentPosition    OPTIONAL
   1129   )
   1130 {
   1131   EFI_STATUS Status;
   1132   UINT64     BufferSize = 0;
   1133 
   1134   if (!FileHandleValid (File)) {
   1135     return 0;
   1136   }
   1137 
   1138   if (CurrentPosition != NULL) {
   1139     *CurrentPosition = File->CurrentPosition;
   1140   }
   1141 
   1142   if (File->Type == EfiOpenLoadFile) {
   1143     // Figure out the File->Size
   1144     File->Buffer = NULL;
   1145     File->Size   = 0;
   1146     Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
   1147     if (Status != EFI_BUFFER_TOO_SMALL) {
   1148       return 0;
   1149     }
   1150 
   1151     File->MaxPosition = (UINT64)File->Size;
   1152   } else if (File->Type == EfiOpenTftp) {
   1153 
   1154     Status = EblMtftp (
   1155       EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
   1156       NULL,
   1157       FALSE,
   1158       &BufferSize,
   1159       NULL,
   1160       &File->ServerIp,
   1161       (UINT8 *)File->FileName,
   1162       NULL,
   1163       TRUE
   1164       );
   1165     if (EFI_ERROR(Status)) {
   1166       AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
   1167       return 0;
   1168     }
   1169 
   1170     File->Size        = (UINTN)BufferSize;
   1171     File->MaxPosition = File->Size;
   1172   }
   1173 
   1174   return File->Size;
   1175 }
   1176 
   1177 
   1178 /**
   1179 Seek to the Offset location in the file. LoadFile and FV device types do
   1180 not support EfiSeek(). It is not possible to grow the file size using
   1181 EfiSeek().
   1182 
   1183 SeekType defines how use Offset to calculate the new file position:
   1184 EfiSeekStart  : Position = Offset
   1185 EfiSeekCurrent: Position is Offset bytes from the current position
   1186 EfiSeekEnd    : Only supported if Offset is zero to seek to end of file.
   1187 
   1188 @param  Stream    Open File Handle
   1189 @param  Offset    Offset to seek too.
   1190 @param  SeekType  Type of seek to perform
   1191 
   1192 
   1193 @return EFI_INVALID_PARAMETER  Stream is not an Open File
   1194 @return EFI_UNSUPPORTED        LoadFile and FV do not support Seek
   1195 @return EFI_NOT_FOUND          Seek past the end of the file.
   1196 @return EFI_SUCCESS            Steam closed
   1197 
   1198 **/
   1199 EFI_STATUS
   1200 EfiSeek (
   1201   IN  EFI_OPEN_FILE     *File,
   1202   IN  EFI_LBA           Offset,
   1203   IN  EFI_SEEK_TYPE     SeekType
   1204   )
   1205 {
   1206   EFI_STATUS    Status;
   1207   UINT64        CurrentPosition;
   1208 
   1209   if (!FileHandleValid (File)) {
   1210     return EFI_INVALID_PARAMETER;
   1211   }
   1212 
   1213   if (File->Type == EfiOpenLoadFile) {
   1214     // LoadFile does not support Seek
   1215     return EFI_UNSUPPORTED;
   1216   }
   1217 
   1218   CurrentPosition = File->CurrentPosition;
   1219   switch (SeekType) {
   1220   case EfiSeekStart:
   1221     if (Offset > File->MaxPosition) {
   1222       return EFI_NOT_FOUND;
   1223     }
   1224     CurrentPosition = Offset;
   1225     break;
   1226 
   1227   case EfiSeekCurrent:
   1228     if ((File->CurrentPosition + Offset) > File->MaxPosition) {
   1229       return EFI_NOT_FOUND;
   1230     }
   1231     CurrentPosition += Offset;
   1232     break;
   1233 
   1234   case EfiSeekEnd:
   1235     if (Offset != 0) {
   1236       // We don't support growing file size via seeking past end of file
   1237       return EFI_UNSUPPORTED;
   1238     }
   1239     CurrentPosition = File->MaxPosition;
   1240     break;
   1241 
   1242   default:
   1243     return EFI_NOT_FOUND;
   1244   }
   1245 
   1246   Status = EFI_SUCCESS;
   1247   if (File->FsFileHandle != NULL) {
   1248     Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
   1249   }
   1250 
   1251   if (!EFI_ERROR (Status)) {
   1252     File->CurrentPosition = CurrentPosition;
   1253   }
   1254 
   1255   return Status;
   1256 }
   1257 
   1258 EFI_STATUS
   1259 CacheTftpFile (
   1260   IN OUT  EFI_OPEN_FILE *File
   1261   )
   1262 {
   1263   EFI_STATUS          Status;
   1264   UINT64              TftpBufferSize;
   1265 
   1266   if (File->IsBufferValid) {
   1267     return EFI_SUCCESS;
   1268   }
   1269 
   1270   // Make sure the file size is set.
   1271   EfiTell (File, NULL);
   1272 
   1273   //Allocate a buffer to hold the whole file.
   1274   File->Buffer = AllocatePool(File->Size);
   1275   if (File->Buffer == NULL) {
   1276     return EFI_OUT_OF_RESOURCES;
   1277   }
   1278 
   1279   TftpBufferSize = File->Size;
   1280 
   1281   Status = EblMtftp (
   1282     EFI_PXE_BASE_CODE_TFTP_READ_FILE,
   1283     File->Buffer,
   1284     FALSE,
   1285     &TftpBufferSize,
   1286     NULL,
   1287     &File->ServerIp,
   1288     (UINT8 *)File->FileName,
   1289     NULL,
   1290     FALSE);
   1291   if (EFI_ERROR(Status)) {
   1292     AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
   1293     FreePool(File->Buffer);
   1294     return Status;
   1295   }
   1296 
   1297   // Set the buffer valid flag.
   1298   File->IsBufferValid = TRUE;
   1299 
   1300   return Status;
   1301 }
   1302 
   1303 /**
   1304 Read BufferSize bytes from the current location in the file. For load file,
   1305 FV, and TFTP case you must read the entire file.
   1306 
   1307 @param  Stream      Open File Handle
   1308 @param  Buffer      Caller allocated buffer.
   1309 @param  BufferSize  Size of buffer in bytes.
   1310 
   1311 
   1312 @return EFI_SUCCESS           Stream is not an Open File
   1313 @return EFI_END_OF_FILE Tried to read past the end of the file
   1314 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1315 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1316 @return "other"               Error returned from device read
   1317 
   1318 **/
   1319 EFI_STATUS
   1320 EfiRead (
   1321   IN  EFI_OPEN_FILE       *File,
   1322   OUT VOID                *Buffer,
   1323   OUT UINTN               *BufferSize
   1324   )
   1325 {
   1326   EFI_STATUS            Status;
   1327   UINT32                AuthenticationStatus;
   1328   EFI_DISK_IO_PROTOCOL  *DiskIo;
   1329 
   1330   if (!FileHandleValid (File)) {
   1331     return EFI_INVALID_PARAMETER;
   1332   }
   1333 
   1334   // Don't read past the end of the file.
   1335   if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1336     return EFI_END_OF_FILE;
   1337   }
   1338 
   1339   switch (File->Type) {
   1340   case EfiOpenLoadFile:
   1341     // Figure out the File->Size
   1342     EfiTell (File, NULL);
   1343 
   1344     Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
   1345     break;
   1346 
   1347   case EfiOpenFirmwareVolume:
   1348     if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
   1349       // This is the entire FV device, so treat like a memory buffer
   1350       CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
   1351       File->CurrentPosition += *BufferSize;
   1352       Status = EFI_SUCCESS;
   1353     } else {
   1354       if (File->Buffer == NULL) {
   1355         if (File->FvSectionType == EFI_SECTION_ALL) {
   1356           Status = File->Fv->ReadFile (
   1357             File->Fv,
   1358             &File->FvNameGuid,
   1359             (VOID **)&File->Buffer,
   1360             &File->Size,
   1361             &File->FvType,
   1362             &File->FvAttributes,
   1363             &AuthenticationStatus
   1364             );
   1365         } else {
   1366           Status = File->Fv->ReadSection (
   1367             File->Fv,
   1368             &File->FvNameGuid,
   1369             File->FvSectionType,
   1370             0,
   1371             (VOID **)&File->Buffer,
   1372             &File->Size,
   1373             &AuthenticationStatus
   1374             );
   1375         }
   1376         if (EFI_ERROR (Status)) {
   1377           return Status;
   1378         }
   1379         File->IsBufferValid = TRUE;
   1380       }
   1381       // Operate on the cached buffer so Seek will work
   1382       CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1383       File->CurrentPosition += *BufferSize;
   1384       Status = EFI_SUCCESS;
   1385     }
   1386     break;
   1387 
   1388   case EfiOpenMemoryBuffer:
   1389     CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1390     File->CurrentPosition += *BufferSize;
   1391     Status = EFI_SUCCESS;
   1392     break;
   1393 
   1394   case EfiOpenFileSystem:
   1395     Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
   1396     File->CurrentPosition += *BufferSize;
   1397     break;
   1398 
   1399   case EfiOpenBlockIo:
   1400     Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
   1401     if (!EFI_ERROR(Status)) {
   1402       Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
   1403     }
   1404     File->CurrentPosition += *BufferSize;
   1405     break;
   1406 
   1407   case EfiOpenTftp:
   1408     // Cache the file if it hasn't been cached yet.
   1409     if (File->IsBufferValid == FALSE) {
   1410       Status = CacheTftpFile (File);
   1411       if (EFI_ERROR (Status)) {
   1412         return Status;
   1413       }
   1414     }
   1415 
   1416     // Copy out the requested data
   1417     CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
   1418     File->CurrentPosition += *BufferSize;
   1419 
   1420     Status = EFI_SUCCESS;
   1421     break;
   1422 
   1423   default:
   1424     return EFI_INVALID_PARAMETER;
   1425   };
   1426 
   1427   return Status;
   1428 }
   1429 
   1430 
   1431 /**
   1432 Read the entire file into a buffer. This routine allocates the buffer and
   1433 returns it to the user full of the read data.
   1434 
   1435 This is very useful for load file where it's hard to know how big the buffer
   1436 must be.
   1437 
   1438 @param  Stream      Open File Handle
   1439 @param  Buffer      Pointer to buffer to return.
   1440 @param  BufferSize  Pointer to Size of buffer return..
   1441 
   1442 
   1443 @return EFI_SUCCESS           Stream is not an Open File
   1444 @return EFI_END_OF_FILE       Tried to read past the end of the file
   1445 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1446 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1447 @return "other"               Error returned from device read
   1448 
   1449 **/
   1450 EFI_STATUS
   1451 EfiReadAllocatePool (
   1452   IN  EFI_OPEN_FILE     *File,
   1453   OUT VOID              **Buffer,
   1454   OUT UINTN             *BufferSize
   1455   )
   1456 {
   1457   if (!FileHandleValid (File)) {
   1458     return EFI_INVALID_PARAMETER;
   1459   }
   1460 
   1461   // Loadfile defers file size determination on Open so use tell to find it
   1462   EfiTell (File, NULL);
   1463 
   1464   *BufferSize = File->Size;
   1465   *Buffer = AllocatePool (*BufferSize);
   1466   if (*Buffer == NULL) {
   1467     return EFI_NOT_FOUND;
   1468   }
   1469 
   1470   return EfiRead (File, *Buffer, BufferSize);
   1471 }
   1472 
   1473 
   1474 /**
   1475 Write data back to the file. For TFTP case you must write the entire file.
   1476 
   1477 @param  Stream      Open File Handle
   1478 @param  Buffer      Pointer to buffer to return.
   1479 @param  BufferSize  Pointer to Size of buffer return..
   1480 
   1481 
   1482 @return EFI_SUCCESS           Stream is not an Open File
   1483 @return EFI_END_OF_FILE       Tried to read past the end of the file
   1484 @return EFI_INVALID_PARAMETER Stream is not an open file handle
   1485 @return EFI_BUFFER_TOO_SMALL  Buffer is not big enough to do the read
   1486 @return "other"               Error returned from device write
   1487 
   1488 **/
   1489 EFI_STATUS
   1490 EfiWrite (
   1491   IN  EFI_OPEN_FILE   *File,
   1492   OUT VOID            *Buffer,
   1493   OUT UINTN           *BufferSize
   1494   )
   1495 {
   1496   EFI_STATUS              Status;
   1497   EFI_FV_WRITE_FILE_DATA  FileData;
   1498   EFI_DISK_IO_PROTOCOL    *DiskIo;
   1499 
   1500   if (!FileHandleValid (File)) {
   1501     return EFI_INVALID_PARAMETER;
   1502   }
   1503 
   1504   switch (File->Type) {
   1505   case EfiOpenMemoryBuffer:
   1506     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1507       return EFI_END_OF_FILE;
   1508     }
   1509 
   1510     CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
   1511     File->CurrentPosition += *BufferSize;
   1512     Status = EFI_SUCCESS;
   1513 
   1514   case EfiOpenLoadFile:
   1515     // LoadFile device is read only be definition
   1516     Status = EFI_UNSUPPORTED;
   1517 
   1518   case EfiOpenFirmwareVolume:
   1519     if (File->FvSectionType != EFI_SECTION_ALL) {
   1520       // Writes not support to a specific section. You have to update entire file
   1521       return EFI_UNSUPPORTED;
   1522     }
   1523 
   1524     FileData.NameGuid       = &(File->FvNameGuid);
   1525     FileData.Type           = File->FvType;
   1526     FileData.FileAttributes = File->FvAttributes;
   1527     FileData.Buffer         = Buffer;
   1528     FileData.BufferSize     = (UINT32)*BufferSize;
   1529     Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
   1530     break;
   1531 
   1532   case EfiOpenFileSystem:
   1533     Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
   1534     File->CurrentPosition += *BufferSize;
   1535     break;
   1536 
   1537   case EfiOpenBlockIo:
   1538     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1539       return EFI_END_OF_FILE;
   1540     }
   1541 
   1542     Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
   1543     if (!EFI_ERROR(Status)) {
   1544       Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
   1545     }
   1546     File->CurrentPosition += *BufferSize;
   1547     break;
   1548 
   1549   case EfiOpenTftp:
   1550     // Cache the file if it hasn't been cached yet.
   1551     if (File->IsBufferValid == FALSE) {
   1552       Status = CacheTftpFile(File);
   1553       if (EFI_ERROR(Status)) {
   1554         return Status;
   1555       }
   1556     }
   1557 
   1558     // Don't overwrite the buffer
   1559     if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
   1560       UINT8 *TempBuffer;
   1561 
   1562       TempBuffer = File->Buffer;
   1563 
   1564       File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
   1565       if (File->Buffer == NULL) {
   1566         return EFI_OUT_OF_RESOURCES;
   1567       }
   1568 
   1569       CopyMem (File->Buffer, TempBuffer, File->Size);
   1570 
   1571       FreePool (TempBuffer);
   1572 
   1573       File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
   1574       File->MaxPosition = (UINT64)File->Size;
   1575     }
   1576 
   1577     // Copy in the requested data
   1578     CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
   1579     File->CurrentPosition += *BufferSize;
   1580 
   1581     // Mark the file dirty
   1582     File->IsDirty = TRUE;
   1583 
   1584     Status = EFI_SUCCESS;
   1585     break;
   1586 
   1587   default:
   1588     Status = EFI_INVALID_PARAMETER;
   1589   };
   1590 
   1591   return Status;
   1592 }
   1593 
   1594 
   1595 /**
   1596 Given Cwd expand Path to remove .. and replace them with real
   1597 directory names.
   1598 
   1599 @param  Cwd     Current Working Directory
   1600 @param  Path    Path to expand
   1601 
   1602 @return NULL     Cwd or Path are not valid
   1603 @return 'other'  Path with .. expanded
   1604 
   1605 **/
   1606 CHAR8 *
   1607 ExpandPath (
   1608   IN CHAR8    *Cwd,
   1609   IN CHAR8    *Path
   1610   )
   1611 {
   1612   CHAR8   *NewPath;
   1613   CHAR8   *Work, *Start, *End;
   1614   UINTN   StrLen;
   1615   INTN    i;
   1616 
   1617   if (Cwd == NULL || Path == NULL) {
   1618     return NULL;
   1619   }
   1620 
   1621   StrLen = AsciiStrSize (Cwd);
   1622   if (StrLen <= 2) {
   1623     // Smallest valid path is 1 char and a null
   1624     return NULL;
   1625   }
   1626 
   1627   StrLen = AsciiStrSize (Path);
   1628   NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
   1629   if (NewPath == NULL) {
   1630     return NULL;
   1631   }
   1632   AsciiStrCpy (NewPath, Cwd);
   1633 
   1634   End = Path + StrLen;
   1635   for (Start = Path ;;) {
   1636     Work = AsciiStrStr (Start, "..") ;
   1637     if (Work == NULL) {
   1638       // Remaining part of Path contains no more ..
   1639       break;
   1640     }
   1641 
   1642     // append path prior to ..
   1643     AsciiStrnCat (NewPath, Start, Work - Start);
   1644     StrLen = AsciiStrLen (NewPath);
   1645     for (i = StrLen; i >= 0; i--) {
   1646       if (NewPath[i] == ':') {
   1647         // too many ..
   1648         return NULL;
   1649       }
   1650       if (NewPath[i] == '/' || NewPath[i] == '\\') {
   1651         if ((i > 0) && (NewPath[i-1] == ':')) {
   1652           // leave the / before a :
   1653           NewPath[i+1] = '\0';
   1654         } else {
   1655           // replace / will Null to remove trailing file/dir reference
   1656           NewPath[i] = '\0';
   1657         }
   1658         break;
   1659       }
   1660     }
   1661 
   1662     Start = Work + 3;
   1663   }
   1664 
   1665   // Handle the path that remains after the ..
   1666   AsciiStrnCat (NewPath, Start, End - Start);
   1667 
   1668   return NewPath;
   1669 }
   1670 
   1671 
   1672 /**
   1673 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
   1674 the path does not contain a device name, The CWD is prepended to the path.
   1675 
   1676 @param  Cwd     Current Working Directory to set
   1677 
   1678 
   1679 @return EFI_SUCCESS           CWD is set
   1680 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
   1681 
   1682 **/
   1683 EFI_STATUS
   1684 EfiSetCwd (
   1685   IN  CHAR8   *Cwd
   1686   )
   1687 {
   1688   EFI_OPEN_FILE *File;
   1689   UINTN         Len;
   1690   CHAR8         *Path;
   1691 
   1692   if (Cwd == NULL) {
   1693     return EFI_INVALID_PARAMETER;
   1694   }
   1695 
   1696   if (AsciiStrCmp (Cwd, ".") == 0) {
   1697     // cd . is a no-op
   1698     return EFI_SUCCESS;
   1699   }
   1700 
   1701   Path = Cwd;
   1702   if (AsciiStrStr (Cwd, "..") != NULL) {
   1703     if (gCwd == NULL) {
   1704       // no parent
   1705       return EFI_SUCCESS;
   1706     }
   1707 
   1708     Len = AsciiStrLen (gCwd);
   1709     if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
   1710       // parent is device so nothing to do
   1711       return EFI_SUCCESS;
   1712     }
   1713 
   1714     // Expand .. in Cwd, given we know current working directory
   1715     Path = ExpandPath (gCwd, Cwd);
   1716     if (Path == NULL) {
   1717       return EFI_NOT_FOUND;
   1718     }
   1719   }
   1720 
   1721   File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
   1722   if (File == NULL) {
   1723     return EFI_INVALID_PARAMETER;
   1724   }
   1725 
   1726   if (gCwd != NULL) {
   1727     FreePool (gCwd);
   1728   }
   1729 
   1730   // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
   1731   // relative to the current gCwd or not.
   1732   gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
   1733   if (gCwd == NULL) {
   1734     return EFI_INVALID_PARAMETER;
   1735   }
   1736 
   1737   AsciiStrCpy (gCwd, File->DeviceName);
   1738   if (File->FileName == NULL) {
   1739     AsciiStrCat (gCwd, ":\\");
   1740   } else {
   1741     AsciiStrCat (gCwd, ":");
   1742     AsciiStrCat (gCwd, File->FileName);
   1743   }
   1744 
   1745 
   1746   EfiClose (File);
   1747   if (Path != Cwd) {
   1748     FreePool (Path);
   1749   }
   1750   return EFI_SUCCESS;
   1751 }
   1752 
   1753 
   1754 /**
   1755 Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
   1756 the path does not contain a device name, The CWD is prepended to the path.
   1757 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
   1758 a call to EfiSetCwd() it is not legal to use the pointer returned by
   1759 this function.
   1760 
   1761 @param  Cwd     Current Working Directory
   1762 
   1763 
   1764 @return ""      No CWD set
   1765 @return 'other' Returns buffer that contains CWD.
   1766 
   1767 **/
   1768 CHAR8 *
   1769 EfiGetCwd (
   1770   VOID
   1771   )
   1772 {
   1773   if (gCwd == NULL) {
   1774     return "";
   1775   }
   1776   return gCwd;
   1777 }
   1778 
   1779 
   1780