diff options
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 134 |
1 files changed, 88 insertions, 46 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 2ca86141d13a..a7ef5e716f3c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -113,6 +113,14 @@ int fuse_open_common(struct inode *inode, struct file *file, int isdir) return err; } +/* Special case for failed iget in CREATE */ +static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) +{ + u64 nodeid = req->in.h.nodeid; + fuse_reset_request(req); + fuse_send_forget(fc, req, nodeid, 1); +} + void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, u64 nodeid, struct inode *inode, int flags, int isdir) { @@ -128,6 +136,8 @@ void fuse_send_release(struct fuse_conn *fc, struct fuse_file *ff, req->in.args[0].size = sizeof(struct fuse_release_in); req->in.args[0].value = inarg; request_send_background(fc, req); + if (!inode) + req->end = fuse_release_end; kfree(ff); } @@ -163,6 +173,9 @@ static int fuse_flush(struct file *file) struct fuse_flush_in inarg; int err; + if (is_bad_inode(inode)) + return -EIO; + if (fc->no_flush) return 0; @@ -199,6 +212,9 @@ int fuse_fsync_common(struct file *file, struct dentry *de, int datasync, struct fuse_fsync_in inarg; int err; + if (is_bad_inode(inode)) + return -EIO; + if ((!isdir && fc->no_fsync) || (isdir && fc->no_fsyncdir)) return 0; @@ -234,54 +250,57 @@ static int fuse_fsync(struct file *file, struct dentry *de, int datasync) return fuse_fsync_common(file, de, datasync, 0); } -size_t fuse_send_read_common(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count, - int isdir) +void fuse_read_fill(struct fuse_req *req, struct file *file, + struct inode *inode, loff_t pos, size_t count, int opcode) { - struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_file *ff = file->private_data; - struct fuse_read_in inarg; + struct fuse_read_in *inarg = &req->misc.read_in; - memset(&inarg, 0, sizeof(struct fuse_read_in)); - inarg.fh = ff->fh; - inarg.offset = pos; - inarg.size = count; - req->in.h.opcode = isdir ? FUSE_READDIR : FUSE_READ; + inarg->fh = ff->fh; + inarg->offset = pos; + inarg->size = count; + req->in.h.opcode = opcode; req->in.h.nodeid = get_node_id(inode); req->inode = inode; req->file = file; req->in.numargs = 1; req->in.args[0].size = sizeof(struct fuse_read_in); - req->in.args[0].value = &inarg; + req->in.args[0].value = inarg; req->out.argpages = 1; req->out.argvar = 1; req->out.numargs = 1; req->out.args[0].size = count; - request_send(fc, req); - return req->out.args[0].size; } -static inline size_t fuse_send_read(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, - size_t count) +static size_t fuse_send_read(struct fuse_req *req, struct file *file, + struct inode *inode, loff_t pos, size_t count) { - return fuse_send_read_common(req, file, inode, pos, count, 0); + struct fuse_conn *fc = get_fuse_conn(inode); + fuse_read_fill(req, file, inode, pos, count, FUSE_READ); + request_send(fc, req); + return req->out.args[0].size; } static int fuse_readpage(struct file *file, struct page *page) { struct inode *inode = page->mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); - loff_t pos = (loff_t) page->index << PAGE_CACHE_SHIFT; - struct fuse_req *req = fuse_get_request(fc); - int err = -EINTR; + struct fuse_req *req; + int err; + + err = -EIO; + if (is_bad_inode(inode)) + goto out; + + err = -EINTR; + req = fuse_get_request(fc); if (!req) goto out; req->out.page_zeroing = 1; req->num_pages = 1; req->pages[0] = page; - fuse_send_read(req, file, inode, pos, PAGE_CACHE_SIZE); + fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE); err = req->out.h.error; fuse_put_request(fc, req); if (!err) @@ -292,21 +311,33 @@ static int fuse_readpage(struct file *file, struct page *page) return err; } -static int fuse_send_readpages(struct fuse_req *req, struct file *file, - struct inode *inode) +static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) { - loff_t pos = (loff_t) req->pages[0]->index << PAGE_CACHE_SHIFT; - size_t count = req->num_pages << PAGE_CACHE_SHIFT; - unsigned i; - req->out.page_zeroing = 1; - fuse_send_read(req, file, inode, pos, count); + int i; + + fuse_invalidate_attr(req->pages[0]->mapping->host); /* atime changed */ + for (i = 0; i < req->num_pages; i++) { struct page *page = req->pages[i]; if (!req->out.h.error) SetPageUptodate(page); + else + SetPageError(page); unlock_page(page); } - return req->out.h.error; + fuse_put_request(fc, req); +} + +static void fuse_send_readpages(struct fuse_req *req, struct file *file, + 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; + req->end = fuse_readpages_end; + fuse_read_fill(req, file, inode, pos, count, FUSE_READ); + request_send_background(fc, req); } struct fuse_readpages_data { @@ -326,12 +357,12 @@ 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)) { - int err = fuse_send_readpages(req, data->file, inode); - if (err) { + fuse_send_readpages(req, data->file, inode); + data->req = req = fuse_get_request(fc); + if (!req) { unlock_page(page); - return err; + return -EINTR; } - fuse_reset_request(req); } req->pages[req->num_pages] = page; req->num_pages ++; @@ -345,6 +376,10 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_readpages_data data; int err; + + if (is_bad_inode(inode)) + return -EIO; + data.file = file; data.inode = inode; data.req = fuse_get_request(fc); @@ -352,10 +387,8 @@ static int fuse_readpages(struct file *file, struct address_space *mapping, return -EINTR; err = read_cache_pages(mapping, pages, fuse_readpages_fill, &data); - if (!err && data.req->num_pages) - err = fuse_send_readpages(data.req, file, inode); - fuse_put_request(fc, data.req); - fuse_invalidate_attr(inode); /* atime changed */ + if (!err) + fuse_send_readpages(data.req, file, inode); return err; } @@ -402,8 +435,13 @@ static int fuse_commit_write(struct file *file, struct page *page, unsigned count = to - offset; struct inode *inode = page->mapping->host; struct fuse_conn *fc = get_fuse_conn(inode); - loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + offset; - struct fuse_req *req = fuse_get_request(fc); + loff_t pos = page_offset(page) + offset; + struct fuse_req *req; + + if (is_bad_inode(inode)) + return -EIO; + + req = fuse_get_request(fc); if (!req) return -EINTR; @@ -454,7 +492,7 @@ static int fuse_get_user_pages(struct fuse_req *req, const char __user *buf, nbytes = min(nbytes, (unsigned) FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); npages = (nbytes + offset + PAGE_SIZE - 1) >> PAGE_SHIFT; - npages = min(npages, FUSE_MAX_PAGES_PER_REQ); + npages = min(max(npages, 1), FUSE_MAX_PAGES_PER_REQ); down_read(¤t->mm->mmap_sem); npages = get_user_pages(current, current->mm, user_addr, npages, write, 0, req->pages, NULL); @@ -475,12 +513,16 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf, size_t nmax = write ? fc->max_write : fc->max_read; loff_t pos = *ppos; ssize_t res = 0; - struct fuse_req *req = fuse_get_request(fc); + struct fuse_req *req; + + if (is_bad_inode(inode)) + return -EIO; + + req = fuse_get_request(fc); if (!req) return -EINTR; while (count) { - size_t tmp; size_t nres; size_t nbytes = min(count, nmax); int err = fuse_get_user_pages(req, buf, nbytes, !write); @@ -488,8 +530,8 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf, res = err; break; } - tmp = (req->num_pages << PAGE_SHIFT) - req->page_offset; - nbytes = min(nbytes, tmp); + nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; + nbytes = min(count, nbytes); if (write) nres = fuse_send_write(req, file, inode, pos, nbytes); else @@ -535,9 +577,9 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, struct inode *inode = file->f_dentry->d_inode; ssize_t res; /* Don't allow parallel writes to the same file */ - down(&inode->i_sem); + mutex_lock(&inode->i_mutex); res = fuse_direct_io(file, buf, count, ppos, 1); - up(&inode->i_sem); + mutex_unlock(&inode->i_mutex); return res; } |