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
] == ':') {
662 if (FileStart
== 0) {
668 // We could add a current working diretory concept
669 CwdPlusPathName
= AllocatePool (AsciiStrSize (gCwd
) + AsciiStrSize (PathName
));
670 if (CwdPlusPathName
== NULL
) {
674 AsciiStrCpy (CwdPlusPathName
, gCwd
);
675 AsciiStrCat (CwdPlusPathName
, PathName
);
676 File
= EfiOpen (CwdPlusPathName
, OpenMode
, SectionType
);
677 FreePool (CwdPlusPathName
);
682 // Matching volume name has precedence over handle based names
684 VolumeNameMatch
= EblMatchVolumeName (PathName
, FileStart
, &DevNumber
);
685 if (!VolumeNameMatch
) {
686 DevNumber
= EblConvertDevStringToNumber ((CHAR8
*)PathName
);
689 File
->DeviceName
= AllocatePool (StrLen
);
690 AsciiStrCpy (File
->DeviceName
, PathName
);
691 File
->DeviceName
[FileStart
- 1] = '\0';
692 File
->FileName
= &File
->DeviceName
[FileStart
];
695 // Use best match algorithm on the dev names so we only need to look at the
696 // first few charters to match the full device name. Short name forms are
697 // legal from the caller.
699 Status
= EFI_SUCCESS
;
700 if (*PathName
== 'f' || *PathName
== 'F' || VolumeNameMatch
) {
701 if (PathName
[1] == 's' || PathName
[1] == 'S' || VolumeNameMatch
) {
702 if (DevNumber
>= mFsCount
) {
705 File
->Type
= EfiOpenFileSystem
;
706 File
->EfiHandle
= mFs
[DevNumber
];
707 Status
= EblFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
709 } else if (PathName
[1] == 'v' || PathName
[1] == 'V') {
710 if (DevNumber
>= mFvCount
) {
713 File
->Type
= EfiOpenFirmwareVolume
;
714 File
->EfiHandle
= mFv
[DevNumber
];
716 if ((PathName
[FileStart
] == '/') || (PathName
[FileStart
] == '\\')) {
717 // Skip leading / as its not really needed for the FV since no directories are supported
720 Status
= EblFvFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
722 } else if ((*PathName
== 'A') || (*PathName
== 'a')) {
723 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
724 File
->Type
= EfiOpenMemoryBuffer
;
725 // 1st colon is at PathName[FileStart - 1]
726 File
->Buffer
= (VOID
*)AsciiStrHexToUintn (&PathName
[FileStart
]);
729 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
733 // If we ran out of string, there's no extra data
734 if (PathName
[FileStart
] == '\0') {
737 File
->Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
740 // if there's no number after the second colon, default
742 if (File
->Size
== 0) {
743 File
->Size
= (UINTN
)(0 - (UINTN
)File
->Buffer
);
746 File
->MaxPosition
= File
->Size
;
747 File
->BaseOffset
= (UINTN
)File
->Buffer
;
749 } else if (*PathName
== 'l' || *PathName
== 'L') {
750 if (DevNumber
>= mLoadFileCount
) {
753 File
->Type
= EfiOpenLoadFile
;
754 File
->EfiHandle
= mLoadFile
[DevNumber
];
756 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&File
->LoadFile
);
757 if (EFI_ERROR (Status
)) {
761 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&DevicePath
);
762 if (EFI_ERROR (Status
)) {
765 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
767 } else if (*PathName
== 'b' || *PathName
== 'B') {
768 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
769 if (DevNumber
>= mBlkIoCount
) {
772 File
->Type
= EfiOpenBlockIo
;
773 File
->EfiHandle
= mBlkIo
[DevNumber
];
774 EblFileDevicePath (File
, "", OpenMode
);
776 // 1st colon is at PathName[FileStart - 1]
777 File
->DiskOffset
= AsciiStrHexToUintn (&PathName
[FileStart
]);
780 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
784 // If we ran out of string, there's no extra data
785 if (PathName
[FileStart
] == '\0') {
788 Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
791 // if a zero size is passed in (or the size is left out entirely),
792 // go to the end of the device.
794 File
->Size
= File
->Size
- File
->DiskOffset
;
799 File
->MaxPosition
= File
->Size
;
800 File
->BaseOffset
= File
->DiskOffset
;
801 } else if ((*PathName
) >= '0' && (*PathName
<= '9')) {
803 // Get current IP address
804 Status
= EblGetCurrentIpAddress (&Ip
);
805 if (EFI_ERROR(Status
)) {
806 AsciiPrint("Device IP Address is not configured.\n");
811 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
812 File
->Type
= EfiOpenTftp
;
813 File
->IsDirty
= FALSE
;
814 File
->IsBufferValid
= FALSE
;
816 Status
= ConvertIpStringToEfiIp (PathName
, &File
->ServerIp
);
819 if (EFI_ERROR (Status
)) {
823 GuardFile
= (EFI_OPEN_FILE_GUARD
*)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD
));
824 if (GuardFile
== NULL
) {
828 GuardFile
->Header
= EFI_OPEN_FILE_GUARD_HEADER
;
829 CopyMem (&(GuardFile
->File
), &FileData
, sizeof (EFI_OPEN_FILE
));
830 GuardFile
->Footer
= EFI_OPEN_FILE_GUARD_FOOTER
;
832 return &(GuardFile
->File
);
835 FreePool (File
->DeviceName
);
839 #define FILE_COPY_CHUNK 0x01000000
843 IN CHAR8
*DestinationFile
,
847 EFI_OPEN_FILE
*Source
= NULL
;
848 EFI_OPEN_FILE
*Destination
= NULL
;
849 EFI_STATUS Status
= EFI_SUCCESS
;
853 UINTN Chunk
= FILE_COPY_CHUNK
;
855 Source
= EfiOpen(SourceFile
, EFI_FILE_MODE_READ
, 0);
856 if (Source
== NULL
) {
857 AsciiPrint("Source file open error.\n");
858 Status
= EFI_NOT_FOUND
;
862 Destination
= EfiOpen(DestinationFile
, EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
863 if (Destination
== NULL
) {
864 AsciiPrint("Destination file open error.\n");
865 Status
= EFI_NOT_FOUND
;
869 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
870 if (Buffer
== NULL
) {
871 Status
= EFI_OUT_OF_RESOURCES
;
875 Size
= EfiTell(Source
, NULL
);
877 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
878 Chunk
= FILE_COPY_CHUNK
;
880 Status
= EfiRead(Source
, Buffer
, &Chunk
);
881 if (EFI_ERROR(Status
)) {
882 AsciiPrint("Read file error\n");
886 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
887 if (EFI_ERROR(Status
)) {
888 AsciiPrint("Write file error\n");
895 Chunk
= Size
- Offset
;
897 Status
= EfiRead(Source
, Buffer
, &Chunk
);
898 if (EFI_ERROR(Status
)) {
899 AsciiPrint("Read file error\n");
903 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
904 if (EFI_ERROR(Status
)) {
905 AsciiPrint("Write file error\n");
911 if (Source
!= NULL
) {
912 Status
= EfiClose(Source
);
913 if (EFI_ERROR(Status
)) {
914 AsciiPrint("Source close error");
918 if (Destination
!= NULL
) {
919 Status
= EfiClose(Destination
);
920 if (EFI_ERROR(Status
)) {
921 AsciiPrint("Destination close error");
925 if (Buffer
!= NULL
) {
933 Use DeviceType and Index to form a valid PathName and try and open it.
935 @param DeviceType Device type to open
936 @param Index Device Index to use. Zero relative.
938 @return NULL Open failed
939 @return Valid EFI_OPEN_FILE handle
943 EfiDeviceOpenByType (
944 IN EFI_OPEN_FILE_TYPE DeviceType
,
949 CHAR8 Path
[MAX_CMD_LINE
];
951 switch (DeviceType
) {
952 case EfiOpenLoadFile
:
953 DevStr
= "loadfile%d:";
955 case EfiOpenFirmwareVolume
:
958 case EfiOpenFileSystem
:
964 case EfiOpenMemoryBuffer
:
971 AsciiSPrint (Path
, MAX_PATHNAME
, DevStr
, Index
);
973 return EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
978 Close a file handle opened by EfiOpen() and free all resources allocated by
981 @param Stream Open File Handle
983 @return EFI_INVALID_PARAMETER Stream is not an Open File
984 @return EFI_SUCCESS Steam closed
989 IN EFI_OPEN_FILE
*File
993 UINT64 TftpBufferSize
;
995 if (!FileHandleValid (File
)) {
996 return EFI_INVALID_PARAMETER
;
999 //Write the buffer contents to TFTP file.
1000 if ((File
->Type
== EfiOpenTftp
) && (File
->IsDirty
)) {
1002 TftpBufferSize
= File
->Size
;
1004 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
,
1010 (UINT8
*)File
->FileName
,
1014 if (EFI_ERROR(Status
)) {
1015 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status
);
1020 if ((File
->Type
== EfiOpenLoadFile
) ||
1021 ((File
->Type
== EfiOpenTftp
) && (File
->IsBufferValid
== TRUE
))) {
1022 EblFreePool(File
->Buffer
);
1025 EblFreePool (File
->DevicePath
);
1026 EblFreePool (File
->DeviceName
);
1027 EblFreePool (File
->FsFileInfo
);
1028 EblFreePool (File
->FsInfo
);
1030 if (File
->FsFileHandle
!= NULL
) {
1031 File
->FsFileHandle
->Close (File
->FsFileHandle
);
1034 // Need to free File and it's Guard structures
1035 EblFreePool (BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
));
1041 Return the size of the file represented by Stream. Also return the current
1042 Seek position. Opening a file will enable a valid file size to be returned.
1043 LoadFile is an exception as a load file size is set to zero.
1045 @param Stream Open File Handle
1047 @return 0 Stream is not an Open File or a valid LoadFile handle
1052 IN EFI_OPEN_FILE
*File
,
1053 OUT EFI_LBA
*CurrentPosition OPTIONAL
1057 UINT64 BufferSize
= 0;
1059 if (!FileHandleValid (File
)) {
1063 if (CurrentPosition
!= NULL
) {
1064 *CurrentPosition
= File
->CurrentPosition
;
1067 if (File
->Type
== EfiOpenLoadFile
) {
1068 // Figure out the File->Size
1069 File
->Buffer
= NULL
;
1071 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, &File
->Size
, File
->Buffer
);
1072 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1076 File
->MaxPosition
= (UINT64
)File
->Size
;
1077 } else if (File
->Type
== EfiOpenTftp
) {
1080 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1086 (UINT8
*)File
->FileName
,
1090 if (EFI_ERROR(Status
)) {
1091 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status
);
1095 File
->Size
= (UINTN
)BufferSize
;
1096 File
->MaxPosition
= File
->Size
;
1104 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1105 not support EfiSeek(). It is not possible to grow the file size using
1108 SeekType defines how use Offset to calculate the new file position:
1109 EfiSeekStart : Position = Offset
1110 EfiSeekCurrent: Position is Offset bytes from the current position
1111 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1113 @param Stream Open File Handle
1114 @param Offset Offset to seek too.
1115 @param SeekType Type of seek to perform
1118 @return EFI_INVALID_PARAMETER Stream is not an Open File
1119 @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1120 @return EFI_NOT_FOUND Seek past the end of the file.
1121 @return EFI_SUCCESS Steam closed
1126 IN EFI_OPEN_FILE
*File
,
1128 IN EFI_SEEK_TYPE SeekType
1132 UINT64 CurrentPosition
;
1134 if (!FileHandleValid (File
)) {
1135 return EFI_INVALID_PARAMETER
;
1138 if (File
->Type
== EfiOpenLoadFile
|| File
->Type
== EfiOpenFirmwareVolume
) {
1139 // LoadFile and FV do not support Seek
1140 return EFI_UNSUPPORTED
;
1143 CurrentPosition
= File
->CurrentPosition
;
1146 if (Offset
> File
->MaxPosition
) {
1147 return EFI_NOT_FOUND
;
1149 CurrentPosition
= Offset
;
1152 case EfiSeekCurrent
:
1153 if ((File
->CurrentPosition
+ Offset
) > File
->MaxPosition
) {
1154 return EFI_NOT_FOUND
;
1156 CurrentPosition
+= Offset
;
1161 // We don't support growing file size via seeking past end of file
1162 return EFI_UNSUPPORTED
;
1164 CurrentPosition
= File
->MaxPosition
;
1168 return EFI_NOT_FOUND
;
1171 Status
= EFI_SUCCESS
;
1172 if (File
->FsFileHandle
!= NULL
) {
1173 Status
= File
->FsFileHandle
->SetPosition (File
->FsFileHandle
, CurrentPosition
);
1176 if (!EFI_ERROR (Status
)) {
1177 File
->CurrentPosition
= CurrentPosition
;
1185 IN OUT EFI_OPEN_FILE
*File
1189 UINT64 TftpBufferSize
;
1191 if (File
->IsBufferValid
) {
1195 // Make sure the file size is set.
1196 EfiTell (File
, NULL
);
1198 //Allocate a buffer to hold the whole file.
1199 File
->Buffer
= AllocatePool(File
->Size
);
1200 if (File
->Buffer
== NULL
) {
1201 return EFI_OUT_OF_RESOURCES
;
1204 TftpBufferSize
= File
->Size
;
1207 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1213 (UINT8
*)File
->FileName
,
1216 if (EFI_ERROR(Status
)) {
1217 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status
);
1218 FreePool(File
->Buffer
);
1222 // Set the buffer valid flag.
1223 File
->IsBufferValid
= TRUE
;
1229 Read BufferSize bytes from the current locaiton in the file. For load file,
1230 FV, and TFTP case you must read the entire file.
1232 @param Stream Open File Handle
1233 @param Buffer Caller allocated buffer.
1234 @param BufferSize Size of buffer in bytes.
1237 @return EFI_SUCCESS Stream is not an Open File
1238 @return EFI_END_OF_FILE Tried to read past the end of the file
1239 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1240 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1241 @return "other" Error returned from device read
1246 IN EFI_OPEN_FILE
*File
,
1248 OUT UINTN
*BufferSize
1252 UINT32 AuthenticationStatus
;
1253 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1255 if (!FileHandleValid (File
)) {
1256 return EFI_INVALID_PARAMETER
;
1259 // Don't read past the end of the file.
1260 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1261 return EFI_END_OF_FILE
;
1264 switch (File
->Type
) {
1265 case EfiOpenMemoryBuffer
:
1266 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1267 File
->CurrentPosition
+= *BufferSize
;
1268 Status
= EFI_SUCCESS
;
1271 case EfiOpenLoadFile
:
1272 // Figure out the File->Size
1273 EfiTell (File
, NULL
);
1275 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, BufferSize
, Buffer
);
1278 case EfiOpenFirmwareVolume
:
1279 if (File
->FvSectionType
== EFI_SECTION_ALL
) {
1280 Status
= File
->Fv
->ReadFile (
1286 &File
->FvAttributes
,
1287 &AuthenticationStatus
1290 Status
= File
->Fv
->ReadSection (
1293 File
->FvSectionType
,
1297 &AuthenticationStatus
1302 case EfiOpenFileSystem
:
1303 Status
= File
->FsFileHandle
->Read (File
->FsFileHandle
, BufferSize
, Buffer
);
1304 File
->CurrentPosition
+= *BufferSize
;
1307 case EfiOpenBlockIo
:
1308 Status
= gBS
->HandleProtocol(File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1309 if (!EFI_ERROR(Status
)) {
1310 Status
= DiskIo
->ReadDisk(DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1312 File
->CurrentPosition
+= *BufferSize
;
1316 // Cache the file if it hasn't been cached yet.
1317 if (File
->IsBufferValid
== FALSE
) {
1318 Status
= CacheTftpFile (File
);
1319 if (EFI_ERROR (Status
)) {
1324 // Copy out the requested data
1325 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1326 File
->CurrentPosition
+= *BufferSize
;
1328 Status
= EFI_SUCCESS
;
1332 return EFI_INVALID_PARAMETER
;
1340 Read the entire file into a buffer. This routine allocates the buffer and
1341 returns it to the user full of the read data.
1343 This is very useful for load flie where it's hard to know how big the buffer
1346 @param Stream Open File Handle
1347 @param Buffer Pointer to buffer to return.
1348 @param BufferSize Pointer to Size of buffer return..
1351 @return EFI_SUCCESS Stream is not an Open File
1352 @return EFI_END_OF_FILE Tried to read past the end of the file
1353 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1354 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1355 @return "other" Error returned from device read
1359 EfiReadAllocatePool (
1360 IN EFI_OPEN_FILE
*File
,
1362 OUT UINTN
*BufferSize
1365 if (!FileHandleValid (File
)) {
1366 return EFI_INVALID_PARAMETER
;
1369 // Loadfile defers file size determination on Open so use tell to find it
1370 EfiTell (File
, NULL
);
1372 *BufferSize
= File
->Size
;
1373 *Buffer
= AllocatePool (*BufferSize
);
1374 if (*Buffer
== NULL
) {
1375 return EFI_NOT_FOUND
;
1378 return EfiRead (File
, *Buffer
, BufferSize
);
1383 Write data back to the file. For TFTP case you must write the entire file.
1385 @param Stream Open File Handle
1386 @param Buffer Pointer to buffer to return.
1387 @param BufferSize Pointer to Size of buffer return..
1390 @return EFI_SUCCESS Stream is not an Open File
1391 @return EFI_END_OF_FILE Tried to read past the end of the file
1392 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1393 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1394 @return "other" Error returned from device write
1399 IN EFI_OPEN_FILE
*File
,
1401 OUT UINTN
*BufferSize
1405 EFI_FV_WRITE_FILE_DATA FileData
;
1406 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1408 if (!FileHandleValid (File
)) {
1409 return EFI_INVALID_PARAMETER
;
1412 switch (File
->Type
) {
1413 case EfiOpenMemoryBuffer
:
1414 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1415 return EFI_END_OF_FILE
;
1418 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1419 File
->CurrentPosition
+= *BufferSize
;
1420 Status
= EFI_SUCCESS
;
1422 case EfiOpenLoadFile
:
1423 // LoadFile device is read only be definition
1424 Status
= EFI_UNSUPPORTED
;
1426 case EfiOpenFirmwareVolume
:
1427 if (File
->FvSectionType
!= EFI_SECTION_ALL
) {
1428 // Writes not support to a specific section. You have to update entire file
1429 return EFI_UNSUPPORTED
;
1432 FileData
.NameGuid
= &(File
->FvNameGuid
);
1433 FileData
.Type
= File
->FvType
;
1434 FileData
.FileAttributes
= File
->FvAttributes
;
1435 FileData
.Buffer
= Buffer
;
1436 FileData
.BufferSize
= (UINT32
)*BufferSize
;
1437 Status
= File
->Fv
->WriteFile (File
->Fv
, 1, EFI_FV_UNRELIABLE_WRITE
, &FileData
);
1440 case EfiOpenFileSystem
:
1441 Status
= File
->FsFileHandle
->Write (File
->FsFileHandle
, BufferSize
, Buffer
);
1442 File
->CurrentPosition
+= *BufferSize
;
1445 case EfiOpenBlockIo
:
1446 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1447 return EFI_END_OF_FILE
;
1450 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1451 if (!EFI_ERROR(Status
)) {
1452 Status
= DiskIo
->WriteDisk (DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1454 File
->CurrentPosition
+= *BufferSize
;
1458 // Cache the file if it hasn't been cached yet.
1459 if (File
->IsBufferValid
== FALSE
) {
1460 Status
= CacheTftpFile(File
);
1461 if (EFI_ERROR(Status
)) {
1466 // Don't overwrite the buffer
1467 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1470 TempBuffer
= File
->Buffer
;
1472 File
->Buffer
= AllocatePool ((UINTN
)(File
->CurrentPosition
+ *BufferSize
));
1473 if (File
->Buffer
== NULL
) {
1474 return EFI_OUT_OF_RESOURCES
;
1477 CopyMem (File
->Buffer
, TempBuffer
, File
->Size
);
1479 FreePool (TempBuffer
);
1481 File
->Size
= (UINTN
)(File
->CurrentPosition
+ *BufferSize
);
1482 File
->MaxPosition
= (UINT64
)File
->Size
;
1485 // Copy in the requested data
1486 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1487 File
->CurrentPosition
+= *BufferSize
;
1489 // Mark the file dirty
1490 File
->IsDirty
= TRUE
;
1492 Status
= EFI_SUCCESS
;
1496 Status
= EFI_INVALID_PARAMETER
;
1505 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1506 the path does not contain a device name, The CWD is prepended to the path.
1508 @param Cwd Current Working Directory to set
1511 @return EFI_SUCCESS CWD is set
1512 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1520 EFI_OPEN_FILE
*File
;
1522 File
= EfiOpen (Cwd
, EFI_FILE_MODE_READ
, 0);
1524 return EFI_INVALID_PARAMETER
;
1533 gCwd
= AllocatePool (AsciiStrSize (Cwd
));
1535 return EFI_INVALID_PARAMETER
;
1537 AsciiStrCpy (gCwd
, Cwd
);
1543 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1544 the path does not contain a device name, The CWD is prepended to the path.
1545 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1546 a call to EfiSetCwd() it is not legal to use the pointer returned by
1549 @param Cwd Current Working Directory
1552 @return NULL No CWD set
1553 @return 'other' Returns buffer that contains CWD.