From: Miklos Szeredi This patch fixes an Oops which was caused by premature release of the fuse_conn object. It does this by introducing reference counting for this object, instead of using the ->file and ->sb members. Since ->file and ->sb are never actually dereferenced, these are replaced by ->connected and ->mounted bool members. Signed-off-by: Miklos Szeredi Signed-off-by: Andrew Morton --- fs/fuse/dev.c | 14 +++++++------- fs/fuse/fuse_i.h | 13 ++++++++----- fs/fuse/inode.c | 24 +++++++++--------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff -puN fs/fuse/dev.c~fuse-mount-options-reference-counting-fix fs/fuse/dev.c --- 25/fs/fuse/dev.c~fuse-mount-options-reference-counting-fix 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/dev.c 2005-05-10 02:22:00.000000000 -0700 @@ -24,7 +24,7 @@ static inline struct fuse_conn *fuse_get struct fuse_conn *fc; spin_lock(&fuse_lock); fc = file->private_data; - if (fc && !fc->sb) + if (fc && !fc->mounted) fc = NULL; spin_unlock(&fuse_lock); return fc; @@ -176,7 +176,7 @@ static void request_end(struct fuse_conn spin_unlock(&fuse_lock); if (req->background) { down_read(&fc->sbput_sem); - if (fc->sb) + if (fc->mounted) fuse_release_background(req); up_read(&fc->sbput_sem); } @@ -306,7 +306,7 @@ static void request_send_wait(struct fus { req->isreply = 1; spin_lock(&fuse_lock); - if (!fc->file) + if (!fc->connected) req->out.h.error = -ENOTCONN; else if (fc->conn_error) req->out.h.error = -ECONNREFUSED; @@ -339,7 +339,7 @@ void request_send_nonint(struct fuse_con static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) { spin_lock(&fuse_lock); - if (fc->file) { + if (fc->connected) { queue_request(fc, req); spin_unlock(&fuse_lock); } else { @@ -592,7 +592,7 @@ static void request_wait(struct fuse_con DECLARE_WAITQUEUE(wait, current); 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); if (signal_pending(current)) break; @@ -631,7 +631,7 @@ static ssize_t fuse_dev_readv(struct fil goto err_unlock; request_wait(fc); err = -ENODEV; - if (!fc->sb) + if (!fc->mounted) goto err_unlock; err = -ERESTARTSYS; if (list_empty(&fc->pending)) @@ -848,7 +848,7 @@ static int fuse_dev_release(struct inode spin_lock(&fuse_lock); fc = file->private_data; if (fc) { - fc->file = NULL; + fc->connected = 0; end_requests(fc, &fc->pending); end_requests(fc, &fc->processing); fuse_release_conn(fc); diff -puN fs/fuse/fuse_i.h~fuse-mount-options-reference-counting-fix fs/fuse/fuse_i.h --- 25/fs/fuse/fuse_i.h~fuse-mount-options-reference-counting-fix 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/fuse_i.h 2005-05-10 02:22:00.000000000 -0700 @@ -196,11 +196,8 @@ struct fuse_req { * unmounted. */ struct fuse_conn { - /** The superblock of the mounted filesystem */ - struct super_block *sb; - - /** The opened client device */ - struct file *file; + /** Reference count */ + int count; /** The user id for this mount */ uid_t user_id; @@ -237,6 +234,12 @@ struct fuse_conn { /** The next unique request id */ int reqctr; + /** Mount is active */ + unsigned mounted : 1; + + /** Connection established */ + unsigned connected : 1; + /** Connection failed (version mismatch) */ unsigned conn_error : 1; diff -puN fs/fuse/inode.c~fuse-mount-options-reference-counting-fix fs/fuse/inode.c --- 25/fs/fuse/inode.c~fuse-mount-options-reference-counting-fix 2005-05-10 02:22:00.000000000 -0700 +++ 25-akpm/fs/fuse/inode.c 2005-05-10 02:22:00.000000000 -0700 @@ -84,8 +84,8 @@ void fuse_send_forget(struct fuse_conn * static void fuse_clear_inode(struct inode *inode) { - struct fuse_conn *fc = get_fuse_conn(inode); - if (fc && (inode->i_sb->s_flags & MS_ACTIVE)) { + if (inode->i_sb->s_flags & MS_ACTIVE) { + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); fuse_send_forget(fc, fi->forget_req, fi->nodeid, fi->nlookup); fi->forget_req = NULL; @@ -196,14 +196,13 @@ static void fuse_put_super(struct super_ struct fuse_req, bg_entry)); spin_lock(&fuse_lock); - fc->sb = NULL; + fc->mounted = 0; fc->user_id = 0; fc->flags = 0; /* Flush all readers on this fs */ wake_up_all(&fc->waitq); up_write(&fc->sbput_sem); fuse_release_conn(fc); - *get_fuse_conn_super_p(sb) = NULL; spin_unlock(&fuse_lock); } @@ -355,7 +354,8 @@ static void free_conn(struct fuse_conn * /* Must be called with the fuse lock held */ void fuse_release_conn(struct fuse_conn *fc) { - if (!fc->sb && !fc->file) + fc->count--; + if (!fc->count) free_conn(fc); } @@ -367,10 +367,6 @@ static struct fuse_conn *new_conn(void) if (fc != NULL) { int i; 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); INIT_LIST_HEAD(&fc->processing); @@ -408,8 +404,10 @@ static struct fuse_conn *get_conn(struct fc = ERR_PTR(-EINVAL); } else { file->private_data = fc; - fc->sb = sb; - fc->file = file; + *get_fuse_conn_super_p(sb) = fc; + fc->mounted = 1; + fc->connected = 1; + fc->count = 2; } spin_unlock(&fuse_lock); return fc; @@ -464,8 +462,6 @@ static int fuse_fill_super(struct super_ fc->flags = d.flags; fc->user_id = d.user_id; - *get_fuse_conn_super_p(sb) = fc; - err = -ENOMEM; root = get_root_inode(sb, d.rootmode); if (root == NULL) @@ -481,10 +477,8 @@ static int fuse_fill_super(struct super_ err: spin_lock(&fuse_lock); - fc->sb = NULL; fuse_release_conn(fc); spin_unlock(&fuse_lock); - *get_fuse_conn_super_p(sb) = NULL; return err; } _