// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
 *          Alexander Warg <warg@os.inf.tu-dresden.de>
 *     economic rights: Technische Universität Dresden (Germany)
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */
#pragma once

#include <l4/l4re_vfs/vfs.h>
#include <l4/crtn/initpriorities.h>

extern "C" void l4re_vfs_select_poll_notify(void);

namespace L4Re { namespace Vfs {

/// Reference to the applications L4Re::Vfs::Ops singleton.
extern L4Re::Vfs::Ops *vfs_ops asm ("l4re_env_posix_vfs_ops");

class Mount_tree;

/**
 * \brief Boiler plate class for implementing an open file for L4Re::Vfs.
 *
 * This class may be used as a base class for everything that a POSIX
 * file descriptor may point to. This are things such as regular files,
 * directories, special device files, streams, pipes, and so on.
 */
class Be_file : public File
{
public:
  void *operator new (size_t size) noexcept
  { return vfs_ops->malloc(size); }

  void *operator new (size_t, void *m) noexcept
  { return m; }

  void operator delete (void *m)
  { vfs_ops->free(m); }

  // used in close, to unlock all locks of a file (as POSIX says)
  int unlock_all_locks() noexcept override
  { return 0; }

  // for mmap
  L4::Cap<L4Re::Dataspace> data_space() noexcept override
  { return L4::Cap<L4Re::Dataspace>::Invalid; }

  /// Default backend for POSIX read and readv functions.
  ssize_t readv(const struct iovec*, int) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX write and writev functions.
  ssize_t writev(const struct iovec*, int) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX pwrite and pwritev functions.
  ssize_t pwritev(const struct iovec*, int, off64_t) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX pread and preadv functions.
  ssize_t preadv(const struct iovec*, int, off64_t) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX seek and lseek functions.
  off64_t lseek(off64_t, int) noexcept override
  { return -ESPIPE; }

  /// Default backend for the POSIX truncate, ftruncate and similar functions.
  int ftruncate(off64_t) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX fsync.
  int fsync() const noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX fdatasync.
  int fdatasync() const noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX ioctl.
  int ioctl(unsigned long, va_list) noexcept override
  { return -EINVAL; }

  int fstat(struct stat64 *) const noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX chmod and fchmod.
  int fchmod(mode_t) noexcept override
  { return -EINVAL; }

  /// Default backend for POSIX fcntl subfunctions.
  int get_status_flags() const noexcept override
  { return 0; }

  /// Default backend for POSIX fcntl subfunctions.
  int set_status_flags(long) noexcept override
  { return 0; }

  /// Default backend for POSIX fcntl subfunctions.
  int get_lock(struct flock64 *) noexcept override
  { return -ENOLCK; }

  /// Default backend for POSIX fcntl subfunctions.
  int set_lock(struct flock64 *, bool) noexcept override
  { return -ENOLCK; }

  /// Default backend for POSIX access and faccessat functions.
  int faccessat(const char *, int, int) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX fchmodat function.
  int fchmodat(const char *, mode_t, int) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX utime.
  int utime(const struct utimbuf *) noexcept override
  { return -EROFS; }

  /// Default backend for POSIX utimes.
  int utimes(const struct timeval [2]) noexcept override
  { return -EROFS; }

  /// Default backend for POSIX utimensat.
  int utimensat(const char *, const struct timespec [2], int) noexcept override
  { return -EROFS; }

  /// Default backend for POSIX mkdir and mkdirat.
  int mkdir(const char *, mode_t) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX unlink, unlinkat.
  int unlink(const char *) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX rename, renameat.
  int rename(const char *, const char *) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX link, linkat.
  int link(const char *, const char *) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX symlink, symlinkat.
  int symlink(const char *, const char *) noexcept override
  { return -EPERM; }

  /// Default backend for POSIX rmdir, rmdirat.
  int rmdir(const char *) noexcept override
  { return -ENOTDIR; }

  /// Default backend for POSIX readlink, readlinkat.
  ssize_t readlink(char *, size_t) override
  { return -EINVAL; }

  ssize_t getdents(char *, size_t) noexcept override
  { return -ENOTDIR; }



  // Socket interface
  int bind(sockaddr const *, socklen_t) noexcept override
  { return -ENOTSOCK; }

  int connect(sockaddr const *, socklen_t) noexcept override
  { return -ENOTSOCK; }

  ssize_t send(void const *, size_t, int) noexcept override
  { return -ENOTSOCK; }

  ssize_t recv(void *, size_t, int) noexcept override
  { return -ENOTSOCK; }

  ssize_t sendto(void const *, size_t, int, sockaddr const *, socklen_t) noexcept
    override
  { return -ENOTSOCK; }

  ssize_t recvfrom(void *, size_t, int, sockaddr *, socklen_t *) noexcept override
  { return -ENOTSOCK; }

  ssize_t sendmsg(msghdr const *, int) noexcept override
  { return -ENOTSOCK; }

  ssize_t recvmsg(msghdr *, int) noexcept override
  { return -ENOTSOCK; }

  int getsockopt(int, int, void *, socklen_t *) noexcept override
  { return -ENOTSOCK; }

  int setsockopt(int, int, void const *, socklen_t) noexcept override
  { return -ENOTSOCK; }

  int listen(int) noexcept override
  { return -ENOTSOCK; }

  int accept(sockaddr *, socklen_t *) noexcept override
  { return -ENOTSOCK; }

  int shutdown(int) noexcept override
  { return -ENOTSOCK; }

  int getsockname(sockaddr *, socklen_t *) noexcept override
  { return -ENOTSOCK; }

  int getpeername(sockaddr *, socklen_t *) noexcept override
  { return -ENOTSOCK; }

  /**
   * \brief Default implementation of a readiness check.
   *
   * By default, we assume a file is not ready for an I/O operation/condition
   * since the proper semantics of that relies on the backend.
   *
   * \return Always false.
   */
  bool check_ready(Ready_type) noexcept override
  { return false; }

  ~Be_file() noexcept = 0;

private:
  /// Default backend for POSIX openat, open.
  int get_entry(const char *, int, mode_t, cxx::Ref_ptr<File> *) noexcept override
  { return -ENOTDIR; }

protected:
  const char *get_mount(const char *path, cxx::Ref_ptr<File> *dir) noexcept;
};

inline
Be_file::~Be_file() noexcept {}

class Be_file_pos : public Be_file
{
public:
  Be_file_pos() noexcept : Be_file(), _pos(0) {}

  virtual off64_t size() const noexcept = 0;

  ssize_t readv(const struct iovec *v, int iovcnt) noexcept override
  {
    ssize_t r = preadv(v, iovcnt, _pos);
    if (r > 0)
      _pos += r;
    return r;
  }

  ssize_t writev(const struct iovec *v, int iovcnt) noexcept override
  {
    ssize_t r = pwritev(v, iovcnt, _pos);
    if (r > 0)
      _pos += r;
    return r;
  }

  ssize_t preadv(const struct iovec *v, int iovcnt, off64_t offset) noexcept override = 0;
  ssize_t pwritev(const struct iovec *v, int iovcnt, off64_t offset) noexcept override = 0;

  off64_t lseek(off64_t offset, int whence) noexcept override
  {
    off64_t r;
    switch (whence)
      {
      case SEEK_SET: r = offset; break;
      case SEEK_CUR: r = _pos + offset; break;
      case SEEK_END: r = size() + offset; break;
      default: return -EINVAL;
      };

    if (r < 0)
      return -EINVAL;

    _pos = r;
    return _pos;
  }

  ~Be_file_pos() noexcept = 0;

protected:
  off64_t pos() const noexcept { return _pos; }

private:
  off64_t _pos;
};

inline Be_file_pos::~Be_file_pos() noexcept {}

class Be_file_stream : public Be_file
{
public:
  ssize_t preadv(const struct iovec *v, int iovcnt, off64_t) noexcept override
  { return readv(v, iovcnt); }

  ssize_t pwritev(const struct iovec *v, int iovcnt, off64_t) noexcept override
  { return writev(v, iovcnt); }

  ~Be_file_stream() noexcept = 0;

};

inline Be_file_stream::~Be_file_stream() noexcept {}

/**
 * \brief Boilerplate class for implementing a L4Re::Vfs::File_system.
 *
 * This class already takes care of registering and unregistering the
 * file system in the global registry and implements the type() method.
 */
class Be_file_system : public File_system
{
private:
  char const *const _fstype;

public:

  /**
   * \brief Create a file-system object for the given \a fstype.
   * \param fstype The type that type() shall return.
   *
   * This constructor takes care of registering the file system
   * in the registry of L4Re::Vfs::vfs_ops.
   */
  explicit Be_file_system(char const *fstype) noexcept
  : File_system(), _fstype(fstype)
  {
    vfs_ops->register_file_system(this);
  }

  /**
   * \brief Destroy a file-system object.
   *
   * This destructor takes care of removing this file system
   * from the registry of L4Re::Vfs::vfs_ops.
   */
  ~Be_file_system() noexcept
  {
    vfs_ops->unregister_file_system(this);
  }

  /**
   * \brief Return the file-system type.
   *
   * Returns the file-system type given as \a fstype in the constructor.
   */
  char const *type() const noexcept override { return _fstype; }
};

/* Make sure filesystems can register before the constructor of libmount
 * runs */
#define L4RE_VFS_FILE_SYSTEM_ATTRIBUTE \
     __attribute__((init_priority(INIT_PRIO_LATE)))

}}
