fix
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 6 Dec 2005 17:59:55 +0000 (17:59 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 6 Dec 2005 17:59:55 +0000 (17:59 +0000)
ChangeLog
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/fuse_kernel.h
kernel/inode.c
lib/fuse_kern_chan.c
lib/fuse_loop_mt.c
lib/fuse_lowlevel.c

index cc490145e5c7281e7cc75672f23f94a01891392c..637ca23f2a44ca047b78f53c7b145d6815678182 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2005-12-06  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Use bigger request buffer size.  write() did not work on archs
+       with > 4k page size, Bug report by Mark Haney
+
+       * ABI version 7.5:
+
+       * Extend INIT reply with data size limits
+
 2005-12-02  Miklos Szeredi <miklos@szeredi.hu>
 
        * Fix memory leak in fuse_read_cmd()/fuse_process_cmd().  Bug
index d5fb2b6ca5ea681cc990cb133473f8d854eba245..4f1e8c088d72277492a1e99239e65fdebc55c84c 100644 (file)
@@ -188,6 +188,37 @@ void fuse_release_background(struct fuse_req *req)
        spin_unlock(&fuse_lock);
 }
 
+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 (arg->major != FUSE_KERNEL_VERSION)
+               fc->conn_error = 1;
+       else {
+               fc->minor = arg->minor;
+               if (fc->minor >= 5) {
+                       fc->name_max = arg->name_max;
+                       fc->symlink_max = arg->symlink_max;
+                       fc->xattr_size_max = arg->xattr_size_max;
+                       fc->max_write = arg->max_write;
+               } else {
+                       /* Old fix values */
+                       fc->name_max = 1024;
+                       fc->symlink_max = 4096;
+                       fc->xattr_size_max = 4096;
+                       fc->max_write = 4096;
+               }
+       }
+
+       /* 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);
+}
+
 /*
  * This function is called when a request is finished.  Either a reply
  * has arrived or it was interrupted (and not yet sent) or some error
@@ -212,21 +243,9 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
                up_read(&fc->sbput_sem);
        }
        wake_up(&req->waitq);
-       if (req->in.h.opcode == FUSE_INIT) {
-               int i;
-
-               if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION)
-                       fc->conn_error = 1;
-
-               fc->minor = req->misc.init_in_out.minor;
-
-               /* 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);
-       } else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
+       if (req->in.h.opcode == FUSE_INIT)
+               process_init_reply(fc, req);
+       else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
                /* Special case for failed iget in CREATE */
                u64 nodeid = req->in.h.nodeid;
                __fuse_get_request(req);
@@ -399,7 +418,7 @@ void fuse_send_init(struct fuse_conn *fc)
        /* This is called from fuse_read_super() so there's guaranteed
           to be a request available */
        struct fuse_req *req = do_get_request(fc);
-       struct fuse_init_in_out *arg = &req->misc.init_in_out;
+       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;
@@ -407,8 +426,12 @@ void fuse_send_init(struct fuse_conn *fc)
        req->in.args[0].size = sizeof(*arg);
        req->in.args[0].value = arg;
        req->out.numargs = 1;
-       req->out.args[0].size = sizeof(*arg);
-       req->out.args[0].value = arg;
+       /* 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;
        request_send_background(fc, req);
 }
 
index 991edcf76f38c6650ed846eb72538bf3d8ce4a74..3258d42c977fad0a12d1a8006e9ef5461213992f 100644 (file)
@@ -202,7 +202,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        struct dentry *newent;
 #endif
 
-       if (entry->d_name.len > FUSE_NAME_MAX)
+       if (entry->d_name.len > fc->name_max)
                return ERR_PTR(-ENAMETOOLONG);
 
        req = fuse_get_request(fc);
@@ -274,7 +274,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
                goto out;
 
        err = -ENAMETOOLONG;
-       if (entry->d_name.len > FUSE_NAME_MAX)
+       if (entry->d_name.len > fc->name_max)
                goto out;
 
        err = -EINTR;
@@ -455,7 +455,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
        unsigned len = strlen(link) + 1;
        struct fuse_req *req;
 
-       if (len > FUSE_SYMLINK_MAX)
+       if (len > fc->symlink_max)
                return -ENAMETOOLONG;
 
        req = fuse_get_request(fc);
@@ -561,7 +561,8 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
                        fuse_invalidate_attr(newdir);
 
                /* newent will end up negative */
-               fuse_invalidate_entry_cache(newent);
+               if (newent->d_inode)
+                       fuse_invalidate_entry_cache(newent);
        } else if (err == -EINTR) {
                /* If request was interrupted, DEITY only knows if the
                   rename actually took place.  If the invalidation
@@ -798,11 +799,13 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
 static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
                         void *dstbuf, filldir_t filldir)
 {
+       struct fuse_conn *fc = get_fuse_conn(file->f_dentry->d_inode);
+
        while (nbytes >= FUSE_NAME_OFFSET) {
                struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
                size_t reclen = FUSE_DIRENT_SIZE(dirent);
                int over;
-               if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
+               if (!dirent->namelen || dirent->namelen > fc->name_max)
                        return -EIO;
                if (reclen > nbytes)
                        break;
@@ -1125,7 +1128,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
        struct fuse_setxattr_in inarg;
        int err;
 
-       if (size > FUSE_XATTR_SIZE_MAX)
+       if (size > fc->xattr_size_max)
                return -E2BIG;
 
        if (fc->no_setxattr)
index a4cc04dea9673d1e8e27058f7972b495ded280e1..2365d2e1604b964593f09c2b80adb24b7b6865fa 100644 (file)
@@ -516,7 +516,12 @@ static int fuse_commit_write(struct file *file, struct page *page,
        struct inode *inode = page->mapping->host;
        struct fuse_conn *fc = get_fuse_conn(inode);
        loff_t pos = page_offset(page) + offset;
-       struct fuse_req *req = fuse_get_request(fc);
+       struct fuse_req *req;
+
+       if (count > fc->max_write)
+               return -EIO;
+
+       req = fuse_get_request(fc);
        if (!req)
                return -EINTR;
 
index 9ae1eec87ddeaaa8876e0a6aa9e8e17ea2ba82d4..7f26b652bd0b5a89c872674da5f6e05e1ed5854f 100644 (file)
@@ -236,7 +236,8 @@ struct fuse_req {
        union {
                struct fuse_forget_in forget_in;
                struct fuse_release_in release_in;
-               struct fuse_init_in_out init_in_out;
+               struct fuse_init_in init_in;
+               struct fuse_init_out init_out;
        } misc;
 
        /** page vector */
@@ -284,6 +285,15 @@ struct fuse_conn {
        /** Maximum write size */
        unsigned max_write;
 
+       /** Maximum path segment length */
+       unsigned name_max;
+
+       /** Maximum symbolic link size */
+       unsigned symlink_max;
+
+       /** Maximum size of xattr data */
+       unsigned xattr_size_max;
+
        /** Readers of the connection are waiting on this */
        wait_queue_head_t waitq;
 
index e43153e463faa7c7cec207b4e105c550ef3f9609..26fff1306ec2d809b80d85388f62dd2088824647 100644 (file)
@@ -49,7 +49,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 4
+#define FUSE_KERNEL_MINOR_VERSION 5
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -143,13 +143,6 @@ enum fuse_opcode {
        FUSE_CREATE        = 35
 };
 
-/* Conservative buffer size for the client */
-#define FUSE_MAX_IN 8192
-
-#define FUSE_NAME_MAX 1024
-#define FUSE_SYMLINK_MAX 4096
-#define FUSE_XATTR_SIZE_MAX 4096
-
 struct fuse_entry_out {
        __u64   nodeid;         /* Inode ID */
        __u64   generation;     /* Inode generation: nodeid:gen must
@@ -283,9 +276,18 @@ struct fuse_access_in {
        __u32   padding;
 };
 
-struct fuse_init_in_out {
+struct fuse_init_in {
+       __u32   major;
+       __u32   minor;
+};
+
+struct fuse_init_out {
        __u32   major;
        __u32   minor;
+       __u32   name_max;
+       __u32   symlink_max;
+       __u32   xattr_size_max;
+       __u32   max_write;
 };
 
 struct fuse_in_header {
index 1d5656690c9a7cafa1ffdcb296676764aa2be397..8c7c7d6a15b6f24ea4b47da9ebb16402afbcaf85 100644 (file)
@@ -647,7 +647,6 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
                fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
 #endif
-       fc->max_write = FUSE_MAX_IN / 2;
 
        err = -ENOMEM;
        root = get_root_inode(sb, d.rootmode);
index d2999c50697e3c3007544958094fba1d3a986133..fbe3943637e66ed22196ef79652a43e5ad8e6d12 100644 (file)
@@ -64,6 +64,8 @@ static void fuse_kern_chan_destroy(struct fuse_chan *ch)
     close(fuse_chan_fd(ch));
 }
 
+#define MIN_BUFSIZE 0x21000
+
 struct fuse_chan *fuse_kern_chan_new(int fd)
 {
     struct fuse_chan_ops op = {
@@ -71,5 +73,7 @@ struct fuse_chan *fuse_kern_chan_new(int fd)
         .send = fuse_kern_chan_send,
         .destroy = fuse_kern_chan_destroy,
     };
-    return fuse_chan_new(&op, fd, FUSE_MAX_IN, NULL);
+    size_t bufsize = getpagesize() + 0x1000;
+    bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
+    return fuse_chan_new(&op, fd, bufsize, NULL);
 }
index 37f0922e848c650734a44a8c27896a7aeddca9f2..3566c605707e493c40b95229c303160ef8219047 100644 (file)
@@ -155,7 +155,7 @@ int fuse_session_loop_mt(struct fuse_session *se)
     memset(w, 0, sizeof(struct fuse_worker));
     w->se = se;
     w->prevch = fuse_session_next_chan(se, NULL);
-    w->ch = fuse_chan_new(&cop, -1, 0, w);
+    w->ch = fuse_chan_new(&cop, -1, fuse_chan_bufsize(w->prevch), w);
     if (w->ch == NULL) {
         free(w);
         return -1;
index 3cc49f0c251c559f59dc7b0f3e8c4e59e9d8dc88..07873689241e238e0662f10a5887b4255817f283 100644 (file)
 
 #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
 
+/* PATH_MAX is 4k on Linux, but I don't dare to define it to PATH_MAX,
+   because it may be much larger on other systems */
+#define MIN_SYMLINK 0x1000
+
+/* Generous 4k overhead for headers, includes room for xattr name
+   (XATTR_NAME_MAX = 255) */
+#define HEADER_OVERHEAD 0x1000
+
+/* 8k, the same as the old FUSE_MAX_IN constant */
+#define MIN_BUFFER_SIZE (MIN_SYMLINK + HEADER_OVERHEAD)
+
 struct fuse_ll {
     unsigned int debug : 1;
     unsigned int allow_root : 1;
@@ -683,10 +694,11 @@ static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, char *name)
         fuse_reply_err(req, ENOSYS);
 }
 
-static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
+static void do_init(fuse_req_t req, struct fuse_init_in *arg)
 {
-    struct fuse_init_in_out outarg;
+    struct fuse_init_out outarg;
     struct fuse_ll *f = req->f;
+    size_t bufsize = fuse_chan_bufsize(req->ch);
 
     if (f->debug) {
         printf("INIT: %u.%u\n", arg->major, arg->minor);
@@ -699,16 +711,37 @@ static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
     f->major = FUSE_KERNEL_VERSION;
     f->minor = arg->minor;
 
+    if (bufsize < MIN_BUFFER_SIZE) {
+        fprintf(stderr, "fuse: warning: buffer size too small: %i\n", bufsize);
+        bufsize = MIN_BUFFER_SIZE;
+    }
+
+    bufsize -= HEADER_OVERHEAD;
+
     memset(&outarg, 0, sizeof(outarg));
     outarg.major = f->major;
     outarg.minor = FUSE_KERNEL_MINOR_VERSION;
 
+    /* The calculated limits may be oversized, but because of the
+       limits in VFS names and symlinks are never larger than PATH_MAX - 1
+       and xattr values never larger than XATTR_SIZE_MAX */
+
+    /* Max two names per request */
+    outarg.symlink_max = outarg.name_max = bufsize / 2;
+    /* But if buffer is small, give more room to link name */
+    if (outarg.symlink_max < MIN_SYMLINK) {
+        outarg.symlink_max = MIN_SYMLINK;
+        /* Borrow from header overhead for the SYMLINK operation */
+        outarg.name_max = HEADER_OVERHEAD / 4;
+    }
+    outarg.xattr_size_max = outarg.max_write = bufsize;
+
     if (f->debug) {
         printf("   INIT: %u.%u\n", outarg.major, outarg.minor);
         fflush(stdout);
     }
 
-    send_reply_ok(req, &outarg, sizeof(outarg));
+    send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
 }
 
 void *fuse_req_userdata(fuse_req_t req)
@@ -759,7 +792,7 @@ static void fuse_ll_process(void *data, const char *buf, size_t len,
         fuse_reply_err(req, EACCES);
     } else switch (in->opcode) {
     case FUSE_INIT:
-        do_init(req, (struct fuse_init_in_out *) inarg);
+        do_init(req, (struct fuse_init_in *) inarg);
         break;
 
     case FUSE_LOOKUP: