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