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