/** The file containing the version in the form MAJOR.MINOR */
#define FUSE_VERSION_FILE "/proc/fs/fuse/version"
-/** Data passed to mount */
-struct fuse_mount_data {
- /** The control file descriptor */
- int fd;
-
- /** The file type of the root inode */
- unsigned int rootmode;
-
- /** The user ID of the user initiating this mount */
- unsigned int uid;
-
- /** FUSE specific mount flags */
- unsigned int flags;
-};
-
-/* FUSE mount flags: */
-
-/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
-module will check permissions based on the file mode. Otherwise no
-permission checking is done in the kernel */
-#define FUSE_DEFAULT_PERMISSIONS (1 << 0)
-
-/** If the FUSE_ALLOW_OTHER flag is given, then not only the user
- doing the mount will be allowed to access the filesystem */
-#define FUSE_ALLOW_OTHER (1 << 1)
-
-/** If the FUSE_KERNEL_CACHE flag is given, then files will be cached
- until the INVALIDATE operation is invoked */
-#define FUSE_KERNEL_CACHE (1 << 2)
-
-/** Allow FUSE to combine reads into 64k chunks. This is useful if
- the filesystem is better at handling large chunks. NOTE: in
- current implementation the raw throughput is worse for large reads
- than for small. */
-#define FUSE_LARGE_READ (1 << 3)
-
struct fuse_attr {
unsigned int mode;
unsigned int nlink;
#define FUSE_BLOCK_PAGE_SHIFT (FUSE_BLOCK_SHIFT - PAGE_CACHE_SHIFT)
+/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
+module will check permissions based on the file mode. Otherwise no
+permission checking is done in the kernel */
+#define FUSE_DEFAULT_PERMISSIONS (1 << 0)
+
+/** If the FUSE_ALLOW_OTHER flag is given, then not only the user
+ doing the mount will be allowed to access the filesystem */
+#define FUSE_ALLOW_OTHER (1 << 1)
+
+/** If the FUSE_KERNEL_CACHE flag is given, then files will be cached
+ until the INVALIDATE operation is invoked */
+#define FUSE_KERNEL_CACHE (1 << 2)
+
+/** Allow FUSE to combine reads into 64k chunks. This is useful if
+ the filesystem is better at handling large chunks. NOTE: in
+ current implementation the raw throughput is worse for large reads
+ than for small. */
+#define FUSE_LARGE_READ (1 << 3)
+
/**
* A Fuse connection.
*
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/file.h>
+#include <linux/mount.h>
#include <linux/proc_fs.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
#ifdef KERNEL_2_6
#include <linux/statfs.h>
#endif
#ifndef FS_SAFE
#define FS_SAFE 0
#endif
-#ifndef FS_BINARY_MOUNTDATA
-#define FS_BINARY_MOUNTDATA 0
-#endif
+
+struct fuse_mount_data {
+ int fd;
+ unsigned int rootmode;
+ unsigned int uid;
+ unsigned int flags;
+};
static void fuse_read_inode(struct inode *inode)
{
return out.h.error;
}
-static struct fuse_conn *get_conn(struct fuse_mount_data *d)
+enum { opt_fd, opt_rootmode, opt_uid, opt_default_permissions,
+ opt_allow_other, opt_kernel_cache, opt_large_read, opt_err };
+
+static match_table_t tokens = {
+ {opt_fd, "fd=%u"},
+ {opt_rootmode, "rootmode=%o"},
+ {opt_uid, "uid=%u"},
+ {opt_default_permissions, "default_permissions"},
+ {opt_allow_other, "allow_other"},
+ {opt_kernel_cache, "kernel_cache"},
+ {opt_large_read, "large_read"},
+ {opt_err, NULL}
+};
+
+static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
+{
+ char *p;
+ memset(d, 0, sizeof(struct fuse_mount_data));
+ d->fd = -1;
+
+ if (opt == NULL)
+ return 0;
+
+ if (opt[PAGE_SIZE - 1] != '\0')
+ return 0;
+
+ while ((p = strsep(&opt, ",")) != NULL) {
+ int token;
+ int value;
+ substring_t args[MAX_OPT_ARGS];
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+ switch (token) {
+ case opt_fd:
+ if (match_int(&args[0], &value))
+ return 0;
+ d->fd = value;
+ break;
+
+ case opt_rootmode:
+ if (match_octal(&args[0], &value))
+ return 0;
+ d->rootmode = value;
+ break;
+
+ case opt_uid:
+ if (match_int(&args[0], &value))
+ return 0;
+ d->uid = value;
+ break;
+
+ case opt_default_permissions:
+ d->flags |= FUSE_DEFAULT_PERMISSIONS;
+ break;
+
+ case opt_allow_other:
+ d->flags |= FUSE_ALLOW_OTHER;
+ break;
+
+ case opt_kernel_cache:
+ d->flags |= FUSE_KERNEL_CACHE;
+ break;
+
+ case opt_large_read:
+ d->flags |= FUSE_LARGE_READ;
+ break;
+ default:
+ return 0;
+ }
+ }
+ if (d->fd == -1)
+ return 0;
+
+ return 1;
+}
+
+static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ struct fuse_conn *fc = SB_FC(mnt->mnt_sb);
+
+ seq_printf(m, ",uid=%u", fc->uid);
+ if (fc->flags & FUSE_DEFAULT_PERMISSIONS)
+ seq_puts(m, ",default_permissions");
+ if (fc->flags & FUSE_ALLOW_OTHER)
+ seq_puts(m, ",allow_other");
+ if (fc->flags & FUSE_KERNEL_CACHE)
+ seq_puts(m, ",kernel_cache");
+ if (fc->flags & FUSE_LARGE_READ)
+ seq_puts(m, ",large_read");
+ return 0;
+}
+
+static struct fuse_conn *get_conn(int fd)
{
struct fuse_conn *fc = NULL;
struct file *file;
struct inode *ino;
- if (d == NULL) {
- printk("fuse_read_super: Bad mount data\n");
- return NULL;
- }
-
- file = fget(d->fd);
+ file = fget(fd);
ino = NULL;
if (file)
ino = file->f_dentry->d_inode;
if (!ino || !proc_fuse_dev || proc_fuse_dev->low_ino != ino->i_ino) {
- printk("fuse_read_super: Bad file: %i\n", d->fd);
+ printk("FUSE: bad communication file descriptor: %i\n", fd);
goto out;
}
.clear_inode = fuse_clear_inode,
.put_super = fuse_put_super,
.statfs = fuse_statfs,
+ .show_options = fuse_show_options,
};
static int fuse_read_super(struct super_block *sb, void *data, int silent)
{
struct fuse_conn *fc;
struct inode *root;
- struct fuse_mount_data *d = data;
+ struct fuse_mount_data d;
- if (!capable(CAP_SYS_ADMIN)) {
- if (d->flags & FUSE_ALLOW_OTHER)
- return -EPERM;
- }
+ if (!parse_fuse_opt((char *) data, &d))
+ return -EINVAL;
+
+ if ((d.flags & FUSE_ALLOW_OTHER) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_export_op = &fuse_export_operations;
#endif
- fc = get_conn(d);
+ fc = get_conn(d.fd);
if (fc == NULL)
return -EINVAL;
+
spin_lock(&fuse_lock);
if (fc->sb != NULL) {
printk("fuse_read_super: connection already mounted\n");
return -EINVAL;
}
fc->sb = sb;
- fc->flags = d->flags;
- fc->uid = d->uid;
+ fc->flags = d.flags;
+ fc->uid = d.uid;
spin_unlock(&fuse_lock);
/* fc is needed in fuse_init_file_inode which could be called
from get_root_inode */
SB_FC(sb) = fc;
- root = get_root_inode(sb, d->rootmode);
+ root = get_root_inode(sb, d.rootmode);
if (root == NULL) {
printk("fuse_read_super: failed to get root inode\n");
return -EINVAL;
.name = "fuse",
.get_sb = fuse_get_sb,
.kill_sb = kill_anon_super,
- .fs_flags = FS_SAFE | FS_BINARY_MOUNTDATA,
+ .fs_flags = FS_SAFE,
};
#else
static struct super_block *fuse_read_super_compat(struct super_block *sb,
const char *progname;
+struct fuse_opts {
+ int kernel_cache;
+ int default_permissions;
+ int allow_other;
+ int large_read;
+};
+
static const char *get_user_name()
{
struct passwd *pw = getpwuid(getuid());
}
static int do_mount(const char *dev, const char *mnt, const char *type,
- mode_t rootmode, int fd, int fuseflags)
+ mode_t rootmode, int fd, struct fuse_opts *opts)
{
int res;
- struct fuse_mount_data data;
int flags = MS_NOSUID | MS_NODEV;
+ char optbuf[1024];
+ char *s = optbuf;
if(getuid() != 0) {
res = drop_privs();
return -1;
}
- data.fd = fd;
- data.rootmode = rootmode;
- data.uid = getuid();
- data.flags = fuseflags;
-
- res = mount(dev, mnt, type, flags, &data);
+ s += sprintf(s, "fd=%i,rootmode=%o,uid=%i", fd, rootmode, getuid());
+ if (opts->kernel_cache)
+ s += sprintf(s, ",kernel_cache");
+ if (opts->default_permissions)
+ s += sprintf(s, ",default_permissions");
+ if (opts->allow_other)
+ s += sprintf(s, ",allow_other");
+ if (opts->large_read)
+ s += sprintf(s, ",large_read");
+
+ res = mount(dev, mnt, type, flags, optbuf);
if(res == -1)
fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno));
return 0;
}
-static int mount_fuse(const char *mnt, int flags, const char *fsname)
+static int mount_fuse(const char *mnt, struct fuse_opts *opts,
+ const char *fsname)
{
int res;
int fd;
if(fsname == NULL)
fsname = dev;
- res = do_mount(fsname, mnt, type, stbuf.st_mode & S_IFMT, fd, flags);
+ res = do_mount(fsname, mnt, type, stbuf.st_mode & S_IFMT, fd, opts);
if(res == -1)
return -1;
int lazy = 0;
char *commfd;
const char *fsname = NULL;
- int flags = 0;
int quiet = 0;
+ struct fuse_opts opts;
int cfd;
+
+ memset(&opts, 0, sizeof(struct fuse_opts));
progname = argv[0];
for(a = 1; a < argc; a++) {
switch(argv[a][1]) {
case 'c':
- flags |= FUSE_KERNEL_CACHE;
+ opts.kernel_cache = 1;
break;
case 'h':
break;
case 'p':
- flags |= FUSE_DEFAULT_PERMISSIONS;
+ opts.default_permissions = 1;
break;
case 'x':
progname, argv[a]);
exit(1);
}
- flags |= FUSE_ALLOW_OTHER;
+ opts.allow_other = 1;
break;
case 'n':
break;
case 'l':
- flags |= FUSE_LARGE_READ;
+ opts.large_read = 1;
break;
case 'q':
exit(1);
}
- fd = mount_fuse(mnt, flags, fsname);
+ fd = mount_fuse(mnt, &opts, fsname);
if(fd == -1)
exit(1);