blob: 219ee1419ea7b1db849ff204a16d5f7f2d0f9a48 [file] [log] [blame]
/*
FUSE: Filesystem in Userspace
Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU LGPL.
See the file COPYING.LIB.
*/
#include "fuse.h"
#include "fuse_opt.h"
#include "fuse_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#define FUSERMOUNT_PROG "fusermount"
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"
enum {
KEY_KERN,
KEY_ALLOW_ROOT,
KEY_RO,
KEY_HELP,
KEY_VERSION,
};
struct mount_opts {
int allow_other;
int allow_root;
int ishelp;
char *kernel_opts;
};
static const struct fuse_opt fuse_mount_opts[] = {
{ "allow_other", offsetof(struct mount_opts, allow_other), 1 },
{ "allow_root", offsetof(struct mount_opts, allow_root), 1 },
FUSE_OPT_KEY("allow_other", KEY_KERN),
FUSE_OPT_KEY("allow_root", KEY_ALLOW_ROOT),
FUSE_OPT_KEY("nonempty", KEY_KERN),
FUSE_OPT_KEY("default_permissions", KEY_KERN),
FUSE_OPT_KEY("fsname=", KEY_KERN),
FUSE_OPT_KEY("large_read", KEY_KERN),
FUSE_OPT_KEY("max_read=", KEY_KERN),
FUSE_OPT_KEY("-r", KEY_RO),
FUSE_OPT_KEY("ro", KEY_KERN),
FUSE_OPT_KEY("rw", KEY_KERN),
FUSE_OPT_KEY("suid", KEY_KERN),
FUSE_OPT_KEY("nosuid", KEY_KERN),
FUSE_OPT_KEY("dev", KEY_KERN),
FUSE_OPT_KEY("nodev", KEY_KERN),
FUSE_OPT_KEY("exec", KEY_KERN),
FUSE_OPT_KEY("noexec", KEY_KERN),
FUSE_OPT_KEY("async", KEY_KERN),
FUSE_OPT_KEY("sync", KEY_KERN),
FUSE_OPT_KEY("dirsync", KEY_KERN),
FUSE_OPT_KEY("atime", KEY_KERN),
FUSE_OPT_KEY("noatime", KEY_KERN),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_END
};
static void mount_help(void)
{
fprintf(stderr,
" -o allow_other allow access to other users\n"
" -o allow_root allow access to root\n"
" -o nonempty allow mounts over non-empty file/dir\n"
" -o default_permissions enable permission checking by kernel\n"
" -o fsname=NAME set filesystem name\n"
" -o large_read issue large read requests (2.4 only)\n"
" -o max_read=N set maximum size of read requests\n"
"\n"
);
}
static void mount_version(void)
{
system(FUSERMOUNT_PROG " --version");
}
static int fuse_mount_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
struct mount_opts *mo = data;
switch (key) {
case KEY_ALLOW_ROOT:
if (fuse_opt_add_opt(&mo->kernel_opts, "allow_other") == -1 ||
fuse_opt_add_arg(outargs, "-oallow_root") == -1)
return -1;
return 0;
case KEY_RO:
arg = "ro";
/* fall through */
case KEY_KERN:
return fuse_opt_add_opt(&mo->kernel_opts, arg);
case KEY_HELP:
mount_help();
mo->ishelp = 1;
break;
case KEY_VERSION:
mount_version();
mo->ishelp = 1;
break;
}
return 1;
}
/* return value:
* >= 0 => fd
* -1 => error
*/
static int receive_fd(int fd)
{
struct msghdr msg;
struct iovec iov;
char buf[1];
int rv;
int connfd = -1;
char ccmsg[CMSG_SPACE(sizeof(connfd))];
struct cmsghdr *cmsg;
iov.iov_base = buf;
iov.iov_len = 1;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* old BSD implementations should use msg_accrights instead of
* msg_control; the interface is different. */
msg.msg_control = ccmsg;
msg.msg_controllen = sizeof(ccmsg);
while(((rv = recvmsg(fd, &msg, 0)) == -1) && errno == EINTR);
if (rv == -1) {
perror("recvmsg");
return -1;
}
if(!rv) {
/* EOF */
return -1;
}
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg->cmsg_type == SCM_RIGHTS) {
fprintf(stderr, "got control message of unknown type %d\n",
cmsg->cmsg_type);
return -1;
}
return *(int*)CMSG_DATA(cmsg);
}
void fuse_unmount(const char *mountpoint)
{
const char *mountprog = FUSERMOUNT_PROG;
int pid;
if (!mountpoint)
return;
#ifdef HAVE_FORK
pid = fork();
#else
pid = vfork();
#endif
if(pid == -1)
return;
if(pid == 0) {
const char *argv[32];
int a = 0;
argv[a++] = mountprog;
argv[a++] = "-u";
argv[a++] = "-q";
argv[a++] = "-z";
argv[a++] = "--";
argv[a++] = mountpoint;
argv[a++] = NULL;
execvp(mountprog, (char **) argv);
exit(1);
}
waitpid(pid, NULL, 0);
}
int fuse_mount_compat22(const char *mountpoint, const char *opts)
{
const char *mountprog = FUSERMOUNT_PROG;
int fds[2], pid;
int res;
int rv;
if (!mountpoint) {
fprintf(stderr, "fuse: missing mountpoint\n");
return -1;
}
res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
if(res == -1) {
perror("fuse: socketpair() failed");
return -1;
}
#ifdef HAVE_FORK
pid = fork();
#else
pid = vfork();
#endif
if(pid == -1) {
perror("fuse: fork() failed");
close(fds[0]);
close(fds[1]);
return -1;
}
if(pid == 0) {
char env[10];
const char *argv[32];
int a = 0;
argv[a++] = mountprog;
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
}
argv[a++] = "--";
argv[a++] = mountpoint;
argv[a++] = NULL;
close(fds[1]);
fcntl(fds[0], F_SETFD, 0);
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
execvp(mountprog, (char **) argv);
perror("fuse: failed to exec fusermount");
exit(1);
}
close(fds[0]);
rv = receive_fd(fds[1]);
close(fds[1]);
waitpid(pid, NULL, 0); /* bury zombie */
return rv;
}
int fuse_mount(const char *mountpoint, struct fuse_args *args)
{
struct mount_opts mo;
int res = -1;
memset(&mo, 0, sizeof(mo));
if (args &&
fuse_opt_parse(args, &mo, fuse_mount_opts, fuse_mount_opt_proc) == -1)
return -1;
if (mo.allow_other && mo.allow_root) {
fprintf(stderr, "fuse: 'allow_other' and 'allow_root' options are mutually exclusive\n");
goto out;
}
if (mo.ishelp)
return 0;
res = fuse_mount_compat22(mountpoint, mo.kernel_opts);
out:
free(mo.kernel_opts);
return res;
}
int fuse_mount_compat1(const char *mountpoint, const char *args[])
{
/* just ignore mount args for now */
(void) args;
return fuse_mount_compat22(mountpoint, NULL);
}
__asm__(".symver fuse_mount_compat22,fuse_mount@");