From 039322d8f1295bf20057303882bf1202a03d125e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 1 Dec 2004 18:39:12 +0000 Subject: [PATCH] cleanup --- ChangeLog | 6 + kernel/cleanup.sh | 2 +- kernel/configure.ac | 11 +- kernel/dev.c | 179 +++++++++-------- kernel/dir.c | 123 +++++------- kernel/file.c | 454 +++++++++++++++++--------------------------- kernel/fuse_i.h | 59 ++++-- kernel/inode.c | 28 ++- 8 files changed, 399 insertions(+), 463 deletions(-) diff --git a/ChangeLog b/ChangeLog index 441669b..63f8b58 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2004-12-01 Miklos Szeredi + + * kernel: clean up writing functions + + * kernel: no allocation on write in direct_io mode + 2004-11-30 Miklos Szeredi * kernel: clean up reading functions diff --git a/kernel/cleanup.sh b/kernel/cleanup.sh index 6255cb2..fb0807e 100755 --- a/kernel/cleanup.sh +++ b/kernel/cleanup.sh @@ -12,5 +12,5 @@ if test "$destdir" = "."; then fi for f in dev.c dir.c file.c inode.c util.c fuse_i.h; do - unifdef -DKERNEL_2_6 -DKERNEL_2_6_6_PLUS -DKERNEL_2_6_10_PLUS -DHAVE_KERNEL_XATTR -DFS_SAFE -DMAX_LFS_FILESIZE -DFUSE_MAINLINE -DBUG_ON -D__user -DMODULE_LICENSE $f > $destdir/$f + unifdef -DKERNEL_2_6 -DKERNEL_2_6_6_PLUS -DKERNEL_2_6_10_PLUS -DHAVE_KERNEL_XATTR -DFS_SAFE -DMAX_LFS_FILESIZE -DFUSE_MAINLINE -DBUG_ON -DHAVE_FS_SUBSYS -D__user -DMODULE_LICENSE $f > $destdir/$f done diff --git a/kernel/configure.ac b/kernel/configure.ac index 3b06f46..f078427 100644 --- a/kernel/configure.ac +++ b/kernel/configure.ac @@ -49,9 +49,18 @@ if echo "$kernsrcver" | grep -q "^2.4"; then [#include ]) CFLAGS="$old_cflags" fi + +AC_MSG_CHECKING([whether fs_subsys is declared]) +if grep -q fs_subsys $kernelsrc/include/linux/fs.h; then + AC_DEFINE(HAVE_FS_SUBSYS, 1, [Kernel defines fs_subsys]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + AC_MSG_CHECKING([if kernel has extended attribute support]) if test -f $kernelsrc/include/linux/xattr.h; then - AC_DEFINE(HAVE_KERNEL_XATTR, 1, [Kernel has xattr support],,) + AC_DEFINE(HAVE_KERNEL_XATTR, 1, [Kernel has xattr support]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) diff --git a/kernel/dev.c b/kernel/dev.c index aca602f..70a0013 100644 --- a/kernel/dev.c +++ b/kernel/dev.c @@ -205,39 +205,74 @@ static void request_wait(struct fuse_conn *fc) remove_wait_queue(&fc->waitq, &wait); } -static inline int copy_in_one(const void *src, size_t srclen, - char __user **dstp, size_t *dstlenp) +static inline int copy_in_page(char __user *buf, struct page *page, + unsigned offset, unsigned count) { - if (*dstlenp < srclen) { + char *tmpbuf = kmap(page); + int err = copy_to_user(buf, tmpbuf + offset, count); + kunmap(page); + return err; +} + +static int copy_in_pages(struct fuse_req *req, size_t nbytes, char __user *buf) +{ + unsigned i; + unsigned offset = req->page_offset; + unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); + for (i = 0; i < req->num_pages && nbytes; i++) { + struct page *page = req->pages[i]; + if (page && copy_in_page(buf, page, offset, count)) + return -EFAULT; + nbytes -= count; + buf += count; + count = min(nbytes, (unsigned) PAGE_SIZE); + offset = 0; + } + return 0; +} + +static inline int copy_in_one(struct fuse_req *req, size_t argsize, + const void *val, int islast, char __user *buf, + size_t nbytes) +{ + if (nbytes < argsize) { printk("fuse_dev_read: buffer too small\n"); return -EINVAL; } - - if (srclen && copy_to_user(*dstp, src, srclen)) + if (islast && req->in.argpages) + return copy_in_pages(req, argsize, buf); + else if (argsize && copy_to_user(buf, val, argsize)) return -EFAULT; - - *dstp += srclen; - *dstlenp -= srclen; - - return 0; + else + return 0; } -static inline int copy_in_args(struct fuse_in *in, char __user *buf, - size_t nbytes) +static int copy_in_args(struct fuse_req *req, char __user *buf, size_t nbytes) { - int err; int i; + int err; + struct fuse_in *in = &req->in; size_t orignbytes = nbytes; - - err = copy_in_one(&in->h, sizeof(in->h), &buf, &nbytes); + unsigned argsize; + + argsize = sizeof(in->h); + err = copy_in_one(req, argsize, &in->h, 0, buf, nbytes); if (err) return err; + buf += argsize; + nbytes -= argsize; + for (i = 0; i < in->numargs; i++) { struct fuse_in_arg *arg = &in->args[i]; - err = copy_in_one(arg->value, arg->size, &buf, &nbytes); + int islast = (i == in->numargs - 1); + err = copy_in_one(req, arg->size, arg->value, islast, buf, + nbytes); if (err) return err; + + buf += arg->size; + nbytes -= arg->size; } return orignbytes - nbytes; @@ -269,7 +304,7 @@ static ssize_t fuse_dev_read(struct file *file, char __user *buf, if (req == NULL) return -EINTR; - ret = copy_in_args(&req->in, buf, nbytes); + ret = copy_in_args(req, buf, nbytes); spin_lock(&fuse_lock); if (req->isreply) { if (ret < 0) { @@ -312,91 +347,78 @@ static void process_getdir(struct fuse_req *req) arg->file = fget(arg->fd); } -static int copy_out_pages(struct fuse_req *req, const char __user *buf, - size_t nbytes) +static inline int copy_out_page(const char __user *buf, struct page *page, + unsigned offset, unsigned count, int zeroing) +{ + int err = 0; + char *tmpbuf = kmap(page); + if (count < PAGE_SIZE && zeroing) + memset(tmpbuf, 0, PAGE_SIZE); + if (count) + err = copy_from_user(tmpbuf + offset, buf, count); + flush_dcache_page(page); + kunmap(page); + return err; +} + +static int copy_out_pages(struct fuse_req *req, size_t nbytes, + const char __user *buf) { - unsigned count; - unsigned page_offset; - unsigned zeroing = req->out.page_zeroing; unsigned i; - - req->out.args[0].size = nbytes; - page_offset = req->page_offset; - count = min(nbytes, (unsigned) PAGE_CACHE_SIZE - page_offset); + unsigned offset = req->page_offset; + unsigned zeroing = req->out.page_zeroing; + unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset); + for (i = 0; i < req->num_pages && (zeroing || nbytes); i++) { struct page *page = req->pages[i]; - char *tmpbuf = kmap(page); - int err = 0; - if (count < PAGE_CACHE_SIZE && zeroing) - memset(tmpbuf, 0, PAGE_CACHE_SIZE); - if (count) - err = copy_from_user(tmpbuf + page_offset, buf, count); - flush_dcache_page(page); - kunmap(page); - if (err) - return -EFAULT; + if (page && copy_out_page(buf, page, offset, count, zeroing)) + return -EFAULT; nbytes -= count; buf += count; - count = min(nbytes, (unsigned) PAGE_CACHE_SIZE); - page_offset = 0; + count = min(nbytes, (unsigned) PAGE_SIZE); + offset = 0; } return 0; } -static inline int copy_out_one(struct fuse_out_arg *arg, - const char __user **srcp, - size_t *srclenp, int allowvar) +static inline int copy_out_one(struct fuse_req *req, struct fuse_out_arg *arg, + int islast, const char __user *buf, size_t nbytes) { - size_t dstlen = arg->size; - if (*srclenp < dstlen) { - if (!allowvar) { + if (nbytes < arg->size) { + if (!islast || !req->out.argvar) { printk("fuse_dev_write: write is short\n"); return -EINVAL; } - dstlen = *srclenp; + arg->size = nbytes; } - - if (dstlen && copy_from_user(arg->value, *srcp, dstlen)) + if (islast && req->out.argpages) + return copy_out_pages(req, arg->size, buf); + else if (arg->size && copy_from_user(arg->value, buf, arg->size)) return -EFAULT; - - *srcp += dstlen; - *srclenp -= dstlen; - arg->size = dstlen; - - return 0; + else + return 0; } -static inline int copy_out_args(struct fuse_req *req, const char __user *buf, - size_t nbytes) +static int copy_out_args(struct fuse_req *req, const char __user *buf, + size_t nbytes) { struct fuse_out *out = &req->out; - int err; int i; buf += sizeof(struct fuse_out_header); nbytes -= sizeof(struct fuse_out_header); if (!out->h.error) { - if (out->argpages) { - if (nbytes <= out->args[0].size) - return copy_out_pages(req, buf, nbytes); - } else { - for (i = 0; i < out->numargs; i++) { - struct fuse_out_arg *arg = &out->args[i]; - int allowvar; - - if (out->argvar && i == out->numargs - 1) - allowvar = 1; - else - allowvar = 0; - - err = copy_out_one(arg, &buf, &nbytes, allowvar); - if (err) - return err; - } + for (i = 0; i < out->numargs; i++) { + struct fuse_out_arg *arg = &out->args[i]; + int islast = (i == out->numargs - 1); + int err = copy_out_one(req, arg, islast, buf, nbytes); + if (err) + return err; + buf += arg->size; + nbytes -= arg->size; } } - if (nbytes != 0) { printk("fuse_dev_write: write is long\n"); return -EINVAL; @@ -412,7 +434,6 @@ static inline int copy_out_header(struct fuse_out_header *oh, printk("fuse_dev_write: write is short\n"); return -EINVAL; } - if (copy_from_user(oh, buf, sizeof(struct fuse_out_header))) return -EFAULT; @@ -591,7 +612,7 @@ struct file_operations fuse_dev_operations = { }; #ifdef KERNEL_2_6 -#ifndef FUSE_MAINLINE +#ifndef HAVE_FS_SUBSYS static decl_subsys(fs, NULL, NULL); #endif static decl_subsys(fuse, NULL, NULL); @@ -607,7 +628,7 @@ static int __init fuse_version_init(void) { int err; -#ifndef FUSE_MAINLINE +#ifndef HAVE_FS_SUBSYS subsystem_register(&fs_subsys); #endif kset_set_kset_s(&fuse_subsys, fs_subsys); @@ -617,7 +638,7 @@ static int __init fuse_version_init(void) err = subsys_create_file(&fuse_subsys, &fuse_attr_version); if (err) { subsystem_unregister(&fuse_subsys); -#ifndef FUSE_MAINLINE +#ifndef HAVE_FS_SUBSYS subsystem_unregister(&fs_subsys); #endif return err; @@ -629,7 +650,7 @@ static void fuse_version_clean(void) { subsys_remove_file(&fuse_subsys, &fuse_attr_version); subsystem_unregister(&fuse_subsys); -#ifndef FUSE_MAINLINE +#ifndef HAVE_FS_SUBSYS subsystem_unregister(&fs_subsys); #endif } diff --git a/kernel/dir.c b/kernel/dir.c index 412ffc8..a56f513 100644 --- a/kernel/dir.c +++ b/kernel/dir.c @@ -92,8 +92,7 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) static int fuse_inode_eq(struct inode *inode, void *_nodeidp) { unsigned long nodeid = *(unsigned long *) _nodeidp; - struct fuse_inode *fi = INO_FI(inode); - if (fi->nodeid == nodeid) + if (get_node_id(inode) == nodeid) return 1; else return 0; @@ -102,8 +101,7 @@ static int fuse_inode_eq(struct inode *inode, void *_nodeidp) static int fuse_inode_set(struct inode *inode, void *_nodeidp) { unsigned long nodeid = *(unsigned long *) _nodeidp; - struct fuse_inode *fi = INO_FI(inode); - fi->nodeid = nodeid; + get_fuse_inode(inode)->nodeid = nodeid; return 0; } @@ -111,7 +109,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, int generation, struct fuse_attr *attr, int version) { struct inode *inode; - struct fuse_conn *fc = SB_FC(sb); + struct fuse_conn *fc = get_fuse_conn_super(sb); inode = iget5_locked(sb, nodeid, fuse_inode_eq, fuse_inode_set, &nodeid); if (!inode) @@ -137,8 +135,7 @@ struct inode *fuse_ilookup(struct super_block *sb, unsigned long nodeid) #else static int fuse_inode_eq(struct inode *inode, unsigned long ino, void *_nodeidp){ unsigned long nodeid = *(unsigned long *) _nodeidp; - struct fuse_inode *fi = INO_FI(inode); - if (inode->u.generic_ip && fi->nodeid == nodeid) + if (inode->u.generic_ip && get_node_id(inode) == nodeid) return 1; else return 0; @@ -154,8 +151,7 @@ struct inode *fuse_iget(struct super_block *sb, unsigned long nodeid, return NULL; if (!inode->u.generic_ip) { - struct fuse_inode *fi = INO_FI(inode); - fi->nodeid = nodeid; + get_fuse_inode(inode)->nodeid = nodeid; inode->u.generic_ip = inode; inode->i_generation = generation; fuse_init_inode(inode, attr); @@ -182,9 +178,8 @@ static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req, struct inode *dir, struct dentry *entry, struct fuse_entry_out *outarg, int *version) { - struct fuse_inode *fi = INO_FI(dir); req->in.h.opcode = FUSE_LOOKUP; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -199,7 +194,7 @@ static int fuse_send_lookup(struct fuse_conn *fc, struct fuse_req *req, static int fuse_do_lookup(struct inode *dir, struct dentry *entry, struct fuse_entry_out *outarg, int *version) { - struct fuse_conn *fc = INO_FC(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req; int err; @@ -227,7 +222,7 @@ static inline unsigned long time_to_jiffies(unsigned long sec, static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, struct inode **inodep) { - struct fuse_conn *fc = INO_FC(dir); + struct fuse_conn *fc = get_fuse_conn(dir); int err; struct fuse_entry_out outarg; int version; @@ -254,7 +249,7 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, return err; if (inode) { - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); entry->d_time = time_to_jiffies(outarg.entry_valid, outarg.entry_valid_nsec); fi->i_time = time_to_jiffies(outarg.attr_valid, @@ -268,8 +263,7 @@ static int fuse_lookup_iget(struct inode *dir, struct dentry *entry, static void fuse_invalidate_attr(struct inode *inode) { - struct fuse_inode *fi = INO_FI(inode); - fi->i_time = jiffies - 1; + get_fuse_inode(inode)->i_time = jiffies - 1; } static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req, @@ -297,7 +291,7 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req, entry->d_time = time_to_jiffies(outarg->entry_valid, outarg->entry_valid_nsec); - fi = INO_FI(inode); + fi = get_fuse_inode(inode); fi->i_time = time_to_jiffies(outarg->attr_valid, outarg->attr_valid_nsec); @@ -309,8 +303,7 @@ static int lookup_new_entry(struct fuse_conn *fc, struct fuse_req *req, static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, dev_t rdev) { - struct fuse_conn *fc = INO_FC(dir); - struct fuse_inode *fi = INO_FI(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req = fuse_get_request(fc); struct fuse_mknod_in inarg; struct fuse_entry_out outarg; @@ -323,7 +316,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, int mode, inarg.mode = mode; inarg.rdev = new_encode_dev(rdev); req->in.h.opcode = FUSE_MKNOD; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -350,8 +343,7 @@ static int fuse_create(struct inode *dir, struct dentry *entry, int mode, static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) { - struct fuse_conn *fc = INO_FC(dir); - struct fuse_inode *fi = INO_FI(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req = fuse_get_request(fc); struct fuse_mkdir_in inarg; struct fuse_entry_out outarg; @@ -363,7 +355,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) memset(&inarg, 0, sizeof(inarg)); inarg.mode = mode; req->in.h.opcode = FUSE_MKDIR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -385,8 +377,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, int mode) static int fuse_symlink(struct inode *dir, struct dentry *entry, const char *link) { - struct fuse_conn *fc = INO_FC(dir); - struct fuse_inode *fi = INO_FI(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req; struct fuse_entry_out outarg; unsigned len = strlen(link) + 1; @@ -400,7 +391,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, return -ERESTARTSYS; req->in.h.opcode = FUSE_SYMLINK; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 2; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -421,8 +412,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, static int fuse_unlink(struct inode *dir, struct dentry *entry) { - struct fuse_conn *fc = INO_FC(dir); - struct fuse_inode *fi = INO_FI(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req = fuse_get_request(fc); int err; @@ -430,7 +420,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) return -ERESTARTSYS; req->in.h.opcode = FUSE_UNLINK; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -452,8 +442,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) static int fuse_rmdir(struct inode *dir, struct dentry *entry) { - struct fuse_conn *fc = INO_FC(dir); - struct fuse_inode *fi = INO_FI(dir); + struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_req *req = fuse_get_request(fc); int err; @@ -461,7 +450,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) return -ERESTARTSYS; req->in.h.opcode = FUSE_RMDIR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(dir); req->in.numargs = 1; req->in.args[0].size = entry->d_name.len + 1; req->in.args[0].value = entry->d_name.name; @@ -478,9 +467,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) static int fuse_rename(struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent) { - struct fuse_conn *fc = INO_FC(olddir); - struct fuse_inode *oldfi = INO_FI(olddir); - struct fuse_inode *newfi = INO_FI(newdir); + struct fuse_conn *fc = get_fuse_conn(olddir); struct fuse_req *req = fuse_get_request(fc); struct fuse_rename_in inarg; int err; @@ -489,9 +476,9 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, return -ERESTARTSYS; memset(&inarg, 0, sizeof(inarg)); - inarg.newdir = newfi->nodeid; + inarg.newdir = get_node_id(newdir); req->in.h.opcode = FUSE_RENAME; - req->in.h.nodeid = oldfi->nodeid; + req->in.h.nodeid = get_node_id(olddir); req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -514,9 +501,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, struct dentry *newent) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); - struct fuse_inode *newfi = INO_FI(newdir); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = fuse_get_request(fc); struct fuse_link_in inarg; struct fuse_entry_out outarg; @@ -526,9 +511,9 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, return -ERESTARTSYS; memset(&inarg, 0, sizeof(inarg)); - inarg.newdir = newfi->nodeid; + inarg.newdir = get_node_id(newdir); req->in.h.opcode = FUSE_LINK; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -551,8 +536,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, int fuse_do_getattr(struct inode *inode) { - struct fuse_inode *fi = INO_FI(inode); - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = fuse_get_request(fc); struct fuse_attr_out arg; int err; @@ -561,13 +545,14 @@ int fuse_do_getattr(struct inode *inode) return -ERESTARTSYS; req->in.h.opcode = FUSE_GETATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->out.numargs = 1; req->out.args[0].size = sizeof(arg); req->out.args[0].value = &arg; request_send(fc, req); err = req->out.h.error; if (!err) { + struct fuse_inode *fi = get_fuse_inode(inode); change_attributes(inode, &arg.attr); fi->i_time = time_to_jiffies(arg.attr_valid, arg.attr_valid_nsec); @@ -579,10 +564,10 @@ int fuse_do_getattr(struct inode *inode) static int fuse_revalidate(struct dentry *entry) { struct inode *inode = entry->d_inode; - struct fuse_inode *fi = INO_FI(inode); - struct fuse_conn *fc = INO_FC(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_conn *fc = get_fuse_conn(inode); - if (fi->nodeid == FUSE_ROOT_ID) { + if (get_node_id(inode) == FUSE_ROOT_ID) { if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->uid && (!(fc->flags & FUSE_ALLOW_ROOT) || @@ -596,7 +581,7 @@ static int fuse_revalidate(struct dentry *entry) static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) { - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->uid && (!(fc->flags & FUSE_ALLOW_ROOT) || current->fsuid != 0)) @@ -692,8 +677,7 @@ static int fuse_checkdir(struct file *cfile, struct file *file) static int fuse_getdir(struct file *file) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = fuse_get_request(fc); struct fuse_getdir_out_i outarg; int err; @@ -702,7 +686,7 @@ static int fuse_getdir(struct file *file) return -ERESTARTSYS; req->in.h.opcode = FUSE_GETDIR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->out.numargs = 1; req->out.args[0].size = sizeof(struct fuse_getdir_out); req->out.args[0].value = &outarg; @@ -745,8 +729,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) static char *read_link(struct dentry *dentry) { struct inode *inode = dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = fuse_get_request(fc); char *link; @@ -759,7 +742,7 @@ static char *read_link(struct dentry *dentry) goto out; } req->in.h.opcode = FUSE_READLINK; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->out.argvar = 1; req->out.numargs = 1; req->out.args[0].size = PAGE_SIZE - 1; @@ -853,8 +836,8 @@ static unsigned iattr_to_fattr(struct iattr *iattr, struct fuse_attr *fattr) static int fuse_setattr(struct dentry *entry, struct iattr *attr) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_req *req; struct fuse_setattr_in inarg; struct fuse_attr_out outarg; @@ -891,7 +874,7 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) memset(&inarg, 0, sizeof(inarg)); inarg.valid = iattr_to_fattr(attr, &inarg.attr); req->in.h.opcode = FUSE_SETATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -925,7 +908,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) return 0; else if (entry->d_time && time_after(jiffies, entry->d_time)) { struct inode *inode = entry->d_inode; - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_entry_out outarg; int version; int ret; @@ -935,7 +918,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, struct nameidata *nd) if (ret) return 0; - if (outarg.nodeid != fi->nodeid) + if (outarg.nodeid != get_node_id(inode)) return 0; change_attributes(inode, &outarg.attr); @@ -1023,8 +1006,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name, #endif { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_setxattr_in inarg; int err; @@ -1043,7 +1025,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name, inarg.size = size; inarg.flags = flags; req->in.h.opcode = FUSE_SETXATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 3; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1065,8 +1047,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name, void *value, size_t size) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_getxattr_in inarg; struct fuse_getxattr_out outarg; @@ -1082,7 +1063,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name, memset(&inarg, 0, sizeof(inarg)); inarg.size = size; req->in.h.opcode = FUSE_GETXATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 2; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1115,8 +1096,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name, static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_getxattr_in inarg; struct fuse_getxattr_out outarg; @@ -1132,7 +1112,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) memset(&inarg, 0, sizeof(inarg)); inarg.size = size; req->in.h.opcode = FUSE_LISTXATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -1163,8 +1143,7 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) static int fuse_removexattr(struct dentry *entry, const char *name) { struct inode *inode = entry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; int err; @@ -1176,7 +1155,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name) return -ERESTARTSYS; req->in.h.opcode = FUSE_REMOVEXATTR; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = strlen(name) + 1; req->in.args[0].value = name; diff --git a/kernel/file.c b/kernel/file.c index 2d15c5b..da0fbb8 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -29,8 +29,7 @@ MODULE_PARM_DESC(user_mmap, "Allow non root user to create a shared writable map static int fuse_open(struct inode *inode, struct file *file) { - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req; struct fuse_open_in inarg; struct fuse_open_out outarg; @@ -43,7 +42,7 @@ static int fuse_open(struct inode *inode, struct file *file) /* If opening the root node, no lookup has been performed on it, so the attributes must be refreshed */ - if (fi->nodeid == FUSE_ROOT_ID) { + if (get_node_id(inode) == FUSE_ROOT_ID) { int err = fuse_do_getattr(inode); if (err) return err; @@ -69,7 +68,7 @@ static int fuse_open(struct inode *inode, struct file *file) memset(&inarg, 0, sizeof(inarg)); inarg.flags = file->f_flags & ~O_EXCL; req->in.h.opcode = FUSE_OPEN; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -117,8 +116,7 @@ void fuse_sync_inode(struct inode *inode) static int fuse_release(struct inode *inode, struct file *file) { - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; struct fuse_req *req = ff->release_req; struct fuse_release_in inarg; @@ -128,6 +126,7 @@ static int fuse_release(struct inode *inode, struct file *file) fuse_sync_inode(inode); if (!list_empty(&ff->ff_list)) { + struct fuse_inode *fi = get_fuse_inode(inode); down_write(&fi->write_sem); list_del(&ff->ff_list); up_write(&fi->write_sem); @@ -137,7 +136,7 @@ static int fuse_release(struct inode *inode, struct file *file) inarg.fh = ff->fh; inarg.flags = file->f_flags & ~O_EXCL; req->in.h.opcode = FUSE_RELEASE; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -153,8 +152,7 @@ static int fuse_release(struct inode *inode, struct file *file) static int fuse_flush(struct file *file) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; struct fuse_req *req = ff->release_req; struct fuse_flush_in inarg; @@ -167,7 +165,7 @@ static int fuse_flush(struct file *file) memset(&inarg, 0, sizeof(inarg)); inarg.fh = ff->fh; req->in.h.opcode = FUSE_FLUSH; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -185,8 +183,8 @@ static int fuse_flush(struct file *file) static int fuse_fsync(struct file *file, struct dentry *de, int datasync) { struct inode *inode = de->d_inode; - struct fuse_inode *fi = INO_FI(inode); - struct fuse_conn *fc = INO_FC(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; struct fuse_req *req; struct fuse_fsync_in inarg; @@ -208,7 +206,7 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync) inarg.fh = ff->fh; inarg.datasync = datasync; req->in.h.opcode = FUSE_FSYNC; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; @@ -225,7 +223,6 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync) static void fuse_read_init(struct fuse_req *req, struct file *file, struct inode *inode, loff_t pos, size_t count) { - struct fuse_inode *fi = INO_FI(inode); struct fuse_file *ff = file->private_data; struct fuse_read_in *inarg = &req->misc.read_in; @@ -233,11 +230,13 @@ static void fuse_read_init(struct fuse_req *req, struct file *file, inarg->offset = pos; inarg->size = count; req->in.h.opcode = FUSE_READ; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_read_in); req->in.args[0].value = inarg; + req->out.argpages = 1; req->out.argvar = 1; + req->out.page_zeroing = 1; req->out.numargs = 1; req->out.args[0].size = count; } @@ -245,14 +244,12 @@ static void fuse_read_init(struct fuse_req *req, struct file *file, static int fuse_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = fuse_get_request_nonint(fc); - loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT; + loff_t pos = (loff_t) page->index << PAGE_SHIFT; int err; - fuse_read_init(req, file, inode, pos, PAGE_CACHE_SIZE); - req->out.argpages = 1; - req->out.page_zeroing = 1; + fuse_read_init(req, file, inode, pos, PAGE_SIZE); req->num_pages = 1; req->pages[0] = page; request_send(fc, req); @@ -280,12 +277,10 @@ static void read_pages_end(struct fuse_conn *fc, struct fuse_req *req) static void fuse_send_readpages(struct fuse_req *req, struct file *file, struct inode *inode) { - struct fuse_conn *fc = INO_FC(inode); - loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT; - size_t count = req->num_pages << PAGE_CACHE_SHIFT; + struct fuse_conn *fc = get_fuse_conn(inode); + loff_t pos = (loff_t) req->pages[0]->index << PAGE_SHIFT; + size_t count = req->num_pages << PAGE_SHIFT; fuse_read_init(req, file, inode, pos, count); - req->out.argpages = 1; - req->out.page_zeroing = 1; request_send_async(fc, req, read_pages_end); } @@ -300,13 +295,13 @@ static int fuse_readpages_fill(void *_data, struct page *page) struct fuse_readpages_data *data = _data; struct fuse_req *req = data->req; struct inode *inode = data->inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); if (req->num_pages && (req->num_pages == FUSE_MAX_PAGES_PER_REQ || - (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || + (req->num_pages + 1) * PAGE_SIZE > fc->max_read || req->pages[req->num_pages - 1]->index + 1 != page->index)) { - struct fuse_conn *fc = INO_FC(page->mapping->host); + struct fuse_conn *fc = get_fuse_conn(page->mapping->host); fuse_send_readpages(req, data->file, inode); data->req = req = fuse_get_request_nonint(fc); } @@ -319,7 +314,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, struct list_head *pages, unsigned nr_pages) { struct inode *inode = mapping->host; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_readpages_data data; data.req = fuse_get_request_nonint(fc); @@ -338,7 +333,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, #define FUSE_BLOCK_SHIFT 16 #define FUSE_BLOCK_SIZE (1UL << FUSE_BLOCK_SHIFT) #define FUSE_BLOCK_MASK (~(FUSE_BLOCK_SIZE-1)) -#if (1UL << (FUSE_BLOCK_SHIFT - PAGE_CACHE_SHIFT)) > FUSE_MAX_PAGES_PER_REQ +#if (1UL << (FUSE_BLOCK_SHIFT - PAGE_SHIFT)) > FUSE_MAX_PAGES_PER_REQ #error FUSE_BLOCK_SHIFT too large #endif @@ -364,7 +359,7 @@ static void fuse_file_read_block(struct fuse_req *req, struct file *file, struct inode *inode, unsigned start, unsigned end) { - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); loff_t pos; size_t count; int index; @@ -375,29 +370,34 @@ static void fuse_file_read_block(struct fuse_req *req, struct file *file, struct page *page = grab_cache_page(inode->i_mapping, index); if (!page) goto out; + if (PageUptodate(page)) { + unlock_page(page); + page_cache_release(page); + page = NULL; + } req->pages[req->num_pages++] = page; } - pos = (loff_t) start << PAGE_CACHE_SHIFT; - count = req->num_pages << PAGE_CACHE_SHIFT; + pos = (loff_t) start << PAGE_SHIFT; + count = req->num_pages << PAGE_SHIFT; fuse_read_init(req, file, inode, pos, count); - req->out.argpages = 1; - req->out.page_zeroing = 1; request_send(fc, req); err = req->out.h.error; out: for (i = 0; i < req->num_pages; i++) { struct page *page = req->pages[i]; - if (!err) - SetPageUptodate(page); - unlock_page(page); - page_cache_release(page); + if (page) { + if (!err) + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } } } static int fuse_file_bigread(struct file *file, struct inode *inode, loff_t pos, size_t count) { - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); unsigned starti; unsigned endi; unsigned nexti; @@ -408,15 +408,15 @@ static int fuse_file_bigread(struct file *file, struct inode *inode, if (end <= pos) return 0; - starti = (pos & FUSE_BLOCK_MASK) >> PAGE_CACHE_SHIFT; - endi = (end + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + starti = (pos & FUSE_BLOCK_MASK) >> PAGE_SHIFT; + endi = (end + PAGE_SIZE - 1) >> PAGE_SHIFT; req = fuse_get_request(fc); if (!req) return -ERESTARTSYS; for (; starti < endi; starti = nexti) { - nexti = starti + (FUSE_BLOCK_SIZE >> PAGE_CACHE_SHIFT); + nexti = starti + (FUSE_BLOCK_SIZE >> PAGE_SHIFT); nexti = min(nexti, endi); if (!fuse_is_block_uptodate(inode, starti, nexti)) { fuse_file_read_block(req, file, inode, starti, nexti); @@ -428,35 +428,37 @@ static int fuse_file_bigread(struct file *file, struct inode *inode, } #endif /* KERNEL_2_6 */ -static void fuse_release_user_pages(struct fuse_req *req) +static void fuse_release_user_pages(struct fuse_req *req, int write) { unsigned i; for (i = 0; i < req->num_pages; i++) { - struct page *page = req->pages[i]; + struct page *page = req->pages[i]; + if (write) { #ifdef KERNEL_2_6 - set_page_dirty_lock(page); + set_page_dirty_lock(page); #else - lock_page(page); - set_page_dirty(page); - unlock_page(page); + lock_page(page); + set_page_dirty(page); + unlock_page(page); #endif + } page_cache_release(page); } } -static int fuse_get_user_pages(struct fuse_req *req, char __user *buf, - unsigned nbytes) +static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, + unsigned nbytes, int write) { unsigned long user_addr = (unsigned long) buf; - unsigned offset = user_addr & ~PAGE_CACHE_MASK; + unsigned offset = user_addr & ~PAGE_MASK; int npages; - nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_CACHE_SHIFT); - npages = (nbytes + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; + 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 = get_user_pages(current, current->mm, user_addr, npages, 1, 0, - req->pages, NULL); + npages = get_user_pages(current, current->mm, user_addr, npages, write, + 0, req->pages, NULL); if (npages < 0) return npages; @@ -469,7 +471,7 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); loff_t pos = *ppos; ssize_t res = 0; struct fuse_req *req = fuse_get_request(fc); @@ -477,20 +479,20 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, return -ERESTARTSYS; while (count) { - unsigned nbytes = min(count, fc->max_read); unsigned nread; unsigned ntmp; - int err = fuse_get_user_pages(req, buf, nbytes); + unsigned nbytes = min(count, fc->max_read); + int err = fuse_get_user_pages(req, buf, nbytes, 1); if (err) { res = err; break; } - ntmp = (req->num_pages << PAGE_CACHE_SHIFT) - req->page_offset; + ntmp = (req->num_pages << PAGE_SHIFT) - req->page_offset; nbytes = min(nbytes, ntmp); fuse_read_init(req, file, inode, pos, nbytes); - req->out.argpages = 1; + req->out.page_zeroing = 0; /* set to 1 by default */ request_send(fc, req); - fuse_release_user_pages(req); + fuse_release_user_pages(req, 1); if (req->out.h.error) { if (!res) res = req->out.h.error; @@ -503,7 +505,8 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, buf += nread; if (nread != nbytes) break; - fuse_reset_request(req); + if (count) + fuse_reset_request(req); } fuse_put_request(fc, req); if (res > 0) @@ -516,7 +519,7 @@ static ssize_t fuse_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); ssize_t res; if (fc->flags & FUSE_DIRECT_IO) @@ -536,72 +539,31 @@ static ssize_t fuse_file_read(struct file *file, char __user *buf, return res; } -static ssize_t fuse_send_write(struct fuse_req *req, struct fuse_file *ff, - struct inode *inode, const char *buf, - loff_t pos, size_t count) +static void fuse_write_init(struct fuse_req *req, struct fuse_file *ff, + struct inode *inode, loff_t pos, size_t count, + int iswritepage) { - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); - struct fuse_write_in inarg; - struct fuse_write_out outarg; - ssize_t res; - - memset(&inarg, 0, sizeof(inarg)); - inarg.writepage = 0; - inarg.fh = ff->fh; - inarg.offset = pos; - inarg.size = count; + struct fuse_write_in *inarg = &req->misc.write.in; + + inarg->writepage = iswritepage; + inarg->fh = ff->fh; + inarg->offset = pos; + inarg->size = count; req->in.h.opcode = FUSE_WRITE; - req->in.h.nodeid = fi->nodeid; + req->in.h.nodeid = get_node_id(inode); + if (iswritepage) { + req->in.h.uid = 0; + req->in.h.gid = 0; + req->in.h.pid = 0; + } + req->in.argpages = 1; req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; + req->in.args[0].size = sizeof(struct fuse_write_in); + req->in.args[0].value = inarg; req->in.args[1].size = count; - req->in.args[1].value = buf; req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - res = req->out.h.error; - if (!res) { - if (outarg.size > count) - return -EPROTO; - else - return outarg.size; - } - else - return res; -} - -static int write_buffer(struct inode *inode, struct file *file, - struct page *page, unsigned offset, size_t count) -{ - struct fuse_conn *fc = INO_FC(inode); - struct fuse_file *ff = file->private_data; - char *buffer; - ssize_t res; - loff_t pos; - struct fuse_req *req; - - req = fuse_get_request(fc); - if (!req) - return -ERESTARTSYS; - - pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; - buffer = kmap(page); - res = fuse_send_write(req, ff, inode, buffer + offset, pos, count); - fuse_put_request(fc, req); - if (res >= 0) { - if (res < count) { - printk("fuse: short write\n"); - res = -EPROTO; - } else - res = 0; - } - kunmap(page); - if (res) - SetPageError(page); - return res; + req->out.args[0].size = sizeof(struct fuse_write_out); + req->out.args[0].value = &req->misc.write.out; } static int get_write_count(struct inode *inode, struct page *page) @@ -610,28 +572,32 @@ static int get_write_count(struct inode *inode, struct page *page) loff_t size = i_size_read(inode); int count; - end_index = size >> PAGE_CACHE_SHIFT; + end_index = size >> PAGE_SHIFT; if (page->index < end_index) - count = PAGE_CACHE_SIZE; + count = PAGE_SIZE; else { - count = size & (PAGE_CACHE_SIZE - 1); + count = size & (PAGE_SIZE - 1); if (page->index > end_index || count == 0) return 0; } return count; } +static inline struct fuse_file *get_write_file(struct fuse_inode *fi) +{ + BUG_ON(list_empty(&fi->write_files)); + return list_entry(fi->write_files.next, struct fuse_file, ff_list); +} + #ifdef KERNEL_2_6 static void write_page_end(struct fuse_conn *fc, struct fuse_req *req) { struct page *page = req->pages[0]; struct inode *inode = page->mapping->host; - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_write_out *outarg = req->out.args[0].value; - if (!req->out.h.error && outarg->size != req->in.args[1].size) { - printk("fuse: short write\n"); + if (!req->out.h.error && outarg->size != req->in.args[1].size) req->out.h.error = -EPROTO; - } if (req->out.h.error) { SetPageError(page); @@ -641,52 +607,15 @@ static void write_page_end(struct fuse_conn *fc, struct fuse_req *req) set_bit(AS_EIO, &page->mapping->flags); } up_read(&fi->write_sem); - end_page_writeback(page); - kunmap(page); fuse_put_request(fc, req); } -static void fuse_send_writepage(struct fuse_req *req, struct inode *inode, - struct page *page, unsigned count) -{ - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); - struct fuse_write_in *inarg; - struct fuse_file *ff; - char *buffer; - - BUG_ON(list_empty(&fi->write_files)); - ff = list_entry(fi->write_files.next, struct fuse_file, ff_list); - - inarg = &req->misc.write.in; - buffer = kmap(page); - inarg->writepage = 1; - inarg->fh = ff->fh; - inarg->offset = ((loff_t) page->index << PAGE_CACHE_SHIFT); - inarg->size = count; - req->in.h.opcode = FUSE_WRITE; - req->in.h.nodeid = fi->nodeid; - req->in.h.uid = 0; - req->in.h.gid = 0; - req->in.h.pid = 0; - req->in.numargs = 2; - req->in.args[0].size = sizeof(struct fuse_write_in); - req->in.args[0].value = inarg; - req->in.args[1].size = count; - req->in.args[1].value = buffer; - req->out.numargs = 1; - req->out.args[0].size = sizeof(struct fuse_write_out); - req->out.args[0].value = &req->misc.write.out; - req->pages[0] = page; - request_send_async(fc, req, write_page_end); -} - static int fuse_writepage(struct page *page, struct writeback_control *wbc) { struct inode *inode = page->mapping->host; - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_req *req; int err; @@ -702,12 +631,16 @@ static int fuse_writepage(struct page *page, struct writeback_control *wbc) else down_read(&fi->write_sem); if (locked) { - unsigned count; + unsigned count = get_write_count(inode, page); + loff_t pos = (loff_t) page->index << PAGE_SHIFT; err = 0; - count = get_write_count(inode, page); if (count) { - SetPageWriteback(page); - fuse_send_writepage(req, inode, page, count); + struct fuse_file *ff = get_write_file(fi); + SetPageWriteback(page); + fuse_write_init(req, ff, inode, pos, count, 1); + req->num_pages = 1; + req->pages[0] = page; + request_send_async(fc, req, write_page_end); goto out; } up_read(&fi->write_sem); @@ -727,89 +660,34 @@ static int fuse_writepage(struct page *page, struct writeback_control *wbc) return err; } #else -static ssize_t fuse_send_writepage(struct fuse_req *req, struct fuse_file *ff, - struct inode *inode, const char *buf, - loff_t pos, size_t count) -{ - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); - struct fuse_write_in inarg; - struct fuse_write_out outarg; - ssize_t res; - - memset(&inarg, 0, sizeof(inarg)); - inarg.writepage = 1; - inarg.fh = ff->fh; - inarg.offset = pos; - inarg.size = count; - req->in.h.opcode = FUSE_WRITE; - req->in.h.nodeid = fi->nodeid; - req->in.h.uid = 0; - req->in.h.gid = 0; - req->in.h.pid = 0; - req->in.numargs = 2; - req->in.args[0].size = sizeof(inarg); - req->in.args[0].value = &inarg; - req->in.args[1].size = count; - req->in.args[1].value = buf; - req->out.numargs = 1; - req->out.args[0].size = sizeof(outarg); - req->out.args[0].value = &outarg; - request_send(fc, req); - res = req->out.h.error; - if (!res) { - if (outarg.size > count) - return -EPROTO; - else - return outarg.size; - } - else - return res; -} - -static int write_page_block(struct inode *inode, struct page *page) +static int fuse_writepage(struct page *page) { - struct fuse_conn *fc = INO_FC(inode); - struct fuse_inode *fi = INO_FI(inode); - char *buffer; - ssize_t res; - loff_t pos; + int err; unsigned count; - struct fuse_req *req; + struct inode *inode = page->mapping->host; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_req *req = fuse_get_request_nonint(fc); - req = fuse_get_request(fc); - if (!req) - return -ERESTARTSYS; - down_read(&fi->write_sem); count = get_write_count(inode, page); - res = 0; + err = 0; if (count) { - struct fuse_file *ff; - BUG_ON(list_empty(&fi->write_files)); - ff = list_entry(fi->write_files.next, struct fuse_file, ff_list); - pos = ((loff_t) page->index << PAGE_CACHE_SHIFT); - buffer = kmap(page); - res = fuse_send_writepage(req, ff, inode, buffer, pos, count); - if (res >= 0) { - if (res < count) { - printk("fuse: short write\n"); - res = -EPROTO; - } else - res = 0; - } + struct fuse_file *ff = get_write_file(fi); + loff_t pos = ((loff_t) page->index << PAGE_SHIFT); + + fuse_write_init(req, ff, inode, pos, count, 1); + req->num_pages = 1; + req->pages[0] = page; + request_send(fc, req); + err = req->out.h.error; + if (!err && req->misc.write.out.size != count) + err = -EPROTO; } up_read(&fi->write_sem); fuse_put_request(fc, req); - kunmap(page); - if (res) + if (err) SetPageError(page); - return res; -} - -static int fuse_writepage(struct page *page) -{ - int err = write_page_block(page->mapping->host, page); unlock_page(page); return err; } @@ -826,15 +704,29 @@ static int fuse_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to) { int err; + unsigned count = to - offset; struct inode *inode = page->mapping->host; + struct fuse_file *ff = file->private_data; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req = fuse_get_request(fc); + loff_t pos = ((loff_t) page->index << PAGE_SHIFT) + offset; + if (!req) + return -ERESTARTSYS; - err = write_buffer(inode, file, page, offset, to - offset); + fuse_write_init(req, ff, inode, pos, count, 0); + req->num_pages = 1; + req->pages[0] = page; + req->page_offset = offset; + request_send(fc, req); + err = req->out.h.error; + if (!err && req->misc.write.out.size != count) + err = -EPROTO; if (!err) { - loff_t pos = (page->index << PAGE_CACHE_SHIFT) + to; + pos += count; if (pos > i_size_read(inode)) i_size_write(inode, pos); - if (offset == 0 && to == PAGE_CACHE_SIZE) { + if (offset == 0 && to == PAGE_SIZE) { #ifdef KERNEL_2_6 clear_page_dirty(page); #else @@ -842,8 +734,8 @@ static int fuse_commit_write(struct file *file, struct page *page, #endif SetPageUptodate(page); } - } + fuse_put_request(fc, req); return err; } @@ -851,49 +743,44 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; - char *tmpbuf; - ssize_t res = 0; loff_t pos = *ppos; - struct fuse_req *req; - size_t max_write = min(fc->max_write, (unsigned) PAGE_SIZE); - - req = fuse_get_request(fc); + ssize_t res = 0; + struct fuse_req *req = fuse_get_request(fc); if (!req) return -ERESTARTSYS; - tmpbuf = (char *) __get_free_page(GFP_KERNEL); - if (!tmpbuf) { - fuse_put_request(fc, req); - return -ENOMEM; - } - while (count) { - size_t nbytes = min(max_write, count); - ssize_t res1; - if (copy_from_user(tmpbuf, buf, nbytes)) { - res = -EFAULT; + unsigned ntmp; + unsigned nwritten; + size_t nbytes = min(count, fc->max_write); + int err = fuse_get_user_pages(req, buf, nbytes, 0); + if (err) { + res = err; break; } - res1 = fuse_send_write(req, ff, inode, tmpbuf, pos, nbytes); - if (res1 < 0) { - res = res1; + ntmp = (req->num_pages << PAGE_SHIFT) - req->page_offset; + nbytes = min(nbytes, ntmp); + fuse_write_init(req, ff, inode, pos, nbytes, 0); + request_send(fc, req); + fuse_release_user_pages(req, 0); + if (req->out.h.error) { + if (!res) + res = req->out.h.error; break; } - res += res1; - count -= res1; - buf += res1; - pos += res1; - if (res1 < nbytes) + nwritten = req->misc.write.out.size; + count -= nwritten; + res += nwritten; + pos += nwritten; + buf += nwritten; + if (nwritten != nbytes) break; - if (count) fuse_reset_request(req); } - free_page((unsigned long) tmpbuf); fuse_put_request(fc, req); - if (res > 0) { if (pos > i_size_read(inode)) i_size_write(inode, pos); @@ -907,7 +794,7 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); if (fc->flags & FUSE_DIRECT_IO) { ssize_t res; @@ -915,6 +802,7 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf, res = fuse_direct_write(file, buf, count, ppos); up(&inode->i_sem); return res; + return -EPERM; } else return generic_file_write(file, buf, count, ppos); @@ -923,14 +811,14 @@ static ssize_t fuse_file_write(struct file *file, const char __user *buf, static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) { struct inode *inode = file->f_dentry->d_inode; - struct fuse_conn *fc = INO_FC(inode); + struct fuse_conn *fc = get_fuse_conn(inode); if (fc->flags & FUSE_DIRECT_IO) return -ENODEV; else { if ((vma->vm_flags & (VM_WRITE | VM_SHARED)) == (VM_WRITE | VM_SHARED)) { - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_file *ff = file->private_data; if (!user_mmap && current->uid != 0) diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h index 37d8cad..b9dd4b8 100644 --- a/kernel/fuse_i.h +++ b/kernel/fuse_i.h @@ -69,15 +69,13 @@ doing the mount will be allowed to access the filesystem */ #define FUSE_ALLOW_OTHER (1 << 1) -/** If the FUSE_KERNEL_CACHE flag is given, then files will be cached - until the INVALIDATE operation is invoked */ +/** If the FUSE_KERNEL_CACHE flag is given, then cached data will not + be flushed on open */ #define FUSE_KERNEL_CACHE (1 << 2) #ifndef KERNEL_2_6 /** Allow FUSE to combine reads into 64k chunks. This is useful if - the filesystem is better at handling large chunks. NOTE: in - current implementation the raw throughput is worse for large reads - than for small. */ + the filesystem is better at handling large chunks */ #define FUSE_LARGE_READ (1 << 31) #endif /** Bypass the page cache for read and write operations */ @@ -89,12 +87,22 @@ /** FUSE specific inode data */ struct fuse_inode { + /** Unique ID, which identifies the inode between userspace + * and kernel */ unsigned long nodeid; + + /** The request used for sending the FORGET message */ struct fuse_req *forget_req; + + /** Semaphore protects the 'write_files' list, and against + truncate racing with async writeouts */ struct rw_semaphore write_sem; + + /** Time in jiffies until the file attributes are valid */ unsigned long i_time; - /* Files which can provide file handles in writepage. - Protected by write_sem */ + + /* List of fuse_files which can provide file handles in + * writepage, protected by write_sem */ struct list_head write_files; }; @@ -113,8 +121,16 @@ struct fuse_in_arg { /** The request input */ struct fuse_in { + /** The request header */ struct fuse_in_header h; + + /** True if the data for the last argument is in req->pages */ + unsigned argpages:1; + + /** Number of arguments */ unsigned numargs; + + /** Array of arguments */ struct fuse_in_arg args[3]; }; @@ -265,13 +281,34 @@ struct fuse_getdir_out_i { void *file; /* Used by kernel only */ }; +static inline struct fuse_conn **get_fuse_conn_super_p(struct super_block *sb) +{ #ifdef KERNEL_2_6 -#define SB_FC(sb) ((sb)->s_fs_info) + return (struct fuse_conn **) &sb->s_fs_info; #else -#define SB_FC(sb) ((sb)->u.generic_sbp) + return (struct fuse_conn **) &sb->u.generic_sbp; #endif -#define INO_FC(inode) SB_FC((inode)->i_sb) -#define INO_FI(i) ((struct fuse_inode *) (((struct inode *)(i))+1)) +} + +static inline struct fuse_conn *get_fuse_conn_super(struct super_block *sb) +{ + return *get_fuse_conn_super_p(sb); +} + +static inline struct fuse_conn *get_fuse_conn(struct inode *inode) +{ + return get_fuse_conn_super(inode->i_sb); +} + +static inline struct fuse_inode *get_fuse_inode(struct inode *inode) +{ + return (struct fuse_inode *) (&inode[1]); +} + +static inline unsigned long get_node_id(struct inode *inode) +{ + return get_fuse_inode(inode)->nodeid; +} /** Device operations */ extern struct file_operations fuse_dev_operations; diff --git a/kernel/inode.c b/kernel/inode.c index 0a70d02..e0a3902 100644 --- a/kernel/inode.c +++ b/kernel/inode.c @@ -65,7 +65,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) inode->u.generic_ip = NULL; #endif - fi = INO_FI(inode); + fi = get_fuse_inode(inode); memset(fi, 0, sizeof(*fi)); fi->forget_req = fuse_request_alloc(); if (!fi->forget_req) { @@ -80,7 +80,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) static void fuse_destroy_inode(struct inode *inode) { - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); BUG_ON(!list_empty(&fi->write_files)); if (fi->forget_req) fuse_request_free(fi->forget_req); @@ -107,10 +107,9 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, static void fuse_clear_inode(struct inode *inode) { - struct fuse_conn *fc = INO_FC(inode); - + struct fuse_conn *fc = get_fuse_conn(inode); if (fc) { - struct fuse_inode *fi = INO_FI(inode); + struct fuse_inode *fi = get_fuse_inode(inode); fuse_send_forget(fc, fi->forget_req, fi->nodeid, inode->i_version); fi->forget_req = NULL; } @@ -118,7 +117,7 @@ static void fuse_clear_inode(struct inode *inode) static void fuse_put_super(struct super_block *sb) { - struct fuse_conn *fc = SB_FC(sb); + struct fuse_conn *fc = get_fuse_conn_super(sb); down(&fc->sb_sem); spin_lock(&fuse_lock); @@ -129,7 +128,7 @@ static void fuse_put_super(struct super_block *sb) /* Flush all readers on this fs */ wake_up_all(&fc->waitq); fuse_release_conn(fc); - SB_FC(sb) = NULL; + *get_fuse_conn_super_p(sb) = NULL; spin_unlock(&fuse_lock); } @@ -148,7 +147,7 @@ static void convert_fuse_statfs(struct kstatfs *stbuf, struct fuse_kstatfs *attr static int fuse_statfs(struct super_block *sb, struct kstatfs *buf) { - struct fuse_conn *fc = SB_FC(sb); + struct fuse_conn *fc = get_fuse_conn_super(sb); struct fuse_req *req; struct fuse_statfs_out outarg; int err; @@ -289,7 +288,7 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d) static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt) { - struct fuse_conn *fc = SB_FC(mnt->mnt_sb); + struct fuse_conn *fc = get_fuse_conn_super(mnt->mnt_sb); seq_printf(m, ",uid=%u", fc->uid); if (fc->flags & FUSE_DEFAULT_PERMISSIONS) @@ -431,7 +430,6 @@ static int fuse_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable) { struct inode *inode = dentry->d_inode; - struct fuse_inode *fi = INO_FI(inode); int len = *max_len; int type = 1; @@ -439,16 +437,14 @@ static int fuse_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, return 255; len = 2; - fh[0] = fi->nodeid; + fh[0] = get_fuse_inode(inode)->nodeid; fh[1] = inode->i_generation; if (connectable && !S_ISDIR(inode->i_mode)) { struct inode *parent; - struct fuse_inode *parent_fi; spin_lock(&dentry->d_lock); parent = dentry->d_parent->d_inode; - parent_fi = INO_FI(parent); - fh[2] = parent_fi->nodeid; + fh[2] = get_fuse_inode(parent)->nodeid; fh[3] = parent->i_generation; spin_unlock(&dentry->d_lock); len = 4; @@ -517,7 +513,7 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) #endif fc->max_write = FUSE_MAX_IN / 2; - SB_FC(sb) = fc; + *get_fuse_conn_super_p(sb) = fc; root = get_root_inode(sb, d.rootmode); if (root == NULL) { @@ -540,7 +536,7 @@ static int fuse_read_super(struct super_block *sb, void *data, int silent) up(&fc->sb_sem); fuse_release_conn(fc); spin_unlock(&fuse_lock); - SB_FC(sb) = NULL; + *get_fuse_conn_super_p(sb) = NULL; return -EINVAL; } -- 2.30.2