aboutsummaryrefslogtreecommitdiff
path: root/fs/fuse
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fuse')
-rw-r--r--fs/fuse/dev.c68
-rw-r--r--fs/fuse/dir.c154
-rw-r--r--fs/fuse/file.c136
-rw-r--r--fs/fuse/fuse_i.h30
-rw-r--r--fs/fuse/inode.c52
5 files changed, 262 insertions, 178 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 3ad22beb24c..db534bcde45 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -129,7 +129,7 @@ static struct fuse_req *get_reserved_req(struct fuse_conn *fc,
struct fuse_file *ff = file->private_data;
do {
- wait_event(fc->blocked_waitq, ff->reserved_req);
+ wait_event(fc->reserved_req_waitq, ff->reserved_req);
spin_lock(&fc->lock);
if (ff->reserved_req) {
req = ff->reserved_req;
@@ -155,7 +155,7 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req)
fuse_request_init(req);
BUG_ON(ff->reserved_req);
ff->reserved_req = req;
- wake_up(&fc->blocked_waitq);
+ wake_up_all(&fc->reserved_req_waitq);
spin_unlock(&fc->lock);
fput(file);
}
@@ -224,13 +224,13 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
fc->blocked = 0;
wake_up_all(&fc->blocked_waitq);
}
+ if (fc->num_background == FUSE_CONGESTION_THRESHOLD) {
+ clear_bdi_congested(&fc->bdi, READ);
+ clear_bdi_congested(&fc->bdi, WRITE);
+ }
fc->num_background--;
}
spin_unlock(&fc->lock);
- dput(req->dentry);
- mntput(req->vfsmount);
- if (req->file)
- fput(req->file);
wake_up(&req->waitq);
if (end)
end(fc, req);
@@ -273,28 +273,41 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
queue_interrupt(fc, req);
}
- if (req->force) {
- spin_unlock(&fc->lock);
- wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
- spin_lock(&fc->lock);
- } else {
+ if (!req->force) {
sigset_t oldset;
/* Only fatal signals may interrupt this */
block_sigs(&oldset);
wait_answer_interruptible(fc, req);
restore_sigs(&oldset);
+
+ if (req->aborted)
+ goto aborted;
+ if (req->state == FUSE_REQ_FINISHED)
+ return;
+
+ /* Request is not yet in userspace, bail out */
+ if (req->state == FUSE_REQ_PENDING) {
+ list_del(&req->list);
+ __fuse_put_request(req);
+ req->out.h.error = -EINTR;
+ return;
+ }
}
- if (req->aborted)
- goto aborted;
- if (req->state == FUSE_REQ_FINISHED)
- return;
+ /*
+ * Either request is already in userspace, or it was forced.
+ * Wait it out.
+ */
+ spin_unlock(&fc->lock);
+ wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
+ spin_lock(&fc->lock);
- req->out.h.error = -EINTR;
- req->aborted = 1;
+ if (!req->aborted)
+ return;
aborted:
+ BUG_ON(req->state != FUSE_REQ_FINISHED);
if (req->locked) {
/* This is uninterruptible sleep, because data is
being copied to/from the buffers of req. During
@@ -305,14 +318,6 @@ static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
wait_event(req->waitq, !req->locked);
spin_lock(&fc->lock);
}
- if (req->state == FUSE_REQ_PENDING) {
- list_del(&req->list);
- __fuse_put_request(req);
- } else if (req->state == FUSE_REQ_SENT) {
- spin_unlock(&fc->lock);
- wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
- spin_lock(&fc->lock);
- }
}
static unsigned len_args(unsigned numargs, struct fuse_arg *args)
@@ -378,6 +383,10 @@ static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
fc->num_background++;
if (fc->num_background == FUSE_MAX_BACKGROUND)
fc->blocked = 1;
+ if (fc->num_background == FUSE_CONGESTION_THRESHOLD) {
+ set_bdi_congested(&fc->bdi, READ);
+ set_bdi_congested(&fc->bdi, WRITE);
+ }
queue_request(fc, req);
spin_unlock(&fc->lock);
@@ -738,11 +747,12 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,
fuse_copy_finish(&cs);
spin_lock(&fc->lock);
req->locked = 0;
- if (!err && req->aborted)
- err = -ENOENT;
+ if (req->aborted) {
+ request_end(fc, req);
+ return -ENODEV;
+ }
if (err) {
- if (!req->aborted)
- req->out.h.error = -EIO;
+ req->out.h.error = -EIO;
request_end(fc, req);
return err;
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index bd5a772d8cc..d1acab93133 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -288,12 +288,11 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
static void fuse_sync_release(struct fuse_conn *fc, struct fuse_file *ff,
u64 nodeid, int flags)
{
- struct fuse_req *req;
-
- req = fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE);
- req->force = 1;
- request_send(fc, req);
- fuse_put_request(fc, req);
+ fuse_release_fill(ff, nodeid, flags, FUSE_RELEASE);
+ ff->reserved_req->force = 1;
+ request_send(fc, ff->reserved_req);
+ fuse_put_request(fc, ff->reserved_req);
+ kfree(ff);
}
/*
@@ -664,7 +663,7 @@ static int fuse_link(struct dentry *entry, struct inode *newdir,
return err;
}
-int fuse_do_getattr(struct inode *inode)
+static int fuse_do_getattr(struct inode *inode)
{
int err;
struct fuse_attr_out arg;
@@ -696,6 +695,20 @@ int fuse_do_getattr(struct inode *inode)
}
/*
+ * Check if attributes are still valid, and if not send a GETATTR
+ * request to refresh them.
+ */
+static int fuse_refresh_attributes(struct inode *inode)
+{
+ struct fuse_inode *fi = get_fuse_inode(inode);
+
+ if (fi->i_time < get_jiffies_64())
+ return fuse_do_getattr(inode);
+ else
+ return 0;
+}
+
+/*
* Calling into a user-controlled filesystem gives the filesystem
* daemon ptrace-like capabilities over the requester process. This
* means, that the filesystem daemon is able to record the exact
@@ -724,30 +737,6 @@ static int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)
return 0;
}
-/*
- * Check whether the inode attributes are still valid
- *
- * If the attribute validity timeout has expired, then fetch the fresh
- * attributes with a 'getattr' request
- *
- * I'm not sure why cached attributes are never returned for the root
- * inode, this is probably being too cautious.
- */
-static int fuse_revalidate(struct dentry *entry)
-{
- struct inode *inode = entry->d_inode;
- struct fuse_inode *fi = get_fuse_inode(inode);
- struct fuse_conn *fc = get_fuse_conn(inode);
-
- if (!fuse_allow_task(fc, current))
- return -EACCES;
- if (get_node_id(inode) != FUSE_ROOT_ID &&
- fi->i_time >= get_jiffies_64())
- return 0;
-
- return fuse_do_getattr(inode);
-}
-
static int fuse_access(struct inode *inode, int mask)
{
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -795,16 +784,31 @@ static int fuse_access(struct inode *inode, int mask)
static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
{
struct fuse_conn *fc = get_fuse_conn(inode);
+ bool refreshed = false;
+ int err = 0;
if (!fuse_allow_task(fc, current))
return -EACCES;
- else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
+
+ /*
+ * If attributes are needed, refresh them before proceeding
+ */
+ if ((fc->flags & FUSE_DEFAULT_PERMISSIONS) ||
+ ((mask & MAY_EXEC) && S_ISREG(inode->i_mode))) {
+ err = fuse_refresh_attributes(inode);
+ if (err)
+ return err;
+
+ refreshed = true;
+ }
+
+ if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
int err = generic_permission(inode, mask, NULL);
/* If permission is denied, try to refresh file
attributes. This is also needed, because the root
node will at first have no permissions */
- if (err == -EACCES) {
+ if (err == -EACCES && !refreshed) {
err = fuse_do_getattr(inode);
if (!err)
err = generic_permission(inode, mask, NULL);
@@ -814,17 +818,19 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd)
exist. So if permissions are revoked this won't be
noticed immediately, only after the attribute
timeout has expired */
-
- return err;
- } else {
- int mode = inode->i_mode;
- if ((mask & MAY_EXEC) && !S_ISDIR(mode) && !(mode & S_IXUGO))
- return -EACCES;
-
- if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR)))
- return fuse_access(inode, mask);
- return 0;
+ } else if (nd && (nd->flags & (LOOKUP_ACCESS | LOOKUP_CHDIR))) {
+ err = fuse_access(inode, mask);
+ } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
+ if (!(inode->i_mode & S_IXUGO)) {
+ if (refreshed)
+ return -EACCES;
+
+ err = fuse_do_getattr(inode);
+ if (!err && !(inode->i_mode & S_IXUGO))
+ return -EACCES;
+ }
}
+ return err;
}
static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
@@ -859,6 +865,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
struct page *page;
struct inode *inode = file->f_path.dentry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
+ struct fuse_file *ff = file->private_data;
struct fuse_req *req;
if (is_bad_inode(inode))
@@ -875,7 +882,7 @@ static int fuse_readdir(struct file *file, void *dstbuf, filldir_t filldir)
}
req->num_pages = 1;
req->pages[0] = page;
- fuse_read_fill(req, file, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
+ fuse_read_fill(req, ff, inode, file->f_pos, PAGE_SIZE, FUSE_READDIR);
request_send(fc, req);
nbytes = req->out.args[0].size;
err = req->out.h.error;
@@ -980,23 +987,6 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
}
}
-static void fuse_vmtruncate(struct inode *inode, loff_t offset)
-{
- struct fuse_conn *fc = get_fuse_conn(inode);
- int need_trunc;
-
- spin_lock(&fc->lock);
- need_trunc = inode->i_size > offset;
- i_size_write(inode, offset);
- spin_unlock(&fc->lock);
-
- if (need_trunc) {
- struct address_space *mapping = inode->i_mapping;
- unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
- truncate_inode_pages(mapping, offset);
- }
-}
-
/*
* Set attributes, and at the same time refresh them.
*
@@ -1014,7 +1004,6 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
struct fuse_setattr_in inarg;
struct fuse_attr_out outarg;
int err;
- int is_truncate = 0;
if (fc->flags & FUSE_DEFAULT_PERMISSIONS) {
err = inode_change_ok(inode, attr);
@@ -1024,7 +1013,6 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
if (attr->ia_valid & ATTR_SIZE) {
unsigned long limit;
- is_truncate = 1;
if (IS_SWAPFILE(inode))
return -ETXTBSY;
limit = current->signal->rlim[RLIMIT_FSIZE].rlim_cur;
@@ -1051,30 +1039,38 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr)
request_send(fc, req);
err = req->out.h.error;
fuse_put_request(fc, req);
- if (!err) {
- if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
- make_bad_inode(inode);
- err = -EIO;
- } else {
- if (is_truncate)
- fuse_vmtruncate(inode, outarg.attr.size);
- fuse_change_attributes(inode, &outarg.attr);
- fi->i_time = time_to_jiffies(outarg.attr_valid,
- outarg.attr_valid_nsec);
- }
- } else if (err == -EINTR)
- fuse_invalidate_attr(inode);
+ if (err) {
+ if (err == -EINTR)
+ fuse_invalidate_attr(inode);
+ return err;
+ }
- return err;
+ if ((inode->i_mode ^ outarg.attr.mode) & S_IFMT) {
+ make_bad_inode(inode);
+ return -EIO;
+ }
+
+ fuse_change_attributes(inode, &outarg.attr);
+ fi->i_time = time_to_jiffies(outarg.attr_valid, outarg.attr_valid_nsec);
+ return 0;
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
struct kstat *stat)
{
struct inode *inode = entry->d_inode;
- int err = fuse_revalidate(entry);
- if (!err)
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ struct fuse_conn *fc = get_fuse_conn(inode);
+ int err;
+
+ if (!fuse_allow_task(fc, current))
+ return -EACCES;
+
+ err = fuse_refresh_attributes(inode);
+ if (!err) {
generic_fillattr(inode, stat);
+ stat->mode = fi->orig_i_mode;
+ }
return err;
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index f79de7c8cdf..c4b98c03a46 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -54,6 +54,7 @@ struct fuse_file *fuse_file_alloc(void)
kfree(ff);
ff = NULL;
}
+ atomic_set(&ff->count, 0);
}
return ff;
}
@@ -64,15 +65,39 @@ void fuse_file_free(struct fuse_file *ff)
kfree(ff);
}
+static struct fuse_file *fuse_file_get(struct fuse_file *ff)
+{
+ atomic_inc(&ff->count);
+ return ff;
+}
+
+static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req)
+{
+ dput(req->dentry);
+ mntput(req->vfsmount);
+ fuse_put_request(fc, req);
+}
+
+static void fuse_file_put(struct fuse_file *ff)
+{
+ if (atomic_dec_and_test(&ff->count)) {
+ struct fuse_req *req = ff->reserved_req;
+ struct fuse_conn *fc = get_fuse_conn(req->dentry->d_inode);
+ req->end = fuse_release_end;
+ request_send_background(fc, req);
+ kfree(ff);
+ }
+}
+
void fuse_finish_open(struct inode *inode, struct file *file,
struct fuse_file *ff, struct fuse_open_out *outarg)
{
if (outarg->open_flags & FOPEN_DIRECT_IO)
file->f_op = &fuse_direct_io_file_operations;
if (!(outarg->open_flags & FOPEN_KEEP_CACHE))
- invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ invalidate_inode_pages2(inode->i_mapping);
ff->fh = outarg->fh;
- file->private_data = ff;
+ file->private_data = fuse_file_get(ff);
}
int fuse_open_common(struct inode *inode, struct file *file, int isdir)
@@ -89,14 +114,6 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
if (err)
return err;
- /* If opening the root node, no lookup has been performed on
- it, so the attributes must be refreshed */
- if (get_node_id(inode) == FUSE_ROOT_ID) {
- err = fuse_do_getattr(inode);
- if (err)
- return err;
- }
-
ff = fuse_file_alloc();
if (!ff)
return -ENOMEM;
@@ -113,8 +130,7 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir)
return err;
}
-struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags,
- int opcode)
+void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode)
{
struct fuse_req *req = ff->reserved_req;
struct fuse_release_in *inarg = &req->misc.release_in;
@@ -126,25 +142,24 @@ struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags,
req->in.numargs = 1;
req->in.args[0].size = sizeof(struct fuse_release_in);
req->in.args[0].value = inarg;
- kfree(ff);
-
- return req;
}
int fuse_release_common(struct inode *inode, struct file *file, int isdir)
{
struct fuse_file *ff = file->private_data;
if (ff) {
- struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_req *req;
-
- req = fuse_release_fill(ff, get_node_id(inode), file->f_flags,
- isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
+ fuse_release_fill(ff, get_node_id(inode), file->f_flags,
+ isdir ? FUSE_RELEASEDIR : FUSE_RELEASE);
/* Hold vfsmount and dentry until release is finished */
- req->vfsmount = mntget(file->f_path.mnt);
- req->dentry = dget(file->f_path.dentry);
- request_send_background(fc, req);
+ ff->reserved_req->vfsmount = mntget(file->f_path.mnt);
+ ff->reserved_req->dentry = dget(file->f_path.dentry);
+ /*
+ * Normally this will send the RELEASE request,
+ * however if some asynchronous READ or WRITE requests
+ * are outstanding, the sending will be delayed
+ */
+ fuse_file_put(ff);
}
/* Return value is ignored by VFS */
@@ -264,10 +279,9 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync)
return fuse_fsync_common(file, de, datasync, 0);
}
-void fuse_read_fill(struct fuse_req *req, struct file *file,
+void fuse_read_fill(struct fuse_req *req, struct fuse_file *ff,
struct inode *inode, loff_t pos, size_t count, int opcode)
{
- struct fuse_file *ff = file->private_data;
struct fuse_read_in *inarg = &req->misc.read_in;
inarg->fh = ff->fh;
@@ -288,7 +302,8 @@ static size_t fuse_send_read(struct fuse_req *req, struct file *file,
struct inode *inode, loff_t pos, size_t count)
{
struct fuse_conn *fc = get_fuse_conn(inode);
- fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+ struct fuse_file *ff = file->private_data;
+ fuse_read_fill(req, ff, inode, pos, count, FUSE_READ);
request_send(fc, req);
return req->out.args[0].size;
}
@@ -337,20 +352,21 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req)
SetPageError(page);
unlock_page(page);
}
+ if (req->ff)
+ fuse_file_put(req->ff);
fuse_put_request(fc, req);
}
-static void fuse_send_readpages(struct fuse_req *req, struct file *file,
+static void fuse_send_readpages(struct fuse_req *req, struct fuse_file *ff,
struct inode *inode)
{
struct fuse_conn *fc = get_fuse_conn(inode);
loff_t pos = page_offset(req->pages[0]);
size_t count = req->num_pages << PAGE_CACHE_SHIFT;
req->out.page_zeroing = 1;
- fuse_read_fill(req, file, inode, pos, count, FUSE_READ);
+ fuse_read_fill(req, ff, inode, pos, count, FUSE_READ);
if (fc->async_read) {
- get_file(file);
- req->file = file;
+ req->ff = fuse_file_get(ff);
req->end = fuse_readpages_end;
request_send_background(fc, req);
} else {
@@ -359,15 +375,15 @@ static void fuse_send_readpages(struct fuse_req *req, struct file *file,
}
}
-struct fuse_readpages_data {
+struct fuse_fill_data {
struct fuse_req *req;
- struct file *file;
+ struct fuse_file *ff;
struct inode *inode;
};
static int fuse_readpages_fill(void *_data, struct page *page)
{
- struct fuse_readpages_data *data = _data;
+ struct fuse_fill_data *data = _data;
struct fuse_req *req = data->req;
struct inode *inode = data->inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -376,7 +392,7 @@ static int fuse_readpages_fill(void *_data, struct page *page)
(req->num_pages == FUSE_MAX_PAGES_PER_REQ ||
(req->num_pages + 1) * PAGE_CACHE_SIZE > fc->max_read ||
req->pages[req->num_pages - 1]->index + 1 != page->index)) {
- fuse_send_readpages(req, data->file, inode);
+ fuse_send_readpages(req, data->ff, inode);
data->req = req = fuse_get_req(fc);
if (IS_ERR(req)) {
unlock_page(page);
@@ -393,14 +409,14 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
{
struct inode *inode = mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
- struct fuse_readpages_data data;
+ struct fuse_fill_data data;
int err;
err = -EIO;
if (is_bad_inode(inode))
goto out;
- data.file = file;
+ data.ff = file->private_data;
data.inode = inode;
data.req = fuse_get_req(fc);
err = PTR_ERR(data.req);
@@ -410,7 +426,7 @@ static int fuse_readpages(struct file *file, struct address_space *mapping,
err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data);
if (!err) {
if (data.req->num_pages)
- fuse_send_readpages(data.req, file, inode);
+ fuse_send_readpages(data.req, data.ff, inode);
else
fuse_put_request(fc, data.req);
}
@@ -444,22 +460,25 @@ static size_t fuse_send_write(struct fuse_req *req, struct file *file,
return outarg.size;
}
-static int fuse_prepare_write(struct file *file, struct page *page,
- unsigned offset, unsigned to)
+static int fuse_write_begin(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned flags,
+ struct page **pagep, void **fsdata)
{
- /* No op */
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+ *pagep = __grab_cache_page(mapping, index);
+ if (!*pagep)
+ return -ENOMEM;
return 0;
}
-static int fuse_commit_write(struct file *file, struct page *page,
- unsigned offset, unsigned to)
+static int fuse_buffered_write(struct file *file, struct inode *inode,
+ loff_t pos, unsigned count, struct page *page)
{
int err;
size_t nres;
- unsigned count = to - offset;
- struct inode *inode = page->mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
- loff_t pos = page_offset(page) + offset;
+ unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
struct fuse_req *req;
if (is_bad_inode(inode))
@@ -475,20 +494,35 @@ static int fuse_commit_write(struct file *file, struct page *page,
nres = fuse_send_write(req, file, inode, pos, count);
err = req->out.h.error;
fuse_put_request(fc, req);
- if (!err && nres != count)
+ if (!err && !nres)
err = -EIO;
if (!err) {
- pos += count;
+ pos += nres;
spin_lock(&fc->lock);
if (pos > inode->i_size)
i_size_write(inode, pos);
spin_unlock(&fc->lock);
- if (offset == 0 && to == PAGE_CACHE_SIZE)
+ if (count == PAGE_CACHE_SIZE)
SetPageUptodate(page);
}
fuse_invalidate_attr(inode);
- return err;
+ return err ? err : nres;
+}
+
+static int fuse_write_end(struct file *file, struct address_space *mapping,
+ loff_t pos, unsigned len, unsigned copied,
+ struct page *page, void *fsdata)
+{
+ struct inode *inode = mapping->host;
+ int res = 0;
+
+ if (copied)
+ res = fuse_buffered_write(file, inode, pos, copied, page);
+
+ unlock_page(page);
+ page_cache_release(page);
+ return res;
}
static void fuse_release_user_pages(struct fuse_req *req, int write)
@@ -819,8 +853,8 @@ static const struct file_operations fuse_direct_io_file_operations = {
static const struct address_space_operations fuse_file_aops = {
.readpage = fuse_readpage,
- .prepare_write = fuse_prepare_write,
- .commit_write = fuse_commit_write,
+ .write_begin = fuse_write_begin,
+ .write_end = fuse_write_end,
.readpages = fuse_readpages,
.set_page_dirty = fuse_set_page_dirty,
.bmap = fuse_bmap,
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 68ae87cbafa..1764506fdd1 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -20,7 +20,10 @@
#define FUSE_MAX_PAGES_PER_REQ 32
/** Maximum number of outstanding background requests */
-#define FUSE_MAX_BACKGROUND 10
+#define FUSE_MAX_BACKGROUND 12
+
+/** Congestion starts at 75% of maximum */
+#define FUSE_CONGESTION_THRESHOLD (FUSE_MAX_BACKGROUND * 75 / 100)
/** It could be as large as PATH_MAX, but would that have any uses? */
#define FUSE_NAME_MAX 1024
@@ -60,6 +63,10 @@ struct fuse_inode {
/** Time in jiffies until the file attributes are valid */
u64 i_time;
+
+ /** The sticky bit in inode->i_mode may have been removed, so
+ preserve the original mode */
+ mode_t orig_i_mode;
};
/** FUSE specific file data */
@@ -69,6 +76,9 @@ struct fuse_file {
/** File handle used by userspace */
u64 fh;
+
+ /** Refcount */
+ atomic_t count;
};
/** One input argument of a request */
@@ -213,7 +223,7 @@ struct fuse_req {
unsigned page_offset;
/** File used in the request (or NULL) */
- struct file *file;
+ struct fuse_file *ff;
/** vfsmount used in release */
struct vfsmount *vfsmount;
@@ -286,6 +296,9 @@ struct fuse_conn {
/** waitq for blocked connection */
wait_queue_head_t blocked_waitq;
+ /** waitq for reserved requests */
+ wait_queue_head_t reserved_req_waitq;
+
/** The next unique request id */
u64 reqctr;
@@ -414,7 +427,7 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req,
/**
* Initialize READ or READDIR request
*/
-void fuse_read_fill(struct fuse_req *req, struct file *file,
+void fuse_read_fill(struct fuse_req *req, struct fuse_file *ff,
struct inode *inode, loff_t pos, size_t count, int opcode);
/**
@@ -427,9 +440,9 @@ void fuse_file_free(struct fuse_file *ff);
void fuse_finish_open(struct inode *inode, struct file *file,
struct fuse_file *ff, struct fuse_open_out *outarg);
-/** */
-struct fuse_req *fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags,
- int opcode);
+/** Fill in ff->reserved_req with a RELEASE request */
+void fuse_release_fill(struct fuse_file *ff, u64 nodeid, int flags, int opcode);
+
/**
* Send RELEASE or RELEASEDIR request
*/
@@ -524,11 +537,6 @@ void request_send_background(struct fuse_conn *fc, struct fuse_req *req);
void fuse_abort_conn(struct fuse_conn *fc);
/**
- * Get the attributes of a file
- */
-int fuse_do_getattr(struct inode *inode);
-
-/**
* Invalidate inode attributes
*/
void fuse_invalidate_attr(struct inode *inode);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 5448f625ab5..fd0735715c1 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -109,20 +109,25 @@ static int fuse_remount_fs(struct super_block *sb, int *flags, char *data)
return 0;
}
+static void fuse_truncate(struct address_space *mapping, loff_t offset)
+{
+ /* See vmtruncate() */
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+ truncate_inode_pages(mapping, offset);
+ unmap_mapping_range(mapping, offset + PAGE_SIZE - 1, 0, 1);
+}
+
void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
{
struct fuse_conn *fc = get_fuse_conn(inode);
- if (S_ISREG(inode->i_mode) && i_size_read(inode) != attr->size)
- invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ struct fuse_inode *fi = get_fuse_inode(inode);
+ loff_t oldsize;
inode->i_ino = attr->ino;
- inode->i_mode = (inode->i_mode & S_IFMT) + (attr->mode & 07777);
+ inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);
inode->i_nlink = attr->nlink;
inode->i_uid = attr->uid;
inode->i_gid = attr->gid;
- spin_lock(&fc->lock);
- i_size_write(inode, attr->size);
- spin_unlock(&fc->lock);
inode->i_blocks = attr->blocks;
inode->i_atime.tv_sec = attr->atime;
inode->i_atime.tv_nsec = attr->atimensec;
@@ -130,6 +135,26 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr)
inode->i_mtime.tv_nsec = attr->mtimensec;
inode->i_ctime.tv_sec = attr->ctime;
inode->i_ctime.tv_nsec = attr->ctimensec;
+
+ /*
+ * Don't set the sticky bit in i_mode, unless we want the VFS
+ * to check permissions. This prevents failures due to the
+ * check in may_delete().
+ */
+ fi->orig_i_mode = inode->i_mode;
+ if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS))
+ inode->i_mode &= ~S_ISVTX;
+
+ spin_lock(&fc->lock);
+ oldsize = inode->i_size;
+ i_size_write(inode, attr->size);
+ spin_unlock(&fc->lock);
+
+ if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
+ if (attr->size < oldsize)
+ fuse_truncate(inode->i_mapping, attr->size);
+ invalidate_inode_pages2(inode->i_mapping);
+ }
}
static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
@@ -232,6 +257,7 @@ static void fuse_put_super(struct super_block *sb)
kill_fasync(&fc->fasync, SIGIO, POLL_IN);
wake_up_all(&fc->waitq);
wake_up_all(&fc->blocked_waitq);
+ wake_up_all(&fc->reserved_req_waitq);
mutex_lock(&fuse_mutex);
list_del(&fc->entry);
fuse_ctl_remove_conn(fc);
@@ -401,6 +427,7 @@ static int fuse_show_options(struct seq_file *m, struct vfsmount *mnt)
static struct fuse_conn *new_conn(void)
{
struct fuse_conn *fc;
+ int err;
fc = kzalloc(sizeof(*fc), GFP_KERNEL);
if (fc) {
@@ -409,6 +436,7 @@ static struct fuse_conn *new_conn(void)
atomic_set(&fc->count, 1);
init_waitqueue_head(&fc->waitq);
init_waitqueue_head(&fc->blocked_waitq);
+ init_waitqueue_head(&fc->reserved_req_waitq);
INIT_LIST_HEAD(&fc->pending);
INIT_LIST_HEAD(&fc->processing);
INIT_LIST_HEAD(&fc->io);
@@ -416,10 +444,17 @@ static struct fuse_conn *new_conn(void)
atomic_set(&fc->num_waiting, 0);
fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
fc->bdi.unplug_io_fn = default_unplug_io_fn;
+ err = bdi_init(&fc->bdi);
+ if (err) {
+ kfree(fc);
+ fc = NULL;
+ goto out;
+ }
fc->reqctr = 0;
fc->blocked = 1;
get_random_bytes(&fc->scramble_key, sizeof(fc->scramble_key));
}
+out:
return fc;
}
@@ -429,6 +464,7 @@ void fuse_conn_put(struct fuse_conn *fc)
if (fc->destroy_req)
fuse_request_free(fc->destroy_req);
mutex_destroy(&fc->inst_mutex);
+ bdi_destroy(&fc->bdi);
kfree(fc);
}
}
@@ -446,6 +482,7 @@ static struct inode *get_root_inode(struct super_block *sb, unsigned mode)
attr.mode = mode;
attr.ino = FUSE_ROOT_ID;
+ attr.nlink = 1;
return fuse_iget(sb, 1, 0, &attr);
}
@@ -683,8 +720,7 @@ static inline void unregister_fuseblk(void)
static decl_subsys(fuse, NULL, NULL);
static decl_subsys(connections, NULL, NULL);
-static void fuse_inode_init_once(void *foo, struct kmem_cache *cachep,
- unsigned long flags)
+static void fuse_inode_init_once(struct kmem_cache *cachep, void *foo)
{
struct inode * inode = foo;