FUSE: reference counting fix
Miklos Szeredi
maintainerPATCH 1/1 FUSE: reference counting fix
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 <[email protected]>
fs/fuse/dev.c +7 -7
@@ -24,7 +24,7 @@ static inline struct fuse_conn *fuse_getstruct 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_connspin_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); }
@@ -333,7 +333,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 ;
@@ -366,7 +366,7 @@ void request_send_nonint(struct fuse_constatic 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 {
@@ -621,7 +621,7 @@ static void request_wait(struct fuse_conDECLARE_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 ;
@@ -660,7 +660,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))
@@ -868,7 +868,7 @@ static int fuse_dev_release(struct inodespin_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);
fs/fuse/fuse_i.h +8 -5
@@ -192,11 +192,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;
@@ -239,6 +236,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;
fs/fuse/inode.c +9 -15
@@ -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, inode->i_version); fi->forget_req =NULL ;
@@ -194,14 +194,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); }
@@ -364,7 +363,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); }
@@ -376,10 +376,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);
@@ -417,8 +413,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;
@@ -539,8 +537,6 @@ static int fuse_fill_super(struct super_ fc->bdi.ra_pages = fc->max_read /PAGE_CACHE_SIZE ; fc->max_write =FUSE_MAX_IN /2 ; - *get_fuse_conn_super_p (sb) = fc; - err = -ENOMEM ; root =get_root_inode (sb, d.rootmode);if (root ==NULL )
@@ -556,10 +552,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; }