diff options
Diffstat (limited to 'fs/fuse/dev.c')
| -rw-r--r-- | fs/fuse/dev.c | 559 | 
1 files changed, 432 insertions, 127 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 6e07696308d..ca887314aba 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -19,6 +19,7 @@  #include <linux/pipe_fs_i.h>  #include <linux/swap.h>  #include <linux/splice.h> +#include <linux/aio.h>  MODULE_ALIAS_MISCDEV(FUSE_MINOR);  MODULE_ALIAS("devname:fuse"); @@ -34,34 +35,67 @@ static struct fuse_conn *fuse_get_conn(struct file *file)  	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(pages, 0, sizeof(*pages) * npages); +	memset(page_descs, 0, sizeof(*page_descs) * npages);  	INIT_LIST_HEAD(&req->list);  	INIT_LIST_HEAD(&req->intr_entry);  	init_waitqueue_head(&req->waitq);  	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); -	if (req) -		fuse_request_init(req); +	struct fuse_req *req = kmem_cache_alloc(fuse_req_cachep, flags); +	if (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;  } + +struct fuse_req *fuse_request_alloc(unsigned npages) +{ +	return __fuse_request_alloc(npages, GFP_KERNEL); +}  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); -	if (req) -		fuse_request_init(req); -	return req; +	return __fuse_request_alloc(npages, GFP_NOFS);  }  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);  } @@ -78,7 +112,7 @@ static void restore_sigs(sigset_t *oldset)  	sigprocmask(SIG_SETMASK, oldset, NULL);  } -static void __fuse_get_request(struct fuse_req *req) +void __fuse_get_request(struct fuse_req *req)  {  	atomic_inc(&req->count);  } @@ -92,45 +126,71 @@ static void __fuse_put_request(struct fuse_req *req)  static void fuse_req_init_context(struct fuse_req *req)  { -	req->in.h.uid = current_fsuid(); -	req->in.h.gid = current_fsgid(); +	req->in.h.uid = from_kuid_munged(&init_user_ns, current_fsuid()); +	req->in.h.gid = from_kgid_munged(&init_user_ns, current_fsgid());  	req->in.h.pid = current->pid;  } -struct fuse_req *fuse_get_req(struct fuse_conn *fc) +static bool fuse_block_alloc(struct fuse_conn *fc, bool for_background) +{ +	return !fc->initialized || (for_background && fc->blocked); +} + +static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, +				       bool for_background)  {  	struct fuse_req *req; -	sigset_t oldset; -	int intr;  	int err; -  	atomic_inc(&fc->num_waiting); -	block_sigs(&oldset); -	intr = wait_event_interruptible(fc->blocked_waitq, !fc->blocked); -	restore_sigs(&oldset); -	err = -EINTR; -	if (intr) -		goto out; + +	if (fuse_block_alloc(fc, for_background)) { +		sigset_t oldset; +		int intr; + +		block_sigs(&oldset); +		intr = wait_event_interruptible_exclusive(fc->blocked_waitq, +				!fuse_block_alloc(fc, for_background)); +		restore_sigs(&oldset); +		err = -EINTR; +		if (intr) +			goto out; +	}  	err = -ENOTCONN;  	if (!fc->connected)  		goto out; -	req = fuse_request_alloc(); +	req = fuse_request_alloc(npages);  	err = -ENOMEM; -	if (!req) +	if (!req) { +		if (for_background) +			wake_up(&fc->blocked_waitq);  		goto out; +	}  	fuse_req_init_context(req);  	req->waiting = 1; +	req->background = for_background;  	return req;   out:  	atomic_dec(&fc->num_waiting);  	return ERR_PTR(err);  } + +struct fuse_req *fuse_get_req(struct fuse_conn *fc, unsigned npages) +{ +	return __fuse_get_req(fc, npages, false); +}  EXPORT_SYMBOL_GPL(fuse_get_req); +struct fuse_req *fuse_get_req_for_background(struct fuse_conn *fc, +					     unsigned npages) +{ +	return __fuse_get_req(fc, npages, true); +} +EXPORT_SYMBOL_GPL(fuse_get_req_for_background); +  /*   * Return request in fuse_file->reserved_req.  However that may   * currently be in use.  If that is the case, wait for it to become @@ -148,8 +208,7 @@ static struct fuse_req *get_reserved_req(struct fuse_conn *fc,  		if (ff->reserved_req) {  			req = ff->reserved_req;  			ff->reserved_req = NULL; -			get_file(file); -			req->stolen_file = file; +			req->stolen_file = get_file(file);  		}  		spin_unlock(&fc->lock);  	} while (!req); @@ -166,7 +225,7 @@ static void put_reserved_req(struct fuse_conn *fc, struct fuse_req *req)  	struct fuse_file *ff = file->private_data;  	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);  	ff->reserved_req = req;  	wake_up_all(&fc->reserved_req_waitq); @@ -187,24 +246,37 @@ 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   * 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;  	atomic_inc(&fc->num_waiting); -	wait_event(fc->blocked_waitq, !fc->blocked); -	req = fuse_request_alloc(); +	wait_event(fc->blocked_waitq, fc->initialized); +	req = fuse_request_alloc(0);  	if (!req)  		req = get_reserved_req(fc, file);  	fuse_req_init_context(req);  	req->waiting = 1; +	req->background = 0;  	return req;  }  void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)  {  	if (atomic_dec_and_test(&req->count)) { +		if (unlikely(req->background)) { +			/* +			 * We get here in the unlikely case that a background +			 * request was allocated but not sent +			 */ +			spin_lock(&fc->lock); +			if (!fc->blocked) +				wake_up(&fc->blocked_waitq); +			spin_unlock(&fc->lock); +		} +  		if (req->waiting)  			atomic_dec(&fc->num_waiting); @@ -251,6 +323,24 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)  	kill_fasync(&fc->fasync, SIGIO, POLL_IN);  } +void fuse_queue_forget(struct fuse_conn *fc, struct fuse_forget_link *forget, +		       u64 nodeid, u64 nlookup) +{ +	forget->forget_one.nodeid = nodeid; +	forget->forget_one.nlookup = nlookup; + +	spin_lock(&fc->lock); +	if (fc->connected) { +		fc->forget_list_tail->next = forget; +		fc->forget_list_tail = forget; +		wake_up(&fc->waitq); +		kill_fasync(&fc->fasync, SIGIO, POLL_IN); +	} else { +		kfree(forget); +	} +	spin_unlock(&fc->lock); +} +  static void flush_bg_queue(struct fuse_conn *fc)  {  	while (fc->active_background < fc->max_background && @@ -284,10 +374,15 @@ __releases(fc->lock)  	list_del(&req->intr_entry);  	req->state = FUSE_REQ_FINISHED;  	if (req->background) { -		if (fc->num_background == fc->max_background) { +		req->background = 0; + +		if (fc->num_background == fc->max_background)  			fc->blocked = 0; -			wake_up_all(&fc->blocked_waitq); -		} + +		/* Wake up next waiter, if any */ +		if (!fc->blocked && waitqueue_active(&fc->blocked_waitq)) +			wake_up(&fc->blocked_waitq); +  		if (fc->num_background == fc->congestion_threshold &&  		    fc->connected && fc->bdi_initialized) {  			clear_bdi_congested(&fc->bdi, BLK_RW_SYNC); @@ -389,9 +484,9 @@ __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; +	BUG_ON(req->background);  	spin_lock(&fc->lock);  	if (!fc->connected)  		req->out.h.error = -ENOTCONN; @@ -408,12 +503,18 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req)  	}  	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);  static void fuse_request_send_nowait_locked(struct fuse_conn *fc,  					    struct fuse_req *req)  { -	req->background = 1; +	BUG_ON(!req->background);  	fc->num_background++;  	if (fc->num_background == fc->max_background)  		fc->blocked = 1; @@ -438,12 +539,6 @@ static void fuse_request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)  	}  } -void fuse_request_send_noreply(struct fuse_conn *fc, struct fuse_req *req) -{ -	req->isreply = 0; -	fuse_request_send_nowait(fc, req); -} -  void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req)  {  	req->isreply = 1; @@ -480,6 +575,27 @@ void fuse_request_send_background_locked(struct fuse_conn *fc,  	fuse_request_send_nowait_locked(fc, req);  } +void fuse_force_forget(struct file *file, u64 nodeid) +{ +	struct inode *inode = file_inode(file); +	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   * anything that could cause a page-fault.  If the request was already @@ -527,9 +643,8 @@ struct fuse_copy_state {  	unsigned long seglen;  	unsigned long addr;  	struct page *pg; -	void *mapaddr; -	void *buf;  	unsigned len; +	unsigned offset;  	unsigned move_pages:1;  }; @@ -550,23 +665,17 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)  	if (cs->currbuf) {  		struct pipe_buffer *buf = cs->currbuf; -		if (!cs->write) { -			buf->ops->unmap(cs->pipe, buf, cs->mapaddr); -		} else { -			kunmap(buf->page); +		if (cs->write)  			buf->len = PAGE_SIZE - cs->len; -		}  		cs->currbuf = NULL; -		cs->mapaddr = NULL; -	} else if (cs->mapaddr) { -		kunmap(cs->pg); +	} else if (cs->pg) {  		if (cs->write) {  			flush_dcache_page(cs->pg);  			set_page_dirty_lock(cs->pg);  		}  		put_page(cs->pg); -		cs->mapaddr = NULL;  	} +	cs->pg = NULL;  }  /* @@ -575,7 +684,7 @@ static void fuse_copy_finish(struct fuse_copy_state *cs)   */  static int fuse_copy_fill(struct fuse_copy_state *cs)  { -	unsigned long offset; +	struct page *page;  	int err;  	unlock_request(cs->fc, cs->req); @@ -590,14 +699,12 @@ 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, 0); +			cs->pg = buf->page; +			cs->offset = buf->offset;  			cs->len = buf->len; -			cs->buf = cs->mapaddr + buf->offset;  			cs->pipebufs++;  			cs->nr_segs--;  		} else { -			struct page *page; -  			if (cs->nr_segs == cs->pipe->buffers)  				return -EIO; @@ -610,8 +717,8 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)  			buf->len = 0;  			cs->currbuf = buf; -			cs->mapaddr = kmap(page); -			cs->buf = cs->mapaddr; +			cs->pg = page; +			cs->offset = 0;  			cs->len = PAGE_SIZE;  			cs->pipebufs++;  			cs->nr_segs++; @@ -624,14 +731,13 @@ static int fuse_copy_fill(struct fuse_copy_state *cs)  			cs->iov++;  			cs->nr_segs--;  		} -		err = get_user_pages_fast(cs->addr, 1, cs->write, &cs->pg); +		err = get_user_pages_fast(cs->addr, 1, cs->write, &page);  		if (err < 0)  			return err;  		BUG_ON(err != 1); -		offset = cs->addr % PAGE_SIZE; -		cs->mapaddr = kmap(cs->pg); -		cs->buf = cs->mapaddr + offset; -		cs->len = min(PAGE_SIZE - offset, cs->seglen); +		cs->pg = page; +		cs->offset = cs->addr % PAGE_SIZE; +		cs->len = min(PAGE_SIZE - cs->offset, cs->seglen);  		cs->seglen -= cs->len;  		cs->addr += cs->len;  	} @@ -644,15 +750,20 @@ static int fuse_copy_do(struct fuse_copy_state *cs, void **val, unsigned *size)  {  	unsigned ncpy = min(*size, cs->len);  	if (val) { +		void *pgaddr = kmap_atomic(cs->pg); +		void *buf = pgaddr + cs->offset; +  		if (cs->write) -			memcpy(cs->buf, *val, ncpy); +			memcpy(buf, *val, ncpy);  		else -			memcpy(*val, cs->buf, ncpy); +			memcpy(*val, buf, ncpy); + +		kunmap_atomic(pgaddr);  		*val += ncpy;  	}  	*size -= ncpy;  	cs->len -= ncpy; -	cs->buf += ncpy; +	cs->offset += ncpy;  	return ncpy;  } @@ -681,8 +792,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)  	struct page *oldpage = *pagep;  	struct page *newpage;  	struct pipe_buffer *buf = cs->pipebufs; -	struct address_space *mapping; -	pgoff_t index;  	unlock_request(cs->fc, cs->req);  	fuse_copy_finish(cs); @@ -713,9 +822,6 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)  	if (fuse_check_page(newpage) != 0)  		goto out_fallback_unlock; -	mapping = oldpage->mapping; -	index = oldpage->index; -  	/*  	 * This is a new and locked page, it shouldn't be mapped or  	 * have any special flags on it @@ -729,14 +835,12 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)  	if (WARN_ON(PageMlocked(oldpage)))  		goto out_fallback_unlock; -	remove_from_page_cache(oldpage); -	page_cache_release(oldpage); - -	err = add_to_page_cache_locked(newpage, mapping, index, GFP_KERNEL); +	err = replace_page_cache_page(oldpage, newpage, GFP_KERNEL);  	if (err) { -		printk(KERN_WARNING "fuse_try_move_page: failed to add page"); -		goto out_fallback_unlock; +		unlock_page(newpage); +		return err;  	} +  	page_cache_get(newpage);  	if (!(buf->flags & PIPE_BUF_FLAG_LRU)) @@ -765,8 +869,8 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)  out_fallback_unlock:  	unlock_page(newpage);  out_fallback: -	cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); -	cs->buf = cs->mapaddr + buf->offset; +	cs->pg = buf->page; +	cs->offset = buf->offset;  	err = lock_request(cs->fc, cs->req);  	if (err) @@ -828,10 +932,10 @@ static int fuse_copy_page(struct fuse_copy_state *cs, struct page **pagep,  			}  		}  		if (page) { -			void *mapaddr = kmap_atomic(page, KM_USER0); +			void *mapaddr = kmap_atomic(page);  			void *buf = mapaddr + offset;  			offset += fuse_copy_do(cs, &buf, &count); -			kunmap_atomic(mapaddr, KM_USER0); +			kunmap_atomic(mapaddr);  		} else  			offset += fuse_copy_do(cs, NULL, &count);  	} @@ -846,11 +950,11 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,  {  	unsigned i;  	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++) {  		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,  				     zeroing); @@ -858,8 +962,6 @@ static int fuse_copy_pages(struct fuse_copy_state *cs, unsigned nbytes,  			return err;  		nbytes -= count; -		count = min(nbytes, (unsigned) PAGE_SIZE); -		offset = 0;  	}  	return 0;  } @@ -896,9 +998,15 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,  	return err;  } +static int forget_pending(struct fuse_conn *fc) +{ +	return fc->forget_list_head.next != NULL; +} +  static int request_pending(struct fuse_conn *fc)  { -	return !list_empty(&fc->pending) || !list_empty(&fc->interrupts); +	return !list_empty(&fc->pending) || !list_empty(&fc->interrupts) || +		forget_pending(fc);  }  /* Wait until a request is available on the pending list */ @@ -960,6 +1068,120 @@ __releases(fc->lock)  	return err ? err : reqsize;  } +static struct fuse_forget_link *dequeue_forget(struct fuse_conn *fc, +					       unsigned max, +					       unsigned *countp) +{ +	struct fuse_forget_link *head = fc->forget_list_head.next; +	struct fuse_forget_link **newhead = &head; +	unsigned count; + +	for (count = 0; *newhead != NULL && count < max; count++) +		newhead = &(*newhead)->next; + +	fc->forget_list_head.next = *newhead; +	*newhead = NULL; +	if (fc->forget_list_head.next == NULL) +		fc->forget_list_tail = &fc->forget_list_head; + +	if (countp != NULL) +		*countp = count; + +	return head; +} + +static int fuse_read_single_forget(struct fuse_conn *fc, +				   struct fuse_copy_state *cs, +				   size_t nbytes) +__releases(fc->lock) +{ +	int err; +	struct fuse_forget_link *forget = dequeue_forget(fc, 1, NULL); +	struct fuse_forget_in arg = { +		.nlookup = forget->forget_one.nlookup, +	}; +	struct fuse_in_header ih = { +		.opcode = FUSE_FORGET, +		.nodeid = forget->forget_one.nodeid, +		.unique = fuse_get_unique(fc), +		.len = sizeof(ih) + sizeof(arg), +	}; + +	spin_unlock(&fc->lock); +	kfree(forget); +	if (nbytes < ih.len) +		return -EINVAL; + +	err = fuse_copy_one(cs, &ih, sizeof(ih)); +	if (!err) +		err = fuse_copy_one(cs, &arg, sizeof(arg)); +	fuse_copy_finish(cs); + +	if (err) +		return err; + +	return ih.len; +} + +static int fuse_read_batch_forget(struct fuse_conn *fc, +				   struct fuse_copy_state *cs, size_t nbytes) +__releases(fc->lock) +{ +	int err; +	unsigned max_forgets; +	unsigned count; +	struct fuse_forget_link *head; +	struct fuse_batch_forget_in arg = { .count = 0 }; +	struct fuse_in_header ih = { +		.opcode = FUSE_BATCH_FORGET, +		.unique = fuse_get_unique(fc), +		.len = sizeof(ih) + sizeof(arg), +	}; + +	if (nbytes < ih.len) { +		spin_unlock(&fc->lock); +		return -EINVAL; +	} + +	max_forgets = (nbytes - ih.len) / sizeof(struct fuse_forget_one); +	head = dequeue_forget(fc, max_forgets, &count); +	spin_unlock(&fc->lock); + +	arg.count = count; +	ih.len += count * sizeof(struct fuse_forget_one); +	err = fuse_copy_one(cs, &ih, sizeof(ih)); +	if (!err) +		err = fuse_copy_one(cs, &arg, sizeof(arg)); + +	while (head) { +		struct fuse_forget_link *forget = head; + +		if (!err) { +			err = fuse_copy_one(cs, &forget->forget_one, +					    sizeof(forget->forget_one)); +		} +		head = forget->next; +		kfree(forget); +	} + +	fuse_copy_finish(cs); + +	if (err) +		return err; + +	return ih.len; +} + +static int fuse_read_forget(struct fuse_conn *fc, struct fuse_copy_state *cs, +			    size_t nbytes) +__releases(fc->lock) +{ +	if (fc->minor < 16 || fc->forget_list_head.next->next == NULL) +		return fuse_read_single_forget(fc, cs, nbytes); +	else +		return fuse_read_batch_forget(fc, cs, nbytes); +} +  /*   * Read a single request into the userspace filesystem's buffer.  This   * function waits until a request is available, then removes it from @@ -998,6 +1220,14 @@ static ssize_t fuse_dev_do_read(struct fuse_conn *fc, struct file *file,  		return fuse_read_interrupt(fc, cs, nbytes, req);  	} +	if (forget_pending(fc)) { +		if (list_empty(&fc->pending) || fc->forget_batch-- > 0) +			return fuse_read_forget(fc, cs, nbytes); + +		if (fc->forget_batch <= -8) +			fc->forget_batch = 16; +	} +  	req = list_entry(fc->pending.next, struct fuse_req, list);  	req->state = FUSE_REQ_READING;  	list_move(&req->list, &fc->io); @@ -1061,22 +1291,6 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,  	return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs));  } -static int fuse_dev_pipe_buf_steal(struct pipe_inode_info *pipe, -				   struct pipe_buffer *buf) -{ -	return 1; -} - -static const struct pipe_buf_operations fuse_dev_pipe_buf_ops = { -	.can_merge = 0, -	.map = generic_pipe_buf_map, -	.unmap = generic_pipe_buf_unmap, -	.confirm = generic_pipe_buf_confirm, -	.release = generic_pipe_buf_release, -	.steal = fuse_dev_pipe_buf_steal, -	.get = generic_pipe_buf_get, -}; -  static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,  				    struct pipe_inode_info *pipe,  				    size_t len, unsigned int flags) @@ -1090,7 +1304,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,  	if (!fc)  		return -EPERM; -	bufs = kmalloc(pipe->buffers * sizeof (struct pipe_buffer), GFP_KERNEL); +	bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL);  	if (!bufs)  		return -ENOMEM; @@ -1123,13 +1337,17 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,  		buf->page = bufs[page_nr].page;  		buf->offset = bufs[page_nr].offset;  		buf->len = bufs[page_nr].len; -		buf->ops = &fuse_dev_pipe_buf_ops; +		/* +		 * Need to be careful about this.  Having buf->ops in module +		 * code can Oops if the buffer persists after module unload. +		 */ +		buf->ops = &nosteal_pipe_buf_ops;  		pipe->nrbufs++;  		page_nr++;  		ret += buf->len; -		if (pipe->inode) +		if (pipe->files)  			do_wakeup = 1;  	} @@ -1224,6 +1442,10 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,  	if (outarg.namelen > FUSE_NAME_MAX)  		goto err; +	err = -EINVAL; +	if (size != sizeof(outarg) + outarg.namelen + 1) +		goto err; +  	name.name = buf;  	name.len = outarg.namelen;  	err = fuse_copy_one(cs, buf, outarg.namelen + 1); @@ -1236,7 +1458,59 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size,  	down_read(&fc->killsb);  	err = -ENOENT;  	if (fc->sb) -		err = fuse_reverse_inval_entry(fc->sb, outarg.parent, &name); +		err = fuse_reverse_inval_entry(fc->sb, outarg.parent, 0, &name); +	up_read(&fc->killsb); +	kfree(buf); +	return err; + +err: +	kfree(buf); +	fuse_copy_finish(cs); +	return err; +} + +static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, +			      struct fuse_copy_state *cs) +{ +	struct fuse_notify_delete_out outarg; +	int err = -ENOMEM; +	char *buf; +	struct qstr name; + +	buf = kzalloc(FUSE_NAME_MAX + 1, GFP_KERNEL); +	if (!buf) +		goto err; + +	err = -EINVAL; +	if (size < sizeof(outarg)) +		goto err; + +	err = fuse_copy_one(cs, &outarg, sizeof(outarg)); +	if (err) +		goto err; + +	err = -ENAMETOOLONG; +	if (outarg.namelen > FUSE_NAME_MAX) +		goto err; + +	err = -EINVAL; +	if (size != sizeof(outarg) + outarg.namelen + 1) +		goto err; + +	name.name = buf; +	name.len = outarg.namelen; +	err = fuse_copy_one(cs, buf, outarg.namelen + 1); +	if (err) +		goto err; +	fuse_copy_finish(cs); +	buf[outarg.namelen] = 0; +	name.hash = full_name_hash(name.name, name.len); + +	down_read(&fc->killsb); +	err = -ENOENT; +	if (fc->sb) +		err = fuse_reverse_inval_entry(fc->sb, outarg.parent, +					       outarg.child, &name);  	up_read(&fc->killsb);  	kfree(buf);  	return err; @@ -1308,7 +1582,8 @@ static int fuse_notify_store(struct fuse_conn *fc, unsigned int size,  		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)) +		if (!err && offset == 0 && +		    (this_num == PAGE_CACHE_SIZE || file_size == end))  			SetPageUptodate(page);  		unlock_page(page);  		page_cache_release(page); @@ -1334,7 +1609,7 @@ out_finish:  static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req)  { -	release_pages(req->pages, req->num_pages, 0); +	release_pages(req->pages, req->num_pages, false);  }  static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, @@ -1348,29 +1623,34 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,  	unsigned int num;  	unsigned int offset;  	size_t total_len = 0; +	int num_pages; + +	offset = outarg->offset & ~PAGE_CACHE_MASK; +	file_size = i_size_read(inode); -	req = fuse_get_req(fc); +	num = outarg->size; +	if (outarg->offset > file_size) +		num = 0; +	else if (outarg->offset + num > file_size) +		num = file_size - outarg->offset; + +	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))  		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->page_descs[0].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) { +	while (num && req->num_pages < num_pages) {  		struct page *page;  		unsigned int this_num; @@ -1380,10 +1660,13 @@ static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode,  		this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset);  		req->pages[req->num_pages] = page; +		req->page_descs[req->num_pages].length = this_num;  		req->num_pages++; +		offset = 0;  		num -= this_num;  		total_len += this_num; +		index++;  	}  	req->misc.retrieve_in.offset = outarg->offset;  	req->misc.retrieve_in.size = total_len; @@ -1454,6 +1737,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,  	case FUSE_NOTIFY_RETRIEVE:  		return fuse_notify_retrieve(fc, size, cs); +	case FUSE_NOTIFY_DELETE: +		return fuse_notify_delete(fc, size, cs); +  	default:  		fuse_copy_finish(cs);  		return -EINVAL; @@ -1463,11 +1749,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code,  /* Look up request on processing list by unique ID */  static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique)  { -	struct list_head *entry; +	struct fuse_req *req; -	list_for_each(entry, &fc->processing) { -		struct fuse_req *req; -		req = list_entry(entry, struct fuse_req, list); +	list_for_each_entry(req, &fc->processing, list) {  		if (req->in.h.unique == unique || req->intr_unique == unique)  			return req;  	} @@ -1626,7 +1910,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,  	if (!fc)  		return -EPERM; -	bufs = kmalloc(pipe->buffers * sizeof (struct pipe_buffer), GFP_KERNEL); +	bufs = kmalloc(pipe->buffers * sizeof(struct pipe_buffer), GFP_KERNEL);  	if (!bufs)  		return -ENOMEM; @@ -1770,6 +2054,23 @@ __acquires(fc->lock)  	flush_bg_queue(fc);  	end_requests(fc, &fc->pending);  	end_requests(fc, &fc->processing); +	while (forget_pending(fc)) +		kfree(dequeue_forget(fc, 1, NULL)); +} + +static void end_polls(struct fuse_conn *fc) +{ +	struct rb_node *p; + +	p = rb_first(&fc->polled_files); + +	while (p) { +		struct fuse_file *ff; +		ff = rb_entry(p, struct fuse_file, polled_node); +		wake_up_interruptible_all(&ff->poll_wait); + +		p = rb_next(p); +	}  }  /* @@ -1797,8 +2098,10 @@ void fuse_abort_conn(struct fuse_conn *fc)  	if (fc->connected) {  		fc->connected = 0;  		fc->blocked = 0; +		fc->initialized = 1;  		end_io_requests(fc);  		end_queued_requests(fc); +		end_polls(fc);  		wake_up_all(&fc->waitq);  		wake_up_all(&fc->blocked_waitq);  		kill_fasync(&fc->fasync, SIGIO, POLL_IN); @@ -1814,7 +2117,9 @@ int fuse_dev_release(struct inode *inode, struct file *file)  		spin_lock(&fc->lock);  		fc->connected = 0;  		fc->blocked = 0; +		fc->initialized = 1;  		end_queued_requests(fc); +		end_polls(fc);  		wake_up_all(&fc->blocked_waitq);  		spin_unlock(&fc->lock);  		fuse_conn_put(fc);  | 
