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