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>
60 CONST EFI_GUID gZeroGuid
= { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
62 #define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
63 #define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
65 // Need to defend against this overflowing
66 #define MAX_CMD_LINE 0x200
72 } EFI_OPEN_FILE_GUARD
;
75 // globals to store current open device info
76 EFI_HANDLE
*mBlkIo
= NULL
;
77 UINTN mBlkIoCount
= 0;
79 EFI_HANDLE
*mFs
= NULL
;
81 // mFsInfo[] array entries must match mFs[] handles
82 EFI_FILE_SYSTEM_INFO
**mFsInfo
= NULL
;
84 EFI_HANDLE
*mFv
= NULL
;
86 EFI_HANDLE
*mLoadFile
= NULL
;
87 UINTN mLoadFileCount
= 0;
92 Internal worker function to validate a File handle.
94 @param File Open File Handle
96 @return TRUE File is valid
97 @return FALSE File is not valid
103 IN EFI_OPEN_FILE
*File
106 EFI_OPEN_FILE_GUARD
*GuardFile
;
108 // Look right before and after file structure for the correct signatures
109 GuardFile
= BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
);
110 if ((GuardFile
->Header
!= EFI_OPEN_FILE_GUARD_HEADER
) ||
111 (GuardFile
->Footer
!= EFI_OPEN_FILE_GUARD_FOOTER
) ) {
119 Internal worker function. If Buffer is not NULL free it.
121 @param Buffer Buffer to FreePool()
129 if (Buffer
!= NULL
) {
135 Update Device List Global Variables
139 EblUpdateDeviceLists (
145 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
146 EFI_FILE_HANDLE Root
;
149 if (mBlkIo
!= NULL
) {
152 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiBlockIoProtocolGuid
, NULL
, &mBlkIoCount
, &mBlkIo
);
157 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiFirmwareVolume2ProtocolGuid
, NULL
, &mFvCount
, &mFv
);
159 if (mLoadFile
!= NULL
) {
160 FreePool (mLoadFile
);
162 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiLoadFileProtocolGuid
, NULL
, &mLoadFileCount
, &mLoadFile
);
168 if (&mFsInfo
[0] != NULL
) {
169 // Need to Free the mFsInfo prior to reclaculating mFsCount so don't move this code
170 for (Index
= 0; Index
< mFsCount
; Index
++) {
171 if (mFsInfo
[Index
] != NULL
) {
172 FreePool (mFsInfo
[Index
]);
178 gBS
->LocateHandleBuffer (ByProtocol
, &gEfiSimpleFileSystemProtocolGuid
, NULL
, &mFsCount
, &mFs
);
181 mFsInfo
= AllocateZeroPool (mFsCount
* sizeof (EFI_FILE_SYSTEM_INFO
*));
182 if (mFsInfo
== NULL
) {
183 // If we can't do this then we can't support file system entries
186 // Loop through all the file system structures and cache the file system info data
187 for (Index
=0; Index
< mFsCount
; Index
++) {
188 Status
= gBS
->HandleProtocol (mFs
[Index
], &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
189 if (!EFI_ERROR (Status
)) {
190 Status
= Fs
->OpenVolume (Fs
, &Root
);
191 if (!EFI_ERROR (Status
)) {
192 // Get information about the volume
194 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
195 if (Status
== EFI_BUFFER_TOO_SMALL
) {
196 mFsInfo
[Index
] = AllocatePool (Size
);
197 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, mFsInfo
[Index
]);
209 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
210 Return TRUE if the <devce name> prefix of PathName matches a file system
211 Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
212 and it can be used with mFs[] to find the handle that needs to be opened
214 @param PathName PathName to check
215 @param FileStart Index of the first character of the <path>
216 @param MatchIndex Index in mFsInfo[] that matches
218 @return TRUE PathName matches a Volume Label and MatchIndex is valid
219 @return FALSE PathName does not match a Volume Label MatchIndex undefined
226 OUT UINTN
*MatchIndex
234 for (Index
=0; Index
< mFsCount
; Index
++) {
235 if (mFsInfo
[Index
] == NULL
) {
236 // FsInfo is not valid so skip it
239 VolStrLen
= StrLen (mFsInfo
[Index
]->VolumeLabel
);
240 for (Compare
= 0, Match
= TRUE
; Compare
< (FileStart
- 1); Compare
++) {
241 if (Compare
> VolStrLen
) {
245 if (PathName
[Compare
] != (CHAR8
)mFsInfo
[Index
]->VolumeLabel
[Compare
]) {
246 // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
247 if (!((PathName
[Compare
] == '_') && (mFsInfo
[Index
]->VolumeLabel
[Compare
] == L
' '))) {
264 Return the number of devices of the current type active in the system
266 @param Type Device type to check
268 @return 0 Invalid type
273 IN EFI_OPEN_FILE_TYPE DeviceType
276 switch (DeviceType
) {
277 case EfiOpenLoadFile
:
278 return mLoadFileCount
;
279 case EfiOpenFirmwareVolume
:
281 case EfiOpenFileSystem
:
291 ConvertIpStringToEfiIp (
293 OUT EFI_IP_ADDRESS
*ServerIp
299 ServerIp
->v4
.Addr
[0] = (UINT8
)AsciiStrDecimalToUintn (Str
);
301 Str
= AsciiStrStr (Str
, ".");
303 return EFI_DEVICE_ERROR
;
306 ServerIp
->v4
.Addr
[1] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
308 Str
= AsciiStrStr (Str
, ".");
310 return EFI_DEVICE_ERROR
;
313 ServerIp
->v4
.Addr
[2] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
315 Str
= AsciiStrStr (Str
, ".");
317 return EFI_DEVICE_ERROR
;
320 ServerIp
->v4
.Addr
[3] = (UINT8
)AsciiStrDecimalToUintn (++Str
);
327 Internal work function to extract a device number from a string skipping
328 text. Easy way to extract numbers from strings like blk7:.
330 @param Str String to extract device number form
332 @return -1 Device string is not valid
337 EblConvertDevStringToNumber (
345 // Find the first digit
346 Max
= AsciiStrLen (Str
);
347 for (Index
= 0; !((*Str
>= '0') && (*Str
<= '9')) && (Index
< Max
); Index
++) {
354 return AsciiStrDecimalToUintn (Str
);
359 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
361 @param File Open file handle
362 @param FileName Name of file after device stripped off
368 IN OUT EFI_OPEN_FILE
*File
,
370 IN CONST UINT64 OpenMode
375 FILEPATH_DEVICE_PATH
*FilePath
;
376 EFI_DEVICE_PATH_PROTOCOL
*FileDevicePath
;
377 CHAR16 UnicodeFileName
[MAX_PATHNAME
];
378 EFI_BLOCK_IO_PROTOCOL
*BlkIo
;
379 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
380 EFI_FILE_HANDLE Root
;
383 if ( *FileName
!= 0 ) {
384 AsciiStrToUnicodeStr (FileName
, UnicodeFileName
);
386 AsciiStrToUnicodeStr ("\\", UnicodeFileName
);
389 Size
= StrSize (UnicodeFileName
);
390 FileDevicePath
= AllocatePool (Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
+ sizeof (EFI_DEVICE_PATH_PROTOCOL
));
391 if (FileDevicePath
!= NULL
) {
392 FilePath
= (FILEPATH_DEVICE_PATH
*) FileDevicePath
;
393 FilePath
->Header
.Type
= MEDIA_DEVICE_PATH
;
394 FilePath
->Header
.SubType
= MEDIA_FILEPATH_DP
;
395 CopyMem (&FilePath
->PathName
, UnicodeFileName
, Size
);
396 SetDevicePathNodeLength (&FilePath
->Header
, Size
+ SIZE_OF_FILEPATH_DEVICE_PATH
);
397 SetDevicePathEndNode (NextDevicePathNode (&FilePath
->Header
));
399 if (File
->EfiHandle
!= NULL
) {
400 File
->DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
403 File
->DevicePath
= AppendDevicePath (File
->DevicePath
, FileDevicePath
);
404 FreePool (FileDevicePath
);
407 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiBlockIoProtocolGuid
, (VOID
**)&BlkIo
);
408 if (!EFI_ERROR (Status
)) {
409 CopyMem (&File
->FsBlockIoMedia
, BlkIo
->Media
, sizeof (EFI_BLOCK_IO_MEDIA
));
411 // If we are not opening the device this will get over written with file info
412 File
->MaxPosition
= MultU64x32 (BlkIo
->Media
->LastBlock
+ 1, BlkIo
->Media
->BlockSize
);
415 if (File
->Type
== EfiOpenFileSystem
) {
416 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**)&Fs
);
417 if (!EFI_ERROR (Status
)) {
418 Status
= Fs
->OpenVolume (Fs
, &Root
);
419 if (!EFI_ERROR (Status
)) {
420 // Get information about the volume
422 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
423 if (Status
== EFI_BUFFER_TOO_SMALL
) {
424 File
->FsInfo
= AllocatePool (Size
);
425 Status
= Root
->GetInfo (Root
, &gEfiFileSystemInfoGuid
, &Size
, File
->FsInfo
);
428 // Get information about the file
429 Status
= Root
->Open (Root
, &File
->FsFileHandle
, UnicodeFileName
, OpenMode
, 0);
430 if (!EFI_ERROR (Status
)) {
432 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, NULL
);
433 if (Status
== EFI_BUFFER_TOO_SMALL
) {
434 File
->FsFileInfo
= AllocatePool (Size
);
435 Status
= File
->FsFileHandle
->GetInfo (File
->FsFileHandle
, &gEfiFileInfoGuid
, &Size
, File
->FsFileInfo
);
436 if (!EFI_ERROR (Status
)) {
437 File
->Size
= (UINTN
)File
->FsFileInfo
->FileSize
;
438 File
->MaxPosition
= (UINT64
)File
->Size
;
446 } else if (File
->Type
== EfiOpenBlockIo
) {
447 File
->Size
= (UINTN
)File
->MaxPosition
;
453 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
456 CompareGuidToString (
465 AsciiSPrint (AsciiGuid
, sizeof(AsciiGuid
), "%g", Guid
);
470 while ((*StringPtr
!= '\0') && (*GuidPtr
!= '\0')) {
472 if (*StringPtr
== '-') {
477 if (*GuidPtr
== '-') {
482 if (ToUpper(*StringPtr
) != ToUpper(*GuidPtr
)) {
483 return EFI_NOT_FOUND
;
495 Internal work function to fill in EFI_OPEN_FILE information for the FV
497 @param File Open file handle
498 @param FileName Name of file after device stripped off
503 EblFvFileDevicePath (
504 IN OUT EFI_OPEN_FILE
*File
,
506 IN CONST UINT64 OpenMode
510 EFI_STATUS GetNextFileStatus
;
511 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode
;
512 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
514 UINT32 AuthenticationStatus
;
515 CHAR8 AsciiSection
[MAX_PATHNAME
];
518 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*Fvb
;
521 UINTN NumberOfBlocks
;
524 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolume2ProtocolGuid
, (VOID
**)&File
->Fv
);
525 if (EFI_ERROR (Status
)) {
529 // Get FVB Info about the handle
530 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiFirmwareVolumeBlockProtocolGuid
, (VOID
**)&Fvb
);
531 if (!EFI_ERROR (Status
)) {
532 Status
= Fvb
->GetPhysicalAddress (Fvb
, &File
->FvStart
);
533 if (!EFI_ERROR (Status
)) {
534 for (Lba
= 0, File
->FvSize
= 0; ; File
->FvSize
+= (BlockSize
* NumberOfBlocks
), Lba
+= NumberOfBlocks
) {
535 Status
= Fvb
->GetBlockSize (Fvb
, Lba
, &BlockSize
, &NumberOfBlocks
);
536 if (EFI_ERROR (Status
)) {
544 DevicePath
= DevicePathFromHandle (File
->EfiHandle
);
546 if (*FileName
== '\0') {
547 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
548 File
->Size
= File
->FvSize
;
549 File
->MaxPosition
= File
->Size
;
553 File
->FvType
= EFI_FV_FILETYPE_ALL
;
554 GetNextFileStatus
= File
->Fv
->GetNextFile (
562 if (!EFI_ERROR (GetNextFileStatus
)) {
565 // Compare GUID first
566 Status
= CompareGuidToString (&File
->FvNameGuid
, FileName
);
567 if (!EFI_ERROR(Status
)) {
571 Status
= File
->Fv
->ReadSection (
574 EFI_SECTION_USER_INTERFACE
,
578 &AuthenticationStatus
580 if (!EFI_ERROR (Status
)) {
581 UnicodeStrToAsciiStr (Section
, AsciiSection
);
582 if (AsciiStriCmp (FileName
, AsciiSection
) == 0) {
589 } while (!EFI_ERROR (GetNextFileStatus
));
591 if (EFI_ERROR (GetNextFileStatus
)) {
592 return GetNextFileStatus
;
595 File
->MaxPosition
= File
->Size
;
596 EfiInitializeFwVolDevicepathNode (&DevicePathNode
, &File
->FvNameGuid
);
597 File
->DevicePath
= AppendDevicePathNode (DevicePath
, (EFI_DEVICE_PATH_PROTOCOL
*)&DevicePathNode
);
601 // FVB not required if FV was soft loaded...
609 Open a device named by PathName. The PathName includes a device name and
610 path seperated by a :. See file header for more details on the PathName
611 syntax. There is no checking to prevent a file from being opened more than
614 SectionType is only used to open an FV. Each file in an FV contains multiple
615 secitons and only the SectionType section is opened.
617 For any file that is opened with EfiOpen() must be closed with EfiClose().
619 @param PathName Path to parse to open
620 @param OpenMode Same as EFI_FILE.Open()
621 @param SectionType Section in FV to open.
623 @return NULL Open failed
624 @return Valid EFI_OPEN_FILE handle
630 IN CONST UINT64 OpenMode
,
631 IN CONST EFI_SECTION_TYPE SectionType
636 EFI_OPEN_FILE FileData
;
640 EFI_OPEN_FILE_GUARD
*GuardFile
;
641 BOOLEAN VolumeNameMatch
;
642 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
645 CHAR8
*CwdPlusPathName
;
647 EblUpdateDeviceLists ();
650 ZeroMem (File
, sizeof (EFI_OPEN_FILE
));
651 File
->FvSectionType
= SectionType
;
653 StrLen
= AsciiStrSize (PathName
);
655 // Smallest valid path is 1 char and a null
659 for (FileStart
= 0; FileStart
< StrLen
; FileStart
++) {
660 if (PathName
[FileStart
] == ':') {
667 // Matching volume name has precedence over handle based names
669 VolumeNameMatch
= EblMatchVolumeName (PathName
, FileStart
, &DevNumber
);
670 if (!VolumeNameMatch
) {
671 if (FileStart
== StrLen
) {
672 // No Volume name or device name, so try Current Working Directory
678 // We could add a current working diretory concept
679 CwdPlusPathName
= AllocatePool (AsciiStrSize (gCwd
) + AsciiStrSize (PathName
));
680 if (CwdPlusPathName
== NULL
) {
684 if ((PathName
[0] == '/') || (PathName
[0] == '\\')) {
685 // PathName starts in / so this means we go to the root of the device in the CWD.
686 CwdPlusPathName
[0] = '\0';
687 for (FileStart
= 0; gCwd
[FileStart
] != '\0'; FileStart
++) {
688 CwdPlusPathName
[FileStart
] = gCwd
[FileStart
];
689 if (gCwd
[FileStart
] == ':') {
691 CwdPlusPathName
[FileStart
] = '\0';
696 AsciiStrCpy (CwdPlusPathName
, gCwd
);
697 StrLen
= AsciiStrLen (gCwd
);
698 if ((*PathName
!= '/') && (*PathName
!= '\\') && (gCwd
[StrLen
-1] != '/') && (gCwd
[StrLen
-1] != '\\')) {
699 AsciiStrCat (CwdPlusPathName
, "\\");
703 AsciiStrCat (CwdPlusPathName
, PathName
);
704 if (AsciiStrStr (CwdPlusPathName
, ":") == NULL
) {
705 // Extra error check to make sure we don't recusre and blow stack
709 File
= EfiOpen (CwdPlusPathName
, OpenMode
, SectionType
);
710 FreePool (CwdPlusPathName
);
714 DevNumber
= EblConvertDevStringToNumber ((CHAR8
*)PathName
);
717 File
->DeviceName
= AllocatePool (StrLen
);
718 AsciiStrCpy (File
->DeviceName
, PathName
);
719 File
->DeviceName
[FileStart
- 1] = '\0';
720 File
->FileName
= &File
->DeviceName
[FileStart
];
721 if (File
->FileName
[0] == '\0') {
722 // if it is just a file name use / as root
723 File
->FileName
= "\\";
727 // Use best match algorithm on the dev names so we only need to look at the
728 // first few charters to match the full device name. Short name forms are
729 // legal from the caller.
731 Status
= EFI_SUCCESS
;
732 if (*PathName
== 'f' || *PathName
== 'F' || VolumeNameMatch
) {
733 if (PathName
[1] == 's' || PathName
[1] == 'S' || VolumeNameMatch
) {
734 if (DevNumber
>= mFsCount
) {
737 File
->Type
= EfiOpenFileSystem
;
738 File
->EfiHandle
= mFs
[DevNumber
];
739 Status
= EblFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
741 } else if (PathName
[1] == 'v' || PathName
[1] == 'V') {
742 if (DevNumber
>= mFvCount
) {
745 File
->Type
= EfiOpenFirmwareVolume
;
746 File
->EfiHandle
= mFv
[DevNumber
];
748 if ((PathName
[FileStart
] == '/') || (PathName
[FileStart
] == '\\')) {
749 // Skip leading / as its not really needed for the FV since no directories are supported
752 Status
= EblFvFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
754 } else if ((*PathName
== 'A') || (*PathName
== 'a')) {
755 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
756 File
->Type
= EfiOpenMemoryBuffer
;
757 // 1st colon is at PathName[FileStart - 1]
758 File
->Buffer
= (VOID
*)AsciiStrHexToUintn (&PathName
[FileStart
]);
761 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
765 // If we ran out of string, there's no extra data
766 if (PathName
[FileStart
] == '\0') {
769 File
->Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
772 // if there's no number after the second colon, default
774 if (File
->Size
== 0) {
775 File
->Size
= (UINTN
)(0 - (UINTN
)File
->Buffer
);
778 File
->MaxPosition
= File
->Size
;
779 File
->BaseOffset
= (UINTN
)File
->Buffer
;
781 } else if (*PathName
== 'l' || *PathName
== 'L') {
782 if (DevNumber
>= mLoadFileCount
) {
785 File
->Type
= EfiOpenLoadFile
;
786 File
->EfiHandle
= mLoadFile
[DevNumber
];
788 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&File
->LoadFile
);
789 if (EFI_ERROR (Status
)) {
793 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&DevicePath
);
794 if (EFI_ERROR (Status
)) {
797 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
799 } else if (*PathName
== 'b' || *PathName
== 'B') {
800 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
801 if (DevNumber
>= mBlkIoCount
) {
804 File
->Type
= EfiOpenBlockIo
;
805 File
->EfiHandle
= mBlkIo
[DevNumber
];
806 EblFileDevicePath (File
, "", OpenMode
);
808 // 1st colon is at PathName[FileStart - 1]
809 File
->DiskOffset
= AsciiStrHexToUintn (&PathName
[FileStart
]);
812 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
816 // If we ran out of string, there's no extra data
817 if (PathName
[FileStart
] == '\0') {
820 Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
823 // if a zero size is passed in (or the size is left out entirely),
824 // go to the end of the device.
826 File
->Size
= File
->Size
- File
->DiskOffset
;
831 File
->MaxPosition
= File
->Size
;
832 File
->BaseOffset
= File
->DiskOffset
;
833 } else if ((*PathName
) >= '0' && (*PathName
<= '9')) {
835 // Get current IP address
836 Status
= EblGetCurrentIpAddress (&Ip
);
837 if (EFI_ERROR(Status
)) {
838 AsciiPrint("Device IP Address is not configured.\n");
843 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
844 File
->Type
= EfiOpenTftp
;
845 File
->IsDirty
= FALSE
;
846 File
->IsBufferValid
= FALSE
;
848 Status
= ConvertIpStringToEfiIp (PathName
, &File
->ServerIp
);
851 if (EFI_ERROR (Status
)) {
855 GuardFile
= (EFI_OPEN_FILE_GUARD
*)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD
));
856 if (GuardFile
== NULL
) {
860 GuardFile
->Header
= EFI_OPEN_FILE_GUARD_HEADER
;
861 CopyMem (&(GuardFile
->File
), &FileData
, sizeof (EFI_OPEN_FILE
));
862 GuardFile
->Footer
= EFI_OPEN_FILE_GUARD_FOOTER
;
864 return &(GuardFile
->File
);
867 FreePool (File
->DeviceName
);
871 #define FILE_COPY_CHUNK 0x01000000
875 IN CHAR8
*DestinationFile
,
879 EFI_OPEN_FILE
*Source
= NULL
;
880 EFI_OPEN_FILE
*Destination
= NULL
;
881 EFI_STATUS Status
= EFI_SUCCESS
;
885 UINTN Chunk
= FILE_COPY_CHUNK
;
887 Source
= EfiOpen(SourceFile
, EFI_FILE_MODE_READ
, 0);
888 if (Source
== NULL
) {
889 AsciiPrint("Source file open error.\n");
890 Status
= EFI_NOT_FOUND
;
894 Destination
= EfiOpen(DestinationFile
, EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
895 if (Destination
== NULL
) {
896 AsciiPrint("Destination file open error.\n");
897 Status
= EFI_NOT_FOUND
;
901 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
902 if (Buffer
== NULL
) {
903 Status
= EFI_OUT_OF_RESOURCES
;
907 Size
= EfiTell(Source
, NULL
);
909 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
910 Chunk
= FILE_COPY_CHUNK
;
912 Status
= EfiRead(Source
, Buffer
, &Chunk
);
913 if (EFI_ERROR(Status
)) {
914 AsciiPrint("Read file error\n");
918 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
919 if (EFI_ERROR(Status
)) {
920 AsciiPrint("Write file error\n");
927 Chunk
= Size
- Offset
;
929 Status
= EfiRead(Source
, Buffer
, &Chunk
);
930 if (EFI_ERROR(Status
)) {
931 AsciiPrint("Read file error\n");
935 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
936 if (EFI_ERROR(Status
)) {
937 AsciiPrint("Write file error\n");
943 if (Source
!= NULL
) {
944 Status
= EfiClose(Source
);
945 if (EFI_ERROR(Status
)) {
946 AsciiPrint("Source close error");
950 if (Destination
!= NULL
) {
951 Status
= EfiClose(Destination
);
952 if (EFI_ERROR(Status
)) {
953 AsciiPrint("Destination close error");
957 if (Buffer
!= NULL
) {
965 Use DeviceType and Index to form a valid PathName and try and open it.
967 @param DeviceType Device type to open
968 @param Index Device Index to use. Zero relative.
970 @return NULL Open failed
971 @return Valid EFI_OPEN_FILE handle
975 EfiDeviceOpenByType (
976 IN EFI_OPEN_FILE_TYPE DeviceType
,
981 CHAR8 Path
[MAX_CMD_LINE
];
983 switch (DeviceType
) {
984 case EfiOpenLoadFile
:
985 DevStr
= "loadfile%d:";
987 case EfiOpenFirmwareVolume
:
990 case EfiOpenFileSystem
:
996 case EfiOpenMemoryBuffer
:
1003 AsciiSPrint (Path
, MAX_PATHNAME
, DevStr
, Index
);
1005 return EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1010 Close a file handle opened by EfiOpen() and free all resources allocated by
1013 @param Stream Open File Handle
1015 @return EFI_INVALID_PARAMETER Stream is not an Open File
1016 @return EFI_SUCCESS Steam closed
1021 IN EFI_OPEN_FILE
*File
1025 UINT64 TftpBufferSize
;
1027 if (!FileHandleValid (File
)) {
1028 return EFI_INVALID_PARAMETER
;
1031 //Write the buffer contents to TFTP file.
1032 if ((File
->Type
== EfiOpenTftp
) && (File
->IsDirty
)) {
1034 TftpBufferSize
= File
->Size
;
1036 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
,
1042 (UINT8
*)File
->FileName
,
1046 if (EFI_ERROR(Status
)) {
1047 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status
);
1052 if ((File
->Type
== EfiOpenLoadFile
) ||
1053 ((File
->Type
== EfiOpenTftp
) && (File
->IsBufferValid
== TRUE
))) {
1054 EblFreePool(File
->Buffer
);
1057 EblFreePool (File
->DevicePath
);
1058 EblFreePool (File
->DeviceName
);
1059 EblFreePool (File
->FsFileInfo
);
1060 EblFreePool (File
->FsInfo
);
1062 if (File
->FsFileHandle
!= NULL
) {
1063 File
->FsFileHandle
->Close (File
->FsFileHandle
);
1066 // Need to free File and it's Guard structures
1067 EblFreePool (BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
));
1073 Return the size of the file represented by Stream. Also return the current
1074 Seek position. Opening a file will enable a valid file size to be returned.
1075 LoadFile is an exception as a load file size is set to zero.
1077 @param Stream Open File Handle
1079 @return 0 Stream is not an Open File or a valid LoadFile handle
1084 IN EFI_OPEN_FILE
*File
,
1085 OUT EFI_LBA
*CurrentPosition OPTIONAL
1089 UINT64 BufferSize
= 0;
1091 if (!FileHandleValid (File
)) {
1095 if (CurrentPosition
!= NULL
) {
1096 *CurrentPosition
= File
->CurrentPosition
;
1099 if (File
->Type
== EfiOpenLoadFile
) {
1100 // Figure out the File->Size
1101 File
->Buffer
= NULL
;
1103 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, &File
->Size
, File
->Buffer
);
1104 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1108 File
->MaxPosition
= (UINT64
)File
->Size
;
1109 } else if (File
->Type
== EfiOpenTftp
) {
1112 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1118 (UINT8
*)File
->FileName
,
1122 if (EFI_ERROR(Status
)) {
1123 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status
);
1127 File
->Size
= (UINTN
)BufferSize
;
1128 File
->MaxPosition
= File
->Size
;
1136 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1137 not support EfiSeek(). It is not possible to grow the file size using
1140 SeekType defines how use Offset to calculate the new file position:
1141 EfiSeekStart : Position = Offset
1142 EfiSeekCurrent: Position is Offset bytes from the current position
1143 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1145 @param Stream Open File Handle
1146 @param Offset Offset to seek too.
1147 @param SeekType Type of seek to perform
1150 @return EFI_INVALID_PARAMETER Stream is not an Open File
1151 @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1152 @return EFI_NOT_FOUND Seek past the end of the file.
1153 @return EFI_SUCCESS Steam closed
1158 IN EFI_OPEN_FILE
*File
,
1160 IN EFI_SEEK_TYPE SeekType
1164 UINT64 CurrentPosition
;
1166 if (!FileHandleValid (File
)) {
1167 return EFI_INVALID_PARAMETER
;
1170 if (File
->Type
== EfiOpenLoadFile
|| File
->Type
== EfiOpenFirmwareVolume
) {
1171 if (!CompareGuid (&File
->FvNameGuid
, &gZeroGuid
)) {
1172 // LoadFile and FV do not support Seek
1173 // You can seek on a raw FV device
1174 return EFI_UNSUPPORTED
;
1178 CurrentPosition
= File
->CurrentPosition
;
1181 if (Offset
> File
->MaxPosition
) {
1182 return EFI_NOT_FOUND
;
1184 CurrentPosition
= Offset
;
1187 case EfiSeekCurrent
:
1188 if ((File
->CurrentPosition
+ Offset
) > File
->MaxPosition
) {
1189 return EFI_NOT_FOUND
;
1191 CurrentPosition
+= Offset
;
1196 // We don't support growing file size via seeking past end of file
1197 return EFI_UNSUPPORTED
;
1199 CurrentPosition
= File
->MaxPosition
;
1203 return EFI_NOT_FOUND
;
1206 Status
= EFI_SUCCESS
;
1207 if (File
->FsFileHandle
!= NULL
) {
1208 Status
= File
->FsFileHandle
->SetPosition (File
->FsFileHandle
, CurrentPosition
);
1211 if (!EFI_ERROR (Status
)) {
1212 File
->CurrentPosition
= CurrentPosition
;
1220 IN OUT EFI_OPEN_FILE
*File
1224 UINT64 TftpBufferSize
;
1226 if (File
->IsBufferValid
) {
1230 // Make sure the file size is set.
1231 EfiTell (File
, NULL
);
1233 //Allocate a buffer to hold the whole file.
1234 File
->Buffer
= AllocatePool(File
->Size
);
1235 if (File
->Buffer
== NULL
) {
1236 return EFI_OUT_OF_RESOURCES
;
1239 TftpBufferSize
= File
->Size
;
1242 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1248 (UINT8
*)File
->FileName
,
1251 if (EFI_ERROR(Status
)) {
1252 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status
);
1253 FreePool(File
->Buffer
);
1257 // Set the buffer valid flag.
1258 File
->IsBufferValid
= TRUE
;
1264 Read BufferSize bytes from the current locaiton in the file. For load file,
1265 FV, and TFTP case you must read the entire file.
1267 @param Stream Open File Handle
1268 @param Buffer Caller allocated buffer.
1269 @param BufferSize Size of buffer in bytes.
1272 @return EFI_SUCCESS Stream is not an Open File
1273 @return EFI_END_OF_FILE Tried to read past the end of the file
1274 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1275 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1276 @return "other" Error returned from device read
1281 IN EFI_OPEN_FILE
*File
,
1283 OUT UINTN
*BufferSize
1287 UINT32 AuthenticationStatus
;
1288 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1290 if (!FileHandleValid (File
)) {
1291 return EFI_INVALID_PARAMETER
;
1294 // Don't read past the end of the file.
1295 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1296 return EFI_END_OF_FILE
;
1299 switch (File
->Type
) {
1300 case EfiOpenMemoryBuffer
:
1301 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1302 File
->CurrentPosition
+= *BufferSize
;
1303 Status
= EFI_SUCCESS
;
1306 case EfiOpenLoadFile
:
1307 // Figure out the File->Size
1308 EfiTell (File
, NULL
);
1310 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, BufferSize
, Buffer
);
1313 case EfiOpenFirmwareVolume
:
1314 if (CompareGuid (&File
->FvNameGuid
, &gZeroGuid
)) {
1315 // This is the entire FV device, so treat like a memory buffer
1316 CopyMem (Buffer
, (VOID
*)(UINTN
)(File
->FvStart
+ File
->CurrentPosition
), *BufferSize
);
1317 File
->CurrentPosition
+= *BufferSize
;
1318 Status
= EFI_SUCCESS
;
1320 if (File
->FvSectionType
== EFI_SECTION_ALL
) {
1321 Status
= File
->Fv
->ReadFile (
1327 &File
->FvAttributes
,
1328 &AuthenticationStatus
1331 Status
= File
->Fv
->ReadSection (
1334 File
->FvSectionType
,
1338 &AuthenticationStatus
1344 case EfiOpenFileSystem
:
1345 Status
= File
->FsFileHandle
->Read (File
->FsFileHandle
, BufferSize
, Buffer
);
1346 File
->CurrentPosition
+= *BufferSize
;
1349 case EfiOpenBlockIo
:
1350 Status
= gBS
->HandleProtocol(File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1351 if (!EFI_ERROR(Status
)) {
1352 Status
= DiskIo
->ReadDisk(DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1354 File
->CurrentPosition
+= *BufferSize
;
1358 // Cache the file if it hasn't been cached yet.
1359 if (File
->IsBufferValid
== FALSE
) {
1360 Status
= CacheTftpFile (File
);
1361 if (EFI_ERROR (Status
)) {
1366 // Copy out the requested data
1367 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1368 File
->CurrentPosition
+= *BufferSize
;
1370 Status
= EFI_SUCCESS
;
1374 return EFI_INVALID_PARAMETER
;
1382 Read the entire file into a buffer. This routine allocates the buffer and
1383 returns it to the user full of the read data.
1385 This is very useful for load flie where it's hard to know how big the buffer
1388 @param Stream Open File Handle
1389 @param Buffer Pointer to buffer to return.
1390 @param BufferSize Pointer to Size of buffer return..
1393 @return EFI_SUCCESS Stream is not an Open File
1394 @return EFI_END_OF_FILE Tried to read past the end of the file
1395 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1396 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1397 @return "other" Error returned from device read
1401 EfiReadAllocatePool (
1402 IN EFI_OPEN_FILE
*File
,
1404 OUT UINTN
*BufferSize
1407 if (!FileHandleValid (File
)) {
1408 return EFI_INVALID_PARAMETER
;
1411 // Loadfile defers file size determination on Open so use tell to find it
1412 EfiTell (File
, NULL
);
1414 *BufferSize
= File
->Size
;
1415 *Buffer
= AllocatePool (*BufferSize
);
1416 if (*Buffer
== NULL
) {
1417 return EFI_NOT_FOUND
;
1420 return EfiRead (File
, *Buffer
, BufferSize
);
1425 Write data back to the file. For TFTP case you must write the entire file.
1427 @param Stream Open File Handle
1428 @param Buffer Pointer to buffer to return.
1429 @param BufferSize Pointer to Size of buffer return..
1432 @return EFI_SUCCESS Stream is not an Open File
1433 @return EFI_END_OF_FILE Tried to read past the end of the file
1434 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1435 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1436 @return "other" Error returned from device write
1441 IN EFI_OPEN_FILE
*File
,
1443 OUT UINTN
*BufferSize
1447 EFI_FV_WRITE_FILE_DATA FileData
;
1448 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1450 if (!FileHandleValid (File
)) {
1451 return EFI_INVALID_PARAMETER
;
1454 switch (File
->Type
) {
1455 case EfiOpenMemoryBuffer
:
1456 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1457 return EFI_END_OF_FILE
;
1460 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1461 File
->CurrentPosition
+= *BufferSize
;
1462 Status
= EFI_SUCCESS
;
1464 case EfiOpenLoadFile
:
1465 // LoadFile device is read only be definition
1466 Status
= EFI_UNSUPPORTED
;
1468 case EfiOpenFirmwareVolume
:
1469 if (File
->FvSectionType
!= EFI_SECTION_ALL
) {
1470 // Writes not support to a specific section. You have to update entire file
1471 return EFI_UNSUPPORTED
;
1474 FileData
.NameGuid
= &(File
->FvNameGuid
);
1475 FileData
.Type
= File
->FvType
;
1476 FileData
.FileAttributes
= File
->FvAttributes
;
1477 FileData
.Buffer
= Buffer
;
1478 FileData
.BufferSize
= (UINT32
)*BufferSize
;
1479 Status
= File
->Fv
->WriteFile (File
->Fv
, 1, EFI_FV_UNRELIABLE_WRITE
, &FileData
);
1482 case EfiOpenFileSystem
:
1483 Status
= File
->FsFileHandle
->Write (File
->FsFileHandle
, BufferSize
, Buffer
);
1484 File
->CurrentPosition
+= *BufferSize
;
1487 case EfiOpenBlockIo
:
1488 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1489 return EFI_END_OF_FILE
;
1492 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1493 if (!EFI_ERROR(Status
)) {
1494 Status
= DiskIo
->WriteDisk (DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1496 File
->CurrentPosition
+= *BufferSize
;
1500 // Cache the file if it hasn't been cached yet.
1501 if (File
->IsBufferValid
== FALSE
) {
1502 Status
= CacheTftpFile(File
);
1503 if (EFI_ERROR(Status
)) {
1508 // Don't overwrite the buffer
1509 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1512 TempBuffer
= File
->Buffer
;
1514 File
->Buffer
= AllocatePool ((UINTN
)(File
->CurrentPosition
+ *BufferSize
));
1515 if (File
->Buffer
== NULL
) {
1516 return EFI_OUT_OF_RESOURCES
;
1519 CopyMem (File
->Buffer
, TempBuffer
, File
->Size
);
1521 FreePool (TempBuffer
);
1523 File
->Size
= (UINTN
)(File
->CurrentPosition
+ *BufferSize
);
1524 File
->MaxPosition
= (UINT64
)File
->Size
;
1527 // Copy in the requested data
1528 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1529 File
->CurrentPosition
+= *BufferSize
;
1531 // Mark the file dirty
1532 File
->IsDirty
= TRUE
;
1534 Status
= EFI_SUCCESS
;
1538 Status
= EFI_INVALID_PARAMETER
;
1546 Given Cwd expand Path to remove .. and replace them with real
1549 @param Cwd Current Working Directory
1550 @param Path Path to expand
1552 @return NULL Cwd or Path are not valid
1553 @return 'other' Path with .. expanded
1563 CHAR8
*Work
, *Start
, *End
;
1567 if (Cwd
== NULL
|| Path
== NULL
) {
1571 StrLen
= AsciiStrSize (Cwd
);
1573 // Smallest valid path is 1 char and a null
1577 StrLen
= AsciiStrSize (Path
);
1578 NewPath
= AllocatePool (AsciiStrSize (Cwd
) + StrLen
+ 1);
1579 if (NewPath
== NULL
) {
1582 AsciiStrCpy (NewPath
, Cwd
);
1584 End
= Path
+ StrLen
;
1585 for (Start
= Path
;;) {
1586 Work
= AsciiStrStr (Start
, "..") ;
1588 // Remaining part of Path contains no more ..
1592 // append path prior to ..
1593 AsciiStrnCat (NewPath
, Start
, Work
- Start
);
1594 StrLen
= AsciiStrLen (NewPath
);
1595 for (i
= StrLen
; i
>= 0; i
--) {
1596 if (NewPath
[i
] == ':') {
1600 if (NewPath
[i
] == '/' || NewPath
[i
] == '\\') {
1601 if ((i
> 0) && (NewPath
[i
-1] == ':')) {
1602 // leave the / before a :
1603 NewPath
[i
+1] = '\0';
1605 // replace / will Null to remove trailing file/dir reference
1615 // Handle the path that remains after the ..
1616 AsciiStrnCat (NewPath
, Start
, End
- Start
);
1623 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1624 the path does not contain a device name, The CWD is prepended to the path.
1626 @param Cwd Current Working Directory to set
1629 @return EFI_SUCCESS CWD is set
1630 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1638 EFI_OPEN_FILE
*File
;
1643 return EFI_INVALID_PARAMETER
;
1646 if (AsciiStrCmp (Cwd
, ".") == 0) {
1652 if (AsciiStrStr (Cwd
, "..") != NULL
) {
1658 Len
= AsciiStrLen (gCwd
);
1659 if ((gCwd
[Len
-2] == ':') && ((gCwd
[Len
-1] == '/') || (gCwd
[Len
-1] == '\\'))) {
1660 // parent is device so nothing to do
1664 // Expand .. in Cwd, given we know current working directory
1665 Path
= ExpandPath (gCwd
, Cwd
);
1667 return EFI_NOT_FOUND
;
1671 File
= EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1673 return EFI_INVALID_PARAMETER
;
1680 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1681 // relative to the current gCwd or not.
1682 gCwd
= AllocatePool (AsciiStrSize (File
->DeviceName
) + AsciiStrSize (File
->FileName
) + 10);
1684 return EFI_INVALID_PARAMETER
;
1686 AsciiStrCpy (gCwd
, File
->DeviceName
);
1687 if (File
->FileName
== NULL
) {
1688 AsciiStrCat (gCwd
, ":\\");
1690 AsciiStrCat (gCwd
, ":");
1691 AsciiStrCat (gCwd
, File
->FileName
);
1703 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1704 the path does not contain a device name, The CWD is prepended to the path.
1705 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1706 a call to EfiSetCwd() it is not legal to use the pointer returned by
1709 @param Cwd Current Working Directory
1712 @return "" No CWD set
1713 @return 'other' Returns buffer that contains CWD.