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