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