From 154ffe2c27e84a29eafedc5327f663a313f2721a Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 15 Dec 2005 16:41:20 +0000 Subject: [PATCH] fix --- ChangeLog | 4 ++++ kernel/dev.c | 39 +++++++++++++++++---------------------- kernel/dir.c | 24 +++++------------------- kernel/file.c | 10 +++------- kernel/fuse_i.h | 15 +++------------ kernel/fuse_kernel.h | 7 ++++--- lib/fuse_lowlevel.c | 32 +++----------------------------- 7 files changed, 39 insertions(+), 92 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9dc49f7..2da637b 100644 --- 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 * Fix readdir() failure on x86_64, of 32bit programs compiled diff --git a/kernel/dev.c b/kernel/dev.c index 4f1e8c0..f7ccb7b 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -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) diff --git a/kernel/dir.c b/kernel/dir.c index b1bf3aa..fa4857f 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -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; diff --git a/kernel/file.c b/kernel/file.c index 0a625a2..2d34e01 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -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(¤t->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 diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 7f26b65..4bf9a32 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -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; diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index 26fff13..f494aff 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -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; }; diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 176b718..3e45a59 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -21,17 +21,6 @@ #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); -- 2.30.2