Home | History | Annotate | Download | only in BootMonFs
      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
     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
     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
    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
    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
    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
    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
    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
    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
    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