/** @file
*
*  Copyright (c) 2012-2014, ARM Limited. All rights reserved.
*
*  This program and the accompanying materials
*  are licensed and made available under the terms and conditions of the BSD License
*  which accompanies this distribution.  The full text of the license may be found at
*  http://opensource.org/licenses/bsd-license.php
*
*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
**/

#include "BootMonFsInternal.h"

EFIAPI
EFI_STATUS
OpenBootMonFsOpenVolume (
  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
  OUT EFI_FILE_PROTOCOL              **Root
  )
{
  BOOTMON_FS_INSTANCE *Instance;

  Instance = BOOTMON_FS_FROM_FS_THIS (This);
  if (Instance == NULL) {
    return EFI_DEVICE_ERROR;
  }

  Instance->RootFile->Info->Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;

  *Root = &Instance->RootFile->File;

  return EFI_SUCCESS;
}

UINT32
BootMonFsGetImageLength (
  IN BOOTMON_FS_FILE      *File
  )
{
  UINT32                   Index;
  UINT32                   FileSize;
  LIST_ENTRY              *RegionToFlushLink;
  BOOTMON_FS_FILE_REGION  *Region;

  FileSize = 0;

  // Look at all Flash areas to determine file size
  for (Index = 0; Index < HW_IMAGE_DESCRIPTION_REGION_MAX; Index++) {
    FileSize += File->HwDescription.Region[Index].Size;
  }

  // Add the regions that have not been flushed yet
  for (RegionToFlushLink = GetFirstNode (&File->RegionToFlushLink);
       !IsNull (&File->RegionToFlushLink, RegionToFlushLink);
       RegionToFlushLink = GetNextNode (&File->RegionToFlushLink, RegionToFlushLink)
       )
  {
    Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
    if (Region->Offset + Region->Size > FileSize) {
      FileSize += Region->Offset + Region->Size;
    }
  }

  return FileSize;
}

UINTN
BootMonFsGetPhysicalSize (
  IN BOOTMON_FS_FILE* File
  )
{
  // Return 0 for files that haven't yet been flushed to media
  if (File->HwDescription.RegionCount == 0) {
    return 0;
  }

  return ((File->HwDescription.BlockEnd - File->HwDescription.BlockStart) + 1 )
          * File->Instance->Media->BlockSize;
}

EFIAPI
EFI_STATUS
BootMonFsSetDirPosition (
  IN EFI_FILE_PROTOCOL  *This,
  IN UINT64             Position
  )
{
  BOOTMON_FS_FILE       *File;

  File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
  if (File == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  // UEFI Spec section 12.5:
  // "The seek request for nonzero is not valid on open directories."
  if (Position != 0) {
    return EFI_UNSUPPORTED;
  }
  File->Position = Position;

  return EFI_SUCCESS;
}

EFI_STATUS
BootMonFsOpenDirectory (
  OUT EFI_FILE_PROTOCOL **NewHandle,
  IN CHAR16             *FileName,
  IN BOOTMON_FS_INSTANCE *Volume
  )
{
  ASSERT(0);

  return EFI_UNSUPPORTED;
}

STATIC
EFI_STATUS
GetFileSystemVolumeLabelInfo (
  IN BOOTMON_FS_INSTANCE *Instance,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  UINTN                         Size;
  EFI_FILE_SYSTEM_VOLUME_LABEL *Label;
  EFI_STATUS                    Status;

  Label = Buffer;

  // Value returned by StrSize includes null terminator.
  Size = SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL
         + StrSize (Instance->FsInfo.VolumeLabel);

  if (*BufferSize >= Size) {
    CopyMem (&Label->VolumeLabel, &Instance->FsInfo.VolumeLabel, Size);
    Status = EFI_SUCCESS;
  } else {
    Status = EFI_BUFFER_TOO_SMALL;
  }
  *BufferSize = Size;
  return Status;
}

// Helper function that calculates a rough "free space" by:
// - Taking the media size
// - Subtracting the sum of all file sizes
// - Subtracting the block size times the number of files
//    (To account for the blocks containing the HW_IMAGE_INFO
STATIC
UINT64
ComputeFreeSpace (
  IN BOOTMON_FS_INSTANCE *Instance
  )
{
  LIST_ENTRY   *FileLink;
  UINT64        FileSizeSum;
  UINT64        MediaSize;
  UINTN         NumFiles;
  EFI_BLOCK_IO_MEDIA *Media;
  BOOTMON_FS_FILE *File;

  Media = Instance->BlockIo->Media;
  MediaSize = Media->BlockSize * (Media->LastBlock + 1);

  NumFiles = 0;
  FileSizeSum = 0;
  for (FileLink = GetFirstNode (&Instance->RootFile->Link);
         !IsNull (&Instance->RootFile->Link, FileLink);
         FileLink = GetNextNode (&Instance->RootFile->Link, FileLink)
         )
  {
    File = BOOTMON_FS_FILE_FROM_LINK_THIS (FileLink);
    FileSizeSum += BootMonFsGetImageLength (File);

    NumFiles++;
  }

  return MediaSize - (FileSizeSum + (Media->BlockSize + NumFiles));
}

STATIC
EFI_STATUS
GetFilesystemInfo (
  IN BOOTMON_FS_INSTANCE *Instance,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  EFI_STATUS              Status;

  if (*BufferSize >= Instance->FsInfo.Size) {
    Instance->FsInfo.FreeSpace = ComputeFreeSpace (Instance);
    CopyMem (Buffer, &Instance->FsInfo, Instance->FsInfo.Size);
    Status = EFI_SUCCESS;
  } else {
    Status = EFI_BUFFER_TOO_SMALL;
  }

  *BufferSize = Instance->FsInfo.Size;
  return Status;
}

STATIC
EFI_STATUS
GetFileInfo (
  IN BOOTMON_FS_INSTANCE  *Instance,
  IN BOOTMON_FS_FILE      *File,
  IN OUT UINTN            *BufferSize,
  OUT VOID                *Buffer
  )
{
  EFI_FILE_INFO  *Info;
  UINTN          ResultSize;

  ResultSize = SIZE_OF_EFI_FILE_INFO + StrSize (File->Info->FileName);

  if (*BufferSize < ResultSize) {
    *BufferSize = ResultSize;
    return EFI_BUFFER_TOO_SMALL;
  }

  Info = Buffer;

  CopyMem (Info, File->Info, ResultSize);
  // Size of the information
  Info->Size = ResultSize;

  *BufferSize = ResultSize;

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
GetBootMonFsFileInfo (
  IN BOOTMON_FS_INSTANCE *Instance,
  IN BOOTMON_FS_FILE     *File,
  IN OUT UINTN           *BufferSize,
  OUT VOID               *Buffer
  )
{
  EFI_STATUS             Status;
  BOOTMON_FS_FILE_INFO   *Info;
  UINTN                  ResultSize;
  UINTN                  Index;

  if (File == Instance->RootFile) {
    Status = EFI_UNSUPPORTED;
  } else {
    ResultSize = SIZE_OF_BOOTMON_FS_FILE_INFO;

    if (*BufferSize < ResultSize) {
      *BufferSize = ResultSize;
      Status = EFI_BUFFER_TOO_SMALL;
    } else {
      Info = Buffer;

      // Zero out the structure
      ZeroMem (Info, ResultSize);

      // Fill in the structure
      Info->Size = ResultSize;

      Info->EntryPoint  = File->HwDescription.EntryPoint;
      Info->RegionCount = File->HwDescription.RegionCount;
      for (Index = 0; Index < File->HwDescription.RegionCount; Index++) {
        Info->Region[Index].LoadAddress = File->HwDescription.Region[Index].LoadAddress;
        Info->Region[Index].Size        = File->HwDescription.Region[Index].Size;
        Info->Region[Index].Offset      = File->HwDescription.Region[Index].Offset;
        Info->Region[Index].Checksum    = File->HwDescription.Region[Index].Checksum;
      }
      *BufferSize = ResultSize;
      Status = EFI_SUCCESS;
    }
  }

  return Status;
}

/**
  Set the name of a file.

  This is a helper function for SetFileInfo().

  @param[in]  Instance  A pointer to the description of the volume
                        the file belongs to.
  @param[in]  File      A pointer to the description of the file.
  @param[in]  FileName  A pointer to the new name of the file.

  @retval  EFI_SUCCESS        The name was set.
  @retval  EFI_ACCESS_DENIED  An attempt is made to change the name of a file
                              to a file that is already present.

**/
STATIC
EFI_STATUS
SetFileName (
  IN  BOOTMON_FS_INSTANCE  *Instance,
  IN  BOOTMON_FS_FILE      *File,
  IN  CONST CHAR16         *FileName
  )
{
  CHAR8            AsciiFileName[MAX_NAME_LENGTH];
  BOOTMON_FS_FILE  *SameFile;

  // If the file path start with a \ strip it. The EFI Shell may
  // insert a \ in front of the file name.
  if (FileName[0] == L'\\') {
    FileName++;
  }

  UnicodeStrToAsciiStrS (FileName, AsciiFileName, MAX_NAME_LENGTH);

  if (BootMonGetFileFromAsciiFileName (
        File->Instance,
        AsciiFileName,
        &SameFile
        ) != EFI_NOT_FOUND) {
    // A file with that name already exists.
    return EFI_ACCESS_DENIED;
  } else {
    // OK, change the filename.
    AsciiStrToUnicodeStrS (AsciiFileName, File->Info->FileName,
      (File->Info->Size - SIZE_OF_EFI_FILE_INFO) / sizeof (CHAR16));
    return EFI_SUCCESS;
  }
}

/**
  Set the size of a file.

  This is a helper function for SetFileInfo().

  @param[in]  Instance  A pointer to the description of the volume
                        the file belongs to.
  @param[in]  File      A pointer to the description of the file.
  @param[in]  NewSize   The requested new size for the file.

  @retval  EFI_SUCCESS           The size was set.
  @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request failed.

**/
STATIC
EFI_STATUS
SetFileSize (
  IN BOOTMON_FS_INSTANCE  *Instance,
  IN BOOTMON_FS_FILE      *BootMonFsFile,
  IN UINTN                 NewSize
  )
{
  EFI_STATUS              Status;
  UINT32                  OldSize;
  LIST_ENTRY              *RegionToFlushLink;
  LIST_ENTRY              *NextRegionToFlushLink;
  BOOTMON_FS_FILE_REGION  *Region;
  EFI_FILE_PROTOCOL       *File;
  CHAR8                   *Buffer;
  UINTN                   BufferSize;
  UINT64                  StoredPosition;

  OldSize = BootMonFsFile->Info->FileSize;

  //
  // In case of file truncation, force the regions waiting for writing to
  // not overflow the new size of the file.
  //
  if (NewSize < OldSize) {
    for (RegionToFlushLink = GetFirstNode (&BootMonFsFile->RegionToFlushLink);
         !IsNull (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
         )
    {
      NextRegionToFlushLink = GetNextNode (&BootMonFsFile->RegionToFlushLink, RegionToFlushLink);
      Region = (BOOTMON_FS_FILE_REGION*)RegionToFlushLink;
      if (Region->Offset > NewSize) {
        RemoveEntryList (RegionToFlushLink);
        FreePool (Region->Buffer);
        FreePool (Region);
      } else {
        Region->Size = MIN (Region->Size, NewSize - Region->Offset);
      }
      RegionToFlushLink = NextRegionToFlushLink;
    }

  } else if (NewSize > OldSize) {
    // Increasing a file's size is potentially complicated as it may require
    // moving the image description on media. The simplest way to do it is to
    // seek past the end of the file (which is valid in UEFI) and perform a
    // Write.
    File = &BootMonFsFile->File;

    // Save position
    Status = File->GetPosition (File, &StoredPosition);
    if (EFI_ERROR (Status)) {
      return Status;
    }
    // Set position at the end of the file
    Status = File->SetPosition (File, OldSize);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    BufferSize = NewSize - OldSize;
    Buffer = AllocateZeroPool (BufferSize);
    if (Buffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    Status = File->Write (File, &BufferSize, Buffer);
    FreePool (Buffer);
    if (EFI_ERROR (Status)) {
      return Status;
    }

    // Restore saved position
    Status = File->SetPosition (File, StoredPosition);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  BootMonFsFile->Info->FileSize = NewSize;

  return EFI_SUCCESS;
}

/**
  Set information about a file.

  @param[in]  Instance  A pointer to the description of the volume
                        the file belongs to.
  @param[in]  File      A pointer to the description of the file.
  @param[in]  Info      A pointer to the file information to write.

  @retval  EFI_SUCCESS           The information was set.
  @retval  EFI_ACCESS_DENIED     An attempt is being made to change the
                                 EFI_FILE_DIRECTORY Attribute.
  @retval  EFI_ACCESS_DENIED     The file was opened in read-only mode and an
                                 attempt is being made to modify a field other
                                 than Attribute.
  @retval  EFI_ACCESS_DENIED     An attempt is made to change the name of a file
                                 to a file that is already present.
  @retval  EFI_WRITE_PROTECTED   An attempt is being made to modify a read-only
                                 attribute.
  @retval  EFI_OUT_OF_RESOURCES  An allocation needed to process the request
                                 failed.

**/
STATIC
EFI_STATUS
SetFileInfo (
  IN BOOTMON_FS_INSTANCE  *Instance,
  IN BOOTMON_FS_FILE      *File,
  IN EFI_FILE_INFO        *Info
  )
{
  EFI_STATUS  Status;
  BOOLEAN     FileSizeIsDifferent;
  BOOLEAN     FileNameIsDifferent;
  BOOLEAN     TimeIsDifferent;

  //
  // A directory can not be changed to a file and a file can
  // not be changed to a directory.
  //
  if ((Info->Attribute & EFI_FILE_DIRECTORY)      !=
      (File->Info->Attribute & EFI_FILE_DIRECTORY)  ) {
    return EFI_ACCESS_DENIED;
  }

  FileSizeIsDifferent = (Info->FileSize != File->Info->FileSize);
  FileNameIsDifferent = (StrnCmp (
                           Info->FileName,
                           File->Info->FileName,
                           MAX_NAME_LENGTH - 1
                           ) != 0);
  //
  // Check if the CreateTime, LastAccess or ModificationTime
  // have been changed. The file system does not support file
  // timestamps thus the three times in "File->Info" are
  // always equal to zero. The following comparison actually
  // checks if all three times are still equal to 0 or not.
  //
  TimeIsDifferent = CompareMem (
                      &Info->CreateTime,
                      &File->Info->CreateTime,
                      3 * sizeof (EFI_TIME)
                      ) != 0;

  //
  // For a file opened in read-only mode, only the Attribute field can be
  // modified. The root directory open mode is forced to read-only at opening
  // thus the following test protects the root directory to be somehow modified.
  //
  if (File->OpenMode == EFI_FILE_MODE_READ) {
    if (FileSizeIsDifferent || FileNameIsDifferent || TimeIsDifferent) {
      return EFI_ACCESS_DENIED;
    }
  }

  if (TimeIsDifferent) {
    return EFI_WRITE_PROTECTED;
  }

  if (FileSizeIsDifferent) {
    Status = SetFileSize (Instance, File, Info->FileSize);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Note down in RAM the Attribute field but we can not
  // ask to store it in flash for the time being.
  //
  File->Info->Attribute = Info->Attribute;

  if (FileNameIsDifferent) {
    Status = SetFileName (Instance, File, Info->FileName);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  return EFI_SUCCESS;
}

EFIAPI
EFI_STATUS
BootMonFsGetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN OUT UINTN          *BufferSize,
  OUT VOID              *Buffer
  )
{
  EFI_STATUS           Status;
  BOOTMON_FS_FILE     *File;
  BOOTMON_FS_INSTANCE *Instance;

  if ((This == NULL)                         ||
      (InformationType == NULL)              ||
      (BufferSize == NULL)                   ||
      ((Buffer == NULL) && (*BufferSize > 0))  ) {
    return EFI_INVALID_PARAMETER;
  }

  File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
  if (File->Info == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  Instance = File->Instance;

  // If the instance has not been initialized yet then do it ...
  if (!Instance->Initialized) {
    Status = BootMonFsInitialize (Instance);
  } else {
    Status = EFI_SUCCESS;
  }

  if (!EFI_ERROR (Status)) {
    if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)
        != 0) {
      Status = GetFileSystemVolumeLabelInfo (Instance, BufferSize, Buffer);
    } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
      Status = GetFilesystemInfo (Instance, BufferSize, Buffer);
    } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
      Status = GetFileInfo (Instance, File, BufferSize, Buffer);
    } else if (CompareGuid (InformationType, &gArmBootMonFsFileInfoGuid) != 0) {
      Status = GetBootMonFsFileInfo (Instance, File, BufferSize, Buffer);
    } else {
      Status = EFI_UNSUPPORTED;
    }
  }

  return Status;
}

/**
  Set information about a file or a volume.

  @param[in]  This             A pointer to the EFI_FILE_PROTOCOL instance that
                               is the file handle the information is for.
  @param[in]  InformationType  The type identifier for the information being set :
                               EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or
                               EFI_FILE_SYSTEM_VOLUME_LABEL_ID
  @param[in]  BufferSize       The size, in bytes, of Buffer.
  @param[in]  Buffer           A pointer to the data buffer to write. The type of the
                               data inside the buffer is indicated by InformationType.

  @retval  EFI_SUCCESS            The information was set.
  @retval  EFI_UNSUPPORTED        The InformationType is not known.
  @retval  EFI_DEVICE_ERROR       The last issued semi-hosting operation failed.
  @retval  EFI_ACCESS_DENIED      An attempt is made to change the name of a file
                                  to a file that is already present.
  @retval  EFI_ACCESS_DENIED      An attempt is being made to change the
                                  EFI_FILE_DIRECTORY Attribute.
  @retval  EFI_ACCESS_DENIED      InformationType is EFI_FILE_INFO_ID and
                                  the file was opened in read-only mode and an
                                  attempt is being made to modify a field other
                                  than Attribute.
  @retval  EFI_WRITE_PROTECTED    An attempt is being made to modify a read-only
                                  attribute.
  @retval  EFI_BAD_BUFFER_SIZE    The size of the buffer is lower than that indicated by
                                  the data inside the buffer.
  @retval  EFI_OUT_OF_RESOURCES   A allocation needed to process the request failed.
  @retval  EFI_INVALID_PARAMETER  At least one of the parameters is invalid.

**/
EFIAPI
EFI_STATUS
BootMonFsSetInfo (
  IN EFI_FILE_PROTOCOL  *This,
  IN EFI_GUID           *InformationType,
  IN UINTN              BufferSize,
  IN VOID               *Buffer
  )
{
  BOOTMON_FS_FILE       *File;
  EFI_FILE_INFO         *Info;
  EFI_FILE_SYSTEM_INFO  *SystemInfo;

  if ((This == NULL)            ||
      (InformationType == NULL) ||
      (Buffer == NULL)             ) {
    return EFI_INVALID_PARAMETER;
  }

  File = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
  if (File->Info == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
    Info = Buffer;
    if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) {
      return EFI_INVALID_PARAMETER;
    }
    if (BufferSize < Info->Size) {
      return EFI_BAD_BUFFER_SIZE;
    }
    return (SetFileInfo (File->Instance, File, Info));
  }

  //
  // The only writable field in the other two information types
  // (i.e. EFI_FILE_SYSTEM_INFO and EFI_FILE_SYSTEM_VOLUME_LABEL) is the
  // filesystem volume label. This can be retrieved with GetInfo, but it is
  // hard-coded into this driver, not stored on media.
  //

  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
    SystemInfo = Buffer;
    if (SystemInfo->Size <
        (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) {
      return EFI_INVALID_PARAMETER;
    }
    if (BufferSize < SystemInfo->Size) {
      return EFI_BAD_BUFFER_SIZE;
    }
    return EFI_WRITE_PROTECTED;
  }

  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
    return EFI_WRITE_PROTECTED;
  }

  return EFI_UNSUPPORTED;
}

EFIAPI
EFI_STATUS
BootMonFsReadDirectory (
  IN EFI_FILE_PROTOCOL    *This,
  IN OUT UINTN            *BufferSize,
  OUT VOID                *Buffer
  )
{
  BOOTMON_FS_INSTANCE *Instance;
  BOOTMON_FS_FILE     *RootFile;
  BOOTMON_FS_FILE     *File;
  EFI_FILE_INFO       *Info;
  UINTN               NameSize;
  UINTN               ResultSize;
  EFI_STATUS          Status;
  UINTN               Index;

  RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
  if (RootFile == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Instance = RootFile->Instance;
  Status = BootMonGetFileFromPosition (Instance, RootFile->Position, &File);
  if (EFI_ERROR (Status)) {
    // No more file
    *BufferSize = 0;
    return EFI_SUCCESS;
  }

  NameSize   = AsciiStrLen (File->HwDescription.Footer.Filename) + 1;
  ResultSize = SIZE_OF_EFI_FILE_INFO + (NameSize * sizeof (CHAR16));
  if (*BufferSize < ResultSize) {
    *BufferSize = ResultSize;
    return EFI_BUFFER_TOO_SMALL;
  }

  // Zero out the structure
  Info = Buffer;
  ZeroMem (Info, ResultSize);

  // Fill in the structure
  Info->Size         = ResultSize;
  Info->FileSize     = BootMonFsGetImageLength (File);
  Info->PhysicalSize = BootMonFsGetPhysicalSize (File);
  for (Index = 0; Index < NameSize; Index++) {
    Info->FileName[Index] = File->HwDescription.Footer.Filename[Index];
  }

  *BufferSize = ResultSize;
  RootFile->Position++;

  return EFI_SUCCESS;
}

EFIAPI
EFI_STATUS
BootMonFsFlushDirectory (
  IN EFI_FILE_PROTOCOL  *This
  )
{
  BOOTMON_FS_FILE *RootFile;
  LIST_ENTRY      *ListFiles;
  LIST_ENTRY      *Link;
  BOOTMON_FS_FILE *File;

  RootFile = BOOTMON_FS_FILE_FROM_FILE_THIS (This);
  if (RootFile == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  ListFiles = &RootFile->Link;

  if (IsListEmpty (ListFiles)) {
    return EFI_SUCCESS;
  }

  //
  // Flush all the files that need to be flushed
  //

  // Go through all the list of files to flush them
  for (Link = GetFirstNode (ListFiles);
       !IsNull (ListFiles, Link);
       Link = GetNextNode (ListFiles, Link)
       )
  {
    File = BOOTMON_FS_FILE_FROM_LINK_THIS (Link);
    File->File.Flush (&File->File);
  }

  return EFI_SUCCESS;
}
