/** @file

Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent


**/

#include "Edb.h"

/**
  Read a file.

  @param  Vol             - File System Volume
  @param  FileName        - The file to be read.
  @param  BufferSize      - The file buffer size
  @param  Buffer          - The file buffer

  @retval EFI_SUCCESS    - read file successfully
  @retval EFI_NOT_FOUND  - file not found

**/
EFI_STATUS
EFIAPI
ReadFileFromVol (
  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Vol,
  IN  CHAR16                           *FileName,
  OUT UINTN                            *BufferSize,
  OUT VOID                             **Buffer
  )
{
  EFI_STATUS       Status;
  EFI_FILE_HANDLE  RootDir;
  EFI_FILE_HANDLE  Handle;
  UINTN            FileInfoSize;
  EFI_FILE_INFO    *FileInfo;
  UINTN            TempBufferSize;
  VOID             *TempBuffer;

  //
  // Open the root directory
  //
  Status = Vol->OpenVolume (Vol, &RootDir);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Open the file
  //
  Status = RootDir->Open (
                      RootDir,
                      &Handle,
                      FileName,
                      EFI_FILE_MODE_READ,
                      0
                      );
  if (EFI_ERROR (Status)) {
    RootDir->Close (RootDir);
    return Status;
  }

  RootDir->Close (RootDir);

  //
  // Get the file information
  //
  FileInfoSize = sizeof (EFI_FILE_INFO) + 1024;

  FileInfo = AllocateZeroPool (FileInfoSize);
  if (FileInfo == NULL) {
    Handle->Close (Handle);
    return Status;
  }

  Status = Handle->GetInfo (
                     Handle,
                     &gEfiFileInfoGuid,
                     &FileInfoSize,
                     FileInfo
                     );
  if (EFI_ERROR (Status)) {
    Handle->Close (Handle);
    gBS->FreePool (FileInfo);
    return Status;
  }

  //
  // Allocate buffer for the file data. The last CHAR16 is for L'\0'
  //
  TempBufferSize = (UINTN)FileInfo->FileSize + sizeof (CHAR16);
  TempBuffer     = AllocateZeroPool (TempBufferSize);
  if (TempBuffer == NULL) {
    Handle->Close (Handle);
    gBS->FreePool (FileInfo);
    return Status;
  }

  gBS->FreePool (FileInfo);

  //
  // Read the file data to the buffer
  //
  Status = Handle->Read (
                     Handle,
                     &TempBufferSize,
                     TempBuffer
                     );
  if (EFI_ERROR (Status)) {
    Handle->Close (Handle);
    gBS->FreePool (TempBuffer);
    return Status;
  }

  Handle->Close (Handle);

  *BufferSize = TempBufferSize;
  *Buffer     = TempBuffer;
  return EFI_SUCCESS;
}

/**

  Read a file.
  If ScanFs is FLASE, it will use DebuggerPrivate->Vol as default Fs.
  If ScanFs is TRUE, it will scan all FS and check the file.
  If there is only one file match the name, it will be read.
  If there is more than one file match the name, it will return Error.

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  FileName        - The file to be read.
  @param  BufferSize      - The file buffer size
  @param  Buffer          - The file buffer
  @param  ScanFs          - Need Scan all FS

  @retval EFI_SUCCESS    - read file successfully
  @retval EFI_NOT_FOUND  - file not found
  @retval EFI_NO_MAPPING - there is duplicated files found

**/
EFI_STATUS
EFIAPI
ReadFileToBuffer (
  IN  EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN  CHAR16                     *FileName,
  OUT UINTN                      *BufferSize,
  OUT VOID                       **Buffer,
  IN  BOOLEAN                    ScanFs
  )
{
  EFI_STATUS                       Status;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Vol;
  UINTN                            TempBufferSize;
  VOID                             *TempBuffer;
  UINTN                            NoHandles;
  EFI_HANDLE                       *HandleBuffer;
  UINTN                            Index;

  //
  // Check parameters
  //
  if ((FileName == NULL) || (Buffer == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // not scan fs
  //
  if (!ScanFs) {
    if (DebuggerPrivate->Vol == NULL) {
      return EFI_INVALID_PARAMETER;
    }

    //
    // Read file directly from Vol
    //
    return ReadFileFromVol (DebuggerPrivate->Vol, FileName, BufferSize, Buffer);
  }

  //
  // need scan fs
  //

  //
  // Get all Vol handle
  //
  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiSimpleFileSystemProtocolGuid,
                  NULL,
                  &NoHandles,
                  &HandleBuffer
                  );
  if (EFI_ERROR (Status) && (NoHandles == 0)) {
    return EFI_NOT_FOUND;
  }

  //
  // Walk through each Vol
  //
  DebuggerPrivate->Vol = NULL;
  *BufferSize          = 0;
  *Buffer              = NULL;
  for (Index = 0; Index < NoHandles; Index++) {
    Status = gBS->HandleProtocol (
                    HandleBuffer[Index],
                    &gEfiSimpleFileSystemProtocolGuid,
                    (VOID **)&Vol
                    );
    if (EFI_ERROR (Status)) {
      continue;
    }

    Status = ReadFileFromVol (Vol, FileName, &TempBufferSize, &TempBuffer);
    if (!EFI_ERROR (Status)) {
      //
      // Read file OK, check duplication
      //
      if (DebuggerPrivate->Vol != NULL) {
        //
        // Find the duplicated file
        //
        gBS->FreePool (TempBuffer);
        gBS->FreePool (*Buffer);
        EDBPrint (L"Duplicated FileName found!\n");
        return EFI_NO_MAPPING;
      } else {
        //
        // Record value
        //
        DebuggerPrivate->Vol = Vol;
        *BufferSize          = TempBufferSize;
        *Buffer              = TempBuffer;
      }
    }
  }

  //
  // Scan Fs done
  //
  if (DebuggerPrivate->Vol == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Done
  //
  return EFI_SUCCESS;
}

/**

  Get file name under this dir with index

  @param  DebuggerPrivate - EBC Debugger private data structure
  @param  DirName         - The dir to be read.
  @param  FileName        - The file name pattern under this dir
  @param  Index           - The file index under this dir

  @return File Name which match the pattern and index.

**/
CHAR16 *
EFIAPI
GetFileNameUnderDir (
  IN  EFI_DEBUGGER_PRIVATE_DATA  *DebuggerPrivate,
  IN  CHAR16                     *DirName,
  IN  CHAR16                     *FileName,
  IN OUT UINTN                   *Index
  )
{
  EFI_STATUS                       Status;
  EFI_FILE_HANDLE                  RootDir;
  EFI_FILE_HANDLE                  Handle;
  UINTN                            FileInfoSize;
  EFI_FILE_INFO                    *FileInfo;
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Vol;
  VOID                             *TempName;
  UINTN                            FileIndex;

  if (DebuggerPrivate->Vol == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiSimpleFileSystemProtocolGuid,
                    NULL,
                    (VOID **)&DebuggerPrivate->Vol
                    );
    if (EFI_ERROR (Status)) {
      return NULL;
    }
  }

  Vol = DebuggerPrivate->Vol;

  //
  // Open the root directory
  //
  Status = Vol->OpenVolume (Vol, &RootDir);
  if (EFI_ERROR (Status)) {
    return NULL;
  }

  //
  // Open the file
  //
  Status = RootDir->Open (
                      RootDir,
                      &Handle,
                      DirName,
                      EFI_FILE_MODE_READ,
                      EFI_FILE_DIRECTORY
                      );
  if (EFI_ERROR (Status)) {
    RootDir->Close (RootDir);
    return NULL;
  }

  RootDir->Close (RootDir);

  //
  // Set Dir Position
  //
  Status = Handle->SetPosition (Handle, 0);
  if (EFI_ERROR (Status)) {
    Handle->Close (Handle);
    return NULL;
  }

  //
  // Get the file information
  //
  FileInfoSize = sizeof (EFI_FILE_INFO) + 1024;

  FileInfo = AllocateZeroPool (FileInfoSize);
  if (FileInfo == NULL) {
    Handle->Close (Handle);
    return NULL;
  }

  //
  // Walk through each file in the directory
  //
  FileIndex = 0;
  TempName  = NULL;
  while (TRUE) {
    //
    // Read a file entry
    //
    FileInfoSize = sizeof (EFI_FILE_INFO) + 1024;

    Status = Handle->Read (
                       Handle,
                       &FileInfoSize,
                       FileInfo
                       );
    if (EFI_ERROR (Status) || (FileInfoSize == 0)) {
      break;
    }

    if ((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) {
      //
      // This is a file
      //

      //
      // Only deal with the EFI key file
      //
      if (!StrEndWith (FileInfo->FileName, FileName)) {
        continue;
      }

      if (FileIndex == *Index) {
        TempName = StrDuplicate (FileInfo->FileName);
        *Index   = *Index + 1;
        break;
      }

      FileIndex++;
    }
  }

  //
  // Free resources
  //
  gBS->FreePool (FileInfo);
  Handle->Close (Handle);

  return TempName;
}
