Logo Search packages:      
Sourcecode: davfs2 version File versions  Download package

kernel_interface.c

/* 
** Interface to the kernel module.
 */

/*
 *  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 2 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 <argz.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>

#include <sys/mount.h>

#include "dav_debug.h"
#include "defaults.h"
#include "cache.h"
#include "kernel_interface.h"


/* Types and private constants from the specific kernel interfaces */
/*=================================================================*/

/* AS it is not possible to include the headers of different versions of the
   same kernel file system, this types and constants have to be maintained
   manually. */

/* Mount data structure of coda to be passed to the mount function. */
struct coda_mount_data {
      int         version;
      int         fd;
};

/* 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

/* Version of the interface for mounting coda. */
#define CODA_MOUNT_VERSION 1

/* Used in fcntl call to the coda device, to get the CODA_KERNEL_VERSION. */
#define CIOC_KERNEL_VERSION _IOWR('c', 10, size_t)

/* 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, 
                              unsigned long int mopts, uid_t owner,
                              gid_t group, mode_t mode) {

    uid_t orig = geteuid();
    seteuid(0);

    if (*kernel_fs == NULL)
        *kernel_fs = strdup("coda");
    if (*kernel_fs == NULL)
        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, mopts,
                          owner, group, mode) == 0) {
                free(*kernel_fs);
                *kernel_fs = strdup("fuse");
                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, mopts,
                      owner, group, 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");
                error(0, 0, "Coda device opened successfully.");
            } else {
                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) {
        system("/sbin/modprobe coda &>/dev/null");
        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);
            if (*dev <= 0) {
                if (mknod(path, S_IFCHR, makedev(CODA_MAJOR, minor)) == 0) {
                    chown(path, 0, 0);
                    chmod(path, S_IRUSR | S_IWUSR);
                    *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);
    DBG1("  Coda_kernel_version %u.", 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 == NULL)
        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);
    if (*dev <= 0) {
        system("/sbin/modprobe fuse &>/dev/null");
        *dev = open(path, O_RDWR | O_NONBLOCK);
    }
    if (*dev <= 0) {
        if (mknod(path, S_IFCHR, makedev(FUSE_MAJOR, FUSE_MINOR)) == 0) {
            chown(path, 0, 0);
            chmod(path, S_IRUSR | S_IWUSR);
            *dev = open(path, O_RDWR | O_NONBLOCK);
        }
    }

    free(path);
    if (*dev <= 0) {
        error(0, 0, "Could not 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 not mount using fuse kernel file system.");
    return -1;
}

Generated by  Doxygen 1.6.0   Back to index