performance improvements
authorMiklos Szeredi <miklos@szeredi.hu>
Sun, 18 Nov 2001 19:15:05 +0000 (19:15 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Sun, 18 Nov 2001 19:15:05 +0000 (19:15 +0000)
12 files changed:
BUGS [new file with mode: 0644]
example/.cvsignore
example/Makefile.am
example/null.c [new file with mode: 0644]
include/linux/fuse.h
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/inode.c
lib/fuse.c
lib/fuse_mt.c

diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..df5f428
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,4 @@
+- It is allowed to mount a directory on a non-directory.
+
+- When a non-directory is mounted the root inode is not filled in, only at
+  the first getattr
index bcfd8b2ee9859312d9db24a560600f3f6888860f..94a32e3b5a6ee94c369526379e0d8fde8a51155f 100644 (file)
@@ -2,3 +2,4 @@ Makefile.in
 Makefile
 .deps
 fusexmp
+null
index 71748f5d120530d153e0cb9a57959e395bb42ae5..19a3c329ffaca4e677096ff220f88f29adb03ac4 100644 (file)
@@ -1,7 +1,8 @@
 ## Process this file with automake to produce Makefile.in
 
-noinst_PROGRAMS = fusexmp
+noinst_PROGRAMS = fusexmp null
 
 fusexmp_SOURCES = fusexmp.c
+null_SOURCES = null.c
 
-fusexmp_LDADD = ../lib/libfuse.a -lpthread
+LDADD = ../lib/libfuse.a -lpthread
diff --git a/example/null.c b/example/null.c
new file mode 100644 (file)
index 0000000..379ba7a
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+    FUSE: Filesystem in Userspace
+    Copyright (C) 2001  Miklos Szeredi (mszeredi@inf.bme.hu)
+
+    This program can be distributed under the terms of the GNU GPL.
+    See the file COPYING.
+*/
+
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#define UNUSED __attribute__((unused))
+
+static char *unmount_cmd;
+
+static int null_getattr(const char *path, struct stat *stbuf)
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+    
+    stbuf->st_mode = S_IFREG | 0644;
+    stbuf->st_nlink = 1;
+    stbuf->st_uid = getuid();
+    stbuf->st_gid = getgid();
+    stbuf->st_size = (1 << 30); /* 1G */
+    stbuf->st_blocks = 0;
+    stbuf->st_atime = stbuf->st_mtime = stbuf->st_ctime = time(NULL);
+
+    return 0;
+}
+
+static int null_truncate(const char *path, off_t UNUSED(size))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return 0;
+}
+
+static int null_open(const char *path, int UNUSED(flags))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return 0;
+}
+
+static int null_read(const char *path, char *UNUSED(buf), size_t size,
+                     off_t UNUSED(offset))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return size;
+}
+
+static int null_write(const char *path, const char *UNUSED(buf), size_t size,
+                     off_t UNUSED(offset))
+{
+    if(strcmp(path, "/") != 0)
+        return -ENOENT;
+
+    return size;
+}
+
+
+static struct fuse_operations null_oper = {
+    getattr:   null_getattr,
+    readlink:  NULL,
+    getdir:     NULL,
+    mknod:     NULL,
+    mkdir:     NULL,
+    symlink:   NULL,
+    unlink:    NULL,
+    rmdir:     NULL,
+    rename:     NULL,
+    link:      NULL,
+    chmod:     NULL,
+    chown:     NULL,
+    truncate:  null_truncate,
+    utime:     NULL,
+    open:      null_open,
+    read:      null_read,
+    write:     null_write,
+};
+
+
+static void exit_handler()
+{
+    close(0);
+    system(unmount_cmd);    
+    exit(0);
+}
+
+static void set_signal_handlers()
+{
+    struct sigaction sa;
+
+    sa.sa_handler = exit_handler;
+    sigemptyset(&(sa.sa_mask));
+    sa.sa_flags = 0;
+
+    if (sigaction(SIGHUP, &sa, NULL) == -1 || 
+       sigaction(SIGINT, &sa, NULL) == -1 || 
+       sigaction(SIGTERM, &sa, NULL) == -1) {
+       
+       perror("Cannot set exit signal handlers");
+        exit(1);
+    }
+
+    sa.sa_handler = SIG_IGN;
+    
+    if(sigaction(SIGPIPE, &sa, NULL) == -1) {
+       perror("Cannot set ignored signals");
+        exit(1);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int argctr;
+    int flags;
+    int multithreaded;
+    struct fuse *fuse;
+
+    if(argc < 2) {
+        fprintf(stderr,
+                "usage: %s unmount_cmd [options] \n"
+                "Options:\n"
+                "    -d      enable debug output\n"
+                "    -s      disable multithreaded operation\n",
+                argv[0]);
+        exit(1);
+    }
+
+    argctr = 1;
+    unmount_cmd = argv[argctr++];
+
+    set_signal_handlers();
+
+    flags = 0;
+    multithreaded = 1;
+    for(; argctr < argc && argv[argctr][0] == '-'; argctr ++) {
+        switch(argv[argctr][1]) {
+        case 'd':
+            flags |= FUSE_DEBUG;
+            break;
+
+        case 's':
+            multithreaded = 0;
+            break;
+
+        default:
+            fprintf(stderr, "invalid option: %s\n", argv[argctr]);
+            exit(1);
+        }
+    }
+    if(argctr != argc) {
+        fprintf(stderr, "missing or surplus argument\n");
+        exit(1);
+    }
+
+    fuse = fuse_new(0, flags);
+    fuse_set_operations(fuse, &null_oper);
+
+    if(multithreaded)
+        fuse_loop_mt(fuse);
+    else
+        fuse_loop(fuse);
+
+    return 0;
+}
index 0cd9e5c227ddddf912391cb710a8340171a1a9c4..fd5ef9c419ed3feb78d35d33aadcb03656ad9759 100644 (file)
@@ -56,22 +56,22 @@ struct fuse_attr {
 #define FATTR_UTIME    (1 << 4)
 
 enum fuse_opcode {
-       FUSE_LOOKUP     = 1,
-       FUSE_FORGET,
-       FUSE_GETATTR,
-       FUSE_SETATTR,
-       FUSE_READLINK,
-       FUSE_SYMLINK,
-       FUSE_GETDIR,
-       FUSE_MKNOD,
-       FUSE_MKDIR,
-       FUSE_UNLINK,
-       FUSE_RMDIR,
-       FUSE_RENAME,
-       FUSE_LINK,
-       FUSE_OPEN,
-       FUSE_READ,
-       FUSE_WRITE,
+       FUSE_LOOKUP     = 1,
+       FUSE_FORGET     = 2,
+       FUSE_GETATTR    = 3,
+       FUSE_SETATTR    = 4,
+       FUSE_READLINK   = 5,
+       FUSE_SYMLINK    = 6,
+       FUSE_GETDIR     = 7,
+       FUSE_MKNOD      = 8,
+       FUSE_MKDIR      = 9,
+       FUSE_UNLINK     = 10,
+       FUSE_RMDIR      = 11,
+       FUSE_RENAME     = 12,
+       FUSE_LINK       = 13,
+       FUSE_OPEN       = 14,
+       FUSE_READ       = 15,
+       FUSE_WRITE      = 16,
 };
 
 /* Conservative buffer size for the client */
@@ -98,7 +98,7 @@ struct fuse_getdir_out {
 struct fuse_mknod_in {
        unsigned short mode;
        unsigned short rdev;
-       char name[1];
+       char name[0];
 };
 
 struct fuse_mknod_out {
@@ -108,17 +108,17 @@ struct fuse_mknod_out {
 
 struct fuse_mkdir_in {
        unsigned short mode;
-       char name[1];
+       char name[0];
 };
 
 struct fuse_rename_in {
        unsigned long newdir;
-       char names[1];
+       char names[0];
 };
 
 struct fuse_link_in {
        unsigned long newdir;
-       char name[1];
+       char name[0];
 };
 
 struct fuse_setattr_in {
@@ -142,7 +142,7 @@ struct fuse_read_in {
 struct fuse_write_in {
        unsigned long long offset;
        unsigned int size;
-       char buf[1];
+       char buf[0];
 };
 
 struct fuse_in_header {
index d4206e136bc428d404cf7035a7d289853ce662d7..4395736a0d58b293c7129b712e26187d918f95e9 100644 (file)
 #include <linux/proc_fs.h>
 #include <linux/file.h>
 
-#define IHSIZE sizeof(struct fuse_in_header)
-#define OHSIZE sizeof(struct fuse_out_header)
+/* If more requests are outstanding, then the operation will block */
+#define MAX_OUTSTANDING 10
 
 static struct proc_dir_entry *proc_fs_fuse;
 struct proc_dir_entry *proc_fuse_dev;
+static kmem_cache_t *fuse_req_cachep;
 
-static int interrupt_error(enum fuse_opcode opcode)
+static struct fuse_req *request_new(void)
+{
+       struct fuse_req *req;
+
+       req = (struct fuse_req *) kmem_cache_alloc(fuse_req_cachep, SLAB_NOFS);
+       if(req) {
+               INIT_LIST_HEAD(&req->list);
+               req->issync = 0;
+               req->locked = 0;
+               req->interrupted = 0;
+               req->sent = 0;
+               req->finished = 0;
+               req->in = NULL;
+               req->out = NULL;
+               init_waitqueue_head(&req->waitq);
+       }
+
+       return req;
+}
+
+static void request_free(struct fuse_req *req)
+{
+       kmem_cache_free(fuse_req_cachep, req);
+}
+
+static int request_restartable(enum fuse_opcode opcode)
 {
        switch(opcode) {
        case FUSE_LOOKUP:
@@ -28,222 +54,214 @@ static int interrupt_error(enum fuse_opcode opcode)
        case FUSE_OPEN:
        case FUSE_READ:
        case FUSE_WRITE:
-               return -ERESTARTSYS;
+               return 1;
 
        default:
-               /* Operations which modify the filesystem cannot be safely
-                   restarted, because it is uncertain whether the
-                   operation has completed or not... */
-               return -EINTR;
+               return 0;
        }
 }
 
-static int request_wait_answer(struct fuse_req *req)
+/* Called with fuse_lock held.  Releases, and then reaquires it. */
+static void request_wait_answer(struct fuse_req *req)
 {
-       int ret = 0;
-       DECLARE_WAITQUEUE(wait, current);
+       int intr;
+       
+       spin_unlock(&fuse_lock);
+       intr = wait_event_interruptible(req->waitq, req->finished);
+       spin_lock(&fuse_lock);
+       if(!intr)
+               return;
 
-       add_wait_queue(&req->waitq, &wait);
-       while(!list_empty(&req->list)) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               if(signal_pending(current)) {
-                       ret = interrupt_error(req->opcode);
-                       break;
-               }
+       /* Request interrupted... Wait for it to be unlocked */
+       if(req->locked) {
+               req->interrupted = 1;
                spin_unlock(&fuse_lock);
-               schedule();
+               wait_event(req->waitq, !req->locked);
                spin_lock(&fuse_lock);
        }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&req->waitq, &wait);
+       
+       /* Operations which modify the filesystem cannot safely be
+          restarted, because it is uncertain whether the operation has
+          completed or not... */
+       if(req->sent && !request_restartable(req->in->h.opcode))
+               req->out->h.error = -EINTR;
+       else
+               req->out->h.error = -ERESTARTSYS;
+}
 
-       return ret;
+static int get_unique(struct fuse_conn *fc)
+{
+       do fc->reqctr++;
+       while(!fc->reqctr);
+       return fc->reqctr;
 }
 
-static int request_check(struct fuse_req *req, struct fuse_out *outp)
+void request_send(struct fuse_conn *fc, struct fuse_in *in,
+                 struct fuse_out *out)
 {
-       struct fuse_out_header *oh;
-       unsigned int size;
+       struct fuse_req *req;
 
-       if(!req->out)
-               return -ECONNABORTED;
+       out->h.error = -ERESTARTSYS;
+       if(down_interruptible(&fc->outstanding))
+               return;
 
-       oh = (struct fuse_out_header *) req->out;
-       size = req->outsize - OHSIZE;
-       
-       if (oh->error <= -512 || oh->error > 0) {
-               printk("fuse: bad error value: %i\n", oh->error);
-               return -EPROTO;
-       }
+       out->h.error = -ENOMEM;
+       req = request_new();
+       if(!req)
+               return;
+
+       req->in = in;
+       req->out = out;
+       req->issync = 1;
 
-       if(size > outp->argsize || 
-          (oh->error == 0 && !outp->argvar && size != outp->argsize) ||
-          (oh->error != 0 && size != 0)) {
-               printk("fuse: invalid argument length: %i (%i)\n", size,
-                      req->opcode);
-               return -EPROTO;
+       spin_lock(&fuse_lock);
+       out->h.error = -ENOTCONN;
+       if(fc->file) {
+               in->h.unique = get_unique(fc);          
+               list_add_tail(&req->list, &fc->pending);
+               wake_up(&fc->waitq);
+               request_wait_answer(req);
+               list_del(&req->list);
        }
-       
-       memcpy(&outp->h, oh, OHSIZE);
-       outp->argsize = size;
-       if(size)
-               memcpy(outp->arg, req->out + OHSIZE, size);
-       
-       return oh->error;
-}
+       spin_unlock(&fuse_lock);
+       request_free(req);
 
-static void request_free(struct fuse_req *req)
-{
-       kfree(req->in);
-       kfree(req->out);
-       kfree(req);
+       up(&fc->outstanding);
 }
 
 
-static struct fuse_req *request_new(struct fuse_conn *fc, struct fuse_in *inp,
-                                   struct fuse_out *outp)
+static inline void destroy_request(struct fuse_conn *fc, struct fuse_req *req)
 {
-       struct fuse_req *req;
-       
-       req = kmalloc(sizeof(*req), GFP_NOFS);
-       if(!req)
-               return NULL;
+       if(req) {
+               int i;
 
-       if(outp)
-               req->outsize = OHSIZE + outp->argsize;
-       else
-               req->outsize = 0;
-       req->out = NULL;
-
-       req->insize = IHSIZE + inp->argsize;
-       req->in = kmalloc(req->insize, GFP_NOFS);
-       if(!req->in) {
+               for(i = 0; i < req->in->numargs; i++)
+                       kfree(req->in->args[i].value);
+               kfree(req->in);
                request_free(req);
-               return NULL;
        }
-       memcpy(req->in, &inp->h, IHSIZE);
-       if(inp->argsize)
-               memcpy(req->in + IHSIZE, inp->arg, inp->argsize);
-
-       req->opcode = inp->h.opcode;
-       init_waitqueue_head(&req->waitq);
-
-       return req;
 }
 
-/* If 'outp' is NULL then the request this is asynchronous */
-void request_send(struct fuse_conn *fc, struct fuse_in *inp,
-                 struct fuse_out *outp)
+/* This one is currently only used for sending the FORGET request, which is
+   a kernel initiated request.  So the outstanding semaphore is not used.  */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in)
 {
-       int ret;
-       struct fuse_in_header *ih;
        struct fuse_req *req;
 
-       ret = -ENOMEM;
-       req = request_new(fc, inp, outp);
+       req = request_new();
        if(!req)
-               goto out;
+               return -ENOMEM;
+
+       req->in = in;
+       req->issync = 0;
 
        spin_lock(&fuse_lock);
-       ret = -ENOTCONN;
-       if(!fc->file)
-               goto out_unlock_free;
-       
-       ih = (struct fuse_in_header *) req->in;
-       if(outp) {
-               do fc->reqctr++;
-               while(!fc->reqctr);
-               ih->unique = req->unique = fc->reqctr;
+       if(!fc->file) {
+               spin_unlock(&fuse_lock);
+               request_free(req);
+               return -ENOTCONN;
        }
-       else
-               ih->unique = req->unique = 0;
 
        list_add_tail(&req->list, &fc->pending);
        wake_up(&fc->waitq);
-
-       /* Async reqests are freed in fuse_dev_read() */
-       if(!outp) 
-               goto out_unlock; 
-       
-       ret = request_wait_answer(req);
-       list_del(&req->list);
-       if(!ret)
-               ret = request_check(req, outp);
-
-  out_unlock_free:
-       request_free(req);
-  out_unlock:
        spin_unlock(&fuse_lock);
-  out:
-       if(outp)
-               outp->h.error = ret;
+       return 0;
 }
 
-static int request_wait(struct fuse_conn *fc)
+static void request_wait(struct fuse_conn *fc)
 {
-       int ret = 0;
        DECLARE_WAITQUEUE(wait, current);
-       
+
        add_wait_queue_exclusive(&fc->waitq, &wait);
        while(list_empty(&fc->pending)) {
                set_current_state(TASK_INTERRUPTIBLE);
-               if(signal_pending(current)) {
-                       ret = -ERESTARTSYS;
+               if(signal_pending(current))
                        break;
-               }
+
                spin_unlock(&fuse_lock);
                schedule();
                spin_lock(&fuse_lock);
        }
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&fc->waitq, &wait);
+}
 
-       return ret;
+static inline int copy_in_one(const void *src, size_t srclen, char **dstp,
+                             size_t *dstlenp)
+{
+       if(*dstlenp < srclen) {
+               printk("fuse_dev_read: buffer too small\n");
+               return -EIO;
+       }
+                       
+       if(copy_to_user(*dstp, src, srclen))
+               return -EFAULT;
+
+       *dstp += srclen;
+       *dstlenp -= srclen;
+
+       return 0;
 }
 
+static inline int copy_in_args(struct fuse_in *in, char *buf, size_t nbytes)
+{
+       int err;
+       int i;
+       size_t orignbytes = nbytes;
+               
+       err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes);
+       if(err)
+               return err;
+
+       for(i = 0; i < in->numargs; i++) {
+               struct fuse_in_arg *arg = &in->args[i];
+               err = copy_in_one(arg->value, arg->size, &buf, &nbytes);
+               if(err)
+                       return err;
+       }
+
+       return orignbytes - nbytes;
+}
 
 static ssize_t fuse_dev_read(struct file *file, char *buf, size_t nbytes,
                             loff_t *off)
 {
-       int ret;
+       ssize_t ret;
        struct fuse_conn *fc = DEV_FC(file);
-       struct fuse_req *req;
-       char *tmpbuf;
-       unsigned int size;
+       struct fuse_req *req = NULL;
 
        if(fc->sb == NULL)
                return -EPERM;
-       
+
        spin_lock(&fuse_lock);
-       ret = request_wait(fc);
-       if(ret)
-               goto err;
-       
-       req = list_entry(fc->pending.next, struct fuse_req, list);
-       size = req->insize;
-       if(nbytes < size) {
-               printk("fuse_dev_read: buffer too small\n");
-               ret = -EIO;
-               goto err;
+       request_wait(fc);
+       if(!list_empty(&fc->pending)) {
+               req = list_entry(fc->pending.next, struct fuse_req, list);
+               list_del_init(&req->list);
+               req->locked = 1;
        }
-       tmpbuf = req->in;
-       req->in = NULL;
-
-       list_del(&req->list);
-       if(req->outsize)
-               list_add_tail(&req->list, &fc->processing);
-       else
-               request_free(req);
        spin_unlock(&fuse_lock);
+       if(req == NULL)
+               return -ERESTARTSYS;
 
-       if(copy_to_user(buf, tmpbuf, size))
-               return -EFAULT;
-       
-       kfree(tmpbuf);
-       return size;
-
-  err:
+       ret = copy_in_args(req->in, buf, nbytes);
+       spin_lock(&fuse_lock);
+       if(req->issync || ret < 0) {
+               if(ret < 0) 
+                       list_add_tail(&req->list, &fc->pending);
+               else {
+                       list_add_tail(&req->list, &fc->processing);
+                       req->sent = 1;
+               }
+               req->locked = 0;
+               if(req->interrupted)
+                       wake_up(&req->waitq);
+               
+               req = NULL;
+       }
        spin_unlock(&fuse_lock);
+       destroy_request(fc, req);
+
        return ret;
 }
 
@@ -255,7 +273,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
        list_for_each(entry, &fc->processing) {
                struct fuse_req *tmp;
                tmp = list_entry(entry, struct fuse_req, list);
-               if(tmp->unique == unique) {
+               if(tmp->in->h.unique == unique) {
                        req = tmp;
                        break;
                }
@@ -264,58 +282,135 @@ static struct fuse_req *request_find(struct fuse_conn *fc, unsigned int unique)
        return req;
 }
 
+static void process_getdir(struct fuse_req *req)
+{
+       struct fuse_getdir_out *arg;
+       arg = (struct fuse_getdir_out *) req->out->args[0].value;
+       arg->file = fget(arg->fd);
+}
+
+static inline int copy_out_one(struct fuse_out_arg *arg, const char **srcp,
+                              size_t *srclenp, int allowvar)
+{
+       size_t dstlen = arg->size;
+       if(*srclenp < dstlen) {
+               if(!allowvar) {
+                       printk("fuse_dev_write: write is short\n");
+                       return -EIO;
+               }
+               dstlen = *srclenp;
+       }
+
+       if(dstlen) {
+               if(copy_from_user(arg->value, *srcp, dstlen))
+                       return -EFAULT;
+       }
+
+       *srcp += dstlen;
+       *srclenp -= dstlen;
+       arg->size = dstlen;
+
+       return 0;
+}
+
+static inline int copy_out_args(struct fuse_out *out, const char *buf,
+                               size_t nbytes)
+{
+       int err;
+       int i;
+
+       buf += sizeof(struct fuse_out_header);
+       nbytes -= sizeof(struct fuse_out_header);
+               
+       if(!out->h.error) {
+               for(i = 0; i < out->numargs; i++) {
+                       struct fuse_out_arg *arg = &out->args[i];
+                       int allowvar;
+
+                       if(out->argvar && i == out->numargs - 1)
+                               allowvar = 1;
+                       else
+                               allowvar = 0;
+
+                       err = copy_out_one(arg, &buf, &nbytes, allowvar);
+                       if(err)
+                               return err;
+               }
+       }
+
+       if(nbytes != 0) {
+               printk("fuse_dev_write: write is long\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static inline int copy_out_header(struct fuse_out_header *oh, const char *buf,
+                                 size_t nbytes)
+{
+       if(nbytes < sizeof(struct fuse_out_header)) {
+               printk("fuse_dev_write: write is short\n");
+               return -EIO;
+       }
+       
+       if(copy_from_user(oh, buf, sizeof(struct fuse_out_header)))
+               return -EFAULT;
+
+        if (oh->error <= -512 || oh->error > 0) {
+                printk("fuse_dev_write: bad error value\n");
+                return -EIO;
+        }
+
+       return 0;
+}
+
 static ssize_t fuse_dev_write(struct file *file, const char *buf,
                              size_t nbytes, loff_t *off)
 {
-       ssize_t ret;
+       int err;
        struct fuse_conn *fc = DEV_FC(file);
        struct fuse_req *req;
-       char *tmpbuf;
-       struct fuse_out_header *oh;
+       struct fuse_out_header oh;
 
        if(!fc->sb)
                return -EPERM;
 
-       ret = -EIO;
-       if(nbytes < OHSIZE || nbytes > OHSIZE + PAGE_SIZE) {
-               printk("fuse_dev_write: write is short or long\n");
-               goto out;
-       }
-       
-       ret = -ENOMEM;
-       tmpbuf = kmalloc(nbytes, GFP_NOFS);
-       if(!tmpbuf)
-               goto out;
-       
-       ret = -EFAULT;
-       if(copy_from_user(tmpbuf, buf, nbytes))
-               goto out_free;
+       err = copy_out_header(&oh, buf, nbytes);
+       if(err)
+               return err;
        
        spin_lock(&fuse_lock);
-       oh =  (struct fuse_out_header *) tmpbuf;
-       req = request_find(fc, oh->unique);
-       if(req == NULL) {
-               ret = -ENOENT;
-               goto out_free_unlock;
+       req = request_find(fc, oh.unique);
+       if(req != NULL) {
+               list_del_init(&req->list);
+               req->locked = 1;
        }
-       list_del_init(&req->list);
-       if(req->opcode == FUSE_GETDIR) {
+       spin_unlock(&fuse_lock);
+       if(!req)
+               return -ENOENT;
+
+       req->out->h = oh;
+       err = copy_out_args(req->out, buf, nbytes);
+
+       spin_lock(&fuse_lock);
+       if(err)
+               list_add_tail(&fc->processing, &req->list);
+       else {
                /* fget() needs to be done in this context */
-               struct fuse_getdir_out *arg;
-               arg = (struct fuse_getdir_out *) (tmpbuf + OHSIZE);
-               arg->file = fget(arg->fd);
-       }
-       req->out = tmpbuf;
-       req->outsize = nbytes;
-       tmpbuf = NULL;
-       ret = nbytes;
-       wake_up(&req->waitq);
-  out_free_unlock:
+               if(req->in->h.opcode == FUSE_GETDIR && !oh.error)
+                       process_getdir(req);
+               req->finished = 1;
+       }       
+       req->locked = 0;
+       if(!err || req->interrupted)
+               wake_up(&req->waitq);
        spin_unlock(&fuse_lock);
-  out_free:
-       kfree(tmpbuf);
-  out:
-       return ret;
+
+       if(!err)
+               return nbytes;
+       else
+               return err;
 }
 
 
@@ -350,6 +445,7 @@ static struct fuse_conn *new_conn(void)
                init_waitqueue_head(&fc->waitq);
                INIT_LIST_HEAD(&fc->pending);
                INIT_LIST_HEAD(&fc->processing);
+               sema_init(&fc->outstanding, MAX_OUTSTANDING);
                fc->reqctr = 1;
        }
        return fc;
@@ -369,16 +465,18 @@ static int fuse_dev_open(struct inode *inode, struct file *file)
        return 0;
 }
 
-static void end_requests(struct list_head *head)
+static void end_requests(struct fuse_conn *fc, struct list_head *head)
 {
        while(!list_empty(head)) {
                struct fuse_req *req;
                req = list_entry(head->next, struct fuse_req, list);
                list_del_init(&req->list);
-               if(req->outsize)
+               if(req->issync) {
+                       req->out->h.error = -ECONNABORTED;
                        wake_up(&req->waitq);
+               }
                else
-                       request_free(req);
+                       destroy_request(fc, req);
        }
 }
 
@@ -388,8 +486,8 @@ static int fuse_dev_release(struct inode *inode, struct file *file)
 
        spin_lock(&fuse_lock);
        fc->file = NULL;
-       end_requests(&fc->pending);
-       end_requests(&fc->processing);
+       end_requests(fc, &fc->pending);
+       end_requests(fc, &fc->processing);
        fuse_release_conn(fc);
        spin_unlock(&fuse_lock);
        return 0;
@@ -411,6 +509,12 @@ int fuse_dev_init()
        proc_fs_fuse = NULL;
        proc_fuse_dev = NULL;
 
+       fuse_req_cachep = kmem_cache_create("fuser_request",
+                                            sizeof(struct fuse_req),
+                                            0, 0, NULL, NULL);
+       if(!fuse_req_cachep)
+               return -ENOMEM;
+
        ret = -EIO;
        proc_fs_fuse = proc_mkdir("fuse", proc_root_fs);
        if(!proc_fs_fuse) {
@@ -440,6 +544,8 @@ void fuse_dev_cleanup()
                remove_proc_entry("dev", proc_fs_fuse);
                remove_proc_entry("fuse", proc_root_fs);
        }
+       
+       kmem_cache_destroy(fuse_req_cachep);
 }
 
 /* 
index 537b71ca2fb3061b8c6e464205356a497e1f5ef7..a3f148512a742935e15494aeb40b448ed86c0d4f 100644 (file)
@@ -79,29 +79,52 @@ struct inode *fuse_iget(struct super_block *sb, ino_t ino,
        return inode;
 }
 
+/* If the inode belongs to an existing directory, then it cannot be
+ assigned to new dentry */
+static int inode_ok(struct inode *inode)
+{
+       struct dentry *alias;
+       if(S_ISDIR(inode->i_mode) && (alias = d_find_alias(inode)) != NULL) {
+               dput(alias);
+               printk("fuse: cannot assign an existing directory\n");
+               return 0;
+
+       }
+       return 1;
+}
+
 static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry)
 {
        int ret;
        struct fuse_conn *fc = INO_FC(dir);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_lookup_out arg;
+       struct fuse_lookup_out outarg;
        struct inode *inode;
 
        in.h.opcode = FUSE_LOOKUP;
        in.h.ino = dir->i_ino;
-       in.argsize = entry->d_name.len + 1;
-       in.arg = entry->d_name.name;
-       out.argsize = sizeof(arg);
-       out.arg = &arg;
+       in.numargs = 1;
+       in.args[0].size = entry->d_name.len + 1;
+       in.args[0].value = entry->d_name.name;
+       out.numargs = 1;
+       out.args[0].size = sizeof(outarg);
+       out.args[0].value = &outarg;
        request_send(fc, &in, &out);
        
        inode = NULL;
        if(!out.h.error) {
                ret = -ENOMEM;
-               inode = fuse_iget(dir->i_sb, arg.ino, &arg.attr, out.h.unique);
+               inode = fuse_iget(dir->i_sb, outarg.ino, &outarg.attr,
+                                 out.h.unique);
                if(!inode) 
                        goto err;
+
+               ret = -EPROTO;
+               if(!inode_ok(inode)) {
+                       iput(inode);
+                       goto err;
+               }
        }
        else if(out.h.error != -ENOENT) {
                ret = out.h.error;
@@ -126,28 +149,25 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
        struct fuse_conn *fc = INO_FC(dir);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_mknod_in *inarg;
-       unsigned int insize;
+       struct fuse_mknod_in inarg;
        struct fuse_mknod_out outarg;
        struct inode *inode;
-       
-       insize = offsetof(struct fuse_mknod_in, name) + entry->d_name.len + 1;
-       inarg = kmalloc(insize, GFP_KERNEL);
-       if(!inarg)
-               return -ENOMEM;
-       
-       inarg->mode = mode;
-       inarg->rdev = rdev;
-       strcpy(inarg->name, entry->d_name.name);
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.mode = mode;
+       inarg.rdev = rdev;
 
        in.h.opcode = FUSE_MKNOD;
        in.h.ino = dir->i_ino;
-       in.argsize = insize;
-       in.arg = inarg;
-       out.argsize = sizeof(outarg);
-       out.arg = &outarg;
+       in.numargs = 2;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       in.args[1].size = entry->d_name.len + 1;
+       in.args[1].value = entry->d_name.name;
+       out.numargs = 1;
+       out.args[0].size = sizeof(outarg);
+       out.args[0].value = &outarg;
        request_send(fc, &in, &out);
-       kfree(inarg);
 
        if(out.h.error) 
                return out.h.error;
@@ -156,6 +176,18 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode,
        if(!inode) 
                return -ENOMEM;
 
+       /* Don't allow userspace to do really stupid things... */
+       if((inode->i_mode ^ mode) & S_IFMT) {
+               iput(inode);
+               printk("fuse_mknod: inode has wrong type\n");
+               return -EPROTO;
+       }
+
+       if(!inode_ok(inode)) {
+               iput(inode);
+               return -EPROTO;
+       }
+
        d_instantiate(entry, inode);
        return 0;
 }
@@ -171,23 +203,19 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode)
        struct fuse_conn *fc = INO_FC(dir);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_mkdir_in *inarg;
-       unsigned int insize;
-       
-       insize = offsetof(struct fuse_mkdir_in, name) + entry->d_name.len + 1;
-       inarg = kmalloc(insize, GFP_KERNEL);
-       if(!inarg)
-               return -ENOMEM;
-       
-       inarg->mode = mode;
-       strcpy(inarg->name, entry->d_name.name);
+       struct fuse_mkdir_in inarg;
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.mode = mode;
 
        in.h.opcode = FUSE_MKDIR;
        in.h.ino = dir->i_ino;
-       in.argsize = insize;
-       in.arg = inarg;
+       in.numargs = 2;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       in.args[1].size = entry->d_name.len + 1;
+       in.args[1].value = entry->d_name.name;
        request_send(fc, &in, &out);
-       kfree(inarg);
 
        return out.h.error;
 }
@@ -198,24 +226,16 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
        struct fuse_conn *fc = INO_FC(dir);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       char *inarg;
-       unsigned int insize;
-       
-       insize = entry->d_name.len + 1 + strlen(link) + 1;
-       inarg = kmalloc(insize, GFP_KERNEL);
-       if(!inarg)
-               return -ENOMEM;
-       
-       strcpy(inarg, entry->d_name.name);
-       strcpy(inarg + entry->d_name.len + 1, link);
 
        in.h.opcode = FUSE_SYMLINK;
        in.h.ino = dir->i_ino;
-       in.argsize = insize;
-       in.arg = inarg;
+       in.numargs = 2;
+       in.args[0].size = entry->d_name.len + 1;
+       in.args[0].value = entry->d_name.name;
+       in.args[1].size = strlen(link) + 1;
+       in.args[1].value = link;
        request_send(fc, &in, &out);
-       kfree(inarg);
-       
+
        return out.h.error;
 }
 
@@ -228,9 +248,11 @@ static int fuse_remove(struct inode *dir, struct dentry *entry,
 
        in.h.opcode = op;
        in.h.ino = dir->i_ino;
-       in.argsize = entry->d_name.len + 1;
-       in.arg = entry->d_name.name;
+       in.numargs = 1;
+       in.args[0].size = entry->d_name.len + 1;
+       in.args[0].value = entry->d_name.name;
        request_send(fc, &in, &out);
+
        return out.h.error;
 }
 
@@ -250,27 +272,21 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        struct fuse_conn *fc = INO_FC(olddir);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_rename_in *inarg;
-       unsigned int oldnamsize = oldent->d_name.len + 1;
-       unsigned int newnamsize = newent->d_name.len + 1;
-       unsigned int insize;
+       struct fuse_rename_in inarg;
        
-       insize = offsetof(struct fuse_rename_in, names) + oldnamsize +
-               newnamsize;
-       inarg = kmalloc(insize, GFP_KERNEL);
-       if(!inarg)
-               return -ENOMEM;
-       
-       inarg->newdir = newdir->i_ino;
-       strcpy(inarg->names, oldent->d_name.name);
-       strcpy(inarg->names + oldnamsize, newent->d_name.name);
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.newdir = newdir->i_ino;
 
        in.h.opcode = FUSE_RENAME;
        in.h.ino = olddir->i_ino;
-       in.argsize = insize;
-       in.arg = inarg;
+       in.numargs = 3;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       in.args[1].size = oldent->d_name.len + 1;
+       in.args[1].value = oldent->d_name.name;
+       in.args[2].size = newent->d_name.len + 1;
+       in.args[2].value = newent->d_name.name;
        request_send(fc, &in, &out);
-       kfree(inarg);
 
        return out.h.error;
 }
@@ -282,23 +298,19 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_link_in *inarg;
-       unsigned int insize;
-       
-       insize = offsetof(struct fuse_link_in, name) + newent->d_name.len + 1;
-       inarg = kmalloc(insize, GFP_KERNEL);
-       if(!inarg)
-               return -ENOMEM;
+       struct fuse_link_in inarg;
        
-       inarg->newdir = newdir->i_ino;
-       strcpy(inarg->name, newent->d_name.name);
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.newdir = newdir->i_ino;
 
        in.h.opcode = FUSE_LINK;
        in.h.ino = inode->i_ino;
-       in.argsize = insize;
-       in.arg = inarg;
+       in.numargs = 2;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       in.args[1].size = newent->d_name.len + 1;
+       in.args[1].value = newent->d_name.name;
        request_send(fc, &in, &out);
-       kfree(inarg);
 
        return out.h.error;
 }
@@ -328,8 +340,9 @@ static int fuse_revalidate(struct dentry *entry)
 
        in.h.opcode = FUSE_GETATTR;
        in.h.ino = inode->i_ino;
-       out.argsize = sizeof(arg);
-       out.arg = &arg;
+       out.numargs = 1;
+       out.args[0].size = sizeof(arg);
+       out.args[0].value = &arg;
        request_send(fc, &in, &out);
        
        if(!out.h.error)
@@ -400,16 +413,17 @@ static char *read_link(struct dentry *dentry)
 
        in.h.opcode = FUSE_READLINK;
        in.h.ino = inode->i_ino;
-       out.arg = link;
-       out.argsize = PAGE_SIZE - 1;
        out.argvar = 1;
+       out.numargs = 1;
+       out.args[0].size = PAGE_SIZE - 1;
+       out.args[0].value = link;
        request_send(fc, &in, &out);
        if(out.h.error) {
                free_page((unsigned long) link);
                return ERR_PTR(out.h.error);
        }
 
-       link[out.argsize] = '\0';
+       link[out.args[0].size] = '\0';
        return link;
 }
 
@@ -453,8 +467,9 @@ static int fuse_dir_open(struct inode *inode, struct file *file)
        
        in.h.opcode = FUSE_GETDIR;
        in.h.ino = inode->i_ino;
-       out.argsize = sizeof(outarg);
-       out.arg = &outarg;
+       out.numargs = 1;
+       out.args[0].size = sizeof(outarg);
+       out.args[0].value = &outarg;
        request_send(fc, &in, &out);
        if(!out.h.error) {
                struct file *cfile = outarg.file;
@@ -521,14 +536,17 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
        struct fuse_setattr_in inarg;
        struct fuse_setattr_out outarg;
 
+       memset(&inarg, 0, sizeof(inarg));
        inarg.valid = iattr_to_fattr(attr, &inarg.attr);
        
        in.h.opcode = FUSE_SETATTR;
        in.h.ino = inode->i_ino;
-       in.argsize = sizeof(inarg);
-       in.arg = &inarg;
-       out.argsize = sizeof(outarg);
-       out.arg = &outarg;
+       in.numargs = 1;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       out.numargs = 1;
+       out.args[0].size = sizeof(outarg);
+       out.args[0].value = &outarg;
        request_send(fc, &in, &out);
 
        if(!out.h.error) {
index a67bdcd117a68ba6621c88f566def61226c8a1db..b7cb2f4fe28e86e4c04ea353b972ce07c8fb65bf 100644 (file)
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 
-
 static int fuse_open(struct inode *inode, struct file *file)
 {
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_open_in arg;
+       struct fuse_open_in inarg;
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.flags = file->f_flags & ~O_EXCL;
 
-       arg.flags = file->f_flags & ~O_EXCL;
        in.h.opcode = FUSE_OPEN;
        in.h.ino = inode->i_ino;
-       in.argsize = sizeof(arg);
-       in.arg = &arg;
+       in.numargs = 1;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
        request_send(fc, &in, &out);
        if(!out.h.error)
                invalidate_inode_pages(inode);
@@ -37,27 +39,30 @@ static int fuse_readpage(struct file *file, struct page *page)
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_read_in arg;
+       struct fuse_read_in inarg;
        char *buffer;
 
        buffer = kmap(page);
-
-       arg.offset = page->index << PAGE_CACHE_SHIFT;
-       arg.size = PAGE_CACHE_SIZE;
+       
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.offset = page->index << PAGE_CACHE_SHIFT;
+       inarg.size = PAGE_CACHE_SIZE;
 
        in.h.opcode = FUSE_READ;
        in.h.ino = inode->i_ino;
-       in.argsize = sizeof(arg);
-       in.arg = &arg;
-       out.argsize = PAGE_CACHE_SIZE;
+       in.numargs = 1;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
        out.argvar = 1;
-       out.arg = buffer;
+       out.numargs = 1;
+       out.args[0].size = PAGE_CACHE_SIZE;
+       out.args[0].value = buffer;
 
        request_send(fc, &in, &out);
        if(!out.h.error) {
-               if(out.argsize < PAGE_CACHE_SIZE) 
-                       memset(buffer + out.argsize, 0,
-                              PAGE_CACHE_SIZE - out.argsize);
+               size_t outsize = out.args[0].size;
+               if(outsize < PAGE_CACHE_SIZE) 
+                       memset(buffer + outsize, 0, PAGE_CACHE_SIZE - outsize);
                SetPageUptodate(page);
        }
 
@@ -73,27 +78,25 @@ static int write_buffer(struct inode *inode, struct page *page,
        struct fuse_conn *fc = INO_FC(inode);
        struct fuse_in in = FUSE_IN_INIT;
        struct fuse_out out = FUSE_OUT_INIT;
-       struct fuse_write_in *arg;
-       size_t argsize;
+       struct fuse_write_in inarg;
        char *buffer;
 
-       argsize = offsetof(struct fuse_write_in, buf) + count;
-       arg = kmalloc(argsize, GFP_KERNEL);
-       if(!arg)
-               return -ENOMEM;
-
-       arg->offset = (page->index << PAGE_CACHE_SHIFT) + offset;
-       arg->size = count;
        buffer = kmap(page);
-       memcpy(arg->buf, buffer + offset, count);
-       kunmap(page);
+
+       memset(&inarg, 0, sizeof(inarg));
+       inarg.offset = (page->index << PAGE_CACHE_SHIFT) + offset;
+       inarg.size = count;
        
        in.h.opcode = FUSE_WRITE;
        in.h.ino = inode->i_ino;
-       in.argsize = argsize;
-       in.arg = arg;
+       in.numargs = 2;
+       in.args[0].size = sizeof(inarg);
+       in.args[0].value = &inarg;
+       in.args[1].size = count;
+       in.args[1].value = buffer + offset;
        request_send(fc, &in, &out);
-       kfree(arg);
+
+       kunmap(page);
 
        return out.h.error;
 }
index e8dde37245555a362c23a2e26e18cd78586fc502..27d8eb36f9c57aad13b07c98897d9864887c1abd 100644 (file)
 #include <linux/list.h>
 #include <linux/spinlock.h>
 
-#define MAX_CLEARED 256
-
 /**
  * A Fuse connection.
  *
  * This structure is created, when the client device is opened, and is
  * destroyed, when the client device is closed _and_ the filesystem is
- * umounted.
+ * unmounted.
  */
 struct fuse_conn {
        /** The superblock of the mounted filesystem */
@@ -36,7 +34,7 @@ struct fuse_conn {
        /** The fuse mount flags for this mount */
        unsigned int flags;
 
-       /** The client wait queue */
+       /** Readers of the connection are waiting on this */
        wait_queue_head_t waitq;
 
        /** The list of pending requests */
@@ -45,10 +43,43 @@ struct fuse_conn {
        /** The list of requests being processed */
        struct list_head processing;
 
-       /** The request id */
+       /** Controls the maximum number of outstanding requests */
+       struct semaphore outstanding;
+       
+       /** The next unique request id */
        int reqctr;
 };
 
+/** One input argument of a request */
+struct fuse_in_arg {
+       unsigned int size;
+       const void *value;
+};
+
+/** The request input */
+struct fuse_in {
+       struct fuse_in_header h;
+       unsigned int numargs;
+       struct fuse_in_arg args[3];
+};
+
+/** One output argument of a request */
+struct fuse_out_arg {
+       unsigned int size;
+       void *value;
+};
+
+/** The request output */
+struct fuse_out {
+       struct fuse_out_header h;
+       unsigned int argvar;
+       unsigned int numargs;
+       struct fuse_out_arg args[3];
+};
+
+#define FUSE_IN_INIT { {0, 0, 0}, 0}
+#define FUSE_OUT_INIT { {0, 0}, 0, 0}
+
 /**
  * A request to the client
  */
@@ -56,25 +87,28 @@ struct fuse_req {
        /** The request list */
        struct list_head list;
 
-       /** The request ID */
-       int unique;
+       /** True if the request is synchronous */
+       unsigned int issync:1;
 
-       /** The opcode */
-       enum fuse_opcode opcode;
-       
-       /** The request input size */
-       unsigned int insize;
+       /** The request is locked */
+       unsigned int locked:1;
+
+       /** The request has been interrupted while it was locked */
+       unsigned int interrupted:1;
+
+       /* The request has been sent to the client */
+       unsigned int sent:1;
+
+       /* The request is finished */
+       unsigned int finished:1;
 
        /** The request input */
-       char *in;
-       
-       /** The maximum request output size */
-       unsigned int outsize;
+       struct fuse_in *in;
 
        /** The request output */
-       char *out;
+       struct fuse_out *out;
 
-       /** The request wait queue */
+       /** Used to wake up the task waiting for completion of request*/
        wait_queue_head_t waitq;
 };
 
@@ -82,22 +116,6 @@ struct fuse_req {
 #define INO_FC(inode) ((struct fuse_conn *) (inode)->i_sb->u.generic_sbp)
 #define DEV_FC(file) ((struct fuse_conn *) (file)->private_data)
 
-struct fuse_in {
-       struct fuse_in_header h;
-       unsigned int argsize;
-       const void *arg;
-};
-
-struct fuse_out {
-       struct fuse_out_header h;
-       unsigned int argsize;
-       unsigned int argvar;
-       void *arg;
-};
-
-#define FUSE_IN_INIT { {0, 0, 0}, 0, 0 }
-#define FUSE_OUT_INIT { {0, 0}, 0, 0, 0 }
-
 
 /**
  * The proc entry for the client device ("/proc/fs/fuse/dev")
@@ -155,6 +173,11 @@ void fuse_fs_cleanup(void);
 void request_send(struct fuse_conn *fc, struct fuse_in *in,
                  struct fuse_out *out);
 
+/**
+ * Send a request for which a reply is not expected
+ */
+int request_send_noreply(struct fuse_conn *fc, struct fuse_in *in);
+
 /*
  * Local Variables:
  * indent-tabs-mode: t
index 36819b732c0a6bfa0580c98e0ed8caa7dcc094f3..df402bae7d19ac260f4166f549d129ebc09702de 100644 (file)
@@ -23,17 +23,35 @@ static void fuse_read_inode(struct inode *inode)
 static void fuse_clear_inode(struct inode *inode)
 {
        struct fuse_conn *fc = INO_FC(inode);
-       struct fuse_in in = FUSE_IN_INIT;
-       struct fuse_forget_in arg;
+       struct fuse_in *in = NULL;
+       struct fuse_forget_in *inarg = NULL;
        
-       arg.version = inode->i_version;
-       
-       in.h.opcode = FUSE_FORGET;
-       in.h.ino = inode->i_ino;
-       in.argsize = sizeof(arg);
-       in.arg = &arg;
+       if(fc == NULL)
+               return;
+
+       in = kmalloc(sizeof(struct fuse_in), GFP_NOFS);
+       if(!in)
+               return;
+
+       inarg = kmalloc(sizeof(struct fuse_forget_in), GFP_NOFS);
+       if(!inarg) 
+               goto out_free;
        
-       request_send(fc, &in, NULL);
+       memset(inarg, 0, sizeof(struct fuse_forget_in));
+       inarg->version = inode->i_version;
+               
+       in->h.opcode = FUSE_FORGET;
+       in->h.ino = inode->i_ino;
+       in->numargs = 1;
+       in->args[0].size = sizeof(struct fuse_forget_in);
+       in->args[0].value = inarg;
+               
+       if(!request_send_noreply(fc, in))
+               return;
+
+  out_free:
+       kfree(inarg);
+       kfree(in);
 }
 
 static void fuse_put_super(struct super_block *sb)
@@ -45,6 +63,7 @@ static void fuse_put_super(struct super_block *sb)
        fc->uid = 0;
        fc->flags = 0;
        fuse_release_conn(fc);
+       sb->u.generic_sbp = NULL;
        spin_unlock(&fuse_lock);
 }
 
index 8ea13ad791d1af7af2f8c60fb8db87f5a1e39060..2ed516906caa87e9919023d73731e1cb1ab5225b 100644 (file)
@@ -165,20 +165,6 @@ static fino_t find_node(struct fuse *f, fino_t parent, char *name,
     return get_ino(node);
 }
 
-static fino_t find_node_dir(struct fuse *f, fino_t parent, char *name)
-{
-    struct node *node;
-
-    pthread_mutex_lock(&f->lock);
-    node = lookup_node(f, parent, name);
-    pthread_mutex_unlock(&f->lock);
-
-    if(node != NULL)
-        return get_ino(node);
-    else
-        return (fino_t) -1;
-}
-
 static char *add_name(char *buf, char *s, const char *name)
 {
     size_t len = strlen(name);
@@ -310,7 +296,7 @@ static int fill_dir(struct fuse_dirhandle *dh, char *name, int type)
     size_t reclen;
     size_t res;
 
-    dirent.ino = find_node_dir(dh->fuse, dh->dir, name);
+    dirent.ino = (unsigned long) -1;
     dirent.namelen = strlen(name);
     strncpy(dirent.name, name, sizeof(dirent.name));
     dirent.type = type;
@@ -323,10 +309,28 @@ static int fill_dir(struct fuse_dirhandle *dh, char *name, int type)
     return 0;
 }
 
+static void send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
+{
+    int res;
+
+    if((f->flags & FUSE_DEBUG)) {
+        struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+        printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
+               out->error, strerror(-out->error), outsize);
+        fflush(stdout);
+    }
+                
+    res = write(f->fd, outbuf, outsize);
+    if(res == -1) {
+        /* ENOENT means the operation was interrupted */
+        if(errno != ENOENT)
+            perror("writing fuse device");
+    }
+}
+
 static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
                        void *arg, size_t argsize)
 {
-    int res;
     char *outbuf;
     size_t outsize;
     struct fuse_out_header *out;
@@ -347,18 +351,7 @@ static void send_reply(struct fuse *f, struct fuse_in_header *in, int error,
     if(argsize != 0)
         memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
 
-    if((f->flags & FUSE_DEBUG)) {
-        printf("   unique: %i, error: %i (%s), outsize: %i\n", out->unique,
-               out->error, strerror(-out->error), outsize);
-        fflush(stdout);
-    }
-                
-    res = write(f->fd, outbuf, outsize);
-    if(res == -1) {
-        /* ENOENT means the operation was interrupted */
-        if(errno != ENOENT)
-            perror("writing fuse device");
-    }
+    send_reply_raw(f, outbuf, outsize);
 
     free(outbuf);
 }
@@ -388,6 +381,10 @@ static void do_lookup(struct fuse *f, struct fuse_in_header *in, char *name)
 static void do_forget(struct fuse *f, struct fuse_in_header *in,
                       struct fuse_forget_in *arg)
 {
+    if(f->flags & FUSE_DEBUG) {
+        printf("FORGET %li/%i\n", in->ino, arg->version);
+        fflush(stdout);
+    }
     destroy_node(f, in->ino, arg->version);
 }
 
@@ -697,8 +694,11 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
 {
     int res;
     char *path;
-    char *buf = (char *) malloc(arg->size);
+    char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+    struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+    char *buf = outbuf + sizeof(struct fuse_out_header);
     size_t size;
+    size_t outsize;
 
     res = -ENOENT;
     path = get_path(f, in->ino);
@@ -714,9 +714,12 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
         size = res;
         res = 0;
     }
-
-    send_reply(f, in, res, buf, size);
-    free(buf);
+    out->unique = in->unique;
+    out->error = res;
+    outsize = sizeof(struct fuse_out_header) + size;
+    
+    send_reply_raw(f, outbuf, outsize);
+    free(outbuf);
 }
 
 static void do_write(struct fuse *f, struct fuse_in_header *in,
@@ -747,6 +750,12 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
     send_reply(f, in, res, NULL, 0);
 }
 
+static void free_cmd(struct fuse_cmd *cmd)
+{
+    free(cmd->buf);
+    free(cmd);
+}
+
 void __fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
 {
     struct fuse_in_header *in = (struct fuse_in_header *) cmd->buf;
@@ -766,10 +775,6 @@ void __fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
         do_lookup(f, in, (char *) inarg);
         break;
 
-    case FUSE_FORGET:
-        do_forget(f, in, (struct fuse_forget_in *) inarg);
-        break;
-
     case FUSE_GETATTR:
         do_getattr(f, in);
         break;
@@ -826,37 +831,45 @@ void __fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd)
 
     default:
         fprintf(stderr, "Operation %i not implemented\n", in->opcode);
-        /* No need to send reply to async requests */
-        if(in->unique != 0)
-            send_reply(f, in, -ENOSYS, NULL, 0);
+        send_reply(f, in, -ENOSYS, NULL, 0);
     }
-    
-    free(cmd->buf);
-    free(cmd);
+
+    free_cmd(cmd);
 }
 
 struct fuse_cmd *__fuse_read_cmd(struct fuse *f)
 {
     ssize_t res;
-    char inbuf[FUSE_MAX_IN];
     struct fuse_cmd *cmd;
-    
-    res = read(f->fd, inbuf, sizeof(inbuf));
-    if(res == -1) {
-        perror("reading fuse device");
-        /* BAD... This will happen again */
-        return NULL;
-    }
-    if((size_t) res < sizeof(struct fuse_in_header)) {
-        fprintf(stderr, "short read on fuse device\n");
-        /* Cannot happen */
-        return NULL;
-    }
+    struct fuse_in_header *in;
 
-    cmd = (struct fuse_cmd *) malloc(sizeof(*cmd));
-    cmd->buflen = res;
-    cmd->buf = (char *) malloc(cmd->buflen);
-    memcpy(cmd->buf, inbuf, cmd->buflen);
+    cmd = (struct fuse_cmd *) malloc(sizeof(struct fuse_cmd));
+    cmd->buf = (char *) malloc(FUSE_MAX_IN);
+
+    do {
+        res = read(f->fd, cmd->buf, FUSE_MAX_IN);
+        if(res == -1) {
+            perror("reading fuse device");
+            /* BAD... This will happen again */
+            free_cmd(cmd);
+            return NULL;
+        }
+        if((size_t) res < sizeof(struct fuse_in_header)) {
+            fprintf(stderr, "short read on fuse device\n");
+            /* Cannot happen */
+            free_cmd(cmd);
+            return NULL;
+        }
+        cmd->buflen = res;
+
+        /* FORGET is special: it can be done without calling filesystem
+           methods. */
+        in = (struct fuse_in_header *) cmd->buf;
+        if(in->opcode == FUSE_FORGET) {
+            void *inarg = cmd->buf + sizeof(struct fuse_in_header);
+            do_forget(f, in, (struct fuse_forget_in *) inarg);
+        }
+    } while(in->opcode == FUSE_FORGET);
 
     return cmd;
 }
index 19cc33cd6eafdb5d5e2d050e79cde3d279160659..61205547f2394a1f13bf42da83337252090834e4 100644 (file)
 #include <string.h>
 #include <pthread.h>
 #include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
 
-struct fuse_thr_data {
+#define FUSE_WORKER_IDLE 10
+
+static pthread_mutex_t fuse_mt_lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+struct fuse_worker {
+    struct fuse_worker *next;
+    struct fuse_worker *prev;
     struct fuse *f;
     void *data;
     fuse_processor_t proc;
     struct fuse_cmd *cmd;
+    int avail;
+    pthread_cond_t start;
 };
 
 static void *do_work(void *data)
 {
-    struct fuse_thr_data *d = (struct fuse_thr_data *) data;
-    d->proc(d->f, d->cmd, d->data);
-    free(d);
+    struct fuse_worker *w = (struct fuse_worker *) data;
+    int ret;
+    
+    do {
+        struct timeval now;
+        struct timespec timeout;
+
+        w->proc(w->f, w->cmd, w->data);
+
+        pthread_mutex_lock(&fuse_mt_lock);
+        w->avail = 1;
+        w->cmd = NULL;
+        gettimeofday(&now, NULL);
+        timeout.tv_sec = now.tv_sec + FUSE_WORKER_IDLE;
+        timeout.tv_nsec = now.tv_usec * 1000;
+
+        ret = 0;
+        while(w->cmd == NULL && ret != ETIMEDOUT)
+            ret = pthread_cond_timedwait(&w->start, &fuse_mt_lock, &timeout);
+
+        if(ret == ETIMEDOUT) {
+            struct fuse_worker *next = w->next;
+            struct fuse_worker *prev = w->prev;
+            prev->next = next;
+            next->prev = prev;
+            pthread_cond_destroy(&w->start);
+            free(w);
+        }
+        w->avail = 0;
+        pthread_mutex_unlock(&fuse_mt_lock);
+
+    } while(ret != ETIMEDOUT);
+
     return NULL;
 }
 
-static void start_thread(struct fuse_thr_data *d)
+static void start_thread(struct fuse_worker *w)
 {
     pthread_t thrid;
     sigset_t oldset;
@@ -39,7 +80,7 @@ static void start_thread(struct fuse_thr_data *d)
     /* Disallow signal reception in worker threads */
     sigfillset(&newset);
     pthread_sigmask(SIG_SETMASK, &newset, &oldset);
-    res = pthread_create(&thrid, NULL, do_work, d);
+    res = pthread_create(&thrid, NULL, do_work, w);
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
     if(res != 0) {
         fprintf(stderr, "Error creating thread: %s\n", strerror(res));
@@ -50,19 +91,47 @@ static void start_thread(struct fuse_thr_data *d)
 
 void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
 {
+    struct fuse_worker *head;
+
+    head = malloc(sizeof(struct fuse_worker));
+    head->next = head;
+    head->prev = head;
+
     while(1) {
-        struct fuse_thr_data *d;
+        struct fuse_worker *w;
         struct fuse_cmd *cmd = __fuse_read_cmd(f);
         if(cmd == NULL)
             exit(1);
 
-        d = malloc(sizeof(struct fuse_thr_data));
-        d->proc = proc;
-        d->f = f;
-        d->cmd = cmd;
-        d->data = data;
-        
-        start_thread(d);
+        pthread_mutex_lock(&fuse_mt_lock);
+        for(w = head->next; w != head; w = w->next) 
+            if(w->avail)
+                break;
+
+        if(w != head) {
+            pthread_cond_signal(&w->start);
+            w->cmd = cmd;
+            w = NULL;
+        }
+        else {
+            struct fuse_worker *prev = head->prev;
+            struct fuse_worker *next = head;
+            w = malloc(sizeof(struct fuse_worker));
+            w->prev = prev;
+            w->next = next;
+            next->prev = w;
+            prev->next = w;
+            w->f = f;
+            w->data = data;
+            w->proc = proc;
+            w->cmd = cmd;
+            w->avail = 0;
+            pthread_cond_init(&w->start, NULL);
+        }
+        pthread_mutex_unlock(&fuse_mt_lock);
+
+        if(w != NULL)
+            start_thread(w);
     }
 }