/** @file
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() member function for the Virtio
  Filesystem driver.

  Copyright (C) 2020, Red Hat, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Library/BaseLib.h>             // InsertTailList()
#include <Library/MemoryAllocationLib.h> // AllocatePool()

#include "VirtioFsDxe.h"

/**
  Open the root directory on the Virtio Filesystem.

  Refer to EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME for the interface
  contract.
**/
EFI_STATUS
EFIAPI
VirtioFsOpenVolume (
  IN  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
  OUT EFI_FILE_PROTOCOL               **Root
  )
{
  VIRTIO_FS      *VirtioFs;
  VIRTIO_FS_FILE *VirtioFsFile;
  EFI_STATUS     Status;
  CHAR8          *CanonicalPathname;
  UINT64         RootDirHandle;

  VirtioFs = VIRTIO_FS_FROM_SIMPLE_FS (This);

  VirtioFsFile = AllocatePool (sizeof *VirtioFsFile);
  if (VirtioFsFile == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  CanonicalPathname = AllocateCopyPool (sizeof "/", "/");
  if (CanonicalPathname == NULL) {
    Status = EFI_OUT_OF_RESOURCES;
    goto FreeVirtioFsFile;
  }

  //
  // Open the root directory.
  //
  Status = VirtioFsFuseOpenDir (VirtioFs, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
             &RootDirHandle);
  if (EFI_ERROR (Status)) {
    goto FreeCanonicalPathname;
  }

  //
  // Populate the new VIRTIO_FS_FILE object.
  //
  VirtioFsFile->Signature              = VIRTIO_FS_FILE_SIG;
  VirtioFsFile->SimpleFile.Revision    = EFI_FILE_PROTOCOL_REVISION;
  VirtioFsFile->SimpleFile.Open        = VirtioFsSimpleFileOpen;
  VirtioFsFile->SimpleFile.Close       = VirtioFsSimpleFileClose;
  VirtioFsFile->SimpleFile.Delete      = VirtioFsSimpleFileDelete;
  VirtioFsFile->SimpleFile.Read        = VirtioFsSimpleFileRead;
  VirtioFsFile->SimpleFile.Write       = VirtioFsSimpleFileWrite;
  VirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
  VirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
  VirtioFsFile->SimpleFile.GetInfo     = VirtioFsSimpleFileGetInfo;
  VirtioFsFile->SimpleFile.SetInfo     = VirtioFsSimpleFileSetInfo;
  VirtioFsFile->SimpleFile.Flush       = VirtioFsSimpleFileFlush;
  VirtioFsFile->IsDirectory            = TRUE;
  VirtioFsFile->IsOpenForWriting       = FALSE;
  VirtioFsFile->OwnerFs                = VirtioFs;
  VirtioFsFile->CanonicalPathname      = CanonicalPathname;
  VirtioFsFile->FilePosition           = 0;
  VirtioFsFile->NodeId                 = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
  VirtioFsFile->FuseHandle             = RootDirHandle;
  VirtioFsFile->FileInfoArray          = NULL;
  VirtioFsFile->SingleFileInfoSize     = 0;
  VirtioFsFile->NumFileInfo            = 0;
  VirtioFsFile->NextFileInfo           = 0;

  //
  // One more file open for the filesystem.
  //
  InsertTailList (&VirtioFs->OpenFiles, &VirtioFsFile->OpenFilesEntry);

  *Root = &VirtioFsFile->SimpleFile;
  return EFI_SUCCESS;

FreeCanonicalPathname:
  FreePool (CanonicalPathname);

FreeVirtioFsFile:
  FreePool (VirtioFsFile);

  return Status;
}
