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 
14 Copyright (c) 2014, ARM Limited. All rights reserved.
15 Copyright (c) 2014 - 2015, Intel Corporation. All rights reserved.<BR>
16 
17 This program and the accompanying materials
18 are licensed and made available under the terms and conditions of the BSD License
19 which accompanies this distribution.  The full text of the license may be found at
20 http://opensource.org/licenses/bsd-license.php
21 
22 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
23 WITHOUT 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 //
32 EFI_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 //
44 EFI_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 **/
75 EFI_STATUS
FvFsFindExecutableSection(IN EFI_FIRMWARE_VOLUME2_PROTOCOL * FvProtocol,IN FV_FILESYSTEM_FILE_INFO * FvFileInfo,IN OUT UINTN * BufferSize,IN OUT VOID ** Buffer)76 FvFsFindExecutableSection (
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 **/
116 EFI_STATUS
FvFsGetFileSize(IN EFI_FIRMWARE_VOLUME2_PROTOCOL * FvProtocol,IN OUT FV_FILESYSTEM_FILE_INFO * FvFileInfo)117 FvFsGetFileSize (
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 **/
218 EFI_STATUS
FvFsReadFile(IN EFI_FIRMWARE_VOLUME2_PROTOCOL * FvProtocol,IN FV_FILESYSTEM_FILE_INFO * FvFileInfo,IN OUT UINTN * BufferSize,IN OUT VOID ** Buffer)219 FvFsReadFile (
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 **/
297 EFI_STATUS
FvFsGetFileInfo(IN FV_FILESYSTEM_FILE_INFO * FvFileInfo,IN OUT UINTN * BufferSize,OUT EFI_FILE_INFO * FileInfo)298 FvFsGetFileInfo (
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 **/
331 BOOLEAN
332 EFIAPI
RemoveLastItemFromPath(IN OUT CHAR16 * Path)333 RemoveLastItemFromPath (
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 **/
375 CHAR16*
376 EFIAPI
TrimFilePathToAbsolutePath(IN CHAR16 * Path)377 TrimFilePathToAbsolutePath (
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 **/
471 EFI_STATUS
472 EFIAPI
FvSimpleFileSystemOpen(IN EFI_FILE_PROTOCOL * This,OUT EFI_FILE_PROTOCOL ** NewHandle,IN CHAR16 * FileName,IN UINT64 OpenMode,IN UINT64 Attributes)473 FvSimpleFileSystemOpen (
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 **/
609 EFI_STATUS
610 EFIAPI
FvSimpleFileSystemClose(IN EFI_FILE_PROTOCOL * This)611 FvSimpleFileSystemClose (
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 **/
648 EFI_STATUS
649 EFIAPI
FvSimpleFileSystemRead(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)650 FvSimpleFileSystemRead (
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 **/
740 EFI_STATUS
741 EFIAPI
FvSimpleFileSystemWrite(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,IN VOID * Buffer)742 FvSimpleFileSystemWrite (
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 **/
773 EFI_STATUS
774 EFIAPI
FvSimpleFileSystemGetPosition(IN EFI_FILE_PROTOCOL * This,OUT UINT64 * Position)775 FvSimpleFileSystemGetPosition (
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 **/
807 EFI_STATUS
808 EFIAPI
FvSimpleFileSystemSetPosition(IN EFI_FILE_PROTOCOL * This,IN UINT64 Position)809 FvSimpleFileSystemSetPosition (
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 **/
854 EFI_STATUS
855 EFIAPI
FvSimpleFileSystemFlush(IN EFI_FILE_PROTOCOL * This)856 FvSimpleFileSystemFlush (
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 **/
873 EFI_STATUS
874 EFIAPI
FvSimpleFileSystemDelete(IN EFI_FILE_PROTOCOL * This)875 FvSimpleFileSystemDelete (
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 **/
907 EFI_STATUS
908 EFIAPI
FvSimpleFileSystemGetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)909 FvSimpleFileSystemGetInfo (
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 **/
1015 EFI_STATUS
1016 EFIAPI
FvSimpleFileSystemSetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN UINTN BufferSize,IN VOID * Buffer)1017 FvSimpleFileSystemSetInfo (
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