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   CHAR16           TruncFileName[MAX_NAME_LENGTH];
308   CHAR8            AsciiFileName[MAX_NAME_LENGTH];
309   BOOTMON_FS_FILE  *SameFile;
310 
311   // If the file path start with a \ strip it. The EFI Shell may
312   // insert a \ in front of the file name.
313   if (FileName[0] == L'\\') {
314     FileName++;
315   }
316 
317   StrnCpy (TruncFileName, FileName, MAX_NAME_LENGTH - 1);
318   TruncFileName[MAX_NAME_LENGTH - 1] = 0;
319   UnicodeStrToAsciiStr (TruncFileName, AsciiFileName);
320 
321   if (BootMonGetFileFromAsciiFileName (
322         File->Instance,
323         AsciiFileName,
324         &SameFile
325         ) != EFI_NOT_FOUND) {
326     // A file with that name already exists.
327     return EFI_ACCESS_DENIED;
328   } else {
329     // OK, change the filename.
330     AsciiStrToUnicodeStr (AsciiFileName, File->Info->FileName);
331     return EFI_SUCCESS;
332   }
333 }
334 
335 /**
336   Set the size of a file.
337 
338   This is a helper function for SetFileInfo().
339 
340   @param[in]  Instance  A pointer to the description of the volume
341                         the file belongs to.
342   @param[in]  File      A pointer to the description of the file.
343   @param[in]  NewSize   The requested new size for the file.
344 
345   @retval  EFI_SUCCESS           The size was set.
346   @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request failed.
347 
348 **/
349 STATIC
350 EFI_STATUS
SetFileSize(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * BootMonFsFile,IN UINTN NewSize)351 SetFileSize (
352   IN BOOTMON_FS_INSTANCE  *Instance,
353   IN BOOTMON_FS_FILE      *BootMonFsFile,
354   IN UINTN                 NewSize
355   )
356 {
357   EFI_STATUS              Status;
358   UINT32                  OldSize;
359   LIST_ENTRY              *RegionToFlushLink;
360   LIST_ENTRY              *NextRegionToFlushLink;
361   BOOTMON_FS_FILE_REGION  *Region;
362   EFI_FILE_PROTOCOL       *File;
363   CHAR8                   *Buffer;
364   UINTN                   BufferSize;
365   UINT64                  StoredPosition;
366 
367   OldSize = BootMonFsFile->Info->FileSize;
368 
369   //
370   // In case of file truncation, force the regions waiting for writing to
371   // not overflow the new size of the file.
372   //
373   if (NewSize < OldSize) {
374     for (RegionToFlushLink = GetFirstNode (&BootMonFsFile->RegionToFlushLink);
375          !IsNull (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
376          )
377     {
378       NextRegionToFlushLink = GetNextNode (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
379       Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
380       if (Region->Offset > NewSize) {
381         RemoveEntryList (RegionToFlushLink);
382         FreePool (Region->Buffer);
383         FreePool (Region);
384       } else {
385         Region->Size = MIN (Region->Size, NewSize - Region->Offset);
386       }
387       RegionToFlushLink = NextRegionToFlushLink;
388     }
389 
390   } else if (NewSize > OldSize) {
391     // Increasing a file's size is potentially complicated as it may require
392     // moving the image description on media. The simplest way to do it is to
393     // seek past the end of the file (which is valid in UEFI) and perform a
394     // Write.
395     File = &BootMonFsFile->File;
396 
397     // Save position
398     Status = File->GetPosition (File, &StoredPosition);
399     if (EFI_ERROR (Status)) {
400       return Status;
401     }
402     // Set position at the end of the file
403     Status = File->SetPosition (File, OldSize);
404     if (EFI_ERROR (Status)) {
405       return Status;
406     }
407 
408     BufferSize = NewSize - OldSize;
409     Buffer = AllocateZeroPool (BufferSize);
410     if (Buffer == NULL) {
411       return EFI_OUT_OF_RESOURCES;
412     }
413 
414     Status = File->Write (File, &BufferSize, Buffer);
415     FreePool (Buffer);
416     if (EFI_ERROR (Status)) {
417       return Status;
418     }
419 
420     // Restore saved position
421     Status = File->SetPosition (File, StoredPosition);
422     if (EFI_ERROR (Status)) {
423       return Status;
424     }
425   }
426 
427   BootMonFsFile->Info->FileSize = NewSize;
428 
429   return EFI_SUCCESS;
430 }
431 
432 /**
433   Set information about a file.
434 
435   @param[in]  Instance  A pointer to the description of the volume
436                         the file belongs to.
437   @param[in]  File      A pointer to the description of the file.
438   @param[in]  Info      A pointer to the file information to write.
439 
440   @retval  EFI_SUCCESS           The information was set.
441   @retval  EFI_ACCESS_DENIED     An attempt is being made to change the
442                                  EFI_FILE_DIRECTORY Attribute.
443   @retval  EFI_ACCESS_DENIED     The file was opened in read-only mode and an
444                                  attempt is being made to modify a field other
445                                  than Attribute.
446   @retval  EFI_ACCESS_DENIED     An attempt is made to change the name of a file
447                                  to a file that is already present.
448   @retval  EFI_WRITE_PROTECTED   An attempt is being made to modify a read-only
449                                  attribute.
450   @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request
451                                  failed.
452 
453 **/
454 STATIC
455 EFI_STATUS
SetFileInfo(IN BOOTMON_FS_INSTANCE * Instance,IN BOOTMON_FS_FILE * File,IN EFI_FILE_INFO * Info)456 SetFileInfo (
457   IN BOOTMON_FS_INSTANCE  *Instance,
458   IN BOOTMON_FS_FILE      *File,
459   IN EFI_FILE_INFO        *Info
460   )
461 {
462   EFI_STATUS  Status;
463   BOOLEAN     FileSizeIsDifferent;
464   BOOLEAN     FileNameIsDifferent;
465   BOOLEAN     TimeIsDifferent;
466 
467   //
468   // A directory can not be changed to a file and a file can
469   // not be changed to a directory.
470   //
471   if ((Info->Attribute & EFI_FILE_DIRECTORY)      !=
472       (File->Info->Attribute & EFI_FILE_DIRECTORY)  ) {
473     return EFI_ACCESS_DENIED;
474   }
475 
476   FileSizeIsDifferent = (Info->FileSize != File->Info->FileSize);
477   FileNameIsDifferent = (StrnCmp (
478                            Info->FileName,
479                            File->Info->FileName,
480                            MAX_NAME_LENGTH - 1
481                            ) != 0);
482   //
483   // Check if the CreateTime, LastAccess or ModificationTime
484   // have been changed. The file system does not support file
485   // timestamps thus the three times in "File->Info" are
486   // always equal to zero. The following comparison actually
487   // checks if all three times are still equal to 0 or not.
488   //
489   TimeIsDifferent = CompareMem (
490                       &Info->CreateTime,
491                       &File->Info->CreateTime,
492                       3 * sizeof (EFI_TIME)
493                       ) != 0;
494 
495   //
496   // For a file opened in read-only mode, only the Attribute field can be
497   // modified. The root directory open mode is forced to read-only at opening
498   // thus the following test protects the root directory to be somehow modified.
499   //
500   if (File->OpenMode == EFI_FILE_MODE_READ) {
501     if (FileSizeIsDifferent || FileNameIsDifferent || TimeIsDifferent) {
502       return EFI_ACCESS_DENIED;
503     }
504   }
505 
506   if (TimeIsDifferent) {
507     return EFI_WRITE_PROTECTED;
508   }
509 
510   if (FileSizeIsDifferent) {
511     Status = SetFileSize (Instance, File, Info->FileSize);
512     if (EFI_ERROR (Status)) {
513       return Status;
514     }
515   }
516 
517   //
518   // Note down in RAM the Attribute field but we can not
519   // ask to store it in flash for the time being.
520   //
521   File->Info->Attribute = Info->Attribute;
522 
523   if (FileNameIsDifferent) {
524     Status = SetFileName (Instance, File, Info->FileName);
525     if (EFI_ERROR (Status)) {
526       return Status;
527     }
528   }
529 
530   return EFI_SUCCESS;
531 }
532 
533 EFIAPI
534 EFI_STATUS
BootMonFsGetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)535 BootMonFsGetInfo (
536   IN EFI_FILE_PROTOCOL  *This,
537   IN EFI_GUID           *InformationType,
538   IN OUT UINTN          *BufferSize,
539   OUT VOID              *Buffer
540   )
541 {
542   EFI_STATUS           Status;
543   BOOTMON_FS_FILE     *File;
544   BOOTMON_FS_INSTANCE *Instance;
545 
546   if ((This == NULL)                         ||
547       (InformationType == NULL)              ||
548       (BufferSize == NULL)                   ||
549       ((Buffer == NULL) && (*BufferSize > 0))  ) {
550     return EFI_INVALID_PARAMETER;
551   }
552 
553   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
554   if (File->Info == NULL) {
555     return EFI_INVALID_PARAMETER;
556   }
557   Instance = File->Instance;
558 
559   // If the instance has not been initialized yet then do it ...
560   if (!Instance->Initialized) {
561     Status = BootMonFsInitialize (Instance);
562   } else {
563     Status = EFI_SUCCESS;
564   }
565 
566   if (!EFI_ERROR (Status)) {
567     if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)
568         != 0) {
569       Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer);
570     } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
571       Status = GetFilesystemInfo (Instance, BufferSize, Buffer);
572     } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
573       Status = GetFileInfo (Instance, File, BufferSize, Buffer);
574     } else if (CompareGuid (InformationType, &gArmBootMonFsFileInfoGuid) != 0) {
575       Status = GetBootMonFsFileInfo (Instance, File, BufferSize, Buffer);
576     } else {
577       Status = EFI_UNSUPPORTED;
578     }
579   }
580 
581   return Status;
582 }
583 
584 /**
585   Set information about a file or a volume.
586 
587   @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that
588                                is the file handle the information is for.
589   @param[in]  InformationType  The type identifier for the information being set :
590                                EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
591                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID
592   @param[in]  BufferSize       The size, in bytes, of Buffer.
593   @param[in]  Buffer           A pointer to the data buffer to write. The type of the
594                                data inside the buffer is indicated by InformationType.
595 
596   @retval  EFI_SUCCESS            The information was set.
597   @retval  EFI_UNSUPPORTED        The InformationType is not known.
598   @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.
599   @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file
600                                   to a file that is already present.
601   @retval  EFI_ACCESS_DENIED      An attempt is being made to change the
602                                   EFI_FILE_DIRECTORY Attribute.
603   @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and
604                                   the file was opened in read-only mode and an
605                                   attempt is being made to modify a field other
606                                   than Attribute.
607   @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a read-only
608                                   attribute.
609   @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by
610                                   the data inside the buffer.
611   @retval  EFI_OUT_OF_RESOURCES   A allocation needed to process the request failed.
612   @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.
613 
614 **/
615 EFIAPI
616 EFI_STATUS
BootMonFsSetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN UINTN BufferSize,IN VOID * Buffer)617 BootMonFsSetInfo (
618   IN EFI_FILE_PROTOCOL  *This,
619   IN EFI_GUID           *InformationType,
620   IN UINTN              BufferSize,
621   IN VOID               *Buffer
622   )
623 {
624   BOOTMON_FS_FILE       *File;
625   EFI_FILE_INFO         *Info;
626   EFI_FILE_SYSTEM_INFO  *SystemInfo;
627 
628   if ((This == NULL)            ||
629       (InformationType == NULL) ||
630       (Buffer == NULL)             ) {
631     return EFI_INVALID_PARAMETER;
632   }
633 
634   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
635   if (File->Info == NULL) {
636     return EFI_INVALID_PARAMETER;
637   }
638 
639   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
640     Info = Buffer;
641     if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) {
642       return EFI_INVALID_PARAMETER;
643     }
644     if (BufferSize < Info->Size) {
645       return EFI_BAD_BUFFER_SIZE;
646     }
647     return (SetFileInfo (File->Instance, File, Info));
648   }
649 
650   //
651   // The only writable field in the other two information types
652   // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
653   // filesystem volume label. This can be retrieved with GetInfo, but it is
654   // hard-coded into this driver, not stored on media.
655   //
656 
657   if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
658     SystemInfo = Buffer;
659     if (SystemInfo->Size <
660         (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) {
661       return EFI_INVALID_PARAMETER;
662     }
663     if (BufferSize < SystemInfo->Size) {
664       return EFI_BAD_BUFFER_SIZE;
665     }
666     return EFI_WRITE_PROTECTED;
667   }
668 
669   if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
670     return EFI_WRITE_PROTECTED;
671   }
672 
673   return EFI_UNSUPPORTED;
674 }
675 
676 EFIAPI
677 EFI_STATUS
BootMonFsReadDirectory(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)678 BootMonFsReadDirectory (
679   IN EFI_FILE_PROTOCOL    *This,
680   IN OUT UINTN            *BufferSize,
681   OUT VOID                *Buffer
682   )
683 {
684   BOOTMON_FS_INSTANCE *Instance;
685   BOOTMON_FS_FILE     *RootFile;
686   BOOTMON_FS_FILE     *File;
687   EFI_FILE_INFO       *Info;
688   UINTN               NameSize;
689   UINTN               ResultSize;
690   EFI_STATUS          Status;
691   UINTN               Index;
692 
693   RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
694   if (RootFile == NULL) {
695     return EFI_INVALID_PARAMETER;
696   }
697 
698   Instance = RootFile->Instance;
699   Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File);
700   if (EFI_ERROR (Status)) {
701     // No more file
702     *BufferSize = 0;
703     return EFI_SUCCESS;
704   }
705 
706   NameSize   = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;
707   ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));
708   if (*BufferSize < ResultSize) {
709     *BufferSize = ResultSize;
710     return EFI_BUFFER_TOO_SMALL;
711   }
712 
713   // Zero out the structure
714   Info = Buffer;
715   ZeroMem (Info, ResultSize);
716 
717   // Fill in the structure
718   Info->Size         = ResultSize;
719   Info->FileSize     = BootMonFsGetImageLength (File);
720   Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
721   for (Index = 0; Index < NameSize; Index++) {
722     Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
723   }
724 
725   *BufferSize = ResultSize;
726   RootFile->Position++;
727 
728   return EFI_SUCCESS;
729 }
730 
731 EFIAPI
732 EFI_STATUS
BootMonFsFlushDirectory(IN EFI_FILE_PROTOCOL * This)733 BootMonFsFlushDirectory (
734   IN EFI_FILE_PROTOCOL  *This
735   )
736 {
737   BOOTMON_FS_FILE *RootFile;
738   LIST_ENTRY      *ListFiles;
739   LIST_ENTRY      *Link;
740   BOOTMON_FS_FILE *File;
741 
742   RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
743   if (RootFile == NULL) {
744     return EFI_INVALID_PARAMETER;
745   }
746 
747   ListFiles = &RootFile->Link;
748 
749   if (IsListEmpty (ListFiles)) {
750     return EFI_SUCCESS;
751   }
752 
753   //
754   // Flush all the files that need to be flushed
755   //
756 
757   // Go through all the list of files to flush them
758   for (Link = GetFirstNode (ListFiles);
759        !IsNull (ListFiles, Link);
760        Link = GetNextNode (ListFiles, Link)
761        )
762   {
763     File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link);
764     File->File.Flush (&File->File);
765   }
766 
767   return EFI_SUCCESS;
768 }
769