blob: bb02a3a25ee603363084e993d6875a22d5f73383 [file] [log] [blame]
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2005 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB.
*/
#include "fuse_i.h"
#include "fuse_opt.h"
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <signal.h>
static struct fuse *fuse_instance;
static void usage(const char *progname)
{
if (progname)
fprintf(stderr,
"usage: %s mountpoint [FUSE options]\n\n", progname);
fprintf(stderr,
"FUSE options:\n"
" -d enable debug output (implies -f)\n"
" -f foreground operation\n"
" -s disable multi-threaded operation\n"
" -r mount read only (equivalent to '-o ro')\n"
" -o opt,[opt...] mount options\n"
" -h print help\n"
"\n"
"Mount options:\n"
" default_permissions enable permission checking\n"
" allow_other allow access to other users\n"
" allow_root allow access to root\n"
" kernel_cache cache files in kernel\n"
" large_read issue large read requests (2.4 only)\n"
" direct_io use direct I/O\n"
" max_read=N set maximum size of read requests\n"
" hard_remove immediate removal (don't hide files)\n"
" debug enable debug output\n"
" fsname=NAME set filesystem name in mtab\n"
" use_ino let filesystem set inode numbers\n"
" readdir_ino try to fill in d_ino in readdir\n"
" nonempty allow mounts over non-empty file/dir\n"
" umask=M set file permissions (octal)\n"
" uid=N set file owner\n"
" gid=N set file group\n"
);
}
static void exit_handler(int sig)
{
(void) sig;
if (fuse_instance != NULL)
fuse_exit(fuse_instance);
}
static int set_one_signal_handler(int sig, void (*handler)(int))
{
struct sigaction sa;
struct sigaction old_sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handler;
sigemptyset(&(sa.sa_mask));
sa.sa_flags = 0;
if (sigaction(sig, NULL, &old_sa) == -1) {
perror("FUSE: cannot get old signal handler");
return -1;
}
if (old_sa.sa_handler == SIG_DFL &&
sigaction(sig, &sa, NULL) == -1) {
perror("Cannot set signal handler");
return -1;
}
return 0;
}
static int set_signal_handlers(void)
{
if (set_one_signal_handler(SIGHUP, exit_handler) == -1 ||
set_one_signal_handler(SIGINT, exit_handler) == -1 ||
set_one_signal_handler(SIGTERM, exit_handler) == -1 ||
set_one_signal_handler(SIGPIPE, SIG_IGN) == -1)
return -1;
return 0;
}
enum {
KEY_HELP,
KEY_HELP_NOHEADER,
KEY_DEBUG,
KEY_KERN,
KEY_ALLOW_ROOT,
KEY_RO,
};
struct helper_opts {
const char *progname;
int singlethread;
int foreground;
int allow_other;
int allow_root;
int fsname;
char *kernel_opts;
char *lib_opts;
char *mountpoint;
};
#define FUSE_HELPER_OPT(t, p) { t, offsetof(struct helper_opts, p), 1 }
#define FUSE_HELPER_KEY(t, k) { t, FUSE_OPT_OFFSET_KEY, k }
static const struct fuse_opt fuse_helper_opts[] = {
FUSE_HELPER_OPT("-d", foreground),
FUSE_HELPER_OPT("debug", foreground),
FUSE_HELPER_OPT("-f", foreground),
FUSE_HELPER_OPT("-s", singlethread),
FUSE_HELPER_OPT("allow_other", allow_other),
FUSE_HELPER_OPT("allow_root", allow_root),
FUSE_HELPER_OPT("fsname=", fsname),
FUSE_HELPER_KEY("-h", KEY_HELP),
FUSE_HELPER_KEY("--help", KEY_HELP),
FUSE_HELPER_KEY("-ho", KEY_HELP_NOHEADER),
FUSE_HELPER_KEY("-d", KEY_DEBUG),
FUSE_HELPER_KEY("debug", KEY_DEBUG),
FUSE_HELPER_KEY("allow_other", KEY_KERN),
FUSE_HELPER_KEY("allow_root", KEY_ALLOW_ROOT),
FUSE_HELPER_KEY("nonempty", KEY_KERN),
FUSE_HELPER_KEY("default_permissions", KEY_KERN),
FUSE_HELPER_KEY("fsname=", KEY_KERN),
FUSE_HELPER_KEY("large_read", KEY_KERN),
FUSE_HELPER_KEY("max_read=", KEY_KERN),
FUSE_HELPER_KEY("-r", KEY_RO),
FUSE_HELPER_KEY("ro", KEY_KERN),
FUSE_HELPER_KEY("rw", KEY_KERN),
FUSE_HELPER_KEY("suid", KEY_KERN),
FUSE_HELPER_KEY("nosuid", KEY_KERN),
FUSE_HELPER_KEY("dev", KEY_KERN),
FUSE_HELPER_KEY("nodev", KEY_KERN),
FUSE_HELPER_KEY("exec", KEY_KERN),
FUSE_HELPER_KEY("noexec", KEY_KERN),
FUSE_HELPER_KEY("async", KEY_KERN),
FUSE_HELPER_KEY("sync", KEY_KERN),
FUSE_HELPER_KEY("atime", KEY_KERN),
FUSE_HELPER_KEY("noatime", KEY_KERN),
FUSE_OPT_END
};
static int fuse_helper_opt_proc(void *data, const char *arg, int key)
{
struct helper_opts *hopts = data;
switch (key) {
case KEY_HELP:
case KEY_HELP_NOHEADER:
usage(key == KEY_HELP ? hopts->progname : NULL);
exit(1);
case FUSE_OPT_KEY_OPT:
return fuse_opt_add_opt(&hopts->lib_opts, arg);
case FUSE_OPT_KEY_NONOPT:
if (hopts->mountpoint)
break;
return fuse_opt_add_opt(&hopts->mountpoint, arg);
case KEY_DEBUG:
return fuse_opt_add_opt(&hopts->lib_opts, "debug");
case KEY_ALLOW_ROOT:
if (fuse_opt_add_opt(&hopts->kernel_opts, "allow_other") == -1 ||
fuse_opt_add_opt(&hopts->lib_opts, "allow_root") == -1)
return -1;
return 0;
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_KERN:
return fuse_opt_add_opt(&hopts->kernel_opts, arg);
}
fprintf(stderr, "fuse: invalid option `%s'\n", arg);
return -1;
}
static int fuse_parse_cmdline(int argc, const char *argv[],
struct helper_opts *hopts)
{
int res;
hopts->progname = argv[0];
res = fuse_opt_parse(argc, (char **) argv, hopts, fuse_helper_opts,
fuse_helper_opt_proc, NULL, NULL);
if (res == -1)
return -1;
if (hopts->allow_other && hopts->allow_root) {
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
return -1;
}
if (!hopts->fsname) {
char *fsname_opt;
const char *basename = strrchr(argv[0], '/');
if (basename == NULL)
basename = argv[0];
else if (basename[1] != '\0')
basename++;
fsname_opt = (char *) malloc(strlen(basename) + 64);
if (fsname_opt == NULL) {
fprintf(stderr, "fuse: memory allocation failed\n");
return -1;
}
sprintf(fsname_opt, "fsname=%s", basename);
res = fuse_opt_add_opt(&hopts->kernel_opts, fsname_opt);
free(fsname_opt);
if (res == -1)
return -1;
}
return 0;
}
static struct fuse *fuse_setup_common(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size,
char **mountpoint,
int *multithreaded,
int *fd,
int compat)
{
struct fuse *fuse;
struct helper_opts hopts;
int res;
if (fuse_instance != NULL) {
fprintf(stderr, "fuse: fuse_setup() called twice\n");
return NULL;
}
memset(&hopts, 0, sizeof(hopts));
res = fuse_parse_cmdline(argc, (const char **) argv, &hopts);
if (res == -1)
goto err_free;
*fd = fuse_mount(hopts.mountpoint, hopts.kernel_opts);
if (*fd == -1)
goto err_free;
fuse = fuse_new_common(*fd, hopts.lib_opts, op, op_size, compat);
if (fuse == NULL)
goto err_unmount;
if (!hopts.foreground) {
res = daemon(0, 0);
if (res == -1) {
perror("fuse: failed to daemonize program\n");
goto err_destroy;
}
}
res = set_signal_handlers();
if (res == -1)
goto err_destroy;
*mountpoint = hopts.mountpoint;
*multithreaded = !hopts.singlethread;
fuse_instance = fuse;
free(hopts.kernel_opts);
free(hopts.lib_opts);
return fuse;
err_destroy:
fuse_destroy(fuse);
err_unmount:
fuse_unmount(hopts.mountpoint);
err_free:
free(hopts.mountpoint);
free(hopts.kernel_opts);
free(hopts.lib_opts);
return NULL;
}
struct fuse *fuse_setup(int argc, char *argv[],
const struct fuse_operations *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd)
{
return fuse_setup_common(argc, argv, op, op_size, mountpoint,
multithreaded, fd, 0);
}
void fuse_teardown(struct fuse *fuse, int fd, char *mountpoint)
{
(void) fd;
if (fuse_instance != fuse)
fprintf(stderr, "fuse: fuse_teardown() with unknown fuse object\n");
else
fuse_instance = NULL;
fuse_unmount(mountpoint);
fuse_destroy(fuse);
free(mountpoint);
}
static int fuse_main_common(int argc, char *argv[],
const struct fuse_operations *op, size_t op_size,
int compat)
{
struct fuse *fuse;
char *mountpoint;
int multithreaded;
int res;
int fd;
fuse = fuse_setup_common(argc, argv, op, op_size, &mountpoint,
&multithreaded, &fd, compat);
if (fuse == NULL)
return 1;
if (multithreaded)
res = fuse_loop_mt(fuse);
else
res = fuse_loop(fuse);
fuse_teardown(fuse, fd, mountpoint);
if (res == -1)
return 1;
return 0;
}
int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op,
size_t op_size)
{
return fuse_main_common(argc, argv, op, op_size, 0);
}
#undef fuse_main
int fuse_main(void)
{
fprintf(stderr, "fuse_main(): This function does not exist\n");
return -1;
}
#ifndef __FreeBSD__
#include "fuse_compat.h"
struct fuse *fuse_setup_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size, char **mountpoint,
int *multithreaded, int *fd)
{
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
op_size, mountpoint, multithreaded, fd, 22);
}
struct fuse *fuse_setup_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op,
char **mountpoint, int *multithreaded,
int *fd)
{
return fuse_setup_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat2),
mountpoint, multithreaded, fd, 21);
}
int fuse_main_real_compat22(int argc, char *argv[],
const struct fuse_operations_compat22 *op,
size_t op_size)
{
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
op_size, 22);
}
void fuse_main_compat1(int argc, char *argv[],
const struct fuse_operations_compat1 *op)
{
fuse_main_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat1), 11);
}
int fuse_main_compat2(int argc, char *argv[],
const struct fuse_operations_compat2 *op)
{
return fuse_main_common(argc, argv, (struct fuse_operations *) op,
sizeof(struct fuse_operations_compat2), 21);
}
__asm__(".symver fuse_setup_compat2,__fuse_setup@");
__asm__(".symver fuse_setup_compat22,fuse_setup@FUSE_2.2");
__asm__(".symver fuse_teardown,__fuse_teardown@");
__asm__(".symver fuse_main_compat2,fuse_main@");
__asm__(".symver fuse_main_real_compat22,fuse_main_real@FUSE_2.2");
#endif /* __FreeBSD__ */