aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--kernel/dev.c39
-rw-r--r--kernel/dir.c24
-rw-r--r--kernel/file.c10
-rw-r--r--kernel/fuse_i.h15
-rw-r--r--kernel/fuse_kernel.h7
-rw-r--r--lib/fuse_lowlevel.c32
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 <miklos@szeredi.hu>
* 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(&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
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);