tty: implement read_iter
Now that the ldisc read() function takes kernel pointers, it's fairly
straightforward to make the tty file operations use .read_iter() instead
of .read().
That automatically gives us vread() and friends, and also makes it
possible to do .splice_read() on ttys again.
Fixes: 36e2c7421f ("fs: don't allow splice read/write without explicit ops")
Reported-by: Oliver Giles <ohw.giles@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									3b830a9c34
								
							
						
					
					
						commit
						dd78b0c483
					
				| @ -142,7 +142,7 @@ LIST_HEAD(tty_drivers);			/* linked list of tty drivers */ | |||||||
| /* Mutex to protect creating and releasing a tty */ | /* Mutex to protect creating and releasing a tty */ | ||||||
| DEFINE_MUTEX(tty_mutex); | DEFINE_MUTEX(tty_mutex); | ||||||
| 
 | 
 | ||||||
| static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); | static ssize_t tty_read(struct kiocb *, struct iov_iter *); | ||||||
| static ssize_t tty_write(struct kiocb *, struct iov_iter *); | static ssize_t tty_write(struct kiocb *, struct iov_iter *); | ||||||
| ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *); | ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *); | ||||||
| static __poll_t tty_poll(struct file *, poll_table *); | static __poll_t tty_poll(struct file *, poll_table *); | ||||||
| @ -476,8 +476,9 @@ static void tty_show_fdinfo(struct seq_file *m, struct file *file) | |||||||
| 
 | 
 | ||||||
| static const struct file_operations tty_fops = { | static const struct file_operations tty_fops = { | ||||||
| 	.llseek		= no_llseek, | 	.llseek		= no_llseek, | ||||||
| 	.read		= tty_read, | 	.read_iter	= tty_read, | ||||||
| 	.write_iter	= tty_write, | 	.write_iter	= tty_write, | ||||||
|  | 	.splice_read	= generic_file_splice_read, | ||||||
| 	.splice_write	= iter_file_splice_write, | 	.splice_write	= iter_file_splice_write, | ||||||
| 	.poll		= tty_poll, | 	.poll		= tty_poll, | ||||||
| 	.unlocked_ioctl	= tty_ioctl, | 	.unlocked_ioctl	= tty_ioctl, | ||||||
| @ -490,8 +491,9 @@ static const struct file_operations tty_fops = { | |||||||
| 
 | 
 | ||||||
| static const struct file_operations console_fops = { | static const struct file_operations console_fops = { | ||||||
| 	.llseek		= no_llseek, | 	.llseek		= no_llseek, | ||||||
| 	.read		= tty_read, | 	.read_iter	= tty_read, | ||||||
| 	.write_iter	= redirected_tty_write, | 	.write_iter	= redirected_tty_write, | ||||||
|  | 	.splice_read	= generic_file_splice_read, | ||||||
| 	.splice_write	= iter_file_splice_write, | 	.splice_write	= iter_file_splice_write, | ||||||
| 	.poll		= tty_poll, | 	.poll		= tty_poll, | ||||||
| 	.unlocked_ioctl	= tty_ioctl, | 	.unlocked_ioctl	= tty_ioctl, | ||||||
| @ -844,16 +846,17 @@ static void tty_update_time(struct timespec64 *time) | |||||||
|  * data or clears the cookie. The cookie may be something that the |  * data or clears the cookie. The cookie may be something that the | ||||||
|  * ldisc maintains state for and needs to free. |  * ldisc maintains state for and needs to free. | ||||||
|  */ |  */ | ||||||
| static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct file *file, | static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, | ||||||
| 		char __user *buf, size_t count) | 		struct file *file, struct iov_iter *to) | ||||||
| { | { | ||||||
| 	int retval = 0; | 	int retval = 0; | ||||||
| 	void *cookie = NULL; | 	void *cookie = NULL; | ||||||
| 	unsigned long offset = 0; | 	unsigned long offset = 0; | ||||||
| 	char kernel_buf[64]; | 	char kernel_buf[64]; | ||||||
|  | 	size_t count = iov_iter_count(to); | ||||||
| 
 | 
 | ||||||
| 	do { | 	do { | ||||||
| 		int size, uncopied; | 		int size, copied; | ||||||
| 
 | 
 | ||||||
| 		size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count; | 		size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count; | ||||||
| 		size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset); | 		size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset); | ||||||
| @ -869,10 +872,9 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct | |||||||
| 			return size; | 			return size; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		uncopied = copy_to_user(buf+offset, kernel_buf, size); | 		copied = copy_to_iter(kernel_buf, size, to); | ||||||
| 		size -= uncopied; | 		offset += copied; | ||||||
| 		offset += size; | 		count -= copied; | ||||||
| 		count -= size; |  | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * If the user copy failed, we still need to do another ->read() | 		 * If the user copy failed, we still need to do another ->read() | ||||||
| @ -880,7 +882,7 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct | |||||||
| 		 * | 		 * | ||||||
| 		 * But make sure size is zeroed. | 		 * But make sure size is zeroed. | ||||||
| 		 */ | 		 */ | ||||||
| 		if (unlikely(uncopied)) { | 		if (unlikely(copied != size)) { | ||||||
| 			count = 0; | 			count = 0; | ||||||
| 			retval = -EFAULT; | 			retval = -EFAULT; | ||||||
| 		} | 		} | ||||||
| @ -907,10 +909,10 @@ static int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, struct | |||||||
|  *	read calls may be outstanding in parallel. |  *	read calls may be outstanding in parallel. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| static ssize_t tty_read(struct file *file, char __user *buf, size_t count, | static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to) | ||||||
| 			loff_t *ppos) |  | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
|  | 	struct file *file = iocb->ki_filp; | ||||||
| 	struct inode *inode = file_inode(file); | 	struct inode *inode = file_inode(file); | ||||||
| 	struct tty_struct *tty = file_tty(file); | 	struct tty_struct *tty = file_tty(file); | ||||||
| 	struct tty_ldisc *ld; | 	struct tty_ldisc *ld; | ||||||
| @ -923,11 +925,9 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, | |||||||
| 	/* We want to wait for the line discipline to sort out in this
 | 	/* We want to wait for the line discipline to sort out in this
 | ||||||
| 	   situation */ | 	   situation */ | ||||||
| 	ld = tty_ldisc_ref_wait(tty); | 	ld = tty_ldisc_ref_wait(tty); | ||||||
| 	if (!ld) |  | ||||||
| 		return hung_up_tty_read(file, buf, count, ppos); |  | ||||||
| 	i = -EIO; | 	i = -EIO; | ||||||
| 	if (ld->ops->read) | 	if (ld && ld->ops->read) | ||||||
| 		i = iterate_tty_read(ld, tty, file, buf, count); | 		i = iterate_tty_read(ld, tty, file, to); | ||||||
| 	tty_ldisc_deref(ld); | 	tty_ldisc_deref(ld); | ||||||
| 
 | 
 | ||||||
| 	if (i > 0) | 	if (i > 0) | ||||||
| @ -2927,7 +2927,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd, | |||||||
| 
 | 
 | ||||||
| static int this_tty(const void *t, struct file *file, unsigned fd) | static int this_tty(const void *t, struct file *file, unsigned fd) | ||||||
| { | { | ||||||
| 	if (likely(file->f_op->read != tty_read)) | 	if (likely(file->f_op->read_iter != tty_read)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	return file_tty(file) != t ? 0 : fd + 1; | 	return file_tty(file) != t ? 0 : fd + 1; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user