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   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
    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
    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
    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
    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
    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
    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
    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