[PATCH] fuse: clean up request size limit checking
Change the way a too large request is handled. Until now in this case the device read returned -EINVAL and the operation returned -EIO. Make it more flexibible by not returning -EINVAL from the read, but restarting it instead. Also remove the fixed limit on setxattr data and let the filesystem provide as large a read buffer as it needs to handle the extended attribute data. The symbolic link length is already checked by VFS to be less than PATH_MAX, so the extra check against FUSE_SYMLINK_MAX is not needed. The check in fuse_create_open() against FUSE_NAME_MAX is not needed, since the dentry has already been looked up, and hence the name already checked. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
248d86e87d
commit
1d3d752b47
5 changed files with 26 additions and 33 deletions
|
@ -617,6 +617,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
|
||||||
struct fuse_copy_state cs;
|
struct fuse_copy_state cs;
|
||||||
unsigned reqsize;
|
unsigned reqsize;
|
||||||
|
|
||||||
|
restart:
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
fc = file->private_data;
|
fc = file->private_data;
|
||||||
err = -EPERM;
|
err = -EPERM;
|
||||||
|
@ -632,20 +633,25 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
|
||||||
|
|
||||||
req = list_entry(fc->pending.next, struct fuse_req, list);
|
req = list_entry(fc->pending.next, struct fuse_req, list);
|
||||||
list_del_init(&req->list);
|
list_del_init(&req->list);
|
||||||
spin_unlock(&fuse_lock);
|
|
||||||
|
|
||||||
in = &req->in;
|
in = &req->in;
|
||||||
reqsize = req->in.h.len;
|
reqsize = in->h.len;
|
||||||
fuse_copy_init(&cs, 1, req, iov, nr_segs);
|
/* If request is too large, reply with an error and restart the read */
|
||||||
err = -EINVAL;
|
if (iov_length(iov, nr_segs) < reqsize) {
|
||||||
if (iov_length(iov, nr_segs) >= reqsize) {
|
req->out.h.error = -EIO;
|
||||||
err = fuse_copy_one(&cs, &in->h, sizeof(in->h));
|
/* SETXATTR is special, since it may contain too large data */
|
||||||
if (!err)
|
if (in->h.opcode == FUSE_SETXATTR)
|
||||||
err = fuse_copy_args(&cs, in->numargs, in->argpages,
|
req->out.h.error = -E2BIG;
|
||||||
(struct fuse_arg *) in->args, 0);
|
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);
|
fuse_copy_finish(&cs);
|
||||||
|
|
||||||
spin_lock(&fuse_lock);
|
spin_lock(&fuse_lock);
|
||||||
req->locked = 0;
|
req->locked = 0;
|
||||||
if (!err && req->interrupted)
|
if (!err && req->interrupted)
|
||||||
|
|
|
@ -236,10 +236,6 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, int mode,
|
||||||
if (fc->no_create)
|
if (fc->no_create)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
err = -ENAMETOOLONG;
|
|
||||||
if (entry->d_name.len > FUSE_NAME_MAX)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
err = -EINTR;
|
err = -EINTR;
|
||||||
req = fuse_get_request(fc);
|
req = fuse_get_request(fc);
|
||||||
if (!req)
|
if (!req)
|
||||||
|
@ -413,12 +409,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry,
|
||||||
{
|
{
|
||||||
struct fuse_conn *fc = get_fuse_conn(dir);
|
struct fuse_conn *fc = get_fuse_conn(dir);
|
||||||
unsigned len = strlen(link) + 1;
|
unsigned len = strlen(link) + 1;
|
||||||
struct fuse_req *req;
|
struct fuse_req *req = fuse_get_request(fc);
|
||||||
|
|
||||||
if (len > FUSE_SYMLINK_MAX)
|
|
||||||
return -ENAMETOOLONG;
|
|
||||||
|
|
||||||
req = fuse_get_request(fc);
|
|
||||||
if (!req)
|
if (!req)
|
||||||
return -EINTR;
|
return -EINTR;
|
||||||
|
|
||||||
|
@ -988,9 +979,6 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
|
||||||
struct fuse_setxattr_in inarg;
|
struct fuse_setxattr_in inarg;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (size > FUSE_XATTR_SIZE_MAX)
|
|
||||||
return -E2BIG;
|
|
||||||
|
|
||||||
if (fc->no_setxattr)
|
if (fc->no_setxattr)
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@
|
||||||
/** If more requests are outstanding, then the operation will block */
|
/** If more requests are outstanding, then the operation will block */
|
||||||
#define FUSE_MAX_OUTSTANDING 10
|
#define FUSE_MAX_OUTSTANDING 10
|
||||||
|
|
||||||
|
/** Maximum size of data in a write request */
|
||||||
|
#define FUSE_MAX_WRITE 4096
|
||||||
|
|
||||||
|
/** 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
|
/** If the FUSE_DEFAULT_PERMISSIONS flag is given, the filesystem
|
||||||
module will check permissions based on the file mode. Otherwise no
|
module will check permissions based on the file mode. Otherwise no
|
||||||
permission checking is done in the kernel */
|
permission checking is done in the kernel */
|
||||||
|
@ -108,9 +114,6 @@ struct fuse_out {
|
||||||
struct fuse_arg args[3];
|
struct fuse_arg args[3];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_req;
|
|
||||||
struct fuse_conn;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A request to the client
|
* A request to the client
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -485,7 +485,7 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
|
||||||
fc->max_read = d.max_read;
|
fc->max_read = d.max_read;
|
||||||
if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
|
if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
|
||||||
fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
|
fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
|
||||||
fc->max_write = FUSE_MAX_IN / 2;
|
fc->max_write = FUSE_MAX_WRITE;
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
root = get_root_inode(sb, d.rootmode);
|
root = get_root_inode(sb, d.rootmode);
|
||||||
|
|
|
@ -108,12 +108,8 @@ enum fuse_opcode {
|
||||||
FUSE_CREATE = 35
|
FUSE_CREATE = 35
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Conservative buffer size for the client */
|
/* The read buffer is required to be at least 8k, but may be much larger */
|
||||||
#define FUSE_MAX_IN 8192
|
#define FUSE_MIN_READ_BUFFER 8192
|
||||||
|
|
||||||
#define FUSE_NAME_MAX 1024
|
|
||||||
#define FUSE_SYMLINK_MAX 4096
|
|
||||||
#define FUSE_XATTR_SIZE_MAX 4096
|
|
||||||
|
|
||||||
struct fuse_entry_out {
|
struct fuse_entry_out {
|
||||||
__u64 nodeid; /* Inode ID */
|
__u64 nodeid; /* Inode ID */
|
||||||
|
|
Loading…
Reference in a new issue