Commit 1e9a4ed9 authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Linus Torvalds

[PATCH] FUSE - mount options

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: default avatarMiklos Szeredi <miklos@szeredi.hu>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent b6aeaded
...@@ -26,7 +26,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file) ...@@ -26,7 +26,7 @@ static inline struct fuse_conn *fuse_get_conn(struct file *file)
struct fuse_conn *fc; struct fuse_conn *fc;
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
fc = file->private_data; fc = file->private_data;
if (fc && !fc->sb) if (fc && !fc->mounted)
fc = NULL; fc = NULL;
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
return fc; return fc;
...@@ -148,6 +148,17 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req) ...@@ -148,6 +148,17 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
fuse_putback_request(fc, req); fuse_putback_request(fc, req);
} }
void fuse_release_background(struct fuse_req *req)
{
iput(req->inode);
iput(req->inode2);
if (req->file)
fput(req->file);
spin_lock(&fuse_lock);
list_del(&req->bg_entry);
spin_unlock(&fuse_lock);
}
/* /*
* This function is called when a request is finished. Either a reply * This function is called when a request is finished. Either a reply
* has arrived or it was interrupted (and not yet sent) or some error * has arrived or it was interrupted (and not yet sent) or some error
...@@ -166,12 +177,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) ...@@ -166,12 +177,10 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
putback = atomic_dec_and_test(&req->count); putback = atomic_dec_and_test(&req->count);
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
if (req->background) { if (req->background) {
if (req->inode) down_read(&fc->sbput_sem);
iput(req->inode); if (fc->mounted)
if (req->inode2) fuse_release_background(req);
iput(req->inode2); up_read(&fc->sbput_sem);
if (req->file)
fput(req->file);
} }
wake_up(&req->waitq); wake_up(&req->waitq);
if (req->in.h.opcode == FUSE_INIT) { if (req->in.h.opcode == FUSE_INIT) {
...@@ -191,11 +200,39 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req) ...@@ -191,11 +200,39 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
fuse_putback_request(fc, req); fuse_putback_request(fc, req);
} }
static void background_request(struct fuse_req *req) /*
* Unfortunately request interruption not just solves the deadlock
* problem, it causes problems too. These stem from the fact, that an
* interrupted request is continued to be processed in userspace,
* while all the locks and object references (inode and file) held
* during the operation are released.
*
* To release the locks is exactly why there's a need to interrupt the
* request, so there's not a lot that can be done about this, except
* introduce additional locking in userspace.
*
* More important is to keep inode and file references until userspace
* has replied, otherwise FORGET and RELEASE could be sent while the
* inode/file is still used by the filesystem.
*
* For this reason the concept of "background" request is introduced.
* An interrupted request is backgrounded if it has been already sent
* to userspace. Backgrounding involves getting an extra reference to
* inode(s) or file used in the request, and adding the request to
* fc->background list. When a reply is received for a background
* request, the object references are released, and the request is
* removed from the list. If the filesystem is unmounted while there
* are still background requests, the list is walked and references
* are released as if a reply was received.
*
* There's one more use for a background request. The RELEASE message is
* always sent as background, since it doesn't return an error or
* data.
*/
static void background_request(struct fuse_conn *fc, struct fuse_req *req)
{ {
/* Need to get hold of the inode(s) and/or file used in the
request, so FORGET and RELEASE are not sent too early */
req->background = 1; req->background = 1;
list_add(&req->bg_entry, &fc->background);
if (req->inode) if (req->inode)
req->inode = igrab(req->inode); req->inode = igrab(req->inode);
if (req->inode2) if (req->inode2)
...@@ -215,7 +252,8 @@ static int request_wait_answer_nonint(struct fuse_req *req) ...@@ -215,7 +252,8 @@ static int request_wait_answer_nonint(struct fuse_req *req)
} }
/* Called with fuse_lock held. Releases, and then reacquires it. */ /* Called with fuse_lock held. Releases, and then reacquires it. */
static void request_wait_answer(struct fuse_req *req, int interruptible) static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req,
int interruptible)
{ {
int intr; int intr;
...@@ -255,7 +293,7 @@ static void request_wait_answer(struct fuse_req *req, int interruptible) ...@@ -255,7 +293,7 @@ static void request_wait_answer(struct fuse_req *req, int interruptible)
list_del(&req->list); list_del(&req->list);
__fuse_put_request(req); __fuse_put_request(req);
} else if (!req->finished && req->sent) } else if (!req->finished && req->sent)
background_request(req); background_request(fc, req);
} }
static unsigned len_args(unsigned numargs, struct fuse_arg *args) static unsigned len_args(unsigned numargs, struct fuse_arg *args)
...@@ -297,7 +335,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, ...@@ -297,7 +335,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
{ {
req->isreply = 1; req->isreply = 1;
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
if (!fc->file) if (!fc->connected)
req->out.h.error = -ENOTCONN; req->out.h.error = -ENOTCONN;
else if (fc->conn_error) else if (fc->conn_error)
req->out.h.error = -ECONNREFUSED; req->out.h.error = -ECONNREFUSED;
...@@ -307,7 +345,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req, ...@@ -307,7 +345,7 @@ static void request_send_wait(struct fuse_conn *fc, struct fuse_req *req,
after request_end() */ after request_end() */
__fuse_get_request(req); __fuse_get_request(req);
request_wait_answer(req, interruptible); request_wait_answer(fc, req, interruptible);
} }
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
} }
...@@ -330,7 +368,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req) ...@@ -330,7 +368,7 @@ void request_send_nonint(struct fuse_conn *fc, struct fuse_req *req)
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{ {
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
if (fc->file) { if (fc->connected) {
queue_request(fc, req); queue_request(fc, req);
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
} else { } else {
...@@ -348,7 +386,9 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) ...@@ -348,7 +386,9 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
void request_send_background(struct fuse_conn *fc, struct fuse_req *req) void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
{ {
req->isreply = 1; req->isreply = 1;
background_request(req); spin_lock(&fuse_lock);
background_request(fc, req);
spin_unlock(&fuse_lock);
request_send_nowait(fc, req); request_send_nowait(fc, req);
} }
...@@ -583,7 +623,7 @@ static void request_wait(struct fuse_conn *fc) ...@@ -583,7 +623,7 @@ static void request_wait(struct fuse_conn *fc)
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
add_wait_queue_exclusive(&fc->waitq, &wait); add_wait_queue_exclusive(&fc->waitq, &wait);
while (fc->sb && list_empty(&fc->pending)) { while (fc->mounted && list_empty(&fc->pending)) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) if (signal_pending(current))
break; break;
...@@ -622,7 +662,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov, ...@@ -622,7 +662,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
goto err_unlock; goto err_unlock;
request_wait(fc); request_wait(fc);
err = -ENODEV; err = -ENODEV;
if (!fc->sb) if (!fc->mounted)
goto err_unlock; goto err_unlock;
err = -ERESTARTSYS; err = -ERESTARTSYS;
if (list_empty(&fc->pending)) if (list_empty(&fc->pending))
...@@ -839,7 +879,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file) ...@@ -839,7 +879,7 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
fc = file->private_data; fc = file->private_data;
if (fc) { if (fc) {
fc->file = NULL; fc->connected = 0;
end_requests(fc, &fc->pending); end_requests(fc, &fc->pending);
end_requests(fc, &fc->processing); end_requests(fc, &fc->processing);
fuse_release_conn(fc); fuse_release_conn(fc);
......
...@@ -418,7 +418,8 @@ static int fuse_revalidate(struct dentry *entry) ...@@ -418,7 +418,8 @@ static int fuse_revalidate(struct dentry *entry)
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (get_node_id(inode) == FUSE_ROOT_ID) { 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)
return -EACCES; return -EACCES;
} else if (time_before_eq(jiffies, fi->i_time)) } else if (time_before_eq(jiffies, fi->i_time))
return 0; return 0;
...@@ -430,9 +431,31 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) ...@@ -430,9 +431,31 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
{ {
struct fuse_conn *fc = get_fuse_conn(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)
return -EACCES; 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; int mode = inode->i_mode;
if ((mask & MAY_WRITE) && IS_RDONLY(inode) && if ((mask & MAY_WRITE) && IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
...@@ -636,6 +659,12 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) ...@@ -636,6 +659,12 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
int err; int err;
int is_truncate = 0; 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) { if (attr->ia_valid & ATTR_SIZE) {
unsigned long limit; unsigned long limit;
is_truncate = 1; is_truncate = 1;
......
...@@ -70,7 +70,7 @@ static int fuse_open(struct inode *inode, struct file *file) ...@@ -70,7 +70,7 @@ static int fuse_open(struct inode *inode, struct file *file)
else else
request_send(fc, req); request_send(fc, req);
err = req->out.h.error; err = req->out.h.error;
if (!err) if (!err && !(fc->flags & FUSE_KERNEL_CACHE))
invalidate_inode_pages(inode->i_mapping); invalidate_inode_pages(inode->i_mapping);
if (err) { if (err) {
fuse_request_free(ff->release_req); fuse_request_free(ff->release_req);
......
...@@ -21,6 +21,19 @@ ...@@ -21,6 +21,19 @@
/** If more requests are outstanding, then the operation will block */ /** If more requests are outstanding, then the operation will block */
#define FUSE_MAX_OUTSTANDING 10 #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)
/** FUSE inode */ /** FUSE inode */
struct fuse_inode { struct fuse_inode {
/** Inode data */ /** Inode data */
...@@ -109,6 +122,9 @@ struct fuse_req { ...@@ -109,6 +122,9 @@ struct fuse_req {
lists in fuse_conn */ lists in fuse_conn */
struct list_head list; struct list_head list;
/** Entry on the background list */
struct list_head bg_entry;
/** refcount */ /** refcount */
atomic_t count; atomic_t count;
...@@ -176,15 +192,15 @@ struct fuse_req { ...@@ -176,15 +192,15 @@ struct fuse_req {
* unmounted. * unmounted.
*/ */
struct fuse_conn { struct fuse_conn {
/** The superblock of the mounted filesystem */ /** Reference count */
struct super_block *sb; int count;
/** The opened client device */
struct file *file;
/** The user id for this mount */ /** The user id for this mount */
uid_t user_id; uid_t user_id;
/** The fuse mount flags for this mount */
unsigned flags;
/** Readers of the connection are waiting on this */ /** Readers of the connection are waiting on this */
wait_queue_head_t waitq; wait_queue_head_t waitq;
...@@ -194,6 +210,10 @@ struct fuse_conn { ...@@ -194,6 +210,10 @@ struct fuse_conn {
/** The list of requests being processed */ /** The list of requests being processed */
struct list_head processing; struct list_head processing;
/** Requests put in the background (RELEASE or any other
interrupted request) */
struct list_head background;
/** Controls the maximum number of outstanding requests */ /** Controls the maximum number of outstanding requests */
struct semaphore outstanding_sem; struct semaphore outstanding_sem;
...@@ -201,12 +221,21 @@ struct fuse_conn { ...@@ -201,12 +221,21 @@ struct fuse_conn {
outstanding_sem would go negative */ outstanding_sem would go negative */
unsigned outstanding_debt; unsigned outstanding_debt;
/** RW semaphore for exclusion with fuse_put_super() */
struct rw_semaphore sbput_sem;
/** The list of unused requests */ /** The list of unused requests */
struct list_head unused_list; struct list_head unused_list;
/** The next unique request id */ /** The next unique request id */
u64 reqctr; u64 reqctr;
/** Mount is active */
unsigned mounted : 1;
/** Connection established */
unsigned connected : 1;
/** Connection failed (version mismatch) */ /** Connection failed (version mismatch) */
unsigned conn_error : 1; unsigned conn_error : 1;
...@@ -261,6 +290,7 @@ extern struct file_operations fuse_dev_operations; ...@@ -261,6 +290,7 @@ extern struct file_operations fuse_dev_operations;
* - the private_data field of the device file * - the private_data field of the device file
* - the s_fs_info field of the super block * - the s_fs_info field of the super block
* - unused_list, pending, processing lists in fuse_conn * - unused_list, pending, processing lists in fuse_conn
* - background list in fuse_conn
* - the unique request ID counter reqctr in fuse_conn * - the unique request ID counter reqctr in fuse_conn
* - the sb (super_block) field in fuse_conn * - the sb (super_block) field in fuse_conn
* - the file (device file) field in fuse_conn * - the file (device file) field in fuse_conn
...@@ -371,6 +401,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req); ...@@ -371,6 +401,11 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req);
*/ */
void request_send_background(struct fuse_conn *fc, struct fuse_req *req); void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
/**
* Release inodes and file assiciated with background request
*/
void fuse_release_background(struct fuse_req *req);
/** /**
* Get the attributes of a file * Get the attributes of a file
*/ */
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/parser.h> #include <linux/parser.h>
#include <linux/statfs.h> #include <linux/statfs.h>
...@@ -25,11 +24,6 @@ MODULE_LICENSE("GPL"); ...@@ -25,11 +24,6 @@ MODULE_LICENSE("GPL");
spinlock_t fuse_lock; spinlock_t fuse_lock;
static kmem_cache_t *fuse_inode_cachep; 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 #define FUSE_SUPER_MAGIC 0x65735546
...@@ -37,6 +31,7 @@ struct fuse_mount_data { ...@@ -37,6 +31,7 @@ struct fuse_mount_data {
int fd; int fd;
unsigned rootmode; unsigned rootmode;
unsigned user_id; unsigned user_id;
unsigned flags;
}; };
static struct inode *fuse_alloc_inode(struct super_block *sb) static struct inode *fuse_alloc_inode(struct super_block *sb)
...@@ -89,8 +84,8 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, ...@@ -89,8 +84,8 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
static void fuse_clear_inode(struct inode *inode) static void fuse_clear_inode(struct inode *inode)
{ {
if (inode->i_sb->s_flags & MS_ACTIVE) {
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (fc) {
struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_inode *fi = get_fuse_inode(inode);
fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup);
fi->forget_req = NULL; fi->forget_req = NULL;
...@@ -195,14 +190,19 @@ static void fuse_put_super(struct super_block *sb) ...@@ -195,14 +190,19 @@ static void fuse_put_super(struct super_block *sb)
{ {
struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_conn *fc = get_fuse_conn_super(sb);
down_write(&fc->sbput_sem);
while (!list_empty(&fc->background))
fuse_release_background(list_entry(fc->background.next,
struct fuse_req, bg_entry));
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
mount_count --; fc->mounted = 0;
fc->sb = NULL;
fc->user_id = 0; fc->user_id = 0;
fc->flags = 0;
/* Flush all readers on this fs */ /* Flush all readers on this fs */
wake_up_all(&fc->waitq); wake_up_all(&fc->waitq);
up_write(&fc->sbput_sem);
fuse_release_conn(fc); fuse_release_conn(fc);
*get_fuse_conn_super_p(sb) = NULL;
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
} }
...@@ -249,7 +249,6 @@ enum { ...@@ -249,7 +249,6 @@ enum {
OPT_USER_ID, OPT_USER_ID,
OPT_DEFAULT_PERMISSIONS, OPT_DEFAULT_PERMISSIONS,
OPT_ALLOW_OTHER, OPT_ALLOW_OTHER,
OPT_ALLOW_ROOT,
OPT_KERNEL_CACHE, OPT_KERNEL_CACHE,
OPT_ERR OPT_ERR
}; };
...@@ -260,7 +259,6 @@ static match_table_t tokens = { ...@@ -260,7 +259,6 @@ static match_table_t tokens = {
{OPT_USER_ID, "user_id=%u"}, {OPT_USER_ID, "user_id=%u"},
{OPT_DEFAULT_PERMISSIONS, "default_permissions"}, {OPT_DEFAULT_PERMISSIONS, "default_permissions"},
{OPT_ALLOW_OTHER, "allow_other"}, {OPT_ALLOW_OTHER, "allow_other"},
{OPT_ALLOW_ROOT, "allow_root"},
{OPT_KERNEL_CACHE, "kernel_cache"}, {OPT_KERNEL_CACHE, "kernel_cache"},
{OPT_ERR, NULL} {OPT_ERR, NULL}
}; };
...@@ -298,6 +296,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) ...@@ -298,6 +296,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
d->user_id = value; d->user_id = value;
break; 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;
default: default:
return 0; return 0;
} }
...@@ -313,6 +323,12 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) ...@@ -313,6 +323,12 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb);
seq_printf(m, ",user_id=%u", fc->user_id); 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_KERNEL_CACHE)
seq_puts(m, ",kernel_cache");
return 0; return 0;
} }
...@@ -330,7 +346,8 @@ static void free_conn(struct fuse_conn *fc) ...@@ -330,7 +346,8 @@ static void free_conn(struct fuse_conn *fc)
/* Must be called with the fuse lock held */ /* Must be called with the fuse lock held */
void fuse_release_conn(struct fuse_conn *fc) void fuse_release_conn(struct fuse_conn *fc)
{ {
if (!fc->sb && !fc->file) fc->count--;
if (!fc->count)
free_conn(fc); free_conn(fc);
} }
...@@ -342,14 +359,13 @@ static struct fuse_conn *new_conn(void) ...@@ -342,14 +359,13 @@ static struct fuse_conn *new_conn(void)
if (fc != NULL) { if (fc != NULL) {
int i; int i;
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
fc->sb = NULL;
fc->file = NULL;
fc->user_id = 0;
init_waitqueue_head(&fc->waitq); init_waitqueue_head(&fc->waitq);
INIT_LIST_HEAD(&fc->pending); INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing); INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->unused_list); INIT_LIST_HEAD(&fc->unused_list);
INIT_LIST_HEAD(&fc->background);
sema_init(&fc->outstanding_sem, 0); sema_init(&fc->outstanding_sem, 0);
init_rwsem(&fc->sbput_sem);
for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) { for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
struct fuse_req *req = fuse_request_alloc(); struct fuse_req *req = fuse_request_alloc();
if (!req) { if (!req) {
...@@ -380,8 +396,10 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb) ...@@ -380,8 +396,10 @@ static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
fc = ERR_PTR(-EINVAL); fc = ERR_PTR(-EINVAL);
} else { } else {
file->private_data = fc; file->private_data = fc;
fc->sb = sb; *get_fuse_conn_super_p(sb) = fc;
fc->file = file; fc->mounted = 1;
fc->connected = 1;
fc->count = 2;
} }
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
return fc; return fc;
...@@ -407,17 +425,6 @@ static struct super_operations fuse_super_operations = { ...@@ -407,17 +425,6 @@ static struct super_operations fuse_super_operations = {
.show_options = fuse_show_options, .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) static int fuse_fill_super(struct super_block *sb, void *data, int silent)
{ {
struct fuse_conn *fc; struct fuse_conn *fc;
...@@ -444,14 +451,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -444,14 +451,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
if (IS_ERR(fc)) if (IS_ERR(fc))
return PTR_ERR(fc); return PTR_ERR(fc);
fc->flags = d.flags;
fc->user_id = d.user_id; 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; err = -ENOMEM;
root = get_root_inode(sb, d.rootmode); root = get_root_inode(sb, d.rootmode);
if (root == NULL) if (root == NULL)
...@@ -467,11 +469,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -467,11 +469,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
err: err:
spin_lock(&fuse_lock); spin_lock(&fuse_lock);
mount_count --;
fc->sb = NULL;
fuse_release_conn(fc); fuse_release_conn(fc);
spin_unlock(&fuse_lock); spin_unlock(&fuse_lock);
*get_fuse_conn_super_p(sb) = NULL;
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment