Commit c4bc705e authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse

Pull fuse updates from Miklos Szeredi:
 "The biggest part of this pull request is a patch series from Maxim
  Patlasov to optimize scatter-gather direct IO.  There's also the
  addition of a "readdirplus" API, poll events and various fixes and
  cleanups.

  There's a one line change outside of fuse to mm/filemap.c which makes
  the argument of iov_iter_single_seg_count() const, required by Maxim's
  patches."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (22 commits)
  fuse: allow control of adaptive readdirplus use
  Synchronize fuse header with one used in library
  fuse: send poll events
  fuse: don't WARN when nlink is zero
  fuse: avoid out-of-scope stack access
  fuse: bump version for READDIRPLUS
  FUSE: Adapt readdirplus to application usage patterns
  Do not use RCU for current process credentials
  fuse: cleanup fuse_direct_io()
  fuse: optimize __fuse_direct_io()
  fuse: optimize fuse_get_user_pages()
  fuse: pass iov[] to fuse_get_user_pages()
  mm: minor cleanup of iov_iter_single_seg_count()
  fuse: use req->page_descs[] for argpages cases
  fuse: add per-page descriptor <offset, length> to fuse_req
  fuse: rework fuse_do_ioctl()
  fuse: rework fuse_perform_write()
  fuse: rework fuse_readpages()
  fuse: rework fuse_retrieve()
  fuse: categorize fuse_get_req()
  ...
parents 2608e3d0 634734b6
...@@ -91,19 +91,22 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count, ...@@ -91,19 +91,22 @@ static ssize_t cuse_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos) loff_t *ppos)
{ {
loff_t pos = 0; loff_t pos = 0;
struct iovec iov = { .iov_base = buf, .iov_len = count };
return fuse_direct_io(file, buf, count, &pos, 0); return fuse_direct_io(file, &iov, 1, count, &pos, 0);
} }
static ssize_t cuse_write(struct file *file, const char __user *buf, static ssize_t cuse_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
loff_t pos = 0; loff_t pos = 0;
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
/* /*
* No locking or generic_write_checks(), the server is * No locking or generic_write_checks(), the server is
* responsible for locking and sanity checks. * responsible for locking and sanity checks.
*/ */
return fuse_direct_io(file, buf, count, &pos, 1); return fuse_direct_io(file, &iov, 1, count, &pos, 1);
} }
static int cuse_open(struct inode *inode, struct file *file) static int cuse_open(struct inode *inode, struct file *file)
...@@ -419,7 +422,7 @@ static int cuse_send_init(struct cuse_conn *cc) ...@@ -419,7 +422,7 @@ static int cuse_send_init(struct cuse_conn *cc)
BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE); BUILD_BUG_ON(CUSE_INIT_INFO_MAX > PAGE_SIZE);
req = fuse_get_req(fc); req = fuse_get_req(fc, 1);
if (IS_ERR(req)) { if (IS_ERR(req)) {
rc = PTR_ERR(req); rc = PTR_ERR(req);
goto err; goto err;
...@@ -449,6 +452,7 @@ static int cuse_send_init(struct cuse_conn *cc) ...@@ -449,6 +452,7 @@ static int cuse_send_init(struct cuse_conn *cc)
req->out.argvar = 1; req->out.argvar = 1;
req->out.argpages = 1; req->out.argpages = 1;
req->pages[0] = page; req->pages[0] = page;
req->page_descs[0].length = req->out.args[1].size;
req->num_pages = 1; req->num_pages = 1;
req->end = cuse_process_init_reply; req->end = cuse_process_init_reply;
fuse_request_send_background(fc, req); fuse_request_send_background(fc, req);
......
...@@ -34,34 +34,67 @@ static struct fuse_conn *fuse_get_conn(struct file *file) ...@@ -34,34 +34,67 @@ static struct fuse_conn *fuse_get_conn(struct file *file)
return file->private_data; return file->private_data;
} }
static void fuse_request_init(struct fuse_req *req) static void fuse_request_init(struct fuse_req *req, struct page **pages,
struct fuse_page_desc *page_descs,
unsigned npages)
{ {
memset(req, 0, sizeof(*req)); memset(req, 0, sizeof(*req));
memset(pages, 0, sizeof(*pages) * npages);
memset(page_descs, 0, sizeof(*page_descs) * npages);
INIT_LIST_HEAD(&req->list); INIT_LIST_HEAD(&req->list);
INIT_LIST_HEAD(&req->intr_entry); INIT_LIST_HEAD(&req->intr_entry);
init_waitqueue_head(&req->waitq); init_waitqueue_head(&req->waitq);
atomic_set(&req->count, 1); atomic_set(&req->count, 1);
req->pages = pages;
req->page_descs = page_descs;
req->max_pages = npages;
} }
struct fuse_req *fuse_request_alloc(void) static struct fuse_req *__fuse_request_alloc(unsigned npages, gfp_t flags)
{ {
struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, GFP_KERNEL); struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, flags);
if (req) if (req) {
fuse_request_init(req); struct page **pages;
struct fuse_page_desc *page_descs;
if (npages <= FUSE_REQ_INLINE_PAGES) {
pages = req->inline_pages;
page_descs = req->inline_page_descs;
} else {
pages = kmalloc(sizeof(struct page *) * npages, flags);
page_descs = kmalloc(sizeof(struct fuse_page_desc) *
npages, flags);
}
if (!pages || !page_descs) {
kfree(pages);
kfree(page_descs);
kmem_cache_free(fuse_req_cachep, req);
return NULL;
}
fuse_request_init(req, pages, page_descs, npages);
}
return req; return req;
} }
struct fuse_req *fuse_request_alloc(unsigned npages)
{
return __fuse_request_alloc(npages, GFP_KERNEL);
}
EXPORT_SYMBOL_GPL(fuse_request_alloc); EXPORT_SYMBOL_GPL(fuse_request_alloc);
struct fuse_req *fuse_request_alloc_nofs(void) struct fuse_req *fuse_request_alloc_nofs(unsigned npages)
{ {
struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, GFP_NOFS); return __fuse_request_alloc(npages, GFP_NOFS);
if (req)
fuse_request_init(req);
return req;
} }
void fuse_request_free(struct fuse_req *req) void fuse_request_free(struct fuse_req *req)
{ {
if (req->pages != req->inline_pages) {
kfree(req->pages);
kfree(req->page_descs);
}
kmem_cache_free(fuse_req_cachep, req); kmem_cache_free(fuse_req_cachep, req);
} }
...@@ -97,7 +130,7 @@ static void fuse_req_init_context(struct fuse_req *req) ...@@ -97,7 +130,7 @@ static void fuse_req_init_context(struct fuse_req *req)
req->in.h.pid = current->pid; req->in.h.pid = current->pid;
} }
struct fuse_req *fuse_get_req(struct fuse_conn *fc) struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages)
{ {
struct fuse_req *req; struct fuse_req *req;
sigset_t oldset; sigset_t oldset;
...@@ -116,7 +149,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc) ...@@ -116,7 +149,7 @@ struct fuse_req *fuse_get_req(struct fuse_conn *fc)
if (!fc->connected) if (!fc->connected)
goto out; goto out;
req = fuse_request_alloc(); req = fuse_request_alloc(npages);
err = -ENOMEM; err = -ENOMEM;
if (!req) if (!req)
goto out; goto out;
...@@ -165,7 +198,7 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req) ...@@ -165,7 +198,7 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req)
struct fuse_file *ff = file->private_data; struct fuse_file *ff = file->private_data;
spin_lock(&fc->lock); spin_lock(&fc->lock);
fuse_request_init(req); fuse_request_init(req, req->pages, req->page_descs, req->max_pages);
BUG_ON(ff->reserved_req); BUG_ON(ff->reserved_req);
ff->reserved_req = req; ff->reserved_req = req;
wake_up_all(&fc->reserved_req_waitq); wake_up_all(&fc->reserved_req_waitq);
...@@ -186,13 +219,14 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req) ...@@ -186,13 +219,14 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req)
* filesystem should not have it's own file open. If deadlock is * filesystem should not have it's own file open. If deadlock is
* intentional, it can still be broken by "aborting" the filesystem. * intentional, it can still be broken by "aborting" the filesystem.
*/ */
struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file) struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
struct file *file)
{ {
struct fuse_req *req; struct fuse_req *req;
atomic_inc(&fc->num_waiting); atomic_inc(&fc->num_waiting);
wait_event(fc->blocked_waitq, !fc->blocked); wait_event(fc->blocked_waitq, !fc->blocked);
req = fuse_request_alloc(); req = fuse_request_alloc(0);
if (!req) if (!req)
req = get_reserved_req(fc, file); req = get_reserved_req(fc, file);
...@@ -406,9 +440,8 @@ __acquires(fc->lock) ...@@ -406,9 +440,8 @@ __acquires(fc->lock)
} }
} }
void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) static void __fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{ {
req->isreply = 1;
spin_lock(&fc->lock); spin_lock(&fc->lock);
if (!fc->connected) if (!fc->connected)
req->out.h.error = -ENOTCONN; req->out.h.error = -ENOTCONN;
...@@ -425,6 +458,12 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) ...@@ -425,6 +458,12 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
} }
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
} }
void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
__fuse_request_send(fc, req);
}
EXPORT_SYMBOL_GPL(fuse_request_send); EXPORT_SYMBOL_GPL(fuse_request_send);
static void fuse_request_send_nowait_locked(struct fuse_conn *fc, static void fuse_request_send_nowait_locked(struct fuse_conn *fc,
...@@ -491,6 +530,27 @@ void fuse_request_send_background_locked(struct fuse_conn *fc, ...@@ -491,6 +530,27 @@ void fuse_request_send_background_locked(struct fuse_conn *fc,
fuse_request_send_nowait_locked(fc, req); fuse_request_send_nowait_locked(fc, req);
} }
void fuse_force_forget(struct file *file, u64 nodeid)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req;
struct fuse_forget_in inarg;
memset(&inarg, 0, sizeof(inarg));
inarg.nlookup = 1;
req = fuse_get_req_nofail_nopages(fc, file);
req->in.h.opcode = FUSE_FORGET;
req->in.h.nodeid = nodeid;
req->in.numargs = 1;
req->in.args[0].size = sizeof(inarg);
req->in.args[0].value = &inarg;
req->isreply = 0;
__fuse_request_send(fc, req);
/* ignore errors */
fuse_put_request(fc, req);
}
/* /*
* Lock the request. Up to the next unlock_request() there mustn't be * Lock the request. Up to the next unlock_request() there mustn't be
* anything that could cause a page-fault. If the request was already * anything that could cause a page-fault. If the request was already
...@@ -850,11 +910,11 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, ...@@ -850,11 +910,11 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
{ {
unsigned i; unsigned i;
struct fuse_req *req = cs->req; struct fuse_req *req = cs->req;
unsigned offset = req->page_offset;
unsigned count = min(nbytes, (unsigned) PAGE_SIZE - offset);
for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) { for (i = 0; i < req->num_pages && (nbytes || zeroing); i++) {
int err; int err;
unsigned offset = req->page_descs[i].offset;
unsigned count = min(nbytes, req->page_descs[i].length);
err = fuse_copy_page(cs, &req->pages[i], offset, count, err = fuse_copy_page(cs, &req->pages[i], offset, count,
zeroing); zeroing);
...@@ -862,8 +922,6 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes, ...@@ -862,8 +922,6 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,
return err; return err;
nbytes -= count; nbytes -= count;
count = min(nbytes, (unsigned) PAGE_SIZE);
offset = 0;
} }
return 0; return 0;
} }
...@@ -1536,29 +1594,34 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, ...@@ -1536,29 +1594,34 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
unsigned int num; unsigned int num;
unsigned int offset; unsigned int offset;
size_t total_len = 0; size_t total_len = 0;
int num_pages;
offset = outarg->offset & ~PAGE_CACHE_MASK;
file_size = i_size_read(inode);
num = outarg->size;
if (outarg->offset > file_size)
num = 0;
else if (outarg->offset + num > file_size)
num = file_size - outarg->offset;
req = fuse_get_req(fc); num_pages = (num + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
num_pages = min(num_pages, FUSE_MAX_PAGES_PER_REQ);
req = fuse_get_req(fc, num_pages);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
offset = outarg->offset & ~PAGE_CACHE_MASK;
req->in.h.opcode = FUSE_NOTIFY_REPLY; req->in.h.opcode = FUSE_NOTIFY_REPLY;
req->in.h.nodeid = outarg->nodeid; req->in.h.nodeid = outarg->nodeid;
req->in.numargs = 2; req->in.numargs = 2;
req->in.argpages = 1; req->in.argpages = 1;
req->page_offset = offset; req->page_descs[0].offset = offset;
req->end = fuse_retrieve_end; req->end = fuse_retrieve_end;
index = outarg->offset >> PAGE_CACHE_SHIFT; index = outarg->offset >> PAGE_CACHE_SHIFT;
file_size = i_size_read(inode);
num = outarg->size;
if (outarg->offset > file_size)
num = 0;
else if (outarg->offset + num > file_size)
num = file_size - outarg->offset;
while (num && req->num_pages < FUSE_MAX_PAGES_PER_REQ) { while (num && req->num_pages < num_pages) {
struct page *page; struct page *page;
unsigned int this_num; unsigned int this_num;
...@@ -1568,6 +1631,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, ...@@ -1568,6 +1631,7 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,
this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);
req->pages[req->num_pages] = page; req->pages[req->num_pages] = page;
req->page_descs[req->num_pages].length = this_num;
req->num_pages++; req->num_pages++;
offset = 0; offset = 0;
......
...@@ -14,6 +14,29 @@ ...@@ -14,6 +14,29 @@
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/slab.h> #include <linux/slab.h>
static bool fuse_use_readdirplus(struct inode *dir, struct file *filp)
{
struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_inode *fi = get_fuse_inode(dir);
if (!fc->do_readdirplus)
return false;
if (!fc->readdirplus_auto)
return true;
if (test_and_clear_bit(FUSE_I_ADVISE_RDPLUS, &fi->state))
return true;
if (filp->f_pos == 0)
return true;
return false;
}
static void fuse_advise_use_readdirplus(struct inode *dir)
{
struct fuse_inode *fi = get_fuse_inode(dir);
set_bit(FUSE_I_ADVISE_RDPLUS, &fi->state);
}
#if BITS_PER_LONG >= 64 #if BITS_PER_LONG >= 64
static inline void fuse_dentry_settime(struct dentry *entry, u64 time) static inline void fuse_dentry_settime(struct dentry *entry, u64 time)
{ {
...@@ -178,7 +201,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) ...@@ -178,7 +201,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
return -ECHILD; return -ECHILD;
fc = get_fuse_conn(inode); fc = get_fuse_conn(inode);
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return 0; return 0;
...@@ -219,6 +242,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) ...@@ -219,6 +242,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
attr_version); attr_version);
fuse_change_entry_timeout(entry, &outarg); fuse_change_entry_timeout(entry, &outarg);
} }
fuse_advise_use_readdirplus(inode);
return 1; return 1;
} }
...@@ -271,7 +295,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, ...@@ -271,7 +295,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name,
if (name->len > FUSE_NAME_MAX) if (name->len > FUSE_NAME_MAX)
goto out; goto out;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
err = PTR_ERR(req); err = PTR_ERR(req);
if (IS_ERR(req)) if (IS_ERR(req))
goto out; goto out;
...@@ -355,6 +379,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, ...@@ -355,6 +379,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
else else
fuse_invalidate_entry_cache(entry); fuse_invalidate_entry_cache(entry);
fuse_advise_use_readdirplus(dir);
return newent; return newent;
out_iput: out_iput:
...@@ -391,7 +416,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, ...@@ -391,7 +416,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
if (!forget) if (!forget)
goto out_err; goto out_err;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
err = PTR_ERR(req); err = PTR_ERR(req);
if (IS_ERR(req)) if (IS_ERR(req))
goto out_put_forget_req; goto out_put_forget_req;
...@@ -592,7 +617,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode, ...@@ -592,7 +617,7 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
{ {
struct fuse_mknod_in inarg; struct fuse_mknod_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -623,7 +648,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t mode) ...@@ -623,7 +648,7 @@ static int fuse_mkdir(struct inode *dir, struct dentry *entry, umode_t mode)
{ {
struct fuse_mkdir_in inarg; struct fuse_mkdir_in inarg;
struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -647,7 +672,7 @@ static int fuse_symlink(struct inode *dir, struct dentry *entry, ...@@ -647,7 +672,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 = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -664,7 +689,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) ...@@ -664,7 +689,7 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
{ {
int err; int err;
struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -682,6 +707,13 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) ...@@ -682,6 +707,13 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
spin_lock(&fc->lock); spin_lock(&fc->lock);
fi->attr_version = ++fc->attr_version; fi->attr_version = ++fc->attr_version;
/*
* If i_nlink == 0 then unlink doesn't make sense, yet this can
* happen if userspace filesystem is careless. It would be
* difficult to enforce correct nlink usage so just ignore this
* condition here
*/
if (inode->i_nlink > 0)
drop_nlink(inode); drop_nlink(inode);
spin_unlock(&fc->lock); spin_unlock(&fc->lock);
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
...@@ -696,7 +728,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) ...@@ -696,7 +728,7 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
{ {
int err; int err;
struct fuse_conn *fc = get_fuse_conn(dir); struct fuse_conn *fc = get_fuse_conn(dir);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -723,7 +755,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, ...@@ -723,7 +755,7 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
int err; int err;
struct fuse_rename_in inarg; struct fuse_rename_in inarg;
struct fuse_conn *fc = get_fuse_conn(olddir); struct fuse_conn *fc = get_fuse_conn(olddir);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -776,7 +808,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, ...@@ -776,7 +808,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
struct fuse_link_in inarg; struct fuse_link_in inarg;
struct inode *inode = entry->d_inode; struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -848,7 +880,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat, ...@@ -848,7 +880,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
struct fuse_req *req; struct fuse_req *req;
u64 attr_version; u64 attr_version;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -985,7 +1017,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, ...@@ -985,7 +1017,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
/* /*
* Calling into a user-controlled filesystem gives the filesystem * Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the requester process. This * daemon ptrace-like capabilities over the current process. This
* means, that the filesystem daemon is able to record the exact * means, that the filesystem daemon is able to record the exact
* filesystem operations performed, and can also control the behavior * filesystem operations performed, and can also control the behavior
* of the requester process in otherwise impossible ways. For example * of the requester process in otherwise impossible ways. For example
...@@ -996,27 +1028,23 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, ...@@ -996,27 +1028,23 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
* for which the owner of the mount has ptrace privilege. This * for which the owner of the mount has ptrace privilege. This
* excludes processes started by other users, suid or sgid processes. * excludes processes started by other users, suid or sgid processes.
*/ */
int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task) int fuse_allow_current_process(struct fuse_conn *fc)
{ {
const struct cred *cred; const struct cred *cred;
int ret;
if (fc->flags & FUSE_ALLOW_OTHER) if (fc->flags & FUSE_ALLOW_OTHER)
return 1; return 1;
rcu_read_lock(); cred = current_cred();
ret = 0;
cred = __task_cred(task);
if (uid_eq(cred->euid, fc->user_id) && if (uid_eq(cred->euid, fc->user_id) &&
uid_eq(cred->suid, fc->user_id) && uid_eq(cred->suid, fc->user_id) &&
uid_eq(cred->uid, fc->user_id) && uid_eq(cred->uid, fc->user_id) &&
gid_eq(cred->egid, fc->group_id) && gid_eq(cred->egid, fc->group_id) &&
gid_eq(cred->sgid, fc->group_id) && gid_eq(cred->sgid, fc->group_id) &&
gid_eq(cred->gid, fc->group_id)) gid_eq(cred->gid, fc->group_id))
ret = 1; return 1;
rcu_read_unlock();
return ret; return 0;
} }
static int fuse_access(struct inode *inode, int mask) static int fuse_access(struct inode *inode, int mask)
...@@ -1029,7 +1057,7 @@ static int fuse_access(struct inode *inode, int mask) ...@@ -1029,7 +1057,7 @@ static int fuse_access(struct inode *inode, int mask)
if (fc->no_access) if (fc->no_access)
return 0; return 0;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1077,7 +1105,7 @@ static int fuse_permission(struct inode *inode, int mask) ...@@ -1077,7 +1105,7 @@ static int fuse_permission(struct inode *inode, int mask)
bool refreshed = false; bool refreshed = false;
int err = 0; int err = 0;
if (!fuse_allow_task(fc, current)) if (!fuse_allow_current_process(fc))
return -EACCES; return -EACCES;
/* /*
...@@ -1155,19 +1183,157 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file, ...@@ -1155,19 +1183,157 @@ static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
return 0; return 0;
} }
static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) static int fuse_direntplus_link(struct file *file,
struct fuse_direntplus *direntplus,
u64 attr_version)
{ {
int err; int err;
struct fuse_entry_out *o = &direntplus->entry_out;
struct fuse_dirent *dirent = &direntplus->dirent;
struct dentry *parent = file->f_path.dentry;
struct qstr name = QSTR_INIT(dirent->name, dirent->namelen);
struct dentry *dentry;
struct dentry *alias;
struct inode *dir = parent->d_inode;
struct fuse_conn *fc;
struct inode *inode;
if (!o->nodeid) {
/*
* Unlike in the case of fuse_lookup, zero nodeid does not mean
* ENOENT. Instead, it only means the userspace filesystem did
* not want to return attributes/handle for this entry.
*
* So do nothing.
*/
return 0;
}
if (name.name[0] == '.') {
/*
* We could potentially refresh the attributes of the directory
* and its parent?
*/
if (name.len == 1)
return 0;
if (name.name[1] == '.' && name.len == 2)
return 0;
}
fc = get_fuse_conn(dir);
name.hash = full_name_hash(name.name, name.len);
dentry = d_lookup(parent, &name);
if (dentry && dentry->d_inode) {
inode = dentry->d_inode;
if (get_node_id(inode) == o->nodeid) {
struct fuse_inode *fi;
fi = get_fuse_inode(inode);
spin_lock(&fc->lock);
fi->nlookup++;
spin_unlock(&fc->lock);
/*
* The other branch to 'found' comes via fuse_iget()
* which bumps nlookup inside
*/
goto found;
}
err = d_invalidate(dentry);
if (err)
goto out;
dput(dentry);
dentry = NULL;
}
dentry = d_alloc(parent, &name);
err = -ENOMEM;
if (!dentry)
goto out;
inode = fuse_iget(dir->i_sb, o->nodeid, o->generation,
&o->attr, entry_attr_timeout(o), attr_version);
if (!inode)
goto out;
alias = d_materialise_unique(dentry, inode);
err = PTR_ERR(alias);
if (IS_ERR(alias))
goto out;
if (alias) {
dput(dentry);
dentry = alias;
}
found:
fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o),
attr_version);
fuse_change_entry_timeout(dentry, o);
err = 0;
out:
if (dentry)
dput(dentry);
return err;
}
static int parse_dirplusfile(char *buf, size_t nbytes, struct file *file,
void *dstbuf, filldir_t filldir, u64 attr_version)
{
struct fuse_direntplus *direntplus;
struct fuse_dirent *dirent;
size_t reclen;
int over = 0;
int ret;
while (nbytes >= FUSE_NAME_OFFSET_DIRENTPLUS) {
direntplus = (struct fuse_direntplus *) buf;
dirent = &direntplus->dirent;
reclen = FUSE_DIRENTPLUS_SIZE(direntplus);
if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
return -EIO;
if (reclen > nbytes)
break;
if (!over) {
/* We fill entries into dstbuf only as much as
it can hold. But we still continue iterating
over remaining entries to link them. If not,
we need to send a FORGET for each of those
which we did not link.
*/
over = filldir(dstbuf, dirent->name, dirent->namelen,
file->f_pos, dirent->ino,
dirent->type);
file->f_pos = dirent->off;
}
buf += reclen;
nbytes -= reclen;
ret = fuse_direntplus_link(file, direntplus, attr_version);
if (ret)
fuse_force_forget(file, direntplus->entry_out.nodeid);
}
return 0;
}
static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
{
int plus, err;
size_t nbytes; size_t nbytes;
struct page *page; struct page *page;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req; struct fuse_req *req;
u64 attr_version = 0;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
return -EIO; return -EIO;
req = fuse_get_req(fc); req = fuse_get_req(fc, 1);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1176,17 +1342,34 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir) ...@@ -1176,17 +1342,34 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
fuse_put_request(fc, req); fuse_put_request(fc, req);
return -ENOMEM; return -ENOMEM;
} }
plus = fuse_use_readdirplus(inode, file);
req->out.argpages = 1; req->out.argpages = 1;
req->num_pages = 1; req->num_pages = 1;
req->pages[0] = page; req->pages[0] = page;
fuse_read_fill(req, file, file->f_pos, PAGE_SIZE, FUSE_READDIR); req->page_descs[0].length = PAGE_SIZE;
if (plus) {
attr_version = fuse_get_attr_version(fc);
fuse_read_fill(req, file, file->f_pos, PAGE_SIZE,
FUSE_READDIRPLUS);
} else {
fuse_read_fill(req, file, file->f_pos, PAGE_SIZE,
FUSE_READDIR);
}
fuse_request_send(fc, req); fuse_request_send(fc, req);
nbytes = req->out.args[0].size; nbytes = req->out.args[0].size;
err = req->out.h.error; err = req->out.h.error;
fuse_put_request(fc, req); fuse_put_request(fc, req);
if (!err) if (!err) {
err = parse_dirfile(page_address(page), nbytes, file, dstbuf, if (plus) {
filldir); err = parse_dirplusfile(page_address(page), nbytes,
file, dstbuf, filldir,
attr_version);
} else {
err = parse_dirfile(page_address(page), nbytes, file,
dstbuf, filldir);
}
}
__free_page(page); __free_page(page);
fuse_invalidate_attr(inode); /* atime changed */ fuse_invalidate_attr(inode); /* atime changed */
...@@ -1197,7 +1380,7 @@ static char *read_link(struct dentry *dentry) ...@@ -1197,7 +1380,7 @@ static char *read_link(struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_req *req = fuse_get_req(fc); struct fuse_req *req = fuse_get_req_nopages(fc);
char *link; char *link;
if (IS_ERR(req)) if (IS_ERR(req))
...@@ -1391,7 +1574,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, ...@@ -1391,7 +1574,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
loff_t oldsize; loff_t oldsize;
int err; int err;
if (!fuse_allow_task(fc, current)) if (!fuse_allow_current_process(fc))
return -EACCES; return -EACCES;
if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
...@@ -1410,7 +1593,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, ...@@ -1410,7 +1593,7 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
if (attr->ia_valid & ATTR_SIZE) if (attr->ia_valid & ATTR_SIZE)
is_truncate = true; is_truncate = true;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1500,7 +1683,7 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, ...@@ -1500,7 +1683,7 @@ static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
struct inode *inode = entry->d_inode; struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (!fuse_allow_task(fc, current)) if (!fuse_allow_current_process(fc))
return -EACCES; return -EACCES;
return fuse_update_attributes(inode, stat, NULL, NULL); return fuse_update_attributes(inode, stat, NULL, NULL);
...@@ -1518,7 +1701,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name, ...@@ -1518,7 +1701,7 @@ static int fuse_setxattr(struct dentry *entry, const char *name,
if (fc->no_setxattr) if (fc->no_setxattr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1557,7 +1740,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name, ...@@ -1557,7 +1740,7 @@ static ssize_t fuse_getxattr(struct dentry *entry, const char *name,
if (fc->no_getxattr) if (fc->no_getxattr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1603,13 +1786,13 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) ...@@ -1603,13 +1786,13 @@ static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
struct fuse_getxattr_out outarg; struct fuse_getxattr_out outarg;
ssize_t ret; ssize_t ret;
if (!fuse_allow_task(fc, current)) if (!fuse_allow_current_process(fc))
return -EACCES; return -EACCES;
if (fc->no_listxattr) if (fc->no_listxattr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1654,7 +1837,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name) ...@@ -1654,7 +1837,7 @@ static int fuse_removexattr(struct dentry *entry, const char *name)
if (fc->no_removexattr) if (fc->no_removexattr)
return -EOPNOTSUPP; return -EOPNOTSUPP;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
......
...@@ -25,7 +25,7 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file, ...@@ -25,7 +25,7 @@ static int fuse_send_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
struct fuse_req *req; struct fuse_req *req;
int err; int err;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -57,7 +57,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc) ...@@ -57,7 +57,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_conn *fc)
return NULL; return NULL;
ff->fc = fc; ff->fc = fc;
ff->reserved_req = fuse_request_alloc(); ff->reserved_req = fuse_request_alloc(0);
if (unlikely(!ff->reserved_req)) { if (unlikely(!ff->reserved_req)) {
kfree(ff); kfree(ff);
return NULL; return NULL;
...@@ -368,7 +368,7 @@ static int fuse_flush(struct file *file, fl_owner_t id) ...@@ -368,7 +368,7 @@ static int fuse_flush(struct file *file, fl_owner_t id)
if (fc->no_flush) if (fc->no_flush)
return 0; return 0;
req = fuse_get_req_nofail(fc, file); req = fuse_get_req_nofail_nopages(fc, file);
memset(&inarg, 0, sizeof(inarg)); memset(&inarg, 0, sizeof(inarg));
inarg.fh = ff->fh; inarg.fh = ff->fh;
inarg.lock_owner = fuse_lock_owner_id(fc, id); inarg.lock_owner = fuse_lock_owner_id(fc, id);
...@@ -436,7 +436,7 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, ...@@ -436,7 +436,7 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end,
fuse_sync_writes(inode); fuse_sync_writes(inode);
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
goto out; goto out;
...@@ -544,7 +544,7 @@ static int fuse_readpage(struct file *file, struct page *page) ...@@ -544,7 +544,7 @@ static int fuse_readpage(struct file *file, struct page *page)
*/ */
fuse_wait_on_page_writeback(inode, page->index); fuse_wait_on_page_writeback(inode, page->index);
req = fuse_get_req(fc); req = fuse_get_req(fc, 1);
err = PTR_ERR(req); err = PTR_ERR(req);
if (IS_ERR(req)) if (IS_ERR(req))
goto out; goto out;
...@@ -555,6 +555,7 @@ static int fuse_readpage(struct file *file, struct page *page) ...@@ -555,6 +555,7 @@ static int fuse_readpage(struct file *file, struct page *page)
req->out.argpages = 1; req->out.argpages = 1;
req->num_pages = 1; req->num_pages = 1;
req->pages[0] = page; req->pages[0] = page;
req->page_descs[0].length = count;
num_read = fuse_send_read(req, file, pos, count, NULL); num_read = fuse_send_read(req, file, pos, count, NULL);
err = req->out.h.error; err = req->out.h.error;
fuse_put_request(fc, req); fuse_put_request(fc, req);
...@@ -641,6 +642,7 @@ struct fuse_fill_data { ...@@ -641,6 +642,7 @@ struct fuse_fill_data {
struct fuse_req *req; struct fuse_req *req;
struct file *file; struct file *file;
struct inode *inode; struct inode *inode;
unsigned nr_pages;
}; };
static int fuse_readpages_fill(void *_data, struct page *page) static int fuse_readpages_fill(void *_data, struct page *page)
...@@ -656,16 +658,26 @@ static int fuse_readpages_fill(void *_data, struct page *page) ...@@ -656,16 +658,26 @@ static int fuse_readpages_fill(void *_data, struct page *page)
(req->num_pages == FUSE_MAX_PAGES_PER_REQ || (req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
(req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read || (req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
req->pages[req->num_pages - 1]->index + 1 != page->index)) { req->pages[req->num_pages - 1]->index + 1 != page->index)) {
int nr_alloc = min_t(unsigned, data->nr_pages,
FUSE_MAX_PAGES_PER_REQ);
fuse_send_readpages(req, data->file); fuse_send_readpages(req, data->file);
data->req = req = fuse_get_req(fc); data->req = req = fuse_get_req(fc, nr_alloc);
if (IS_ERR(req)) { if (IS_ERR(req)) {
unlock_page(page); unlock_page(page);
return PTR_ERR(req); return PTR_ERR(req);
} }
} }
if (WARN_ON(req->num_pages >= req->max_pages)) {
fuse_put_request(fc, req);
return -EIO;
}
page_cache_get(page); page_cache_get(page);
req->pages[req->num_pages] = page; req->pages[req->num_pages] = page;
req->page_descs[req->num_pages].length = PAGE_SIZE;
req->num_pages++; req->num_pages++;
data->nr_pages--;
return 0; return 0;
} }
...@@ -676,6 +688,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, ...@@ -676,6 +688,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_fill_data data; struct fuse_fill_data data;
int err; int err;
int nr_alloc = min_t(unsigned, nr_pages, FUSE_MAX_PAGES_PER_REQ);
err = -EIO; err = -EIO;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
...@@ -683,7 +696,8 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, ...@@ -683,7 +696,8 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
data.file = file; data.file = file;
data.inode = inode; data.inode = inode;
data.req = fuse_get_req(fc); data.req = fuse_get_req(fc, nr_alloc);
data.nr_pages = nr_pages;
err = PTR_ERR(data.req); err = PTR_ERR(data.req);
if (IS_ERR(data.req)) if (IS_ERR(data.req))
goto out; goto out;
...@@ -786,7 +800,7 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, ...@@ -786,7 +800,7 @@ static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file,
res = fuse_send_write(req, file, pos, count, NULL); res = fuse_send_write(req, file, pos, count, NULL);
offset = req->page_offset; offset = req->page_descs[0].offset;
count = res; count = res;
for (i = 0; i < req->num_pages; i++) { for (i = 0; i < req->num_pages; i++) {
struct page *page = req->pages[i]; struct page *page = req->pages[i];
...@@ -817,7 +831,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, ...@@ -817,7 +831,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
int err; int err;
req->in.argpages = 1; req->in.argpages = 1;
req->page_offset = offset; req->page_descs[0].offset = offset;
do { do {
size_t tmp; size_t tmp;
...@@ -857,6 +871,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, ...@@ -857,6 +871,7 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
err = 0; err = 0;
req->pages[req->num_pages] = page; req->pages[req->num_pages] = page;
req->page_descs[req->num_pages].length = tmp;
req->num_pages++; req->num_pages++;
iov_iter_advance(ii, tmp); iov_iter_advance(ii, tmp);
...@@ -869,11 +884,19 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req, ...@@ -869,11 +884,19 @@ static ssize_t fuse_fill_write_pages(struct fuse_req *req,
if (!fc->big_writes) if (!fc->big_writes)
break; break;
} while (iov_iter_count(ii) && count < fc->max_write && } while (iov_iter_count(ii) && count < fc->max_write &&
req->num_pages < FUSE_MAX_PAGES_PER_REQ && offset == 0); req->num_pages < req->max_pages && offset == 0);
return count > 0 ? count : err; return count > 0 ? count : err;
} }
static inline unsigned fuse_wr_pages(loff_t pos, size_t len)
{
return min_t(unsigned,
((pos + len - 1) >> PAGE_CACHE_SHIFT) -
(pos >> PAGE_CACHE_SHIFT) + 1,
FUSE_MAX_PAGES_PER_REQ);
}
static ssize_t fuse_perform_write(struct file *file, static ssize_t fuse_perform_write(struct file *file,
struct address_space *mapping, struct address_space *mapping,
struct iov_iter *ii, loff_t pos) struct iov_iter *ii, loff_t pos)
...@@ -889,8 +912,9 @@ static ssize_t fuse_perform_write(struct file *file, ...@@ -889,8 +912,9 @@ static ssize_t fuse_perform_write(struct file *file,
do { do {
struct fuse_req *req; struct fuse_req *req;
ssize_t count; ssize_t count;
unsigned nr_pages = fuse_wr_pages(pos, iov_iter_count(ii));
req = fuse_get_req(fc); req = fuse_get_req(fc, nr_pages);
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
break; break;
...@@ -1023,47 +1047,110 @@ static void fuse_release_user_pages(struct fuse_req *req, int write) ...@@ -1023,47 +1047,110 @@ static void fuse_release_user_pages(struct fuse_req *req, int write)
} }
} }
static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, static inline void fuse_page_descs_length_init(struct fuse_req *req,
unsigned index, unsigned nr_pages)
{
int i;
for (i = index; i < index + nr_pages; i++)
req->page_descs[i].length = PAGE_SIZE -
req->page_descs[i].offset;
}
static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
{
return (unsigned long)ii->iov->iov_base + ii->iov_offset;
}
static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
size_t max_size)
{
return min(iov_iter_single_seg_count(ii), max_size);
}
static int fuse_get_user_pages(struct fuse_req *req, struct iov_iter *ii,
size_t *nbytesp, int write) size_t *nbytesp, int write)
{ {
size_t nbytes = *nbytesp; size_t nbytes = 0; /* # bytes already packed in req */
unsigned long user_addr = (unsigned long) buf;
unsigned offset = user_addr & ~PAGE_MASK;
int npages;
/* Special case for kernel I/O: can copy directly into the buffer */ /* Special case for kernel I/O: can copy directly into the buffer */
if (segment_eq(get_fs(), KERNEL_DS)) { if (segment_eq(get_fs(), KERNEL_DS)) {
unsigned long user_addr = fuse_get_user_addr(ii);
size_t frag_size = fuse_get_frag_size(ii, *nbytesp);
if (write) if (write)
req->in.args[1].value = (void *) user_addr; req->in.args[1].value = (void *) user_addr;
else else
req->out.args[0].value = (void *) user_addr; req->out.args[0].value = (void *) user_addr;
iov_iter_advance(ii, frag_size);
*nbytesp = frag_size;
return 0; return 0;
} }
nbytes = min_t(size_t, nbytes, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); while (nbytes < *nbytesp && req->num_pages < req->max_pages) {
npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; unsigned npages;
npages = clamp(npages, 1, FUSE_MAX_PAGES_PER_REQ); unsigned long user_addr = fuse_get_user_addr(ii);
npages = get_user_pages_fast(user_addr, npages, !write, req->pages); unsigned offset = user_addr & ~PAGE_MASK;
if (npages < 0) size_t frag_size = fuse_get_frag_size(ii, *nbytesp - nbytes);
return npages; int ret;
unsigned n = req->max_pages - req->num_pages;
frag_size = min_t(size_t, frag_size, n << PAGE_SHIFT);
npages = (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
npages = clamp(npages, 1U, n);
ret = get_user_pages_fast(user_addr, npages, !write,
&req->pages[req->num_pages]);
if (ret < 0)
return ret;
npages = ret;
frag_size = min_t(size_t, frag_size,
(npages << PAGE_SHIFT) - offset);
iov_iter_advance(ii, frag_size);
req->page_descs[req->num_pages].offset = offset;
fuse_page_descs_length_init(req, req->num_pages, npages);
req->num_pages += npages;
req->page_descs[req->num_pages - 1].length -=
(npages << PAGE_SHIFT) - offset - frag_size;
req->num_pages = npages; nbytes += frag_size;
req->page_offset = offset; }
if (write) if (write)
req->in.argpages = 1; req->in.argpages = 1;
else else
req->out.argpages = 1; req->out.argpages = 1;
nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; *nbytesp = nbytes;
*nbytesp = min(*nbytesp, nbytes);
return 0; return 0;
} }
ssize_t fuse_direct_io(struct file *file, const char __user *buf, static inline int fuse_iter_npages(const struct iov_iter *ii_p)
size_t count, loff_t *ppos, int write) {
struct iov_iter ii = *ii_p;
int npages = 0;
while (iov_iter_count(&ii) && npages < FUSE_MAX_PAGES_PER_REQ) {
unsigned long user_addr = fuse_get_user_addr(&ii);
unsigned offset = user_addr & ~PAGE_MASK;
size_t frag_size = iov_iter_single_seg_count(&ii);
npages += (frag_size + offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
iov_iter_advance(&ii, frag_size);
}
return min(npages, FUSE_MAX_PAGES_PER_REQ);
}
ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
unsigned long nr_segs, size_t count, loff_t *ppos,
int write)
{ {
struct fuse_file *ff = file->private_data; struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fc; struct fuse_conn *fc = ff->fc;
...@@ -1071,8 +1158,11 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf, ...@@ -1071,8 +1158,11 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf,
loff_t pos = *ppos; loff_t pos = *ppos;
ssize_t res = 0; ssize_t res = 0;
struct fuse_req *req; struct fuse_req *req;
struct iov_iter ii;
req = fuse_get_req(fc); iov_iter_init(&ii, iov, nr_segs, count, 0);
req = fuse_get_req(fc, fuse_iter_npages(&ii));
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1080,7 +1170,7 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf, ...@@ -1080,7 +1170,7 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf,
size_t nres; size_t nres;
fl_owner_t owner = current->files; fl_owner_t owner = current->files;
size_t nbytes = min(count, nmax); size_t nbytes = min(count, nmax);
int err = fuse_get_user_pages(req, buf, &nbytes, write); int err = fuse_get_user_pages(req, &ii, &nbytes, write);
if (err) { if (err) {
res = err; res = err;
break; break;
...@@ -1103,12 +1193,11 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf, ...@@ -1103,12 +1193,11 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf,
count -= nres; count -= nres;
res += nres; res += nres;
pos += nres; pos += nres;
buf += nres;
if (nres != nbytes) if (nres != nbytes)
break; break;
if (count) { if (count) {
fuse_put_request(fc, req); fuse_put_request(fc, req);
req = fuse_get_req(fc); req = fuse_get_req(fc, fuse_iter_npages(&ii));
if (IS_ERR(req)) if (IS_ERR(req))
break; break;
} }
...@@ -1122,8 +1211,8 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf, ...@@ -1122,8 +1211,8 @@ ssize_t fuse_direct_io(struct file *file, const char __user *buf,
} }
EXPORT_SYMBOL_GPL(fuse_direct_io); EXPORT_SYMBOL_GPL(fuse_direct_io);
static ssize_t fuse_direct_read(struct file *file, char __user *buf, static ssize_t __fuse_direct_read(struct file *file, const struct iovec *iov,
size_t count, loff_t *ppos) unsigned long nr_segs, loff_t *ppos)
{ {
ssize_t res; ssize_t res;
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
...@@ -1131,22 +1220,31 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf, ...@@ -1131,22 +1220,31 @@ static ssize_t fuse_direct_read(struct file *file, char __user *buf,
if (is_bad_inode(inode)) if (is_bad_inode(inode))
return -EIO; return -EIO;
res = fuse_direct_io(file, buf, count, ppos, 0); res = fuse_direct_io(file, iov, nr_segs, iov_length(iov, nr_segs),
ppos, 0);
fuse_invalidate_attr(inode); fuse_invalidate_attr(inode);
return res; return res;
} }
static ssize_t __fuse_direct_write(struct file *file, const char __user *buf, static ssize_t fuse_direct_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{
struct iovec iov = { .iov_base = buf, .iov_len = count };
return __fuse_direct_read(file, &iov, 1, ppos);
}
static ssize_t __fuse_direct_write(struct file *file, const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos)
{ {
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
size_t count = iov_length(iov, nr_segs);
ssize_t res; ssize_t res;
res = generic_write_checks(file, ppos, &count, 0); res = generic_write_checks(file, ppos, &count, 0);
if (!res) { if (!res) {
res = fuse_direct_io(file, buf, count, ppos, 1); res = fuse_direct_io(file, iov, nr_segs, count, ppos, 1);
if (res > 0) if (res > 0)
fuse_write_update_size(inode, *ppos); fuse_write_update_size(inode, *ppos);
} }
...@@ -1159,6 +1257,7 @@ static ssize_t __fuse_direct_write(struct file *file, const char __user *buf, ...@@ -1159,6 +1257,7 @@ static ssize_t __fuse_direct_write(struct file *file, const char __user *buf,
static ssize_t fuse_direct_write(struct file *file, const char __user *buf, static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count };
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
ssize_t res; ssize_t res;
...@@ -1167,7 +1266,7 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, ...@@ -1167,7 +1266,7 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf,
/* Don't allow parallel writes to the same file */ /* Don't allow parallel writes to the same file */
mutex_lock(&inode->i_mutex); mutex_lock(&inode->i_mutex);
res = __fuse_direct_write(file, buf, count, ppos); res = __fuse_direct_write(file, &iov, 1, ppos);
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return res; return res;
...@@ -1272,7 +1371,7 @@ static int fuse_writepage_locked(struct page *page) ...@@ -1272,7 +1371,7 @@ static int fuse_writepage_locked(struct page *page)
set_page_writeback(page); set_page_writeback(page);
req = fuse_request_alloc_nofs(); req = fuse_request_alloc_nofs(1);
if (!req) if (!req)
goto err; goto err;
...@@ -1293,7 +1392,8 @@ static int fuse_writepage_locked(struct page *page) ...@@ -1293,7 +1392,8 @@ static int fuse_writepage_locked(struct page *page)
req->in.argpages = 1; req->in.argpages = 1;
req->num_pages = 1; req->num_pages = 1;
req->pages[0] = tmp_page; req->pages[0] = tmp_page;
req->page_offset = 0; req->page_descs[0].offset = 0;
req->page_descs[0].length = PAGE_SIZE;
req->end = fuse_writepage_end; req->end = fuse_writepage_end;
req->inode = inode; req->inode = inode;
...@@ -1471,7 +1571,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl) ...@@ -1471,7 +1571,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl)
struct fuse_lk_out outarg; struct fuse_lk_out outarg;
int err; int err;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1506,7 +1606,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) ...@@ -1506,7 +1606,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock)
if (fl->fl_flags & FL_CLOSE) if (fl->fl_flags & FL_CLOSE)
return 0; return 0;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -1575,7 +1675,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block) ...@@ -1575,7 +1675,7 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block)
if (!inode->i_sb->s_bdev || fc->no_bmap) if (!inode->i_sb->s_bdev || fc->no_bmap)
return 0; return 0;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return 0; return 0;
...@@ -1873,7 +1973,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -1873,7 +1973,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
num_pages++; num_pages++;
} }
req = fuse_get_req(fc); req = fuse_get_req(fc, num_pages);
if (IS_ERR(req)) { if (IS_ERR(req)) {
err = PTR_ERR(req); err = PTR_ERR(req);
req = NULL; req = NULL;
...@@ -1881,6 +1981,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, ...@@ -1881,6 +1981,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
} }
memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages); memcpy(req->pages, pages, sizeof(req->pages[0]) * num_pages);
req->num_pages = num_pages; req->num_pages = num_pages;
fuse_page_descs_length_init(req, 0, req->num_pages);
/* okay, let's send it to the client */ /* okay, let's send it to the client */
req->in.h.opcode = FUSE_IOCTL; req->in.h.opcode = FUSE_IOCTL;
...@@ -1981,7 +2082,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd, ...@@ -1981,7 +2082,7 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd,
struct inode *inode = file->f_dentry->d_inode; struct inode *inode = file->f_dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_conn *fc = get_fuse_conn(inode);
if (!fuse_allow_task(fc, current)) if (!fuse_allow_current_process(fc))
return -EACCES; return -EACCES;
if (is_bad_inode(inode)) if (is_bad_inode(inode))
...@@ -2066,6 +2167,7 @@ unsigned fuse_file_poll(struct file *file, poll_table *wait) ...@@ -2066,6 +2167,7 @@ unsigned fuse_file_poll(struct file *file, poll_table *wait)
return DEFAULT_POLLMASK; return DEFAULT_POLLMASK;
poll_wait(file, &ff->poll_wait, wait); poll_wait(file, &ff->poll_wait, wait);
inarg.events = (__u32)poll_requested_events(wait);
/* /*
* Ask for notification iff there's someone waiting for it. * Ask for notification iff there's someone waiting for it.
...@@ -2076,7 +2178,7 @@ unsigned fuse_file_poll(struct file *file, poll_table *wait) ...@@ -2076,7 +2178,7 @@ unsigned fuse_file_poll(struct file *file, poll_table *wait)
fuse_register_polled_file(fc, ff); fuse_register_polled_file(fc, ff);
} }
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return POLLERR; return POLLERR;
...@@ -2126,41 +2228,6 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc, ...@@ -2126,41 +2228,6 @@ int fuse_notify_poll_wakeup(struct fuse_conn *fc,
return 0; return 0;
} }
static ssize_t fuse_loop_dio(struct file *filp, const struct iovec *iov,
unsigned long nr_segs, loff_t *ppos, int rw)
{
const struct iovec *vector = iov;
ssize_t ret = 0;
while (nr_segs > 0) {
void __user *base;
size_t len;
ssize_t nr;
base = vector->iov_base;
len = vector->iov_len;
vector++;
nr_segs--;
if (rw == WRITE)
nr = __fuse_direct_write(filp, base, len, ppos);
else
nr = fuse_direct_read(filp, base, len, ppos);
if (nr < 0) {
if (!ret)
ret = nr;
break;
}
ret += nr;
if (nr != len)
break;
}
return ret;
}
static ssize_t static ssize_t
fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
loff_t offset, unsigned long nr_segs) loff_t offset, unsigned long nr_segs)
...@@ -2172,7 +2239,10 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, ...@@ -2172,7 +2239,10 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
file = iocb->ki_filp; file = iocb->ki_filp;
pos = offset; pos = offset;
ret = fuse_loop_dio(file, iov, nr_segs, &pos, rw); if (rw == WRITE)
ret = __fuse_direct_write(file, iov, nr_segs, &pos);
else
ret = __fuse_direct_read(file, iov, nr_segs, &pos);
return ret; return ret;
} }
...@@ -2194,7 +2264,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, ...@@ -2194,7 +2264,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
if (fc->no_fallocate) if (fc->no_fallocate)
return -EOPNOTSUPP; return -EOPNOTSUPP;
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
......
...@@ -44,6 +44,9 @@ ...@@ -44,6 +44,9 @@
doing the mount will be allowed to access the filesystem */ doing the mount will be allowed to access the filesystem */
#define FUSE_ALLOW_OTHER (1 << 1) #define FUSE_ALLOW_OTHER (1 << 1)
/** Number of page pointers embedded in fuse_req */
#define FUSE_REQ_INLINE_PAGES 1
/** List of active connections */ /** List of active connections */
extern struct list_head fuse_conn_list; extern struct list_head fuse_conn_list;
...@@ -103,6 +106,15 @@ struct fuse_inode { ...@@ -103,6 +106,15 @@ struct fuse_inode {
/** List of writepage requestst (pending or sent) */ /** List of writepage requestst (pending or sent) */
struct list_head writepages; struct list_head writepages;
/** Miscellaneous bits describing inode state */
unsigned long state;
};
/** FUSE inode state bits */
enum {
/** Advise readdirplus */
FUSE_I_ADVISE_RDPLUS,
}; };
struct fuse_conn; struct fuse_conn;
...@@ -200,6 +212,12 @@ struct fuse_out { ...@@ -200,6 +212,12 @@ struct fuse_out {
struct fuse_arg args[3]; struct fuse_arg args[3];
}; };
/** FUSE page descriptor */
struct fuse_page_desc {
unsigned int length;
unsigned int offset;
};
/** The request state */ /** The request state */
enum fuse_req_state { enum fuse_req_state {
FUSE_REQ_INIT = 0, FUSE_REQ_INIT = 0,
...@@ -291,14 +309,23 @@ struct fuse_req { ...@@ -291,14 +309,23 @@ struct fuse_req {
} misc; } misc;
/** page vector */ /** page vector */
struct page *pages[FUSE_MAX_PAGES_PER_REQ]; struct page **pages;
/** page-descriptor vector */
struct fuse_page_desc *page_descs;
/** size of the 'pages' array */
unsigned max_pages;
/** inline page vector */
struct page *inline_pages[FUSE_REQ_INLINE_PAGES];
/** inline page-descriptor vector */
struct fuse_page_desc inline_page_descs[FUSE_REQ_INLINE_PAGES];
/** number of pages in vector */ /** number of pages in vector */
unsigned num_pages; unsigned num_pages;
/** offset of data on first page */
unsigned page_offset;
/** File used in the request (or NULL) */ /** File used in the request (or NULL) */
struct fuse_file *ff; struct fuse_file *ff;
...@@ -487,6 +514,12 @@ struct fuse_conn { ...@@ -487,6 +514,12 @@ struct fuse_conn {
/** Use enhanced/automatic page cache invalidation. */ /** Use enhanced/automatic page cache invalidation. */
unsigned auto_inval_data:1; unsigned auto_inval_data:1;
/** Does the filesystem support readdirplus? */
unsigned do_readdirplus:1;
/** Does the filesystem want adaptive readdirplus? */
unsigned readdirplus_auto:1;
/** The number of requests waiting for completion */ /** The number of requests waiting for completion */
atomic_t num_waiting; atomic_t num_waiting;
...@@ -578,6 +611,9 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, ...@@ -578,6 +611,9 @@ void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget,
struct fuse_forget_link *fuse_alloc_forget(void); struct fuse_forget_link *fuse_alloc_forget(void);
/* Used by READDIRPLUS */
void fuse_force_forget(struct file *file, u64 nodeid);
/** /**
* Initialize READ or READDIR request * Initialize READ or READDIR request
*/ */
...@@ -658,9 +694,9 @@ void fuse_ctl_cleanup(void); ...@@ -658,9 +694,9 @@ void fuse_ctl_cleanup(void);
/** /**
* Allocate a request * Allocate a request
*/ */
struct fuse_req *fuse_request_alloc(void); struct fuse_req *fuse_request_alloc(unsigned npages);
struct fuse_req *fuse_request_alloc_nofs(void); struct fuse_req *fuse_request_alloc_nofs(unsigned npages);
/** /**
* Free a request * Free a request
...@@ -668,14 +704,25 @@ struct fuse_req *fuse_request_alloc_nofs(void); ...@@ -668,14 +704,25 @@ struct fuse_req *fuse_request_alloc_nofs(void);
void fuse_request_free(struct fuse_req *req); void fuse_request_free(struct fuse_req *req);
/** /**
* Get a request, may fail with -ENOMEM * Get a request, may fail with -ENOMEM,
* caller should specify # elements in req->pages[] explicitly
*/ */
struct fuse_req *fuse_get_req(struct fuse_conn *fc); struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages);
/**
* Get a request, may fail with -ENOMEM,
* useful for callers who doesn't use req->pages[]
*/
static inline struct fuse_req *fuse_get_req_nopages(struct fuse_conn *fc)
{
return fuse_get_req(fc, 0);
}
/** /**
* Gets a requests for a file operation, always succeeds * Gets a requests for a file operation, always succeeds
*/ */
struct fuse_req *fuse_get_req_nofail(struct fuse_conn *fc, struct file *file); struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc,
struct file *file);
/** /**
* Decrement reference count of a request. If count goes to zero free * Decrement reference count of a request. If count goes to zero free
...@@ -739,9 +786,9 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc); ...@@ -739,9 +786,9 @@ void fuse_ctl_remove_conn(struct fuse_conn *fc);
int fuse_valid_type(int m); int fuse_valid_type(int m);
/** /**
* Is task allowed to perform filesystem operation? * Is current process allowed to perform filesystem operation?
*/ */
int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task); int fuse_allow_current_process(struct fuse_conn *fc);
u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id); u64 fuse_lock_owner_id(struct fuse_conn *fc, fl_owner_t id);
...@@ -776,8 +823,9 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, ...@@ -776,8 +823,9 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file, int fuse_do_open(struct fuse_conn *fc, u64 nodeid, struct file *file,
bool isdir); bool isdir);
ssize_t fuse_direct_io(struct file *file, const char __user *buf, ssize_t fuse_direct_io(struct file *file, const struct iovec *iov,
size_t count, loff_t *ppos, int write); unsigned long nr_segs, size_t count, loff_t *ppos,
int write);
long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
unsigned int flags); unsigned int flags);
long fuse_ioctl_common(struct file *file, unsigned int cmd, long fuse_ioctl_common(struct file *file, unsigned int cmd,
......
...@@ -92,6 +92,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb) ...@@ -92,6 +92,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
fi->attr_version = 0; fi->attr_version = 0;
fi->writectr = 0; fi->writectr = 0;
fi->orig_ino = 0; fi->orig_ino = 0;
fi->state = 0;
INIT_LIST_HEAD(&fi->write_files); INIT_LIST_HEAD(&fi->write_files);
INIT_LIST_HEAD(&fi->queued_writes); INIT_LIST_HEAD(&fi->queued_writes);
INIT_LIST_HEAD(&fi->writepages); INIT_LIST_HEAD(&fi->writepages);
...@@ -408,12 +409,12 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -408,12 +409,12 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
struct fuse_statfs_out outarg; struct fuse_statfs_out outarg;
int err; int err;
if (!fuse_allow_task(fc, current)) { if (!fuse_allow_current_process(fc)) {
buf->f_type = FUSE_SUPER_MAGIC; buf->f_type = FUSE_SUPER_MAGIC;
return 0; return 0;
} }
req = fuse_get_req(fc); req = fuse_get_req_nopages(fc);
if (IS_ERR(req)) if (IS_ERR(req))
return PTR_ERR(req); return PTR_ERR(req);
...@@ -863,6 +864,10 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) ...@@ -863,6 +864,10 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
fc->dont_mask = 1; fc->dont_mask = 1;
if (arg->flags & FUSE_AUTO_INVAL_DATA) if (arg->flags & FUSE_AUTO_INVAL_DATA)
fc->auto_inval_data = 1; fc->auto_inval_data = 1;
if (arg->flags & FUSE_DO_READDIRPLUS)
fc->do_readdirplus = 1;
if (arg->flags & FUSE_READDIRPLUS_AUTO)
fc->readdirplus_auto = 1;
} else { } else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE; ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1; fc->no_lock = 1;
...@@ -889,7 +894,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) ...@@ -889,7 +894,8 @@ static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC | arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK | FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ | FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA; FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA |
FUSE_DO_READDIRPLUS | FUSE_READDIRPLUS_AUTO;
req->in.h.opcode = FUSE_INIT; req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1; req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg); req->in.args[0].size = sizeof(*arg);
...@@ -1034,12 +1040,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1034,12 +1040,12 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
/* only now - we want root dentry with NULL ->d_op */ /* only now - we want root dentry with NULL ->d_op */
sb->s_d_op = &fuse_dentry_operations; sb->s_d_op = &fuse_dentry_operations;
init_req = fuse_request_alloc(); init_req = fuse_request_alloc(0);
if (!init_req) if (!init_req)
goto err_put_root; goto err_put_root;
if (is_bdev) { if (is_bdev) {
fc->destroy_req = fuse_request_alloc(); fc->destroy_req = fuse_request_alloc(0);
if (!fc->destroy_req) if (!fc->destroy_req)
goto err_free_init_req; goto err_free_init_req;
} }
......
...@@ -301,7 +301,7 @@ size_t iov_iter_copy_from_user(struct page *page, ...@@ -301,7 +301,7 @@ size_t iov_iter_copy_from_user(struct page *page,
struct iov_iter *i, unsigned long offset, size_t bytes); struct iov_iter *i, unsigned long offset, size_t bytes);
void iov_iter_advance(struct iov_iter *i, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes);
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
size_t iov_iter_single_seg_count(struct iov_iter *i); size_t iov_iter_single_seg_count(const struct iov_iter *i);
static inline void iov_iter_init(struct iov_iter *i, static inline void iov_iter_init(struct iov_iter *i,
const struct iovec *iov, unsigned long nr_segs, const struct iovec *iov, unsigned long nr_segs,
......
/* /*
FUSE: Filesystem in Userspace This file defines the kernel interface of FUSE
Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu> Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL. This program can be distributed under the terms of the GNU GPL.
See the file COPYING. See the file COPYING.
This -- and only this -- header file may also be distributed under
the terms of the BSD Licence as follows:
Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
*/ */
/* /*
...@@ -60,12 +86,25 @@ ...@@ -60,12 +86,25 @@
* *
* 7.20 * 7.20
* - add FUSE_AUTO_INVAL_DATA * - add FUSE_AUTO_INVAL_DATA
*
* 7.21
* - add FUSE_READDIRPLUS
* - send the requested events in POLL request
*/ */
#ifndef _LINUX_FUSE_H #ifndef _LINUX_FUSE_H
#define _LINUX_FUSE_H #define _LINUX_FUSE_H
#ifdef __linux__
#include <linux/types.h> #include <linux/types.h>
#else
#include <stdint.h>
#define __u64 uint64_t
#define __s64 int64_t
#define __u32 uint32_t
#define __s32 int32_t
#define __u16 uint16_t
#endif
/* /*
* Version negotiation: * Version negotiation:
...@@ -91,7 +130,7 @@ ...@@ -91,7 +130,7 @@
#define FUSE_KERNEL_VERSION 7 #define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */ /** Minor version number of this interface */
#define FUSE_KERNEL_MINOR_VERSION 20 #define FUSE_KERNEL_MINOR_VERSION 21
/** The node ID of the root inode */ /** The node ID of the root inode */
#define FUSE_ROOT_ID 1 #define FUSE_ROOT_ID 1
...@@ -179,6 +218,8 @@ struct fuse_file_lock { ...@@ -179,6 +218,8 @@ struct fuse_file_lock {
* FUSE_FLOCK_LOCKS: remote locking for BSD style file locks * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
* FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories
* FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages
* FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one)
* FUSE_READDIRPLUS_AUTO: adaptive readdirplus
*/ */
#define FUSE_ASYNC_READ (1 << 0) #define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1) #define FUSE_POSIX_LOCKS (1 << 1)
...@@ -193,6 +234,8 @@ struct fuse_file_lock { ...@@ -193,6 +234,8 @@ struct fuse_file_lock {
#define FUSE_FLOCK_LOCKS (1 << 10) #define FUSE_FLOCK_LOCKS (1 << 10)
#define FUSE_HAS_IOCTL_DIR (1 << 11) #define FUSE_HAS_IOCTL_DIR (1 << 11)
#define FUSE_AUTO_INVAL_DATA (1 << 12) #define FUSE_AUTO_INVAL_DATA (1 << 12)
#define FUSE_DO_READDIRPLUS (1 << 13)
#define FUSE_READDIRPLUS_AUTO (1 << 14)
/** /**
* CUSE INIT request/reply flags * CUSE INIT request/reply flags
...@@ -299,6 +342,7 @@ enum fuse_opcode { ...@@ -299,6 +342,7 @@ enum fuse_opcode {
FUSE_NOTIFY_REPLY = 41, FUSE_NOTIFY_REPLY = 41,
FUSE_BATCH_FORGET = 42, FUSE_BATCH_FORGET = 42,
FUSE_FALLOCATE = 43, FUSE_FALLOCATE = 43,
FUSE_READDIRPLUS = 44,
/* CUSE specific operations */ /* CUSE specific operations */
CUSE_INIT = 4096, CUSE_INIT = 4096,
...@@ -580,7 +624,7 @@ struct fuse_poll_in { ...@@ -580,7 +624,7 @@ struct fuse_poll_in {
__u64 fh; __u64 fh;
__u64 kh; __u64 kh;
__u32 flags; __u32 flags;
__u32 padding; __u32 events;
}; };
struct fuse_poll_out { struct fuse_poll_out {
...@@ -630,6 +674,16 @@ struct fuse_dirent { ...@@ -630,6 +674,16 @@ struct fuse_dirent {
#define FUSE_DIRENT_SIZE(d) \ #define FUSE_DIRENT_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
struct fuse_direntplus {
struct fuse_entry_out entry_out;
struct fuse_dirent dirent;
};
#define FUSE_NAME_OFFSET_DIRENTPLUS \
offsetof(struct fuse_direntplus, dirent.name)
#define FUSE_DIRENTPLUS_SIZE(d) \
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
struct fuse_notify_inval_inode_out { struct fuse_notify_inval_inode_out {
__u64 ino; __u64 ino;
__s64 off; __s64 off;
......
...@@ -2056,7 +2056,7 @@ EXPORT_SYMBOL(iov_iter_fault_in_readable); ...@@ -2056,7 +2056,7 @@ EXPORT_SYMBOL(iov_iter_fault_in_readable);
/* /*
* Return the count of just the current iov_iter segment. * Return the count of just the current iov_iter segment.
*/ */
size_t iov_iter_single_seg_count(struct iov_iter *i) size_t iov_iter_single_seg_count(const struct iov_iter *i)
{ {
const struct iovec *iov = i->iov; const struct iovec *iov = i->iov;
if (i->nr_segs == 1) if (i->nr_segs == 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment