X-Git-Url: http://pilppa.org/gitweb/?a=blobdiff_plain;f=fs%2Ffuse%2Ffile.c;h=535b37399009128df2413d3b7632160390113b9c;hb=5398c48244ced950171282c3c11159145fd6b547;hp=11f22a3d728a747212066c107533313f1eaf9f01;hpb=ebc283118ee448dcb6e6cae74a8a43f17a1ccc3f;p=linux-2.6-omap-h63xx.git diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 11f22a3d728..535b3739900 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -28,7 +28,9 @@ static int fuse_send_open(struct inode *inode, struct file *file, int isdir, return PTR_ERR(req); memset(&inarg, 0, sizeof(inarg)); - inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); + inarg.flags = file->f_flags & ~(O_CREAT | O_EXCL | O_NOCTTY); + if (!fc->atomic_o_trunc) + inarg.flags &= ~O_TRUNC; req->in.h.opcode = isdir ? FUSE_OPENDIR : FUSE_OPEN; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; @@ -53,6 +55,9 @@ struct fuse_file *fuse_file_alloc(void) if (!ff->reserved_req) { kfree(ff); ff = NULL; + } else { + INIT_LIST_HEAD(&ff->write_entry); + atomic_set(&ff->count, 0); } } return ff; @@ -64,15 +69,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 +118,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 +134,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,9 +146,6 @@ 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) @@ -136,15 +153,23 @@ 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); + + spin_lock(&fc->lock); + list_del(&ff->write_entry); + spin_unlock(&fc->lock); + /* + * 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 */ @@ -165,7 +190,7 @@ static int fuse_release(struct inode *inode, struct file *file) * Scramble the ID space with XTEA, so that the value of the files_struct * pointer is not exposed to userspace. */ -static 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) { u32 *k = fc->scramble_key; u64 v = (unsigned long) id; @@ -264,10 +289,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; @@ -285,10 +309,19 @@ void fuse_read_fill(struct fuse_req *req, struct file *file, } static size_t fuse_send_read(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count) + struct inode *inode, loff_t pos, size_t count, + fl_owner_t owner) { 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); + if (owner != NULL) { + struct fuse_read_in *inarg = &req->misc.read_in; + + inarg->read_flags |= FUSE_READ_LOCKOWNER; + inarg->lock_owner = fuse_lock_owner_id(fc, owner); + } request_send(fc, req); return req->out.args[0].size; } @@ -312,7 +345,8 @@ static int fuse_readpage(struct file *file, struct page *page) req->out.page_zeroing = 1; req->num_pages = 1; req->pages[0] = page; - fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE); + fuse_send_read(req, file, inode, page_offset(page), PAGE_CACHE_SIZE, + NULL); err = req->out.h.error; fuse_put_request(fc, req); if (!err) @@ -337,20 +371,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 +394,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 +411,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 +428,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 +445,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); } @@ -418,30 +453,47 @@ out: return err; } -static size_t fuse_send_write(struct fuse_req *req, struct file *file, - struct inode *inode, loff_t pos, size_t count) +static void fuse_write_fill(struct fuse_req *req, struct fuse_file *ff, + struct inode *inode, loff_t pos, size_t count, + int writepage) { struct fuse_conn *fc = get_fuse_conn(inode); - struct fuse_file *ff = file->private_data; - struct fuse_write_in inarg; - struct fuse_write_out outarg; + struct fuse_write_in *inarg = &req->misc.write.in; + struct fuse_write_out *outarg = &req->misc.write.out; - memset(&inarg, 0, sizeof(struct fuse_write_in)); - inarg.fh = ff->fh; - inarg.offset = pos; - inarg.size = count; + memset(inarg, 0, sizeof(struct fuse_write_in)); + inarg->fh = ff->fh; + inarg->offset = pos; + inarg->size = count; + inarg->write_flags = writepage ? FUSE_WRITE_CACHE : 0; req->in.h.opcode = FUSE_WRITE; req->in.h.nodeid = get_node_id(inode); req->in.argpages = 1; req->in.numargs = 2; - req->in.args[0].size = sizeof(struct fuse_write_in); - req->in.args[0].value = &inarg; + if (fc->minor < 9) + req->in.args[0].size = FUSE_COMPAT_WRITE_IN_SIZE; + else + req->in.args[0].size = sizeof(struct fuse_write_in); + req->in.args[0].value = inarg; req->in.args[1].size = count; req->out.numargs = 1; req->out.args[0].size = sizeof(struct fuse_write_out); - req->out.args[0].value = &outarg; + req->out.args[0].value = outarg; +} + +static size_t fuse_send_write(struct fuse_req *req, struct file *file, + struct inode *inode, loff_t pos, size_t count, + fl_owner_t owner) +{ + struct fuse_conn *fc = get_fuse_conn(inode); + fuse_write_fill(req, file->private_data, inode, pos, count, 0); + if (owner != NULL) { + struct fuse_write_in *inarg = &req->misc.write.in; + inarg->write_flags |= FUSE_WRITE_LOCKOWNER; + inarg->lock_owner = fuse_lock_owner_id(fc, owner); + } request_send(fc, req); - return outarg.size; + return req->misc.write.out.size; } static int fuse_write_begin(struct file *file, struct address_space *mapping, @@ -462,6 +514,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode, int err; size_t nres; struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); unsigned offset = pos & (PAGE_CACHE_SIZE - 1); struct fuse_req *req; @@ -475,7 +528,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode, req->num_pages = 1; req->pages[0] = page; req->page_offset = offset; - nres = fuse_send_write(req, file, inode, pos, count); + nres = fuse_send_write(req, file, inode, pos, count, NULL); err = req->out.h.error; fuse_put_request(fc, req); if (!err && !nres) @@ -483,6 +536,7 @@ static int fuse_buffered_write(struct file *file, struct inode *inode, if (!err) { pos += nres; spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; if (pos > inode->i_size) i_size_write(inode, pos); spin_unlock(&fc->lock); @@ -575,9 +629,11 @@ static ssize_t fuse_direct_io(struct file *file, const char __user *buf, nbytes = (req->num_pages << PAGE_SHIFT) - req->page_offset; nbytes = min(count, nbytes); if (write) - nres = fuse_send_write(req, file, inode, pos, nbytes); + nres = fuse_send_write(req, file, inode, pos, nbytes, + current->files); else - nres = fuse_send_read(req, file, inode, pos, nbytes); + nres = fuse_send_read(req, file, inode, pos, nbytes, + current->files); fuse_release_user_pages(req, !write); if (req->out.h.error) { if (!res) @@ -679,7 +735,8 @@ static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, } static void fuse_lk_fill(struct fuse_req *req, struct file *file, - const struct file_lock *fl, int opcode, pid_t pid) + const struct file_lock *fl, int opcode, pid_t pid, + int flock) { struct inode *inode = file->f_path.dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); @@ -692,6 +749,8 @@ static void fuse_lk_fill(struct fuse_req *req, struct file *file, arg->lk.end = fl->fl_end; arg->lk.type = fl->fl_type; arg->lk.pid = pid; + if (flock) + arg->lk_flags |= FUSE_LK_FLOCK; req->in.h.opcode = opcode; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; @@ -711,7 +770,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl) if (IS_ERR(req)) return PTR_ERR(req); - fuse_lk_fill(req, file, fl, FUSE_GETLK, 0); + fuse_lk_fill(req, file, fl, FUSE_GETLK, 0, 0); req->out.numargs = 1; req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; @@ -724,7 +783,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl) return err; } -static int fuse_setlk(struct file *file, struct file_lock *fl) +static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) { struct inode *inode = file->f_path.dentry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); @@ -741,7 +800,7 @@ static int fuse_setlk(struct file *file, struct file_lock *fl) if (IS_ERR(req)) return PTR_ERR(req); - fuse_lk_fill(req, file, fl, opcode, pid); + fuse_lk_fill(req, file, fl, opcode, pid, flock); request_send(fc, req); err = req->out.h.error; /* locking is restartable */ @@ -767,11 +826,28 @@ static int fuse_file_lock(struct file *file, int cmd, struct file_lock *fl) if (fc->no_lock) err = posix_lock_file_wait(file, fl); else - err = fuse_setlk(file, fl); + err = fuse_setlk(file, fl, 0); } return err; } +static int fuse_file_flock(struct file *file, int cmd, struct file_lock *fl) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + int err; + + if (fc->no_lock) { + err = flock_lock_file_wait(file, fl); + } else { + /* emulate flock with POSIX locks */ + fl->fl_owner = (fl_owner_t) file; + err = fuse_setlk(file, fl, 1); + } + + return err; +} + static sector_t fuse_bmap(struct address_space *mapping, sector_t block) { struct inode *inode = mapping->host; @@ -820,6 +896,7 @@ static const struct file_operations fuse_file_operations = { .release = fuse_release, .fsync = fuse_fsync, .lock = fuse_file_lock, + .flock = fuse_file_flock, .splice_read = generic_file_splice_read, }; @@ -832,6 +909,7 @@ static const struct file_operations fuse_direct_io_file_operations = { .release = fuse_release, .fsync = fuse_fsync, .lock = fuse_file_lock, + .flock = fuse_file_flock, /* no mmap and splice_read */ };