/** @file
  Type and macro definitions specific to the Virtio Filesystem device.

  At the time of this writing, the latest released Virtio specification (v1.1)
  does not include the virtio-fs device. The development version of the
  specification defines it however; see the latest version at
  <https://github.com/oasis-tcs/virtio-spec/blob/87fa6b5d8155/virtio-fs.tex>.

  This header file is minimal, and only defines the types and macros that are
  necessary for the OvmfPkg implementation.

  Copyright (C) 2020, Red Hat, Inc.

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

#ifndef VIRTIO_FS_H_
#define VIRTIO_FS_H_

#include <IndustryStandard/Virtio.h>

//
// Lowest numbered queue for sending normal priority requests.
//
#define VIRTIO_FS_REQUEST_QUEUE  1

//
// Number of bytes in the "VIRTIO_FS_CONFIG.Tag" field.
//
#define VIRTIO_FS_TAG_BYTES  36

//
// Device configuration layout.
//
#pragma pack (1)
typedef struct {
  //
  // The Tag field can be considered the filesystem label, or a mount point
  // hint. It is UTF-8 encoded, and padded to full size with NUL bytes. If the
  // encoded bytes take up the entire Tag field, then there is no NUL
  // terminator.
  //
  UINT8     Tag[VIRTIO_FS_TAG_BYTES];
  //
  // The total number of request virtqueues exposed by the device (i.e.,
  // excluding the "hiprio" queue).
  //
  UINT32    NumReqQueues;
} VIRTIO_FS_CONFIG;
#pragma pack ()

//
// FUSE-related definitions follow.
//
// From virtio-v1.1-cs01-87fa6b5d8155, 5.11 File System Device: "[...] The
// driver acts as the FUSE client mounting the file system. The virtio file
// system device provides the mechanism for transporting FUSE requests [...]"
//
// Unfortunately, the documentation of the FUSE wire protocol is lacking. The
// Virtio spec (as of this writing) simply defers to
// "include/uapi/linux/fuse.h" in the Linux kernel source -- see the reference
// in virtio spec file "introduction.tex", at commit 87fa6b5d8155.
//
// Of course, "include/uapi/linux/fuse.h" is a moving target (the virtio spec
// does not specify a particular FUSE interface version). The OvmfPkg code
// targets version 7.31, because that's the lowest version that the QEMU
// virtio-fs daemon supports at this time -- see QEMU commit 72c42e2d6551
// ("virtiofsd: Trim out compatibility code", 2020-01-23).
//
// Correspondingly, Linux's "include/uapi/linux/fuse.h" is consulted as checked
// out at commit (c6ff213fe5b8^) = d78092e4937d ("fuse: fix page dereference
// after free", 2020-09-18); that is, right before commit c6ff213fe5b8 ("fuse:
// add submount support to <uapi/linux/fuse.h>", 2020-09-18) introduces FUSE
// interface version 7.32.
//
#define VIRTIO_FS_FUSE_MAJOR  7
#define VIRTIO_FS_FUSE_MINOR  31

//
// The inode number of the root directory.
//
#define VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID  1

//
// Distinguished errno values.
//
#define VIRTIO_FS_FUSE_ERRNO_ENOENT  (-2)

//
// File mode bitmasks.
//
#define VIRTIO_FS_FUSE_MODE_TYPE_MASK  0170000u
#define VIRTIO_FS_FUSE_MODE_TYPE_REG   0100000u
#define VIRTIO_FS_FUSE_MODE_TYPE_DIR   0040000u
#define VIRTIO_FS_FUSE_MODE_PERM_RWXU  0000700u
#define VIRTIO_FS_FUSE_MODE_PERM_RUSR  0000400u
#define VIRTIO_FS_FUSE_MODE_PERM_WUSR  0000200u
#define VIRTIO_FS_FUSE_MODE_PERM_XUSR  0000100u
#define VIRTIO_FS_FUSE_MODE_PERM_RWXG  0000070u
#define VIRTIO_FS_FUSE_MODE_PERM_RGRP  0000040u
#define VIRTIO_FS_FUSE_MODE_PERM_WGRP  0000020u
#define VIRTIO_FS_FUSE_MODE_PERM_XGRP  0000010u
#define VIRTIO_FS_FUSE_MODE_PERM_RWXO  0000007u
#define VIRTIO_FS_FUSE_MODE_PERM_ROTH  0000004u
#define VIRTIO_FS_FUSE_MODE_PERM_WOTH  0000002u
#define VIRTIO_FS_FUSE_MODE_PERM_XOTH  0000001u

//
// Flags for VirtioFsFuseOpSetAttr, in the VIRTIO_FS_FUSE_SETATTR_REQUEST.Valid
// field.
//
#define VIRTIO_FS_FUSE_SETATTR_REQ_F_MODE   BIT0
#define VIRTIO_FS_FUSE_SETATTR_REQ_F_SIZE   BIT3
#define VIRTIO_FS_FUSE_SETATTR_REQ_F_ATIME  BIT4
#define VIRTIO_FS_FUSE_SETATTR_REQ_F_MTIME  BIT5

//
// Flags for VirtioFsFuseOpOpen.
//
#define VIRTIO_FS_FUSE_OPEN_REQ_F_RDONLY  0
#define VIRTIO_FS_FUSE_OPEN_REQ_F_RDWR    2

//
// Flags for VirtioFsFuseOpInit.
//
#define VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS  BIT13

/**
  Macro for calculating the size of a directory stream entry.

  The macro may evaluate Namelen multiple times.

  The macro evaluates to a UINTN value that is safe to cast to UINT32.

  @param[in] Namelen  The size of the filename byte array that follows
                      VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE in the directory
                      stream, as reported by
                      VIRTIO_FS_FUSE_STATFS_RESPONSE.Namelen or
                      VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE.Namelen. The filename
                      byte array is not NUL-terminated.

  @retval 0  Namelen was zero or greater than SIZE_4KB.

  @return    The number of bytes in the directory entry, including the
             VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE header.
**/
#define VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE(Namelen)             \
  ((Namelen) == 0 || (Namelen) > SIZE_4KB ?                          \
   (UINTN)0 :                                                        \
   ALIGN_VALUE (                                                     \
     sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE) + (UINTN)(Namelen), \
     sizeof (UINT64)                                                 \
     )                                                               \
   )

//
// Flags for VirtioFsFuseOpRename2.
//
#define VIRTIO_FS_FUSE_RENAME2_REQ_F_NOREPLACE  BIT0

//
// FUSE operation codes.
//
typedef enum {
  VirtioFsFuseOpLookup      =  1,
  VirtioFsFuseOpForget      =  2,
  VirtioFsFuseOpGetAttr     =  3,
  VirtioFsFuseOpSetAttr     =  4,
  VirtioFsFuseOpMkDir       =  9,
  VirtioFsFuseOpUnlink      = 10,
  VirtioFsFuseOpRmDir       = 11,
  VirtioFsFuseOpOpen        = 14,
  VirtioFsFuseOpRead        = 15,
  VirtioFsFuseOpWrite       = 16,
  VirtioFsFuseOpStatFs      = 17,
  VirtioFsFuseOpRelease     = 18,
  VirtioFsFuseOpFsync       = 20,
  VirtioFsFuseOpFlush       = 25,
  VirtioFsFuseOpInit        = 26,
  VirtioFsFuseOpOpenDir     = 27,
  VirtioFsFuseOpReleaseDir  = 29,
  VirtioFsFuseOpFsyncDir    = 30,
  VirtioFsFuseOpCreate      = 35,
  VirtioFsFuseOpReadDirPlus = 44,
  VirtioFsFuseOpRename2     = 45,
} VIRTIO_FS_FUSE_OPCODE;

#pragma pack (1)
//
// Request-response headers common to all request types.
//
typedef struct {
  UINT32    Len;
  UINT32    Opcode;
  UINT64    Unique;
  UINT64    NodeId;
  UINT32    Uid;
  UINT32    Gid;
  UINT32    Pid;
  UINT32    Padding;
} VIRTIO_FS_FUSE_REQUEST;

typedef struct {
  UINT32    Len;
  INT32     Error;
  UINT64    Unique;
} VIRTIO_FS_FUSE_RESPONSE;

//
// Structure with which the Virtio Filesystem device reports a NodeId to the
// FUSE client (i.e., to the Virtio Filesystem driver). This structure is a
// part of the response headers for operations that inform the FUSE client of
// an inode.
//
typedef struct {
  UINT64    NodeId;
  UINT64    Generation;
  UINT64    EntryValid;
  UINT64    AttrValid;
  UINT32    EntryValidNsec;
  UINT32    AttrValidNsec;
} VIRTIO_FS_FUSE_NODE_RESPONSE;

//
// Structure describing the host-side attributes of an inode. This structure is
// a part of the response headers for operations that inform the FUSE client of
// an inode.
//
typedef struct {
  UINT64    Ino;
  UINT64    Size;
  UINT64    Blocks;
  UINT64    Atime;
  UINT64    Mtime;
  UINT64    Ctime;
  UINT32    AtimeNsec;
  UINT32    MtimeNsec;
  UINT32    CtimeNsec;
  UINT32    Mode;
  UINT32    Nlink;
  UINT32    Uid;
  UINT32    Gid;
  UINT32    Rdev;
  UINT32    Blksize;
  UINT32    Padding;
} VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE;

//
// Header for VirtioFsFuseOpForget.
//
typedef struct {
  UINT64    NumberOfLookups;
} VIRTIO_FS_FUSE_FORGET_REQUEST;

//
// Headers for VirtioFsFuseOpGetAttr (VIRTIO_FS_FUSE_GETATTR_RESPONSE is also
// for VirtioFsFuseOpSetAttr).
//
typedef struct {
  UINT32    GetAttrFlags;
  UINT32    Dummy;
  UINT64    FileHandle;
} VIRTIO_FS_FUSE_GETATTR_REQUEST;

typedef struct {
  UINT64    AttrValid;
  UINT32    AttrValidNsec;
  UINT32    Dummy;
} VIRTIO_FS_FUSE_GETATTR_RESPONSE;

//
// Header for VirtioFsFuseOpSetAttr.
//
typedef struct {
  UINT32    Valid;
  UINT32    Padding;
  UINT64    FileHandle;
  UINT64    Size;
  UINT64    LockOwner;
  UINT64    Atime;
  UINT64    Mtime;
  UINT64    Ctime;
  UINT32    AtimeNsec;
  UINT32    MtimeNsec;
  UINT32    CtimeNsec;
  UINT32    Mode;
  UINT32    Unused4;
  UINT32    Uid;
  UINT32    Gid;
  UINT32    Unused5;
} VIRTIO_FS_FUSE_SETATTR_REQUEST;

//
// Header for VirtioFsFuseOpMkDir.
//
typedef struct {
  UINT32    Mode;
  UINT32    Umask;
} VIRTIO_FS_FUSE_MKDIR_REQUEST;

//
// Headers for VirtioFsFuseOpOpen and VirtioFsFuseOpOpenDir.
//
typedef struct {
  UINT32    Flags;
  UINT32    Unused;
} VIRTIO_FS_FUSE_OPEN_REQUEST;

typedef struct {
  UINT64    FileHandle;
  UINT32    OpenFlags;
  UINT32    Padding;
} VIRTIO_FS_FUSE_OPEN_RESPONSE;

//
// Header for VirtioFsFuseOpRead and VirtioFsFuseOpReadDirPlus.
//
typedef struct {
  UINT64    FileHandle;
  UINT64    Offset;
  UINT32    Size;
  UINT32    ReadFlags;
  UINT64    LockOwner;
  UINT32    Flags;
  UINT32    Padding;
} VIRTIO_FS_FUSE_READ_REQUEST;

//
// Headers for VirtioFsFuseOpWrite.
//
typedef struct {
  UINT64    FileHandle;
  UINT64    Offset;
  UINT32    Size;
  UINT32    WriteFlags;
  UINT64    LockOwner;
  UINT32    Flags;
  UINT32    Padding;
} VIRTIO_FS_FUSE_WRITE_REQUEST;

typedef struct {
  UINT32    Size;
  UINT32    Padding;
} VIRTIO_FS_FUSE_WRITE_RESPONSE;

//
// Header for VirtioFsFuseOpStatFs.
//
typedef struct {
  UINT64    Blocks;
  UINT64    Bfree;
  UINT64    Bavail;
  UINT64    Files;
  UINT64    Ffree;
  UINT32    Bsize;
  UINT32    Namelen;
  UINT32    Frsize;
  UINT32    Padding;
  UINT32    Spare[6];
} VIRTIO_FS_FUSE_STATFS_RESPONSE;

//
// Header for VirtioFsFuseOpRelease and VirtioFsFuseOpReleaseDir.
//
typedef struct {
  UINT64    FileHandle;
  UINT32    Flags;
  UINT32    ReleaseFlags;
  UINT64    LockOwner;
} VIRTIO_FS_FUSE_RELEASE_REQUEST;

//
// Header for VirtioFsFuseOpFsync and VirtioFsFuseOpFsyncDir.
//
typedef struct {
  UINT64    FileHandle;
  UINT32    FsyncFlags;
  UINT32    Padding;
} VIRTIO_FS_FUSE_FSYNC_REQUEST;

//
// Header for VirtioFsFuseOpFlush.
//
typedef struct {
  UINT64    FileHandle;
  UINT32    Unused;
  UINT32    Padding;
  UINT64    LockOwner;
} VIRTIO_FS_FUSE_FLUSH_REQUEST;

//
// Headers for VirtioFsFuseOpInit.
//
typedef struct {
  UINT32    Major;
  UINT32    Minor;
  UINT32    MaxReadahead;
  UINT32    Flags;
} VIRTIO_FS_FUSE_INIT_REQUEST;

typedef struct {
  UINT32    Major;
  UINT32    Minor;
  UINT32    MaxReadahead;
  UINT32    Flags;
  UINT16    MaxBackground;
  UINT16    CongestionThreshold;
  UINT32    MaxWrite;
  UINT32    TimeGran;
  UINT16    MaxPages;
  UINT16    MapAlignment;
  UINT32    Unused[8];
} VIRTIO_FS_FUSE_INIT_RESPONSE;

//
// Header for VirtioFsFuseOpCreate.
//
typedef struct {
  UINT32    Flags;
  UINT32    Mode;
  UINT32    Umask;
  UINT32    Padding;
} VIRTIO_FS_FUSE_CREATE_REQUEST;

//
// Header for VirtioFsFuseOpReadDirPlus.
//
// Diverging from the rest of the headers, this structure embeds other
// structures. The reason is that a scatter list cannot be used to receive
// NodeResp and AttrResp separately; the record below is followed by a variable
// size filename byte array, and then such pairs are repeated a number of
// times. Thus, later header start offsets depend on earlier filename array
// sizes.
//
typedef struct {
  VIRTIO_FS_FUSE_NODE_RESPONSE          NodeResp;
  VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE    AttrResp;
  UINT64                                NodeId;
  UINT64                                CookieForNextEntry;
  UINT32                                Namelen;
  UINT32                                Type;
} VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE;

//
// Header for VirtioFsFuseOpRename2.
//
typedef struct {
  UINT64    NewDir;
  UINT32    Flags;
  UINT32    Padding;
} VIRTIO_FS_FUSE_RENAME2_REQUEST;
#pragma pack ()

#endif // VIRTIO_FS_H_
