]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c
EmbeddedPkg: Fix mispellings
[mirror_edk2.git] / EmbeddedPkg / Library / EfiFileLib / EfiFileLib.c
CommitLineData
2ef2b01e 1/** @file
66c0b446 2File IO routines inspired by Streams with an EFI flavor
3
60274cca
HT
4Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
66c0b446 6
60274cca 7This program and the accompanying materials
66c0b446 8are licensed and made available under the terms and conditions of the BSD License
9which accompanies this distribution. The full text of the license may be found at
10http://opensource.org/licenses/bsd-license.php
11
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15Basic support for opening files on different device types. The device string
16is in the form of DevType:Path. Current DevType is required as there is no
17current mounted device concept of current working directory concept implement
18by this library.
19
7ca9e5a4 20Device names are case insensitive and only check the leading characters for
66c0b446 21unique matches. Thus the following are all the same:
22LoadFile0:
23l0:
24L0:
25Lo0:
26
27Supported Device Names:
28A0x1234:0x12 - A memory buffer starting at address 0x1234 for 0x12 bytes
29l1: - EFI LoadFile device one.
30B0: - EFI BlockIo zero.
31fs3: - EFI Simple File System device 3
32Fv2: - EFI Firmware VOlume device 2
3310.0.1.102: - TFTP service IP followed by the file name
2ef2b01e
A
34**/
35
36#include <PiDxe.h>
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>
56
57
85e385f4 58CHAR8 *gCwd = NULL;
59
a6d7123e 60CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
85e385f4 61
2ef2b01e
A
62#define EFI_OPEN_FILE_GUARD_HEADER 0x4B4D4641
63#define EFI_OPEN_FILE_GUARD_FOOTER 0x444D5A56
64
65// Need to defend against this overflowing
66#define MAX_CMD_LINE 0x200
67
68typedef struct {
69 UINT32 Header;
70 EFI_OPEN_FILE File;
71 UINT32 Footer;
72} EFI_OPEN_FILE_GUARD;
73
74
75// globals to store current open device info
76EFI_HANDLE *mBlkIo = NULL;
77UINTN mBlkIoCount = 0;
78
79EFI_HANDLE *mFs = NULL;
80UINTN mFsCount = 0;
81// mFsInfo[] array entries must match mFs[] handles
82EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;
83
84EFI_HANDLE *mFv = NULL;
85UINTN mFvCount = 0;
86EFI_HANDLE *mLoadFile = NULL;
87UINTN mLoadFileCount = 0;
88
89
90
91/**
66c0b446 92Internal worker function to validate a File handle.
2ef2b01e 93
66c0b446 94@param File Open File Handle
2ef2b01e 95
66c0b446 96@return TRUE File is valid
97@return FALSE File is not valid
2ef2b01e
A
98
99
100**/
101BOOLEAN
102FileHandleValid (
f6381f4c 103 IN EFI_OPEN_FILE *File
104 )
2ef2b01e
A
105{
106 EFI_OPEN_FILE_GUARD *GuardFile;
107
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) ||
66c0b446 111 (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
112 return FALSE;
113 }
2ef2b01e 114
66c0b446 115 return TRUE;
2ef2b01e
A
116}
117
118/**
66c0b446 119Internal worker function. If Buffer is not NULL free it.
2ef2b01e 120
66c0b446 121@param Buffer Buffer to FreePool()
2ef2b01e
A
122
123**/
124VOID
125EblFreePool (
f6381f4c 126 IN VOID *Buffer
127 )
2ef2b01e
A
128{
129 if (Buffer != NULL) {
130 FreePool (Buffer);
131 }
132}
133
134/**
66c0b446 135Update Device List Global Variables
2ef2b01e
A
136
137**/
138VOID
139EblUpdateDeviceLists (
f6381f4c 140 VOID
141 )
2ef2b01e
A
142{
143 EFI_STATUS Status;
144 UINTN Size;
145 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
146 EFI_FILE_HANDLE Root;
147 UINTN Index;
148
149 if (mBlkIo != NULL) {
150 FreePool (mBlkIo);
151 }
152 gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
153
0a0951ea 154
155
2ef2b01e
A
156 if (mFv != NULL) {
157 FreePool (mFv);
158 }
159 gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
66c0b446 160
2ef2b01e
A
161 if (mLoadFile != NULL) {
162 FreePool (mLoadFile);
163 }
164 gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &mLoadFileCount, &mLoadFile);
165
166 if (mFs != NULL) {
167 FreePool (mFs);
168 }
169
170 if (&mFsInfo[0] != NULL) {
7ca9e5a4 171 // Need to Free the mFsInfo prior to recalculating mFsCount so don't move this code
2ef2b01e
A
172 for (Index = 0; Index < mFsCount; Index++) {
173 if (mFsInfo[Index] != NULL) {
174 FreePool (mFsInfo[Index]);
175 }
176 }
177 FreePool (mFsInfo);
178 }
179
180 gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &mFsCount, &mFs);
181
182
183 mFsInfo = AllocateZeroPool (mFsCount * sizeof (EFI_FILE_SYSTEM_INFO *));
184 if (mFsInfo == NULL) {
185 // If we can't do this then we can't support file system entries
186 mFsCount = 0;
187 } else {
188 // Loop through all the file system structures and cache the file system info data
189 for (Index =0; Index < mFsCount; Index++) {
190 Status = gBS->HandleProtocol (mFs[Index], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
191 if (!EFI_ERROR (Status)) {
192 Status = Fs->OpenVolume (Fs, &Root);
193 if (!EFI_ERROR (Status)) {
194 // Get information about the volume
195 Size = 0;
196 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
197 if (Status == EFI_BUFFER_TOO_SMALL) {
198 mFsInfo[Index] = AllocatePool (Size);
199 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, mFsInfo[Index]);
200 }
201
202 Root->Close (Root);
203 }
204 }
205 }
206 }
207}
208
209
210/**
66c0b446 211PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
212Return TRUE if the <devce name> prefix of PathName matches a file system
213Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
214and it can be used with mFs[] to find the handle that needs to be opened
2ef2b01e 215
66c0b446 216@param PathName PathName to check
217@param FileStart Index of the first character of the <path>
218@param MatchIndex Index in mFsInfo[] that matches
2ef2b01e 219
66c0b446 220@return TRUE PathName matches a Volume Label and MatchIndex is valid
221@return FALSE PathName does not match a Volume Label MatchIndex undefined
2ef2b01e
A
222
223**/
224BOOLEAN
225EblMatchVolumeName (
f6381f4c 226 IN CHAR8 *PathName,
227 IN UINTN FileStart,
228 OUT UINTN *MatchIndex
229 )
2ef2b01e
A
230{
231 UINTN Index;
232 UINTN Compare;
233 UINTN VolStrLen;
234 BOOLEAN Match;
235
236 for (Index =0; Index < mFsCount; Index++) {
237 if (mFsInfo[Index] == NULL) {
238 // FsInfo is not valid so skip it
239 continue;
240 }
241 VolStrLen = StrLen (mFsInfo[Index]->VolumeLabel);
242 for (Compare = 0, Match = TRUE; Compare < (FileStart - 1); Compare++) {
243 if (Compare > VolStrLen) {
244 Match = FALSE;
245 break;
246 }
247 if (PathName[Compare] != (CHAR8)mFsInfo[Index]->VolumeLabel[Compare]) {
248 // If the VolumeLabel has a space allow a _ to match with it in addition to ' '
249 if (!((PathName[Compare] == '_') && (mFsInfo[Index]->VolumeLabel[Compare] == L' '))) {
250 Match = FALSE;
251 break;
252 }
253 }
254 }
255 if (Match) {
256 *MatchIndex = Index;
257 return TRUE;
258 }
259 }
260
261 return FALSE;
262}
263
264
265/**
66c0b446 266Return the number of devices of the current type active in the system
2ef2b01e 267
66c0b446 268@param Type Device type to check
2ef2b01e 269
66c0b446 270@return 0 Invalid type
2ef2b01e
A
271
272**/
273UINTN
274EfiGetDeviceCounts (
f6381f4c 275 IN EFI_OPEN_FILE_TYPE DeviceType
276 )
2ef2b01e
A
277{
278 switch (DeviceType) {
279 case EfiOpenLoadFile:
280 return mLoadFileCount;
281 case EfiOpenFirmwareVolume:
282 return mFvCount;
283 case EfiOpenFileSystem:
284 return mFsCount;
285 case EfiOpenBlockIo:
286 return mBlkIoCount;
287 default:
288 return 0;
289 }
290}
291
292EFI_STATUS
293ConvertIpStringToEfiIp (
f6381f4c 294 IN CHAR8 *PathName,
295 OUT EFI_IP_ADDRESS *ServerIp
296 )
2ef2b01e
A
297{
298 CHAR8 *Str;
299
300 Str = PathName;
301 ServerIp->v4.Addr[0] = (UINT8)AsciiStrDecimalToUintn (Str);
302
303 Str = AsciiStrStr (Str, ".");
304 if (Str == NULL) {
305 return EFI_DEVICE_ERROR;
306 }
307
308 ServerIp->v4.Addr[1] = (UINT8)AsciiStrDecimalToUintn (++Str);
309
310 Str = AsciiStrStr (Str, ".");
311 if (Str == NULL) {
312 return EFI_DEVICE_ERROR;
313 }
314
315 ServerIp->v4.Addr[2] = (UINT8)AsciiStrDecimalToUintn (++Str);
316
317 Str = AsciiStrStr (Str, ".");
318 if (Str == NULL) {
319 return EFI_DEVICE_ERROR;
320 }
321
322 ServerIp->v4.Addr[3] = (UINT8)AsciiStrDecimalToUintn (++Str);
66c0b446 323
2ef2b01e
A
324 return EFI_SUCCESS;
325}
326
327
328/**
66c0b446 329Internal work function to extract a device number from a string skipping
330text. Easy way to extract numbers from strings like blk7:.
2ef2b01e 331
66c0b446 332@param Str String to extract device number form
2ef2b01e 333
66c0b446 334@return -1 Device string is not valid
335@return Device #
2ef2b01e
A
336
337**/
338UINTN
339EblConvertDevStringToNumber (
f6381f4c 340 IN CHAR8 *Str
341 )
2ef2b01e
A
342{
343 UINTN Max;
344 UINTN Index;
345
346
347 // Find the first digit
348 Max = AsciiStrLen (Str);
349 for (Index = 0; !((*Str >= '0') && (*Str <= '9')) && (Index < Max); Index++) {
350 Str++;
351 }
352 if (Index == Max) {
353 return (UINTN)-1;
354 }
355
356 return AsciiStrDecimalToUintn (Str);
357}
358
359
360/**
66c0b446 361Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
2ef2b01e 362
66c0b446 363@param File Open file handle
364@param FileName Name of file after device stripped off
2ef2b01e
A
365
366
367**/
368EFI_STATUS
369EblFileDevicePath (
f6381f4c 370 IN OUT EFI_OPEN_FILE *File,
371 IN CHAR8 *FileName,
372 IN CONST UINT64 OpenMode
373 )
2ef2b01e
A
374{
375 EFI_STATUS Status;
376 UINTN Size;
377 FILEPATH_DEVICE_PATH *FilePath;
378 EFI_DEVICE_PATH_PROTOCOL *FileDevicePath;
379 CHAR16 UnicodeFileName[MAX_PATHNAME];
380 EFI_BLOCK_IO_PROTOCOL *BlkIo;
381 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
382 EFI_FILE_HANDLE Root;
383
384
385 if ( *FileName != 0 ) {
386 AsciiStrToUnicodeStr (FileName, UnicodeFileName);
387 } else {
388 AsciiStrToUnicodeStr ("\\", UnicodeFileName);
389 }
390
391 Size = StrSize (UnicodeFileName);
392 FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + sizeof (EFI_DEVICE_PATH_PROTOCOL));
393 if (FileDevicePath != NULL) {
394 FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath;
395 FilePath->Header.Type = MEDIA_DEVICE_PATH;
396 FilePath->Header.SubType = MEDIA_FILEPATH_DP;
397 CopyMem (&FilePath->PathName, UnicodeFileName, Size);
398 SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH);
399 SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header));
400
401 if (File->EfiHandle != NULL) {
402 File->DevicePath = DevicePathFromHandle (File->EfiHandle);
403 }
404
405 File->DevicePath = AppendDevicePath (File->DevicePath, FileDevicePath);
406 FreePool (FileDevicePath);
407 }
408
409 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiBlockIoProtocolGuid, (VOID **)&BlkIo);
410 if (!EFI_ERROR (Status)) {
0a0951ea 411 File->FsBlockIoMedia = BlkIo->Media;
208a8330 412 File->FsBlockIo = BlkIo;
2ef2b01e
A
413
414 // If we are not opening the device this will get over written with file info
415 File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
416 }
417
418 if (File->Type == EfiOpenFileSystem) {
419 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
420 if (!EFI_ERROR (Status)) {
421 Status = Fs->OpenVolume (Fs, &Root);
422 if (!EFI_ERROR (Status)) {
423 // Get information about the volume
424 Size = 0;
425 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
426 if (Status == EFI_BUFFER_TOO_SMALL) {
427 File->FsInfo = AllocatePool (Size);
428 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
429 }
66c0b446 430
2ef2b01e
A
431 // Get information about the file
432 Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
433 if (!EFI_ERROR (Status)) {
434 Size = 0;
435 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
436 if (Status == EFI_BUFFER_TOO_SMALL) {
437 File->FsFileInfo = AllocatePool (Size);
438 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
439 if (!EFI_ERROR (Status)) {
440 File->Size = (UINTN)File->FsFileInfo->FileSize;
441 File->MaxPosition = (UINT64)File->Size;
442 }
443 }
444 }
445
446 Root->Close (Root);
447 }
448 }
449 } else if (File->Type == EfiOpenBlockIo) {
450 File->Size = (UINTN)File->MaxPosition;
451 }
66c0b446 452
2ef2b01e
A
453 return Status;
454}
455
456#define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
457
458EFI_STATUS
459CompareGuidToString (
f6381f4c 460 IN EFI_GUID *Guid,
461 IN CHAR8 *String
462 )
2ef2b01e
A
463{
464 CHAR8 AsciiGuid[64];
465 CHAR8 *StringPtr;
466 CHAR8 *GuidPtr;
467
468 AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
469
470 StringPtr = String;
471 GuidPtr = AsciiGuid;
472
473 while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
474 // Skip dashes
475 if (*StringPtr == '-') {
476 StringPtr++;
477 continue;
478 }
479
480 if (*GuidPtr == '-') {
481 GuidPtr++;
482 continue;
483 }
484
485 if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
486 return EFI_NOT_FOUND;
487 }
488
489 StringPtr++;
490 GuidPtr++;
491 }
492
493 return EFI_SUCCESS;
494}
495
496
497/**
66c0b446 498Internal work function to fill in EFI_OPEN_FILE information for the FV
2ef2b01e 499
66c0b446 500@param File Open file handle
501@param FileName Name of file after device stripped off
2ef2b01e
A
502
503
504**/
505EFI_STATUS
506EblFvFileDevicePath (
f6381f4c 507 IN OUT EFI_OPEN_FILE *File,
508 IN CHAR8 *FileName,
509 IN CONST UINT64 OpenMode
510 )
2ef2b01e
A
511{
512 EFI_STATUS Status;
513 EFI_STATUS GetNextFileStatus;
514 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode;
515 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
516 UINTN Key;
517 UINT32 AuthenticationStatus;
518 CHAR8 AsciiSection[MAX_PATHNAME];
519 VOID *Section;
520 UINTN SectionSize;
521 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
522 EFI_LBA Lba;
523 UINTN BlockSize;
524 UINTN NumberOfBlocks;
60428d00 525 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;
526 UINTN Index;
2ef2b01e
A
527
528
529 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
530 if (EFI_ERROR (Status)) {
531 return Status;
532 }
533
a6d7123e 534 // Get FVB Info about the handle
535 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
536 if (!EFI_ERROR (Status)) {
537 Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
538 if (!EFI_ERROR (Status)) {
60428d00 539 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
540 File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
541 for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
542 File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
543 }
66c0b446 544
60428d00 545 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
a6d7123e 546 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
547 if (EFI_ERROR (Status)) {
548 break;
549 }
550 }
551 }
552 }
553
554
2ef2b01e
A
555 DevicePath = DevicePathFromHandle (File->EfiHandle);
556
557 if (*FileName == '\0') {
558 File->DevicePath = DuplicateDevicePath (DevicePath);
a6d7123e 559 File->Size = File->FvSize;
560 File->MaxPosition = File->Size;
2ef2b01e
A
561 } else {
562 Key = 0;
563 do {
564 File->FvType = EFI_FV_FILETYPE_ALL;
565 GetNextFileStatus = File->Fv->GetNextFile (
66c0b446 566 File->Fv,
567 &Key,
568 &File->FvType,
569 &File->FvNameGuid,
570 &File->FvAttributes,
571 &File->Size
572 );
2ef2b01e 573 if (!EFI_ERROR (GetNextFileStatus)) {
2ef2b01e
A
574 // Compare GUID first
575 Status = CompareGuidToString (&File->FvNameGuid, FileName);
576 if (!EFI_ERROR(Status)) {
577 break;
578 }
66c0b446 579
875c883e 580 Section = NULL;
2ef2b01e 581 Status = File->Fv->ReadSection (
66c0b446 582 File->Fv,
583 &File->FvNameGuid,
584 EFI_SECTION_USER_INTERFACE,
585 0,
586 &Section,
587 &SectionSize,
588 &AuthenticationStatus
589 );
2ef2b01e
A
590 if (!EFI_ERROR (Status)) {
591 UnicodeStrToAsciiStr (Section, AsciiSection);
592 if (AsciiStriCmp (FileName, AsciiSection) == 0) {
593 FreePool (Section);
594 break;
595 }
596 FreePool (Section);
597 }
598 }
599 } while (!EFI_ERROR (GetNextFileStatus));
600
601 if (EFI_ERROR (GetNextFileStatus)) {
602 return GetNextFileStatus;
603 }
604
875c883e 605 if (OpenMode != EFI_SECTION_ALL) {
606 // Calculate the size of the section we are targeting
607 Section = NULL;
608 File->Size = 0;
609 Status = File->Fv->ReadSection (
66c0b446 610 File->Fv,
611 &File->FvNameGuid,
612 (EFI_SECTION_TYPE)OpenMode,
613 0,
614 &Section,
615 &File->Size,
616 &AuthenticationStatus
617 );
875c883e 618 if (EFI_ERROR (Status)) {
619 return Status;
620 }
621 }
622
2ef2b01e
A
623 File->MaxPosition = File->Size;
624 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
625 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
626 }
627
66c0b446 628
2ef2b01e
A
629 // FVB not required if FV was soft loaded...
630 return EFI_SUCCESS;
631}
632
633
634
635
636/**
66c0b446 637Open a device named by PathName. The PathName includes a device name and
7ca9e5a4 638path separated by a :. See file header for more details on the PathName
66c0b446 639syntax. There is no checking to prevent a file from being opened more than
640one type.
2ef2b01e 641
66c0b446 642SectionType is only used to open an FV. Each file in an FV contains multiple
7ca9e5a4 643sections and only the SectionType section is opened.
2ef2b01e 644
66c0b446 645For any file that is opened with EfiOpen() must be closed with EfiClose().
2ef2b01e 646
66c0b446 647@param PathName Path to parse to open
648@param OpenMode Same as EFI_FILE.Open()
649@param SectionType Section in FV to open.
2ef2b01e 650
66c0b446 651@return NULL Open failed
652@return Valid EFI_OPEN_FILE handle
2ef2b01e
A
653
654**/
655EFI_OPEN_FILE *
656EfiOpen (
f6381f4c 657 IN CHAR8 *PathName,
658 IN CONST UINT64 OpenMode,
659 IN CONST EFI_SECTION_TYPE SectionType
660 )
2ef2b01e
A
661{
662 EFI_STATUS Status;
663 EFI_OPEN_FILE *File;
664 EFI_OPEN_FILE FileData;
665 UINTN StrLen;
666 UINTN FileStart;
667 UINTN DevNumber = 0;
668 EFI_OPEN_FILE_GUARD *GuardFile;
669 BOOLEAN VolumeNameMatch;
670 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
671 UINTN Size;
672 EFI_IP_ADDRESS Ip;
85e385f4 673 CHAR8 *CwdPlusPathName;
875c883e 674 UINTN Index;
675 EFI_SECTION_TYPE ModifiedSectionType;
2ef2b01e
A
676
677 EblUpdateDeviceLists ();
66c0b446 678
2ef2b01e
A
679 File = &FileData;
680 ZeroMem (File, sizeof (EFI_OPEN_FILE));
2ef2b01e
A
681
682 StrLen = AsciiStrSize (PathName);
16ccac42 683 if (StrLen <= 1) {
2ef2b01e
A
684 // Smallest valid path is 1 char and a null
685 return NULL;
686 }
687
688 for (FileStart = 0; FileStart < StrLen; FileStart++) {
689 if (PathName[FileStart] == ':') {
690 FileStart++;
691 break;
692 }
693 }
694
2ef2b01e
A
695 //
696 // Matching volume name has precedence over handle based names
697 //
698 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
699 if (!VolumeNameMatch) {
37e0f9ac 700 if (FileStart == StrLen) {
701 // No Volume name or device name, so try Current Working Directory
702 if (gCwd == NULL) {
703 // No CWD
704 return NULL;
705 }
66c0b446 706
7ca9e5a4 707 // We could add a current working directory concept
37e0f9ac 708 CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
709 if (CwdPlusPathName == NULL) {
710 return NULL;
711 }
66c0b446 712
37e0f9ac 713 if ((PathName[0] == '/') || (PathName[0] == '\\')) {
714 // PathName starts in / so this means we go to the root of the device in the CWD.
715 CwdPlusPathName[0] = '\0';
716 for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
717 CwdPlusPathName[FileStart] = gCwd[FileStart];
718 if (gCwd[FileStart] == ':') {
719 FileStart++;
720 CwdPlusPathName[FileStart] = '\0';
721 break;
722 }
723 }
724 } else {
725 AsciiStrCpy (CwdPlusPathName, gCwd);
726 StrLen = AsciiStrLen (gCwd);
727 if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
728 AsciiStrCat (CwdPlusPathName, "\\");
729 }
730 }
66c0b446 731
37e0f9ac 732 AsciiStrCat (CwdPlusPathName, PathName);
733 if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
7ca9e5a4 734 // Extra error check to make sure we don't recurse and blow stack
37e0f9ac 735 return NULL;
736 }
66c0b446 737
37e0f9ac 738 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
739 FreePool (CwdPlusPathName);
740 return File;
741 }
742
2ef2b01e
A
743 DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
744 }
745
746 File->DeviceName = AllocatePool (StrLen);
747 AsciiStrCpy (File->DeviceName, PathName);
748 File->DeviceName[FileStart - 1] = '\0';
749 File->FileName = &File->DeviceName[FileStart];
16ccac42 750 if (File->FileName[0] == '\0') {
751 // if it is just a file name use / as root
e83c843b 752 File->FileName = "\\";
16ccac42 753 }
2ef2b01e
A
754
755 //
756 // Use best match algorithm on the dev names so we only need to look at the
757 // first few charters to match the full device name. Short name forms are
758 // legal from the caller.
759 //
760 Status = EFI_SUCCESS;
761 if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
762 if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
763 if (DevNumber >= mFsCount) {
764 goto ErrorExit;
765 }
766 File->Type = EfiOpenFileSystem;
767 File->EfiHandle = mFs[DevNumber];
768 Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
66c0b446 769
2ef2b01e
A
770 } else if (PathName[1] == 'v' || PathName[1] == 'V') {
771 if (DevNumber >= mFvCount) {
772 goto ErrorExit;
773 }
774 File->Type = EfiOpenFirmwareVolume;
775 File->EfiHandle = mFv[DevNumber];
776
777 if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
778 // Skip leading / as its not really needed for the FV since no directories are supported
779 FileStart++;
780 }
66c0b446 781
875c883e 782 // Check for 2nd :
783 ModifiedSectionType = SectionType;
784 for (Index = FileStart; PathName[Index] != '\0'; Index++) {
785 if (PathName[Index] == ':') {
786 // Support fv0:\DxeCore:0x10
787 // This means open the PE32 Section of the file
66c0b446 788 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
875c883e 789 PathName[Index] = '\0';
790 }
791 }
792 File->FvSectionType = ModifiedSectionType;
793 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
2ef2b01e
A
794 }
795 } else if ((*PathName == 'A') || (*PathName == 'a')) {
796 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
797 File->Type = EfiOpenMemoryBuffer;
798 // 1st colon is at PathName[FileStart - 1]
799 File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
800
801 // Find 2nd colon
802 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
803 FileStart++;
804 }
66c0b446 805
2ef2b01e
A
806 // If we ran out of string, there's no extra data
807 if (PathName[FileStart] == '\0') {
808 File->Size = 0;
809 } else {
810 File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
811 }
66c0b446 812
2ef2b01e
A
813 // if there's no number after the second colon, default
814 // the end of memory
815 if (File->Size == 0) {
816 File->Size = (UINTN)(0 - (UINTN)File->Buffer);
817 }
66c0b446 818
2ef2b01e
A
819 File->MaxPosition = File->Size;
820 File->BaseOffset = (UINTN)File->Buffer;
821
822 } else if (*PathName== 'l' || *PathName == 'L') {
823 if (DevNumber >= mLoadFileCount) {
824 goto ErrorExit;
825 }
826 File->Type = EfiOpenLoadFile;
827 File->EfiHandle = mLoadFile[DevNumber];
66c0b446 828
2ef2b01e
A
829 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
830 if (EFI_ERROR (Status)) {
831 goto ErrorExit;
832 }
833
834 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
835 if (EFI_ERROR (Status)) {
836 goto ErrorExit;
837 }
838 File->DevicePath = DuplicateDevicePath (DevicePath);
66c0b446 839
2ef2b01e
A
840 } else if (*PathName == 'b' || *PathName == 'B') {
841 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
842 if (DevNumber >= mBlkIoCount) {
843 goto ErrorExit;
844 }
845 File->Type = EfiOpenBlockIo;
846 File->EfiHandle = mBlkIo[DevNumber];
847 EblFileDevicePath (File, "", OpenMode);
848
849 // 1st colon is at PathName[FileStart - 1]
850 File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
851
852 // Find 2nd colon
853 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
854 FileStart++;
855 }
856
857 // If we ran out of string, there's no extra data
858 if (PathName[FileStart] == '\0') {
859 Size = 0;
860 } else {
861 Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
862 }
66c0b446 863
2ef2b01e
A
864 // if a zero size is passed in (or the size is left out entirely),
865 // go to the end of the device.
866 if (Size == 0) {
867 File->Size = File->Size - File->DiskOffset;
868 } else {
869 File->Size = Size;
870 }
66c0b446 871
2ef2b01e
A
872 File->MaxPosition = File->Size;
873 File->BaseOffset = File->DiskOffset;
874 } else if ((*PathName) >= '0' && (*PathName <= '9')) {
875
876 // Get current IP address
877 Status = EblGetCurrentIpAddress (&Ip);
878 if (EFI_ERROR(Status)) {
879 AsciiPrint("Device IP Address is not configured.\n");
880 goto ErrorExit;
881 }
66c0b446 882
2ef2b01e
A
883
884 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
885 File->Type = EfiOpenTftp;
886 File->IsDirty = FALSE;
887 File->IsBufferValid = FALSE;
888
889 Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
890 }
891
892 if (EFI_ERROR (Status)) {
893 goto ErrorExit;
894 }
895
896 GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
897 if (GuardFile == NULL) {
898 goto ErrorExit;
899 }
900
901 GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
902 CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
903 GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
904
905 return &(GuardFile->File);
906
907ErrorExit:
908 FreePool (File->DeviceName);
909 return NULL;
910}
911
912#define FILE_COPY_CHUNK 0x01000000
913
914EFI_STATUS
915EfiCopyFile (
f6381f4c 916 IN CHAR8 *DestinationFile,
917 IN CHAR8 *SourceFile
918 )
2ef2b01e
A
919{
920 EFI_OPEN_FILE *Source = NULL;
921 EFI_OPEN_FILE *Destination = NULL;
922 EFI_STATUS Status = EFI_SUCCESS;
923 VOID *Buffer = NULL;
924 UINTN Size;
925 UINTN Offset;
926 UINTN Chunk = FILE_COPY_CHUNK;
66c0b446 927
875c883e 928 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
2ef2b01e
A
929 if (Source == NULL) {
930 AsciiPrint("Source file open error.\n");
931 Status = EFI_NOT_FOUND;
932 goto Exit;
933 }
66c0b446 934
875c883e 935 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
2ef2b01e
A
936 if (Destination == NULL) {
937 AsciiPrint("Destination file open error.\n");
938 Status = EFI_NOT_FOUND;
939 goto Exit;
940 }
941
942 Buffer = AllocatePool(FILE_COPY_CHUNK);
943 if (Buffer == NULL) {
944 Status = EFI_OUT_OF_RESOURCES;
945 goto Exit;
946 }
66c0b446 947
2ef2b01e
A
948 Size = EfiTell(Source, NULL);
949
950 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
951 Chunk = FILE_COPY_CHUNK;
66c0b446 952
2ef2b01e
A
953 Status = EfiRead(Source, Buffer, &Chunk);
954 if (EFI_ERROR(Status)) {
f6381f4c 955 AsciiPrint("Read file error %r\n", Status);
2ef2b01e
A
956 goto Exit;
957 }
958
959 Status = EfiWrite(Destination, Buffer, &Chunk);
960 if (EFI_ERROR(Status)) {
f6381f4c 961 AsciiPrint("Write file error %r\n", Status);
2ef2b01e
A
962 goto Exit;
963 }
964 }
66c0b446 965
2ef2b01e
A
966 // Any left over?
967 if (Offset < Size) {
968 Chunk = Size - Offset;
66c0b446 969
2ef2b01e
A
970 Status = EfiRead(Source, Buffer, &Chunk);
971 if (EFI_ERROR(Status)) {
972 AsciiPrint("Read file error\n");
973 goto Exit;
974 }
975
976 Status = EfiWrite(Destination, Buffer, &Chunk);
977 if (EFI_ERROR(Status)) {
978 AsciiPrint("Write file error\n");
979 goto Exit;
980 }
981 }
982
983Exit:
984 if (Source != NULL) {
985 Status = EfiClose(Source);
986 if (EFI_ERROR(Status)) {
987 AsciiPrint("Source close error");
988 }
989 }
66c0b446 990
2ef2b01e
A
991 if (Destination != NULL) {
992 Status = EfiClose(Destination);
993 if (EFI_ERROR(Status)) {
994 AsciiPrint("Destination close error");
995 }
996 }
66c0b446 997
2ef2b01e
A
998 if (Buffer != NULL) {
999 FreePool(Buffer);
1000 }
66c0b446 1001
2ef2b01e
A
1002 return Status;
1003}
1004
1005/**
66c0b446 1006Use DeviceType and Index to form a valid PathName and try and open it.
2ef2b01e 1007
66c0b446 1008@param DeviceType Device type to open
1009@param Index Device Index to use. Zero relative.
2ef2b01e 1010
66c0b446 1011@return NULL Open failed
1012@return Valid EFI_OPEN_FILE handle
2ef2b01e
A
1013
1014**/
1015EFI_OPEN_FILE *
1016EfiDeviceOpenByType (
f6381f4c 1017 IN EFI_OPEN_FILE_TYPE DeviceType,
1018 IN UINTN Index
1019 )
2ef2b01e
A
1020{
1021 CHAR8 *DevStr;
1022 CHAR8 Path[MAX_CMD_LINE];
1023
1024 switch (DeviceType) {
1025 case EfiOpenLoadFile:
1026 DevStr = "loadfile%d:";
1027 break;
1028 case EfiOpenFirmwareVolume:
1029 DevStr = "fv%d:";
1030 break;
1031 case EfiOpenFileSystem:
1032 DevStr = "fs%d:";
1033 break;
1034 case EfiOpenBlockIo:
1035 DevStr = "blk%d:";
1036 break;
1037 case EfiOpenMemoryBuffer:
1038 DevStr = "a%d:";
1039 break;
1040 default:
1041 return NULL;
1042 }
1043
1044 AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
1045
1046 return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1047}
1048
1049
1050/**
66c0b446 1051Close a file handle opened by EfiOpen() and free all resources allocated by
1052EfiOpen().
2ef2b01e 1053
66c0b446 1054@param Stream Open File Handle
2ef2b01e 1055
66c0b446 1056@return EFI_INVALID_PARAMETER Stream is not an Open File
1057@return EFI_SUCCESS Steam closed
2ef2b01e
A
1058
1059**/
1060EFI_STATUS
1061EfiClose (
f6381f4c 1062 IN EFI_OPEN_FILE *File
1063 )
2ef2b01e
A
1064{
1065 EFI_STATUS Status;
1066 UINT64 TftpBufferSize;
1067
1068 if (!FileHandleValid (File)) {
1069 return EFI_INVALID_PARAMETER;
1070 }
1071
1072 //Write the buffer contents to TFTP file.
1073 if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
1074
1075 TftpBufferSize = File->Size;
1076 Status = EblMtftp (
66c0b446 1077 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
1078 File->Buffer,
1079 TRUE,
1080 &TftpBufferSize,
1081 NULL,
1082 &File->ServerIp,
1083 (UINT8 *)File->FileName,
1084 NULL,
1085 FALSE
1086 );
2ef2b01e
A
1087 if (EFI_ERROR(Status)) {
1088 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
1089 return Status;
1090 }
1091 }
1092
1093 if ((File->Type == EfiOpenLoadFile) ||
66c0b446 1094 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
1095 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
f6381f4c 1096 EblFreePool(File->Buffer);
1097 }
2ef2b01e 1098
f6381f4c 1099 EblFreePool (File->DevicePath);
1100 EblFreePool (File->DeviceName);
1101 EblFreePool (File->FsFileInfo);
1102 EblFreePool (File->FsInfo);
2ef2b01e 1103
f6381f4c 1104 if (File->FsFileHandle != NULL) {
1105 File->FsFileHandle->Close (File->FsFileHandle);
1106 }
66c0b446 1107
f6381f4c 1108 // Need to free File and it's Guard structures
1109 EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
1110 return EFI_SUCCESS;
2ef2b01e
A
1111}
1112
1113
1114/**
66c0b446 1115Return the size of the file represented by Stream. Also return the current
1116Seek position. Opening a file will enable a valid file size to be returned.
1117LoadFile is an exception as a load file size is set to zero.
2ef2b01e 1118
66c0b446 1119@param Stream Open File Handle
2ef2b01e 1120
66c0b446 1121@return 0 Stream is not an Open File or a valid LoadFile handle
2ef2b01e
A
1122
1123**/
1124UINTN
1125EfiTell (
f6381f4c 1126 IN EFI_OPEN_FILE *File,
1127 OUT EFI_LBA *CurrentPosition OPTIONAL
1128 )
2ef2b01e
A
1129{
1130 EFI_STATUS Status;
1131 UINT64 BufferSize = 0;
66c0b446 1132
2ef2b01e
A
1133 if (!FileHandleValid (File)) {
1134 return 0;
1135 }
1136
1137 if (CurrentPosition != NULL) {
1138 *CurrentPosition = File->CurrentPosition;
1139 }
1140
1141 if (File->Type == EfiOpenLoadFile) {
1142 // Figure out the File->Size
1143 File->Buffer = NULL;
1144 File->Size = 0;
1145 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
1146 if (Status != EFI_BUFFER_TOO_SMALL) {
1147 return 0;
1148 }
66c0b446 1149
2ef2b01e
A
1150 File->MaxPosition = (UINT64)File->Size;
1151 } else if (File->Type == EfiOpenTftp) {
66c0b446 1152
2ef2b01e 1153 Status = EblMtftp (
66c0b446 1154 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
1155 NULL,
1156 FALSE,
1157 &BufferSize,
1158 NULL,
1159 &File->ServerIp,
1160 (UINT8 *)File->FileName,
1161 NULL,
1162 TRUE
1163 );
2ef2b01e
A
1164 if (EFI_ERROR(Status)) {
1165 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
1166 return 0;
1167 }
1168
ab2a68f4 1169 File->Size = (UINTN)BufferSize;
2ef2b01e
A
1170 File->MaxPosition = File->Size;
1171 }
1172
1173 return File->Size;
1174}
1175
1176
1177/**
7ca9e5a4 1178Seek to the Offset location in the file. LoadFile and FV device types do
66c0b446 1179not support EfiSeek(). It is not possible to grow the file size using
1180EfiSeek().
1181
1182SeekType defines how use Offset to calculate the new file position:
1183EfiSeekStart : Position = Offset
1184EfiSeekCurrent: Position is Offset bytes from the current position
1185EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
2ef2b01e 1186
66c0b446 1187@param Stream Open File Handle
1188@param Offset Offset to seek too.
1189@param SeekType Type of seek to perform
2ef2b01e
A
1190
1191
66c0b446 1192@return EFI_INVALID_PARAMETER Stream is not an Open File
7ca9e5a4 1193@return EFI_UNSUPPORTED LoadFile and FV do not support Seek
66c0b446 1194@return EFI_NOT_FOUND Seek past the end of the file.
1195@return EFI_SUCCESS Steam closed
2ef2b01e
A
1196
1197**/
1198EFI_STATUS
1199EfiSeek (
f6381f4c 1200 IN EFI_OPEN_FILE *File,
1201 IN EFI_LBA Offset,
1202 IN EFI_SEEK_TYPE SeekType
1203 )
2ef2b01e
A
1204{
1205 EFI_STATUS Status;
1206 UINT64 CurrentPosition;
1207
1208 if (!FileHandleValid (File)) {
1209 return EFI_INVALID_PARAMETER;
1210 }
1211
875c883e 1212 if (File->Type == EfiOpenLoadFile) {
1213 // LoadFile does not support Seek
1214 return EFI_UNSUPPORTED;
2ef2b01e
A
1215 }
1216
1217 CurrentPosition = File->CurrentPosition;
1218 switch (SeekType) {
1219 case EfiSeekStart:
1220 if (Offset > File->MaxPosition) {
1221 return EFI_NOT_FOUND;
1222 }
1223 CurrentPosition = Offset;
1224 break;
1225
1226 case EfiSeekCurrent:
1227 if ((File->CurrentPosition + Offset) > File->MaxPosition) {
1228 return EFI_NOT_FOUND;
1229 }
1230 CurrentPosition += Offset;
1231 break;
1232
1233 case EfiSeekEnd:
1234 if (Offset != 0) {
1235 // We don't support growing file size via seeking past end of file
1236 return EFI_UNSUPPORTED;
1237 }
1238 CurrentPosition = File->MaxPosition;
1239 break;
1240
1241 default:
1242 return EFI_NOT_FOUND;
1243 }
1244
1245 Status = EFI_SUCCESS;
1246 if (File->FsFileHandle != NULL) {
1247 Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
1248 }
1249
1250 if (!EFI_ERROR (Status)) {
1251 File->CurrentPosition = CurrentPosition;
1252 }
1253
1254 return Status;
1255}
1256
1257EFI_STATUS
1258CacheTftpFile (
f6381f4c 1259 IN OUT EFI_OPEN_FILE *File
1260 )
2ef2b01e
A
1261{
1262 EFI_STATUS Status;
1263 UINT64 TftpBufferSize;
1264
1265 if (File->IsBufferValid) {
1266 return EFI_SUCCESS;
1267 }
1268
1269 // Make sure the file size is set.
1270 EfiTell (File, NULL);
1271
1272 //Allocate a buffer to hold the whole file.
1273 File->Buffer = AllocatePool(File->Size);
1274 if (File->Buffer == NULL) {
1275 return EFI_OUT_OF_RESOURCES;
1276 }
1277
1278 TftpBufferSize = File->Size;
1279
1280 Status = EblMtftp (
66c0b446 1281 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1282 File->Buffer,
1283 FALSE,
1284 &TftpBufferSize,
1285 NULL,
1286 &File->ServerIp,
1287 (UINT8 *)File->FileName,
1288 NULL,
1289 FALSE);
2ef2b01e
A
1290 if (EFI_ERROR(Status)) {
1291 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
1292 FreePool(File->Buffer);
1293 return Status;
1294 }
1295
1296 // Set the buffer valid flag.
1297 File->IsBufferValid = TRUE;
1298
1299 return Status;
1300}
1301
1302/**
7ca9e5a4 1303Read BufferSize bytes from the current location in the file. For load file,
66c0b446 1304FV, and TFTP case you must read the entire file.
2ef2b01e 1305
66c0b446 1306@param Stream Open File Handle
1307@param Buffer Caller allocated buffer.
1308@param BufferSize Size of buffer in bytes.
2ef2b01e
A
1309
1310
66c0b446 1311@return EFI_SUCCESS Stream is not an Open File
1312@return EFI_END_OF_FILE Tried to read past the end of the file
1313@return EFI_INVALID_PARAMETER Stream is not an open file handle
1314@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1315@return "other" Error returned from device read
2ef2b01e
A
1316
1317**/
1318EFI_STATUS
1319EfiRead (
f6381f4c 1320 IN EFI_OPEN_FILE *File,
1321 OUT VOID *Buffer,
1322 OUT UINTN *BufferSize
1323 )
2ef2b01e
A
1324{
1325 EFI_STATUS Status;
1326 UINT32 AuthenticationStatus;
1327 EFI_DISK_IO_PROTOCOL *DiskIo;
1328
1329 if (!FileHandleValid (File)) {
1330 return EFI_INVALID_PARAMETER;
1331 }
1332
1333 // Don't read past the end of the file.
1334 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1335 return EFI_END_OF_FILE;
1336 }
1337
1338 switch (File->Type) {
2ef2b01e
A
1339 case EfiOpenLoadFile:
1340 // Figure out the File->Size
1341 EfiTell (File, NULL);
1342
1343 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
1344 break;
66c0b446 1345
2ef2b01e 1346 case EfiOpenFirmwareVolume:
a6d7123e 1347 if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
1348 // This is the entire FV device, so treat like a memory buffer
1349 CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
1350 File->CurrentPosition += *BufferSize;
1351 Status = EFI_SUCCESS;
2ef2b01e 1352 } else {
875c883e 1353 if (File->Buffer == NULL) {
1354 if (File->FvSectionType == EFI_SECTION_ALL) {
1355 Status = File->Fv->ReadFile (
66c0b446 1356 File->Fv,
1357 &File->FvNameGuid,
1358 (VOID **)&File->Buffer,
1359 &File->Size,
1360 &File->FvType,
1361 &File->FvAttributes,
1362 &AuthenticationStatus
1363 );
875c883e 1364 } else {
1365 Status = File->Fv->ReadSection (
66c0b446 1366 File->Fv,
1367 &File->FvNameGuid,
1368 File->FvSectionType,
1369 0,
1370 (VOID **)&File->Buffer,
1371 &File->Size,
1372 &AuthenticationStatus
1373 );
875c883e 1374 }
1375 if (EFI_ERROR (Status)) {
1376 return Status;
1377 }
1378 File->IsBufferValid = TRUE;
a6d7123e 1379 }
875c883e 1380 // Operate on the cached buffer so Seek will work
1381 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1382 File->CurrentPosition += *BufferSize;
1383 Status = EFI_SUCCESS;
2ef2b01e
A
1384 }
1385 break;
66c0b446 1386
875c883e 1387 case EfiOpenMemoryBuffer:
1388 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1389 File->CurrentPosition += *BufferSize;
1390 Status = EFI_SUCCESS;
1391 break;
2ef2b01e
A
1392
1393 case EfiOpenFileSystem:
1394 Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
1395 File->CurrentPosition += *BufferSize;
1396 break;
1397
1398 case EfiOpenBlockIo:
1399 Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1400 if (!EFI_ERROR(Status)) {
0a0951ea 1401 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
2ef2b01e
A
1402 }
1403 File->CurrentPosition += *BufferSize;
1404 break;
66c0b446 1405
2ef2b01e
A
1406 case EfiOpenTftp:
1407 // Cache the file if it hasn't been cached yet.
1408 if (File->IsBufferValid == FALSE) {
1409 Status = CacheTftpFile (File);
1410 if (EFI_ERROR (Status)) {
1411 return Status;
1412 }
1413 }
1414
1415 // Copy out the requested data
1416 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1417 File->CurrentPosition += *BufferSize;
1418
1419 Status = EFI_SUCCESS;
1420 break;
66c0b446 1421
2ef2b01e
A
1422 default:
1423 return EFI_INVALID_PARAMETER;
1424 };
1425
1426 return Status;
1427}
1428
1429
1430/**
66c0b446 1431Read the entire file into a buffer. This routine allocates the buffer and
1432returns it to the user full of the read data.
2ef2b01e 1433
7ca9e5a4 1434This is very useful for load file where it's hard to know how big the buffer
66c0b446 1435must be.
2ef2b01e 1436
66c0b446 1437@param Stream Open File Handle
1438@param Buffer Pointer to buffer to return.
1439@param BufferSize Pointer to Size of buffer return..
2ef2b01e
A
1440
1441
66c0b446 1442@return EFI_SUCCESS Stream is not an Open File
1443@return EFI_END_OF_FILE Tried to read past the end of the file
1444@return EFI_INVALID_PARAMETER Stream is not an open file handle
1445@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1446@return "other" Error returned from device read
2ef2b01e
A
1447
1448**/
1449EFI_STATUS
1450EfiReadAllocatePool (
f6381f4c 1451 IN EFI_OPEN_FILE *File,
1452 OUT VOID **Buffer,
1453 OUT UINTN *BufferSize
1454 )
2ef2b01e
A
1455{
1456 if (!FileHandleValid (File)) {
1457 return EFI_INVALID_PARAMETER;
1458 }
1459
1460 // Loadfile defers file size determination on Open so use tell to find it
1461 EfiTell (File, NULL);
1462
1463 *BufferSize = File->Size;
1464 *Buffer = AllocatePool (*BufferSize);
1465 if (*Buffer == NULL) {
1466 return EFI_NOT_FOUND;
1467 }
1468
1469 return EfiRead (File, *Buffer, BufferSize);
1470}
1471
1472
1473/**
66c0b446 1474Write data back to the file. For TFTP case you must write the entire file.
2ef2b01e 1475
66c0b446 1476@param Stream Open File Handle
1477@param Buffer Pointer to buffer to return.
1478@param BufferSize Pointer to Size of buffer return..
2ef2b01e
A
1479
1480
66c0b446 1481@return EFI_SUCCESS Stream is not an Open File
1482@return EFI_END_OF_FILE Tried to read past the end of the file
1483@return EFI_INVALID_PARAMETER Stream is not an open file handle
1484@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1485@return "other" Error returned from device write
2ef2b01e
A
1486
1487**/
1488EFI_STATUS
1489EfiWrite (
f6381f4c 1490 IN EFI_OPEN_FILE *File,
1491 OUT VOID *Buffer,
1492 OUT UINTN *BufferSize
1493 )
2ef2b01e
A
1494{
1495 EFI_STATUS Status;
1496 EFI_FV_WRITE_FILE_DATA FileData;
1497 EFI_DISK_IO_PROTOCOL *DiskIo;
1498
1499 if (!FileHandleValid (File)) {
1500 return EFI_INVALID_PARAMETER;
1501 }
1502
1503 switch (File->Type) {
1504 case EfiOpenMemoryBuffer:
1505 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1506 return EFI_END_OF_FILE;
1507 }
1508
1509 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1510 File->CurrentPosition += *BufferSize;
1511 Status = EFI_SUCCESS;
1512
1513 case EfiOpenLoadFile:
1514 // LoadFile device is read only be definition
1515 Status = EFI_UNSUPPORTED;
66c0b446 1516
2ef2b01e
A
1517 case EfiOpenFirmwareVolume:
1518 if (File->FvSectionType != EFI_SECTION_ALL) {
1519 // Writes not support to a specific section. You have to update entire file
1520 return EFI_UNSUPPORTED;
1521 }
1522
1523 FileData.NameGuid = &(File->FvNameGuid);
1524 FileData.Type = File->FvType;
1525 FileData.FileAttributes = File->FvAttributes;
1526 FileData.Buffer = Buffer;
1527 FileData.BufferSize = (UINT32)*BufferSize;
1528 Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
1529 break;
66c0b446 1530
2ef2b01e
A
1531 case EfiOpenFileSystem:
1532 Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
1533 File->CurrentPosition += *BufferSize;
1534 break;
1535
1536 case EfiOpenBlockIo:
1537 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1538 return EFI_END_OF_FILE;
1539 }
1540
1541 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1542 if (!EFI_ERROR(Status)) {
0a0951ea 1543 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
2ef2b01e
A
1544 }
1545 File->CurrentPosition += *BufferSize;
1546 break;
1547
1548 case EfiOpenTftp:
1549 // Cache the file if it hasn't been cached yet.
1550 if (File->IsBufferValid == FALSE) {
1551 Status = CacheTftpFile(File);
1552 if (EFI_ERROR(Status)) {
1553 return Status;
1554 }
1555 }
1556
1557 // Don't overwrite the buffer
1558 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1559 UINT8 *TempBuffer;
1560
1561 TempBuffer = File->Buffer;
1562
ab2a68f4 1563 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
2ef2b01e
A
1564 if (File->Buffer == NULL) {
1565 return EFI_OUT_OF_RESOURCES;
1566 }
1567
1568 CopyMem (File->Buffer, TempBuffer, File->Size);
1569
1570 FreePool (TempBuffer);
1571
ab2a68f4 1572 File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
1573 File->MaxPosition = (UINT64)File->Size;
2ef2b01e
A
1574 }
1575
1576 // Copy in the requested data
1577 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1578 File->CurrentPosition += *BufferSize;
1579
1580 // Mark the file dirty
1581 File->IsDirty = TRUE;
1582
1583 Status = EFI_SUCCESS;
1584 break;
1585
1586 default:
1587 Status = EFI_INVALID_PARAMETER;
1588 };
1589
1590 return Status;
1591}
1592
85e385f4 1593
16ccac42 1594/**
66c0b446 1595Given Cwd expand Path to remove .. and replace them with real
1596directory names.
16ccac42 1597
66c0b446 1598@param Cwd Current Working Directory
1599@param Path Path to expand
1600
1601@return NULL Cwd or Path are not valid
1602@return 'other' Path with .. expanded
16ccac42 1603
1604**/
1605CHAR8 *
1606ExpandPath (
f6381f4c 1607 IN CHAR8 *Cwd,
1608 IN CHAR8 *Path
1609 )
16ccac42 1610{
1611 CHAR8 *NewPath;
1612 CHAR8 *Work, *Start, *End;
1613 UINTN StrLen;
0a0951ea 1614 INTN i;
66c0b446 1615
16ccac42 1616 if (Cwd == NULL || Path == NULL) {
1617 return NULL;
1618 }
66c0b446 1619
16ccac42 1620 StrLen = AsciiStrSize (Cwd);
1621 if (StrLen <= 2) {
1622 // Smallest valid path is 1 char and a null
1623 return NULL;
1624 }
1625
1626 StrLen = AsciiStrSize (Path);
1627 NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
1628 if (NewPath == NULL) {
1629 return NULL;
1630 }
1631 AsciiStrCpy (NewPath, Cwd);
66c0b446 1632
16ccac42 1633 End = Path + StrLen;
1634 for (Start = Path ;;) {
1635 Work = AsciiStrStr (Start, "..") ;
1636 if (Work == NULL) {
1637 // Remaining part of Path contains no more ..
1638 break;
1639 }
66c0b446 1640
16ccac42 1641 // append path prior to ..
1642 AsciiStrnCat (NewPath, Start, Work - Start);
1643 StrLen = AsciiStrLen (NewPath);
1644 for (i = StrLen; i >= 0; i--) {
1645 if (NewPath[i] == ':') {
1646 // too many ..
1647 return NULL;
1648 }
1649 if (NewPath[i] == '/' || NewPath[i] == '\\') {
1650 if ((i > 0) && (NewPath[i-1] == ':')) {
1651 // leave the / before a :
1652 NewPath[i+1] = '\0';
1653 } else {
1654 // replace / will Null to remove trailing file/dir reference
1655 NewPath[i] = '\0';
1656 }
1657 break;
1658 }
1659 }
66c0b446 1660
16ccac42 1661 Start = Work + 3;
1662 }
66c0b446 1663
16ccac42 1664 // Handle the path that remains after the ..
1665 AsciiStrnCat (NewPath, Start, End - Start);
66c0b446 1666
16ccac42 1667 return NewPath;
1668}
1669
85e385f4 1670
1671/**
7ca9e5a4 1672Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
66c0b446 1673the path does not contain a device name, The CWD is prepended to the path.
1674
1675@param Cwd Current Working Directory to set
85e385f4 1676
1677
66c0b446 1678@return EFI_SUCCESS CWD is set
1679@return EFI_INVALID_PARAMETER Cwd is not a valid device:path
85e385f4 1680
1681**/
1682EFI_STATUS
1683EfiSetCwd (
f6381f4c 1684 IN CHAR8 *Cwd
1685 )
85e385f4 1686{
1687 EFI_OPEN_FILE *File;
16ccac42 1688 UINTN Len;
1689 CHAR8 *Path;
66c0b446 1690
16ccac42 1691 if (Cwd == NULL) {
85e385f4 1692 return EFI_INVALID_PARAMETER;
1693 }
66c0b446 1694
16ccac42 1695 if (AsciiStrCmp (Cwd, ".") == 0) {
1696 // cd . is a no-op
1697 return EFI_SUCCESS;
1698 }
66c0b446 1699
16ccac42 1700 Path = Cwd;
1701 if (AsciiStrStr (Cwd, "..") != NULL) {
1702 if (gCwd == NULL) {
1703 // no parent
1704 return EFI_SUCCESS;
1705 }
66c0b446 1706
16ccac42 1707 Len = AsciiStrLen (gCwd);
1708 if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
1709 // parent is device so nothing to do
1710 return EFI_SUCCESS;
1711 }
66c0b446 1712
16ccac42 1713 // Expand .. in Cwd, given we know current working directory
1714 Path = ExpandPath (gCwd, Cwd);
1715 if (Path == NULL) {
1716 return EFI_NOT_FOUND;
1717 }
1718 }
66c0b446 1719
16ccac42 1720 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1721 if (File == NULL) {
1722 return EFI_INVALID_PARAMETER;
1723 }
66c0b446 1724
85e385f4 1725 if (gCwd != NULL) {
1726 FreePool (gCwd);
1727 }
66c0b446 1728
16ccac42 1729 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1730 // relative to the current gCwd or not.
9c0ce9f3 1731 gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
85e385f4 1732 if (gCwd == NULL) {
1733 return EFI_INVALID_PARAMETER;
1734 }
66c0b446 1735
208a8330 1736 AsciiStrCpy (gCwd, File->DeviceName);
1737 if (File->FileName == NULL) {
1738 AsciiStrCat (gCwd, ":\\");
1739 } else {
1740 AsciiStrCat (gCwd, ":");
1741 AsciiStrCat (gCwd, File->FileName);
1742 }
1743
1744
16ccac42 1745 EfiClose (File);
1746 if (Path != Cwd) {
1747 FreePool (Path);
1748 }
85e385f4 1749 return EFI_SUCCESS;
1750}
1751
1752
1753/**
7ca9e5a4 1754Set the Current Working Directory (CWD). If a call is made to EfiOpen () and
66c0b446 1755the path does not contain a device name, The CWD is prepended to the path.
1756The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1757a call to EfiSetCwd() it is not legal to use the pointer returned by
7ca9e5a4 1758this function.
66c0b446 1759
1760@param Cwd Current Working Directory
1761
1762
1763@return "" No CWD set
1764@return 'other' Returns buffer that contains CWD.
1765
85e385f4 1766**/
1767CHAR8 *
16ccac42 1768EfiGetCwd (
f6381f4c 1769 VOID
1770 )
85e385f4 1771{
16ccac42 1772 if (gCwd == NULL) {
1773 return "";
1774 }
85e385f4 1775 return gCwd;
1776}
1777
1778