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