]> git.proxmox.com Git - mirror_edk2.git/blob - 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
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 insensative 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 #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
58 CHAR8 *gCwd = NULL;
59
60 CONST EFI_GUID gZeroGuid = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
61
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
68 typedef 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
76 EFI_HANDLE *mBlkIo = NULL;
77 UINTN mBlkIoCount = 0;
78
79 EFI_HANDLE *mFs = NULL;
80 UINTN mFsCount = 0;
81 // mFsInfo[] array entries must match mFs[] handles
82 EFI_FILE_SYSTEM_INFO **mFsInfo = NULL;
83
84 EFI_HANDLE *mFv = NULL;
85 UINTN mFvCount = 0;
86 EFI_HANDLE *mLoadFile = NULL;
87 UINTN mLoadFileCount = 0;
88
89
90
91 /**
92 Internal worker function to validate a File handle.
93
94 @param File Open File Handle
95
96 @return TRUE File is valid
97 @return FALSE File is not valid
98
99
100 **/
101 BOOLEAN
102 FileHandleValid (
103 IN EFI_OPEN_FILE *File
104 )
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) ||
111 (GuardFile->Footer != EFI_OPEN_FILE_GUARD_FOOTER) ) {
112 return FALSE;
113 }
114
115 return TRUE;
116 }
117
118 /**
119 Internal worker function. If Buffer is not NULL free it.
120
121 @param Buffer Buffer to FreePool()
122
123 **/
124 VOID
125 EblFreePool (
126 IN VOID *Buffer
127 )
128 {
129 if (Buffer != NULL) {
130 FreePool (Buffer);
131 }
132 }
133
134 /**
135 Update Device List Global Variables
136
137 **/
138 VOID
139 EblUpdateDeviceLists (
140 VOID
141 )
142 {
143 EFI_STATUS Status;
144 UINTN Size;
145 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
146 EFI_BLOCK_IO_PROTOCOL *BlkIo;
147 EFI_FILE_HANDLE Root;
148 UINTN Index;
149 BOOLEAN Update;
150
151 if (mBlkIo != NULL) {
152 FreePool (mBlkIo);
153 }
154 gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &mBlkIoCount, &mBlkIo);
155
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
181 if (mFv != NULL) {
182 FreePool (mFv);
183 }
184 gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &mFvCount, &mFv);
185
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 /**
236 PathName is in the form <device name>:<path> for example fs1:\ or ROOT:\.
237 Return TRUE if the <devce name> prefix of PathName matches a file system
238 Volume Name. MatchIndex is the array index in mFsInfo[] of the match,
239 and it can be used with mFs[] to find the handle that needs to be opened
240
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
244
245 @return TRUE PathName matches a Volume Label and MatchIndex is valid
246 @return FALSE PathName does not match a Volume Label MatchIndex undefined
247
248 **/
249 BOOLEAN
250 EblMatchVolumeName (
251 IN CHAR8 *PathName,
252 IN UINTN FileStart,
253 OUT UINTN *MatchIndex
254 )
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 /**
291 Return the number of devices of the current type active in the system
292
293 @param Type Device type to check
294
295 @return 0 Invalid type
296
297 **/
298 UINTN
299 EfiGetDeviceCounts (
300 IN EFI_OPEN_FILE_TYPE DeviceType
301 )
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
317 EFI_STATUS
318 ConvertIpStringToEfiIp (
319 IN CHAR8 *PathName,
320 OUT EFI_IP_ADDRESS *ServerIp
321 )
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);
348
349 return EFI_SUCCESS;
350 }
351
352
353 /**
354 Internal work function to extract a device number from a string skipping
355 text. Easy way to extract numbers from strings like blk7:.
356
357 @param Str String to extract device number form
358
359 @return -1 Device string is not valid
360 @return Device #
361
362 **/
363 UINTN
364 EblConvertDevStringToNumber (
365 IN CHAR8 *Str
366 )
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 /**
386 Internal work function to fill in EFI_OPEN_FILE information for the Fs and BlkIo
387
388 @param File Open file handle
389 @param FileName Name of file after device stripped off
390
391
392 **/
393 EFI_STATUS
394 EblFileDevicePath (
395 IN OUT EFI_OPEN_FILE *File,
396 IN CHAR8 *FileName,
397 IN CONST UINT64 OpenMode
398 )
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)) {
436 File->FsBlockIoMedia = BlkIo->Media;
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 }
454
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 }
476
477 return Status;
478 }
479
480 #define ToUpper(a) ((((a) >= 'a') && ((a) <= 'z')) ? ((a) - 'a' + 'A') : (a))
481
482 EFI_STATUS
483 CompareGuidToString (
484 IN EFI_GUID *Guid,
485 IN CHAR8 *String
486 )
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 /**
522 Internal work function to fill in EFI_OPEN_FILE information for the FV
523
524 @param File Open file handle
525 @param FileName Name of file after device stripped off
526
527
528 **/
529 EFI_STATUS
530 EblFvFileDevicePath (
531 IN OUT EFI_OPEN_FILE *File,
532 IN CHAR8 *FileName,
533 IN CONST UINT64 OpenMode
534 )
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;
549 EFI_FIRMWARE_VOLUME_HEADER *FvHeader = NULL;
550 UINTN Index;
551
552
553 Status = gBS->HandleProtocol (File->EfiHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&File->Fv);
554 if (EFI_ERROR (Status)) {
555 return Status;
556 }
557
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)) {
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 }
568
569 for (Lba = 0, File->FvSize = 0, NumberOfBlocks = 0; ; File->FvSize += (BlockSize * NumberOfBlocks), Lba += NumberOfBlocks) {
570 Status = Fvb->GetBlockSize (Fvb, Lba, &BlockSize, &NumberOfBlocks);
571 if (EFI_ERROR (Status)) {
572 break;
573 }
574 }
575 }
576 }
577
578
579 DevicePath = DevicePathFromHandle (File->EfiHandle);
580
581 if (*FileName == '\0') {
582 File->DevicePath = DuplicateDevicePath (DevicePath);
583 File->Size = File->FvSize;
584 File->MaxPosition = File->Size;
585 } else {
586 Key = 0;
587 do {
588 File->FvType = EFI_FV_FILETYPE_ALL;
589 GetNextFileStatus = File->Fv->GetNextFile (
590 File->Fv,
591 &Key,
592 &File->FvType,
593 &File->FvNameGuid,
594 &File->FvAttributes,
595 &File->Size
596 );
597 if (!EFI_ERROR (GetNextFileStatus)) {
598 // Compare GUID first
599 Status = CompareGuidToString (&File->FvNameGuid, FileName);
600 if (!EFI_ERROR(Status)) {
601 break;
602 }
603
604 Section = NULL;
605 Status = File->Fv->ReadSection (
606 File->Fv,
607 &File->FvNameGuid,
608 EFI_SECTION_USER_INTERFACE,
609 0,
610 &Section,
611 &SectionSize,
612 &AuthenticationStatus
613 );
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
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 (
634 File->Fv,
635 &File->FvNameGuid,
636 (EFI_SECTION_TYPE)OpenMode,
637 0,
638 &Section,
639 &File->Size,
640 &AuthenticationStatus
641 );
642 if (EFI_ERROR (Status)) {
643 return Status;
644 }
645 }
646
647 File->MaxPosition = File->Size;
648 EfiInitializeFwVolDevicepathNode (&DevicePathNode, &File->FvNameGuid);
649 File->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&DevicePathNode);
650 }
651
652
653 // FVB not required if FV was soft loaded...
654 return EFI_SUCCESS;
655 }
656
657
658
659
660 /**
661 Open a device named by PathName. The PathName includes a device name and
662 path seperated by a :. See file header for more details on the PathName
663 syntax. There is no checking to prevent a file from being opened more than
664 one type.
665
666 SectionType is only used to open an FV. Each file in an FV contains multiple
667 secitons and only the SectionType section is opened.
668
669 For any file that is opened with EfiOpen() must be closed with EfiClose().
670
671 @param PathName Path to parse to open
672 @param OpenMode Same as EFI_FILE.Open()
673 @param SectionType Section in FV to open.
674
675 @return NULL Open failed
676 @return Valid EFI_OPEN_FILE handle
677
678 **/
679 EFI_OPEN_FILE *
680 EfiOpen (
681 IN CHAR8 *PathName,
682 IN CONST UINT64 OpenMode,
683 IN CONST EFI_SECTION_TYPE SectionType
684 )
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;
697 CHAR8 *CwdPlusPathName;
698 UINTN Index;
699 EFI_SECTION_TYPE ModifiedSectionType;
700
701 EblUpdateDeviceLists ();
702
703 File = &FileData;
704 ZeroMem (File, sizeof (EFI_OPEN_FILE));
705
706 StrLen = AsciiStrSize (PathName);
707 if (StrLen <= 1) {
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
719 //
720 // Matching volume name has precedence over handle based names
721 //
722 VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber);
723 if (!VolumeNameMatch) {
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 }
730
731 // We could add a current working diretory concept
732 CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName));
733 if (CwdPlusPathName == NULL) {
734 return NULL;
735 }
736
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 }
755
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 }
761
762 File = EfiOpen (CwdPlusPathName, OpenMode, SectionType);
763 FreePool (CwdPlusPathName);
764 return File;
765 }
766
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];
774 if (File->FileName[0] == '\0') {
775 // if it is just a file name use / as root
776 File->FileName = "\\";
777 }
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);
793
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 }
805
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
812 ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]);
813 PathName[Index] = '\0';
814 }
815 }
816 File->FvSectionType = ModifiedSectionType;
817 Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType);
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 }
829
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 }
836
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 }
842
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];
852
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);
863
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 }
887
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 }
895
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 }
906
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
931 ErrorExit:
932 FreePool (File->DeviceName);
933 return NULL;
934 }
935
936 #define FILE_COPY_CHUNK 0x01000000
937
938 EFI_STATUS
939 EfiCopyFile (
940 IN CHAR8 *DestinationFile,
941 IN CHAR8 *SourceFile
942 )
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;
951
952 Source = EfiOpen (SourceFile, EFI_FILE_MODE_READ, 0);
953 if (Source == NULL) {
954 AsciiPrint("Source file open error.\n");
955 Status = EFI_NOT_FOUND;
956 goto Exit;
957 }
958
959 Destination = EfiOpen (DestinationFile, EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
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 }
971
972 Size = EfiTell(Source, NULL);
973
974 for (Offset = 0; Offset + FILE_COPY_CHUNK <= Size; Offset += Chunk) {
975 Chunk = FILE_COPY_CHUNK;
976
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 }
989
990 // Any left over?
991 if (Offset < Size) {
992 Chunk = Size - Offset;
993
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
1007 Exit:
1008 if (Source != NULL) {
1009 Status = EfiClose(Source);
1010 if (EFI_ERROR(Status)) {
1011 AsciiPrint("Source close error");
1012 }
1013 }
1014
1015 if (Destination != NULL) {
1016 Status = EfiClose(Destination);
1017 if (EFI_ERROR(Status)) {
1018 AsciiPrint("Destination close error");
1019 }
1020 }
1021
1022 if (Buffer != NULL) {
1023 FreePool(Buffer);
1024 }
1025
1026 return Status;
1027 }
1028
1029 /**
1030 Use DeviceType and Index to form a valid PathName and try and open it.
1031
1032 @param DeviceType Device type to open
1033 @param Index Device Index to use. Zero relative.
1034
1035 @return NULL Open failed
1036 @return Valid EFI_OPEN_FILE handle
1037
1038 **/
1039 EFI_OPEN_FILE *
1040 EfiDeviceOpenByType (
1041 IN EFI_OPEN_FILE_TYPE DeviceType,
1042 IN UINTN Index
1043 )
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 /**
1075 Close a file handle opened by EfiOpen() and free all resources allocated by
1076 EfiOpen().
1077
1078 @param Stream Open File Handle
1079
1080 @return EFI_INVALID_PARAMETER Stream is not an Open File
1081 @return EFI_SUCCESS Steam closed
1082
1083 **/
1084 EFI_STATUS
1085 EfiClose (
1086 IN EFI_OPEN_FILE *File
1087 )
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 (
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 );
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) ||
1118 ((File->Type == EfiOpenTftp) && (File->IsBufferValid == TRUE)) ||
1119 ((File->Type == EfiOpenFirmwareVolume) && (File->IsBufferValid == TRUE))) {
1120 EblFreePool(File->Buffer);
1121 }
1122
1123 EblFreePool (File->DevicePath);
1124 EblFreePool (File->DeviceName);
1125 EblFreePool (File->FsFileInfo);
1126 EblFreePool (File->FsInfo);
1127
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;
1135 }
1136
1137
1138 /**
1139 Return the size of the file represented by Stream. Also return the current
1140 Seek position. Opening a file will enable a valid file size to be returned.
1141 LoadFile is an exception as a load file size is set to zero.
1142
1143 @param Stream Open File Handle
1144
1145 @return 0 Stream is not an Open File or a valid LoadFile handle
1146
1147 **/
1148 UINTN
1149 EfiTell (
1150 IN EFI_OPEN_FILE *File,
1151 OUT EFI_LBA *CurrentPosition OPTIONAL
1152 )
1153 {
1154 EFI_STATUS Status;
1155 UINT64 BufferSize = 0;
1156
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 }
1173
1174 File->MaxPosition = (UINT64)File->Size;
1175 } else if (File->Type == EfiOpenTftp) {
1176
1177 Status = EblMtftp (
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 );
1188 if (EFI_ERROR(Status)) {
1189 AsciiPrint("TFTP error during APPLE_NSP_TFTP_GET_FILE_SIZE: %r\n", Status);
1190 return 0;
1191 }
1192
1193 File->Size = (UINTN)BufferSize;
1194 File->MaxPosition = File->Size;
1195 }
1196
1197 return File->Size;
1198 }
1199
1200
1201 /**
1202 Seek to the Offset locaiton in the file. LoadFile and FV device types do
1203 not support EfiSeek(). It is not possible to grow the file size using
1204 EfiSeek().
1205
1206 SeekType defines how use Offset to calculate the new file position:
1207 EfiSeekStart : Position = Offset
1208 EfiSeekCurrent: Position is Offset bytes from the current position
1209 EfiSeekEnd : Only supported if Offset is zero to seek to end of file.
1210
1211 @param Stream Open File Handle
1212 @param Offset Offset to seek too.
1213 @param SeekType Type of seek to perform
1214
1215
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
1220
1221 **/
1222 EFI_STATUS
1223 EfiSeek (
1224 IN EFI_OPEN_FILE *File,
1225 IN EFI_LBA Offset,
1226 IN EFI_SEEK_TYPE SeekType
1227 )
1228 {
1229 EFI_STATUS Status;
1230 UINT64 CurrentPosition;
1231
1232 if (!FileHandleValid (File)) {
1233 return EFI_INVALID_PARAMETER;
1234 }
1235
1236 if (File->Type == EfiOpenLoadFile) {
1237 // LoadFile does not support Seek
1238 return EFI_UNSUPPORTED;
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
1281 EFI_STATUS
1282 CacheTftpFile (
1283 IN OUT EFI_OPEN_FILE *File
1284 )
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 (
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);
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 /**
1327 Read BufferSize bytes from the current locaiton in the file. For load file,
1328 FV, and TFTP case you must read the entire file.
1329
1330 @param Stream Open File Handle
1331 @param Buffer Caller allocated buffer.
1332 @param BufferSize Size of buffer in bytes.
1333
1334
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
1340
1341 **/
1342 EFI_STATUS
1343 EfiRead (
1344 IN EFI_OPEN_FILE *File,
1345 OUT VOID *Buffer,
1346 OUT UINTN *BufferSize
1347 )
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) {
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;
1369
1370 case EfiOpenFirmwareVolume:
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;
1376 } else {
1377 if (File->Buffer == NULL) {
1378 if (File->FvSectionType == EFI_SECTION_ALL) {
1379 Status = File->Fv->ReadFile (
1380 File->Fv,
1381 &File->FvNameGuid,
1382 (VOID **)&File->Buffer,
1383 &File->Size,
1384 &File->FvType,
1385 &File->FvAttributes,
1386 &AuthenticationStatus
1387 );
1388 } else {
1389 Status = File->Fv->ReadSection (
1390 File->Fv,
1391 &File->FvNameGuid,
1392 File->FvSectionType,
1393 0,
1394 (VOID **)&File->Buffer,
1395 &File->Size,
1396 &AuthenticationStatus
1397 );
1398 }
1399 if (EFI_ERROR (Status)) {
1400 return Status;
1401 }
1402 File->IsBufferValid = TRUE;
1403 }
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;
1408 }
1409 break;
1410
1411 case EfiOpenMemoryBuffer:
1412 CopyMem (Buffer, File->Buffer + File->CurrentPosition, *BufferSize);
1413 File->CurrentPosition += *BufferSize;
1414 Status = EFI_SUCCESS;
1415 break;
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)) {
1425 Status = DiskIo->ReadDisk(DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
1426 }
1427 File->CurrentPosition += *BufferSize;
1428 break;
1429
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;
1445
1446 default:
1447 return EFI_INVALID_PARAMETER;
1448 };
1449
1450 return Status;
1451 }
1452
1453
1454 /**
1455 Read the entire file into a buffer. This routine allocates the buffer and
1456 returns it to the user full of the read data.
1457
1458 This is very useful for load flie where it's hard to know how big the buffer
1459 must be.
1460
1461 @param Stream Open File Handle
1462 @param Buffer Pointer to buffer to return.
1463 @param BufferSize Pointer to Size of buffer return..
1464
1465
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
1471
1472 **/
1473 EFI_STATUS
1474 EfiReadAllocatePool (
1475 IN EFI_OPEN_FILE *File,
1476 OUT VOID **Buffer,
1477 OUT UINTN *BufferSize
1478 )
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 /**
1498 Write data back to the file. For TFTP case you must write the entire file.
1499
1500 @param Stream Open File Handle
1501 @param Buffer Pointer to buffer to return.
1502 @param BufferSize Pointer to Size of buffer return..
1503
1504
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
1510
1511 **/
1512 EFI_STATUS
1513 EfiWrite (
1514 IN EFI_OPEN_FILE *File,
1515 OUT VOID *Buffer,
1516 OUT UINTN *BufferSize
1517 )
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;
1540
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;
1554
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)) {
1567 Status = DiskIo->WriteDisk (DiskIo, File->FsBlockIoMedia->MediaId, File->DiskOffset + File->CurrentPosition, *BufferSize, Buffer);
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
1587 File->Buffer = AllocatePool ((UINTN)(File->CurrentPosition + *BufferSize));
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
1596 File->Size = (UINTN)(File->CurrentPosition + *BufferSize);
1597 File->MaxPosition = (UINT64)File->Size;
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
1617
1618 /**
1619 Given Cwd expand Path to remove .. and replace them with real
1620 directory names.
1621
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
1627
1628 **/
1629 CHAR8 *
1630 ExpandPath (
1631 IN CHAR8 *Cwd,
1632 IN CHAR8 *Path
1633 )
1634 {
1635 CHAR8 *NewPath;
1636 CHAR8 *Work, *Start, *End;
1637 UINTN StrLen;
1638 INTN i;
1639
1640 if (Cwd == NULL || Path == NULL) {
1641 return NULL;
1642 }
1643
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);
1656
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 }
1664
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 }
1684
1685 Start = Work + 3;
1686 }
1687
1688 // Handle the path that remains after the ..
1689 AsciiStrnCat (NewPath, Start, End - Start);
1690
1691 return NewPath;
1692 }
1693
1694
1695 /**
1696 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1697 the path does not contain a device name, The CWD is prepended to the path.
1698
1699 @param Cwd Current Working Directory to set
1700
1701
1702 @return EFI_SUCCESS CWD is set
1703 @return EFI_INVALID_PARAMETER Cwd is not a valid device:path
1704
1705 **/
1706 EFI_STATUS
1707 EfiSetCwd (
1708 IN CHAR8 *Cwd
1709 )
1710 {
1711 EFI_OPEN_FILE *File;
1712 UINTN Len;
1713 CHAR8 *Path;
1714
1715 if (Cwd == NULL) {
1716 return EFI_INVALID_PARAMETER;
1717 }
1718
1719 if (AsciiStrCmp (Cwd, ".") == 0) {
1720 // cd . is a no-op
1721 return EFI_SUCCESS;
1722 }
1723
1724 Path = Cwd;
1725 if (AsciiStrStr (Cwd, "..") != NULL) {
1726 if (gCwd == NULL) {
1727 // no parent
1728 return EFI_SUCCESS;
1729 }
1730
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 }
1736
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 }
1743
1744 File = EfiOpen (Path, EFI_FILE_MODE_READ, 0);
1745 if (File == NULL) {
1746 return EFI_INVALID_PARAMETER;
1747 }
1748
1749 if (gCwd != NULL) {
1750 FreePool (gCwd);
1751 }
1752
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.
1755 gCwd = AllocatePool (AsciiStrSize (File->DeviceName) + AsciiStrSize (File->FileName) + 10);
1756 if (gCwd == NULL) {
1757 return EFI_INVALID_PARAMETER;
1758 }
1759
1760 EfiClose (File);
1761 if (Path != Cwd) {
1762 FreePool (Path);
1763 }
1764 return EFI_SUCCESS;
1765 }
1766
1767
1768 /**
1769 Set the Curent Working Directory (CWD). If a call is made to EfiOpen () and
1770 the path does not contain a device name, The CWD is prepended to the path.
1771 The CWD buffer is only valid until a new call is made to EfiSetCwd(). After
1772 a call to EfiSetCwd() it is not legal to use the pointer returned by
1773 this funciton.
1774
1775 @param Cwd Current Working Directory
1776
1777
1778 @return "" No CWD set
1779 @return 'other' Returns buffer that contains CWD.
1780
1781 **/
1782 CHAR8 *
1783 EfiGetCwd (
1784 VOID
1785 )
1786 {
1787 if (gCwd == NULL) {
1788 return "";
1789 }
1790 return gCwd;
1791 }
1792
1793