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