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
== StrLen
) {
668 // We could add a current working diretory concept
669 CwdPlusPathName
= AllocatePool (AsciiStrSize (gCwd
) + AsciiStrSize (PathName
));
670 if (CwdPlusPathName
== NULL
) {
674 if ((PathName
[0] == '/') || (PathName
[0] == '\\')) {
675 // PathName starts in / so this means we go to the root of the device in the CWD.
676 CwdPlusPathName
[0] = '\0';
677 for (FileStart
= 0; gCwd
[FileStart
] != '\0'; FileStart
++) {
678 CwdPlusPathName
[FileStart
] = gCwd
[FileStart
];
679 if (gCwd
[FileStart
] == ':') {
681 CwdPlusPathName
[FileStart
] = '\0';
686 AsciiStrCpy (CwdPlusPathName
, gCwd
);
687 StrLen
= AsciiStrLen (gCwd
);
688 if ((*PathName
!= '/') && (*PathName
!= '\\') && (gCwd
[StrLen
-1] != '/') && (gCwd
[StrLen
-1] != '\\')) {
689 AsciiStrCat (CwdPlusPathName
, "\\");
693 AsciiStrCat (CwdPlusPathName
, PathName
);
694 if (AsciiStrStr (CwdPlusPathName
, ":") == NULL
) {
695 // Extra error check to make sure we don't recusre and blow stack
699 File
= EfiOpen (CwdPlusPathName
, OpenMode
, SectionType
);
700 FreePool (CwdPlusPathName
);
705 // Matching volume name has precedence over handle based names
707 VolumeNameMatch
= EblMatchVolumeName (PathName
, FileStart
, &DevNumber
);
708 if (!VolumeNameMatch
) {
709 DevNumber
= EblConvertDevStringToNumber ((CHAR8
*)PathName
);
712 File
->DeviceName
= AllocatePool (StrLen
);
713 AsciiStrCpy (File
->DeviceName
, PathName
);
714 File
->DeviceName
[FileStart
- 1] = '\0';
715 File
->FileName
= &File
->DeviceName
[FileStart
];
716 if (File
->FileName
[0] == '\0') {
717 // if it is just a file name use / as root
718 File
->FileName
= "\\";
722 // Use best match algorithm on the dev names so we only need to look at the
723 // first few charters to match the full device name. Short name forms are
724 // legal from the caller.
726 Status
= EFI_SUCCESS
;
727 if (*PathName
== 'f' || *PathName
== 'F' || VolumeNameMatch
) {
728 if (PathName
[1] == 's' || PathName
[1] == 'S' || VolumeNameMatch
) {
729 if (DevNumber
>= mFsCount
) {
732 File
->Type
= EfiOpenFileSystem
;
733 File
->EfiHandle
= mFs
[DevNumber
];
734 Status
= EblFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
736 } else if (PathName
[1] == 'v' || PathName
[1] == 'V') {
737 if (DevNumber
>= mFvCount
) {
740 File
->Type
= EfiOpenFirmwareVolume
;
741 File
->EfiHandle
= mFv
[DevNumber
];
743 if ((PathName
[FileStart
] == '/') || (PathName
[FileStart
] == '\\')) {
744 // Skip leading / as its not really needed for the FV since no directories are supported
747 Status
= EblFvFileDevicePath (File
, &PathName
[FileStart
], OpenMode
);
749 } else if ((*PathName
== 'A') || (*PathName
== 'a')) {
750 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
751 File
->Type
= EfiOpenMemoryBuffer
;
752 // 1st colon is at PathName[FileStart - 1]
753 File
->Buffer
= (VOID
*)AsciiStrHexToUintn (&PathName
[FileStart
]);
756 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
760 // If we ran out of string, there's no extra data
761 if (PathName
[FileStart
] == '\0') {
764 File
->Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
767 // if there's no number after the second colon, default
769 if (File
->Size
== 0) {
770 File
->Size
= (UINTN
)(0 - (UINTN
)File
->Buffer
);
773 File
->MaxPosition
= File
->Size
;
774 File
->BaseOffset
= (UINTN
)File
->Buffer
;
776 } else if (*PathName
== 'l' || *PathName
== 'L') {
777 if (DevNumber
>= mLoadFileCount
) {
780 File
->Type
= EfiOpenLoadFile
;
781 File
->EfiHandle
= mLoadFile
[DevNumber
];
783 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&File
->LoadFile
);
784 if (EFI_ERROR (Status
)) {
788 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&DevicePath
);
789 if (EFI_ERROR (Status
)) {
792 File
->DevicePath
= DuplicateDevicePath (DevicePath
);
794 } else if (*PathName
== 'b' || *PathName
== 'B') {
795 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
796 if (DevNumber
>= mBlkIoCount
) {
799 File
->Type
= EfiOpenBlockIo
;
800 File
->EfiHandle
= mBlkIo
[DevNumber
];
801 EblFileDevicePath (File
, "", OpenMode
);
803 // 1st colon is at PathName[FileStart - 1]
804 File
->DiskOffset
= AsciiStrHexToUintn (&PathName
[FileStart
]);
807 while ((PathName
[FileStart
] != ':') && (PathName
[FileStart
] != '\0')) {
811 // If we ran out of string, there's no extra data
812 if (PathName
[FileStart
] == '\0') {
815 Size
= AsciiStrHexToUintn (&PathName
[FileStart
+ 1]);
818 // if a zero size is passed in (or the size is left out entirely),
819 // go to the end of the device.
821 File
->Size
= File
->Size
- File
->DiskOffset
;
826 File
->MaxPosition
= File
->Size
;
827 File
->BaseOffset
= File
->DiskOffset
;
828 } else if ((*PathName
) >= '0' && (*PathName
<= '9')) {
830 // Get current IP address
831 Status
= EblGetCurrentIpAddress (&Ip
);
832 if (EFI_ERROR(Status
)) {
833 AsciiPrint("Device IP Address is not configured.\n");
838 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
839 File
->Type
= EfiOpenTftp
;
840 File
->IsDirty
= FALSE
;
841 File
->IsBufferValid
= FALSE
;
843 Status
= ConvertIpStringToEfiIp (PathName
, &File
->ServerIp
);
846 if (EFI_ERROR (Status
)) {
850 GuardFile
= (EFI_OPEN_FILE_GUARD
*)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD
));
851 if (GuardFile
== NULL
) {
855 GuardFile
->Header
= EFI_OPEN_FILE_GUARD_HEADER
;
856 CopyMem (&(GuardFile
->File
), &FileData
, sizeof (EFI_OPEN_FILE
));
857 GuardFile
->Footer
= EFI_OPEN_FILE_GUARD_FOOTER
;
859 return &(GuardFile
->File
);
862 FreePool (File
->DeviceName
);
866 #define FILE_COPY_CHUNK 0x01000000
870 IN CHAR8
*DestinationFile
,
874 EFI_OPEN_FILE
*Source
= NULL
;
875 EFI_OPEN_FILE
*Destination
= NULL
;
876 EFI_STATUS Status
= EFI_SUCCESS
;
880 UINTN Chunk
= FILE_COPY_CHUNK
;
882 Source
= EfiOpen(SourceFile
, EFI_FILE_MODE_READ
, 0);
883 if (Source
== NULL
) {
884 AsciiPrint("Source file open error.\n");
885 Status
= EFI_NOT_FOUND
;
889 Destination
= EfiOpen(DestinationFile
, EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
, 0);
890 if (Destination
== NULL
) {
891 AsciiPrint("Destination file open error.\n");
892 Status
= EFI_NOT_FOUND
;
896 Buffer
= AllocatePool(FILE_COPY_CHUNK
);
897 if (Buffer
== NULL
) {
898 Status
= EFI_OUT_OF_RESOURCES
;
902 Size
= EfiTell(Source
, NULL
);
904 for (Offset
= 0; Offset
+ FILE_COPY_CHUNK
<= Size
; Offset
+= Chunk
) {
905 Chunk
= FILE_COPY_CHUNK
;
907 Status
= EfiRead(Source
, Buffer
, &Chunk
);
908 if (EFI_ERROR(Status
)) {
909 AsciiPrint("Read file error\n");
913 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
914 if (EFI_ERROR(Status
)) {
915 AsciiPrint("Write file error\n");
922 Chunk
= Size
- Offset
;
924 Status
= EfiRead(Source
, Buffer
, &Chunk
);
925 if (EFI_ERROR(Status
)) {
926 AsciiPrint("Read file error\n");
930 Status
= EfiWrite(Destination
, Buffer
, &Chunk
);
931 if (EFI_ERROR(Status
)) {
932 AsciiPrint("Write file error\n");
938 if (Source
!= NULL
) {
939 Status
= EfiClose(Source
);
940 if (EFI_ERROR(Status
)) {
941 AsciiPrint("Source close error");
945 if (Destination
!= NULL
) {
946 Status
= EfiClose(Destination
);
947 if (EFI_ERROR(Status
)) {
948 AsciiPrint("Destination close error");
952 if (Buffer
!= NULL
) {
960 Use DeviceType and Index to form a valid PathName and try and open it.
962 @param DeviceType Device type to open
963 @param Index Device Index to use. Zero relative.
965 @return NULL Open failed
966 @return Valid EFI_OPEN_FILE handle
970 EfiDeviceOpenByType (
971 IN EFI_OPEN_FILE_TYPE DeviceType
,
976 CHAR8 Path
[MAX_CMD_LINE
];
978 switch (DeviceType
) {
979 case EfiOpenLoadFile
:
980 DevStr
= "loadfile%d:";
982 case EfiOpenFirmwareVolume
:
985 case EfiOpenFileSystem
:
991 case EfiOpenMemoryBuffer
:
998 AsciiSPrint (Path
, MAX_PATHNAME
, DevStr
, Index
);
1000 return EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1005 Close a file handle opened by EfiOpen() and free all resources allocated by
1008 @param Stream Open File Handle
1010 @return EFI_INVALID_PARAMETER Stream is not an Open File
1011 @return EFI_SUCCESS Steam closed
1016 IN EFI_OPEN_FILE
*File
1020 UINT64 TftpBufferSize
;
1022 if (!FileHandleValid (File
)) {
1023 return EFI_INVALID_PARAMETER
;
1026 //Write the buffer contents to TFTP file.
1027 if ((File
->Type
== EfiOpenTftp
) && (File
->IsDirty
)) {
1029 TftpBufferSize
= File
->Size
;
1031 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE
,
1037 (UINT8
*)File
->FileName
,
1041 if (EFI_ERROR(Status
)) {
1042 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status
);
1047 if ((File
->Type
== EfiOpenLoadFile
) ||
1048 ((File
->Type
== EfiOpenTftp
) && (File
->IsBufferValid
== TRUE
))) {
1049 EblFreePool(File
->Buffer
);
1052 EblFreePool (File
->DevicePath
);
1053 EblFreePool (File
->DeviceName
);
1054 EblFreePool (File
->FsFileInfo
);
1055 EblFreePool (File
->FsInfo
);
1057 if (File
->FsFileHandle
!= NULL
) {
1058 File
->FsFileHandle
->Close (File
->FsFileHandle
);
1061 // Need to free File and it's Guard structures
1062 EblFreePool (BASE_CR (File
, EFI_OPEN_FILE_GUARD
, File
));
1068 Return the size of the file represented by Stream. Also return the current
1069 Seek position. Opening a file will enable a valid file size to be returned.
1070 LoadFile is an exception as a load file size is set to zero.
1072 @param Stream Open File Handle
1074 @return 0 Stream is not an Open File or a valid LoadFile handle
1079 IN EFI_OPEN_FILE
*File
,
1080 OUT EFI_LBA
*CurrentPosition OPTIONAL
1084 UINT64 BufferSize
= 0;
1086 if (!FileHandleValid (File
)) {
1090 if (CurrentPosition
!= NULL
) {
1091 *CurrentPosition
= File
->CurrentPosition
;
1094 if (File
->Type
== EfiOpenLoadFile
) {
1095 // Figure out the File->Size
1096 File
->Buffer
= NULL
;
1098 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, &File
->Size
, File
->Buffer
);
1099 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
1103 File
->MaxPosition
= (UINT64
)File
->Size
;
1104 } else if (File
->Type
== EfiOpenTftp
) {
1107 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE
,
1113 (UINT8
*)File
->FileName
,
1117 if (EFI_ERROR(Status
)) {
1118 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status
);
1122 File
->Size
= (UINTN
)BufferSize
;
1123 File
->MaxPosition
= File
->Size
;
1131 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1132 not support EfiSeek(). It is not possible to grow the file size using
1135 SeekType defines how use Offset to calculate the new file position:
1136 EfiSeekStart : Position = Offset
1137 EfiSeekCurrent: Position is Offset bytes from the current position
1138 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1140 @param Stream Open File Handle
1141 @param Offset Offset to seek too.
1142 @param SeekType Type of seek to perform
1145 @return EFI_INVALID_PARAMETER Stream is not an Open File
1146 @return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1147 @return EFI_NOT_FOUND Seek past the end of the file.
1148 @return EFI_SUCCESS Steam closed
1153 IN EFI_OPEN_FILE
*File
,
1155 IN EFI_SEEK_TYPE SeekType
1159 UINT64 CurrentPosition
;
1161 if (!FileHandleValid (File
)) {
1162 return EFI_INVALID_PARAMETER
;
1165 if (File
->Type
== EfiOpenLoadFile
|| File
->Type
== EfiOpenFirmwareVolume
) {
1166 // LoadFile and FV do not support Seek
1167 return EFI_UNSUPPORTED
;
1170 CurrentPosition
= File
->CurrentPosition
;
1173 if (Offset
> File
->MaxPosition
) {
1174 return EFI_NOT_FOUND
;
1176 CurrentPosition
= Offset
;
1179 case EfiSeekCurrent
:
1180 if ((File
->CurrentPosition
+ Offset
) > File
->MaxPosition
) {
1181 return EFI_NOT_FOUND
;
1183 CurrentPosition
+= Offset
;
1188 // We don't support growing file size via seeking past end of file
1189 return EFI_UNSUPPORTED
;
1191 CurrentPosition
= File
->MaxPosition
;
1195 return EFI_NOT_FOUND
;
1198 Status
= EFI_SUCCESS
;
1199 if (File
->FsFileHandle
!= NULL
) {
1200 Status
= File
->FsFileHandle
->SetPosition (File
->FsFileHandle
, CurrentPosition
);
1203 if (!EFI_ERROR (Status
)) {
1204 File
->CurrentPosition
= CurrentPosition
;
1212 IN OUT EFI_OPEN_FILE
*File
1216 UINT64 TftpBufferSize
;
1218 if (File
->IsBufferValid
) {
1222 // Make sure the file size is set.
1223 EfiTell (File
, NULL
);
1225 //Allocate a buffer to hold the whole file.
1226 File
->Buffer
= AllocatePool(File
->Size
);
1227 if (File
->Buffer
== NULL
) {
1228 return EFI_OUT_OF_RESOURCES
;
1231 TftpBufferSize
= File
->Size
;
1234 EFI_PXE_BASE_CODE_TFTP_READ_FILE
,
1240 (UINT8
*)File
->FileName
,
1243 if (EFI_ERROR(Status
)) {
1244 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status
);
1245 FreePool(File
->Buffer
);
1249 // Set the buffer valid flag.
1250 File
->IsBufferValid
= TRUE
;
1256 Read BufferSize bytes from the current locaiton in the file. For load file,
1257 FV, and TFTP case you must read the entire file.
1259 @param Stream Open File Handle
1260 @param Buffer Caller allocated buffer.
1261 @param BufferSize Size of buffer in bytes.
1264 @return EFI_SUCCESS Stream is not an Open File
1265 @return EFI_END_OF_FILE Tried to read past the end of the file
1266 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1267 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1268 @return "other" Error returned from device read
1273 IN EFI_OPEN_FILE
*File
,
1275 OUT UINTN
*BufferSize
1279 UINT32 AuthenticationStatus
;
1280 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1282 if (!FileHandleValid (File
)) {
1283 return EFI_INVALID_PARAMETER
;
1286 // Don't read past the end of the file.
1287 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1288 return EFI_END_OF_FILE
;
1291 switch (File
->Type
) {
1292 case EfiOpenMemoryBuffer
:
1293 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1294 File
->CurrentPosition
+= *BufferSize
;
1295 Status
= EFI_SUCCESS
;
1298 case EfiOpenLoadFile
:
1299 // Figure out the File->Size
1300 EfiTell (File
, NULL
);
1302 Status
= File
->LoadFile
->LoadFile (File
->LoadFile
, File
->DevicePath
, FALSE
, BufferSize
, Buffer
);
1305 case EfiOpenFirmwareVolume
:
1306 if (File
->FvSectionType
== EFI_SECTION_ALL
) {
1307 Status
= File
->Fv
->ReadFile (
1313 &File
->FvAttributes
,
1314 &AuthenticationStatus
1317 Status
= File
->Fv
->ReadSection (
1320 File
->FvSectionType
,
1324 &AuthenticationStatus
1329 case EfiOpenFileSystem
:
1330 Status
= File
->FsFileHandle
->Read (File
->FsFileHandle
, BufferSize
, Buffer
);
1331 File
->CurrentPosition
+= *BufferSize
;
1334 case EfiOpenBlockIo
:
1335 Status
= gBS
->HandleProtocol(File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1336 if (!EFI_ERROR(Status
)) {
1337 Status
= DiskIo
->ReadDisk(DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1339 File
->CurrentPosition
+= *BufferSize
;
1343 // Cache the file if it hasn't been cached yet.
1344 if (File
->IsBufferValid
== FALSE
) {
1345 Status
= CacheTftpFile (File
);
1346 if (EFI_ERROR (Status
)) {
1351 // Copy out the requested data
1352 CopyMem (Buffer
, File
->Buffer
+ File
->CurrentPosition
, *BufferSize
);
1353 File
->CurrentPosition
+= *BufferSize
;
1355 Status
= EFI_SUCCESS
;
1359 return EFI_INVALID_PARAMETER
;
1367 Read the entire file into a buffer. This routine allocates the buffer and
1368 returns it to the user full of the read data.
1370 This is very useful for load flie where it's hard to know how big the buffer
1373 @param Stream Open File Handle
1374 @param Buffer Pointer to buffer to return.
1375 @param BufferSize Pointer to Size of buffer return..
1378 @return EFI_SUCCESS Stream is not an Open File
1379 @return EFI_END_OF_FILE Tried to read past the end of the file
1380 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1381 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1382 @return "other" Error returned from device read
1386 EfiReadAllocatePool (
1387 IN EFI_OPEN_FILE
*File
,
1389 OUT UINTN
*BufferSize
1392 if (!FileHandleValid (File
)) {
1393 return EFI_INVALID_PARAMETER
;
1396 // Loadfile defers file size determination on Open so use tell to find it
1397 EfiTell (File
, NULL
);
1399 *BufferSize
= File
->Size
;
1400 *Buffer
= AllocatePool (*BufferSize
);
1401 if (*Buffer
== NULL
) {
1402 return EFI_NOT_FOUND
;
1405 return EfiRead (File
, *Buffer
, BufferSize
);
1410 Write data back to the file. For TFTP case you must write the entire file.
1412 @param Stream Open File Handle
1413 @param Buffer Pointer to buffer to return.
1414 @param BufferSize Pointer to Size of buffer return..
1417 @return EFI_SUCCESS Stream is not an Open File
1418 @return EFI_END_OF_FILE Tried to read past the end of the file
1419 @return EFI_INVALID_PARAMETER Stream is not an open file handle
1420 @return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1421 @return "other" Error returned from device write
1426 IN EFI_OPEN_FILE
*File
,
1428 OUT UINTN
*BufferSize
1432 EFI_FV_WRITE_FILE_DATA FileData
;
1433 EFI_DISK_IO_PROTOCOL
*DiskIo
;
1435 if (!FileHandleValid (File
)) {
1436 return EFI_INVALID_PARAMETER
;
1439 switch (File
->Type
) {
1440 case EfiOpenMemoryBuffer
:
1441 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1442 return EFI_END_OF_FILE
;
1445 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1446 File
->CurrentPosition
+= *BufferSize
;
1447 Status
= EFI_SUCCESS
;
1449 case EfiOpenLoadFile
:
1450 // LoadFile device is read only be definition
1451 Status
= EFI_UNSUPPORTED
;
1453 case EfiOpenFirmwareVolume
:
1454 if (File
->FvSectionType
!= EFI_SECTION_ALL
) {
1455 // Writes not support to a specific section. You have to update entire file
1456 return EFI_UNSUPPORTED
;
1459 FileData
.NameGuid
= &(File
->FvNameGuid
);
1460 FileData
.Type
= File
->FvType
;
1461 FileData
.FileAttributes
= File
->FvAttributes
;
1462 FileData
.Buffer
= Buffer
;
1463 FileData
.BufferSize
= (UINT32
)*BufferSize
;
1464 Status
= File
->Fv
->WriteFile (File
->Fv
, 1, EFI_FV_UNRELIABLE_WRITE
, &FileData
);
1467 case EfiOpenFileSystem
:
1468 Status
= File
->FsFileHandle
->Write (File
->FsFileHandle
, BufferSize
, Buffer
);
1469 File
->CurrentPosition
+= *BufferSize
;
1472 case EfiOpenBlockIo
:
1473 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1474 return EFI_END_OF_FILE
;
1477 Status
= gBS
->HandleProtocol (File
->EfiHandle
, &gEfiDiskIoProtocolGuid
, (VOID
**)&DiskIo
);
1478 if (!EFI_ERROR(Status
)) {
1479 Status
= DiskIo
->WriteDisk (DiskIo
, File
->FsBlockIoMedia
.MediaId
, File
->DiskOffset
+ File
->CurrentPosition
, *BufferSize
, Buffer
);
1481 File
->CurrentPosition
+= *BufferSize
;
1485 // Cache the file if it hasn't been cached yet.
1486 if (File
->IsBufferValid
== FALSE
) {
1487 Status
= CacheTftpFile(File
);
1488 if (EFI_ERROR(Status
)) {
1493 // Don't overwrite the buffer
1494 if ((File
->CurrentPosition
+ *BufferSize
) > File
->MaxPosition
) {
1497 TempBuffer
= File
->Buffer
;
1499 File
->Buffer
= AllocatePool ((UINTN
)(File
->CurrentPosition
+ *BufferSize
));
1500 if (File
->Buffer
== NULL
) {
1501 return EFI_OUT_OF_RESOURCES
;
1504 CopyMem (File
->Buffer
, TempBuffer
, File
->Size
);
1506 FreePool (TempBuffer
);
1508 File
->Size
= (UINTN
)(File
->CurrentPosition
+ *BufferSize
);
1509 File
->MaxPosition
= (UINT64
)File
->Size
;
1512 // Copy in the requested data
1513 CopyMem (File
->Buffer
+ File
->CurrentPosition
, Buffer
, *BufferSize
);
1514 File
->CurrentPosition
+= *BufferSize
;
1516 // Mark the file dirty
1517 File
->IsDirty
= TRUE
;
1519 Status
= EFI_SUCCESS
;
1523 Status
= EFI_INVALID_PARAMETER
;
1531 Given Cwd expand Path to remove .. and replace them with real
1534 @param Cwd Current Working Directory
1535 @param Path Path to expand
1537 @return NULL Cwd or Path are not valid
1538 @return 'other' Path with .. expanded
1548 CHAR8
*Work
, *Start
, *End
;
1552 if (Cwd
== NULL
|| Path
== NULL
) {
1556 StrLen
= AsciiStrSize (Cwd
);
1558 // Smallest valid path is 1 char and a null
1562 StrLen
= AsciiStrSize (Path
);
1563 NewPath
= AllocatePool (AsciiStrSize (Cwd
) + StrLen
+ 1);
1564 if (NewPath
== NULL
) {
1567 AsciiStrCpy (NewPath
, Cwd
);
1569 End
= Path
+ StrLen
;
1570 for (Start
= Path
;;) {
1571 Work
= AsciiStrStr (Start
, "..") ;
1573 // Remaining part of Path contains no more ..
1577 // append path prior to ..
1578 AsciiStrnCat (NewPath
, Start
, Work
- Start
);
1579 StrLen
= AsciiStrLen (NewPath
);
1580 for (i
= StrLen
; i
>= 0; i
--) {
1581 if (NewPath
[i
] == ':') {
1585 if (NewPath
[i
] == '/' || NewPath
[i
] == '\\') {
1586 if ((i
> 0) && (NewPath
[i
-1] == ':')) {
1587 // leave the / before a :
1588 NewPath
[i
+1] = '\0';
1590 // replace / will Null to remove trailing file/dir reference
1600 // Handle the path that remains after the ..
1601 AsciiStrnCat (NewPath
, Start
, End
- Start
);
1608 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1609 the path does not contain a device name, The CWD is prepended to the path.
1611 @param Cwd Current Working Directory to set
1614 @return EFI_SUCCESS CWD is set
1615 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1623 EFI_OPEN_FILE
*File
;
1628 return EFI_INVALID_PARAMETER
;
1631 if (AsciiStrCmp (Cwd
, ".") == 0) {
1637 if (AsciiStrStr (Cwd
, "..") != NULL
) {
1643 Len
= AsciiStrLen (gCwd
);
1644 if ((gCwd
[Len
-2] == ':') && ((gCwd
[Len
-1] == '/') || (gCwd
[Len
-1] == '\\'))) {
1645 // parent is device so nothing to do
1649 // Expand .. in Cwd, given we know current working directory
1650 Path
= ExpandPath (gCwd
, Cwd
);
1652 return EFI_NOT_FOUND
;
1656 File
= EfiOpen (Path
, EFI_FILE_MODE_READ
, 0);
1658 return EFI_INVALID_PARAMETER
;
1665 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1666 // relative to the current gCwd or not.
1667 gCwd
= AllocatePool (AsciiStrSize (File
->DeviceName
) + AsciiStrSize (File
->FileName
) + 1);
1669 return EFI_INVALID_PARAMETER
;
1671 AsciiStrCpy (gCwd
, File
->DeviceName
);
1672 if (File
->FileName
== NULL
) {
1673 AsciiStrCat (gCwd
, ":\\");
1675 AsciiStrCat (gCwd
, ":");
1676 AsciiStrCat (gCwd
, File
->FileName
);
1688 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1689 the path does not contain a device name, The CWD is prepended to the path.
1690 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1691 a call to EfiSetCwd() it is not legal to use the pointer returned by
1694 @param Cwd Current Working Directory
1697 @return "" No CWD set
1698 @return 'other' Returns buffer that contains CWD.