1 /** @file
2   Provides interface to EFI_FILE_HANDLE functionality.
3 
4   Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved. <BR>
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include <Uefi.h>
16 
17 #include <Protocol/SimpleFileSystem.h>
18 #include <Protocol/UnicodeCollation.h>
19 
20 #include <Guid/FileInfo.h>
21 
22 #include <Library/DebugLib.h>
23 #include <Library/MemoryAllocationLib.h>
24 #include <Library/BaseLib.h>
25 #include <Library/BaseMemoryLib.h>
26 #include <Library/FileHandleLib.h>
27 #include <Library/PcdLib.h>
28 #include <Library/PrintLib.h>
29 
30 CONST UINT16 gUnicodeFileTag = EFI_UNICODE_BYTE_ORDER_MARK;
31 
32 #define MAX_FILE_NAME_LEN 522 // (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes)
33 #define FIND_XXXXX_FILE_BUFFER_SIZE (SIZE_OF_EFI_FILE_INFO + MAX_FILE_NAME_LEN)
34 
35 /**
36   This function will retrieve the information about the file for the handle
37   specified and store it in allocated pool memory.
38 
39   This function allocates a buffer to store the file's information. It is the
40   caller's responsibility to free the buffer
41 
42   @param  FileHandle  The file handle of the file for which information is
43   being requested.
44 
45   @retval NULL information could not be retrieved.
46 
47   @return the information about the file
48 **/
49 EFI_FILE_INFO*
50 EFIAPI
FileHandleGetInfo(IN EFI_FILE_HANDLE FileHandle)51 FileHandleGetInfo (
52   IN EFI_FILE_HANDLE            FileHandle
53   )
54 {
55   EFI_FILE_INFO   *FileInfo;
56   UINTN           FileInfoSize;
57   EFI_STATUS      Status;
58 
59   if (FileHandle == NULL) {
60     return (NULL);
61   }
62 
63   //
64   // Get the required size to allocate
65   //
66   FileInfoSize = 0;
67   FileInfo = NULL;
68   Status = FileHandle->GetInfo(FileHandle,
69                                &gEfiFileInfoGuid,
70                                &FileInfoSize,
71                                NULL);
72   if (Status == EFI_BUFFER_TOO_SMALL){
73     //
74     // error is expected.  getting size to allocate
75     //
76     FileInfo = AllocateZeroPool(FileInfoSize);
77     //
78     // now get the information
79     //
80     Status = FileHandle->GetInfo(FileHandle,
81                                  &gEfiFileInfoGuid,
82                                  &FileInfoSize,
83                                  FileInfo);
84     //
85     // if we got an error free the memory and return NULL
86     //
87     if (EFI_ERROR(Status) && (FileInfo != NULL)) {
88       FreePool(FileInfo);
89       FileInfo = NULL;
90     }
91   }
92   return (FileInfo);
93 }
94 
95 /**
96   This function sets the information about the file for the opened handle
97   specified.
98 
99   @param[in]  FileHandle        The file handle of the file for which information
100                                 is being set.
101 
102   @param[in]  FileInfo          The information to set.
103 
104   @retval EFI_SUCCESS           The information was set.
105   @retval EFI_INVALID_PARAMETER A parameter was out of range or invalid.
106   @retval EFI_UNSUPPORTED       The FileHandle does not support FileInfo.
107   @retval EFI_NO_MEDIA          The device has no medium.
108   @retval EFI_DEVICE_ERROR      The device reported an error.
109   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
110   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
111   @retval EFI_ACCESS_DENIED     The file was opened read only.
112   @retval EFI_VOLUME_FULL       The volume is full.
113 **/
114 EFI_STATUS
115 EFIAPI
FileHandleSetInfo(IN EFI_FILE_HANDLE FileHandle,IN CONST EFI_FILE_INFO * FileInfo)116 FileHandleSetInfo (
117   IN EFI_FILE_HANDLE            FileHandle,
118   IN CONST EFI_FILE_INFO        *FileInfo
119   )
120 {
121 
122   if (FileHandle == NULL || FileInfo == NULL) {
123     return (EFI_INVALID_PARAMETER);
124   }
125 
126   //
127   // Set the info
128   //
129   return (FileHandle->SetInfo(FileHandle,
130                               &gEfiFileInfoGuid,
131                               (UINTN)FileInfo->Size,
132                               (EFI_FILE_INFO*)FileInfo));
133 }
134 
135 /**
136   This function reads information from an opened file.
137 
138   If FileHandle is not a directory, the function reads the requested number of
139   bytes from the file at the file's current position and returns them in Buffer.
140   If the read goes beyond the end of the file, the read length is truncated to the
141   end of the file. The file's current position is increased by the number of bytes
142   returned.  If FileHandle is a directory, the function reads the directory entry
143   at the file's current position and returns the entry in Buffer. If the Buffer
144   is not large enough to hold the current directory entry, then
145   EFI_BUFFER_TOO_SMALL is returned and the current file position is not updated.
146   BufferSize is set to be the size of the buffer needed to read the entry. On
147   success, the current position is updated to the next directory entry. If there
148   are no more directory entries, the read returns a zero-length buffer.
149   EFI_FILE_INFO is the structure returned as the directory entry.
150 
151   @param FileHandle             the opened file handle
152   @param BufferSize             on input the size of buffer in bytes.  on return
153                                 the number of bytes written.
154   @param Buffer                 the buffer to put read data into.
155 
156   @retval EFI_SUCCESS           Data was read.
157   @retval EFI_NO_MEDIA          The device has no media.
158   @retval EFI_DEVICE_ERROR      The device reported an error.
159   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
160   @retval EFI_BUFFER_TO_SMALL   Buffer is too small. ReadSize contains required
161                                 size.
162 
163 **/
164 EFI_STATUS
165 EFIAPI
FileHandleRead(IN EFI_FILE_HANDLE FileHandle,IN OUT UINTN * BufferSize,OUT VOID * Buffer)166 FileHandleRead(
167   IN EFI_FILE_HANDLE            FileHandle,
168   IN OUT UINTN                  *BufferSize,
169   OUT VOID                      *Buffer
170   )
171 {
172   if (FileHandle == NULL) {
173     return (EFI_INVALID_PARAMETER);
174   }
175 
176   //
177   // Perform the read based on EFI_FILE_PROTOCOL
178   //
179   return (FileHandle->Read(FileHandle, BufferSize, Buffer));
180 }
181 
182 
183 /**
184   Write data to a file.
185 
186   This function writes the specified number of bytes to the file at the current
187   file position. The current file position is advanced the actual number of bytes
188   written, which is returned in BufferSize. Partial writes only occur when there
189   has been a data error during the write attempt (such as "volume space full").
190   The file is automatically grown to hold the data if required. Direct writes to
191   opened directories are not supported.
192 
193   @param FileHandle           The opened file for writing
194   @param BufferSize           on input the number of bytes in Buffer.  On output
195                               the number of bytes written.
196   @param Buffer               the buffer containing data to write is stored.
197 
198  @retval EFI_SUCCESS          Data was written.
199  @retval EFI_UNSUPPORTED      Writes to an open directory are not supported.
200  @retval EFI_NO_MEDIA         The device has no media.
201  @retval EFI_DEVICE_ERROR     The device reported an error.
202  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
203  @retval EFI_WRITE_PROTECTED  The device is write-protected.
204  @retval EFI_ACCESS_DENIED    The file was open for read only.
205  @retval EFI_VOLUME_FULL      The volume is full.
206 **/
207 EFI_STATUS
208 EFIAPI
FileHandleWrite(IN EFI_FILE_HANDLE FileHandle,IN OUT UINTN * BufferSize,IN VOID * Buffer)209 FileHandleWrite(
210   IN EFI_FILE_HANDLE            FileHandle,
211   IN OUT UINTN                  *BufferSize,
212   IN VOID                       *Buffer
213   )
214 {
215   if (FileHandle == NULL) {
216     return (EFI_INVALID_PARAMETER);
217   }
218 
219   //
220   // Perform the write based on EFI_FILE_PROTOCOL
221   //
222   return (FileHandle->Write(FileHandle, BufferSize, Buffer));
223 }
224 
225 /**
226   Close an open file handle.
227 
228   This function closes a specified file handle. All "dirty" cached file data is
229   flushed to the device, and the file is closed. In all cases the handle is
230   closed.
231 
232 @param FileHandle               the file handle to close.
233 
234 @retval EFI_SUCCESS             the file handle was closed sucessfully.
235 **/
236 EFI_STATUS
237 EFIAPI
FileHandleClose(IN EFI_FILE_HANDLE FileHandle)238 FileHandleClose (
239   IN EFI_FILE_HANDLE            FileHandle
240   )
241 {
242   EFI_STATUS Status;
243 
244   if (FileHandle == NULL) {
245     return (EFI_INVALID_PARAMETER);
246   }
247 
248   //
249   // Perform the Close based on EFI_FILE_PROTOCOL
250   //
251   Status = FileHandle->Close(FileHandle);
252   return Status;
253 }
254 
255 /**
256   Delete a file and close the handle
257 
258   This function closes and deletes a file. In all cases the file handle is closed.
259   If the file cannot be deleted, the warning code EFI_WARN_DELETE_FAILURE is
260   returned, but the handle is still closed.
261 
262   @param FileHandle             the file handle to delete
263 
264   @retval EFI_SUCCESS           the file was closed sucessfully
265   @retval EFI_WARN_DELETE_FAILURE the handle was closed, but the file was not
266                                 deleted
267   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
268 **/
269 EFI_STATUS
270 EFIAPI
FileHandleDelete(IN EFI_FILE_HANDLE FileHandle)271 FileHandleDelete (
272   IN EFI_FILE_HANDLE    FileHandle
273   )
274 {
275   EFI_STATUS Status;
276 
277   if (FileHandle == NULL) {
278     return (EFI_INVALID_PARAMETER);
279   }
280 
281   //
282   // Perform the Delete based on EFI_FILE_PROTOCOL
283   //
284   Status = FileHandle->Delete(FileHandle);
285   return Status;
286 }
287 
288 /**
289   Set the current position in a file.
290 
291   This function sets the current file position for the handle to the position
292   supplied. With the exception of seeking to position 0xFFFFFFFFFFFFFFFF, only
293   absolute positioning is supported, and seeking past the end of the file is
294   allowed (a subsequent write would grow the file). Seeking to position
295   0xFFFFFFFFFFFFFFFF causes the current position to be set to the end of the file.
296   If FileHandle is a directory, the only position that may be set is zero. This
297   has the effect of starting the read process of the directory entries over.
298 
299   @param FileHandle             The file handle on which the position is being set
300   @param Position               Byte position from begining of file
301 
302   @retval EFI_SUCCESS           Operation completed sucessfully.
303   @retval EFI_UNSUPPORTED       the seek request for non-zero is not valid on
304                                 directories.
305   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
306 **/
307 EFI_STATUS
308 EFIAPI
FileHandleSetPosition(IN EFI_FILE_HANDLE FileHandle,IN UINT64 Position)309 FileHandleSetPosition (
310   IN EFI_FILE_HANDLE    FileHandle,
311   IN UINT64             Position
312   )
313 {
314   if (FileHandle == NULL) {
315     return (EFI_INVALID_PARAMETER);
316   }
317 
318   //
319   // Perform the SetPosition based on EFI_FILE_PROTOCOL
320   //
321   return (FileHandle->SetPosition(FileHandle, Position));
322 }
323 
324 /**
325   Gets a file's current position
326 
327   This function retrieves the current file position for the file handle. For
328   directories, the current file position has no meaning outside of the file
329   system driver and as such the operation is not supported. An error is returned
330   if FileHandle is a directory.
331 
332   @param FileHandle             The open file handle on which to get the position.
333   @param Position               Byte position from begining of file.
334 
335   @retval EFI_SUCCESS           the operation completed sucessfully.
336   @retval INVALID_PARAMETER     One of the parameters has an invalid value.
337   @retval EFI_UNSUPPORTED       the request is not valid on directories.
338 **/
339 EFI_STATUS
340 EFIAPI
FileHandleGetPosition(IN EFI_FILE_HANDLE FileHandle,OUT UINT64 * Position)341 FileHandleGetPosition (
342   IN EFI_FILE_HANDLE            FileHandle,
343   OUT UINT64                    *Position
344   )
345 {
346   if (Position == NULL || FileHandle == NULL) {
347     return (EFI_INVALID_PARAMETER);
348   }
349 
350   //
351   // Perform the GetPosition based on EFI_FILE_PROTOCOL
352   //
353   return (FileHandle->GetPosition(FileHandle, Position));
354 }
355 /**
356   Flushes data on a file
357 
358   This function flushes all modified data associated with a file to a device.
359 
360   @param FileHandle             The file handle on which to flush data
361 
362   @retval EFI_SUCCESS           The data was flushed.
363   @retval EFI_NO_MEDIA          The device has no media.
364   @retval EFI_DEVICE_ERROR      The device reported an error.
365   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
366   @retval EFI_WRITE_PROTECTED   The file or medium is write protected.
367   @retval EFI_ACCESS_DENIED     The file was opened for read only.
368 **/
369 EFI_STATUS
370 EFIAPI
FileHandleFlush(IN EFI_FILE_HANDLE FileHandle)371 FileHandleFlush (
372   IN EFI_FILE_HANDLE            FileHandle
373   )
374 {
375   if (FileHandle == NULL) {
376     return (EFI_INVALID_PARAMETER);
377   }
378 
379   //
380   // Perform the Flush based on EFI_FILE_PROTOCOL
381   //
382   return (FileHandle->Flush(FileHandle));
383 }
384 
385 /**
386   function to determine if a given handle is a directory handle
387 
388   if DirHandle is NULL then return error
389 
390   open the file information on the DirHandle and verify that the Attribute
391   includes EFI_FILE_DIRECTORY bit set.
392 
393   @param DirHandle              Handle to open file
394 
395   @retval EFI_SUCCESS           DirHandle is a directory
396   @retval EFI_INVALID_PARAMETER DirHandle did not have EFI_FILE_INFO available
397   @retval EFI_NOT_FOUND         DirHandle is not a directory
398 **/
399 EFI_STATUS
400 EFIAPI
FileHandleIsDirectory(IN EFI_FILE_HANDLE DirHandle)401 FileHandleIsDirectory (
402   IN EFI_FILE_HANDLE            DirHandle
403   )
404 {
405   EFI_FILE_INFO *DirInfo;
406 
407   if (DirHandle == NULL) {
408     return (EFI_INVALID_PARAMETER);
409   }
410 
411   //
412   // get the file information for DirHandle
413   //
414   DirInfo = FileHandleGetInfo (DirHandle);
415 
416   //
417   // Parse DirInfo
418   //
419   if (DirInfo == NULL) {
420     //
421     // We got nothing...
422     //
423     return (EFI_INVALID_PARAMETER);
424   }
425   if ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
426     //
427     // Attributes say this is not a directory
428     //
429     FreePool (DirInfo);
430     return (EFI_NOT_FOUND);
431   }
432   //
433   // all good...
434   //
435   FreePool (DirInfo);
436   return (EFI_SUCCESS);
437 }
438 
439 /** Retrieve first entry from a directory.
440 
441   This function takes an open directory handle and gets information from the
442   first entry in the directory.  A buffer is allocated to contain
443   the information and a pointer to the buffer is returned in *Buffer.  The
444   caller can use FileHandleFindNextFile() to get subsequent directory entries.
445 
446   The buffer will be freed by FileHandleFindNextFile() when the last directory
447   entry is read.  Otherwise, the caller must free the buffer, using FreePool,
448   when finished with it.
449 
450   @param[in]  DirHandle         The file handle of the directory to search.
451   @param[out] Buffer            The pointer to pointer to buffer for file's information.
452 
453   @retval EFI_SUCCESS           Found the first file.
454   @retval EFI_NOT_FOUND         Cannot find the directory.
455   @retval EFI_NO_MEDIA          The device has no media.
456   @retval EFI_DEVICE_ERROR      The device reported an error.
457   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
458   @return Others                status of FileHandleGetInfo, FileHandleSetPosition,
459                                 or FileHandleRead
460 **/
461 EFI_STATUS
462 EFIAPI
FileHandleFindFirstFile(IN EFI_FILE_HANDLE DirHandle,OUT EFI_FILE_INFO ** Buffer)463 FileHandleFindFirstFile (
464   IN EFI_FILE_HANDLE            DirHandle,
465   OUT EFI_FILE_INFO             **Buffer
466   )
467 {
468   EFI_STATUS    Status;
469   UINTN         BufferSize;
470 
471   if (Buffer == NULL || DirHandle == NULL) {
472     return (EFI_INVALID_PARAMETER);
473   }
474 
475   //
476   // verify that DirHandle is a directory
477   //
478   Status = FileHandleIsDirectory(DirHandle);
479   if (EFI_ERROR(Status)) {
480     return (Status);
481   }
482 
483   //
484   // Allocate a buffer sized to struct size + enough for the string at the end
485   //
486   BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE;
487   *Buffer = AllocateZeroPool(BufferSize);
488   if (*Buffer == NULL){
489     return (EFI_OUT_OF_RESOURCES);
490   }
491 
492   //
493   // reset to the begining of the directory
494   //
495   Status = FileHandleSetPosition(DirHandle, 0);
496   if (EFI_ERROR(Status)) {
497     FreePool(*Buffer);
498     *Buffer = NULL;
499     return (Status);
500   }
501 
502   //
503   // read in the info about the first file
504   //
505   Status = FileHandleRead (DirHandle, &BufferSize, *Buffer);
506   ASSERT(Status != EFI_BUFFER_TOO_SMALL);
507   if (EFI_ERROR(Status) || BufferSize == 0) {
508     FreePool(*Buffer);
509     *Buffer = NULL;
510     if (BufferSize == 0) {
511       return (EFI_NOT_FOUND);
512     }
513     return (Status);
514   }
515   return (EFI_SUCCESS);
516 }
517 
518 /** Retrieve next entries from a directory.
519 
520   To use this function, the caller must first call the FileHandleFindFirstFile()
521   function to get the first directory entry.  Subsequent directory entries are
522   retrieved by using the FileHandleFindNextFile() function.  This function can
523   be called several times to get each entry from the directory.  If the call of
524   FileHandleFindNextFile() retrieved the last directory entry, the next call of
525   this function will set *NoFile to TRUE and free the buffer.
526 
527   @param[in]  DirHandle         The file handle of the directory.
528   @param[out] Buffer            The pointer to buffer for file's information.
529   @param[out] NoFile            The pointer to boolean when last file is found.
530 
531   @retval EFI_SUCCESS           Found the next file, or reached last file
532   @retval EFI_NO_MEDIA          The device has no media.
533   @retval EFI_DEVICE_ERROR      The device reported an error.
534   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
535 **/
536 EFI_STATUS
537 EFIAPI
FileHandleFindNextFile(IN EFI_FILE_HANDLE DirHandle,OUT EFI_FILE_INFO * Buffer,OUT BOOLEAN * NoFile)538 FileHandleFindNextFile(
539   IN EFI_FILE_HANDLE          DirHandle,
540   OUT EFI_FILE_INFO          *Buffer,
541   OUT BOOLEAN                *NoFile
542   )
543 {
544   EFI_STATUS    Status;
545   UINTN         BufferSize;
546 
547   if (DirHandle == NULL || Buffer == NULL || NoFile == NULL) {
548     return (EFI_INVALID_PARAMETER);
549   }
550 
551   //
552   // This BufferSize MUST stay equal to the originally allocated one in GetFirstFile
553   //
554   BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE;
555 
556   //
557   // read in the info about the next file
558   //
559   Status = FileHandleRead (DirHandle, &BufferSize, Buffer);
560   ASSERT(Status != EFI_BUFFER_TOO_SMALL);
561   if (EFI_ERROR(Status)) {
562     return (Status);
563   }
564 
565   //
566   // If we read 0 bytes (but did not have erros) we already read in the last file.
567   //
568   if (BufferSize == 0) {
569     FreePool(Buffer);
570     *NoFile = TRUE;
571   }
572 
573   return (EFI_SUCCESS);
574 }
575 
576 /**
577   Retrieve the size of a file.
578 
579   if FileHandle is NULL then return error
580   if Size is NULL then return error
581 
582   This function extracts the file size info from the FileHandle's EFI_FILE_INFO
583   data.
584 
585   @param FileHandle             file handle from which size is retrieved
586   @param Size                   pointer to size
587 
588   @retval EFI_SUCCESS           operation was completed sucessfully
589   @retval EFI_DEVICE_ERROR      cannot access the file
590 **/
591 EFI_STATUS
592 EFIAPI
FileHandleGetSize(IN EFI_FILE_HANDLE FileHandle,OUT UINT64 * Size)593 FileHandleGetSize (
594   IN EFI_FILE_HANDLE            FileHandle,
595   OUT UINT64                    *Size
596   )
597 {
598   EFI_FILE_INFO                 *FileInfo;
599 
600   if (FileHandle == NULL || Size == NULL) {
601     return (EFI_INVALID_PARAMETER);
602   }
603 
604   //
605   // get the FileInfo structure
606   //
607   FileInfo = FileHandleGetInfo(FileHandle);
608   if (FileInfo == NULL) {
609     return (EFI_DEVICE_ERROR);
610   }
611 
612   //
613   // Assign the Size pointer to the correct value
614   //
615   *Size = FileInfo->FileSize;
616 
617   //
618   // free the FileInfo memory
619   //
620   FreePool(FileInfo);
621 
622   return (EFI_SUCCESS);
623 }
624 
625 /**
626   Set the size of a file.
627 
628   If FileHandle is NULL then return error.
629 
630   This function changes the file size info from the FileHandle's EFI_FILE_INFO
631   data.
632 
633   @param FileHandle             File handle whose size is to be changed.
634   @param Size                   New size.
635 
636   @retval EFI_SUCCESS           operation was completed sucessfully.
637   @retval EFI_DEVICE_ERROR      cannot access the file.
638 **/
639 EFI_STATUS
640 EFIAPI
FileHandleSetSize(IN EFI_FILE_HANDLE FileHandle,IN UINT64 Size)641 FileHandleSetSize (
642   IN EFI_FILE_HANDLE            FileHandle,
643   IN UINT64                     Size
644   )
645 {
646   EFI_FILE_INFO                 *FileInfo;
647   EFI_STATUS                    Status;
648 
649   if (FileHandle == NULL) {
650     return (EFI_INVALID_PARAMETER);
651   }
652 
653   //
654   // get the FileInfo structure
655   //
656   FileInfo = FileHandleGetInfo(FileHandle);
657   if (FileInfo == NULL) {
658     return (EFI_DEVICE_ERROR);
659   }
660 
661   //
662   // Assign the FileSize pointer to the new value
663   //
664   FileInfo->FileSize = Size;
665 
666   Status = FileHandleSetInfo(FileHandle, FileInfo);
667   //
668   // free the FileInfo memory
669   //
670   FreePool(FileInfo);
671 
672   return (Status);
673 }
674 
675 /**
676   Safely append (on the left) with automatic string resizing given length of Destination and
677   desired length of copy from Source.
678 
679   append the first D characters of Source to the end of Destination, where D is
680   the lesser of Count and the StrLen() of Source. If appending those D characters
681   will fit within Destination (whose Size is given as CurrentSize) and
682   still leave room for a NULL terminator, then those characters are appended,
683   starting at the original terminating NULL of Destination, and a new terminating
684   NULL is appended.
685 
686   If appending D characters onto Destination will result in a overflow of the size
687   given in CurrentSize the string will be grown such that the copy can be performed
688   and CurrentSize will be updated to the new size.
689 
690   If Source is NULL, there is nothing to append, just return the current buffer in
691   Destination.
692 
693   if Destination is NULL, then return error
694   if Destination's current length (including NULL terminator) is already more then
695   CurrentSize, then ASSERT()
696 
697   @param[in, out] Destination   The String to append onto
698   @param[in, out] CurrentSize   on call the number of bytes in Destination.  On
699                                 return possibly the new size (still in bytes).  if NULL
700                                 then allocate whatever is needed.
701   @param[in]      Source        The String to append from
702   @param[in]      Count         Maximum number of characters to append.  if 0 then
703                                 all are appended.
704 
705   @return Destination           return the resultant string.
706 **/
707 CHAR16*
708 EFIAPI
StrnCatGrowLeft(IN OUT CHAR16 ** Destination,IN OUT UINTN * CurrentSize,IN CONST CHAR16 * Source,IN UINTN Count)709 StrnCatGrowLeft (
710   IN OUT CHAR16           **Destination,
711   IN OUT UINTN            *CurrentSize,
712   IN     CONST CHAR16     *Source,
713   IN     UINTN            Count
714   )
715 {
716   UINTN DestinationStartSize;
717   UINTN NewSize;
718   UINTN CopySize;
719 
720   if (Destination == NULL) {
721     return (NULL);
722   }
723 
724   //
725   // If there's nothing to do then just return Destination
726   //
727   if (Source == NULL) {
728     return (*Destination);
729   }
730 
731   //
732   // allow for NULL pointers address as Destination
733   //
734   if (*Destination != NULL) {
735     ASSERT(CurrentSize != 0);
736     DestinationStartSize = StrSize(*Destination);
737     ASSERT(DestinationStartSize <= *CurrentSize);
738   } else {
739     DestinationStartSize = 0;
740 //    ASSERT(*CurrentSize == 0);
741   }
742 
743   //
744   // Append all of Source?
745   //
746   if (Count == 0) {
747     Count = StrSize(Source);
748   }
749 
750   //
751   // Test and grow if required
752   //
753   if (CurrentSize != NULL) {
754     NewSize = *CurrentSize;
755     while (NewSize < (DestinationStartSize + Count)) {
756       NewSize += 2 * Count;
757     }
758     *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination);
759     *CurrentSize = NewSize;
760   } else {
761     *Destination = AllocateZeroPool(Count+sizeof(CHAR16));
762   }
763   if (*Destination == NULL) {
764     return NULL;
765   }
766 
767   CopySize = StrSize(*Destination);
768   CopyMem((*Destination)+((Count-2)/sizeof(CHAR16)), *Destination, CopySize);
769   CopyMem(*Destination, Source, Count-2);
770   return (*Destination);
771 }
772 
773 /**
774   Function to get a full filename given a EFI_FILE_HANDLE somewhere lower on the
775   directory 'stack'.
776 
777   if Handle is NULL, return EFI_INVALID_PARAMETER
778 
779   @param[in] Handle             Handle to the Directory or File to create path to.
780   @param[out] FullFileName      pointer to pointer to generated full file name.  It
781                                 is the responsibility of the caller to free this memory
782                                 with a call to FreePool().
783   @retval EFI_SUCCESS           the operation was sucessful and the FullFileName is valid.
784   @retval EFI_INVALID_PARAMETER Handle was NULL.
785   @retval EFI_INVALID_PARAMETER FullFileName was NULL.
786   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
787 **/
788 EFI_STATUS
789 EFIAPI
FileHandleGetFileName(IN CONST EFI_FILE_HANDLE Handle,OUT CHAR16 ** FullFileName)790 FileHandleGetFileName (
791   IN CONST EFI_FILE_HANDLE      Handle,
792   OUT CHAR16                    **FullFileName
793   )
794 {
795   EFI_STATUS      Status;
796   UINTN           Size;
797   EFI_FILE_HANDLE CurrentHandle;
798   EFI_FILE_HANDLE NextHigherHandle;
799   EFI_FILE_INFO   *FileInfo;
800 
801   Size = 0;
802 
803   //
804   // Check our parameters
805   //
806   if (FullFileName == NULL || Handle == NULL) {
807     return (EFI_INVALID_PARAMETER);
808   }
809 
810   *FullFileName = NULL;
811   CurrentHandle = NULL;
812 
813   Status = Handle->Open(Handle, &CurrentHandle, L".", EFI_FILE_MODE_READ, 0);
814   if (!EFI_ERROR(Status)) {
815     //
816     // Reverse out the current directory on the device
817     //
818     for (;;) {
819       FileInfo = FileHandleGetInfo(CurrentHandle);
820       if (FileInfo == NULL) {
821         Status = EFI_OUT_OF_RESOURCES;
822         break;
823       } else {
824         //
825         // We got info... do we have a name? if yes preceed the current path with it...
826         //
827         if (StrLen (FileInfo->FileName) == 0) {
828           if (*FullFileName == NULL) {
829             ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
830             *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
831           }
832           FreePool(FileInfo);
833           break;
834         } else {
835           if (*FullFileName == NULL) {
836             ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
837             *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
838           }
839           ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
840           *FullFileName = StrnCatGrowLeft(FullFileName, &Size, FileInfo->FileName, 0);
841           *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
842           FreePool(FileInfo);
843         }
844       }
845       //
846       // Move to the parent directory
847       //
848       Status = CurrentHandle->Open (CurrentHandle, &NextHigherHandle, L"..", EFI_FILE_MODE_READ, 0);
849       if (EFI_ERROR (Status)) {
850         break;
851       }
852 
853       FileHandleClose(CurrentHandle);
854       CurrentHandle = NextHigherHandle;
855     }
856   } else if (Status == EFI_NOT_FOUND) {
857     Status = EFI_SUCCESS;
858     ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
859     *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
860   }
861 
862   if (CurrentHandle != NULL) {
863     CurrentHandle->Close (CurrentHandle);
864   }
865 
866   if (EFI_ERROR(Status) && *FullFileName != NULL) {
867     FreePool(*FullFileName);
868   }
869 
870   return (Status);
871 }
872 
873 /**
874   Function to read a single line from a file. The \n is not included in the returned
875   buffer.  The returned buffer must be callee freed.
876 
877   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
878   maintained and not changed for all operations with the same file.
879 
880   @param[in]       Handle        FileHandle to read from.
881   @param[in, out]  Ascii         Boolean value for indicating whether the file is Ascii (TRUE) or UCS2 (FALSE);
882 
883   @return                       The line of text from the file.
884 
885   @sa FileHandleReadLine
886 **/
887 CHAR16*
888 EFIAPI
FileHandleReturnLine(IN EFI_FILE_HANDLE Handle,IN OUT BOOLEAN * Ascii)889 FileHandleReturnLine(
890   IN EFI_FILE_HANDLE            Handle,
891   IN OUT BOOLEAN                *Ascii
892   )
893 {
894   CHAR16          *RetVal;
895   UINTN           Size;
896   EFI_STATUS      Status;
897 
898   Size = 0;
899   RetVal = NULL;
900 
901   Status = FileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
902   if (Status == EFI_BUFFER_TOO_SMALL) {
903     RetVal = AllocateZeroPool(Size);
904     Status = FileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
905   }
906   ASSERT_EFI_ERROR(Status);
907   if (EFI_ERROR(Status) && (RetVal != NULL)) {
908     FreePool(RetVal);
909     RetVal = NULL;
910   }
911   return (RetVal);
912 }
913 
914 /**
915   Function to read a single line (up to but not including the \n) from a EFI_FILE_HANDLE.
916 
917   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
918   maintained and not changed for all operations with the same file.
919 
920   @param[in]       Handle        FileHandle to read from
921   @param[in, out]  Buffer        pointer to buffer to read into
922   @param[in, out]  Size          pointer to number of bytes in buffer
923   @param[in]       Truncate      if TRUE then allows for truncation of the line to fit.
924                                  if FALSE will reset the position to the begining of the
925                                  line if the buffer is not large enough.
926   @param[in, out]  Ascii         Boolean value for indicating whether the file is Ascii (TRUE) or UCS2 (FALSE);
927 
928   @retval EFI_SUCCESS           the operation was sucessful.  the line is stored in
929                                 Buffer.
930   @retval EFI_INVALID_PARAMETER Handle was NULL.
931   @retval EFI_INVALID_PARAMETER Size was NULL.
932   @retval EFI_BUFFER_TOO_SMALL  Size was not enough space to store the line.
933                                 Size was updated to minimum space required.
934   @sa FileHandleRead
935 **/
936 EFI_STATUS
937 EFIAPI
FileHandleReadLine(IN EFI_FILE_HANDLE Handle,IN OUT CHAR16 * Buffer,IN OUT UINTN * Size,IN BOOLEAN Truncate,IN OUT BOOLEAN * Ascii)938 FileHandleReadLine(
939   IN EFI_FILE_HANDLE            Handle,
940   IN OUT CHAR16                 *Buffer,
941   IN OUT UINTN                  *Size,
942   IN BOOLEAN                    Truncate,
943   IN OUT BOOLEAN                *Ascii
944   )
945 {
946   EFI_STATUS  Status;
947   CHAR16      CharBuffer;
948   UINTN       CharSize;
949   UINTN       CountSoFar;
950   UINT64      OriginalFilePosition;
951 
952 
953   if (Handle == NULL
954     ||Size   == NULL
955     ||(Buffer==NULL&&*Size!=0)
956    ){
957     return (EFI_INVALID_PARAMETER);
958   }
959   if (Buffer != NULL) {
960     *Buffer = CHAR_NULL;
961   }
962   FileHandleGetPosition(Handle, &OriginalFilePosition);
963   if (OriginalFilePosition == 0) {
964     CharSize = sizeof(CHAR16);
965     Status = FileHandleRead(Handle, &CharSize, &CharBuffer);
966     ASSERT_EFI_ERROR(Status);
967     if (CharBuffer == gUnicodeFileTag) {
968       *Ascii = FALSE;
969     } else {
970       *Ascii = TRUE;
971       FileHandleSetPosition(Handle, OriginalFilePosition);
972     }
973   }
974 
975   for (CountSoFar = 0;;CountSoFar++){
976     CharBuffer = 0;
977     if (*Ascii) {
978       CharSize = sizeof(CHAR8);
979     } else {
980       CharSize = sizeof(CHAR16);
981     }
982     Status = FileHandleRead(Handle, &CharSize, &CharBuffer);
983     if (  EFI_ERROR(Status)
984        || CharSize == 0
985        || (CharBuffer == L'\n' && !(*Ascii))
986        || (CharBuffer ==  '\n' && *Ascii)
987      ){
988       break;
989     }
990     //
991     // if we have space save it...
992     //
993     if ((CountSoFar+1)*sizeof(CHAR16) < *Size){
994       ASSERT(Buffer != NULL);
995       ((CHAR16*)Buffer)[CountSoFar] = CharBuffer;
996       ((CHAR16*)Buffer)[CountSoFar+1] = CHAR_NULL;
997     }
998   }
999 
1000   //
1001   // if we ran out of space tell when...
1002   //
1003   if ((CountSoFar+1)*sizeof(CHAR16) > *Size){
1004     *Size = (CountSoFar+1)*sizeof(CHAR16);
1005     if (!Truncate) {
1006       FileHandleSetPosition(Handle, OriginalFilePosition);
1007     } else {
1008       DEBUG((DEBUG_WARN, "The line was truncated in FileHandleReadLine"));
1009     }
1010     return (EFI_BUFFER_TOO_SMALL);
1011   }
1012   while(Buffer[StrLen(Buffer)-1] == L'\r') {
1013     Buffer[StrLen(Buffer)-1] = CHAR_NULL;
1014   }
1015 
1016   return (Status);
1017 }
1018 
1019 /**
1020   function to write a line of unicode text to a file.
1021 
1022   if Handle is NULL, return error.
1023   if Buffer is NULL, do nothing.  (return SUCCESS)
1024 
1025   @param[in]     Handle         FileHandle to write to
1026   @param[in]     Buffer         Buffer to write
1027 
1028   @retval  EFI_SUCCESS          the data was written.
1029   @retval  other                failure.
1030 
1031   @sa FileHandleWrite
1032 **/
1033 EFI_STATUS
1034 EFIAPI
FileHandleWriteLine(IN EFI_FILE_HANDLE Handle,IN CHAR16 * Buffer)1035 FileHandleWriteLine(
1036   IN EFI_FILE_HANDLE Handle,
1037   IN CHAR16          *Buffer
1038   )
1039 {
1040   EFI_STATUS Status;
1041   UINTN      Size;
1042 
1043   if (Buffer == NULL) {
1044     return (EFI_SUCCESS);
1045   }
1046 
1047   if (Handle == NULL) {
1048     return (EFI_INVALID_PARAMETER);
1049   }
1050 
1051   Size = StrSize(Buffer) - sizeof(Buffer[0]);
1052   Status = FileHandleWrite(Handle, &Size, Buffer);
1053   if (EFI_ERROR(Status)) {
1054     return (Status);
1055   }
1056   Size = StrSize(L"\r\n") - sizeof(CHAR16);
1057   return FileHandleWrite(Handle, &Size, L"\r\n");
1058 }
1059 
1060 /**
1061   function to take a formatted argument and print it to a file.
1062 
1063   @param[in] Handle   the file handle for the file to write to
1064   @param[in] Format   the format argument (see printlib for format specifier)
1065   @param[in] ...      the variable arguments for the format
1066 
1067   @retval EFI_SUCCESS the operation was sucessful
1068   @return other       a return value from FileHandleWriteLine
1069 
1070   @sa FileHandleWriteLine
1071 **/
1072 EFI_STATUS
1073 EFIAPI
FileHandlePrintLine(IN EFI_FILE_HANDLE Handle,IN CONST CHAR16 * Format,...)1074 FileHandlePrintLine(
1075   IN EFI_FILE_HANDLE  Handle,
1076   IN CONST CHAR16     *Format,
1077   ...
1078   )
1079 {
1080   VA_LIST           Marker;
1081   CHAR16            *Buffer;
1082   EFI_STATUS        Status;
1083 
1084   //
1085   // Get a buffer to print into
1086   //
1087   Buffer = AllocateZeroPool (PcdGet16 (PcdShellPrintBufferSize));
1088   if (Buffer == NULL) {
1089     return (EFI_OUT_OF_RESOURCES);
1090   }
1091 
1092   //
1093   // Print into our buffer
1094   //
1095   VA_START (Marker, Format);
1096   UnicodeVSPrint (Buffer, PcdGet16 (PcdShellPrintBufferSize), Format, Marker);
1097   VA_END (Marker);
1098 
1099   //
1100   // Print buffer into file
1101   //
1102   Status = FileHandleWriteLine(Handle, Buffer);
1103 
1104   //
1105   // Cleanup and return
1106   //
1107   FreePool(Buffer);
1108   return (Status);
1109 }
1110 
1111 /**
1112   Function to determine if a FILE_HANDLE is at the end of the file.
1113 
1114   This will NOT work on directories.
1115 
1116   If Handle is NULL, then return False.
1117 
1118   @param[in] Handle     the file handle
1119 
1120   @retval TRUE          the position is at the end of the file
1121   @retval FALSE         the position is not at the end of the file
1122 **/
1123 BOOLEAN
1124 EFIAPI
FileHandleEof(IN EFI_FILE_HANDLE Handle)1125 FileHandleEof(
1126   IN EFI_FILE_HANDLE Handle
1127   )
1128 {
1129   EFI_FILE_INFO *Info;
1130   UINT64        Pos;
1131   BOOLEAN       RetVal;
1132 
1133   if (Handle == NULL) {
1134     return (FALSE);
1135   }
1136 
1137   FileHandleGetPosition(Handle, &Pos);
1138   Info = FileHandleGetInfo (Handle);
1139 
1140   if (Info == NULL) {
1141     return (FALSE);
1142   }
1143 
1144   FileHandleSetPosition(Handle, Pos);
1145 
1146   if (Pos == Info->FileSize) {
1147     RetVal = TRUE;
1148   } else {
1149     RetVal = FALSE;
1150   }
1151 
1152   FreePool (Info);
1153 
1154   return (RetVal);
1155 }
1156