1/** @file
2  Provides interface to EFI_FILE_HANDLE functionality.
3
4  Copyright (c) 2006 - 2016, 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
30CONST 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**/
49EFI_FILE_INFO*
50EFIAPI
51FileHandleGetInfo (
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**/
114EFI_STATUS
115EFIAPI
116FileHandleSetInfo (
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**/
164EFI_STATUS
165EFIAPI
166FileHandleRead(
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**/
207EFI_STATUS
208EFIAPI
209FileHandleWrite(
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 successfully.
235**/
236EFI_STATUS
237EFIAPI
238FileHandleClose (
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 successfully
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**/
269EFI_STATUS
270EFIAPI
271FileHandleDelete (
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 beginning of file
301
302  @retval EFI_SUCCESS           Operation completed successfully.
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**/
307EFI_STATUS
308EFIAPI
309FileHandleSetPosition (
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 beginning of file.
334
335  @retval EFI_SUCCESS           the operation completed successfully.
336  @retval INVALID_PARAMETER     One of the parameters has an invalid value.
337  @retval EFI_UNSUPPORTED       the request is not valid on directories.
338**/
339EFI_STATUS
340EFIAPI
341FileHandleGetPosition (
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**/
369EFI_STATUS
370EFIAPI
371FileHandleFlush (
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**/
398EFI_STATUS
399EFIAPI
400FileHandleIsDirectory (
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**/
460EFI_STATUS
461EFIAPI
462FileHandleFindFirstFile (
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 beginning 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**/
535EFI_STATUS
536EFIAPI
537FileHandleFindNextFile(
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 successfully.
585  @retval EFI_DEVICE_ERROR      Cannot access the file.
586  @retval EFI_INVALID_PARAMETER FileHandle is NULL.
587                                Size is NULL.
588**/
589EFI_STATUS
590EFIAPI
591FileHandleGetSize (
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**/
636EFI_STATUS
637EFIAPI
638FileHandleSetSize (
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**/
704CHAR16*
705EFIAPI
706StrnCatGrowLeft (
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**/
787EFI_STATUS
788EFIAPI
789FileHandleGetFileName (
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 precede 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**/
894CHAR16*
895EFIAPI
896FileHandleReturnLine(
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**/
949EFI_STATUS
950EFIAPI
951FileHandleReadLine(
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**/
1072EFI_STATUS
1073EFIAPI
1074FileHandleWriteLine(
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    UnicodeStrToAsciiStrS (Buffer, AsciiBuffer, Size);
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 successful
1185  @return other       a return value from FileHandleWriteLine
1186
1187  @sa FileHandleWriteLine
1188**/
1189EFI_STATUS
1190EFIAPI
1191FileHandlePrintLine(
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**/
1240BOOLEAN
1241EFIAPI
1242FileHandleEof(
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