Loading fs/fuse/dev.c +223 −6 Original line number Diff line number Diff line Loading @@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc) static void queue_request(struct fuse_conn *fc, struct fuse_req *req) { req->in.h.unique = fuse_get_unique(fc); req->in.h.len = sizeof(struct fuse_in_header) + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); list_add_tail(&req->list, &fc->pending); Loading @@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc) req = list_entry(fc->bg_queue.next, struct fuse_req, list); list_del(&req->list); fc->active_background++; req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); } } Loading Loading @@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) else if (fc->conn_error) req->out.h.error = -ECONNREFUSED; else { req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); /* acquire extra reference, since request is still needed after request_end() */ Loading Loading @@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) } EXPORT_SYMBOL_GPL(fuse_request_send_background); static int fuse_request_send_notify_reply(struct fuse_conn *fc, struct fuse_req *req, u64 unique) { int err = -ENODEV; req->isreply = 0; req->in.h.unique = unique; spin_lock(&fc->lock); if (fc->connected) { queue_request(fc, req); err = 0; } spin_unlock(&fc->lock); return err; } /* * Called under fc->lock * Loading Loading @@ -535,13 +553,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) if (!cs->write) { buf->ops->unmap(cs->pipe, buf, cs->mapaddr); } else { kunmap_atomic(cs->mapaddr, KM_USER0); kunmap(buf->page); buf->len = PAGE_SIZE - cs->len; } cs->currbuf = NULL; cs->mapaddr = NULL; } else if (cs->mapaddr) { kunmap_atomic(cs->mapaddr, KM_USER0); kunmap(cs->pg); if (cs->write) { flush_dcache_page(cs->pg); set_page_dirty_lock(cs->pg); Loading Loading @@ -572,7 +590,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) BUG_ON(!cs->nr_segs); cs->currbuf = buf; cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); cs->mapaddr = buf->ops->map(cs->pipe, buf, 0); cs->len = buf->len; cs->buf = cs->mapaddr + buf->offset; cs->pipebufs++; Loading @@ -592,7 +610,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) buf->len = 0; cs->currbuf = buf; cs->mapaddr = kmap_atomic(page, KM_USER0); cs->mapaddr = kmap(page); cs->buf = cs->mapaddr; cs->len = PAGE_SIZE; cs->pipebufs++; Loading @@ -611,7 +629,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) return err; BUG_ON(err != 1); offset = cs->addr % PAGE_SIZE; cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); cs->mapaddr = kmap(cs->pg); cs->buf = cs->mapaddr + offset; cs->len = min(PAGE_SIZE - offset, cs->seglen); cs->seglen -= cs->len; Loading Loading @@ -1231,6 +1249,199 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, return err; } static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_store_out outarg; struct inode *inode; struct address_space *mapping; u64 nodeid; int err; pgoff_t index; unsigned int offset; unsigned int num; loff_t file_size; loff_t end; err = -EINVAL; if (size < sizeof(outarg)) goto out_finish; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) goto out_finish; err = -EINVAL; if (size - sizeof(outarg) != outarg.size) goto out_finish; nodeid = outarg.nodeid; down_read(&fc->killsb); err = -ENOENT; if (!fc->sb) goto out_up_killsb; inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); if (!inode) goto out_up_killsb; mapping = inode->i_mapping; index = outarg.offset >> PAGE_CACHE_SHIFT; offset = outarg.offset & ~PAGE_CACHE_MASK; file_size = i_size_read(inode); end = outarg.offset + outarg.size; if (end > file_size) { file_size = end; fuse_write_update_size(inode, file_size); } num = outarg.size; while (num) { struct page *page; unsigned int this_num; err = -ENOMEM; page = find_or_create_page(mapping, index, mapping_gfp_mask(mapping)); if (!page) goto out_iput; this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); err = fuse_copy_page(cs, &page, offset, this_num, 0); if (!err && offset == 0 && (num != 0 || file_size == end)) SetPageUptodate(page); unlock_page(page); page_cache_release(page); if (err) goto out_iput; num -= this_num; offset = 0; index++; } err = 0; out_iput: iput(inode); out_up_killsb: up_read(&fc->killsb); out_finish: fuse_copy_finish(cs); return err; } static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req) { int i; for (i = 0; i < req->num_pages; i++) { struct page *page = req->pages[i]; page_cache_release(page); } } static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, struct fuse_notify_retrieve_out *outarg) { int err; struct address_space *mapping = inode->i_mapping; struct fuse_req *req; pgoff_t index; loff_t file_size; unsigned int num; unsigned int offset; size_t total_len; req = fuse_get_req(fc); if (IS_ERR(req)) return PTR_ERR(req); offset = outarg->offset & ~PAGE_CACHE_MASK; req->in.h.opcode = FUSE_NOTIFY_REPLY; req->in.h.nodeid = outarg->nodeid; req->in.numargs = 2; req->in.argpages = 1; req->page_offset = offset; req->end = fuse_retrieve_end; 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) { struct page *page; unsigned int this_num; page = find_get_page(mapping, index); if (!page) break; this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); req->pages[req->num_pages] = page; req->num_pages++; num -= this_num; total_len += this_num; } req->misc.retrieve_in.offset = outarg->offset; req->misc.retrieve_in.size = total_len; req->in.args[0].size = sizeof(req->misc.retrieve_in); req->in.args[0].value = &req->misc.retrieve_in; req->in.args[1].size = total_len; err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique); if (err) fuse_retrieve_end(fc, req); return err; } static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_retrieve_out outarg; struct inode *inode; int err; err = -EINVAL; if (size != sizeof(outarg)) goto copy_finish; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) goto copy_finish; fuse_copy_finish(cs); down_read(&fc->killsb); err = -ENOENT; if (fc->sb) { u64 nodeid = outarg.nodeid; inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); if (inode) { err = fuse_retrieve(fc, inode, &outarg); iput(inode); } } up_read(&fc->killsb); return err; copy_finish: fuse_copy_finish(cs); return err; } static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { Loading @@ -1244,6 +1455,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, case FUSE_NOTIFY_INVAL_ENTRY: return fuse_notify_inval_entry(fc, size, cs); case FUSE_NOTIFY_STORE: return fuse_notify_store(fc, size, cs); case FUSE_NOTIFY_RETRIEVE: return fuse_notify_retrieve(fc, size, cs); default: fuse_copy_finish(cs); return -EINVAL; Loading fs/fuse/file.c +1 −1 Original line number Diff line number Diff line Loading @@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, return 0; } static void fuse_write_update_size(struct inode *inode, loff_t pos) void fuse_write_update_size(struct inode *inode, loff_t pos) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); Loading fs/fuse/fuse_i.h +3 −0 Original line number Diff line number Diff line Loading @@ -272,6 +272,7 @@ struct fuse_req { struct fuse_write_in in; struct fuse_write_out out; } write; struct fuse_notify_retrieve_in retrieve_in; struct fuse_lk_in lk_in; } misc; Loading Loading @@ -748,4 +749,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, unsigned fuse_file_poll(struct file *file, poll_table *wait); int fuse_dev_release(struct inode *inode, struct file *file); void fuse_write_update_size(struct inode *inode, loff_t pos); #endif /* _FS_FUSE_I_H */ include/linux/fuse.h +33 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,10 @@ * * 7.14 * - add splice support to fuse device * * 7.15 * - add store notify * - add retrieve notify */ #ifndef _LINUX_FUSE_H Loading Loading @@ -68,7 +72,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ #define FUSE_KERNEL_MINOR_VERSION 14 #define FUSE_KERNEL_MINOR_VERSION 15 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 Loading Loading @@ -251,6 +255,7 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, /* CUSE specific operations */ CUSE_INIT = 4096, Loading @@ -260,6 +265,8 @@ enum fuse_notify_code { FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_CODE_MAX, }; Loading Loading @@ -568,4 +575,29 @@ struct fuse_notify_inval_entry_out { __u32 padding; }; struct fuse_notify_store_out { __u64 nodeid; __u64 offset; __u32 size; __u32 padding; }; struct fuse_notify_retrieve_out { __u64 notify_unique; __u64 nodeid; __u64 offset; __u32 size; __u32 padding; }; /* Matches the size of fuse_write_in */ struct fuse_notify_retrieve_in { __u64 dummy1; __u64 offset; __u32 size; __u32 dummy2; __u64 dummy3; __u64 dummy4; }; #endif /* _LINUX_FUSE_H */ Loading
fs/fuse/dev.c +223 −6 Original line number Diff line number Diff line Loading @@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc) static void queue_request(struct fuse_conn *fc, struct fuse_req *req) { req->in.h.unique = fuse_get_unique(fc); req->in.h.len = sizeof(struct fuse_in_header) + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); list_add_tail(&req->list, &fc->pending); Loading @@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc) req = list_entry(fc->bg_queue.next, struct fuse_req, list); list_del(&req->list); fc->active_background++; req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); } } Loading Loading @@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) else if (fc->conn_error) req->out.h.error = -ECONNREFUSED; else { req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); /* acquire extra reference, since request is still needed after request_end() */ Loading Loading @@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) } EXPORT_SYMBOL_GPL(fuse_request_send_background); static int fuse_request_send_notify_reply(struct fuse_conn *fc, struct fuse_req *req, u64 unique) { int err = -ENODEV; req->isreply = 0; req->in.h.unique = unique; spin_lock(&fc->lock); if (fc->connected) { queue_request(fc, req); err = 0; } spin_unlock(&fc->lock); return err; } /* * Called under fc->lock * Loading Loading @@ -535,13 +553,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) if (!cs->write) { buf->ops->unmap(cs->pipe, buf, cs->mapaddr); } else { kunmap_atomic(cs->mapaddr, KM_USER0); kunmap(buf->page); buf->len = PAGE_SIZE - cs->len; } cs->currbuf = NULL; cs->mapaddr = NULL; } else if (cs->mapaddr) { kunmap_atomic(cs->mapaddr, KM_USER0); kunmap(cs->pg); if (cs->write) { flush_dcache_page(cs->pg); set_page_dirty_lock(cs->pg); Loading Loading @@ -572,7 +590,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) BUG_ON(!cs->nr_segs); cs->currbuf = buf; cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); cs->mapaddr = buf->ops->map(cs->pipe, buf, 0); cs->len = buf->len; cs->buf = cs->mapaddr + buf->offset; cs->pipebufs++; Loading @@ -592,7 +610,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) buf->len = 0; cs->currbuf = buf; cs->mapaddr = kmap_atomic(page, KM_USER0); cs->mapaddr = kmap(page); cs->buf = cs->mapaddr; cs->len = PAGE_SIZE; cs->pipebufs++; Loading @@ -611,7 +629,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) return err; BUG_ON(err != 1); offset = cs->addr % PAGE_SIZE; cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); cs->mapaddr = kmap(cs->pg); cs->buf = cs->mapaddr + offset; cs->len = min(PAGE_SIZE - offset, cs->seglen); cs->seglen -= cs->len; Loading Loading @@ -1231,6 +1249,199 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, return err; } static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_store_out outarg; struct inode *inode; struct address_space *mapping; u64 nodeid; int err; pgoff_t index; unsigned int offset; unsigned int num; loff_t file_size; loff_t end; err = -EINVAL; if (size < sizeof(outarg)) goto out_finish; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) goto out_finish; err = -EINVAL; if (size - sizeof(outarg) != outarg.size) goto out_finish; nodeid = outarg.nodeid; down_read(&fc->killsb); err = -ENOENT; if (!fc->sb) goto out_up_killsb; inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); if (!inode) goto out_up_killsb; mapping = inode->i_mapping; index = outarg.offset >> PAGE_CACHE_SHIFT; offset = outarg.offset & ~PAGE_CACHE_MASK; file_size = i_size_read(inode); end = outarg.offset + outarg.size; if (end > file_size) { file_size = end; fuse_write_update_size(inode, file_size); } num = outarg.size; while (num) { struct page *page; unsigned int this_num; err = -ENOMEM; page = find_or_create_page(mapping, index, mapping_gfp_mask(mapping)); if (!page) goto out_iput; this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); err = fuse_copy_page(cs, &page, offset, this_num, 0); if (!err && offset == 0 && (num != 0 || file_size == end)) SetPageUptodate(page); unlock_page(page); page_cache_release(page); if (err) goto out_iput; num -= this_num; offset = 0; index++; } err = 0; out_iput: iput(inode); out_up_killsb: up_read(&fc->killsb); out_finish: fuse_copy_finish(cs); return err; } static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req) { int i; for (i = 0; i < req->num_pages; i++) { struct page *page = req->pages[i]; page_cache_release(page); } } static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, struct fuse_notify_retrieve_out *outarg) { int err; struct address_space *mapping = inode->i_mapping; struct fuse_req *req; pgoff_t index; loff_t file_size; unsigned int num; unsigned int offset; size_t total_len; req = fuse_get_req(fc); if (IS_ERR(req)) return PTR_ERR(req); offset = outarg->offset & ~PAGE_CACHE_MASK; req->in.h.opcode = FUSE_NOTIFY_REPLY; req->in.h.nodeid = outarg->nodeid; req->in.numargs = 2; req->in.argpages = 1; req->page_offset = offset; req->end = fuse_retrieve_end; 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) { struct page *page; unsigned int this_num; page = find_get_page(mapping, index); if (!page) break; this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); req->pages[req->num_pages] = page; req->num_pages++; num -= this_num; total_len += this_num; } req->misc.retrieve_in.offset = outarg->offset; req->misc.retrieve_in.size = total_len; req->in.args[0].size = sizeof(req->misc.retrieve_in); req->in.args[0].value = &req->misc.retrieve_in; req->in.args[1].size = total_len; err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique); if (err) fuse_retrieve_end(fc, req); return err; } static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, struct fuse_copy_state *cs) { struct fuse_notify_retrieve_out outarg; struct inode *inode; int err; err = -EINVAL; if (size != sizeof(outarg)) goto copy_finish; err = fuse_copy_one(cs, &outarg, sizeof(outarg)); if (err) goto copy_finish; fuse_copy_finish(cs); down_read(&fc->killsb); err = -ENOENT; if (fc->sb) { u64 nodeid = outarg.nodeid; inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); if (inode) { err = fuse_retrieve(fc, inode, &outarg); iput(inode); } } up_read(&fc->killsb); return err; copy_finish: fuse_copy_finish(cs); return err; } static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { Loading @@ -1244,6 +1455,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, case FUSE_NOTIFY_INVAL_ENTRY: return fuse_notify_inval_entry(fc, size, cs); case FUSE_NOTIFY_STORE: return fuse_notify_store(fc, size, cs); case FUSE_NOTIFY_RETRIEVE: return fuse_notify_retrieve(fc, size, cs); default: fuse_copy_finish(cs); return -EINVAL; Loading
fs/fuse/file.c +1 −1 Original line number Diff line number Diff line Loading @@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, return 0; } static void fuse_write_update_size(struct inode *inode, loff_t pos) void fuse_write_update_size(struct inode *inode, loff_t pos) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); Loading
fs/fuse/fuse_i.h +3 −0 Original line number Diff line number Diff line Loading @@ -272,6 +272,7 @@ struct fuse_req { struct fuse_write_in in; struct fuse_write_out out; } write; struct fuse_notify_retrieve_in retrieve_in; struct fuse_lk_in lk_in; } misc; Loading Loading @@ -748,4 +749,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, unsigned fuse_file_poll(struct file *file, poll_table *wait); int fuse_dev_release(struct inode *inode, struct file *file); void fuse_write_update_size(struct inode *inode, loff_t pos); #endif /* _FS_FUSE_I_H */
include/linux/fuse.h +33 −1 Original line number Diff line number Diff line Loading @@ -37,6 +37,10 @@ * * 7.14 * - add splice support to fuse device * * 7.15 * - add store notify * - add retrieve notify */ #ifndef _LINUX_FUSE_H Loading Loading @@ -68,7 +72,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ #define FUSE_KERNEL_MINOR_VERSION 14 #define FUSE_KERNEL_MINOR_VERSION 15 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 Loading Loading @@ -251,6 +255,7 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, FUSE_NOTIFY_REPLY = 41, /* CUSE specific operations */ CUSE_INIT = 4096, Loading @@ -260,6 +265,8 @@ enum fuse_notify_code { FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_CODE_MAX, }; Loading Loading @@ -568,4 +575,29 @@ struct fuse_notify_inval_entry_out { __u32 padding; }; struct fuse_notify_store_out { __u64 nodeid; __u64 offset; __u32 size; __u32 padding; }; struct fuse_notify_retrieve_out { __u64 notify_unique; __u64 nodeid; __u64 offset; __u32 size; __u32 padding; }; /* Matches the size of fuse_write_in */ struct fuse_notify_retrieve_in { __u64 dummy1; __u64 offset; __u32 size; __u32 dummy2; __u64 dummy3; __u64 dummy4; }; #endif /* _LINUX_FUSE_H */