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