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