fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / fuse / inode.c
index 9fe1642..12450d2 100644 (file)
@@ -1,6 +1,6 @@
 /*
   FUSE: Filesystem in Userspace
 /*
   FUSE: Filesystem in Userspace
-  Copyright (C) 2001-2005  Miklos Szeredi <miklos@szeredi.hu>
+  Copyright (C) 2001-2006  Miklos Szeredi <miklos@szeredi.hu>
 
   This program can be distributed under the terms of the GNU GPL.
   See the file COPYING.
 
   This program can be distributed under the terms of the GNU GPL.
   See the file COPYING.
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/file.h>
-#include <linux/mount.h>
 #include <linux/seq_file.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/parser.h>
 #include <linux/statfs.h>
 #include <linux/seq_file.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/parser.h>
 #include <linux/statfs.h>
+#include <linux/random.h>
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Filesystem in Userspace");
 MODULE_LICENSE("GPL");
 
 
 MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
 MODULE_DESCRIPTION("Filesystem in Userspace");
 MODULE_LICENSE("GPL");
 
-spinlock_t fuse_lock;
-static kmem_cache_t *fuse_inode_cachep;
-static struct subsystem connections_subsys;
-
-struct fuse_conn_attr {
-       struct attribute attr;
-       ssize_t (*show)(struct fuse_conn *, char *);
-       ssize_t (*store)(struct fuse_conn *, const char *, size_t);
-};
+static struct kmem_cache *fuse_inode_cachep;
+struct list_head fuse_conn_list;
+DEFINE_MUTEX(fuse_mutex);
 
 #define FUSE_SUPER_MAGIC 0x65735546
 
 
 #define FUSE_SUPER_MAGIC 0x65735546
 
@@ -45,6 +39,7 @@ struct fuse_mount_data {
        unsigned group_id_present : 1;
        unsigned flags;
        unsigned max_read;
        unsigned group_id_present : 1;
        unsigned flags;
        unsigned max_read;
+       unsigned blksize;
 };
 
 static struct inode *fuse_alloc_inode(struct super_block *sb)
 };
 
 static struct inode *fuse_alloc_inode(struct super_block *sb)
@@ -52,12 +47,12 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
        struct inode *inode;
        struct fuse_inode *fi;
 
        struct inode *inode;
        struct fuse_inode *fi;
 
-       inode = kmem_cache_alloc(fuse_inode_cachep, SLAB_KERNEL);
+       inode = kmem_cache_alloc(fuse_inode_cachep, GFP_KERNEL);
        if (!inode)
                return NULL;
 
        fi = get_fuse_inode(inode);
        if (!inode)
                return NULL;
 
        fi = get_fuse_inode(inode);
-       fi->i_time = jiffies - 1;
+       fi->i_time = 0;
        fi->nodeid = 0;
        fi->nlookup = 0;
        fi->forget_req = fuse_request_alloc();
        fi->nodeid = 0;
        fi->nlookup = 0;
        fi->forget_req = fuse_request_alloc();
@@ -105,8 +100,17 @@ static void fuse_clear_inode(struct inode *inode)
        }
 }
 
        }
 }
 
+static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
+{
+       if (*flags & MS_MANDLOCK)
+               return -EINVAL;
+
+       return 0;
+}
+
 void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
 void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
 {
+       struct fuse_conn *fc = get_fuse_conn(inode);
        if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
                invalidate_inode_pages(inode->i_mapping);
 
        if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
                invalidate_inode_pages(inode->i_mapping);
 
@@ -115,10 +119,9 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
        inode->i_nlink   = attr->nlink;
        inode->i_uid     = attr->uid;
        inode->i_gid     = attr->gid;
        inode->i_nlink   = attr->nlink;
        inode->i_uid     = attr->uid;
        inode->i_gid     = attr->gid;
-       spin_lock(&fuse_lock);
+       spin_lock(&fc->lock);
        i_size_write(inode, attr->size);
        i_size_write(inode, attr->size);
-       spin_unlock(&fuse_lock);
-       inode->i_blksize = PAGE_CACHE_SIZE;
+       spin_unlock(&fc->lock);
        inode->i_blocks  = attr->blocks;
        inode->i_atime.tv_sec   = attr->atime;
        inode->i_atime.tv_nsec  = attr->atimensec;
        inode->i_blocks  = attr->blocks;
        inode->i_atime.tv_sec   = attr->atime;
        inode->i_atime.tv_nsec  = attr->atimensec;
@@ -170,7 +173,6 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
        struct inode *inode;
        struct fuse_inode *fi;
        struct fuse_conn *fc = get_fuse_conn_super(sb);
        struct inode *inode;
        struct fuse_inode *fi;
        struct fuse_conn *fc = get_fuse_conn_super(sb);
-       int retried = 0;
 
  retry:
        inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
 
  retry:
        inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid);
@@ -184,43 +186,56 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
                fuse_init_inode(inode, attr);
                unlock_new_inode(inode);
        } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
                fuse_init_inode(inode, attr);
                unlock_new_inode(inode);
        } else if ((inode->i_mode ^ attr->mode) & S_IFMT) {
-               BUG_ON(retried);
                /* Inode has changed type, any I/O on the old should fail */
                make_bad_inode(inode);
                iput(inode);
                /* Inode has changed type, any I/O on the old should fail */
                make_bad_inode(inode);
                iput(inode);
-               retried = 1;
                goto retry;
        }
 
        fi = get_fuse_inode(inode);
                goto retry;
        }
 
        fi = get_fuse_inode(inode);
+       spin_lock(&fc->lock);
        fi->nlookup ++;
        fi->nlookup ++;
+       spin_unlock(&fc->lock);
        fuse_change_attributes(inode, attr);
        return inode;
 }
 
        fuse_change_attributes(inode, attr);
        return inode;
 }
 
-static void fuse_umount_begin(struct super_block *sb)
+static void fuse_umount_begin(struct vfsmount *vfsmnt, int flags)
+{
+       if (flags & MNT_FORCE)
+               fuse_abort_conn(get_fuse_conn_super(vfsmnt->mnt_sb));
+}
+
+static void fuse_send_destroy(struct fuse_conn *fc)
 {
 {
-       fuse_abort_conn(get_fuse_conn_super(sb));
+       struct fuse_req *req = fc->destroy_req;
+       if (req && fc->conn_init) {
+               fc->destroy_req = NULL;
+               req->in.h.opcode = FUSE_DESTROY;
+               req->force = 1;
+               request_send(fc, req);
+               fuse_put_request(fc, req);
+       }
 }
 
 static void fuse_put_super(struct super_block *sb)
 {
        struct fuse_conn *fc = get_fuse_conn_super(sb);
 
 }
 
 static void fuse_put_super(struct super_block *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);
-       fc->mounted = 0;
+       fuse_send_destroy(fc);
+       spin_lock(&fc->lock);
        fc->connected = 0;
        fc->connected = 0;
-       spin_unlock(&fuse_lock);
-       up_write(&fc->sbput_sem);
+       fc->blocked = 0;
+       spin_unlock(&fc->lock);
        /* Flush all readers on this fs */
        /* Flush all readers on this fs */
+       kill_fasync(&fc->fasync, SIGIO, POLL_IN);
        wake_up_all(&fc->waitq);
        wake_up_all(&fc->waitq);
-       kobject_del(&fc->kobj);
-       kobject_put(&fc->kobj);
+       wake_up_all(&fc->blocked_waitq);
+       mutex_lock(&fuse_mutex);
+       list_del(&fc->entry);
+       fuse_ctl_remove_conn(fc);
+       mutex_unlock(&fuse_mutex);
+       fuse_conn_put(fc);
 }
 
 static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
 }
 
 static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr)
@@ -237,20 +252,22 @@ static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr
        /* fsid is left zero */
 }
 
        /* fsid is left zero */
 }
 
-static int fuse_statfs(struct super_block *sb, struct kstatfs *buf)
+static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 {
+       struct super_block *sb = dentry->d_sb;
        struct fuse_conn *fc = get_fuse_conn_super(sb);
        struct fuse_req *req;
        struct fuse_statfs_out outarg;
        int err;
 
        struct fuse_conn *fc = get_fuse_conn_super(sb);
        struct fuse_req *req;
        struct fuse_statfs_out outarg;
        int err;
 
-        req = fuse_get_request(fc);
-       if (!req)
-               return -EINTR;
+       req = fuse_get_req(fc);
+       if (IS_ERR(req))
+               return PTR_ERR(req);
 
        memset(&outarg, 0, sizeof(outarg));
        req->in.numargs = 0;
        req->in.h.opcode = FUSE_STATFS;
 
        memset(&outarg, 0, sizeof(outarg));
        req->in.numargs = 0;
        req->in.h.opcode = FUSE_STATFS;
+       req->in.h.nodeid = get_node_id(dentry->d_inode);
        req->out.numargs = 1;
        req->out.args[0].size =
                fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg);
        req->out.numargs = 1;
        req->out.args[0].size =
                fc->minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(outarg);
@@ -271,6 +288,7 @@ enum {
        OPT_DEFAULT_PERMISSIONS,
        OPT_ALLOW_OTHER,
        OPT_MAX_READ,
        OPT_DEFAULT_PERMISSIONS,
        OPT_ALLOW_OTHER,
        OPT_MAX_READ,
+       OPT_BLKSIZE,
        OPT_ERR
 };
 
        OPT_ERR
 };
 
@@ -282,14 +300,16 @@ static match_table_t tokens = {
        {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
        {OPT_ALLOW_OTHER,               "allow_other"},
        {OPT_MAX_READ,                  "max_read=%u"},
        {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
        {OPT_ALLOW_OTHER,               "allow_other"},
        {OPT_MAX_READ,                  "max_read=%u"},
+       {OPT_BLKSIZE,                   "blksize=%u"},
        {OPT_ERR,                       NULL}
 };
 
        {OPT_ERR,                       NULL}
 };
 
-static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
+static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)
 {
        char *p;
        memset(d, 0, sizeof(struct fuse_mount_data));
        d->max_read = ~0;
 {
        char *p;
        memset(d, 0, sizeof(struct fuse_mount_data));
        d->max_read = ~0;
+       d->blksize = 512;
 
        while ((p = strsep(&opt, ",")) != NULL) {
                int token;
 
        while ((p = strsep(&opt, ",")) != NULL) {
                int token;
@@ -342,6 +362,12 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d)
                        d->max_read = value;
                        break;
 
                        d->max_read = value;
                        break;
 
+               case OPT_BLKSIZE:
+                       if (!is_bdev || match_int(&args[0], &value))
+                               return 0;
+                       d->blksize = value;
+                       break;
+
                default:
                        return 0;
                }
                default:
                        return 0;
                }
@@ -369,81 +395,45 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
        return 0;
 }
 
        return 0;
 }
 
-static void fuse_conn_release(struct kobject *kobj)
-{
-       struct fuse_conn *fc = get_fuse_conn_kobj(kobj);
-
-       while (!list_empty(&fc->unused_list)) {
-               struct fuse_req *req;
-               req = list_entry(fc->unused_list.next, struct fuse_req, list);
-               list_del(&req->list);
-               fuse_request_free(req);
-       }
-       kfree(fc);
-}
-
 static struct fuse_conn *new_conn(void)
 {
        struct fuse_conn *fc;
 
        fc = kzalloc(sizeof(*fc), GFP_KERNEL);
        if (fc) {
 static struct fuse_conn *new_conn(void)
 {
        struct fuse_conn *fc;
 
        fc = kzalloc(sizeof(*fc), GFP_KERNEL);
        if (fc) {
-               int i;
+               spin_lock_init(&fc->lock);
+               mutex_init(&fc->inst_mutex);
+               atomic_set(&fc->count, 1);
                init_waitqueue_head(&fc->waitq);
                init_waitqueue_head(&fc->waitq);
+               init_waitqueue_head(&fc->blocked_waitq);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
                INIT_LIST_HEAD(&fc->io);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
                INIT_LIST_HEAD(&fc->io);
-               INIT_LIST_HEAD(&fc->unused_list);
-               INIT_LIST_HEAD(&fc->background);
-               sema_init(&fc->outstanding_sem, 1); /* One for INIT */
-               init_rwsem(&fc->sbput_sem);
-               kobj_set_kset_s(fc, connections_subsys);
-               kobject_init(&fc->kobj);
+               INIT_LIST_HEAD(&fc->interrupts);
                atomic_set(&fc->num_waiting, 0);
                atomic_set(&fc->num_waiting, 0);
-               for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
-                       struct fuse_req *req = fuse_request_alloc();
-                       if (!req) {
-                               kobject_put(&fc->kobj);
-                               return NULL;
-                       }
-                       list_add(&req->list, &fc->unused_list);
-               }
                fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
                fc->bdi.unplug_io_fn = default_unplug_io_fn;
                fc->reqctr = 0;
                fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
                fc->bdi.unplug_io_fn = default_unplug_io_fn;
                fc->reqctr = 0;
+               fc->blocked = 1;
+               get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
        }
        return fc;
 }
 
        }
        return fc;
 }
 
-static struct fuse_conn *get_conn(struct file *file, struct super_block *sb)
+void fuse_conn_put(struct fuse_conn *fc)
 {
 {
-       struct fuse_conn *fc;
-       int err;
-
-       err = -EINVAL;
-       if (file->f_op != &fuse_dev_operations)
-               goto out_err;
-
-       err = -ENOMEM;
-       fc = new_conn();
-       if (!fc)
-               goto out_err;
-
-       spin_lock(&fuse_lock);
-       err = -EINVAL;
-       if (file->private_data)
-               goto out_unlock;
+       if (atomic_dec_and_test(&fc->count)) {
+               if (fc->destroy_req)
+                       fuse_request_free(fc->destroy_req);
+               mutex_destroy(&fc->inst_mutex);
+               kfree(fc);
+       }
+}
 
 
-       kobject_get(&fc->kobj);
-       file->private_data = fc;
-       spin_unlock(&fuse_lock);
+struct fuse_conn *fuse_conn_get(struct fuse_conn *fc)
+{
+       atomic_inc(&fc->count);
        return fc;
        return fc;
-
- out_unlock:
-       spin_unlock(&fuse_lock);
-       kobject_put(&fc->kobj);
- out_err:
-       return ERR_PTR(err);
 }
 
 static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
 }
 
 static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
@@ -461,6 +451,7 @@ static struct super_operations fuse_super_operations = {
        .destroy_inode  = fuse_destroy_inode,
        .read_inode     = fuse_read_inode,
        .clear_inode    = fuse_clear_inode,
        .destroy_inode  = fuse_destroy_inode,
        .read_inode     = fuse_read_inode,
        .clear_inode    = fuse_clear_inode,
+       .remount_fs     = fuse_remount_fs,
        .put_super      = fuse_put_super,
        .umount_begin   = fuse_umount_begin,
        .statfs         = fuse_statfs,
        .put_super      = fuse_put_super,
        .umount_begin   = fuse_umount_begin,
        .statfs         = fuse_statfs,
@@ -469,7 +460,6 @@ static struct super_operations fuse_super_operations = {
 
 static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 {
 
 static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
 {
-       int i;
        struct fuse_init_out *arg = &req->misc.init_out;
 
        if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
        struct fuse_init_out *arg = &req->misc.init_out;
 
        if (req->out.h.error || arg->major != FUSE_KERNEL_VERSION)
@@ -481,35 +471,31 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
                        ra_pages = arg->max_readahead / PAGE_CACHE_SIZE;
                        if (arg->flags & FUSE_ASYNC_READ)
                                fc->async_read = 1;
                        ra_pages = arg->max_readahead / PAGE_CACHE_SIZE;
                        if (arg->flags & FUSE_ASYNC_READ)
                                fc->async_read = 1;
-               } else
+                       if (!(arg->flags & FUSE_POSIX_LOCKS))
+                               fc->no_lock = 1;
+               } else {
                        ra_pages = fc->max_read / PAGE_CACHE_SIZE;
                        ra_pages = fc->max_read / PAGE_CACHE_SIZE;
+                       fc->no_lock = 1;
+               }
 
                fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages);
                fc->minor = arg->minor;
                fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
 
                fc->bdi.ra_pages = min(fc->bdi.ra_pages, ra_pages);
                fc->minor = arg->minor;
                fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
+               fc->conn_init = 1;
        }
        }
-
-       /* After INIT reply is received other requests can go
-          out.  So do (FUSE_MAX_OUTSTANDING - 1) number of
-          up()s on outstanding_sem.  The last up() is done in
-          fuse_putback_request() */
-       for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
-               up(&fc->outstanding_sem);
-
        fuse_put_request(fc, req);
        fuse_put_request(fc, req);
+       fc->blocked = 0;
+       wake_up_all(&fc->blocked_waitq);
 }
 
 }
 
-static void fuse_send_init(struct fuse_conn *fc)
+static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
 {
 {
-       /* This is called from fuse_read_super() so there's guaranteed
-          to be exactly one request available */
-       struct fuse_req *req = fuse_get_request(fc);
        struct fuse_init_in *arg = &req->misc.init_in;
 
        arg->major = FUSE_KERNEL_VERSION;
        arg->minor = FUSE_KERNEL_MINOR_VERSION;
        arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
        struct fuse_init_in *arg = &req->misc.init_in;
 
        arg->major = FUSE_KERNEL_VERSION;
        arg->minor = FUSE_KERNEL_MINOR_VERSION;
        arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
-       arg->flags |= FUSE_ASYNC_READ;
+       arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS;
        req->in.h.opcode = FUSE_INIT;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(*arg);
        req->in.h.opcode = FUSE_INIT;
        req->in.numargs = 1;
        req->in.args[0].size = sizeof(*arg);
@@ -525,14 +511,10 @@ static void fuse_send_init(struct fuse_conn *fc)
        request_send_background(fc, req);
 }
 
        request_send_background(fc, req);
 }
 
-static unsigned long long conn_id(void)
+static u64 conn_id(void)
 {
 {
-       static unsigned long long ctr = 1;
-       unsigned long long val;
-       spin_lock(&fuse_lock);
-       val = ctr++;
-       spin_unlock(&fuse_lock);
-       return val;
+       static u64 ctr = 1;
+       return ctr++;
 }
 
 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)
@@ -542,13 +524,25 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        struct fuse_mount_data d;
        struct file *file;
        struct dentry *root_dentry;
        struct fuse_mount_data d;
        struct file *file;
        struct dentry *root_dentry;
+       struct fuse_req *init_req;
        int err;
        int err;
+       int is_bdev = sb->s_bdev != NULL;
+
+       if (sb->s_flags & MS_MANDLOCK)
+               return -EINVAL;
 
 
-       if (!parse_fuse_opt((char *) data, &d))
+       if (!parse_fuse_opt((char *) data, &d, is_bdev))
                return -EINVAL;
 
                return -EINVAL;
 
-       sb->s_blocksize = PAGE_CACHE_SIZE;
-       sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+       if (is_bdev) {
+#ifdef CONFIG_BLOCK
+               if (!sb_set_blocksize(sb, d.blksize))
+                       return -EINVAL;
+#endif
+       } else {
+               sb->s_blocksize = PAGE_CACHE_SIZE;
+               sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+       }
        sb->s_magic = FUSE_SUPER_MAGIC;
        sb->s_op = &fuse_super_operations;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
        sb->s_magic = FUSE_SUPER_MAGIC;
        sb->s_op = &fuse_super_operations;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -557,10 +551,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        if (!file)
                return -EINVAL;
 
        if (!file)
                return -EINVAL;
 
-       fc = get_conn(file, sb);
-       fput(file);
-       if (IS_ERR(fc))
-               return PTR_ERR(fc);
+       if (file->f_op != &fuse_dev_operations)
+               return -EINVAL;
+
+       fc = new_conn();
+       if (!fc)
+               return -ENOMEM;
 
        fc->flags = d.flags;
        fc->user_id = d.user_id;
 
        fc->flags = d.flags;
        fc->user_id = d.user_id;
@@ -581,36 +577,58 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
                goto err;
        }
 
                goto err;
        }
 
-       err = kobject_set_name(&fc->kobj, "%llu", conn_id());
-       if (err)
+       init_req = fuse_request_alloc();
+       if (!init_req)
                goto err_put_root;
 
                goto err_put_root;
 
-       err = kobject_add(&fc->kobj);
+       if (is_bdev) {
+               fc->destroy_req = fuse_request_alloc();
+               if (!fc->destroy_req)
+                       goto err_put_root;
+       }
+
+       mutex_lock(&fuse_mutex);
+       err = -EINVAL;
+       if (file->private_data)
+               goto err_unlock;
+
+       fc->id = conn_id();
+       err = fuse_ctl_add_conn(fc);
        if (err)
        if (err)
-               goto err_put_root;
+               goto err_unlock;
 
 
+       list_add_tail(&fc->entry, &fuse_conn_list);
        sb->s_root = root_dentry;
        sb->s_root = root_dentry;
-       spin_lock(&fuse_lock);
-       fc->mounted = 1;
        fc->connected = 1;
        fc->connected = 1;
-       spin_unlock(&fuse_lock);
+       file->private_data = fuse_conn_get(fc);
+       mutex_unlock(&fuse_mutex);
+       /*
+        * atomic_dec_and_test() in fput() provides the necessary
+        * memory barrier for file->private_data to be visible on all
+        * CPUs after this
+        */
+       fput(file);
 
 
-       fuse_send_init(fc);
+       fuse_send_init(fc, init_req);
 
        return 0;
 
 
        return 0;
 
+ err_unlock:
+       mutex_unlock(&fuse_mutex);
+       fuse_request_free(init_req);
  err_put_root:
        dput(root_dentry);
  err:
  err_put_root:
        dput(root_dentry);
  err:
-       kobject_put(&fc->kobj);
+       fput(file);
+       fuse_conn_put(fc);
        return err;
 }
 
        return err;
 }
 
-static struct super_block *fuse_get_sb(struct file_system_type *fs_type,
-                                      int flags, const char *dev_name,
-                                      void *raw_data)
+static int fuse_get_sb(struct file_system_type *fs_type,
+                      int flags, const char *dev_name,
+                      void *raw_data, struct vfsmount *mnt)
 {
 {
-       return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super);
+       return get_sb_nodev(fs_type, flags, raw_data, fuse_fill_super, mnt);
 }
 
 static struct file_system_type fuse_fs_type = {
 }
 
 static struct file_system_type fuse_fs_type = {
@@ -620,70 +638,47 @@ static struct file_system_type fuse_fs_type = {
        .kill_sb        = kill_anon_super,
 };
 
        .kill_sb        = kill_anon_super,
 };
 
-static ssize_t fuse_conn_waiting_show(struct fuse_conn *fc, char *page)
+#ifdef CONFIG_BLOCK
+static int fuse_get_sb_blk(struct file_system_type *fs_type,
+                          int flags, const char *dev_name,
+                          void *raw_data, struct vfsmount *mnt)
 {
 {
-       return sprintf(page, "%i\n", atomic_read(&fc->num_waiting));
+       return get_sb_bdev(fs_type, flags, dev_name, raw_data, fuse_fill_super,
+                          mnt);
 }
 
 }
 
-static ssize_t fuse_conn_abort_store(struct fuse_conn *fc, const char *page,
-                                    size_t count)
-{
-       fuse_abort_conn(fc);
-       return count;
-}
-
-static struct fuse_conn_attr fuse_conn_waiting =
-       __ATTR(waiting, 0400, fuse_conn_waiting_show, NULL);
-static struct fuse_conn_attr fuse_conn_abort =
-       __ATTR(abort, 0600, NULL, fuse_conn_abort_store);
-
-static struct attribute *fuse_conn_attrs[] = {
-       &fuse_conn_waiting.attr,
-       &fuse_conn_abort.attr,
-       NULL,
+static struct file_system_type fuseblk_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "fuseblk",
+       .get_sb         = fuse_get_sb_blk,
+       .kill_sb        = kill_block_super,
+       .fs_flags       = FS_REQUIRES_DEV,
 };
 
 };
 
-static ssize_t fuse_conn_attr_show(struct kobject *kobj,
-                                  struct attribute *attr,
-                                  char *page)
+static inline int register_fuseblk(void)
 {
 {
-       struct fuse_conn_attr *fca =
-               container_of(attr, struct fuse_conn_attr, attr);
-
-       if (fca->show)
-               return fca->show(get_fuse_conn_kobj(kobj), page);
-       else
-               return -EACCES;
+       return register_filesystem(&fuseblk_fs_type);
 }
 
 }
 
-static ssize_t fuse_conn_attr_store(struct kobject *kobj,
-                                   struct attribute *attr,
-                                   const char *page, size_t count)
+static inline void unregister_fuseblk(void)
 {
 {
-       struct fuse_conn_attr *fca =
-               container_of(attr, struct fuse_conn_attr, attr);
-
-       if (fca->store)
-               return fca->store(get_fuse_conn_kobj(kobj), page, count);
-       else
-               return -EACCES;
+       unregister_filesystem(&fuseblk_fs_type);
+}
+#else
+static inline int register_fuseblk(void)
+{
+       return 0;
 }
 
 }
 
-static struct sysfs_ops fuse_conn_sysfs_ops = {
-       .show   = &fuse_conn_attr_show,
-       .store  = &fuse_conn_attr_store,
-};
-
-static struct kobj_type ktype_fuse_conn = {
-       .release        = fuse_conn_release,
-       .sysfs_ops      = &fuse_conn_sysfs_ops,
-       .default_attrs  = fuse_conn_attrs,
-};
+static inline void unregister_fuseblk(void)
+{
+}
+#endif
 
 static decl_subsys(fuse, NULL, NULL);
 
 static decl_subsys(fuse, NULL, NULL);
-static decl_subsys(connections, &ktype_fuse_conn, NULL);
+static decl_subsys(connections, NULL, NULL);
 
 
-static void fuse_inode_init_once(void *foo, kmem_cache_t *cachep,
+static void fuse_inode_init_once(void *foo, struct kmem_cache *cachep,
                                 unsigned long flags)
 {
        struct inode * inode = foo;
                                 unsigned long flags)
 {
        struct inode * inode = foo;
@@ -699,24 +694,34 @@ static int __init fuse_fs_init(void)
 
        err = register_filesystem(&fuse_fs_type);
        if (err)
 
        err = register_filesystem(&fuse_fs_type);
        if (err)
-               printk("fuse: failed to register filesystem\n");
-       else {
-               fuse_inode_cachep = kmem_cache_create("fuse_inode",
-                                                     sizeof(struct fuse_inode),
-                                                     0, SLAB_HWCACHE_ALIGN,
-                                                     fuse_inode_init_once, NULL);
-               if (!fuse_inode_cachep) {
-                       unregister_filesystem(&fuse_fs_type);
-                       err = -ENOMEM;
-               }
-       }
+               goto out;
+
+       err = register_fuseblk();
+       if (err)
+               goto out_unreg;
 
 
+       fuse_inode_cachep = kmem_cache_create("fuse_inode",
+                                             sizeof(struct fuse_inode),
+                                             0, SLAB_HWCACHE_ALIGN,
+                                             fuse_inode_init_once, NULL);
+       err = -ENOMEM;
+       if (!fuse_inode_cachep)
+               goto out_unreg2;
+
+       return 0;
+
+ out_unreg2:
+       unregister_fuseblk();
+ out_unreg:
+       unregister_filesystem(&fuse_fs_type);
+ out:
        return err;
 }
 
 static void fuse_fs_cleanup(void)
 {
        unregister_filesystem(&fuse_fs_type);
        return err;
 }
 
 static void fuse_fs_cleanup(void)
 {
        unregister_filesystem(&fuse_fs_type);
+       unregister_fuseblk();
        kmem_cache_destroy(fuse_inode_cachep);
 }
 
        kmem_cache_destroy(fuse_inode_cachep);
 }
 
@@ -755,7 +760,7 @@ static int __init fuse_init(void)
        printk("fuse init (API version %i.%i)\n",
               FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
 
        printk("fuse init (API version %i.%i)\n",
               FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION);
 
-       spin_lock_init(&fuse_lock);
+       INIT_LIST_HEAD(&fuse_conn_list);
        res = fuse_fs_init();
        if (res)
                goto err;
        res = fuse_fs_init();
        if (res)
                goto err;
@@ -768,8 +773,14 @@ static int __init fuse_init(void)
        if (res)
                goto err_dev_cleanup;
 
        if (res)
                goto err_dev_cleanup;
 
+       res = fuse_ctl_init();
+       if (res)
+               goto err_sysfs_cleanup;
+
        return 0;
 
        return 0;
 
+ err_sysfs_cleanup:
+       fuse_sysfs_cleanup();
  err_dev_cleanup:
        fuse_dev_cleanup();
  err_fs_cleanup:
  err_dev_cleanup:
        fuse_dev_cleanup();
  err_fs_cleanup:
@@ -782,6 +793,7 @@ static void __exit fuse_exit(void)
 {
        printk(KERN_DEBUG "fuse exit\n");
 
 {
        printk(KERN_DEBUG "fuse exit\n");
 
+       fuse_ctl_cleanup();
        fuse_sysfs_cleanup();
        fuse_fs_cleanup();
        fuse_dev_cleanup();
        fuse_sysfs_cleanup();
        fuse_fs_cleanup();
        fuse_dev_cleanup();