/*  kernel_interface.c: interface to fuse and coda kernel mocule.
    Copyright (C) 2006, 2007, 2008, 2009 Werner Baumann

    This file is part of davfs2.

    davfs2 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 3 of the License, or
    (at your option) any later version.

    davfs2 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 davfs2; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */


#include "config.h"

#include <error.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif

#include "defaults.h"
#include "mount_davfs.h"
#include "cache.h"
//#include "coda.h"
#include "kernel_interface.h"

#ifdef ENABLE_NLS
#define _(String) gettext(String)
#else
#define _(String) String
#endif


/* Private constants */
/*===================*/

/* Name, major number and minor number of the devices to communicate with the
   kernel file system. */
//#define CODA_DEV_NAME "cfs"
//#define CODA_MAJOR 67
//#define MAX_CODADEVS  5   /* Coda minor number may be from 0 to 4. */
#define FUSE_DEV_NAME "fuse"
#define FUSE_MAJOR 10
#define FUSE_MINOR 229

/* Minimum size of buffer for fuse. Must be big enough for any version.*/
#define FUSE_MIN_READ_BUFFER 16384


/* Private function prototypes */
/*=============================*/

/*static int
init_coda(int *dev, dav_run_msgloop_fn *msg_loop, void **mdata);*/

static int
init_fuse(int *dev, dav_run_msgloop_fn *msg_loop, void **mdata,
          size_t *buf_size, const char *url, const char *mpoint,
          unsigned long int mopts, uid_t owner, gid_t group, mode_t mode);


/* Public functions */
/*==================*/

int
dav_init_kernel_interface(int *dev, dav_run_msgloop_fn *msg_loop, void **mdata,
                          char **kernel_fs, size_t *buf_size, const char *url,
                          const char *mpoint, const dav_args *args)
{
    uid_t orig = geteuid();
    seteuid(0);

    if (!*kernel_fs)
        *kernel_fs = strdup("fuse");
    if (!*kernel_fs) abort();

    int mounted = 0;
    /* if (strcmp(*kernel_fs, "coda") == 0) {

        if (init_coda(dev, msg_loop, mdata) != 0) {
            error(0, 0, _("trying fuse kernel file system"));
            if (init_fuse(dev, msg_loop, mdata, buf_size, url, mpoint,
                          args->mopts, args->uid, args->gid, args->dir_mode)
                                                                        == 0) {
                free(*kernel_fs);
                *kernel_fs = strdup("fuse");
                if (!*kernel_fs) abort();
                mounted = 1;
                error(0, 0, _("fuse device opened successfully"));
            } else {
                exit(EXIT_FAILURE);
            }
        }

    } else */
	if (strcmp(*kernel_fs, "fuse") == 0) {

        if (init_fuse(dev, msg_loop, mdata, buf_size, url, mpoint, args->mopts,
                      args->uid, args->gid, args->dir_mode) == 0) {
            mounted = 1;
        } else {
            /*error(0, 0, _("trying coda kernel file system"));
            if (init_coda(dev, msg_loop, mdata) == 0) {
                free(*kernel_fs);
                *kernel_fs = strdup("coda");
                if (*kernel_fs == NULL)
                    abort();
                error(0, 0, _("coda device opened successfully"));
            } else {
                exit(EXIT_FAILURE);
            }*/
            exit(EXIT_FAILURE);
        }

    } else {

        error(EXIT_FAILURE, 0, _("unknown kernel file system %s"), *kernel_fs);
    }

    seteuid(orig);
    return mounted;
}


/* Private functions */
/*===================*/

/*static int
init_coda(int *dev, dav_run_msgloop_fn *msg_loop, void **mdata)
{
    *dev = 0;
    int minor = 0;
    while (*dev <= 0 && minor < MAX_CODADEVS) {
        char *path;
        if (asprintf(&path, "%s/%s%i", DAV_DEV_DIR, CODA_DEV_NAME, minor) < 0)
            abort();
        *dev = open(path, O_RDWR | O_NONBLOCK);
        free(path);
        ++minor;
    }

    if (*dev <= 0) {
        error(0, 0, _("no free coda device to mount"));
        return -1;
    }

    int version = 0;
    ioctl(*dev, CIOC_KERNEL_VERSION, &version);
    if (version == 2) {
#if SIZEOF_VOID_P == 8
        error(0, 0,
              _("CODA_KERNEL_VERSION %u can not be used on 64 bit systems"),
              version);
        close(*dev);
        return -1;
#else
        *msg_loop = dav_coda2_loop;
#endif
    } else if (version == 3) {
        *msg_loop = dav_coda3_loop;
    } else {
        error(0, 0, _("CODA_KERNEL_VERSION %u not supported"), version);
        close(*dev);
        return -1;
    }

    struct coda_mount_data *md = malloc(sizeof(struct coda_mount_data));
    if (!md) abort();
    md->version = CODA_MOUNT_VERSION;
    md->fd = *dev;
    *mdata = md;

    return 0;
}*/


static int
init_fuse(int *dev, dav_run_msgloop_fn *msg_loop, void **mdata,
          size_t *buf_size, const char *url, const char *mpoint,
          unsigned long int mopts, uid_t owner, gid_t group, mode_t mode)
{
    char *path;
    if (asprintf(&path, "%s/%s", DAV_DEV_DIR, FUSE_DEV_NAME) < 0)
            abort();

    *dev = open(path, O_RDWR | O_NONBLOCK);
    free(path);
    if (*dev <= 0) {
        error(0, 0, _("can't open fuse device"));
        return -1;
    }

    if (*buf_size < (FUSE_MIN_READ_BUFFER + 4096)) {
        *buf_size = FUSE_MIN_READ_BUFFER + 4096;
    }

    /* fuse kernel version 7 demands option group_id. */
#if SIZEOF_VOID_P == 8
    if (asprintf((char **) mdata, "fd=%i,rootmode=%o,user_id=%i,group_id=%i,"
                 "allow_other,max_read=%lu", *dev, mode, owner, group,
                 *buf_size - 4096) < 0)
        abort();
#else
    if (asprintf((char **) mdata, "fd=%i,rootmode=%o,user_id=%i,group_id=%i,"
                 "allow_other,max_read=%u", *dev, mode, owner, group,
                 *buf_size - 4096) < 0)
        abort();
#endif
    if (mount(url, mpoint, "fuse", mopts, *mdata) == 0) {
        *msg_loop = dav_fuse7_loop;
        return 0;
    }

    /* fuse kernel version 5 does not allow option group_id. */
/*    free(*mdata);
#if SIZEOF_VOID_P == 8
    if (asprintf((char **) mdata,
                 "fd=%i,rootmode=%o,user_id=%i,allow_other,max_read=%lu",
                 *dev, mode, owner, *buf_size - 4096) < 0)
        abort();
#else
    if (asprintf((char **) mdata,
                 "fd=%i,rootmode=%o,user_id=%i,allow_other,max_read=%u",
                 *dev, mode, owner, *buf_size - 4096) < 0)
        abort();
#endif
    if (mount(url, mpoint, "fuse", mopts, *mdata) == 0) {
        *msg_loop = dav_fuse5_loop;
        return 0;
    }
*/

    free(*mdata);
    close(*dev);
    error(0, 0, _("can't mount using fuse kernel file system"));
    return -1;
}