fix
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 15 Dec 2005 16:41:20 +0000 (16:41 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Thu, 15 Dec 2005 16:41:20 +0000 (16:41 +0000)
ChangeLog
kernel/dev.c
kernel/dir.c
kernel/file.c
kernel/fuse_i.h
kernel/fuse_kernel.h
lib/fuse_lowlevel.c

index 9dc49f717cbb9d6e75906368aa89fb9adaaf8588..2da637b2a76478ebd4deae566da6bc1f5ee9005c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,10 @@
        * fusermount: check if /mnt/mtab is a symlink and don't modify it
        in that case
 
+       * kernel: simplify request size limiting. INIT only contains
+       maximum write size, maximum path component size remains fixed at
+       1024 bytes, and maximum xattr size depends on read buffer.
+
 2005-12-14  Miklos Szeredi <miklos@szeredi.hu>
 
        * Fix readdir() failure on x86_64, of 32bit programs compiled
index 4f1e8c088d72277492a1e99239e65fdebc55c84c..f7ccb7bc445c2d20e3a9c35fe07a1e4708ee5e4b 100644 (file)
@@ -197,18 +197,7 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
                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;
-               }
+               fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
        }
 
        /* After INIT reply is received other requests can go
@@ -691,6 +680,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        struct fuse_copy_state cs;
        unsigned reqsize;
 
+ restart:
        spin_lock(&fuse_lock);
        fc = file->private_data;
        err = -EPERM;
@@ -706,20 +696,25 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
 
        req = list_entry(fc->pending.next, struct fuse_req, list);
        list_del_init(&req->list);
-       spin_unlock(&fuse_lock);
 
        in = &req->in;
-       reqsize = req->in.h.len;
-       fuse_copy_init(&cs, 1, req, iov, nr_segs);
-       err = -EINVAL;
-       if (iov_length(iov, nr_segs) >= reqsize) {
-               err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
-               if (!err)
-                       err = fuse_copy_args(&cs, in->numargs, in->argpages,
-                                            (struct fuse_arg *) in->args, 0);
+       reqsize = in->h.len;
+       /* If request is too large, reply with an error and restart the read */
+       if (iov_length(iov, nr_segs) < reqsize) {
+               req->out.h.error = -EIO;
+               /* SETXATTR is special, since it may contain too large data */
+               if (in->h.opcode == FUSE_SETXATTR)
+                       req->out.h.error = -E2BIG;
+               request_end(fc, req);
+               goto restart;
        }
+       spin_unlock(&fuse_lock);
+       fuse_copy_init(&cs, 1, req, iov, nr_segs);
+       err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
+       if (!err)
+               err = fuse_copy_args(&cs, in->numargs, in->argpages,
+                                    (struct fuse_arg *) in->args, 0);
        fuse_copy_finish(&cs);
-
        spin_lock(&fuse_lock);
        req->locked = 0;
        if (!err && req->interrupted)
index b1bf3aa8042ee7d138acb801655d5a43e76be7e6..fa4857f3cba7aa9b0ff20d4111c086090c086794 100644 (file)
@@ -202,13 +202,13 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
        int err;
        struct fuse_entry_out outarg;
        struct inode *inode = NULL;
-       struct fuse_conn *fc = get_fuse_conn(dir);
-       struct fuse_req *req;
 #if !defined(FUSE_MAINLINE) && defined(KERNEL_2_6)
        struct dentry *newent;
 #endif
+       struct fuse_conn *fc = get_fuse_conn(dir);
+       struct fuse_req *req;
 
-       if (entry->d_name.len > fc->name_max)
+       if (entry->d_name.len > FUSE_NAME_MAX)
                return ERR_PTR(-ENAMETOOLONG);
 
        req = fuse_get_request(fc);
@@ -280,10 +280,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
        if (fc->no_create)
                goto out;
 
-       err = -ENAMETOOLONG;
-       if (entry->d_name.len > fc->name_max)
-               goto out;
-
        err = -EINTR;
        req = fuse_get_request(fc);
        if (!req)
@@ -466,12 +462,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
 {
        struct fuse_conn *fc = get_fuse_conn(dir);
        unsigned len = strlen(link) + 1;
-       struct fuse_req *req;
-
-       if (len > fc->symlink_max)
-               return -ENAMETOOLONG;
-
-       req = fuse_get_request(fc);
+       struct fuse_req *req = fuse_get_request(fc);
        if (!req)
                return -EINTR;
 
@@ -812,13 +803,11 @@ 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 > fc->name_max)
+               if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
                        return -EIO;
                if (reclen > nbytes)
                        break;
@@ -1146,9 +1135,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
        struct fuse_setxattr_in inarg;
        int err;
 
-       if (size > fc->xattr_size_max)
-               return -E2BIG;
-
        if (fc->no_setxattr)
                return -EOPNOTSUPP;
 
index 0a625a23602c2616cb7fc997693f1f5dcdb6538a..2d34e01bcecf8a8cf2331f72f83897b032aabdf6 100644 (file)
@@ -538,9 +538,6 @@ static int fuse_commit_write(struct file *file, struct page *page,
        if (is_bad_inode(inode))
                return -EIO;
 
-       if (count > fc->max_write)
-               return -EIO;
-
        req = fuse_get_request(fc);
        if (!req)
                return -EINTR;
@@ -592,7 +589,7 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf,
 
        nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT);
        npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       npages = min(npages, FUSE_MAX_PAGES_PER_REQ);
+       npages = min(max(npages, 1), FUSE_MAX_PAGES_PER_REQ);
        down_read(&current->mm->mmap_sem);
        npages = get_user_pages(current, current->mm, user_addr, npages, write,
                                0, req->pages, NULL);
@@ -623,7 +620,6 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                return -EINTR;
 
        while (count) {
-               size_t tmp;
                size_t nres;
                size_t nbytes = min(count, nmax);
                int err = fuse_get_user_pages(req, buf, nbytes, !write);
@@ -631,8 +627,8 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf,
                        res = err;
                        break;
                }
-               tmp = (req->num_pages << PAGE_SHIFT) - req->page_offset;
-               nbytes = min(nbytes, tmp);
+               nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset;
+               nbytes = min(count, nbytes);
                if (write)
                        nres = fuse_send_write(req, file, inode, pos, nbytes);
                else
index 7f26b652bd0b5a89c872674da5f6e05e1ed5854f..4bf9a32a9394a8586d51c85e9e47d9d80849504e 100644 (file)
@@ -93,6 +93,9 @@ static inline void set_page_dirty_lock(struct page *page)
 /** If more requests are outstanding, then the operation will block */
 #define FUSE_MAX_OUTSTANDING 10
 
+/** It could be as large as PATH_MAX, but would that have any uses? */
+#define FUSE_NAME_MAX 1024
+
 /** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
     module will check permissions based on the file mode.  Otherwise no
     permission checking is done in the kernel */
@@ -185,9 +188,6 @@ struct fuse_out {
        struct fuse_arg args[3];
 };
 
-struct fuse_req;
-struct fuse_conn;
-
 /**
  * A request to the client
  */
@@ -285,15 +285,6 @@ 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 26fff1306ec2d809b80d85388f62dd2088824647..f494aff1628b35f405bcbf644cc9b69995ec693f 100644 (file)
@@ -143,6 +143,9 @@ enum fuse_opcode {
        FUSE_CREATE        = 35
 };
 
+/* The read buffer is required to be at least 8k, but may be much larger */
+#define FUSE_MIN_READ_BUFFER 8192
+
 struct fuse_entry_out {
        __u64   nodeid;         /* Inode ID */
        __u64   generation;     /* Inode generation: nodeid:gen must
@@ -284,9 +287,7 @@ struct fuse_init_in {
 struct fuse_init_out {
        __u32   major;
        __u32   minor;
-       __u32   name_max;
-       __u32   symlink_max;
-       __u32   xattr_size_max;
+       __u32   unused[3];
        __u32   max_write;
 };
 
index 176b718f5a60dac9c2f7bdad5b1f0458c8fa3f98..3e45a59dea8428d7c546d7cf61fa64285d8020e1 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 {
     int debug;
     int allow_root;
@@ -713,30 +702,15 @@ static void do_init(fuse_req_t req, struct fuse_init_in *arg)
     f->major = FUSE_KERNEL_VERSION;
     f->minor = arg->minor;
 
-    if (bufsize < MIN_BUFFER_SIZE) {
+    if (bufsize < FUSE_MIN_READ_BUFFER) {
         fprintf(stderr, "fuse: warning: buffer size too small: %i\n", bufsize);
-        bufsize = MIN_BUFFER_SIZE;
+        bufsize = FUSE_MIN_READ_BUFFER;
     }
 
-    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;
+    outarg.max_write = bufsize - 4096;
 
     if (f->debug) {
         printf("   INIT: %u.%u\n", outarg.major, outarg.minor);