/*
 *  Copyright (C) 2024  stsp
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <string.h>
#include <assert.h>
#include "emu.h"
#include "utilities.h"
#include "fslib_ops.h"
#include "fslib.h"

static const struct fslib_ops *fssvc;
static int num_def_drives;

int mfs_define_drive(const char *path)
{
  int ret;

  ret = fssvc->add_path(path);
  assert(ret != -1);
  return ret + 1;
}

int fslib_add_path_list(const char *list)
{
  int ret = fssvc->add_path_list(list);
  assert(ret == 0);
  return ret;
}

int fslib_add_path_ex(const char *path)
{
  int ret = fssvc->add_path_ex(path);
  assert(ret == 0);
  return ret;
}

int mfs_open_file(int mfs_idx, const char *path, int flags)
{
  return fssvc->open(mfs_idx - 1, path, flags);
}

int mfs_create_file(int mfs_idx, const char *path, int flags, mode_t mode)
{
  return fssvc->create(mfs_idx - 1, path, flags, mode);
}

int mfs_unlink_file(int mfs_idx, const char *path)
{
  return fssvc->unlink(mfs_idx - 1, path);
}

int mfs_setxattr_file(int mfs_idx, const char *path, int attr)
{
  return fssvc->setxattr(mfs_idx - 1, path, attr);
}

int mfs_getxattr_file(int mfs_idx, const char *path)
{
  return fssvc->getxattr(mfs_idx - 1, path);
}

int mfs_rename_file(int mfs_idx, const char *oldpath, const char *newpath)
{
  return fssvc->rename(mfs_idx - 1, oldpath, mfs_idx - 1, newpath);
}

int mfs_mkdir(int mfs_idx, const char *path, mode_t mode)
{
  return fssvc->mkdir(mfs_idx - 1, path, mode);
}

int mfs_rmdir(int mfs_idx, const char *path)
{
  return fssvc->rmdir(mfs_idx - 1, path);
}

int mfs_stat_file(int mfs_idx, const char *path, struct stat *sb)
{
  return fssvc->stat(mfs_idx - 1, path, sb);
}

int do_mfs_statvfs(int mfs_idx, const char *path, struct statvfs *sb)
{
  return fssvc->statvfs(mfs_idx - 1, path, sb);
}

int mfs_access(int mfs_idx, const char *path, int mode)
{
  return fssvc->access(mfs_idx - 1, path, mode);
}

int mfs_utime(int mfs_idx, const char *fpath, time_t atime, time_t mtime)
{
  return fssvc->utime(mfs_idx - 1, fpath, atime, mtime);
}

void *mfs_open_async(int mfs_idx, const char *path, int flags)
{
  return fssvc->open_async(mfs_idx - 1, path, flags);
}

int mfs_async_getfd(void *handle)
{
    return fssvc->async_getfd(handle);
}

static const char *def_name =
#ifdef SEARPC_SUPPORT
  "rpc"
#else
  "local"
#endif
  ;

void fslib_init(plist_idx_t plist_idx, setattr_t setattr_cb,
    getattr_t getattr_cb)
{
  int err;

#ifdef SEARPC_SUPPORT
  load_plugin("searpc");
#endif
  fslocal_init();
  if (!fssvc && !config.fs_backend) {
    def_name = "local";
    fslocal_init();
    assert(fssvc);
  }
  if (!fssvc) {
    assert(config.fs_backend);
    error("fs service %s unavailable\n", config.fs_backend);
    _exit(1);
  }
  err = fssvc->init(plist_idx, setattr_cb, getattr_cb);
  assert(!err);
}

void fslib_seal(void)
{
  num_def_drives = fssvc->seal();
  assert(num_def_drives != -1);
}

void fslib_done(void)
{
  fssvc->exit();
}

int fslib_path_ok(int idx, const char *path)
{
  return fssvc->path_ok(idx, path);
}

int fslib_num_drives(void)
{
  return num_def_drives;
}

int fslib_shm_open(const char *name, int oflag, mode_t mode)
{
  return fssvc->shm_open(name, oflag, mode);
}

int fslib_shm_unlink(const char *name)
{
  return fssvc->shm_unlink(name);
}

int fslib_set_command(int subsys, int cookie, const char *cmd)
{
  return fssvc->set_command(subsys, cookie, cmd);
}

int fslib_popen(int subsys, int mfs_idx, const char *path, int cookie,
    struct popen2 *file)
{
  return fssvc->popen(subsys, mfs_idx - 1, path, cookie, file);
}

int fslib_popen_knownpath(int subsys, int cookie, struct popen2 *file)
{
  return fssvc->popen_knownpath(subsys, cookie, file);
}

int fslib_waitpid(int pid, int *status)
{
  return fssvc->waitpid(pid, status);
}

int fslib_debug(int mfs_idx, const char *file)
{
  return fssvc->debug(mfs_idx - 1, file);
}

void fslib_register_ops(const struct fslib_ops *ops)
{
  const char *expect = (config.fs_backend ?: def_name);
  if (strcmp(ops->name, expect) != 0)
    return;
  assert(!fssvc);
  if ((ops->flags & FSFLG_NOSUID) && running_suid_orig()) {
    error("FS \"%s\" does not support privsep mode, try -no-priv-sep\n",
        ops->name);
    config.exitearly = 1;
  }
  fssvc = ops;
}

typedef int (*dmx_fn)(const char *, int, struct popen2 *);
static const dmx_fn dmx_ops[SUBSYS_MAX] = {
  unix_run_secure,
};

int fslib_demux(int subsys, const char *path, int cookie, struct popen2 *file)
{
  assert(subsys < SUBSYS_MAX);
  return dmx_ops[subsys](path, cookie, file);
}

typedef int (*dmx_fn_kp)(int, struct popen2 *);
static const dmx_fn_kp dmx_ops_kp[SUBSYS_KP_MAX] = {
  lpt_popen,
};

int fslib_demux_kp(int subsys, int cookie, struct popen2 *file)
{
  assert(subsys < SUBSYS_KP_MAX);
  return dmx_ops_kp[subsys](cookie, file);
}
