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