2 File IO routines inspired by Streams with an EFI flavor
4 Copyright (c) 2007, Intel Corporation<BR>
5 Portions copyright (c) 2008-2009, Apple Inc. All rights reserved.
7 All rights reserved. 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
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.
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
20 Device names are case insensative and only check the leading characters for
21 unique matches. Thus the following are all the same:
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
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 #include <Guid/FileInfo.h>
44 #include <Library/BaseLib.h>
45 #include <Library/MemoryAllocationLib.h>
46 #include <Library/DevicePathLib.h>
47 #include <Library/PrintLib.h>
48 #include <Library/BaseMemoryLib.h>
49 #include <Library/UefiLib.h>
50 #include <Library/UefiBootServicesTableLib.h>
51 #include <Library/UefiRuntimeServicesTableLib.h>
52 #include <Library/DebugLib.h>
53 #include <Library/EfiFileLib.h>
54 #include <Library/PcdLib.h>
55 #include <Library/EblNetworkLib.h>
61 #define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
62 #define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
64 // Need to defend against this overflowing
65 #define MAX_CMD_LINE 0x200
71 } EFI_OPEN_FILE_GUARD
;
74 // globals to store current open device info
75 EFI_HANDLE
*mBlkIo
= NULL
;
76 UINTN mBlkIoCount
= 0;
78 EFI_HANDLE
*mFs
= NULL
;
80 // mFsInfo[] array entries must match mFs[] handles
81 EFI_FILE_SYSTEM_INFO
**mFsInfo
= NULL
;
83 EFI_HANDLE
*mFv
= NULL
;
85 EFI_HANDLE
*mLoadFile
= NULL
;
86 UINTN mLoadFileCount
= 0;
91 Internal worker function to validate a File handle.
93 @param File Open File Handle
95 @return TRUE File is valid
96 @return FALSE File is not valid
102 IN EFI_OPEN_FILE
*File
105 EFI_OPEN_FILE_GUARD
*GuardFile
;
107 // Look right before and after file structure for the correct signatures
108 GuardFile
= BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
);
109 if ((GuardFile
->Header
!= EFI_OPEN_FILE_GUARD_HEADER
) ||
110 (GuardFile
->Footer
!= EFI_OPEN_FILE_GUARD_FOOTER
) ) {
118 Internal worker function. If Buffer is not NULL free it.
120 @param Buffer Buffer to FreePool()
128 if (Buffer
!= NULL
) {
134 Update Device List Global Variables
138 EblUpdateDeviceLists (
144 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
145 EFI_FILE_HANDLE Root
;
148 if (mBlkIo
!= NULL
) {
151 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiBlockIoProtocolGuid
, NULL
, &mBlkIoCount
, &mBlkIo
);
156 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiFirmwareVolume2ProtocolGuid
, NULL
, &mFvCount
, &mFv
);
158 if (mLoadFile
!= NULL
) {
159 FreePool (mLoadFile
);
161 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiLoadFileProtocolGuid
, NULL
, &mLoadFileCount
, &mLoadFile
);
167 if (&mFsInfo
[0] != NULL
) {
168 // Need to Free the mFsInfo prior to reclaculating mFsCount so don't move this code
169 for (Index
= 0; Index
< mFsCount
; Index
++) {
170 if (mFsInfo
[Index
] != NULL
) {
171 FreePool (mFsInfo
[Index
]);
177 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiSimpleFileSystemProtocolGuid
, NULL
, &mFsCount
, &mFs
);
180 mFsInfo
= AllocateZeroPool (mFsCount
* sizeof (EFI_FILE_SYSTEM_INFO
*));
181 if (mFsInfo
== NULL
) {
182 // If we can't do this then we can't support file system entries
185 // Loop through all the file system structures and cache the file system info data
186 for (Index
=0; Index
< mFsCount
; Index
++) {
187 Status
= gBS
->HandleProtocol (mFs
[Index
], &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
188 if (!EFI_ERROR (Status
)) {
189 Status
= Fs
->OpenVolume (Fs
, &Root
);
190 if (!EFI_ERROR (Status
)) {
191 // Get information about the volume
193 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
194 if (Status
== EFI_BUFFER_TOO_SMALL
) {
195 mFsInfo
[Index
] = AllocatePool (Size
);
196 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
208 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
209 Return TRUE if the <devce name> prefix of PathName matches a file system
210 Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
211 and it can be used with mFs[] to find the handle that needs to be opened
213 @param PathName PathName to check
214 @param FileStart Index of the first character of the <path>
215 @param MatchIndex Index in mFsInfo[] that matches
217 @return TRUE PathName matches a Volume Label and MatchIndex is valid
218 @return FALSE PathName does not match a Volume Label MatchIndex undefined
225 OUT UINTN
*MatchIndex
233 for (Index
=0; Index
< mFsCount
; Index
++) {
234 if (mFsInfo
[Index
] == NULL
) {
235 // FsInfo is not valid so skip it
238 VolStrLen
= StrLen (mFsInfo
[Index
]->VolumeLabel
);
239 for (Compare
= 0, Match
= TRUE
; Compare
< (FileStart
- 1); Compare
++) {
240 if (Compare
> VolStrLen
) {
244 if (PathName
[Compare
] != (CHAR8
)mFsInfo
[Index
]->VolumeLabel
[Compare
]) {
245 // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
246 if (!((PathName
[Compare
] == '_') && (mFsInfo
[Index
]->VolumeLabel
[Compare
] == L
' '))) {
263 Return the number of devices of the current type active in the system
265 @param Type Device type to check
267 @return 0 Invalid type
272 IN EFI_OPEN_FILE_TYPE DeviceType
275 switch (DeviceType
) {
276 case EfiOpenLoadFile
:
277 return mLoadFileCount
;
278 case EfiOpenFirmwareVolume
:
280 case EfiOpenFileSystem
:
290 ConvertIpStringToEfiIp (
292 OUT EFI_IP_ADDRESS
*ServerIp
298 ServerIp
->v4
.Addr
[0] = (UINT8
)AsciiStrDecimalToUintn (Str
);
300 Str
= AsciiStrStr (Str
, ".");
302 return EFI_DEVICE_ERROR
;
305 ServerIp
->v4
.Addr
[1] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
307 Str
= AsciiStrStr (Str
, ".");
309 return EFI_DEVICE_ERROR
;
312 ServerIp
->v4
.Addr
[2] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
314 Str
= AsciiStrStr (Str
, ".");
316 return EFI_DEVICE_ERROR
;
319 ServerIp
->v4
.Addr
[3] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
326 Internal work function to extract a device number from a string skipping
327 text. Easy way to extract numbers from strings like blk7:.
329 @param Str String to extract device number form
331 @return -1 Device string is not valid
336 EblConvertDevStringToNumber (
344 // Find the first digit
345 Max
= AsciiStrLen (Str
);
346 for (Index
= 0; !((*Str
>= '0') && (*Str
<= '9')) && (Index
< Max
); Index
++) {
353 return AsciiStrDecimalToUintn (Str
);
358 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
360 @param File Open file handle
361 @param FileName Name of file after device stripped off
367 IN OUT EFI_OPEN_FILE
*File
,
369 IN CONST UINT64 OpenMode
374 FILEPATH_DEVICE_PATH
*FilePath
;
375 EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
;
376 CHAR16 UnicodeFileName
[MAX_PATHNAME
];
377 EFI_BLOCK_IO_PROTOCOL
*BlkIo
;
378 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
379 EFI_FILE_HANDLE Root
;
382 if ( *FileName
!= 0 ) {
383 AsciiStrToUnicodeStr (FileName
, UnicodeFileName
);
385 AsciiStrToUnicodeStr ("\\", UnicodeFileName
);
388 Size
= StrSize (UnicodeFileName
);
389 FileDevicePath
= AllocatePool (Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
));
390 if (FileDevicePath
!= NULL
) {
391 FilePath
= (FILEPATH_DEVICE_PATH
*) FileDevicePath
;
392 FilePath
->Header
.Type
= MEDIA_DEVICE_PATH
;
393 FilePath
->Header
.SubType
= MEDIA_FILEPATH_DP
;
394 CopyMem (&FilePath
->PathName
, UnicodeFileName
, Size
);
395 SetDevicePathNodeLength (&FilePath
->Header
, Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
);
396 SetDevicePathEndNode (NextDevicePathNode (&FilePath
->Header
));
398 if (File
->EfiHandle
!= NULL
) {
399 File
->DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
402 File
->DevicePath
= AppendDevicePath (File
->DevicePath
, FileDevicePath
);
403 FreePool (FileDevicePath
);
406 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlkIo
);
407 if (!EFI_ERROR (Status
)) {
408 CopyMem (&File
->FsBlockIoMedia
, BlkIo
->Media
, sizeof (EFI_BLOCK_IO_MEDIA
));
410 // If we are not opening the device this will get over written with file info
411 File
->MaxPosition
= MultU64x32 (BlkIo
->Media
->LastBlock
+ 1, BlkIo
->Media
->BlockSize
);
414 if (File
->Type
== EfiOpenFileSystem
) {
415 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
416 if (!EFI_ERROR (Status
)) {
417 Status
= Fs
->OpenVolume (Fs
, &Root
);
418 if (!EFI_ERROR (Status
)) {
419 // Get information about the volume
421 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
422 if (Status
== EFI_BUFFER_TOO_SMALL
) {
423 File
->FsInfo
= AllocatePool (Size
);
424 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
427 // Get information about the file
428 Status
= Root
->Open (Root
, &File
->FsFileHandle
, UnicodeFileName
, OpenMode
, 0);
429 if (!EFI_ERROR (Status
)) {
431 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, NULL
);
432 if (Status
== EFI_BUFFER_TOO_SMALL
) {
433 File
->FsFileInfo
= AllocatePool (Size
);
434 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, File
->FsFileInfo
);
435 if (!EFI_ERROR (Status
)) {
436 File
->Size
= (UINTN
)File
->FsFileInfo
->FileSize
;
437 File
->MaxPosition
= (UINT64
)File
->Size
;
445 } else if (File
->Type
== EfiOpenBlockIo
) {
446 File
->Size
= (UINTN
)File
->MaxPosition
;
452 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
455 CompareGuidToString (
464 AsciiSPrint (AsciiGuid
, sizeof(AsciiGuid
), "%g", Guid
);
469 while ((*StringPtr
!= '\0') && (*GuidPtr
!= '\0')) {
471 if (*StringPtr
== '-') {
476 if (*GuidPtr
== '-') {
481 if (ToUpper(*StringPtr
) != ToUpper(*GuidPtr
)) {
482 return EFI_NOT_FOUND
;
494 Internal work function to fill in EFI_OPEN_FILE information for the FV
496 @param File Open file handle
497 @param FileName Name of file after device stripped off
502 EblFvFileDevicePath (
503 IN OUT EFI_OPEN_FILE
*File
,
505 IN CONST UINT64 OpenMode
509 EFI_STATUS GetNextFileStatus
;
510 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode
;
511 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
513 UINT32 AuthenticationStatus
;
514 CHAR8 AsciiSection
[MAX_PATHNAME
];
517 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
520 UINTN NumberOfBlocks
;
523 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolume2ProtocolGuid
, (VOID
**)&File
->Fv
);
524 if (EFI_ERROR (Status
)) {
528 DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
530 if (*FileName
== '\0') {
531 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
535 File
->FvType
= EFI_FV_FILETYPE_ALL
;
536 GetNextFileStatus
= File
->Fv
->GetNextFile (
544 if (!EFI_ERROR (GetNextFileStatus
)) {
547 // Compare GUID first
548 Status
= CompareGuidToString (&File
->FvNameGuid
, FileName
);
549 if (!EFI_ERROR(Status
)) {
553 Status
= File
->Fv
->ReadSection (
556 EFI_SECTION_USER_INTERFACE
,
560 &AuthenticationStatus
562 if (!EFI_ERROR (Status
)) {
563 UnicodeStrToAsciiStr (Section
, AsciiSection
);
564 if (AsciiStriCmp (FileName
, AsciiSection
) == 0) {
571 } while (!EFI_ERROR (GetNextFileStatus
));
573 if (EFI_ERROR (GetNextFileStatus
)) {
574 return GetNextFileStatus
;
577 File
->MaxPosition
= File
->Size
;
578 EfiInitializeFwVolDevicepathNode (&DevicePathNode
, &File
->FvNameGuid
);
579 File
->DevicePath
= AppendDevicePathNode (DevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*)&DevicePathNode
);
583 // Get FVB Info about the handle
584 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolumeBlockProtocolGuid
, (VOID
**)&Fvb
);
585 if (!EFI_ERROR (Status
)) {
586 Status
= Fvb
->GetPhysicalAddress (Fvb
, &File
->FvStart
);
587 if (!EFI_ERROR (Status
)) {
588 for (Lba
= 0, File
->FvSize
= 0; ; File
->FvSize
+= (BlockSize
* NumberOfBlocks
), Lba
+= NumberOfBlocks
) {
589 Status
= Fvb
->GetBlockSize (Fvb
, Lba
, &BlockSize
, &NumberOfBlocks
);
590 if (EFI_ERROR (Status
)) {
597 // FVB not required if FV was soft loaded...
605 Open a device named by PathName. The PathName includes a device name and
606 path seperated by a :. See file header for more details on the PathName
607 syntax. There is no checking to prevent a file from being opened more than
610 SectionType is only used to open an FV. Each file in an FV contains multiple
611 secitons and only the SectionType section is opened.
613 For any file that is opened with EfiOpen() must be closed with EfiClose().
615 @param PathName Path to parse to open
616 @param OpenMode Same as EFI_FILE.Open()
617 @param SectionType Section in FV to open.
619 @return NULL Open failed
620 @return Valid EFI_OPEN_FILE handle
626 IN CONST UINT64 OpenMode
,
627 IN CONST EFI_SECTION_TYPE SectionType
632 EFI_OPEN_FILE FileData
;
636 EFI_OPEN_FILE_GUARD
*GuardFile
;
637 BOOLEAN VolumeNameMatch
;
638 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
641 CHAR8
*CwdPlusPathName
;
643 EblUpdateDeviceLists ();
646 ZeroMem (File
, sizeof (EFI_OPEN_FILE
));
647 File
->FvSectionType
= SectionType
;
649 StrLen
= AsciiStrSize (PathName
);
651 // Smallest valid path is 1 char and a null
655 for (FileStart
= 0; FileStart
< StrLen
; FileStart
++) {
656 if (PathName
[FileStart
] == ':') {
663 // Matching volume name has precedence over handle based names
665 VolumeNameMatch
= EblMatchVolumeName (PathName
, FileStart
, &DevNumber
);
666 if (!VolumeNameMatch
) {
667 if (FileStart
== StrLen
) {
668 // No Volume name or device name, so try Current Working Directory
674 // We could add a current working diretory concept
675 CwdPlusPathName
= AllocatePool (AsciiStrSize (gCwd
) + AsciiStrSize (PathName
));
676 if (CwdPlusPathName
== NULL
) {
680 if ((PathName
[0] == '/') || (PathName
[0] == '\\')) {
681 // PathName starts in / so this means we go to the root of the device in the CWD.
682 CwdPlusPathName
[0] = '\0';
683 for (FileStart
= 0; gCwd
[FileStart
] != '\0'; FileStart
++) {
684 CwdPlusPathName
[FileStart
] = gCwd
[FileStart
];
685 if (gCwd
[FileStart
] == ':') {
687 CwdPlusPathName
[FileStart
] = '\0';
692 AsciiStrCpy (CwdPlusPathName
, gCwd
);
693 StrLen
= AsciiStrLen (gCwd
);
694 if ((*PathName
!= '/') && (*PathName
!= '\\') && (gCwd
[StrLen
-1] != '/') && (gCwd
[StrLen
-1] != '\\')) {
695 AsciiStrCat (CwdPlusPathName
, "\\");
699 AsciiStrCat (CwdPlusPathName
, PathName
);
700 if (AsciiStrStr (CwdPlusPathName
, ":") == NULL
) {
701 // Extra error check to make sure we don't recusre and blow stack
705 File
= EfiOpen (CwdPlusPathName
, OpenMode
, SectionType
);
706 FreePool (CwdPlusPathName
);
710 DevNumber
= EblConvertDevStringToNumber ((CHAR8
*)PathName
);
713 File
->DeviceName
= AllocatePool (StrLen
);
714 AsciiStrCpy (File
->DeviceName
, PathName
);
715 File
->DeviceName
[FileStart
- 1] = '\0';
716 File
->FileName
= &File
->DeviceName
[FileStart
];
717 if (File
->FileName
[0] == '\0') {
718 // if it is just a file name use / as root
719 File
->FileName
= "\\";
723 // Use best match algorithm on the dev names so we only need to look at the
724 // first few charters to match the full device name. Short name forms are
725 // legal from the caller.
727 Status
= EFI_SUCCESS
;
728 if (*PathName
== 'f' || *PathName
== 'F' || VolumeNameMatch
) {
729 if (PathName
[1] == 's' || PathName
[1] == 'S' || VolumeNameMatch
) {
730 if (DevNumber
>= mFsCount
) {
733 File
->Type
= EfiOpenFileSystem
;
734 File
->EfiHandle
= mFs
[DevNumber
];
735 Status
= EblFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
737 } else if (PathName
[1] == 'v' || PathName
[1] == 'V') {
738 if (DevNumber
>= mFvCount
) {
741 File
->Type
= EfiOpenFirmwareVolume
;
742 File
->EfiHandle
= mFv
[DevNumber
];
744 if ((PathName
[FileStart
] == '/') || (PathName
[FileStart
] == '\\')) {
745 // Skip leading / as its not really needed for the FV since no directories are supported
748 Status
= EblFvFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
750 } else if ((*PathName
== 'A') || (*PathName
== 'a')) {
751 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
752 File
->Type
= EfiOpenMemoryBuffer
;
753 // 1st colon is at PathName[FileStart - 1]
754 File
->Buffer
= (VOID
*)AsciiStrHexToUintn (&PathName
[FileStart
]);
757 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
761 // If we ran out of string, there's no extra data
762 if (PathName
[FileStart
] == '\0') {
765 File
->Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
768 // if there's no number after the second colon, default
770 if (File
->Size
== 0) {
771 File
->Size
= (UINTN
)(0 - (UINTN
)File
->Buffer
);
774 File
->MaxPosition
= File
->Size
;
775 File
->BaseOffset
= (UINTN
)File
->Buffer
;
777 } else if (*PathName
== 'l' || *PathName
== 'L') {
778 if (DevNumber
>= mLoadFileCount
) {
781 File
->Type
= EfiOpenLoadFile
;
782 File
->EfiHandle
= mLoadFile
[DevNumber
];
784 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&File
->LoadFile
);
785 if (EFI_ERROR (Status
)) {
789 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&DevicePath
);
790 if (EFI_ERROR (Status
)) {
793 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
795 } else if (*PathName
== 'b' || *PathName
== 'B') {
796 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
797 if (DevNumber
>= mBlkIoCount
) {
800 File
->Type
= EfiOpenBlockIo
;
801 File
->EfiHandle
= mBlkIo
[DevNumber
];
802 EblFileDevicePath (File
, "", OpenMode
);
804 // 1st colon is at PathName[FileStart - 1]
805 File
->DiskOffset
= AsciiStrHexToUintn (&PathName
[FileStart
]);
808 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
812 // If we ran out of string, there's no extra data
813 if (PathName
[FileStart
] == '\0') {
816 Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
819 // if a zero size is passed in (or the size is left out entirely),
820 // go to the end of the device.
822 File
->Size
= File
->Size
- File
->DiskOffset
;
827 File
->MaxPosition
= File
->Size
;
828 File
->BaseOffset
= File
->DiskOffset
;
829 } else if ((*PathName
) >= '0' && (*PathName
<= '9')) {
831 // Get current IP address
832 Status
= EblGetCurrentIpAddress (&Ip
);
833 if (EFI_ERROR(Status
)) {
834 AsciiPrint("Device IP Address is not configured.\n");
839 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
840 File
->Type
= EfiOpenTftp
;
841 File
->IsDirty
= FALSE
;
842 File
->IsBufferValid
= FALSE
;
844 Status
= ConvertIpStringToEfiIp (PathName
, &File
->ServerIp
);
847 if (EFI_ERROR (Status
)) {
851 GuardFile
= (EFI_OPEN_FILE_GUARD
*)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD
));
852 if (GuardFile
== NULL
) {
856 GuardFile
->Header
= EFI_OPEN_FILE_GUARD_HEADER
;
857 CopyMem (&(GuardFile
->File
), &FileData
, sizeof (EFI_OPEN_FILE
));
858 GuardFile
->Footer
= EFI_OPEN_FILE_GUARD_FOOTER
;
860 return &(GuardFile
->File
);
863 FreePool (File
->DeviceName
);
867 #define FILE_COPY_CHUNK 0x01000000
871 IN CHAR8
*DestinationFile
,
875 EFI_OPEN_FILE
*Source
= NULL
;
876 EFI_OPEN_FILE
*Destination
= NULL
;
877 EFI_STATUS Status
= EFI_SUCCESS
;
881 UINTN Chunk
= FILE_COPY_CHUNK
;
883 Source
= EfiOpen(SourceFile
, EFI_FILE_MODE_READ
, 0);
884 if (Source
== NULL
) {
885 AsciiPrint("Source file open error.\n");
886 Status
= EFI_NOT_FOUND
;
890 Destination
= EfiOpen(DestinationFile
, EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
891 if (Destination
== NULL
) {
892 AsciiPrint("Destination file open error.\n");
893 Status
= EFI_NOT_FOUND
;
897 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
898 if (Buffer
== NULL
) {
899 Status
= EFI_OUT_OF_RESOURCES
;
903 Size
= EfiTell(Source
, NULL
);
905 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
906 Chunk
= FILE_COPY_CHUNK
;
908 Status
= EfiRead(Source
, Buffer
, &Chunk
);
909 if (EFI_ERROR(Status
)) {
910 AsciiPrint("Read file error\n");
914 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
915 if (EFI_ERROR(Status
)) {
916 AsciiPrint("Write file error\n");
923 Chunk
= Size
- Offset
;
925 Status
= EfiRead(Source
, Buffer
, &Chunk
);
926 if (EFI_ERROR(Status
)) {
927 AsciiPrint("Read file error\n");
931 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
932 if (EFI_ERROR(Status
)) {
933 AsciiPrint("Write file error\n");
939 if (Source
!= NULL
) {
940 Status
= EfiClose(Source
);
941 if (EFI_ERROR(Status
)) {
942 AsciiPrint("Source close error");
946 if (Destination
!= NULL
) {
947 Status
= EfiClose(Destination
);
948 if (EFI_ERROR(Status
)) {
949 AsciiPrint("Destination close error");
953 if (Buffer
!= NULL
) {
961 Use DeviceType and Index to form a valid PathName and try and open it.
963 @param DeviceType Device type to open
964 @param Index Device Index to use. Zero relative.
966 @return NULL Open failed
967 @return Valid EFI_OPEN_FILE handle
971 EfiDeviceOpenByType (
972 IN EFI_OPEN_FILE_TYPE DeviceType
,
977 CHAR8 Path
[MAX_CMD_LINE
];
979 switch (DeviceType
) {
980 case EfiOpenLoadFile
:
981 DevStr
= "loadfile%d:";
983 case EfiOpenFirmwareVolume
:
986 case EfiOpenFileSystem
:
992 case EfiOpenMemoryBuffer
:
999 AsciiSPrint (Path
, MAX_PATHNAME
, DevStr
, Index
);
1001 return EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1006 Close a file handle opened by EfiOpen() and free all resources allocated by
1009 @param Stream Open File Handle
1011 @return EFI_INVALID_PARAMETER Stream is not an Open File
1012 @return EFI_SUCCESS Steam closed
1017 IN EFI_OPEN_FILE
*File
1021 UINT64 TftpBufferSize
;
1023 if (!FileHandleValid (File
)) {
1024 return EFI_INVALID_PARAMETER
;
1027 //Write the buffer contents to TFTP file.
1028 if ((File
->Type
== EfiOpenTftp
) && (File
->IsDirty
)) {
1030 TftpBufferSize
= File
->Size
;
1032 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
,
1038 (UINT8
*)File
->FileName
,
1042 if (EFI_ERROR(Status
)) {
1043 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status
);
1048 if ((File
->Type
== EfiOpenLoadFile
) ||
1049 ((File
->Type
== EfiOpenTftp
) && (File
->IsBufferValid
== TRUE
))) {
1050 EblFreePool(File
->Buffer
);
1053 EblFreePool (File
->DevicePath
);
1054 EblFreePool (File
->DeviceName
);
1055 EblFreePool (File
->FsFileInfo
);
1056 EblFreePool (File
->FsInfo
);
1058 if (File
->FsFileHandle
!= NULL
) {
1059 File
->FsFileHandle
->Close (File
->FsFileHandle
);
1062 // Need to free File and it's Guard structures
1063 EblFreePool (BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
));
1069 Return the size of the file represented by Stream. Also return the current
1070 Seek position. Opening a file will enable a valid file size to be returned.
1071 LoadFile is an exception as a load file size is set to zero.
1073 @param Stream Open File Handle
1075 @return 0 Stream is not an Open File or a valid LoadFile handle
1080 IN EFI_OPEN_FILE
*File
,
1081 OUT EFI_LBA
*CurrentPosition OPTIONAL
1085 UINT64 BufferSize
= 0;
1087 if (!FileHandleValid (File
)) {
1091 if (CurrentPosition
!= NULL
) {
1092 *CurrentPosition
= File
->CurrentPosition
;
1095 if (File
->Type
== EfiOpenLoadFile
) {
1096 // Figure out the File->Size
1097 File
->Buffer
= NULL
;
1099 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, &File
->Size
, File
->Buffer
);
1100 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1104 File
->MaxPosition
= (UINT64
)File
->Size
;
1105 } else if (File
->Type
== EfiOpenTftp
) {
1108 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1114 (UINT8
*)File
->FileName
,
1118 if (EFI_ERROR(Status
)) {
1119 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status
);
1123 File
->Size
= (UINTN
)BufferSize
;
1124 File
->MaxPosition
= File
->Size
;
1132 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1133 not support EfiSeek(). It is not possible to grow the file size using
1136 SeekType defines how use Offset to calculate the new file position:
1137 EfiSeekStart : Position = Offset
1138 EfiSeekCurrent: Position is Offset bytes from the current position
1139 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1141 @param Stream Open File Handle
1142 @param Offset Offset to seek too.
1143 @param SeekType Type of seek to perform
1146 @return EFI_INVALID_PARAMETER Stream is not an Open File
1147 @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1148 @return EFI_NOT_FOUND Seek past the end of the file.
1149 @return EFI_SUCCESS Steam closed
1154 IN EFI_OPEN_FILE
*File
,
1156 IN EFI_SEEK_TYPE SeekType
1160 UINT64 CurrentPosition
;
1162 if (!FileHandleValid (File
)) {
1163 return EFI_INVALID_PARAMETER
;
1166 if (File
->Type
== EfiOpenLoadFile
|| File
->Type
== EfiOpenFirmwareVolume
) {
1167 // LoadFile and FV do not support Seek
1168 return EFI_UNSUPPORTED
;
1171 CurrentPosition
= File
->CurrentPosition
;
1174 if (Offset
> File
->MaxPosition
) {
1175 return EFI_NOT_FOUND
;
1177 CurrentPosition
= Offset
;
1180 case EfiSeekCurrent
:
1181 if ((File
->CurrentPosition
+ Offset
) > File
->MaxPosition
) {
1182 return EFI_NOT_FOUND
;
1184 CurrentPosition
+= Offset
;
1189 // We don't support growing file size via seeking past end of file
1190 return EFI_UNSUPPORTED
;
1192 CurrentPosition
= File
->MaxPosition
;
1196 return EFI_NOT_FOUND
;
1199 Status
= EFI_SUCCESS
;
1200 if (File
->FsFileHandle
!= NULL
) {
1201 Status
= File
->FsFileHandle
->SetPosition (File
->FsFileHandle
, CurrentPosition
);
1204 if (!EFI_ERROR (Status
)) {
1205 File
->CurrentPosition
= CurrentPosition
;
1213 IN OUT EFI_OPEN_FILE
*File
1217 UINT64 TftpBufferSize
;
1219 if (File
->IsBufferValid
) {
1223 // Make sure the file size is set.
1224 EfiTell (File
, NULL
);
1226 //Allocate a buffer to hold the whole file.
1227 File
->Buffer
= AllocatePool(File
->Size
);
1228 if (File
->Buffer
== NULL
) {
1229 return EFI_OUT_OF_RESOURCES
;
1232 TftpBufferSize
= File
->Size
;
1235 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1241 (UINT8
*)File
->FileName
,
1244 if (EFI_ERROR(Status
)) {
1245 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status
);
1246 FreePool(File
->Buffer
);
1250 // Set the buffer valid flag.
1251 File
->IsBufferValid
= TRUE
;
1257 Read BufferSize bytes from the current locaiton in the file. For load file,
1258 FV, and TFTP case you must read the entire file.
1260 @param Stream Open File Handle
1261 @param Buffer Caller allocated buffer.
1262 @param BufferSize Size of buffer in bytes.
1265 @return EFI_SUCCESS Stream is not an Open File
1266 @return EFI_END_OF_FILE Tried to read past the end of the file
1267 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1268 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1269 @return "other" Error returned from device read
1274 IN EFI_OPEN_FILE
*File
,
1276 OUT UINTN
*BufferSize
1280 UINT32 AuthenticationStatus
;
1281 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1283 if (!FileHandleValid (File
)) {
1284 return EFI_INVALID_PARAMETER
;
1287 // Don't read past the end of the file.
1288 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1289 return EFI_END_OF_FILE
;
1292 switch (File
->Type
) {
1293 case EfiOpenMemoryBuffer
:
1294 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1295 File
->CurrentPosition
+= *BufferSize
;
1296 Status
= EFI_SUCCESS
;
1299 case EfiOpenLoadFile
:
1300 // Figure out the File->Size
1301 EfiTell (File
, NULL
);
1303 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, BufferSize
, Buffer
);
1306 case EfiOpenFirmwareVolume
:
1307 if (File
->FvSectionType
== EFI_SECTION_ALL
) {
1308 Status
= File
->Fv
->ReadFile (
1314 &File
->FvAttributes
,
1315 &AuthenticationStatus
1318 Status
= File
->Fv
->ReadSection (
1321 File
->FvSectionType
,
1325 &AuthenticationStatus
1330 case EfiOpenFileSystem
:
1331 Status
= File
->FsFileHandle
->Read (File
->FsFileHandle
, BufferSize
, Buffer
);
1332 File
->CurrentPosition
+= *BufferSize
;
1335 case EfiOpenBlockIo
:
1336 Status
= gBS
->HandleProtocol(File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1337 if (!EFI_ERROR(Status
)) {
1338 Status
= DiskIo
->ReadDisk(DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1340 File
->CurrentPosition
+= *BufferSize
;
1344 // Cache the file if it hasn't been cached yet.
1345 if (File
->IsBufferValid
== FALSE
) {
1346 Status
= CacheTftpFile (File
);
1347 if (EFI_ERROR (Status
)) {
1352 // Copy out the requested data
1353 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1354 File
->CurrentPosition
+= *BufferSize
;
1356 Status
= EFI_SUCCESS
;
1360 return EFI_INVALID_PARAMETER
;
1368 Read the entire file into a buffer. This routine allocates the buffer and
1369 returns it to the user full of the read data.
1371 This is very useful for load flie where it's hard to know how big the buffer
1374 @param Stream Open File Handle
1375 @param Buffer Pointer to buffer to return.
1376 @param BufferSize Pointer to Size of buffer return..
1379 @return EFI_SUCCESS Stream is not an Open File
1380 @return EFI_END_OF_FILE Tried to read past the end of the file
1381 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1382 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1383 @return "other" Error returned from device read
1387 EfiReadAllocatePool (
1388 IN EFI_OPEN_FILE
*File
,
1390 OUT UINTN
*BufferSize
1393 if (!FileHandleValid (File
)) {
1394 return EFI_INVALID_PARAMETER
;
1397 // Loadfile defers file size determination on Open so use tell to find it
1398 EfiTell (File
, NULL
);
1400 *BufferSize
= File
->Size
;
1401 *Buffer
= AllocatePool (*BufferSize
);
1402 if (*Buffer
== NULL
) {
1403 return EFI_NOT_FOUND
;
1406 return EfiRead (File
, *Buffer
, BufferSize
);
1411 Write data back to the file. For TFTP case you must write the entire file.
1413 @param Stream Open File Handle
1414 @param Buffer Pointer to buffer to return.
1415 @param BufferSize Pointer to Size of buffer return..
1418 @return EFI_SUCCESS Stream is not an Open File
1419 @return EFI_END_OF_FILE Tried to read past the end of the file
1420 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1421 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1422 @return "other" Error returned from device write
1427 IN EFI_OPEN_FILE
*File
,
1429 OUT UINTN
*BufferSize
1433 EFI_FV_WRITE_FILE_DATA FileData
;
1434 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1436 if (!FileHandleValid (File
)) {
1437 return EFI_INVALID_PARAMETER
;
1440 switch (File
->Type
) {
1441 case EfiOpenMemoryBuffer
:
1442 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1443 return EFI_END_OF_FILE
;
1446 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1447 File
->CurrentPosition
+= *BufferSize
;
1448 Status
= EFI_SUCCESS
;
1450 case EfiOpenLoadFile
:
1451 // LoadFile device is read only be definition
1452 Status
= EFI_UNSUPPORTED
;
1454 case EfiOpenFirmwareVolume
:
1455 if (File
->FvSectionType
!= EFI_SECTION_ALL
) {
1456 // Writes not support to a specific section. You have to update entire file
1457 return EFI_UNSUPPORTED
;
1460 FileData
.NameGuid
= &(File
->FvNameGuid
);
1461 FileData
.Type
= File
->FvType
;
1462 FileData
.FileAttributes
= File
->FvAttributes
;
1463 FileData
.Buffer
= Buffer
;
1464 FileData
.BufferSize
= (UINT32
)*BufferSize
;
1465 Status
= File
->Fv
->WriteFile (File
->Fv
, 1, EFI_FV_UNRELIABLE_WRITE
, &FileData
);
1468 case EfiOpenFileSystem
:
1469 Status
= File
->FsFileHandle
->Write (File
->FsFileHandle
, BufferSize
, Buffer
);
1470 File
->CurrentPosition
+= *BufferSize
;
1473 case EfiOpenBlockIo
:
1474 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1475 return EFI_END_OF_FILE
;
1478 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1479 if (!EFI_ERROR(Status
)) {
1480 Status
= DiskIo
->WriteDisk (DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1482 File
->CurrentPosition
+= *BufferSize
;
1486 // Cache the file if it hasn't been cached yet.
1487 if (File
->IsBufferValid
== FALSE
) {
1488 Status
= CacheTftpFile(File
);
1489 if (EFI_ERROR(Status
)) {
1494 // Don't overwrite the buffer
1495 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1498 TempBuffer
= File
->Buffer
;
1500 File
->Buffer
= AllocatePool ((UINTN
)(File
->CurrentPosition
+ *BufferSize
));
1501 if (File
->Buffer
== NULL
) {
1502 return EFI_OUT_OF_RESOURCES
;
1505 CopyMem (File
->Buffer
, TempBuffer
, File
->Size
);
1507 FreePool (TempBuffer
);
1509 File
->Size
= (UINTN
)(File
->CurrentPosition
+ *BufferSize
);
1510 File
->MaxPosition
= (UINT64
)File
->Size
;
1513 // Copy in the requested data
1514 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1515 File
->CurrentPosition
+= *BufferSize
;
1517 // Mark the file dirty
1518 File
->IsDirty
= TRUE
;
1520 Status
= EFI_SUCCESS
;
1524 Status
= EFI_INVALID_PARAMETER
;
1532 Given Cwd expand Path to remove .. and replace them with real
1535 @param Cwd Current Working Directory
1536 @param Path Path to expand
1538 @return NULL Cwd or Path are not valid
1539 @return 'other' Path with .. expanded
1549 CHAR8
*Work
, *Start
, *End
;
1553 if (Cwd
== NULL
|| Path
== NULL
) {
1557 StrLen
= AsciiStrSize (Cwd
);
1559 // Smallest valid path is 1 char and a null
1563 StrLen
= AsciiStrSize (Path
);
1564 NewPath
= AllocatePool (AsciiStrSize (Cwd
) + StrLen
+ 1);
1565 if (NewPath
== NULL
) {
1568 AsciiStrCpy (NewPath
, Cwd
);
1570 End
= Path
+ StrLen
;
1571 for (Start
= Path
;;) {
1572 Work
= AsciiStrStr (Start
, "..") ;
1574 // Remaining part of Path contains no more ..
1578 // append path prior to ..
1579 AsciiStrnCat (NewPath
, Start
, Work
- Start
);
1580 StrLen
= AsciiStrLen (NewPath
);
1581 for (i
= StrLen
; i
>= 0; i
--) {
1582 if (NewPath
[i
] == ':') {
1586 if (NewPath
[i
] == '/' || NewPath
[i
] == '\\') {
1587 if ((i
> 0) && (NewPath
[i
-1] == ':')) {
1588 // leave the / before a :
1589 NewPath
[i
+1] = '\0';
1591 // replace / will Null to remove trailing file/dir reference
1601 // Handle the path that remains after the ..
1602 AsciiStrnCat (NewPath
, Start
, End
- Start
);
1609 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1610 the path does not contain a device name, The CWD is prepended to the path.
1612 @param Cwd Current Working Directory to set
1615 @return EFI_SUCCESS CWD is set
1616 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1624 EFI_OPEN_FILE
*File
;
1629 return EFI_INVALID_PARAMETER
;
1632 if (AsciiStrCmp (Cwd
, ".") == 0) {
1638 if (AsciiStrStr (Cwd
, "..") != NULL
) {
1644 Len
= AsciiStrLen (gCwd
);
1645 if ((gCwd
[Len
-2] == ':') && ((gCwd
[Len
-1] == '/') || (gCwd
[Len
-1] == '\\'))) {
1646 // parent is device so nothing to do
1650 // Expand .. in Cwd, given we know current working directory
1651 Path
= ExpandPath (gCwd
, Cwd
);
1653 return EFI_NOT_FOUND
;
1657 File
= EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1659 return EFI_INVALID_PARAMETER
;
1666 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1667 // relative to the current gCwd or not.
1668 gCwd
= AllocatePool (AsciiStrSize (File
->DeviceName
) + AsciiStrSize (File
->FileName
) + 10);
1670 return EFI_INVALID_PARAMETER
;
1672 AsciiStrCpy (gCwd
, File
->DeviceName
);
1673 if (File
->FileName
== NULL
) {
1674 AsciiStrCat (gCwd
, ":\\");
1676 AsciiStrCat (gCwd
, ":");
1677 AsciiStrCat (gCwd
, File
->FileName
);
1689 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1690 the path does not contain a device name, The CWD is prepended to the path.
1691 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1692 a call to EfiSetCwd() it is not legal to use the pointer returned by
1695 @param Cwd Current Working Directory
1698 @return "" No CWD set
1699 @return 'other' Returns buffer that contains CWD.