]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Library/EfiFileLib/EfiFileLib.c
Remove probe for removable media from FileLib, it was getting called way too much...
[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
20Device names are case insensative and only check the leading characters for
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) {
171 // Need to Free the mFsInfo prior to reclaculating mFsCount so don't move this code
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;
2ef2b01e
A
412
413 // If we are not opening the device this will get over written with file info
414 File->MaxPosition = MultU64x32 (BlkIo->Media->LastBlock + 1, BlkIo->Media->BlockSize);
415 }
416
417 if (File->Type == EfiOpenFileSystem) {
418 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);
419 if (!EFI_ERROR (Status)) {
420 Status = Fs->OpenVolume (Fs, &Root);
421 if (!EFI_ERROR (Status)) {
422 // Get information about the volume
423 Size = 0;
424 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
425 if (Status == EFI_BUFFER_TOO_SMALL) {
426 File->FsInfo = AllocatePool (Size);
427 Status = Root->GetInfo (Root, &gEfiFileSystemInfoGuid, &Size, File->FsInfo);
428 }
66c0b446 429
2ef2b01e
A
430 // Get information about the file
431 Status = Root->Open (Root, &File->FsFileHandle, UnicodeFileName, OpenMode, 0);
432 if (!EFI_ERROR (Status)) {
433 Size = 0;
434 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, NULL);
435 if (Status == EFI_BUFFER_TOO_SMALL) {
436 File->FsFileInfo = AllocatePool (Size);
437 Status = File->FsFileHandle->GetInfo (File->FsFileHandle, &gEfiFileInfoGuid, &Size, File->FsFileInfo);
438 if (!EFI_ERROR (Status)) {
439 File->Size = (UINTN)File->FsFileInfo->FileSize;
440 File->MaxPosition = (UINT64)File->Size;
441 }
442 }
443 }
444
445 Root->Close (Root);
446 }
447 }
448 } else if (File->Type == EfiOpenBlockIo) {
449 File->Size = (UINTN)File->MaxPosition;
450 }
66c0b446 451
2ef2b01e
A
452 return Status;
453}
454
455#define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
456
457EFI_STATUS
458CompareGuidToString (
f6381f4c 459 IN EFI_GUID *Guid,
460 IN CHAR8 *String
461 )
2ef2b01e
A
462{
463 CHAR8 AsciiGuid[64];
464 CHAR8 *StringPtr;
465 CHAR8 *GuidPtr;
466
467 AsciiSPrint (AsciiGuid, sizeof(AsciiGuid), "%g", Guid);
468
469 StringPtr = String;
470 GuidPtr = AsciiGuid;
471
472 while ((*StringPtr != '\0') && (*GuidPtr != '\0')) {
473 // Skip dashes
474 if (*StringPtr == '-') {
475 StringPtr++;
476 continue;
477 }
478
479 if (*GuidPtr == '-') {
480 GuidPtr++;
481 continue;
482 }
483
484 if (ToUpper(*StringPtr) != ToUpper(*GuidPtr)) {
485 return EFI_NOT_FOUND;
486 }
487
488 StringPtr++;
489 GuidPtr++;
490 }
491
492 return EFI_SUCCESS;
493}
494
495
496/**
66c0b446 497Internal work function to fill in EFI_OPEN_FILE information for the FV
2ef2b01e 498
66c0b446 499@param File Open file handle
500@param FileName Name of file after device stripped off
2ef2b01e
A
501
502
503**/
504EFI_STATUS
505EblFvFileDevicePath (
f6381f4c 506 IN OUT EFI_OPEN_FILE *File,
507 IN CHAR8 *FileName,
508 IN CONST UINT64 OpenMode
509 )
2ef2b01e
A
510{
511 EFI_STATUS Status;
512 EFI_STATUS GetNextFileStatus;
513 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH DevicePathNode;
514 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
515 UINTN Key;
516 UINT32 AuthenticationStatus;
517 CHAR8 AsciiSection[MAX_PATHNAME];
518 VOID *Section;
519 UINTN SectionSize;
520 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb;
521 EFI_LBA Lba;
522 UINTN BlockSize;
523 UINTN NumberOfBlocks;
60428d00 524 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;
525 UINTN Index;
2ef2b01e
A
526
527
528 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
529 if (EFI_ERROR (Status)) {
530 return Status;
531 }
532
a6d7123e 533 // Get FVB Info about the handle
534 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb);
535 if (!EFI_ERROR (Status)) {
536 Status = Fvb->GetPhysicalAddress (Fvb, &File->FvStart);
537 if (!EFI_ERROR (Status)) {
60428d00 538 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)File->FvStart;
539 File->FvHeaderSize = sizeof (EFI_FIRMWARE_VOLUME_HEADER);
540 for (Index = 0; FvHeader->BlockMap[Index].Length !=0; Index++) {
541 File->FvHeaderSize += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
542 }
66c0b446 543
60428d00 544 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
a6d7123e 545 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
546 if (EFI_ERROR (Status)) {
547 break;
548 }
549 }
550 }
551 }
552
553
2ef2b01e
A
554 DevicePath = DevicePathFromHandle (File->EfiHandle);
555
556 if (*FileName == '\0') {
557 File->DevicePath = DuplicateDevicePath (DevicePath);
a6d7123e 558 File->Size = File->FvSize;
559 File->MaxPosition = File->Size;
2ef2b01e
A
560 } else {
561 Key = 0;
562 do {
563 File->FvType = EFI_FV_FILETYPE_ALL;
564 GetNextFileStatus = File->Fv->GetNextFile (
66c0b446 565 File->Fv,
566 &Key,
567 &File->FvType,
568 &File->FvNameGuid,
569 &File->FvAttributes,
570 &File->Size
571 );
2ef2b01e 572 if (!EFI_ERROR (GetNextFileStatus)) {
2ef2b01e
A
573 // Compare GUID first
574 Status = CompareGuidToString (&File->FvNameGuid, FileName);
575 if (!EFI_ERROR(Status)) {
576 break;
577 }
66c0b446 578
875c883e 579 Section = NULL;
2ef2b01e 580 Status = File->Fv->ReadSection (
66c0b446 581 File->Fv,
582 &File->FvNameGuid,
583 EFI_SECTION_USER_INTERFACE,
584 0,
585 &Section,
586 &SectionSize,
587 &AuthenticationStatus
588 );
2ef2b01e
A
589 if (!EFI_ERROR (Status)) {
590 UnicodeStrToAsciiStr (Section, AsciiSection);
591 if (AsciiStriCmp (FileName, AsciiSection) == 0) {
592 FreePool (Section);
593 break;
594 }
595 FreePool (Section);
596 }
597 }
598 } while (!EFI_ERROR (GetNextFileStatus));
599
600 if (EFI_ERROR (GetNextFileStatus)) {
601 return GetNextFileStatus;
602 }
603
875c883e 604 if (OpenMode != EFI_SECTION_ALL) {
605 // Calculate the size of the section we are targeting
606 Section = NULL;
607 File->Size = 0;
608 Status = File->Fv->ReadSection (
66c0b446 609 File->Fv,
610 &File->FvNameGuid,
611 (EFI_SECTION_TYPE)OpenMode,
612 0,
613 &Section,
614 &File->Size,
615 &AuthenticationStatus
616 );
875c883e 617 if (EFI_ERROR (Status)) {
618 return Status;
619 }
620 }
621
2ef2b01e
A
622 File->MaxPosition = File->Size;
623 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
624 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
625 }
626
66c0b446 627
2ef2b01e
A
628 // FVB not required if FV was soft loaded...
629 return EFI_SUCCESS;
630}
631
632
633
634
635/**
66c0b446 636Open a device named by PathName. The PathName includes a device name and
637path seperated by a :. See file header for more details on the PathName
638syntax. There is no checking to prevent a file from being opened more than
639one type.
2ef2b01e 640
66c0b446 641SectionType is only used to open an FV. Each file in an FV contains multiple
642secitons and only the SectionType section is opened.
2ef2b01e 643
66c0b446 644For any file that is opened with EfiOpen() must be closed with EfiClose().
2ef2b01e 645
66c0b446 646@param PathName Path to parse to open
647@param OpenMode Same as EFI_FILE.Open()
648@param SectionType Section in FV to open.
2ef2b01e 649
66c0b446 650@return NULL Open failed
651@return Valid EFI_OPEN_FILE handle
2ef2b01e
A
652
653**/
654EFI_OPEN_FILE *
655EfiOpen (
f6381f4c 656 IN CHAR8 *PathName,
657 IN CONST UINT64 OpenMode,
658 IN CONST EFI_SECTION_TYPE SectionType
659 )
2ef2b01e
A
660{
661 EFI_STATUS Status;
662 EFI_OPEN_FILE *File;
663 EFI_OPEN_FILE FileData;
664 UINTN StrLen;
665 UINTN FileStart;
666 UINTN DevNumber = 0;
667 EFI_OPEN_FILE_GUARD *GuardFile;
668 BOOLEAN VolumeNameMatch;
669 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
670 UINTN Size;
671 EFI_IP_ADDRESS Ip;
85e385f4 672 CHAR8 *CwdPlusPathName;
875c883e 673 UINTN Index;
674 EFI_SECTION_TYPE ModifiedSectionType;
2ef2b01e
A
675
676 EblUpdateDeviceLists ();
66c0b446 677
2ef2b01e
A
678 File = &FileData;
679 ZeroMem (File, sizeof (EFI_OPEN_FILE));
2ef2b01e
A
680
681 StrLen = AsciiStrSize (PathName);
16ccac42 682 if (StrLen <= 1) {
2ef2b01e
A
683 // Smallest valid path is 1 char and a null
684 return NULL;
685 }
686
687 for (FileStart = 0; FileStart < StrLen; FileStart++) {
688 if (PathName[FileStart] == ':') {
689 FileStart++;
690 break;
691 }
692 }
693
2ef2b01e
A
694 //
695 // Matching volume name has precedence over handle based names
696 //
697 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
698 if (!VolumeNameMatch) {
37e0f9ac 699 if (FileStart == StrLen) {
700 // No Volume name or device name, so try Current Working Directory
701 if (gCwd == NULL) {
702 // No CWD
703 return NULL;
704 }
66c0b446 705
37e0f9ac 706 // We could add a current working diretory concept
707 CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
708 if (CwdPlusPathName == NULL) {
709 return NULL;
710 }
66c0b446 711
37e0f9ac 712 if ((PathName[0] == '/') || (PathName[0] == '\\')) {
713 // PathName starts in / so this means we go to the root of the device in the CWD.
714 CwdPlusPathName[0] = '\0';
715 for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) {
716 CwdPlusPathName[FileStart] = gCwd[FileStart];
717 if (gCwd[FileStart] == ':') {
718 FileStart++;
719 CwdPlusPathName[FileStart] = '\0';
720 break;
721 }
722 }
723 } else {
724 AsciiStrCpy (CwdPlusPathName, gCwd);
725 StrLen = AsciiStrLen (gCwd);
726 if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) {
727 AsciiStrCat (CwdPlusPathName, "\\");
728 }
729 }
66c0b446 730
37e0f9ac 731 AsciiStrCat (CwdPlusPathName, PathName);
732 if (AsciiStrStr (CwdPlusPathName, ":") == NULL) {
733 // Extra error check to make sure we don't recusre and blow stack
734 return NULL;
735 }
66c0b446 736
37e0f9ac 737 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
738 FreePool (CwdPlusPathName);
739 return File;
740 }
741
2ef2b01e
A
742 DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName);
743 }
744
745 File->DeviceName = AllocatePool (StrLen);
746 AsciiStrCpy (File->DeviceName, PathName);
747 File->DeviceName[FileStart - 1] = '\0';
748 File->FileName = &File->DeviceName[FileStart];
16ccac42 749 if (File->FileName[0] == '\0') {
750 // if it is just a file name use / as root
e83c843b 751 File->FileName = "\\";
16ccac42 752 }
2ef2b01e
A
753
754 //
755 // Use best match algorithm on the dev names so we only need to look at the
756 // first few charters to match the full device name. Short name forms are
757 // legal from the caller.
758 //
759 Status = EFI_SUCCESS;
760 if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) {
761 if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) {
762 if (DevNumber >= mFsCount) {
763 goto ErrorExit;
764 }
765 File->Type = EfiOpenFileSystem;
766 File->EfiHandle = mFs[DevNumber];
767 Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode);
66c0b446 768
2ef2b01e
A
769 } else if (PathName[1] == 'v' || PathName[1] == 'V') {
770 if (DevNumber >= mFvCount) {
771 goto ErrorExit;
772 }
773 File->Type = EfiOpenFirmwareVolume;
774 File->EfiHandle = mFv[DevNumber];
775
776 if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) {
777 // Skip leading / as its not really needed for the FV since no directories are supported
778 FileStart++;
779 }
66c0b446 780
875c883e 781 // Check for 2nd :
782 ModifiedSectionType = SectionType;
783 for (Index = FileStart; PathName[Index] != '\0'; Index++) {
784 if (PathName[Index] == ':') {
785 // Support fv0:\DxeCore:0x10
786 // This means open the PE32 Section of the file
66c0b446 787 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
875c883e 788 PathName[Index] = '\0';
789 }
790 }
791 File->FvSectionType = ModifiedSectionType;
792 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
2ef2b01e
A
793 }
794 } else if ((*PathName == 'A') || (*PathName == 'a')) {
795 // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE
796 File->Type = EfiOpenMemoryBuffer;
797 // 1st colon is at PathName[FileStart - 1]
798 File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]);
799
800 // Find 2nd colon
801 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
802 FileStart++;
803 }
66c0b446 804
2ef2b01e
A
805 // If we ran out of string, there's no extra data
806 if (PathName[FileStart] == '\0') {
807 File->Size = 0;
808 } else {
809 File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
810 }
66c0b446 811
2ef2b01e
A
812 // if there's no number after the second colon, default
813 // the end of memory
814 if (File->Size == 0) {
815 File->Size = (UINTN)(0 - (UINTN)File->Buffer);
816 }
66c0b446 817
2ef2b01e
A
818 File->MaxPosition = File->Size;
819 File->BaseOffset = (UINTN)File->Buffer;
820
821 } else if (*PathName== 'l' || *PathName == 'L') {
822 if (DevNumber >= mLoadFileCount) {
823 goto ErrorExit;
824 }
825 File->Type = EfiOpenLoadFile;
826 File->EfiHandle = mLoadFile[DevNumber];
66c0b446 827
2ef2b01e
A
828 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile);
829 if (EFI_ERROR (Status)) {
830 goto ErrorExit;
831 }
832
833 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath);
834 if (EFI_ERROR (Status)) {
835 goto ErrorExit;
836 }
837 File->DevicePath = DuplicateDevicePath (DevicePath);
66c0b446 838
2ef2b01e
A
839 } else if (*PathName == 'b' || *PathName == 'B') {
840 // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE
841 if (DevNumber >= mBlkIoCount) {
842 goto ErrorExit;
843 }
844 File->Type = EfiOpenBlockIo;
845 File->EfiHandle = mBlkIo[DevNumber];
846 EblFileDevicePath (File, "", OpenMode);
847
848 // 1st colon is at PathName[FileStart - 1]
849 File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]);
850
851 // Find 2nd colon
852 while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) {
853 FileStart++;
854 }
855
856 // If we ran out of string, there's no extra data
857 if (PathName[FileStart] == '\0') {
858 Size = 0;
859 } else {
860 Size = AsciiStrHexToUintn (&PathName[FileStart + 1]);
861 }
66c0b446 862
2ef2b01e
A
863 // if a zero size is passed in (or the size is left out entirely),
864 // go to the end of the device.
865 if (Size == 0) {
866 File->Size = File->Size - File->DiskOffset;
867 } else {
868 File->Size = Size;
869 }
66c0b446 870
2ef2b01e
A
871 File->MaxPosition = File->Size;
872 File->BaseOffset = File->DiskOffset;
873 } else if ((*PathName) >= '0' && (*PathName <= '9')) {
874
875 // Get current IP address
876 Status = EblGetCurrentIpAddress (&Ip);
877 if (EFI_ERROR(Status)) {
878 AsciiPrint("Device IP Address is not configured.\n");
879 goto ErrorExit;
880 }
66c0b446 881
2ef2b01e
A
882
883 // Parse X.X.X.X:Filename, only support IPv4 TFTP for now...
884 File->Type = EfiOpenTftp;
885 File->IsDirty = FALSE;
886 File->IsBufferValid = FALSE;
887
888 Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp);
889 }
890
891 if (EFI_ERROR (Status)) {
892 goto ErrorExit;
893 }
894
895 GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD));
896 if (GuardFile == NULL) {
897 goto ErrorExit;
898 }
899
900 GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER;
901 CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE));
902 GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER;
903
904 return &(GuardFile->File);
905
906ErrorExit:
907 FreePool (File->DeviceName);
908 return NULL;
909}
910
911#define FILE_COPY_CHUNK 0x01000000
912
913EFI_STATUS
914EfiCopyFile (
f6381f4c 915 IN CHAR8 *DestinationFile,
916 IN CHAR8 *SourceFile
917 )
2ef2b01e
A
918{
919 EFI_OPEN_FILE *Source = NULL;
920 EFI_OPEN_FILE *Destination = NULL;
921 EFI_STATUS Status = EFI_SUCCESS;
922 VOID *Buffer = NULL;
923 UINTN Size;
924 UINTN Offset;
925 UINTN Chunk = FILE_COPY_CHUNK;
66c0b446 926
875c883e 927 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
2ef2b01e
A
928 if (Source == NULL) {
929 AsciiPrint("Source file open error.\n");
930 Status = EFI_NOT_FOUND;
931 goto Exit;
932 }
66c0b446 933
875c883e 934 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
2ef2b01e
A
935 if (Destination == NULL) {
936 AsciiPrint("Destination file open error.\n");
937 Status = EFI_NOT_FOUND;
938 goto Exit;
939 }
940
941 Buffer = AllocatePool(FILE_COPY_CHUNK);
942 if (Buffer == NULL) {
943 Status = EFI_OUT_OF_RESOURCES;
944 goto Exit;
945 }
66c0b446 946
2ef2b01e
A
947 Size = EfiTell(Source, NULL);
948
949 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
950 Chunk = FILE_COPY_CHUNK;
66c0b446 951
2ef2b01e
A
952 Status = EfiRead(Source, Buffer, &Chunk);
953 if (EFI_ERROR(Status)) {
f6381f4c 954 AsciiPrint("Read file error %r\n", Status);
2ef2b01e
A
955 goto Exit;
956 }
957
958 Status = EfiWrite(Destination, Buffer, &Chunk);
959 if (EFI_ERROR(Status)) {
f6381f4c 960 AsciiPrint("Write file error %r\n", Status);
2ef2b01e
A
961 goto Exit;
962 }
963 }
66c0b446 964
2ef2b01e
A
965 // Any left over?
966 if (Offset < Size) {
967 Chunk = Size - Offset;
66c0b446 968
2ef2b01e
A
969 Status = EfiRead(Source, Buffer, &Chunk);
970 if (EFI_ERROR(Status)) {
971 AsciiPrint("Read file error\n");
972 goto Exit;
973 }
974
975 Status = EfiWrite(Destination, Buffer, &Chunk);
976 if (EFI_ERROR(Status)) {
977 AsciiPrint("Write file error\n");
978 goto Exit;
979 }
980 }
981
982Exit:
983 if (Source != NULL) {
984 Status = EfiClose(Source);
985 if (EFI_ERROR(Status)) {
986 AsciiPrint("Source close error");
987 }
988 }
66c0b446 989
2ef2b01e
A
990 if (Destination != NULL) {
991 Status = EfiClose(Destination);
992 if (EFI_ERROR(Status)) {
993 AsciiPrint("Destination close error");
994 }
995 }
66c0b446 996
2ef2b01e
A
997 if (Buffer != NULL) {
998 FreePool(Buffer);
999 }
66c0b446 1000
2ef2b01e
A
1001 return Status;
1002}
1003
1004/**
66c0b446 1005Use DeviceType and Index to form a valid PathName and try and open it.
2ef2b01e 1006
66c0b446 1007@param DeviceType Device type to open
1008@param Index Device Index to use. Zero relative.
2ef2b01e 1009
66c0b446 1010@return NULL Open failed
1011@return Valid EFI_OPEN_FILE handle
2ef2b01e
A
1012
1013**/
1014EFI_OPEN_FILE *
1015EfiDeviceOpenByType (
f6381f4c 1016 IN EFI_OPEN_FILE_TYPE DeviceType,
1017 IN UINTN Index
1018 )
2ef2b01e
A
1019{
1020 CHAR8 *DevStr;
1021 CHAR8 Path[MAX_CMD_LINE];
1022
1023 switch (DeviceType) {
1024 case EfiOpenLoadFile:
1025 DevStr = "loadfile%d:";
1026 break;
1027 case EfiOpenFirmwareVolume:
1028 DevStr = "fv%d:";
1029 break;
1030 case EfiOpenFileSystem:
1031 DevStr = "fs%d:";
1032 break;
1033 case EfiOpenBlockIo:
1034 DevStr = "blk%d:";
1035 break;
1036 case EfiOpenMemoryBuffer:
1037 DevStr = "a%d:";
1038 break;
1039 default:
1040 return NULL;
1041 }
1042
1043 AsciiSPrint (Path, MAX_PATHNAME, DevStr, Index);
1044
1045 return EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1046}
1047
1048
1049/**
66c0b446 1050Close a file handle opened by EfiOpen() and free all resources allocated by
1051EfiOpen().
2ef2b01e 1052
66c0b446 1053@param Stream Open File Handle
2ef2b01e 1054
66c0b446 1055@return EFI_INVALID_PARAMETER Stream is not an Open File
1056@return EFI_SUCCESS Steam closed
2ef2b01e
A
1057
1058**/
1059EFI_STATUS
1060EfiClose (
f6381f4c 1061 IN EFI_OPEN_FILE *File
1062 )
2ef2b01e
A
1063{
1064 EFI_STATUS Status;
1065 UINT64 TftpBufferSize;
1066
1067 if (!FileHandleValid (File)) {
1068 return EFI_INVALID_PARAMETER;
1069 }
1070
1071 //Write the buffer contents to TFTP file.
1072 if ((File->Type == EfiOpenTftp) && (File->IsDirty)) {
1073
1074 TftpBufferSize = File->Size;
1075 Status = EblMtftp (
66c0b446 1076 EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
1077 File->Buffer,
1078 TRUE,
1079 &TftpBufferSize,
1080 NULL,
1081 &File->ServerIp,
1082 (UINT8 *)File->FileName,
1083 NULL,
1084 FALSE
1085 );
2ef2b01e
A
1086 if (EFI_ERROR(Status)) {
1087 AsciiPrint("TFTP error during APPLE_NSP_TFTP_WRITE_FILE: %r\n", Status);
1088 return Status;
1089 }
1090 }
1091
1092 if ((File->Type == EfiOpenLoadFile) ||
66c0b446 1093 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
1094 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
f6381f4c 1095 EblFreePool(File->Buffer);
1096 }
2ef2b01e 1097
f6381f4c 1098 EblFreePool (File->DevicePath);
1099 EblFreePool (File->DeviceName);
1100 EblFreePool (File->FsFileInfo);
1101 EblFreePool (File->FsInfo);
2ef2b01e 1102
f6381f4c 1103 if (File->FsFileHandle != NULL) {
1104 File->FsFileHandle->Close (File->FsFileHandle);
1105 }
66c0b446 1106
f6381f4c 1107 // Need to free File and it's Guard structures
1108 EblFreePool (BASE_CR (File, EFI_OPEN_FILE_GUARD, File));
1109 return EFI_SUCCESS;
2ef2b01e
A
1110}
1111
1112
1113/**
66c0b446 1114Return the size of the file represented by Stream. Also return the current
1115Seek position. Opening a file will enable a valid file size to be returned.
1116LoadFile is an exception as a load file size is set to zero.
2ef2b01e 1117
66c0b446 1118@param Stream Open File Handle
2ef2b01e 1119
66c0b446 1120@return 0 Stream is not an Open File or a valid LoadFile handle
2ef2b01e
A
1121
1122**/
1123UINTN
1124EfiTell (
f6381f4c 1125 IN EFI_OPEN_FILE *File,
1126 OUT EFI_LBA *CurrentPosition OPTIONAL
1127 )
2ef2b01e
A
1128{
1129 EFI_STATUS Status;
1130 UINT64 BufferSize = 0;
66c0b446 1131
2ef2b01e
A
1132 if (!FileHandleValid (File)) {
1133 return 0;
1134 }
1135
1136 if (CurrentPosition != NULL) {
1137 *CurrentPosition = File->CurrentPosition;
1138 }
1139
1140 if (File->Type == EfiOpenLoadFile) {
1141 // Figure out the File->Size
1142 File->Buffer = NULL;
1143 File->Size = 0;
1144 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, &File->Size, File->Buffer);
1145 if (Status != EFI_BUFFER_TOO_SMALL) {
1146 return 0;
1147 }
66c0b446 1148
2ef2b01e
A
1149 File->MaxPosition = (UINT64)File->Size;
1150 } else if (File->Type == EfiOpenTftp) {
66c0b446 1151
2ef2b01e 1152 Status = EblMtftp (
66c0b446 1153 EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
1154 NULL,
1155 FALSE,
1156 &BufferSize,
1157 NULL,
1158 &File->ServerIp,
1159 (UINT8 *)File->FileName,
1160 NULL,
1161 TRUE
1162 );
2ef2b01e
A
1163 if (EFI_ERROR(Status)) {
1164 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
1165 return 0;
1166 }
1167
ab2a68f4 1168 File->Size = (UINTN)BufferSize;
2ef2b01e
A
1169 File->MaxPosition = File->Size;
1170 }
1171
1172 return File->Size;
1173}
1174
1175
1176/**
66c0b446 1177Seek to the Offset locaiton in the file. LoadFile and FV device types do
1178not support EfiSeek(). It is not possible to grow the file size using
1179EfiSeek().
1180
1181SeekType defines how use Offset to calculate the new file position:
1182EfiSeekStart : Position = Offset
1183EfiSeekCurrent: Position is Offset bytes from the current position
1184EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
2ef2b01e 1185
66c0b446 1186@param Stream Open File Handle
1187@param Offset Offset to seek too.
1188@param SeekType Type of seek to perform
2ef2b01e
A
1189
1190
66c0b446 1191@return EFI_INVALID_PARAMETER Stream is not an Open File
1192@return EFI_UNSUPPORTED LoadFile and FV doe not support Seek
1193@return EFI_NOT_FOUND Seek past the end of the file.
1194@return EFI_SUCCESS Steam closed
2ef2b01e
A
1195
1196**/
1197EFI_STATUS
1198EfiSeek (
f6381f4c 1199 IN EFI_OPEN_FILE *File,
1200 IN EFI_LBA Offset,
1201 IN EFI_SEEK_TYPE SeekType
1202 )
2ef2b01e
A
1203{
1204 EFI_STATUS Status;
1205 UINT64 CurrentPosition;
1206
1207 if (!FileHandleValid (File)) {
1208 return EFI_INVALID_PARAMETER;
1209 }
1210
875c883e 1211 if (File->Type == EfiOpenLoadFile) {
1212 // LoadFile does not support Seek
1213 return EFI_UNSUPPORTED;
2ef2b01e
A
1214 }
1215
1216 CurrentPosition = File->CurrentPosition;
1217 switch (SeekType) {
1218 case EfiSeekStart:
1219 if (Offset > File->MaxPosition) {
1220 return EFI_NOT_FOUND;
1221 }
1222 CurrentPosition = Offset;
1223 break;
1224
1225 case EfiSeekCurrent:
1226 if ((File->CurrentPosition + Offset) > File->MaxPosition) {
1227 return EFI_NOT_FOUND;
1228 }
1229 CurrentPosition += Offset;
1230 break;
1231
1232 case EfiSeekEnd:
1233 if (Offset != 0) {
1234 // We don't support growing file size via seeking past end of file
1235 return EFI_UNSUPPORTED;
1236 }
1237 CurrentPosition = File->MaxPosition;
1238 break;
1239
1240 default:
1241 return EFI_NOT_FOUND;
1242 }
1243
1244 Status = EFI_SUCCESS;
1245 if (File->FsFileHandle != NULL) {
1246 Status = File->FsFileHandle->SetPosition (File->FsFileHandle, CurrentPosition);
1247 }
1248
1249 if (!EFI_ERROR (Status)) {
1250 File->CurrentPosition = CurrentPosition;
1251 }
1252
1253 return Status;
1254}
1255
1256EFI_STATUS
1257CacheTftpFile (
f6381f4c 1258 IN OUT EFI_OPEN_FILE *File
1259 )
2ef2b01e
A
1260{
1261 EFI_STATUS Status;
1262 UINT64 TftpBufferSize;
1263
1264 if (File->IsBufferValid) {
1265 return EFI_SUCCESS;
1266 }
1267
1268 // Make sure the file size is set.
1269 EfiTell (File, NULL);
1270
1271 //Allocate a buffer to hold the whole file.
1272 File->Buffer = AllocatePool(File->Size);
1273 if (File->Buffer == NULL) {
1274 return EFI_OUT_OF_RESOURCES;
1275 }
1276
1277 TftpBufferSize = File->Size;
1278
1279 Status = EblMtftp (
66c0b446 1280 EFI_PXE_BASE_CODE_TFTP_READ_FILE,
1281 File->Buffer,
1282 FALSE,
1283 &TftpBufferSize,
1284 NULL,
1285 &File->ServerIp,
1286 (UINT8 *)File->FileName,
1287 NULL,
1288 FALSE);
2ef2b01e
A
1289 if (EFI_ERROR(Status)) {
1290 AsciiPrint("TFTP error during APPLE_NSP_TFTP_READ_FILE: %r\n", Status);
1291 FreePool(File->Buffer);
1292 return Status;
1293 }
1294
1295 // Set the buffer valid flag.
1296 File->IsBufferValid = TRUE;
1297
1298 return Status;
1299}
1300
1301/**
66c0b446 1302Read BufferSize bytes from the current locaiton in the file. For load file,
1303FV, and TFTP case you must read the entire file.
2ef2b01e 1304
66c0b446 1305@param Stream Open File Handle
1306@param Buffer Caller allocated buffer.
1307@param BufferSize Size of buffer in bytes.
2ef2b01e
A
1308
1309
66c0b446 1310@return EFI_SUCCESS Stream is not an Open File
1311@return EFI_END_OF_FILE Tried to read past the end of the file
1312@return EFI_INVALID_PARAMETER Stream is not an open file handle
1313@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1314@return "other" Error returned from device read
2ef2b01e
A
1315
1316**/
1317EFI_STATUS
1318EfiRead (
f6381f4c 1319 IN EFI_OPEN_FILE *File,
1320 OUT VOID *Buffer,
1321 OUT UINTN *BufferSize
1322 )
2ef2b01e
A
1323{
1324 EFI_STATUS Status;
1325 UINT32 AuthenticationStatus;
1326 EFI_DISK_IO_PROTOCOL *DiskIo;
1327
1328 if (!FileHandleValid (File)) {
1329 return EFI_INVALID_PARAMETER;
1330 }
1331
1332 // Don't read past the end of the file.
1333 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1334 return EFI_END_OF_FILE;
1335 }
1336
1337 switch (File->Type) {
2ef2b01e
A
1338 case EfiOpenLoadFile:
1339 // Figure out the File->Size
1340 EfiTell (File, NULL);
1341
1342 Status = File->LoadFile->LoadFile (File->LoadFile, File->DevicePath, FALSE, BufferSize, Buffer);
1343 break;
66c0b446 1344
2ef2b01e 1345 case EfiOpenFirmwareVolume:
a6d7123e 1346 if (CompareGuid (&File->FvNameGuid, &gZeroGuid)) {
1347 // This is the entire FV device, so treat like a memory buffer
1348 CopyMem (Buffer, (VOID *)(UINTN)(File->FvStart + File->CurrentPosition), *BufferSize);
1349 File->CurrentPosition += *BufferSize;
1350 Status = EFI_SUCCESS;
2ef2b01e 1351 } else {
875c883e 1352 if (File->Buffer == NULL) {
1353 if (File->FvSectionType == EFI_SECTION_ALL) {
1354 Status = File->Fv->ReadFile (
66c0b446 1355 File->Fv,
1356 &File->FvNameGuid,
1357 (VOID **)&File->Buffer,
1358 &File->Size,
1359 &File->FvType,
1360 &File->FvAttributes,
1361 &AuthenticationStatus
1362 );
875c883e 1363 } else {
1364 Status = File->Fv->ReadSection (
66c0b446 1365 File->Fv,
1366 &File->FvNameGuid,
1367 File->FvSectionType,
1368 0,
1369 (VOID **)&File->Buffer,
1370 &File->Size,
1371 &AuthenticationStatus
1372 );
875c883e 1373 }
1374 if (EFI_ERROR (Status)) {
1375 return Status;
1376 }
1377 File->IsBufferValid = TRUE;
a6d7123e 1378 }
875c883e 1379 // Operate on the cached buffer so Seek will work
1380 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1381 File->CurrentPosition += *BufferSize;
1382 Status = EFI_SUCCESS;
2ef2b01e
A
1383 }
1384 break;
66c0b446 1385
875c883e 1386 case EfiOpenMemoryBuffer:
1387 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1388 File->CurrentPosition += *BufferSize;
1389 Status = EFI_SUCCESS;
1390 break;
2ef2b01e
A
1391
1392 case EfiOpenFileSystem:
1393 Status = File->FsFileHandle->Read (File->FsFileHandle, BufferSize, Buffer);
1394 File->CurrentPosition += *BufferSize;
1395 break;
1396
1397 case EfiOpenBlockIo:
1398 Status = gBS->HandleProtocol(File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1399 if (!EFI_ERROR(Status)) {
0a0951ea 1400 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
2ef2b01e
A
1401 }
1402 File->CurrentPosition += *BufferSize;
1403 break;
66c0b446 1404
2ef2b01e
A
1405 case EfiOpenTftp:
1406 // Cache the file if it hasn't been cached yet.
1407 if (File->IsBufferValid == FALSE) {
1408 Status = CacheTftpFile (File);
1409 if (EFI_ERROR (Status)) {
1410 return Status;
1411 }
1412 }
1413
1414 // Copy out the requested data
1415 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1416 File->CurrentPosition += *BufferSize;
1417
1418 Status = EFI_SUCCESS;
1419 break;
66c0b446 1420
2ef2b01e
A
1421 default:
1422 return EFI_INVALID_PARAMETER;
1423 };
1424
1425 return Status;
1426}
1427
1428
1429/**
66c0b446 1430Read the entire file into a buffer. This routine allocates the buffer and
1431returns it to the user full of the read data.
2ef2b01e 1432
66c0b446 1433This is very useful for load flie where it's hard to know how big the buffer
1434must be.
2ef2b01e 1435
66c0b446 1436@param Stream Open File Handle
1437@param Buffer Pointer to buffer to return.
1438@param BufferSize Pointer to Size of buffer return..
2ef2b01e
A
1439
1440
66c0b446 1441@return EFI_SUCCESS Stream is not an Open File
1442@return EFI_END_OF_FILE Tried to read past the end of the file
1443@return EFI_INVALID_PARAMETER Stream is not an open file handle
1444@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1445@return "other" Error returned from device read
2ef2b01e
A
1446
1447**/
1448EFI_STATUS
1449EfiReadAllocatePool (
f6381f4c 1450 IN EFI_OPEN_FILE *File,
1451 OUT VOID **Buffer,
1452 OUT UINTN *BufferSize
1453 )
2ef2b01e
A
1454{
1455 if (!FileHandleValid (File)) {
1456 return EFI_INVALID_PARAMETER;
1457 }
1458
1459 // Loadfile defers file size determination on Open so use tell to find it
1460 EfiTell (File, NULL);
1461
1462 *BufferSize = File->Size;
1463 *Buffer = AllocatePool (*BufferSize);
1464 if (*Buffer == NULL) {
1465 return EFI_NOT_FOUND;
1466 }
1467
1468 return EfiRead (File, *Buffer, BufferSize);
1469}
1470
1471
1472/**
66c0b446 1473Write data back to the file. For TFTP case you must write the entire file.
2ef2b01e 1474
66c0b446 1475@param Stream Open File Handle
1476@param Buffer Pointer to buffer to return.
1477@param BufferSize Pointer to Size of buffer return..
2ef2b01e
A
1478
1479
66c0b446 1480@return EFI_SUCCESS Stream is not an Open File
1481@return EFI_END_OF_FILE Tried to read past the end of the file
1482@return EFI_INVALID_PARAMETER Stream is not an open file handle
1483@return EFI_BUFFER_TOO_SMALL Buffer is not big enough to do the read
1484@return "other" Error returned from device write
2ef2b01e
A
1485
1486**/
1487EFI_STATUS
1488EfiWrite (
f6381f4c 1489 IN EFI_OPEN_FILE *File,
1490 OUT VOID *Buffer,
1491 OUT UINTN *BufferSize
1492 )
2ef2b01e
A
1493{
1494 EFI_STATUS Status;
1495 EFI_FV_WRITE_FILE_DATA FileData;
1496 EFI_DISK_IO_PROTOCOL *DiskIo;
1497
1498 if (!FileHandleValid (File)) {
1499 return EFI_INVALID_PARAMETER;
1500 }
1501
1502 switch (File->Type) {
1503 case EfiOpenMemoryBuffer:
1504 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1505 return EFI_END_OF_FILE;
1506 }
1507
1508 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1509 File->CurrentPosition += *BufferSize;
1510 Status = EFI_SUCCESS;
1511
1512 case EfiOpenLoadFile:
1513 // LoadFile device is read only be definition
1514 Status = EFI_UNSUPPORTED;
66c0b446 1515
2ef2b01e
A
1516 case EfiOpenFirmwareVolume:
1517 if (File->FvSectionType != EFI_SECTION_ALL) {
1518 // Writes not support to a specific section. You have to update entire file
1519 return EFI_UNSUPPORTED;
1520 }
1521
1522 FileData.NameGuid = &(File->FvNameGuid);
1523 FileData.Type = File->FvType;
1524 FileData.FileAttributes = File->FvAttributes;
1525 FileData.Buffer = Buffer;
1526 FileData.BufferSize = (UINT32)*BufferSize;
1527 Status = File->Fv->WriteFile (File->Fv, 1, EFI_FV_UNRELIABLE_WRITE, &FileData);
1528 break;
66c0b446 1529
2ef2b01e
A
1530 case EfiOpenFileSystem:
1531 Status = File->FsFileHandle->Write (File->FsFileHandle, BufferSize, Buffer);
1532 File->CurrentPosition += *BufferSize;
1533 break;
1534
1535 case EfiOpenBlockIo:
1536 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1537 return EFI_END_OF_FILE;
1538 }
1539
1540 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDiskIoProtocolGuid, (VOID **)&DiskIo);
1541 if (!EFI_ERROR(Status)) {
0a0951ea 1542 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
2ef2b01e
A
1543 }
1544 File->CurrentPosition += *BufferSize;
1545 break;
1546
1547 case EfiOpenTftp:
1548 // Cache the file if it hasn't been cached yet.
1549 if (File->IsBufferValid == FALSE) {
1550 Status = CacheTftpFile(File);
1551 if (EFI_ERROR(Status)) {
1552 return Status;
1553 }
1554 }
1555
1556 // Don't overwrite the buffer
1557 if ((File->CurrentPosition + *BufferSize) > File->MaxPosition) {
1558 UINT8 *TempBuffer;
1559
1560 TempBuffer = File->Buffer;
1561
ab2a68f4 1562 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
2ef2b01e
A
1563 if (File->Buffer == NULL) {
1564 return EFI_OUT_OF_RESOURCES;
1565 }
1566
1567 CopyMem (File->Buffer, TempBuffer, File->Size);
1568
1569 FreePool (TempBuffer);
1570
ab2a68f4 1571 File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
1572 File->MaxPosition = (UINT64)File->Size;
2ef2b01e
A
1573 }
1574
1575 // Copy in the requested data
1576 CopyMem (File->Buffer + File->CurrentPosition, Buffer, *BufferSize);
1577 File->CurrentPosition += *BufferSize;
1578
1579 // Mark the file dirty
1580 File->IsDirty = TRUE;
1581
1582 Status = EFI_SUCCESS;
1583 break;
1584
1585 default:
1586 Status = EFI_INVALID_PARAMETER;
1587 };
1588
1589 return Status;
1590}
1591
85e385f4 1592
16ccac42 1593/**
66c0b446 1594Given Cwd expand Path to remove .. and replace them with real
1595directory names.
16ccac42 1596
66c0b446 1597@param Cwd Current Working Directory
1598@param Path Path to expand
1599
1600@return NULL Cwd or Path are not valid
1601@return 'other' Path with .. expanded
16ccac42 1602
1603**/
1604CHAR8 *
1605ExpandPath (
f6381f4c 1606 IN CHAR8 *Cwd,
1607 IN CHAR8 *Path
1608 )
16ccac42 1609{
1610 CHAR8 *NewPath;
1611 CHAR8 *Work, *Start, *End;
1612 UINTN StrLen;
0a0951ea 1613 INTN i;
66c0b446 1614
16ccac42 1615 if (Cwd == NULL || Path == NULL) {
1616 return NULL;
1617 }
66c0b446 1618
16ccac42 1619 StrLen = AsciiStrSize (Cwd);
1620 if (StrLen <= 2) {
1621 // Smallest valid path is 1 char and a null
1622 return NULL;
1623 }
1624
1625 StrLen = AsciiStrSize (Path);
1626 NewPath = AllocatePool (AsciiStrSize (Cwd) + StrLen + 1);
1627 if (NewPath == NULL) {
1628 return NULL;
1629 }
1630 AsciiStrCpy (NewPath, Cwd);
66c0b446 1631
16ccac42 1632 End = Path + StrLen;
1633 for (Start = Path ;;) {
1634 Work = AsciiStrStr (Start, "..") ;
1635 if (Work == NULL) {
1636 // Remaining part of Path contains no more ..
1637 break;
1638 }
66c0b446 1639
16ccac42 1640 // append path prior to ..
1641 AsciiStrnCat (NewPath, Start, Work - Start);
1642 StrLen = AsciiStrLen (NewPath);
1643 for (i = StrLen; i >= 0; i--) {
1644 if (NewPath[i] == ':') {
1645 // too many ..
1646 return NULL;
1647 }
1648 if (NewPath[i] == '/' || NewPath[i] == '\\') {
1649 if ((i > 0) && (NewPath[i-1] == ':')) {
1650 // leave the / before a :
1651 NewPath[i+1] = '\0';
1652 } else {
1653 // replace / will Null to remove trailing file/dir reference
1654 NewPath[i] = '\0';
1655 }
1656 break;
1657 }
1658 }
66c0b446 1659
16ccac42 1660 Start = Work + 3;
1661 }
66c0b446 1662
16ccac42 1663 // Handle the path that remains after the ..
1664 AsciiStrnCat (NewPath, Start, End - Start);
66c0b446 1665
16ccac42 1666 return NewPath;
1667}
1668
85e385f4 1669
1670/**
66c0b446 1671Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1672the path does not contain a device name, The CWD is prepended to the path.
1673
1674@param Cwd Current Working Directory to set
85e385f4 1675
1676
66c0b446 1677@return EFI_SUCCESS CWD is set
1678@return EFI_INVALID_PARAMETER Cwd is not a valid device:path
85e385f4 1679
1680**/
1681EFI_STATUS
1682EfiSetCwd (
f6381f4c 1683 IN CHAR8 *Cwd
1684 )
85e385f4 1685{
1686 EFI_OPEN_FILE *File;
16ccac42 1687 UINTN Len;
1688 CHAR8 *Path;
66c0b446 1689
16ccac42 1690 if (Cwd == NULL) {
85e385f4 1691 return EFI_INVALID_PARAMETER;
1692 }
66c0b446 1693
16ccac42 1694 if (AsciiStrCmp (Cwd, ".") == 0) {
1695 // cd . is a no-op
1696 return EFI_SUCCESS;
1697 }
66c0b446 1698
16ccac42 1699 Path = Cwd;
1700 if (AsciiStrStr (Cwd, "..") != NULL) {
1701 if (gCwd == NULL) {
1702 // no parent
1703 return EFI_SUCCESS;
1704 }
66c0b446 1705
16ccac42 1706 Len = AsciiStrLen (gCwd);
1707 if ((gCwd[Len-2] == ':') && ((gCwd[Len-1] == '/') || (gCwd[Len-1] == '\\'))) {
1708 // parent is device so nothing to do
1709 return EFI_SUCCESS;
1710 }
66c0b446 1711
16ccac42 1712 // Expand .. in Cwd, given we know current working directory
1713 Path = ExpandPath (gCwd, Cwd);
1714 if (Path == NULL) {
1715 return EFI_NOT_FOUND;
1716 }
1717 }
66c0b446 1718
16ccac42 1719 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1720 if (File == NULL) {
1721 return EFI_INVALID_PARAMETER;
1722 }
66c0b446 1723
85e385f4 1724 if (gCwd != NULL) {
1725 FreePool (gCwd);
1726 }
66c0b446 1727
16ccac42 1728 // Use the info returned from EfiOpen as it can add in CWD if needed. So Cwd could be
1729 // relative to the current gCwd or not.
9c0ce9f3 1730 gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
85e385f4 1731 if (gCwd == NULL) {
1732 return EFI_INVALID_PARAMETER;
1733 }
66c0b446 1734
16ccac42 1735 EfiClose (File);
1736 if (Path != Cwd) {
1737 FreePool (Path);
1738 }
85e385f4 1739 return EFI_SUCCESS;
1740}
1741
1742
1743/**
66c0b446 1744Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1745the path does not contain a device name, The CWD is prepended to the path.
1746The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1747a call to EfiSetCwd() it is not legal to use the pointer returned by
1748this funciton.
1749
1750@param Cwd Current Working Directory
1751
1752
1753@return "" No CWD set
1754@return 'other' Returns buffer that contains CWD.
1755
85e385f4 1756**/
1757CHAR8 *
16ccac42 1758EfiGetCwd (
f6381f4c 1759 VOID
1760 )
85e385f4 1761{
16ccac42 1762 if (gCwd == NULL) {
1763 return "";
1764 }
85e385f4 1765 return gCwd;
1766}
1767
1768