]> err.no Git - linux-2.6/blobdiff - fs/fuse/inode.c
[PATCH] fuse: fix bitfield race
[linux-2.6] / fs / fuse / inode.c
index 182235923cdda95d8ff9526bd3ebefbd731266a9..c755a0440a6640848fff524c522e940da50b294a 100644 (file)
@@ -196,6 +196,11 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid,
        return inode;
 }
 
+static void fuse_umount_begin(struct super_block *sb)
+{
+       fuse_abort_conn(get_fuse_conn_super(sb));
+}
+
 static void fuse_put_super(struct super_block *sb)
 {
        struct fuse_conn *fc = get_fuse_conn_super(sb);
@@ -392,6 +397,7 @@ static struct fuse_conn *new_conn(void)
                init_rwsem(&fc->sbput_sem);
                kobj_set_kset_s(fc, connections_subsys);
                kobject_init(&fc->kobj);
+               atomic_set(&fc->num_waiting, 0);
                for (i = 0; i < FUSE_MAX_OUTSTANDING; i++) {
                        struct fuse_req *req = fuse_request_alloc();
                        if (!req) {
@@ -454,10 +460,57 @@ static struct super_operations fuse_super_operations = {
        .read_inode     = fuse_read_inode,
        .clear_inode    = fuse_clear_inode,
        .put_super      = fuse_put_super,
+       .umount_begin   = fuse_umount_begin,
        .statfs         = fuse_statfs,
        .show_options   = fuse_show_options,
 };
 
+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)
+               fc->conn_error = 1;
+       else {
+               fc->minor = arg->minor;
+               fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
+       }
+
+       /* 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);
+}
+
+static void fuse_send_init(struct fuse_conn *fc)
+{
+       /* 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;
+       req->in.h.opcode = FUSE_INIT;
+       req->in.numargs = 1;
+       req->in.args[0].size = sizeof(*arg);
+       req->in.args[0].value = arg;
+       req->out.numargs = 1;
+       /* Variable length arguement used for backward compatibility
+          with interface version < 7.5.  Rest of init_out is zeroed
+          by do_get_request(), so a short reply is not a problem */
+       req->out.argvar = 1;
+       req->out.args[0].size = sizeof(struct fuse_init_out);
+       req->out.args[0].value = &req->misc.init_out;
+       req->end = process_init_reply;
+       request_send_background(fc, req);
+}
+
 static unsigned long long conn_id(void)
 {
        static unsigned long long ctr = 1;
@@ -560,11 +613,21 @@ static ssize_t fuse_conn_waiting_show(struct fuse_conn *fc, char *page)
        return sprintf(page, "%i\n", atomic_read(&fc->num_waiting));
 }
 
+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,
 };