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