1 /** @file
2 *
3 *  Copyright (c) 2012-2015, 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 // Clear a file's image description on storage media:
18 // UEFI allows you to seek past the end of a file, a subsequent write will grow
19 // the file. It does not specify how space between the former end of the file
20 // and the beginning of the write should be filled. It's therefore possible that
21 // BootMonFs metadata, that comes after the end of a file, could be left there
22 // and wrongly detected by BootMonFsImageInBlock.
23 STATIC
24 EFI_STATUS
InvalidateImageDescription(IN BOOTMON_FS_FILE * File)25 InvalidateImageDescription (
26   IN  BOOTMON_FS_FILE  *File
27   )
28 {
29   EFI_DISK_IO_PROTOCOL   *DiskIo;
30   EFI_BLOCK_IO_PROTOCOL  *BlockIo;
31   UINT32                  MediaId;
32   VOID                   *Buffer;
33   EFI_STATUS              Status;
34 
35   DiskIo = File->Instance->DiskIo;
36   BlockIo = File->Instance->BlockIo;
37   MediaId = BlockIo->Media->MediaId;
38 
39   Buffer = AllocateZeroPool (sizeof (HW_IMAGE_DESCRIPTION));
40 
41   if (Buffer == NULL) {
42     return EFI_OUT_OF_RESOURCES;
43   }
44 
45   Status = DiskIo->WriteDisk (DiskIo,
46                     MediaId,
47                     File->HwDescAddress,
48                     sizeof (HW_IMAGE_DESCRIPTION),
49                     Buffer
50                     );
51 
52   FreePool(Buffer);
53 
54   return Status;
55 }
56 
57 /**
58   Write the description of a file to storage media.
59 
60   This function uses DiskIo to write to the media, so call BlockIo->FlushBlocks()
61   after calling it to ensure the data are written on the media.
62 
63   @param[in]  File       Description of the file whose description on the
64                          storage media has to be updated.
65   @param[in]  FileName   Name of the file. Its length is assumed to be
66                          lower than MAX_NAME_LENGTH.
67   @param[in]  DataSize   Number of data bytes of the file.
68   @param[in]  FileStart  File's starting position on media. FileStart must
69                          be aligned to the media's block size.
70 
71   @retval  EFI_WRITE_PROTECTED  The device cannot be written to.
72   @retval  EFI_DEVICE_ERROR     The device reported an error while performing
73                                 the write operation.
74 
75 **/
76 STATIC
77 EFI_STATUS
WriteFileDescription(IN BOOTMON_FS_FILE * File,IN CHAR8 * FileName,IN UINT32 DataSize,IN UINT64 FileStart)78 WriteFileDescription (
79   IN  BOOTMON_FS_FILE  *File,
80   IN  CHAR8            *FileName,
81   IN  UINT32            DataSize,
82   IN  UINT64            FileStart
83   )
84 {
85   EFI_STATUS            Status;
86   EFI_DISK_IO_PROTOCOL  *DiskIo;
87   UINTN                 BlockSize;
88   UINT32                FileSize;
89   HW_IMAGE_DESCRIPTION  *Description;
90 
91   DiskIo    = File->Instance->DiskIo;
92   BlockSize = File->Instance->BlockIo->Media->BlockSize;
93   ASSERT (FileStart % BlockSize == 0);
94 
95   //
96   // Construct the file description
97   //
98 
99   FileSize = DataSize + sizeof (HW_IMAGE_DESCRIPTION);
100   Description = &File->HwDescription;
101   Description->Attributes = 1;
102   Description->BlockStart = FileStart / BlockSize;
103   Description->BlockEnd   = Description->BlockStart + (FileSize / BlockSize);
104   AsciiStrCpyS (Description->Footer.Filename,
105     sizeof Description->Footer.Filename, FileName);
106 
107 #ifdef MDE_CPU_ARM
108   Description->Footer.Offset  = HW_IMAGE_FOOTER_OFFSET;
109   Description->Footer.Version = HW_IMAGE_FOOTER_VERSION;
110 #else
111   Description->Footer.Offset  = HW_IMAGE_FOOTER_OFFSET2;
112   Description->Footer.Version = HW_IMAGE_FOOTER_VERSION2;
113 #endif
114   Description->Footer.FooterSignature1 = HW_IMAGE_FOOTER_SIGNATURE_1;
115   Description->Footer.FooterSignature2 = HW_IMAGE_FOOTER_SIGNATURE_2;
116   Description->RegionCount = 1;
117   Description->Region[0].Checksum = 0;
118   Description->Region[0].Offset = Description->BlockStart * BlockSize;
119   Description->Region[0].Size = DataSize;
120 
121   Status = BootMonFsComputeFooterChecksum (Description);
122   if (EFI_ERROR (Status)) {
123     return Status;
124   }
125 
126   File->HwDescAddress = ((Description->BlockEnd + 1) * BlockSize) - sizeof (HW_IMAGE_DESCRIPTION);
127 
128   // Update the file description on the media
129   Status = DiskIo->WriteDisk (
130                     DiskIo,
131                     File->Instance->Media->MediaId,
132                     File->HwDescAddress,
133                     sizeof (HW_IMAGE_DESCRIPTION),
134                     Description
135                     );
136   ASSERT_EFI_ERROR (Status);
137 
138   return Status;
139 }
140 
141 // Find a space on media for a file that has not yet been flushed to disk.
142 // Just returns the first space that's big enough.
143 // This function could easily be adapted to:
144 // - Find space for moving an existing file that has outgrown its space
145 //   (We do not currently move files, just return EFI_VOLUME_FULL)
146 // - Find space for a fragment of a file that has outgrown its space
147 //   (We do not currently fragment files - it's not clear whether fragmentation
148 //    is actually part of BootMonFs as there is no spec)
149 // - Be more clever about finding space (choosing the largest or smallest
150 //   suitable space)
151 // Parameters:
152 // File - the new (not yet flushed) file for which we need to find space.
153 // FileStart - the position on media of the file (in bytes).
154 STATIC
155 EFI_STATUS
BootMonFsFindSpaceForNewFile(IN BOOTMON_FS_FILE * File,IN UINT64 FileSize,OUT UINT64 * FileStart)156 BootMonFsFindSpaceForNewFile (
157   IN  BOOTMON_FS_FILE     *File,
158   IN  UINT64              FileSize,
159   OUT UINT64              *FileStart
160   )
161 {
162   LIST_ENTRY              *FileLink;
163   BOOTMON_FS_FILE         *RootFile;
164   BOOTMON_FS_FILE         *FileEntry;
165   UINTN                    BlockSize;
166   EFI_BLOCK_IO_MEDIA      *Media;
167 
168   Media = File->Instance->BlockIo->Media;
169   BlockSize = Media->BlockSize;
170   RootFile = File->Instance->RootFile;
171 
172   // This function must only be called for file which has not been flushed into
173   // Flash yet
174   ASSERT (File->HwDescription.RegionCount == 0);
175 
176   *FileStart = 0;
177   // Go through all the files in the list
178   for (FileLink = GetFirstNode (&RootFile->Link);
179          !IsNull (&RootFile->Link, FileLink);
180          FileLink = GetNextNode (&RootFile->Link, FileLink)
181          )
182   {
183     FileEntry = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
184     // Skip files that aren't on disk yet
185     if (FileEntry->HwDescription.RegionCount == 0) {
186       continue;
187     }
188 
189     // If the free space preceding the file is big enough to contain the new
190     // file then use it!
191     if (((FileEntry->HwDescription.BlockStart * BlockSize) - *FileStart)
192         >= FileSize) {
193       // The file list must be in disk-order
194       RemoveEntryList (&File->Link);
195       File->Link.BackLink = FileLink->BackLink;
196       File->Link.ForwardLink = FileLink;
197       FileLink->BackLink->ForwardLink = &File->Link;
198       FileLink->BackLink = &File->Link;
199 
200       return EFI_SUCCESS;
201     } else {
202       *FileStart = (FileEntry->HwDescription.BlockEnd + 1) * BlockSize;
203     }
204   }
205   // See if there's space after the last file
206   if ((((Media->LastBlock + 1) * BlockSize) - *FileStart) >= FileSize) {
207     return EFI_SUCCESS;
208   } else {
209     return EFI_VOLUME_FULL;
210   }
211 }
212 
213 // Free the resources in the file's Region list.
214 STATIC
215 VOID
FreeFileRegions(IN BOOTMON_FS_FILE * File)216 FreeFileRegions (
217   IN  BOOTMON_FS_FILE *File
218   )
219 {
220   LIST_ENTRY              *RegionToFlushLink;
221   BOOTMON_FS_FILE_REGION  *Region;
222 
223   RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
224   while (!IsNull (&File->RegionToFlushLink, RegionToFlushLink)) {
225     // Repeatedly remove the first node from the list and free its resources.
226     Region = (BOOTMON_FS_FILE_REGION *) RegionToFlushLink;
227     RemoveEntryList (RegionToFlushLink);
228     FreePool (Region->Buffer);
229     FreePool (Region);
230 
231     RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
232   }
233 }
234 
235 /**
236   Flush all modified data associated with a file to a device.
237 
238   @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the
239                     file handle to flush.
240 
241   @retval  EFI_SUCCESS            The data was flushed.
242   @retval  EFI_ACCESS_DENIED      The file was opened read-only.
243   @retval  EFI_DEVICE_ERROR       The device reported an error.
244   @retval  EFI_VOLUME_FULL        The volume is full.
245   @retval  EFI_OUT_OF_RESOURCES   Not enough resources were available to flush the data.
246   @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.
247 
248 **/
249 EFIAPI
250 EFI_STATUS
BootMonFsFlushFile(IN EFI_FILE_PROTOCOL * This)251 BootMonFsFlushFile (
252   IN EFI_FILE_PROTOCOL  *This
253   )
254 {
255   EFI_STATUS               Status;
256   BOOTMON_FS_INSTANCE     *Instance;
257   EFI_FILE_INFO           *Info;
258   EFI_BLOCK_IO_PROTOCOL   *BlockIo;
259   EFI_BLOCK_IO_MEDIA      *Media;
260   EFI_DISK_IO_PROTOCOL    *DiskIo;
261   UINTN                    BlockSize;
262   CHAR8                    AsciiFileName[MAX_NAME_LENGTH];
263   LIST_ENTRY              *RegionToFlushLink;
264   BOOTMON_FS_FILE         *File;
265   BOOTMON_FS_FILE         *NextFile;
266   BOOTMON_FS_FILE_REGION  *Region;
267   LIST_ENTRY              *FileLink;
268   UINTN                    CurrentPhysicalSize;
269   UINT64                   FileStart;
270   UINT64                   FileEnd;
271   UINT64                   RegionStart;
272   UINT64                   RegionEnd;
273   UINT64                   NewDataSize;
274   UINT64                   NewFileSize;
275   UINT64                   EndOfAppendSpace;
276   BOOLEAN                  HasSpace;
277 
278   if (This == NULL) {
279     return EFI_INVALID_PARAMETER;
280   }
281 
282   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
283   if (File->Info == NULL) {
284     return EFI_INVALID_PARAMETER;
285   }
286 
287   if (File->OpenMode == EFI_FILE_MODE_READ) {
288     return EFI_ACCESS_DENIED;
289   }
290 
291   Instance  = File->Instance;
292   Info      = File->Info;
293   BlockIo   = Instance->BlockIo;
294   Media     = BlockIo->Media;
295   DiskIo    = Instance->DiskIo;
296   BlockSize = Media->BlockSize;
297 
298   UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, MAX_NAME_LENGTH);
299 
300   // If the file doesn't exist then find a space for it
301   if (File->HwDescription.RegionCount == 0) {
302     Status = BootMonFsFindSpaceForNewFile (
303                File,
304                Info->FileSize + sizeof (HW_IMAGE_DESCRIPTION),
305                &FileStart
306                );
307     if (EFI_ERROR (Status)) {
308       return Status;
309     }
310   } else {
311     FileStart = File->HwDescription.BlockStart * BlockSize;
312   }
313   // FileEnd is the current NOR address of the end of the file's data
314   FileEnd = FileStart + File->HwDescription.Region[0].Size;
315 
316   for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
317        !IsNull (&File->RegionToFlushLink, RegionToFlushLink);
318        RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
319        )
320   {
321     Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
322     if (Region->Size == 0) {
323       continue;
324     }
325 
326     // RegionStart and RegionEnd are the the intended NOR address of the
327     // start and end of the region
328     RegionStart = FileStart   + Region->Offset;
329     RegionEnd   = RegionStart + Region->Size;
330 
331     if (RegionEnd < FileEnd) {
332       // Handle regions representing edits to existing portions of the file
333       // Write the region data straight into the file
334       Status = DiskIo->WriteDisk (DiskIo,
335                         Media->MediaId,
336                         RegionStart,
337                         Region->Size,
338                         Region->Buffer
339                         );
340       if (EFI_ERROR (Status)) {
341         return Status;
342       }
343     } else {
344       // Handle regions representing appends to the file
345       //
346       // Note: Since seeking past the end of the file with SetPosition() is
347       //  valid, it's possible there will be a gap between the current end of
348       //  the file and the beginning of the new region. Since the UEFI spec
349       //  says nothing about this case (except "a subsequent write would grow
350       //  the file"), we just leave garbage in the gap.
351 
352       // Check if there is space to append the new region
353       HasSpace = FALSE;
354       NewDataSize = RegionEnd - FileStart;
355       NewFileSize = NewDataSize + sizeof (HW_IMAGE_DESCRIPTION);
356       CurrentPhysicalSize = BootMonFsGetPhysicalSize (File);
357       if (NewFileSize <= CurrentPhysicalSize) {
358         HasSpace = TRUE;
359       } else {
360         // Get the File Description for the next file
361         FileLink = GetNextNode (&Instance->RootFile->Link, &File->Link);
362         if (!IsNull (&Instance->RootFile->Link, FileLink)) {
363           NextFile = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
364 
365           // If there is space between the beginning of the current file and the
366           // beginning of the next file then use it
367           EndOfAppendSpace = NextFile->HwDescription.BlockStart * BlockSize;
368         } else {
369           // We are flushing the last file.
370           EndOfAppendSpace = (Media->LastBlock + 1) * BlockSize;
371         }
372         if (EndOfAppendSpace - FileStart >= NewFileSize) {
373           HasSpace = TRUE;
374         }
375       }
376 
377       if (HasSpace == TRUE) {
378         // Invalidate the current image description of the file if any.
379         if (File->HwDescAddress != 0) {
380           Status = InvalidateImageDescription (File);
381           if (EFI_ERROR (Status)) {
382             return Status;
383           }
384         }
385 
386         // Write the new file data
387         Status = DiskIo->WriteDisk (
388                     DiskIo,
389                     Media->MediaId,
390                     RegionStart,
391                     Region->Size,
392                     Region->Buffer
393                     );
394         if (EFI_ERROR (Status)) {
395           return Status;
396         }
397 
398         Status = WriteFileDescription (File, AsciiFileName, NewDataSize, FileStart);
399         if (EFI_ERROR (Status)) {
400           return Status;
401         }
402 
403       } else {
404         // There isn't a space for the file.
405         // Options here are to move the file or fragment it. However as files
406         // may represent boot images at fixed positions, these options will
407         // break booting if the bootloader doesn't use BootMonFs to find the
408         // image.
409 
410         return EFI_VOLUME_FULL;
411       }
412     }
413   }
414 
415   FreeFileRegions (File);
416   Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
417 
418   if ((AsciiStrCmp (AsciiFileName, File->HwDescription.Footer.Filename) != 0) ||
419       (Info->FileSize != File->HwDescription.Region[0].Size)               ) {
420     Status = WriteFileDescription (File, AsciiFileName, Info->FileSize, FileStart);
421     if (EFI_ERROR (Status)) {
422       return Status;
423     }
424   }
425 
426   // Flush DiskIo Buffers (see UEFI Spec 12.7 - DiskIo buffers are flushed by
427   // calling FlushBlocks on the same device's BlockIo).
428   BlockIo->FlushBlocks (BlockIo);
429 
430   return EFI_SUCCESS;
431 }
432 
433 /**
434   Close a specified file handle.
435 
436   @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
437                     handle to close.
438 
439   @retval  EFI_SUCCESS            The file was closed.
440   @retval  EFI_INVALID_PARAMETER  The parameter "This" is NULL or is not an open
441                                   file handle.
442 
443 **/
444 EFIAPI
445 EFI_STATUS
BootMonFsCloseFile(IN EFI_FILE_PROTOCOL * This)446 BootMonFsCloseFile (
447   IN EFI_FILE_PROTOCOL  *This
448   )
449 {
450   BOOTMON_FS_FILE  *File;
451 
452   if (This == NULL) {
453     return EFI_INVALID_PARAMETER;
454   }
455 
456   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
457   if (File->Info == NULL) {
458     return EFI_INVALID_PARAMETER;
459   }
460 
461   // In the case of a file and not the root directory
462   if (This != &File->Instance->RootFile->File) {
463     This->Flush (This);
464     FreePool (File->Info);
465     File->Info = NULL;
466   }
467 
468   return EFI_SUCCESS;
469 }
470 
471 /**
472   Open a file on the boot monitor file system.
473 
474   The boot monitor file system does not allow for sub-directories. There is only
475   one directory, the root one. On any attempt to create a directory, the function
476   returns in error with the EFI_WRITE_PROTECTED error code.
477 
478   @param[in]   This        A pointer to the EFI_FILE_PROTOCOL instance that is
479                            the file handle to source location.
480   @param[out]  NewHandle   A pointer to the location to return the opened
481                            handle for the new file.
482   @param[in]   FileName    The Null-terminated string of the name of the file
483                            to be opened.
484   @param[in]   OpenMode    The mode to open the file : Read or Read/Write or
485                            Read/Write/Create
486   @param[in]   Attributes  Attributes of the file in case of a file creation
487 
488   @retval  EFI_SUCCESS            The file was open.
489   @retval  EFI_NOT_FOUND          The specified file could not be found or the specified
490                                   directory in which to create a file could not be found.
491   @retval  EFI_DEVICE_ERROR       The device reported an error.
492   @retval  EFI_WRITE_PROTECTED    Attempt to create a directory. This is not possible
493                                   with the Boot Monitor file system.
494   @retval  EFI_OUT_OF_RESOURCES   Not enough resources were available to open the file.
495   @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.
496 
497 **/
498 EFIAPI
499 EFI_STATUS
BootMonFsOpenFile(IN EFI_FILE_PROTOCOL * This,OUT EFI_FILE_PROTOCOL ** NewHandle,IN CHAR16 * FileName,IN UINT64 OpenMode,IN UINT64 Attributes)500 BootMonFsOpenFile (
501   IN EFI_FILE_PROTOCOL   *This,
502   OUT EFI_FILE_PROTOCOL  **NewHandle,
503   IN CHAR16              *FileName,
504   IN UINT64              OpenMode,
505   IN UINT64              Attributes
506   )
507 {
508   EFI_STATUS           Status;
509   BOOTMON_FS_FILE      *Directory;
510   BOOTMON_FS_FILE      *File;
511   BOOTMON_FS_INSTANCE  *Instance;
512   CHAR8                *Buf;
513   CHAR16               *Path;
514   CHAR16               *Separator;
515   CHAR8                *AsciiFileName;
516   EFI_FILE_INFO        *Info;
517   UINTN                AsciiFileNameSize;
518 
519   if (This == NULL) {
520     return EFI_INVALID_PARAMETER;
521   }
522 
523   Directory = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
524   if (Directory->Info == NULL) {
525     return EFI_INVALID_PARAMETER;
526   }
527 
528   if ((FileName == NULL) || (NewHandle == NULL)) {
529     return EFI_INVALID_PARAMETER;
530   }
531 
532   //
533   // The only valid modes are read, read/write, and read/write/create
534   //
535   if ( (OpenMode != EFI_FILE_MODE_READ) &&
536        (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) &&
537        (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE)) ) {
538     return EFI_INVALID_PARAMETER;
539   }
540 
541   Instance = Directory->Instance;
542 
543   //
544   // If the instance has not been initialized yet then do it ...
545   //
546   if (!Instance->Initialized) {
547     Status = BootMonFsInitialize (Instance);
548     if (EFI_ERROR (Status)) {
549       return Status;
550     }
551   }
552 
553   //
554   // Copy the file path to be able to work on it. We do not want to
555   // modify the input file name string "FileName".
556   //
557   Buf = AllocateCopyPool (StrSize (FileName), FileName);
558   if (Buf == NULL) {
559     return EFI_OUT_OF_RESOURCES;
560   }
561   Path = (CHAR16*)Buf;
562   AsciiFileName = NULL;
563   Info = NULL;
564 
565   //
566   // Handle single periods, double periods and convert forward slashes '/'
567   // to backward '\' ones. Does not handle a '.' at the beginning of the
568   // path for the time being.
569   //
570   if (PathCleanUpDirectories (Path) == NULL) {
571     Status = EFI_INVALID_PARAMETER;
572     goto Error;
573   }
574 
575   //
576   // Detect if the first component of the path refers to a directory.
577   // This is done to return the correct error code when trying to
578   // access or create a directory other than the root directory.
579   //
580 
581   //
582   // Search for the '\\' sequence and if found return in error
583   // with the EFI_INVALID_PARAMETER error code. ere in the path.
584   //
585   if (StrStr (Path, L"\\\\") != NULL) {
586     Status = EFI_INVALID_PARAMETER;
587     goto Error;
588   }
589   //
590   // Get rid of the leading '\' if any.
591   //
592   Path += (Path[0] == L'\\');
593 
594   //
595   // Look for a '\' in the file path. If one is found then
596   // the first component of the path refers to a directory
597   // that is not the root directory.
598   //
599   Separator = StrStr (Path, L"\\");
600   if (Separator != NULL) {
601     //
602     // In the case '<dir name>\' and a creation, return
603     // EFI_WRITE_PROTECTED if this is for a directory
604     // creation, EFI_INVALID_PARAMETER otherwise.
605     //
606     if ((*(Separator + 1) == '\0') && ((OpenMode & EFI_FILE_MODE_CREATE) != 0)) {
607       if (Attributes & EFI_FILE_DIRECTORY) {
608         Status = EFI_WRITE_PROTECTED;
609       } else {
610         Status = EFI_INVALID_PARAMETER;
611       }
612     } else {
613       //
614       // Attempt to open a file or a directory that is not in the
615       // root directory or to open without creation a directory
616       // located in the root directory, returns EFI_NOT_FOUND.
617       //
618       Status = EFI_NOT_FOUND;
619     }
620     goto Error;
621   }
622 
623   //
624   // BootMonFs interface requires ASCII filenames
625   //
626   AsciiFileNameSize = StrLen (Path) + 1;
627   if (AsciiFileNameSize > MAX_NAME_LENGTH) {
628     AsciiFileNameSize = MAX_NAME_LENGTH;
629   }
630   AsciiFileName = AllocatePool (AsciiFileNameSize);
631   if (AsciiFileName == NULL) {
632     Status = EFI_OUT_OF_RESOURCES;
633     goto Error;
634   }
635   UnicodeStrToAsciiStrS (Path, AsciiFileName, AsciiFileNameSize);
636 
637   if ((AsciiFileName[0] == '\0') ||
638       (AsciiFileName[0] == '.' )    ) {
639     //
640     // Opening the root directory
641     //
642 
643     *NewHandle = &Instance->RootFile->File;
644     Instance->RootFile->Position = 0;
645     Status = EFI_SUCCESS;
646   } else {
647 
648     if ((OpenMode & EFI_FILE_MODE_CREATE) &&
649         (Attributes & EFI_FILE_DIRECTORY)    ) {
650       Status = EFI_WRITE_PROTECTED;
651       goto Error;
652     }
653 
654     //
655     // Allocate a buffer to store the characteristics of the file while the
656     // file is open. We allocate the maximum size to not have to reallocate
657     // if the file name is changed.
658     //
659     Info = AllocateZeroPool (
660              SIZE_OF_EFI_FILE_INFO + (sizeof (CHAR16) * MAX_NAME_LENGTH));
661     if (Info == NULL) {
662       Status = EFI_OUT_OF_RESOURCES;
663       goto Error;
664     }
665 
666     //
667     // Open or create a file in the root directory.
668     //
669 
670     Status = BootMonGetFileFromAsciiFileName (Instance, AsciiFileName, &File);
671     if (Status == EFI_NOT_FOUND) {
672       if ((OpenMode & EFI_FILE_MODE_CREATE) == 0) {
673         goto Error;
674       }
675 
676       Status = BootMonFsCreateFile (Instance, &File);
677       if (EFI_ERROR (Status)) {
678         goto Error;
679       }
680       InsertHeadList (&Instance->RootFile->Link, &File->Link);
681       Info->Attribute = Attributes;
682     } else {
683       //
684       // File already open, not supported yet.
685       //
686       if (File->Info != NULL) {
687         Status = EFI_UNSUPPORTED;
688         goto Error;
689       }
690     }
691 
692     Info->FileSize     = BootMonFsGetImageLength (File);
693     Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
694     AsciiStrToUnicodeStrS (AsciiFileName, Info->FileName, MAX_NAME_LENGTH);
695 
696     File->Info = Info;
697     Info = NULL;
698     File->Position = 0;
699     File->OpenMode = OpenMode;
700 
701     *NewHandle = &File->File;
702   }
703 
704 Error:
705 
706   FreePool (Buf);
707   if (AsciiFileName != NULL) {
708     FreePool (AsciiFileName);
709   }
710   if (Info != NULL) {
711     FreePool (Info);
712   }
713 
714   return Status;
715 }
716 
717 // Delete() for the root directory's EFI_FILE_PROTOCOL instance
718 EFIAPI
719 EFI_STATUS
BootMonFsDeleteFail(IN EFI_FILE_PROTOCOL * This)720 BootMonFsDeleteFail (
721   IN EFI_FILE_PROTOCOL *This
722   )
723 {
724   This->Close(This);
725   // You can't delete the root directory
726   return EFI_WARN_DELETE_FAILURE;
727 }
728 
729 /**
730   Close and delete a file from the boot monitor file system.
731 
732   @param[in]  This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
733                     handle to delete.
734 
735   @retval  EFI_SUCCESS              The file was closed and deleted.
736   @retval  EFI_INVALID_PARAMETER    The parameter "This" is NULL or is not an open
737                                     file handle.
738   @retval  EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not deleted.
739 
740 **/
741 EFIAPI
742 EFI_STATUS
BootMonFsDelete(IN EFI_FILE_PROTOCOL * This)743 BootMonFsDelete (
744   IN EFI_FILE_PROTOCOL *This
745   )
746 {
747   EFI_STATUS               Status;
748   BOOTMON_FS_FILE         *File;
749   LIST_ENTRY              *RegionToFlushLink;
750   BOOTMON_FS_FILE_REGION  *Region;
751 
752   if (This == NULL) {
753     return EFI_INVALID_PARAMETER;
754   }
755 
756   File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
757   if (File->Info == NULL) {
758     return EFI_INVALID_PARAMETER;
759   }
760 
761   if (!IsListEmpty (&File->RegionToFlushLink)) {
762     // Free the entries from the Buffer List
763     RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
764     do {
765       Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
766 
767       //
768       // Get next element of the list before deleting the region description
769       // that contain the LIST_ENTRY structure.
770       //
771       RegionToFlushLink = RemoveEntryList (RegionToFlushLink);
772 
773       // Free the buffers
774       FreePool (Region->Buffer);
775       FreePool (Region);
776     } while (!IsListEmpty (&File->RegionToFlushLink));
777   }
778 
779   // If (RegionCount is greater than 0) then the file already exists
780   if (File->HwDescription.RegionCount > 0) {
781     // Invalidate the last Block
782     Status = InvalidateImageDescription (File);
783     ASSERT_EFI_ERROR (Status);
784     if (EFI_ERROR (Status)) {
785       return  EFI_WARN_DELETE_FAILURE;
786     }
787   }
788 
789   // Remove the entry from the list
790   RemoveEntryList (&File->Link);
791   FreePool (File->Info);
792   FreePool (File);
793 
794   return EFI_SUCCESS;
795 }
796