1 /** @file
2 *
3 *  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
4 *
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 "BootMonFsInternal.h"
16 
17 EFIAPI
18 EFI_STATUS
OpenBootMonFsOpenVolume(IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * This,OUT EFI_FILE_PROTOCOL ** Root)19 OpenBootMonFsOpenVolume (
20   IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
21   OUT EFI_FILE_PROTOCOL              **Root
22   )
23 {
24   BOOTMON_FS_INSTANCE *Instance;
25 
26   Instance = BOOTMON_FS_FROM_FS_THIS (This);
27   if (Instance == NULL) {
28     return EFI_DEVICE_ERROR;
29   }
30 
31   Instance->RootFile->Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
32 
33   *Root = &Instance->RootFile->File;
34 
35   return EFI_SUCCESS;
36 }
37 
38 UINT32
BootMonFsGetImageLength(IN BOOTMON_FS_FILE * File)39 BootMonFsGetImageLength (
40   IN BOOTMON_FS_FILE      *File
41   )
42 {
43   UINT32                   Index;
44   UINT32                   FileSize;
45   LIST_ENTRY              *RegionToFlushLink;
46   BOOTMON_FS_FILE_REGION  *Region;
47 
48   FileSize = 0;
49 
50   // Look at all Flash areas to determine file size
51   for (Index = 0; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) {
52     FileSize += File->HwDescription.Region[Index].Size;
53   }
54 
55   // Add the regions that have not been flushed yet
56   for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
57        !IsNull (&File->RegionToFlushLink, RegionToFlushLink);
58        RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
59        )
60   {
61     Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
62     if (Region->Offset + Region->Size > FileSize) {
63       FileSize += Region->Offset + Region->Size;
64     }
65   }
66 
67   return FileSize;
68 }
69 
70 UINTN
BootMonFsGetPhysicalSize(IN BOOTMON_FS_FILE * File)71 BootMonFsGetPhysicalSize (
72   IN BOOTMON_FS_FILE* File
73   )
74 {
75   // Return 0 for files that haven't yet been flushed to media
76   if (File->HwDescription.RegionCount == 0) {
77     return 0;
78   }
79 
80   return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 )
81           * File->Instance->Media->BlockSize;
82 }
83 
84 EFIAPI
85 EFI_STATUS
BootMonFsSetDirPosition(IN EFI_FILE_PROTOCOL * This,IN UINT64 Position)86 BootMonFsSetDirPosition (
87   IN EFI_FILE_PROTOCOL  *This,
88   IN UINT64             Position
89   )
90 {
91   BOOTMON_FS_FILE       *File;
92 
93   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
94   if (File == NULL) {
95     return EFI_INVALID_PARAMETER;
96   }
97 
98   // UEFI Spec section 12.5:
99   // "The seek request for nonzero is not valid on open directories."
100   if (Position != 0) {
101     return EFI_UNSUPPORTED;
102   }
103   File->Position = Position;
104 
105   return EFI_SUCCESS;
106 }
107 
108 EFI_STATUS
BootMonFsOpenDirectory(OUT EFI_FILE_PROTOCOL ** NewHandle,IN CHAR16 * FileName,IN BOOTMON_FS_INSTANCE * Volume)109 BootMonFsOpenDirectory (
110   OUT EFI_FILE_PROTOCOL **NewHandle,
111   IN CHAR16             *FileName,
112   IN BOOTMON_FS_INSTANCE *Volume
113   )
114 {
115   ASSERT(0);
116 
117   return EFI_UNSUPPORTED;
118 }
119 
120 STATIC
121 EFI_STATUS
GetFileSystemVolumeLabelInfo(IN BOOTMON_FS_INSTANCE * Instance,IN OUT UINTN * BufferSize,OUT VOID * Buffer)122 GetFileSystemVolumeLabelInfo (
123   IN BOOTMON_FS_INSTANCE *Instance,
124   IN OUT UINTN          *BufferSize,
125   OUT VOID              *Buffer
126   )
127 {
128   UINTN                         Size;
129   EFI_FILE_SYSTEM_VOLUME_LABEL *Label;
130   EFI_STATUS                    Status;
131 
132   Label = Buffer;
133 
134   // Value returned by StrSize includes null terminator.
135   Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
136          + StrSize (Instance->FsInfo.VolumeLabel);
137 
138   if (*BufferSize >= Size) {
139     CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size);
140     Status = EFI_SUCCESS;
141   } else {
142     Status = EFI_BUFFER_TOO_SMALL;
143   }
144   *BufferSize = Size;
145   return Status;
146 }
147 
148 // Helper function that calculates a rough "free space" by:
149 // - Taking the media size
150 // - Subtracting the sum of all file sizes
151 // - Subtracting the block size times the number of files
152 //    (To account for the blocks containing the HW_IMAGE_INFO
153 STATIC
154 UINT64
ComputeFreeSpace(IN BOOTMON_FS_INSTANCE * Instance)155 ComputeFreeSpace (
156   IN BOOTMON_FS_INSTANCE *Instance
157   )
158 {
159   LIST_ENTRY   *FileLink;
160   UINT64        FileSizeSum;
161   UINT64        MediaSize;
162   UINTN         NumFiles;
163   EFI_BLOCK_IO_MEDIA *Media;
164   BOOTMON_FS_FILE *File;
165 
166   Media = Instance->BlockIo->Media;
167   MediaSize = Media->BlockSize * (Media->LastBlock + 1);
168 
169   NumFiles = 0;
170   FileSizeSum = 0;
171   for (FileLink = GetFirstNode (&Instance->RootFile->Link);
172          !IsNull (&Instance->RootFile->Link, FileLink);
173          FileLink = GetNextNode (&Instance->RootFile->Link, FileLink)
174          )
175   {
176     File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
177     FileSizeSum += BootMonFsGetImageLength (File);
178 
179     NumFiles++;
180   }
181 
182   return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles));
183 }
184 
185 STATIC
186 EFI_STATUS
GetFilesystemInfo(IN BOOTMON_FS_INSTANCE * Instance,IN OUT UINTN * BufferSize,OUT VOID * Buffer)187 GetFilesystemInfo (
188   IN BOOTMON_FS_INSTANCE *Instance,
189   IN OUT UINTN          *BufferSize,
190   OUT VOID              *Buffer
191   )
192 {
193   EFI_STATUS              Status;
194 
195   if (*BufferSize >= Instance->FsInfo.Size) {
196     Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance);
197     CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size);
198     Status = EFI_SUCCESS;
199   } else {
200     Status = EFI_BUFFER_TOO_SMALL;
201   }
202 
203   *BufferSize = Instance->FsInfo.Size;
204   return Status;
205 }
206 
207 STATIC
208 EFI_STATUS
GetFileInfo(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * File,IN OUT UINTN * BufferSize,OUT VOID * Buffer)209 GetFileInfo (
210   IN BOOTMON_FS_INSTANCE  *Instance,
211   IN BOOTMON_FS_FILE      *File,
212   IN OUT UINTN            *BufferSize,
213   OUT VOID                *Buffer
214   )
215 {
216   EFI_FILE_INFO  *Info;
217   UINTN          ResultSize;
218 
219   ResultSize = SIZE_OF_EFI_FILE_INFO + StrSize (File->Info->FileName);
220 
221   if (*BufferSize < ResultSize) {
222     *BufferSize = ResultSize;
223     return EFI_BUFFER_TOO_SMALL;
224   }
225 
226   Info = Buffer;
227 
228   CopyMem (Info, File->Info, ResultSize);
229   // Size of the information
230   Info->Size = ResultSize;
231 
232   *BufferSize = ResultSize;
233 
234   return EFI_SUCCESS;
235 }
236 
237 STATIC
238 EFI_STATUS
GetBootMonFsFileInfo(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * File,IN OUT UINTN * BufferSize,OUT VOID * Buffer)239 GetBootMonFsFileInfo (
240   IN BOOTMON_FS_INSTANCE *Instance,
241   IN BOOTMON_FS_FILE     *File,
242   IN OUT UINTN           *BufferSize,
243   OUT VOID               *Buffer
244   )
245 {
246   EFI_STATUS             Status;
247   BOOTMON_FS_FILE_INFO   *Info;
248   UINTN                  ResultSize;
249   UINTN                  Index;
250 
251   if (File == Instance->RootFile) {
252     Status = EFI_UNSUPPORTED;
253   } else {
254     ResultSize = SIZE_OF_BOOTMON_FS_FILE_INFO;
255 
256     if (*BufferSize < ResultSize) {
257       *BufferSize = ResultSize;
258       Status = EFI_BUFFER_TOO_SMALL;
259     } else {
260       Info = Buffer;
261 
262       // Zero out the structure
263       ZeroMem (Info, ResultSize);
264 
265       // Fill in the structure
266       Info->Size = ResultSize;
267 
268       Info->EntryPoint  = File->HwDescription.EntryPoint;
269       Info->RegionCount = File->HwDescription.RegionCount;
270       for (Index = 0; Index < File->HwDescription.RegionCount; Index++) {
271         Info->Region[Index].LoadAddress = File->HwDescription.Region[Index].LoadAddress;
272         Info->Region[Index].Size        = File->HwDescription.Region[Index].Size;
273         Info->Region[Index].Offset      = File->HwDescription.Region[Index].Offset;
274         Info->Region[Index].Checksum    = File->HwDescription.Region[Index].Checksum;
275       }
276       *BufferSize = ResultSize;
277       Status = EFI_SUCCESS;
278     }
279   }
280 
281   return Status;
282 }
283 
284 /**
285   Set the name of a file.
286 
287   This is a helper function for SetFileInfo().
288 
289   @param[in]  Instance  A pointer to the description of the volume
290                         the file belongs to.
291   @param[in]  File      A pointer to the description of the file.
292   @param[in]  FileName  A pointer to the new name of the file.
293 
294   @retval  EFI_SUCCESS        The name was set.
295   @retval  EFI_ACCESS_DENIED  An attempt is made to change the name of a file
296                               to a file that is already present.
297 
298 **/
299 STATIC
300 EFI_STATUS
SetFileName(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * File,IN CONST CHAR16 * FileName)301 SetFileName (
302   IN  BOOTMON_FS_INSTANCE  *Instance,
303   IN  BOOTMON_FS_FILE      *File,
304   IN  CONST CHAR16         *FileName
305   )
306 {
307   CHAR8            AsciiFileName[MAX_NAME_LENGTH];
308   BOOTMON_FS_FILE  *SameFile;
309 
310   // If the file path start with a \ strip it. The EFI Shell may
311   // insert a \ in front of the file name.
312   if (FileName[0] == L'\\') {
313     FileName++;
314   }
315 
316   UnicodeStrToAsciiStrS (FileName, AsciiFileName, MAX_NAME_LENGTH);
317 
318   if (BootMonGetFileFromAsciiFileName (
319         File->Instance,
320         AsciiFileName,
321         &SameFile
322         ) != EFI_NOT_FOUND) {
323     // A file with that name already exists.
324     return EFI_ACCESS_DENIED;
325   } else {
326     // OK, change the filename.
327     AsciiStrToUnicodeStrS (AsciiFileName, File->Info->FileName,
328       (File->Info->Size - SIZE_OF_EFI_FILE_INFO) / sizeof (CHAR16));
329     return EFI_SUCCESS;
330   }
331 }
332 
333 /**
334   Set the size of a file.
335 
336   This is a helper function for SetFileInfo().
337 
338   @param[in]  Instance  A pointer to the description of the volume
339                         the file belongs to.
340   @param[in]  File      A pointer to the description of the file.
341   @param[in]  NewSize   The requested new size for the file.
342 
343   @retval  EFI_SUCCESS           The size was set.
344   @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request failed.
345 
346 **/
347 STATIC
348 EFI_STATUS
SetFileSize(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * BootMonFsFile,IN UINTN NewSize)349 SetFileSize (
350   IN BOOTMON_FS_INSTANCE  *Instance,
351   IN BOOTMON_FS_FILE      *BootMonFsFile,
352   IN UINTN                 NewSize
353   )
354 {
355   EFI_STATUS              Status;
356   UINT32                  OldSize;
357   LIST_ENTRY              *RegionToFlushLink;
358   LIST_ENTRY              *NextRegionToFlushLink;
359   BOOTMON_FS_FILE_REGION  *Region;
360   EFI_FILE_PROTOCOL       *File;
361   CHAR8                   *Buffer;
362   UINTN                   BufferSize;
363   UINT64                  StoredPosition;
364 
365   OldSize = BootMonFsFile->Info->FileSize;
366 
367   //
368   // In case of file truncation, force the regions waiting for writing to
369   // not overflow the new size of the file.
370   //
371   if (NewSize < OldSize) {
372     for (RegionToFlushLink = GetFirstNode (&BootMonFsFile->RegionToFlushLink);
373          !IsNull (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
374          )
375     {
376       NextRegionToFlushLink = GetNextNode (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
377       Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
378       if (Region->Offset > NewSize) {
379         RemoveEntryList (RegionToFlushLink);
380         FreePool (Region->Buffer);
381         FreePool (Region);
382       } else {
383         Region->Size = MIN (Region->Size, NewSize - Region->Offset);
384       }
385       RegionToFlushLink = NextRegionToFlushLink;
386     }
387 
388   } else if (NewSize > OldSize) {
389     // Increasing a file's size is potentially complicated as it may require
390     // moving the image description on media. The simplest way to do it is to
391     // seek past the end of the file (which is valid in UEFI) and perform a
392     // Write.
393     File = &BootMonFsFile->File;
394 
395     // Save position
396     Status = File->GetPosition (File, &StoredPosition);
397     if (EFI_ERROR (Status)) {
398       return Status;
399     }
400     // Set position at the end of the file
401     Status = File->SetPosition (File, OldSize);
402     if (EFI_ERROR (Status)) {
403       return Status;
404     }
405 
406     BufferSize = NewSize - OldSize;
407     Buffer = AllocateZeroPool (BufferSize);
408     if (Buffer == NULL) {
409       return EFI_OUT_OF_RESOURCES;
410     }
411 
412     Status = File->Write (File, &BufferSize, Buffer);
413     FreePool (Buffer);
414     if (EFI_ERROR (Status)) {
415       return Status;
416     }
417 
418     // Restore saved position
419     Status = File->SetPosition (File, StoredPosition);
420     if (EFI_ERROR (Status)) {
421       return Status;
422     }
423   }
424 
425   BootMonFsFile->Info->FileSize = NewSize;
426 
427   return EFI_SUCCESS;
428 }
429 
430 /**
431   Set information about a file.
432 
433   @param[in]  Instance  A pointer to the description of the volume
434                         the file belongs to.
435   @param[in]  File      A pointer to the description of the file.
436   @param[in]  Info      A pointer to the file information to write.
437 
438   @retval  EFI_SUCCESS           The information was set.
439   @retval  EFI_ACCESS_DENIED     An attempt is being made to change the
440                                  EFI_FILE_DIRECTORY Attribute.
441   @retval  EFI_ACCESS_DENIED     The file was opened in read-only mode and an
442                                  attempt is being made to modify a field other
443                                  than Attribute.
444   @retval  EFI_ACCESS_DENIED     An attempt is made to change the name of a file
445                                  to a file that is already present.
446   @retval  EFI_WRITE_PROTECTED   An attempt is being made to modify a read-only
447                                  attribute.
448   @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request
449                                  failed.
450 
451 **/
452 STATIC
453 EFI_STATUS
SetFileInfo(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * File,IN EFI_FILE_INFO * Info)454 SetFileInfo (
455   IN BOOTMON_FS_INSTANCE  *Instance,
456   IN BOOTMON_FS_FILE      *File,
457   IN EFI_FILE_INFO        *Info
458   )
459 {
460   EFI_STATUS  Status;
461   BOOLEAN     FileSizeIsDifferent;
462   BOOLEAN     FileNameIsDifferent;
463   BOOLEAN     TimeIsDifferent;
464 
465   //
466   // A directory can not be changed to a file and a file can
467   // not be changed to a directory.
468   //
469   if ((Info->Attribute & EFI_FILE_DIRECTORY)      !=
470       (File->Info->Attribute & EFI_FILE_DIRECTORY)  ) {
471     return EFI_ACCESS_DENIED;
472   }
473 
474   FileSizeIsDifferent = (Info->FileSize != File->Info->FileSize);
475   FileNameIsDifferent = (StrnCmp (
476                            Info->FileName,
477                            File->Info->FileName,
478                            MAX_NAME_LENGTH - 1
479                            ) != 0);
480   //
481   // Check if the CreateTime, LastAccess or ModificationTime
482   // have been changed. The file system does not support file
483   // timestamps thus the three times in "File->Info" are
484   // always equal to zero. The following comparison actually
485   // checks if all three times are still equal to 0 or not.
486   //
487   TimeIsDifferent = CompareMem (
488                       &Info->CreateTime,
489                       &File->Info->CreateTime,
490                       3 * sizeof (EFI_TIME)
491                       ) != 0;
492 
493   //
494   // For a file opened in read-only mode, only the Attribute field can be
495   // modified. The root directory open mode is forced to read-only at opening
496   // thus the following test protects the root directory to be somehow modified.
497   //
498   if (File->OpenMode == EFI_FILE_MODE_READ) {
499     if (FileSizeIsDifferent || FileNameIsDifferent || TimeIsDifferent) {
500       return EFI_ACCESS_DENIED;
501     }
502   }
503 
504   if (TimeIsDifferent) {
505     return EFI_WRITE_PROTECTED;
506   }
507 
508   if (FileSizeIsDifferent) {
509     Status = SetFileSize (Instance, File, Info->FileSize);
510     if (EFI_ERROR (Status)) {
511       return Status;
512     }
513   }
514 
515   //
516   // Note down in RAM the Attribute field but we can not
517   // ask to store it in flash for the time being.
518   //
519   File->Info->Attribute = Info->Attribute;
520 
521   if (FileNameIsDifferent) {
522     Status = SetFileName (Instance, File, Info->FileName);
523     if (EFI_ERROR (Status)) {
524       return Status;
525     }
526   }
527 
528   return EFI_SUCCESS;
529 }
530 
531 EFIAPI
532 EFI_STATUS
BootMonFsGetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)533 BootMonFsGetInfo (
534   IN EFI_FILE_PROTOCOL  *This,
535   IN EFI_GUID           *InformationType,
536   IN OUT UINTN          *BufferSize,
537   OUT VOID              *Buffer
538   )
539 {
540   EFI_STATUS           Status;
541   BOOTMON_FS_FILE     *File;
542   BOOTMON_FS_INSTANCE *Instance;
543 
544   if ((This == NULL)                         ||
545       (InformationType == NULL)              ||
546       (BufferSize == NULL)                   ||
547       ((Buffer == NULL) && (*BufferSize > 0))  ) {
548     return EFI_INVALID_PARAMETER;
549   }
550 
551   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
552   if (File->Info == NULL) {
553     return EFI_INVALID_PARAMETER;
554   }
555   Instance = File->Instance;
556 
557   // If the instance has not been initialized yet then do it ...
558   if (!Instance->Initialized) {
559     Status = BootMonFsInitialize (Instance);
560   } else {
561     Status = EFI_SUCCESS;
562   }
563 
564   if (!EFI_ERROR (Status)) {
565     if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)
566         != 0) {
567       Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer);
568     } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
569       Status = GetFilesystemInfo (Instance, BufferSize, Buffer);
570     } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
571       Status = GetFileInfo (Instance, File, BufferSize, Buffer);
572     } else if (CompareGuid (InformationType, &gArmBootMonFsFileInfoGuid) != 0) {
573       Status = GetBootMonFsFileInfo (Instance, File, BufferSize, Buffer);
574     } else {
575       Status = EFI_UNSUPPORTED;
576     }
577   }
578 
579   return Status;
580 }
581 
582 /**
583   Set information about a file or a volume.
584 
585   @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that
586                                is the file handle the information is for.
587   @param[in]  InformationType  The type identifier for the information being set :
588                                EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
589                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID
590   @param[in]  BufferSize       The size, in bytes, of Buffer.
591   @param[in]  Buffer           A pointer to the data buffer to write. The type of the
592                                data inside the buffer is indicated by InformationType.
593 
594   @retval  EFI_SUCCESS            The information was set.
595   @retval  EFI_UNSUPPORTED        The InformationType is not known.
596   @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.
597   @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file
598                                   to a file that is already present.
599   @retval  EFI_ACCESS_DENIED      An attempt is being made to change the
600                                   EFI_FILE_DIRECTORY Attribute.
601   @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and
602                                   the file was opened in read-only mode and an
603                                   attempt is being made to modify a field other
604                                   than Attribute.
605   @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a read-only
606                                   attribute.
607   @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by
608                                   the data inside the buffer.
609   @retval  EFI_OUT_OF_RESOURCES   A allocation needed to process the request failed.
610   @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.
611 
612 **/
613 EFIAPI
614 EFI_STATUS
BootMonFsSetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN UINTN BufferSize,IN VOID * Buffer)615 BootMonFsSetInfo (
616   IN EFI_FILE_PROTOCOL  *This,
617   IN EFI_GUID           *InformationType,
618   IN UINTN              BufferSize,
619   IN VOID               *Buffer
620   )
621 {
622   BOOTMON_FS_FILE       *File;
623   EFI_FILE_INFO         *Info;
624   EFI_FILE_SYSTEM_INFO  *SystemInfo;
625 
626   if ((This == NULL)            ||
627       (InformationType == NULL) ||
628       (Buffer == NULL)             ) {
629     return EFI_INVALID_PARAMETER;
630   }
631 
632   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
633   if (File->Info == NULL) {
634     return EFI_INVALID_PARAMETER;
635   }
636 
637   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
638     Info = Buffer;
639     if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) {
640       return EFI_INVALID_PARAMETER;
641     }
642     if (BufferSize < Info->Size) {
643       return EFI_BAD_BUFFER_SIZE;
644     }
645     return (SetFileInfo (File->Instance, File, Info));
646   }
647 
648   //
649   // The only writable field in the other two information types
650   // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
651   // filesystem volume label. This can be retrieved with GetInfo, but it is
652   // hard-coded into this driver, not stored on media.
653   //
654 
655   if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
656     SystemInfo = Buffer;
657     if (SystemInfo->Size <
658         (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) {
659       return EFI_INVALID_PARAMETER;
660     }
661     if (BufferSize < SystemInfo->Size) {
662       return EFI_BAD_BUFFER_SIZE;
663     }
664     return EFI_WRITE_PROTECTED;
665   }
666 
667   if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
668     return EFI_WRITE_PROTECTED;
669   }
670 
671   return EFI_UNSUPPORTED;
672 }
673 
674 EFIAPI
675 EFI_STATUS
BootMonFsReadDirectory(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)676 BootMonFsReadDirectory (
677   IN EFI_FILE_PROTOCOL    *This,
678   IN OUT UINTN            *BufferSize,
679   OUT VOID                *Buffer
680   )
681 {
682   BOOTMON_FS_INSTANCE *Instance;
683   BOOTMON_FS_FILE     *RootFile;
684   BOOTMON_FS_FILE     *File;
685   EFI_FILE_INFO       *Info;
686   UINTN               NameSize;
687   UINTN               ResultSize;
688   EFI_STATUS          Status;
689   UINTN               Index;
690 
691   RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
692   if (RootFile == NULL) {
693     return EFI_INVALID_PARAMETER;
694   }
695 
696   Instance = RootFile->Instance;
697   Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File);
698   if (EFI_ERROR (Status)) {
699     // No more file
700     *BufferSize = 0;
701     return EFI_SUCCESS;
702   }
703 
704   NameSize   = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;
705   ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));
706   if (*BufferSize < ResultSize) {
707     *BufferSize = ResultSize;
708     return EFI_BUFFER_TOO_SMALL;
709   }
710 
711   // Zero out the structure
712   Info = Buffer;
713   ZeroMem (Info, ResultSize);
714 
715   // Fill in the structure
716   Info->Size         = ResultSize;
717   Info->FileSize     = BootMonFsGetImageLength (File);
718   Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
719   for (Index = 0; Index < NameSize; Index++) {
720     Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
721   }
722 
723   *BufferSize = ResultSize;
724   RootFile->Position++;
725 
726   return EFI_SUCCESS;
727 }
728 
729 EFIAPI
730 EFI_STATUS
BootMonFsFlushDirectory(IN EFI_FILE_PROTOCOL * This)731 BootMonFsFlushDirectory (
732   IN EFI_FILE_PROTOCOL  *This
733   )
734 {
735   BOOTMON_FS_FILE *RootFile;
736   LIST_ENTRY      *ListFiles;
737   LIST_ENTRY      *Link;
738   BOOTMON_FS_FILE *File;
739 
740   RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
741   if (RootFile == NULL) {
742     return EFI_INVALID_PARAMETER;
743   }
744 
745   ListFiles = &RootFile->Link;
746 
747   if (IsListEmpty (ListFiles)) {
748     return EFI_SUCCESS;
749   }
750 
751   //
752   // Flush all the files that need to be flushed
753   //
754 
755   // Go through all the list of files to flush them
756   for (Link = GetFirstNode (ListFiles);
757        !IsNull (ListFiles, Link);
758        Link = GetNextNode (ListFiles, Link)
759        )
760   {
761     File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link);
762     File->File.Flush (&File->File);
763   }
764 
765   return EFI_SUCCESS;
766 }
767