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