1 /** @file
2   Provides interface to EFI_FILE_HANDLE functionality.
3 
4   Copyright (c) 2006 - 2015, 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   Open the file information on the DirHandle and verify that the Attribute
389   includes EFI_FILE_DIRECTORY bit set.
390 
391   @param[in] DirHandle          Handle to open file.
392 
393   @retval EFI_SUCCESS           DirHandle is a directory.
394   @retval EFI_INVALID_PARAMETER DirHandle is NULL.
395                                 The file information returns from FileHandleGetInfo is NULL.
396   @retval EFI_NOT_FOUND         DirHandle is not a directory.
397 **/
398 EFI_STATUS
399 EFIAPI
FileHandleIsDirectory(IN EFI_FILE_HANDLE DirHandle)400 FileHandleIsDirectory (
401   IN EFI_FILE_HANDLE            DirHandle
402   )
403 {
404   EFI_FILE_INFO *DirInfo;
405 
406   if (DirHandle == NULL) {
407     return (EFI_INVALID_PARAMETER);
408   }
409 
410   //
411   // get the file information for DirHandle
412   //
413   DirInfo = FileHandleGetInfo (DirHandle);
414 
415   //
416   // Parse DirInfo
417   //
418   if (DirInfo == NULL) {
419     //
420     // We got nothing...
421     //
422     return (EFI_INVALID_PARAMETER);
423   }
424   if ((DirInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
425     //
426     // Attributes say this is not a directory
427     //
428     FreePool (DirInfo);
429     return (EFI_NOT_FOUND);
430   }
431   //
432   // all good...
433   //
434   FreePool (DirInfo);
435   return (EFI_SUCCESS);
436 }
437 
438 /** Retrieve first entry from a directory.
439 
440   This function takes an open directory handle and gets information from the
441   first entry in the directory.  A buffer is allocated to contain
442   the information and a pointer to the buffer is returned in *Buffer.  The
443   caller can use FileHandleFindNextFile() to get subsequent directory entries.
444 
445   The buffer will be freed by FileHandleFindNextFile() when the last directory
446   entry is read.  Otherwise, the caller must free the buffer, using FreePool,
447   when finished with it.
448 
449   @param[in]  DirHandle         The file handle of the directory to search.
450   @param[out] Buffer            The pointer to pointer to buffer for file's information.
451 
452   @retval EFI_SUCCESS           Found the first file.
453   @retval EFI_NOT_FOUND         Cannot find the directory.
454   @retval EFI_NO_MEDIA          The device has no media.
455   @retval EFI_DEVICE_ERROR      The device reported an error.
456   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
457   @return Others                status of FileHandleGetInfo, FileHandleSetPosition,
458                                 or FileHandleRead
459 **/
460 EFI_STATUS
461 EFIAPI
FileHandleFindFirstFile(IN EFI_FILE_HANDLE DirHandle,OUT EFI_FILE_INFO ** Buffer)462 FileHandleFindFirstFile (
463   IN EFI_FILE_HANDLE            DirHandle,
464   OUT EFI_FILE_INFO             **Buffer
465   )
466 {
467   EFI_STATUS    Status;
468   UINTN         BufferSize;
469 
470   if (Buffer == NULL || DirHandle == NULL) {
471     return (EFI_INVALID_PARAMETER);
472   }
473 
474   //
475   // verify that DirHandle is a directory
476   //
477   Status = FileHandleIsDirectory(DirHandle);
478   if (EFI_ERROR(Status)) {
479     return (Status);
480   }
481 
482   //
483   // Allocate a buffer sized to struct size + enough for the string at the end
484   //
485   BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE;
486   *Buffer = AllocateZeroPool(BufferSize);
487   if (*Buffer == NULL){
488     return (EFI_OUT_OF_RESOURCES);
489   }
490 
491   //
492   // reset to the begining of the directory
493   //
494   Status = FileHandleSetPosition(DirHandle, 0);
495   if (EFI_ERROR(Status)) {
496     FreePool(*Buffer);
497     *Buffer = NULL;
498     return (Status);
499   }
500 
501   //
502   // read in the info about the first file
503   //
504   Status = FileHandleRead (DirHandle, &BufferSize, *Buffer);
505   ASSERT(Status != EFI_BUFFER_TOO_SMALL);
506   if (EFI_ERROR(Status) || BufferSize == 0) {
507     FreePool(*Buffer);
508     *Buffer = NULL;
509     if (BufferSize == 0) {
510       return (EFI_NOT_FOUND);
511     }
512     return (Status);
513   }
514   return (EFI_SUCCESS);
515 }
516 
517 /** Retrieve next entries from a directory.
518 
519   To use this function, the caller must first call the FileHandleFindFirstFile()
520   function to get the first directory entry.  Subsequent directory entries are
521   retrieved by using the FileHandleFindNextFile() function.  This function can
522   be called several times to get each entry from the directory.  If the call of
523   FileHandleFindNextFile() retrieved the last directory entry, the next call of
524   this function will set *NoFile to TRUE and free the buffer.
525 
526   @param[in]  DirHandle         The file handle of the directory.
527   @param[out] Buffer            The pointer to buffer for file's information.
528   @param[out] NoFile            The pointer to boolean when last file is found.
529 
530   @retval EFI_SUCCESS           Found the next file, or reached last file
531   @retval EFI_NO_MEDIA          The device has no media.
532   @retval EFI_DEVICE_ERROR      The device reported an error.
533   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
534 **/
535 EFI_STATUS
536 EFIAPI
FileHandleFindNextFile(IN EFI_FILE_HANDLE DirHandle,OUT EFI_FILE_INFO * Buffer,OUT BOOLEAN * NoFile)537 FileHandleFindNextFile(
538   IN EFI_FILE_HANDLE          DirHandle,
539   OUT EFI_FILE_INFO          *Buffer,
540   OUT BOOLEAN                *NoFile
541   )
542 {
543   EFI_STATUS    Status;
544   UINTN         BufferSize;
545 
546   if (DirHandle == NULL || Buffer == NULL || NoFile == NULL) {
547     return (EFI_INVALID_PARAMETER);
548   }
549 
550   //
551   // This BufferSize MUST stay equal to the originally allocated one in GetFirstFile
552   //
553   BufferSize = FIND_XXXXX_FILE_BUFFER_SIZE;
554 
555   //
556   // read in the info about the next file
557   //
558   Status = FileHandleRead (DirHandle, &BufferSize, Buffer);
559   ASSERT(Status != EFI_BUFFER_TOO_SMALL);
560   if (EFI_ERROR(Status)) {
561     return (Status);
562   }
563 
564   //
565   // If we read 0 bytes (but did not have erros) we already read in the last file.
566   //
567   if (BufferSize == 0) {
568     FreePool(Buffer);
569     *NoFile = TRUE;
570   }
571 
572   return (EFI_SUCCESS);
573 }
574 
575 /**
576   Retrieve the size of a file.
577 
578   This function extracts the file size info from the FileHandle's EFI_FILE_INFO
579   data.
580 
581   @param[in] FileHandle         The file handle from which size is retrieved.
582   @param[out] Size              The pointer to size.
583 
584   @retval EFI_SUCCESS           Operation was completed sucessfully.
585   @retval EFI_DEVICE_ERROR      Cannot access the file.
586   @retval EFI_INVALID_PARAMETER FileHandle is NULL.
587                                 Size is NULL.
588 **/
589 EFI_STATUS
590 EFIAPI
FileHandleGetSize(IN EFI_FILE_HANDLE FileHandle,OUT UINT64 * Size)591 FileHandleGetSize (
592   IN EFI_FILE_HANDLE            FileHandle,
593   OUT UINT64                    *Size
594   )
595 {
596   EFI_FILE_INFO                 *FileInfo;
597 
598   if (FileHandle == NULL || Size == NULL) {
599     return (EFI_INVALID_PARAMETER);
600   }
601 
602   //
603   // get the FileInfo structure
604   //
605   FileInfo = FileHandleGetInfo(FileHandle);
606   if (FileInfo == NULL) {
607     return (EFI_DEVICE_ERROR);
608   }
609 
610   //
611   // Assign the Size pointer to the correct value
612   //
613   *Size = FileInfo->FileSize;
614 
615   //
616   // free the FileInfo memory
617   //
618   FreePool(FileInfo);
619 
620   return (EFI_SUCCESS);
621 }
622 
623 /**
624   Set the size of a file.
625 
626   This function changes the file size info from the FileHandle's EFI_FILE_INFO
627   data.
628 
629   @param[in] FileHandle         The file handle whose size is to be changed.
630   @param[in] Size               The new size.
631 
632   @retval EFI_SUCCESS           The operation completed successfully.
633   @retval EFI_DEVICE_ERROR      Cannot access the file.
634   @retval EFI_INVALID_PARAMETER FileHandle is NULL.
635 **/
636 EFI_STATUS
637 EFIAPI
FileHandleSetSize(IN EFI_FILE_HANDLE FileHandle,IN UINT64 Size)638 FileHandleSetSize (
639   IN EFI_FILE_HANDLE            FileHandle,
640   IN UINT64                     Size
641   )
642 {
643   EFI_FILE_INFO                 *FileInfo;
644   EFI_STATUS                    Status;
645 
646   if (FileHandle == NULL) {
647     return (EFI_INVALID_PARAMETER);
648   }
649 
650   //
651   // get the FileInfo structure
652   //
653   FileInfo = FileHandleGetInfo(FileHandle);
654   if (FileInfo == NULL) {
655     return (EFI_DEVICE_ERROR);
656   }
657 
658   //
659   // Assign the FileSize pointer to the new value
660   //
661   FileInfo->FileSize = Size;
662 
663   Status = FileHandleSetInfo(FileHandle, FileInfo);
664   //
665   // free the FileInfo memory
666   //
667   FreePool(FileInfo);
668 
669   return (Status);
670 }
671 
672 /**
673   Safely append (on the left) with automatic string resizing given length of Destination and
674   desired length of copy from Source.
675 
676   append the first D characters of Source to the end of Destination, where D is
677   the lesser of Count and the StrLen() of Source. If appending those D characters
678   will fit within Destination (whose Size is given as CurrentSize) and
679   still leave room for a NULL terminator, then those characters are appended,
680   starting at the original terminating NULL of Destination, and a new terminating
681   NULL is appended.
682 
683   If appending D characters onto Destination will result in a overflow of the size
684   given in CurrentSize the string will be grown such that the copy can be performed
685   and CurrentSize will be updated to the new size.
686 
687   If Source is NULL, there is nothing to append, just return the current buffer in
688   Destination.
689 
690   if Destination is NULL, then return error
691   if Destination's current length (including NULL terminator) is already more then
692   CurrentSize, then ASSERT()
693 
694   @param[in, out] Destination   The String to append onto
695   @param[in, out] CurrentSize   on call the number of bytes in Destination.  On
696                                 return possibly the new size (still in bytes).  if NULL
697                                 then allocate whatever is needed.
698   @param[in]      Source        The String to append from
699   @param[in]      Count         Maximum number of characters to append.  if 0 then
700                                 all are appended.
701 
702   @return Destination           return the resultant string.
703 **/
704 CHAR16*
705 EFIAPI
StrnCatGrowLeft(IN OUT CHAR16 ** Destination,IN OUT UINTN * CurrentSize,IN CONST CHAR16 * Source,IN UINTN Count)706 StrnCatGrowLeft (
707   IN OUT CHAR16           **Destination,
708   IN OUT UINTN            *CurrentSize,
709   IN     CONST CHAR16     *Source,
710   IN     UINTN            Count
711   )
712 {
713   UINTN DestinationStartSize;
714   UINTN NewSize;
715   UINTN CopySize;
716 
717   if (Destination == NULL) {
718     return (NULL);
719   }
720 
721   //
722   // If there's nothing to do then just return Destination
723   //
724   if (Source == NULL) {
725     return (*Destination);
726   }
727 
728   //
729   // allow for NULL pointers address as Destination
730   //
731   if (*Destination != NULL) {
732     ASSERT(CurrentSize != 0);
733     DestinationStartSize = StrSize(*Destination);
734     ASSERT(DestinationStartSize <= *CurrentSize);
735   } else {
736     DestinationStartSize = 0;
737 //    ASSERT(*CurrentSize == 0);
738   }
739 
740   //
741   // Append all of Source?
742   //
743   if (Count == 0) {
744     Count = StrSize(Source);
745   }
746 
747   //
748   // Test and grow if required
749   //
750   if (CurrentSize != NULL) {
751     NewSize = *CurrentSize;
752     while (NewSize < (DestinationStartSize + Count)) {
753       NewSize += 2 * Count;
754     }
755     *Destination = ReallocatePool(*CurrentSize, NewSize, *Destination);
756     *CurrentSize = NewSize;
757   } else {
758     *Destination = AllocateZeroPool(Count+sizeof(CHAR16));
759   }
760   if (*Destination == NULL) {
761     return NULL;
762   }
763 
764   CopySize = StrSize(*Destination);
765   CopyMem((*Destination)+((Count-2)/sizeof(CHAR16)), *Destination, CopySize);
766   CopyMem(*Destination, Source, Count-2);
767   return (*Destination);
768 }
769 
770 /**
771   Function to get a full filename given a EFI_FILE_HANDLE somewhere lower on the
772   directory 'stack'. If the file is a directory, then append the '\' char at the
773   end of name string. If it's not a directory, then the last '\' should not be
774   added.
775 
776   if Handle is NULL, return EFI_INVALID_PARAMETER
777 
778   @param[in] Handle             Handle to the Directory or File to create path to.
779   @param[out] FullFileName      pointer to pointer to generated full file name.  It
780                                 is the responsibility of the caller to free this memory
781                                 with a call to FreePool().
782   @retval EFI_SUCCESS           the operation was sucessful and the FullFileName is valid.
783   @retval EFI_INVALID_PARAMETER Handle was NULL.
784   @retval EFI_INVALID_PARAMETER FullFileName was NULL.
785   @retval EFI_OUT_OF_RESOURCES  a memory allocation failed.
786 **/
787 EFI_STATUS
788 EFIAPI
FileHandleGetFileName(IN CONST EFI_FILE_HANDLE Handle,OUT CHAR16 ** FullFileName)789 FileHandleGetFileName (
790   IN CONST EFI_FILE_HANDLE      Handle,
791   OUT CHAR16                    **FullFileName
792   )
793 {
794   EFI_STATUS      Status;
795   UINTN           Size;
796   EFI_FILE_HANDLE CurrentHandle;
797   EFI_FILE_HANDLE NextHigherHandle;
798   EFI_FILE_INFO   *FileInfo;
799 
800   Size = 0;
801 
802   //
803   // Check our parameters
804   //
805   if (FullFileName == NULL || Handle == NULL) {
806     return (EFI_INVALID_PARAMETER);
807   }
808 
809   *FullFileName = NULL;
810   CurrentHandle = NULL;
811 
812   Status = Handle->Open(Handle, &CurrentHandle, L".", EFI_FILE_MODE_READ, 0);
813   if (!EFI_ERROR(Status)) {
814     //
815     // Reverse out the current directory on the device
816     //
817     for (;;) {
818       FileInfo = FileHandleGetInfo(CurrentHandle);
819       if (FileInfo == NULL) {
820         Status = EFI_OUT_OF_RESOURCES;
821         break;
822       } else {
823         //
824         // We got info... do we have a name? if yes preceed the current path with it...
825         //
826         if (StrLen (FileInfo->FileName) == 0) {
827           if (*FullFileName == NULL) {
828             ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
829             *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
830           }
831           FreePool(FileInfo);
832           break;
833         } else {
834           if (*FullFileName == NULL) {
835             ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
836             *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
837           }
838           ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
839           *FullFileName = StrnCatGrowLeft(FullFileName, &Size, FileInfo->FileName, 0);
840           *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
841           FreePool(FileInfo);
842         }
843       }
844       //
845       // Move to the parent directory
846       //
847       Status = CurrentHandle->Open (CurrentHandle, &NextHigherHandle, L"..", EFI_FILE_MODE_READ, 0);
848       if (EFI_ERROR (Status)) {
849         break;
850       }
851 
852       FileHandleClose(CurrentHandle);
853       CurrentHandle = NextHigherHandle;
854     }
855   } else if (Status == EFI_NOT_FOUND) {
856     Status = EFI_SUCCESS;
857     ASSERT((*FullFileName == NULL && Size == 0) || (*FullFileName != NULL));
858     *FullFileName = StrnCatGrowLeft(FullFileName, &Size, L"\\", 0);
859   }
860 
861   if (*FullFileName != NULL &&
862       (*FullFileName)[StrLen(*FullFileName) - 1] == L'\\' &&
863       StrLen(*FullFileName) > 1 &&
864       FileHandleIsDirectory(Handle) == EFI_NOT_FOUND
865      ) {
866     (*FullFileName)[StrLen(*FullFileName) - 1] = CHAR_NULL;
867   }
868 
869   if (CurrentHandle != NULL) {
870     CurrentHandle->Close (CurrentHandle);
871   }
872 
873   if (EFI_ERROR(Status) && *FullFileName != NULL) {
874     FreePool(*FullFileName);
875   }
876 
877   return (Status);
878 }
879 
880 /**
881   Function to read a single line from a file. The \n is not included in the returned
882   buffer.  The returned buffer must be callee freed.
883 
884   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
885   maintained and not changed for all operations with the same file.
886 
887   @param[in]       Handle        FileHandle to read from.
888   @param[in, out]  Ascii         Boolean value for indicating whether the file is Ascii (TRUE) or UCS2 (FALSE);
889 
890   @return                       The line of text from the file.
891 
892   @sa FileHandleReadLine
893 **/
894 CHAR16*
895 EFIAPI
FileHandleReturnLine(IN EFI_FILE_HANDLE Handle,IN OUT BOOLEAN * Ascii)896 FileHandleReturnLine(
897   IN EFI_FILE_HANDLE            Handle,
898   IN OUT BOOLEAN                *Ascii
899   )
900 {
901   CHAR16          *RetVal;
902   UINTN           Size;
903   EFI_STATUS      Status;
904 
905   Size = 0;
906   RetVal = NULL;
907 
908   Status = FileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
909   if (Status == EFI_BUFFER_TOO_SMALL) {
910     RetVal = AllocateZeroPool(Size);
911     Status = FileHandleReadLine(Handle, RetVal, &Size, FALSE, Ascii);
912   }
913   ASSERT_EFI_ERROR(Status);
914   if (EFI_ERROR(Status) && (RetVal != NULL)) {
915     FreePool(RetVal);
916     RetVal = NULL;
917   }
918   return (RetVal);
919 }
920 
921 /**
922   Function to read a single line (up to but not including the \n) from a file.
923 
924   If the position upon start is 0, then the Ascii Boolean will be set.  This should be
925   maintained and not changed for all operations with the same file.
926   The function will not return the \r and \n character in buffer. When an empty line is
927   read a CHAR_NULL character will be returned in buffer.
928 
929   @param[in]       Handle        FileHandle to read from.
930   @param[in, out]  Buffer        The pointer to buffer to read into.
931   @param[in, out]  Size          The pointer to number of bytes in Buffer.
932   @param[in]       Truncate      If the buffer is large enough, this has no effect.
933                                  If the buffer is is too small and Truncate is TRUE,
934                                  the line will be truncated.
935                                  If the buffer is is too small and Truncate is FALSE,
936                                  then no read will occur.
937 
938   @param[in, out]  Ascii         Boolean value for indicating whether the file is
939                                  Ascii (TRUE) or UCS2 (FALSE).
940 
941   @retval EFI_SUCCESS           The operation was successful.  The line is stored in
942                                 Buffer.
943   @retval EFI_INVALID_PARAMETER Handle was NULL.
944   @retval EFI_INVALID_PARAMETER Size was NULL.
945   @retval EFI_BUFFER_TOO_SMALL  Size was not large enough to store the line.
946                                 Size was updated to the minimum space required.
947   @sa FileHandleRead
948 **/
949 EFI_STATUS
950 EFIAPI
FileHandleReadLine(IN EFI_FILE_HANDLE Handle,IN OUT CHAR16 * Buffer,IN OUT UINTN * Size,IN BOOLEAN Truncate,IN OUT BOOLEAN * Ascii)951 FileHandleReadLine(
952   IN EFI_FILE_HANDLE            Handle,
953   IN OUT CHAR16                 *Buffer,
954   IN OUT UINTN                  *Size,
955   IN BOOLEAN                    Truncate,
956   IN OUT BOOLEAN                *Ascii
957   )
958 {
959   EFI_STATUS  Status;
960   CHAR16      CharBuffer;
961   UINT64      FileSize;
962   UINTN       CharSize;
963   UINTN       CountSoFar;
964   UINTN       CrCount;
965   UINT64      OriginalFilePosition;
966 
967   if (Handle == NULL
968     ||Size   == NULL
969     ||(Buffer==NULL&&*Size!=0)
970    ){
971     return (EFI_INVALID_PARAMETER);
972   }
973 
974   if (Buffer != NULL && *Size != 0) {
975     *Buffer = CHAR_NULL;
976   }
977 
978   Status = FileHandleGetSize (Handle, &FileSize);
979   if (EFI_ERROR (Status)) {
980     return Status;
981   } else if (FileSize == 0) {
982     *Ascii = TRUE;
983     return EFI_SUCCESS;
984   }
985 
986   FileHandleGetPosition(Handle, &OriginalFilePosition);
987   if (OriginalFilePosition == 0) {
988     CharSize = sizeof(CHAR16);
989     Status = FileHandleRead(Handle, &CharSize, &CharBuffer);
990     ASSERT_EFI_ERROR(Status);
991     if (CharBuffer == gUnicodeFileTag) {
992       *Ascii = FALSE;
993     } else {
994       *Ascii = TRUE;
995       FileHandleSetPosition(Handle, OriginalFilePosition);
996     }
997   }
998 
999   CrCount = 0;
1000   for (CountSoFar = 0;;CountSoFar++){
1001     CharBuffer = 0;
1002     if (*Ascii) {
1003       CharSize = sizeof(CHAR8);
1004     } else {
1005       CharSize = sizeof(CHAR16);
1006     }
1007     Status = FileHandleRead(Handle, &CharSize, &CharBuffer);
1008     if (  EFI_ERROR(Status)
1009        || CharSize == 0
1010        || (CharBuffer == L'\n' && !(*Ascii))
1011        || (CharBuffer ==  '\n' && *Ascii)
1012      ){
1013       break;
1014     } else if (
1015         (CharBuffer == L'\r' && !(*Ascii)) ||
1016         (CharBuffer ==  '\r' && *Ascii)
1017       ) {
1018       CrCount++;
1019       continue;
1020     }
1021     //
1022     // if we have space save it...
1023     //
1024     if ((CountSoFar+1-CrCount)*sizeof(CHAR16) < *Size){
1025       ASSERT(Buffer != NULL);
1026       ((CHAR16*)Buffer)[CountSoFar-CrCount] = CharBuffer;
1027       ((CHAR16*)Buffer)[CountSoFar+1-CrCount] = CHAR_NULL;
1028     }
1029   }
1030 
1031   //
1032   // if we ran out of space tell when...
1033   //
1034   if ((CountSoFar+1-CrCount)*sizeof(CHAR16) > *Size){
1035     *Size = (CountSoFar+1-CrCount)*sizeof(CHAR16);
1036     if (!Truncate) {
1037       if (Buffer != NULL && *Size != 0) {
1038         ZeroMem(Buffer, *Size);
1039       }
1040       FileHandleSetPosition(Handle, OriginalFilePosition);
1041       return (EFI_BUFFER_TOO_SMALL);
1042     } else {
1043       DEBUG((DEBUG_WARN, "The line was truncated in FileHandleReadLine"));
1044       return (EFI_SUCCESS);
1045     }
1046   }
1047 
1048   return (Status);
1049 }
1050 
1051 /**
1052   Function to write a line of text to a file.
1053 
1054   If the file is a Unicode file (with UNICODE file tag) then write the unicode
1055   text.
1056   If the file is an ASCII file then write the ASCII text.
1057   If the size of file is zero (without file tag at the beginning) then write
1058   ASCII text as default.
1059 
1060   @param[in]     Handle         FileHandle to write to.
1061   @param[in]     Buffer         Buffer to write, if NULL the function will
1062                                 take no action and return EFI_SUCCESS.
1063 
1064   @retval  EFI_SUCCESS            The data was written.
1065                                   Buffer is NULL.
1066   @retval  EFI_INVALID_PARAMETER  Handle is NULL.
1067   @retval  EFI_OUT_OF_RESOURCES   Unable to allocate temporary space for ASCII
1068                                   string due to out of resources.
1069 
1070   @sa FileHandleWrite
1071 **/
1072 EFI_STATUS
1073 EFIAPI
FileHandleWriteLine(IN EFI_FILE_HANDLE Handle,IN CHAR16 * Buffer)1074 FileHandleWriteLine(
1075   IN EFI_FILE_HANDLE Handle,
1076   IN CHAR16          *Buffer
1077   )
1078 {
1079   EFI_STATUS  Status;
1080   CHAR16      CharBuffer;
1081   UINTN       Size;
1082   UINTN       Index;
1083   UINTN       CharSize;
1084   UINT64      FileSize;
1085   UINT64      OriginalFilePosition;
1086   BOOLEAN     Ascii;
1087   CHAR8       *AsciiBuffer;
1088 
1089   if (Buffer == NULL) {
1090     return (EFI_SUCCESS);
1091   }
1092 
1093   if (Handle == NULL) {
1094     return (EFI_INVALID_PARAMETER);
1095   }
1096 
1097   Ascii = FALSE;
1098   AsciiBuffer = NULL;
1099 
1100   Status = FileHandleGetPosition(Handle, &OriginalFilePosition);
1101   if (EFI_ERROR(Status)) {
1102     return Status;
1103   }
1104 
1105   Status = FileHandleSetPosition(Handle, 0);
1106   if (EFI_ERROR(Status)) {
1107     return Status;
1108   }
1109 
1110   Status = FileHandleGetSize(Handle, &FileSize);
1111   if (EFI_ERROR(Status)) {
1112     return Status;
1113   }
1114 
1115   if (FileSize == 0) {
1116     Ascii = TRUE;
1117   } else {
1118     CharSize = sizeof (CHAR16);
1119     Status = FileHandleRead (Handle, &CharSize, &CharBuffer);
1120     ASSERT_EFI_ERROR (Status);
1121     if (CharBuffer == gUnicodeFileTag) {
1122       Ascii = FALSE;
1123     } else {
1124       Ascii = TRUE;
1125     }
1126   }
1127 
1128   Status = FileHandleSetPosition(Handle, OriginalFilePosition);
1129   if (EFI_ERROR(Status)) {
1130     return Status;
1131   }
1132 
1133   if (Ascii) {
1134     Size = ( StrSize(Buffer) / sizeof(CHAR16) ) * sizeof(CHAR8);
1135     AsciiBuffer = (CHAR8 *)AllocateZeroPool(Size);
1136     if (AsciiBuffer == NULL) {
1137       return EFI_OUT_OF_RESOURCES;
1138     }
1139     UnicodeStrToAsciiStr (Buffer, AsciiBuffer);
1140     for (Index = 0; Index < Size; Index++) {
1141       if (!((AsciiBuffer[Index] >= 0) && (AsciiBuffer[Index] < 128))){
1142         FreePool(AsciiBuffer);
1143         return EFI_INVALID_PARAMETER;
1144       }
1145     }
1146 
1147     Size = AsciiStrSize(AsciiBuffer) - sizeof(CHAR8);
1148     Status = FileHandleWrite(Handle, &Size, AsciiBuffer);
1149     if (EFI_ERROR(Status)) {
1150       FreePool (AsciiBuffer);
1151       return (Status);
1152     }
1153     Size = AsciiStrSize("\r\n") - sizeof(CHAR8);
1154     Status = FileHandleWrite(Handle, &Size, "\r\n");
1155   } else {
1156     if (OriginalFilePosition == 0) {
1157       Status = FileHandleSetPosition (Handle, sizeof(CHAR16));
1158       if (EFI_ERROR(Status)) {
1159         return Status;
1160       }
1161     }
1162     Size = StrSize(Buffer) - sizeof(CHAR16);
1163     Status = FileHandleWrite(Handle, &Size, Buffer);
1164     if (EFI_ERROR(Status)) {
1165       return (Status);
1166     }
1167     Size = StrSize(L"\r\n") - sizeof(CHAR16);
1168     Status = FileHandleWrite(Handle, &Size, L"\r\n");
1169   }
1170 
1171   if (AsciiBuffer != NULL) {
1172     FreePool (AsciiBuffer);
1173   }
1174   return Status;
1175 }
1176 
1177 /**
1178   function to take a formatted argument and print it to a file.
1179 
1180   @param[in] Handle   the file handle for the file to write to
1181   @param[in] Format   the format argument (see printlib for format specifier)
1182   @param[in] ...      the variable arguments for the format
1183 
1184   @retval EFI_SUCCESS the operation was sucessful
1185   @return other       a return value from FileHandleWriteLine
1186 
1187   @sa FileHandleWriteLine
1188 **/
1189 EFI_STATUS
1190 EFIAPI
FileHandlePrintLine(IN EFI_FILE_HANDLE Handle,IN CONST CHAR16 * Format,...)1191 FileHandlePrintLine(
1192   IN EFI_FILE_HANDLE  Handle,
1193   IN CONST CHAR16     *Format,
1194   ...
1195   )
1196 {
1197   VA_LIST           Marker;
1198   CHAR16            *Buffer;
1199   EFI_STATUS        Status;
1200 
1201   //
1202   // Get a buffer to print into
1203   //
1204   Buffer = AllocateZeroPool (PcdGet16 (PcdUefiFileHandleLibPrintBufferSize));
1205   if (Buffer == NULL) {
1206     return (EFI_OUT_OF_RESOURCES);
1207   }
1208 
1209   //
1210   // Print into our buffer
1211   //
1212   VA_START (Marker, Format);
1213   UnicodeVSPrint (Buffer, PcdGet16 (PcdUefiFileHandleLibPrintBufferSize), Format, Marker);
1214   VA_END (Marker);
1215 
1216   //
1217   // Print buffer into file
1218   //
1219   Status = FileHandleWriteLine(Handle, Buffer);
1220 
1221   //
1222   // Cleanup and return
1223   //
1224   FreePool(Buffer);
1225   return (Status);
1226 }
1227 
1228 /**
1229   Function to determine if a FILE_HANDLE is at the end of the file.
1230 
1231   This will NOT work on directories.
1232 
1233   If Handle is NULL, then return False.
1234 
1235   @param[in] Handle     the file handle
1236 
1237   @retval TRUE          the position is at the end of the file
1238   @retval FALSE         the position is not at the end of the file
1239 **/
1240 BOOLEAN
1241 EFIAPI
FileHandleEof(IN EFI_FILE_HANDLE Handle)1242 FileHandleEof(
1243   IN EFI_FILE_HANDLE Handle
1244   )
1245 {
1246   EFI_FILE_INFO *Info;
1247   UINT64        Pos;
1248   BOOLEAN       RetVal;
1249 
1250   if (Handle == NULL) {
1251     return (FALSE);
1252   }
1253 
1254   FileHandleGetPosition(Handle, &Pos);
1255   Info = FileHandleGetInfo (Handle);
1256 
1257   if (Info == NULL) {
1258     return (FALSE);
1259   }
1260 
1261   FileHandleSetPosition(Handle, Pos);
1262 
1263   if (Pos == Info->FileSize) {
1264     RetVal = TRUE;
1265   } else {
1266     RetVal = FALSE;
1267   }
1268 
1269   FreePool (Info);
1270 
1271   return (RetVal);
1272 }
1273