1/** @file
2  This driver uses the EFI_FIRMWARE_VOLUME2_PROTOCOL to expose files in firmware
3  volumes via the the EFI_SIMPLE_FILESYSTEM_PROTOCOL and EFI_FILE_PROTOCOL.
4
5  It will expose a single directory, containing one file for each file in the firmware
6  volume. If a file has a UI section, its contents will be used as a filename.
7  Otherwise, a string representation of the GUID will be used.
8  Files of an executable type (That is PEIM, DRIVER, COMBINED_PEIM_DRIVER and APPLICATION)
9  will have ".efi" added to their filename.
10
11  Its primary intended use is to be able to start EFI applications embedded in FVs
12  from the UEFI shell. It is entirely read-only.
13
14Copyright (c) 2014, ARM Limited. All rights reserved.
15Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
16
17This program and the accompanying materials
18are licensed and made available under the terms and conditions of the BSD License
19which accompanies this distribution.  The full text of the license may be found at
20http://opensource.org/licenses/bsd-license.php
21
22THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
23WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
24
25**/
26
27#include "FvSimpleFileSystemInternal.h"
28
29//
30// Template for EFI_FILE_SYSTEM_INFO data structure.
31//
32EFI_FILE_SYSTEM_INFO mFsInfoTemplate = {
33  0,    // Populate at runtime
34  TRUE, // Read-only
35  0,    // Don't know volume size
36  0,    // No free space
37  0,    // Don't know block size
38  L""   // Populate at runtime
39};
40
41//
42// Template for EFI_FILE_PROTOCOL data structure.
43//
44EFI_FILE_PROTOCOL mFileSystemTemplate = {
45  EFI_FILE_PROTOCOL_REVISION,
46  FvSimpleFileSystemOpen,
47  FvSimpleFileSystemClose,
48  FvSimpleFileSystemDelete,
49  FvSimpleFileSystemRead,
50  FvSimpleFileSystemWrite,
51  FvSimpleFileSystemGetPosition,
52  FvSimpleFileSystemSetPosition,
53  FvSimpleFileSystemGetInfo,
54  FvSimpleFileSystemSetInfo,
55  FvSimpleFileSystemFlush
56};
57
58/**
59  Find and call ReadSection on the first section found of an executable type.
60
61  @param  FvProtocol                  A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance.
62  @param  FvFileInfo                  A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct
63                                      representing a file's info.
64  @param  BufferSize                  Pointer to a caller-allocated UINTN. It indicates the size of
65                                      the memory represented by *Buffer.
66  @param  Buffer                      Pointer to a pointer to a data buffer to contain file content.
67
68  @retval EFI_SUCCESS                 The call completed successfully.
69  @retval EFI_WARN_BUFFER_TOO_SMALL   The buffer is too small to contain the requested output.
70  @retval EFI_ACCESS_DENIED           The firmware volume is configured to disallow reads.
71  @retval EFI_NOT_FOUND               The requested file was not found in the firmware volume.
72  @retval EFI_DEVICE_ERROR            A hardware error occurred when attempting toaccess the firmware volume.
73
74**/
75EFI_STATUS
76FvFsFindExecutableSection (
77  IN     EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol,
78  IN     FV_FILESYSTEM_FILE_INFO           *FvFileInfo,
79  IN OUT UINTN                             *BufferSize,
80  IN OUT VOID                              **Buffer
81  )
82{
83  EFI_SECTION_TYPE                    SectionType;
84  UINT32                              AuthenticationStatus;
85  EFI_STATUS                          Status;
86
87  for (SectionType = EFI_SECTION_PE32; SectionType <= EFI_SECTION_TE; SectionType++) {
88    Status = FvProtocol->ReadSection (
89                           FvProtocol,
90                           &FvFileInfo->NameGuid,
91                           SectionType,
92                           0,
93                           Buffer,
94                           BufferSize,
95                           &AuthenticationStatus
96                           );
97    if (Status != EFI_NOT_FOUND) {
98      return Status;
99    }
100  }
101
102  return EFI_NOT_FOUND;
103}
104
105/**
106  Get the size of the buffer that will be returned by FvFsReadFile.
107
108  @param  FvProtocol                  A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance.
109  @param  FvFileInfo                  A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct
110                                      representing a file's info.
111
112  @retval EFI_SUCCESS                 The file size was gotten correctly.
113  @retval Others                      The file size wasn't gotten correctly.
114
115**/
116EFI_STATUS
117FvFsGetFileSize (
118  IN     EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol,
119  IN OUT FV_FILESYSTEM_FILE_INFO           *FvFileInfo
120  )
121{
122  UINT32                         AuthenticationStatus;
123  EFI_FV_FILETYPE                FoundType;
124  EFI_FV_FILE_ATTRIBUTES         Attributes;
125  EFI_STATUS                     Status;
126  UINT8                          IgnoredByte;
127  VOID                           *IgnoredPtr;
128
129  //
130  // To get the size of a section, we pass 0 for BufferSize. But we can't pass
131  // NULL for Buffer, as that will cause a return of INVALID_PARAMETER, and we
132  // can't pass NULL for *Buffer, as that will cause the callee to allocate
133  // a buffer of the sections size.
134  //
135  IgnoredPtr = &IgnoredByte;
136  FvFileInfo->FileInfo.FileSize = 0;
137
138  if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) {
139    //
140    // Get the size of the first executable section out of the file.
141    //
142    Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, (UINTN*)&FvFileInfo->FileInfo.FileSize, &IgnoredPtr);
143    if (Status == EFI_WARN_BUFFER_TOO_SMALL) {
144      return EFI_SUCCESS;
145    }
146  } else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) {
147    //
148    // Try to get the size of a raw section out of the file
149    //
150    Status = FvProtocol->ReadSection (
151                           FvProtocol,
152                           &FvFileInfo->NameGuid,
153                           EFI_SECTION_RAW,
154                           0,
155                           &IgnoredPtr,
156                           (UINTN*)&FvFileInfo->FileInfo.FileSize,
157                           &AuthenticationStatus
158                           );
159    if (Status == EFI_WARN_BUFFER_TOO_SMALL) {
160      return EFI_SUCCESS;
161    }
162    if (EFI_ERROR (Status)) {
163      //
164      // Didn't find a raw section, just return the whole file's size.
165      //
166      return FvProtocol->ReadFile (
167                           FvProtocol,
168                           &FvFileInfo->NameGuid,
169                           NULL,
170                           (UINTN*)&FvFileInfo->FileInfo.FileSize,
171                           &FoundType,
172                           &Attributes,
173                           &AuthenticationStatus
174                           );
175    }
176  } else {
177    //
178    // Get the size of the entire file
179    //
180    return FvProtocol->ReadFile (
181                         FvProtocol,
182                         &FvFileInfo->NameGuid,
183                         NULL,
184                         (UINTN*)&FvFileInfo->FileInfo.FileSize,
185                         &FoundType,
186                         &Attributes,
187                         &AuthenticationStatus
188                         );
189  }
190
191  return Status;
192}
193
194/**
195  Helper function to read a file.
196
197  The data returned depends on the type of the underlying FV file:
198  - For executable types, the first section found that contains executable code is returned.
199  - For files of type FREEFORM, the driver attempts to return the first section of type RAW.
200    If none is found, the entire contents of the FV file are returned.
201  - On all other files the entire contents of the FV file is returned, as by
202    EFI_FIRMWARE_VOLUME2_PROTOCOL.ReadFile.
203
204  @param  FvProtocol                  A pointer to the EFI_FIRMWARE_VOLUME2_PROTOCOL instance.
205  @param  FvFileInfo                  A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct
206                                      representing a file's info.
207  @param  BufferSize                  Pointer to a caller-allocated UINTN. It indicates the size of
208                                      the memory represented by *Buffer.
209  @param  Buffer                      Pointer to a pointer to a data buffer to contain file content.
210
211  @retval EFI_SUCCESS                 The call completed successfully.
212  @retval EFI_WARN_BUFFER_TOO_SMALL   The buffer is too small to contain the requested output.
213  @retval EFI_ACCESS_DENIED           The firmware volume is configured to disallow reads.
214  @retval EFI_NOT_FOUND               The requested file was not found in the firmware volume.
215  @retval EFI_DEVICE_ERROR            A hardware error occurred when attempting toaccess the firmware volume.
216
217**/
218EFI_STATUS
219FvFsReadFile (
220  IN     EFI_FIRMWARE_VOLUME2_PROTOCOL     *FvProtocol,
221  IN     FV_FILESYSTEM_FILE_INFO           *FvFileInfo,
222  IN OUT UINTN                             *BufferSize,
223  IN OUT VOID                              **Buffer
224  )
225{
226  UINT32                         AuthenticationStatus;
227  EFI_FV_FILETYPE                FoundType;
228  EFI_FV_FILE_ATTRIBUTES         Attributes;
229  EFI_STATUS                     Status;
230
231  if (FV_FILETYPE_IS_EXECUTABLE (FvFileInfo->Type)) {
232    //
233    // Read the first executable section out of the file.
234    //
235    Status = FvFsFindExecutableSection (FvProtocol, FvFileInfo, BufferSize, Buffer);
236  } else if (FvFileInfo->Type == EFI_FV_FILETYPE_FREEFORM) {
237    //
238    // Try to read a raw section out of the file
239    //
240    Status = FvProtocol->ReadSection (
241                           FvProtocol,
242                           &FvFileInfo->NameGuid,
243                           EFI_SECTION_RAW,
244                           0,
245                           Buffer,
246                           BufferSize,
247                           &AuthenticationStatus
248                           );
249    if (EFI_ERROR (Status)) {
250      //
251      // Didn't find a raw section, just return the whole file.
252      //
253      Status = FvProtocol->ReadFile (
254                             FvProtocol,
255                             &FvFileInfo->NameGuid,
256                             Buffer,
257                             BufferSize,
258                             &FoundType,
259                             &Attributes,
260                             &AuthenticationStatus
261                             );
262    }
263  } else {
264    //
265    // Read the entire file
266    //
267    Status = FvProtocol->ReadFile (
268                           FvProtocol,
269                           &FvFileInfo->NameGuid,
270                           Buffer,
271                           BufferSize,
272                           &FoundType,
273                           &Attributes,
274                           &AuthenticationStatus
275                           );
276  }
277
278  return Status;
279}
280
281/**
282  Helper function for populating an EFI_FILE_INFO for a file.
283
284  Note the CreateTime, LastAccessTime and ModificationTime fields in EFI_FILE_INFO
285  are full zero as FV2 protocol has no corresponding info to fill.
286
287  @param  FvFileInfo                  A pointer to the FV_FILESYSTEM_FILE_INFO instance that is a struct
288                                      representing a file's info.
289  @param  BufferSize                  Pointer to a caller-allocated UINTN. It indicates the size of
290                                      the memory represented by FileInfo.
291  @param  FileInfo                    A pointer to EFI_FILE_INFO to contain the returned file info.
292
293  @retval EFI_SUCCESS                 The call completed successfully.
294  @retval EFI_BUFFER_TOO_SMALL        The buffer is too small to contain the requested output.
295
296**/
297EFI_STATUS
298FvFsGetFileInfo (
299  IN     FV_FILESYSTEM_FILE_INFO           *FvFileInfo,
300  IN OUT UINTN                             *BufferSize,
301     OUT EFI_FILE_INFO                     *FileInfo
302  )
303{
304  UINTN                      InfoSize;
305
306  InfoSize = (UINTN)FvFileInfo->FileInfo.Size;
307  if (*BufferSize < InfoSize) {
308    *BufferSize = InfoSize;
309    return EFI_BUFFER_TOO_SMALL;
310  }
311
312  //
313  // Initialize FileInfo
314  //
315  CopyMem (FileInfo, &FvFileInfo->FileInfo, InfoSize);
316
317  *BufferSize = InfoSize;
318  return EFI_SUCCESS;
319}
320
321/**
322  Removes the last directory or file entry in a path by changing the last
323  L'\' to a CHAR_NULL.
324
325  @param  Path      The pointer to the path to modify.
326
327  @retval FALSE     Nothing was found to remove.
328  @retval TRUE      A directory or file was removed.
329
330**/
331BOOLEAN
332EFIAPI
333RemoveLastItemFromPath (
334  IN OUT CHAR16 *Path
335  )
336{
337  CHAR16        *Walker;
338  CHAR16        *LastSlash;
339  //
340  // get directory name from path... ('chop' off extra)
341  //
342  for ( Walker = Path, LastSlash = NULL
343      ; Walker != NULL && *Walker != CHAR_NULL
344      ; Walker++
345     ){
346    if (*Walker == L'\\' && *(Walker + 1) != CHAR_NULL) {
347      LastSlash = Walker + 1;
348    }
349  }
350
351  if (LastSlash != NULL) {
352    *LastSlash = CHAR_NULL;
353    return (TRUE);
354  }
355
356  return (FALSE);
357}
358
359/**
360  Function to clean up paths.
361
362  - Single periods in the path are removed.
363  - Double periods in the path are removed along with a single parent directory.
364  - Forward slashes L'/' are converted to backward slashes L'\'.
365
366  This will be done inline and the existing buffer may be larger than required
367  upon completion.
368
369  @param  Path          The pointer to the string containing the path.
370
371  @retval NULL          An error occured.
372  @return Path in all other instances.
373
374**/
375CHAR16*
376EFIAPI
377TrimFilePathToAbsolutePath (
378  IN CHAR16 *Path
379  )
380{
381  CHAR16  *TempString;
382  UINTN   TempSize;
383
384  if (Path == NULL) {
385    return NULL;
386  }
387
388  //
389  // Fix up the '/' vs '\'
390  //
391  for (TempString = Path ; (TempString != NULL) && (*TempString != CHAR_NULL); TempString++) {
392    if (*TempString == L'/') {
393      *TempString = L'\\';
394    }
395  }
396
397  //
398  // Fix up the ..
399  //
400  while ((TempString = StrStr (Path, L"\\..\\")) != NULL) {
401    *TempString  = CHAR_NULL;
402    TempString  += 4;
403    RemoveLastItemFromPath (Path);
404    TempSize     = StrSize (TempString);
405    CopyMem (Path + StrLen (Path), TempString, TempSize);
406  }
407
408  if (((TempString = StrStr (Path, L"\\..")) != NULL) && (*(TempString + 3) == CHAR_NULL)) {
409    *TempString  = CHAR_NULL;
410    RemoveLastItemFromPath (Path);
411  }
412
413  //
414  // Fix up the .
415  //
416  while ((TempString = StrStr (Path, L"\\.\\")) != NULL) {
417    *TempString  = CHAR_NULL;
418    TempString  += 2;
419    TempSize     = StrSize (TempString);
420    CopyMem(Path + StrLen (Path), TempString, TempSize);
421  }
422
423  if (((TempString = StrStr (Path, L"\\.")) != NULL) && (*(TempString + 2) == CHAR_NULL)) {
424    *(TempString + 1) = CHAR_NULL;
425  }
426
427  while ((TempString = StrStr (Path, L"\\\\")) != NULL) {
428    *TempString  = CHAR_NULL;
429    TempString  += 1;
430    TempSize     = StrSize(TempString);
431    CopyMem(Path + StrLen(Path), TempString, TempSize);
432  }
433
434  if (((TempString = StrStr(Path, L"\\\\")) != NULL) && (*(TempString + 1) == CHAR_NULL)) {
435    *(TempString) = CHAR_NULL;
436  }
437
438  return Path;
439}
440
441/**
442  Opens a new file relative to the source file's location.
443
444  @param  This       A pointer to the EFI_FILE_PROTOCOL instance that is the file
445                     handle to the source location. This would typically be an open
446                     handle to a directory.
447  @param  NewHandle  A pointer to the location to return the opened handle for the new
448                     file.
449  @param  FileName   The Null-terminated string of the name of the file to be opened.
450                     The file name may contain the following path modifiers: "\", ".",
451                     and "..".
452  @param  OpenMode   The mode to open the file. The only valid combinations that the
453                     file may be opened with are: Read, Read/Write, or Create/Read/Write.
454  @param  Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these are the
455                     attribute bits for the newly created file.
456
457  @retval EFI_SUCCESS          The file was opened.
458  @retval EFI_NOT_FOUND        The specified file could not be found on the device.
459  @retval EFI_NO_MEDIA         The device has no medium.
460  @retval EFI_MEDIA_CHANGED    The device has a different medium in it or the medium is no
461                               longer supported.
462  @retval EFI_DEVICE_ERROR     The device reported an error.
463  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
464  @retval EFI_WRITE_PROTECTED  An attempt was made to create a file, or open a file for write
465                               when the media is write-protected.
466  @retval EFI_ACCESS_DENIED    The service denied access to the file.
467  @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file.
468  @retval EFI_VOLUME_FULL      The volume is full.
469
470**/
471EFI_STATUS
472EFIAPI
473FvSimpleFileSystemOpen (
474  IN     EFI_FILE_PROTOCOL    *This,
475     OUT EFI_FILE_PROTOCOL    **NewHandle,
476  IN     CHAR16               *FileName,
477  IN     UINT64               OpenMode,
478  IN     UINT64               Attributes
479  )
480{
481  FV_FILESYSTEM_INSTANCE      *Instance;
482  FV_FILESYSTEM_FILE          *File;
483  FV_FILESYSTEM_FILE          *NewFile;
484  FV_FILESYSTEM_FILE_INFO     *FvFileInfo;
485  LIST_ENTRY                  *FvFileInfoLink;
486  EFI_STATUS                  Status;
487  UINTN                       FileNameLength;
488  UINTN                       NewFileNameLength;
489  CHAR16                      *FileNameWithExtension;
490
491  //
492  // Check for a valid mode
493  //
494  switch (OpenMode) {
495  case EFI_FILE_MODE_READ:
496    break;
497
498  default:
499    return EFI_WRITE_PROTECTED;
500  }
501
502  File = FVFS_FILE_FROM_FILE_THIS (This);
503  Instance = File->Instance;
504
505  FileName = TrimFilePathToAbsolutePath (FileName);
506  if (FileName == NULL) {
507    return EFI_INVALID_PARAMETER;
508  }
509
510  if (FileName[0] == L'\\') {
511    FileName++;
512  }
513
514  //
515  // Check for opening root
516  //
517  if (StrCmp (FileName, L".") == 0 || StrCmp (FileName, L"") == 0) {
518    NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE));
519    if (NewFile == NULL) {
520      return EFI_OUT_OF_RESOURCES;
521    }
522    NewFile->Signature = FVFS_FILE_SIGNATURE;
523    NewFile->Instance  = Instance;
524    NewFile->FvFileInfo = File->FvFileInfo;
525    CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate));
526    InitializeListHead (&NewFile->Link);
527    InsertHeadList (&Instance->FileHead, &NewFile->Link);
528
529    NewFile->DirReadNext = NULL;
530    if (!IsListEmpty (&Instance->FileInfoHead)) {
531      NewFile->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance);
532    }
533
534    *NewHandle = &NewFile->FileProtocol;
535    return EFI_SUCCESS;
536  }
537
538  //
539  // Do a linear search for a file in the FV with a matching filename
540  //
541  Status     = EFI_NOT_FOUND;
542  FvFileInfo = NULL;
543  for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead);
544      !IsNull (&Instance->FileInfoHead, FvFileInfoLink);
545       FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) {
546    FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink);
547    if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileName) == 0) {
548      Status = EFI_SUCCESS;
549      break;
550    }
551  }
552
553  // If the file has not been found check if the filename exists with an extension
554  // in case there was no extension present.
555  // FvFileSystem adds a 'virtual' extension '.EFI' to EFI applications and drivers
556  // present in the Firmware Volume
557  if (Status == EFI_NOT_FOUND) {
558    FileNameLength = StrLen (FileName);
559
560    // Does the filename already contain the '.EFI' extension?
561    if (mUnicodeCollation->StriColl (mUnicodeCollation, FileName + FileNameLength - 4, L".efi") != 0) {
562      // No, there was no extension. So add one and search again for the file
563      // NewFileNameLength = FileNameLength + 1 + 4 = (Number of non-null character) + (file extension) + (a null character)
564      NewFileNameLength = FileNameLength + 1 + 4;
565      FileNameWithExtension = AllocateCopyPool (NewFileNameLength * 2, FileName);
566      StrCatS (FileNameWithExtension, NewFileNameLength, L".EFI");
567
568      for (FvFileInfoLink = GetFirstNode (&Instance->FileInfoHead);
569          !IsNull (&Instance->FileInfoHead, FvFileInfoLink);
570           FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, FvFileInfoLink)) {
571        FvFileInfo = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink);
572        if (mUnicodeCollation->StriColl (mUnicodeCollation, &FvFileInfo->FileInfo.FileName[0], FileNameWithExtension) == 0) {
573          Status = EFI_SUCCESS;
574          break;
575        }
576      }
577    }
578  }
579
580  if (!EFI_ERROR (Status)) {
581    NewFile = AllocateZeroPool (sizeof (FV_FILESYSTEM_FILE));
582    if (NewFile == NULL) {
583      return EFI_OUT_OF_RESOURCES;
584    }
585
586    NewFile->Signature = FVFS_FILE_SIGNATURE;
587    NewFile->Instance  = Instance;
588    NewFile->FvFileInfo = FvFileInfo;
589    CopyMem (&NewFile->FileProtocol, &mFileSystemTemplate, sizeof (mFileSystemTemplate));
590    InitializeListHead (&NewFile->Link);
591    InsertHeadList (&Instance->FileHead, &NewFile->Link);
592
593    *NewHandle = &NewFile->FileProtocol;
594    return EFI_SUCCESS;
595  }
596
597  return EFI_NOT_FOUND;
598}
599
600/**
601  Closes a specified file handle.
602
603  @param  This          A pointer to the EFI_FILE_PROTOCOL instance that is the file
604                        handle to close.
605
606  @retval EFI_SUCCESS   The file was closed.
607
608**/
609EFI_STATUS
610EFIAPI
611FvSimpleFileSystemClose (
612  IN EFI_FILE_PROTOCOL  *This
613  )
614{
615  FV_FILESYSTEM_INSTANCE      *Instance;
616  FV_FILESYSTEM_FILE          *File;
617
618  File = FVFS_FILE_FROM_FILE_THIS (This);
619  Instance = File->Instance;
620
621  if (File != Instance->Root) {
622    RemoveEntryList (&File->Link);
623    FreePool (File);
624  }
625  return EFI_SUCCESS;
626}
627
628/**
629  Reads data from a file.
630
631  @param  This       A pointer to the EFI_FILE_PROTOCOL instance that is the file
632                     handle to read data from.
633  @param  BufferSize On input, the size of the Buffer. On output, the amount of data
634                     returned in Buffer. In both cases, the size is measured in bytes.
635  @param  Buffer     The buffer into which the data is read.
636
637  @retval EFI_SUCCESS          Data was read.
638  @retval EFI_NO_MEDIA         The device has no medium.
639  @retval EFI_DEVICE_ERROR     The device reported an error.
640  @retval EFI_DEVICE_ERROR     An attempt was made to read from a deleted file.
641  @retval EFI_DEVICE_ERROR     On entry, the current file position is beyond the end of the file.
642  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
643  @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory
644                               entry. BufferSize has been updated with the size
645                               needed to complete the request.
646
647**/
648EFI_STATUS
649EFIAPI
650FvSimpleFileSystemRead (
651  IN     EFI_FILE_PROTOCOL      *This,
652  IN OUT UINTN                  *BufferSize,
653     OUT VOID                   *Buffer
654  )
655{
656  FV_FILESYSTEM_INSTANCE        *Instance;
657  FV_FILESYSTEM_FILE            *File;
658  EFI_STATUS                    Status;
659  LIST_ENTRY                    *FvFileInfoLink;
660  VOID                          *FileBuffer;
661  UINTN                         FileSize;
662
663  File = FVFS_FILE_FROM_FILE_THIS (This);
664  Instance = File->Instance;
665
666  if (File->FvFileInfo == Instance->Root->FvFileInfo) {
667    if (File->DirReadNext) {
668      //
669      // Directory read: populate Buffer with an EFI_FILE_INFO
670      //
671      Status = FvFsGetFileInfo (File->DirReadNext, BufferSize, Buffer);
672      if (!EFI_ERROR (Status)) {
673        //
674        // Successfully read a directory entry, now update the pointer to the
675        // next file, which will be read on the next call to this function
676        //
677        FvFileInfoLink = GetNextNode (&Instance->FileInfoHead, &File->DirReadNext->Link);
678        if (IsNull (&Instance->FileInfoHead, FvFileInfoLink)) {
679          //
680          // No more files left
681          //
682          File->DirReadNext = NULL;
683        } else {
684          File->DirReadNext = FVFS_FILE_INFO_FROM_LINK (FvFileInfoLink);
685        }
686      }
687      return Status;
688    } else {
689      //
690      // Directory read. All entries have been read, so return a zero-size
691      // buffer.
692      //
693      *BufferSize = 0;
694      return EFI_SUCCESS;
695    }
696  } else {
697    FileSize = (UINTN)File->FvFileInfo->FileInfo.FileSize;
698
699    FileBuffer = AllocateZeroPool (FileSize);
700    if (FileBuffer == NULL) {
701      return EFI_DEVICE_ERROR;
702    }
703
704    Status = FvFsReadFile (File->Instance->FvProtocol, File->FvFileInfo, &FileSize, &FileBuffer);
705    if (EFI_ERROR (Status)) {
706      return EFI_DEVICE_ERROR;
707    }
708
709    if (*BufferSize + File->Position > FileSize) {
710      *BufferSize = (UINTN)(FileSize - File->Position);
711    }
712
713    CopyMem (Buffer, (UINT8*)FileBuffer + File->Position, *BufferSize);
714    File->Position += *BufferSize;
715
716    return EFI_SUCCESS;
717  }
718}
719
720/**
721  Writes data to a file.
722
723  @param  This       A pointer to the EFI_FILE_PROTOCOL instance that is the file
724                     handle to write data to.
725  @param  BufferSize On input, the size of the Buffer. On output, the amount of data
726                     actually written. In both cases, the size is measured in bytes.
727  @param  Buffer     The buffer of data to write.
728
729  @retval EFI_SUCCESS          Data was written.
730  @retval EFI_UNSUPPORTED      Writes to open directory files are not supported.
731  @retval EFI_NO_MEDIA         The device has no medium.
732  @retval EFI_DEVICE_ERROR     The device reported an error.
733  @retval EFI_DEVICE_ERROR     An attempt was made to write to a deleted file.
734  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
735  @retval EFI_WRITE_PROTECTED  The file or medium is write-protected.
736  @retval EFI_ACCESS_DENIED    The file was opened read only.
737  @retval EFI_VOLUME_FULL      The volume is full.
738
739**/
740EFI_STATUS
741EFIAPI
742FvSimpleFileSystemWrite (
743  IN     EFI_FILE_PROTOCOL    *This,
744  IN OUT UINTN                *BufferSize,
745  IN     VOID                 *Buffer
746  )
747{
748  FV_FILESYSTEM_INSTANCE        *Instance;
749  FV_FILESYSTEM_FILE            *File;
750
751  File = FVFS_FILE_FROM_FILE_THIS (This);
752  Instance = File->Instance;
753
754  if (File->FvFileInfo == Instance->Root->FvFileInfo) {
755    return EFI_UNSUPPORTED;
756  } else {
757    return EFI_WRITE_PROTECTED;
758  }
759}
760
761/**
762  Returns a file's current position.
763
764  @param  This            A pointer to the EFI_FILE_PROTOCOL instance that is the file
765                          handle to get the current position on.
766  @param  Position        The address to return the file's current position value.
767
768  @retval EFI_SUCCESS      The position was returned.
769  @retval EFI_UNSUPPORTED  The request is not valid on open directories.
770  @retval EFI_DEVICE_ERROR An attempt was made to get the position from a deleted file.
771
772**/
773EFI_STATUS
774EFIAPI
775FvSimpleFileSystemGetPosition (
776  IN     EFI_FILE_PROTOCOL    *This,
777     OUT UINT64               *Position
778  )
779{
780  FV_FILESYSTEM_INSTANCE        *Instance;
781  FV_FILESYSTEM_FILE            *File;
782
783  File = FVFS_FILE_FROM_FILE_THIS (This);
784  Instance = File->Instance;
785
786  if (File->FvFileInfo == Instance->Root->FvFileInfo) {
787    return EFI_UNSUPPORTED;
788  } else {
789    *Position = File->Position;
790    return EFI_SUCCESS;
791  }
792}
793
794/**
795  Sets a file's current position.
796
797  @param  This            A pointer to the EFI_FILE_PROTOCOL instance that is the
798                          file handle to set the requested position on.
799  @param  Position        The byte position from the start of the file to set.
800
801  @retval EFI_SUCCESS      The position was set.
802  @retval EFI_UNSUPPORTED  The seek request for nonzero is not valid on open
803                           directories.
804  @retval EFI_DEVICE_ERROR An attempt was made to set the position of a deleted file.
805
806**/
807EFI_STATUS
808EFIAPI
809FvSimpleFileSystemSetPosition (
810  IN EFI_FILE_PROTOCOL        *This,
811  IN UINT64                   Position
812  )
813{
814  FV_FILESYSTEM_INSTANCE      *Instance;
815  FV_FILESYSTEM_FILE          *File;
816
817  File = FVFS_FILE_FROM_FILE_THIS (This);
818  Instance = File->Instance;
819
820  if (File->FvFileInfo == Instance->Root->FvFileInfo) {
821    if (Position != 0) {
822      return EFI_UNSUPPORTED;
823    }
824    //
825    // Reset directory position to first entry
826    //
827    if (File->DirReadNext) {
828      File->DirReadNext = FVFS_GET_FIRST_FILE_INFO (Instance);
829    }
830  } else if (Position == 0xFFFFFFFFFFFFFFFFull) {
831    File->Position = File->FvFileInfo->FileInfo.FileSize;
832  } else {
833    File->Position = Position;
834  }
835
836  return EFI_SUCCESS;
837}
838
839/**
840  Flushes all modified data associated with a file to a device.
841
842  @param  This A pointer to the EFI_FILE_PROTOCOL instance that is the file
843               handle to flush.
844
845  @retval EFI_SUCCESS          The data was flushed.
846  @retval EFI_NO_MEDIA         The device has no medium.
847  @retval EFI_DEVICE_ERROR     The device reported an error.
848  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
849  @retval EFI_WRITE_PROTECTED  The file or medium is write-protected.
850  @retval EFI_ACCESS_DENIED    The file was opened read-only.
851  @retval EFI_VOLUME_FULL      The volume is full.
852
853**/
854EFI_STATUS
855EFIAPI
856FvSimpleFileSystemFlush (
857  IN EFI_FILE_PROTOCOL  *This
858  )
859{
860  return EFI_WRITE_PROTECTED;
861}
862
863/**
864  Close and delete the file handle.
865
866  @param  This                     A pointer to the EFI_FILE_PROTOCOL instance that is the
867                                   handle to the file to delete.
868
869  @retval EFI_SUCCESS              The file was closed and deleted, and the handle was closed.
870  @retval EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not deleted.
871
872**/
873EFI_STATUS
874EFIAPI
875FvSimpleFileSystemDelete (
876  IN EFI_FILE_PROTOCOL *This
877  )
878{
879  EFI_STATUS       Status;
880
881  Status = FvSimpleFileSystemClose (This);
882  ASSERT_EFI_ERROR (Status);
883
884  return EFI_WARN_DELETE_FAILURE;
885}
886
887/**
888  Returns information about a file.
889
890  @param  This            A pointer to the EFI_FILE_PROTOCOL instance that is the file
891                          handle the requested information is for.
892  @param  InformationType The type identifier for the information being requested.
893  @param  BufferSize      On input, the size of Buffer. On output, the amount of data
894                          returned in Buffer. In both cases, the size is measured in bytes.
895  @param  Buffer          A pointer to the data buffer to return. The buffer's type is
896                          indicated by InformationType.
897
898  @retval EFI_SUCCESS          The information was returned.
899  @retval EFI_UNSUPPORTED      The InformationType is not known.
900  @retval EFI_NO_MEDIA         The device has no medium.
901  @retval EFI_DEVICE_ERROR     The device reported an error.
902  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
903  @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
904                               BufferSize has been updated with the size needed to complete
905                               the request.
906**/
907EFI_STATUS
908EFIAPI
909FvSimpleFileSystemGetInfo (
910  IN     EFI_FILE_PROTOCOL    *This,
911  IN     EFI_GUID             *InformationType,
912  IN OUT UINTN                *BufferSize,
913     OUT VOID                 *Buffer
914  )
915{
916  FV_FILESYSTEM_FILE           *File;
917  EFI_FILE_SYSTEM_INFO         *FsInfoOut;
918  EFI_FILE_SYSTEM_VOLUME_LABEL *FsVolumeLabel;
919  FV_FILESYSTEM_INSTANCE       *Instance;
920  UINTN                        Size;
921  EFI_STATUS                   Status;
922
923  File = FVFS_FILE_FROM_FILE_THIS (This);
924
925  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
926    //
927    // Return filesystem info
928    //
929    Instance = File->Instance;
930
931    Size = sizeof (EFI_FILE_SYSTEM_INFO) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16);
932
933    if (*BufferSize < Size) {
934      *BufferSize = Size;
935      return EFI_BUFFER_TOO_SMALL;
936    }
937
938    //
939    // Cast output buffer for convenience
940    //
941    FsInfoOut = (EFI_FILE_SYSTEM_INFO *) Buffer;
942
943    CopyMem (FsInfoOut, &mFsInfoTemplate, sizeof (EFI_FILE_SYSTEM_INFO));
944    Status = StrnCpyS ( FsInfoOut->VolumeLabel,
945                        (*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_INFO, VolumeLabel)) / sizeof (CHAR16),
946                        Instance->VolumeLabel,
947                        StrLen (Instance->VolumeLabel)
948                        );
949    ASSERT_EFI_ERROR (Status);
950    FsInfoOut->Size = Size;
951    return Status;
952  } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
953    //
954    // Return file info
955    //
956    return FvFsGetFileInfo (File->FvFileInfo, BufferSize, (EFI_FILE_INFO *) Buffer);
957  } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
958    //
959    // Return Volume Label
960    //
961    Instance = File->Instance;
962    Size     = sizeof (EFI_FILE_SYSTEM_VOLUME_LABEL) + StrSize (Instance->VolumeLabel) - sizeof (CHAR16);;
963    if (*BufferSize < Size) {
964      *BufferSize = Size;
965      return EFI_BUFFER_TOO_SMALL;
966    }
967
968    FsVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL*) Buffer;
969    Status        = StrnCpyS (FsVolumeLabel->VolumeLabel,
970                              (*BufferSize - OFFSET_OF (EFI_FILE_SYSTEM_VOLUME_LABEL, VolumeLabel)) / sizeof (CHAR16),
971                              Instance->VolumeLabel,
972                              StrLen (Instance->VolumeLabel)
973                              );
974    ASSERT_EFI_ERROR (Status);
975    return Status;
976  } else {
977    return EFI_UNSUPPORTED;
978  }
979}
980
981/**
982  Sets information about a file.
983
984  @param  This            A pointer to the EFI_FILE_PROTOCOL instance that is the file
985                          handle the information is for.
986  @param  InformationType The type identifier for the information being set.
987  @param  BufferSize      The size, in bytes, of Buffer.
988  @param  Buffer          A pointer to the data buffer to write. The buffer's type is
989                          indicated by InformationType.
990
991  @retval EFI_SUCCESS          The information was set.
992  @retval EFI_UNSUPPORTED      The InformationType is not known.
993  @retval EFI_NO_MEDIA         The device has no medium.
994  @retval EFI_DEVICE_ERROR     The device reported an error.
995  @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
996  @retval EFI_WRITE_PROTECTED  InformationType is EFI_FILE_INFO_ID and the media is
997                               read-only.
998  @retval EFI_WRITE_PROTECTED  InformationType is EFI_FILE_PROTOCOL_SYSTEM_INFO_ID
999                               and the media is read only.
1000  @retval EFI_WRITE_PROTECTED  InformationType is EFI_FILE_SYSTEM_VOLUME_LABEL_ID
1001                               and the media is read-only.
1002  @retval EFI_ACCESS_DENIED    An attempt is made to change the name of a file to a
1003                               file that is already present.
1004  @retval EFI_ACCESS_DENIED    An attempt is being made to change the EFI_FILE_DIRECTORY
1005                               Attribute.
1006  @retval EFI_ACCESS_DENIED    An attempt is being made to change the size of a directory.
1007  @retval EFI_ACCESS_DENIED    InformationType is EFI_FILE_INFO_ID and the file was opened
1008                               read-only and an attempt is being made to modify a field
1009                               other than Attribute.
1010  @retval EFI_VOLUME_FULL      The volume is full.
1011  @retval EFI_BAD_BUFFER_SIZE  BufferSize is smaller than the size of the type indicated
1012                               by InformationType.
1013
1014**/
1015EFI_STATUS
1016EFIAPI
1017FvSimpleFileSystemSetInfo (
1018  IN EFI_FILE_PROTOCOL        *This,
1019  IN EFI_GUID                 *InformationType,
1020  IN UINTN                    BufferSize,
1021  IN VOID                     *Buffer
1022  )
1023{
1024  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) ||
1025      CompareGuid (InformationType, &gEfiFileInfoGuid) ||
1026      CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
1027    return EFI_WRITE_PROTECTED;
1028  }
1029
1030  return EFI_UNSUPPORTED;
1031}
1032
1033