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