From: Miklos Szeredi This patch adds miscellaneous mount options to the FUSE filesystem. The following mount options are added: o default_permissions: check permissions with generic_permission() o allow_other: allow other users to access files o allow_root: allow root to access files o kernel_cache: don't invalidate page cache on open Signed-off-by: Miklos Szeredi Signed-off-by: Andrew Morton --- fs/fuse/dir.c | 38 +++++++++++++++++++++++++++++++++++--- fs/fuse/file.c | 2 +- fs/fuse/fuse_i.h | 20 ++++++++++++++++++++ fs/fuse/inode.c | 51 ++++++++++++++++++++++++++++----------------------- 4 files changed, 84 insertions(+), 27 deletions(-) diff -puN fs/fuse/dir.c~fuse-mount-options fs/fuse/dir.c --- 25/fs/fuse/dir.c~fuse-mount-options 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/dir.c 2005-05-10 02:22:00.000000000 -0700 @@ -418,7 +418,10 @@ static int fuse_revalidate(struct dentry struct fuse_conn *fc = get_fuse_conn(inode); if (get_node_id(inode) == FUSE_ROOT_ID) { - if (current->fsuid != fc->user_id) + if (!(fc->flags & FUSE_ALLOW_OTHER) && + current->fsuid != fc->user_id && + (!(fc->flags & FUSE_ALLOW_ROOT) || + !capable(CAP_DAC_OVERRIDE))) return -EACCES; } else if (time_before_eq(jiffies, fi->i_time)) return 0; @@ -430,9 +433,32 @@ static int fuse_permission(struct inode { struct fuse_conn *fc = get_fuse_conn(inode); - if (current->fsuid != fc->user_id) + if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id && + (!(fc->flags & FUSE_ALLOW_ROOT) || !capable(CAP_DAC_OVERRIDE))) return -EACCES; - else { + else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { + int err = generic_permission(inode, mask, NULL); + + /* If permission is denied, try to refresh file + attributes. This is also needed, because the root + node will at first have no permissions */ + if (err == -EACCES) { + err = fuse_do_getattr(inode); + if (!err) + err = generic_permission(inode, mask, NULL); + } + + /* FIXME: Need some mechanism to revoke permissions: + currently if the filesystem suddenly changes the + file mode, we will not be informed about it, and + continue to allow access to the file/directory. + + This is actually not so grave, since the user can + simply keep access to the file/directory anyway by + keeping it open... */ + + return err; + } else { int mode = inode->i_mode; if ((mask & MAY_WRITE) && IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) @@ -636,6 +662,12 @@ static int fuse_setattr(struct dentry *e int err; int is_truncate = 0; + if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { + err = inode_change_ok(inode, attr); + if (err) + return err; + } + if (attr->ia_valid & ATTR_SIZE) { unsigned long limit; is_truncate = 1; diff -puN fs/fuse/file.c~fuse-mount-options fs/fuse/file.c --- 25/fs/fuse/file.c~fuse-mount-options 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/file.c 2005-05-10 02:22:00.000000000 -0700 @@ -70,7 +70,7 @@ static int fuse_open(struct inode *inode else request_send(fc, req); err = req->out.h.error; - if (!err) + if (!err && !(fc->flags & FUSE_KERNEL_CACHE)) invalidate_inode_pages(inode->i_mapping); if (err) { fuse_request_free(ff->release_req); diff -puN fs/fuse/fuse_i.h~fuse-mount-options fs/fuse/fuse_i.h --- 25/fs/fuse/fuse_i.h~fuse-mount-options 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/fuse_i.h 2005-05-10 02:22:00.000000000 -0700 @@ -21,6 +21,23 @@ /** If more requests are outstanding, then the operation will block */ #define FUSE_MAX_OUTSTANDING 10 +/** 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 cached data will not + be flushed on open */ +#define FUSE_KERNEL_CACHE (1 << 2) + +/** Allow root and setuid-root programs to access fuse-mounted + filesystems */ +#define FUSE_ALLOW_ROOT (1 << 4) + /** FUSE inode */ struct fuse_inode { /** Inode data */ @@ -185,6 +202,9 @@ struct fuse_conn { /** The user id for this mount */ uid_t user_id; + /** The fuse mount flags for this mount */ + unsigned flags; + /** Readers of the connection are waiting on this */ wait_queue_head_t waitq; diff -puN fs/fuse/inode.c~fuse-mount-options fs/fuse/inode.c --- 25/fs/fuse/inode.c~fuse-mount-options 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/inode.c 2005-05-10 02:22:00.000000000 -0700 @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -25,11 +24,6 @@ MODULE_LICENSE("GPL"); spinlock_t fuse_lock; static kmem_cache_t *fuse_inode_cachep; -static int mount_count; - -static int mount_max = 1000; -module_param(mount_max, int, 0644); -MODULE_PARM_DESC(mount_max, "Maximum number of FUSE mounts allowed, if -1 then unlimited (default: 1000)"); #define FUSE_SUPER_MAGIC 0x65735546 @@ -37,6 +31,7 @@ struct fuse_mount_data { int fd; unsigned rootmode; unsigned user_id; + unsigned flags; }; static struct inode *fuse_alloc_inode(struct super_block *sb) @@ -196,9 +191,9 @@ static void fuse_put_super(struct super_ struct fuse_conn *fc = get_fuse_conn_super(sb); spin_lock(&fuse_lock); - mount_count --; fc->sb = NULL; fc->user_id = 0; + fc->flags = 0; /* Flush all readers on this fs */ wake_up_all(&fc->waitq); fuse_release_conn(fc); @@ -298,6 +293,22 @@ static int parse_fuse_opt(char *opt, str d->user_id = value; break; + case OPT_DEFAULT_PERMISSIONS: + d->flags |= FUSE_DEFAULT_PERMISSIONS; + break; + + case OPT_ALLOW_OTHER: + d->flags |= FUSE_ALLOW_OTHER; + break; + + case OPT_ALLOW_ROOT: + d->flags |= FUSE_ALLOW_ROOT; + break; + + case OPT_KERNEL_CACHE: + d->flags |= FUSE_KERNEL_CACHE; + break; + default: return 0; } @@ -313,6 +324,14 @@ static int fuse_show_options(struct seq_ struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); seq_printf(m, ",user_id=%u", fc->user_id); + 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_ALLOW_ROOT) + seq_puts(m, ",allow_root"); + if (fc->flags & FUSE_KERNEL_CACHE) + seq_puts(m, ",kernel_cache"); return 0; } @@ -344,6 +363,7 @@ static struct fuse_conn *new_conn(void) memset(fc, 0, sizeof(*fc)); fc->sb = NULL; fc->file = NULL; + fc->flags = 0; fc->user_id = 0; init_waitqueue_head(&fc->waitq); INIT_LIST_HEAD(&fc->pending); @@ -407,17 +427,6 @@ static struct super_operations fuse_supe .show_options = fuse_show_options, }; -static int inc_mount_count(void) -{ - int success = 0; - spin_lock(&fuse_lock); - mount_count ++; - if (mount_max == -1 || mount_count <= mount_max) - success = 1; - spin_unlock(&fuse_lock); - return success; -} - static int fuse_fill_super(struct super_block *sb, void *data, int silent) { struct fuse_conn *fc; @@ -444,14 +453,11 @@ static int fuse_fill_super(struct super_ if (IS_ERR(fc)) return PTR_ERR(fc); + fc->flags = d.flags; fc->user_id = d.user_id; *get_fuse_conn_super_p(sb) = fc; - err = -ENFILE; - if (!inc_mount_count() && current->uid != 0) - goto err; - err = -ENOMEM; root = get_root_inode(sb, d.rootmode); if (root == NULL) @@ -467,7 +473,6 @@ static int fuse_fill_super(struct super_ err: spin_lock(&fuse_lock); - mount_count --; fc->sb = NULL; fuse_release_conn(fc); spin_unlock(&fuse_lock); _