forked from mirrors/linux
		
	tty: implement write_iter
This makes the tty layer use the .write_iter() function instead of the traditional .write() functionality. That allows writev(), but more importantly also makes it possible to enable .splice_write() for ttys, reinstating the "splice to tty" functionality that was lost in commit36e2c7421f("fs: don't allow splice read/write without explicit ops"). 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
							
								
									2c85ebc57b
								
							
						
					
					
						commit
						9bb48c82ac
					
				
					 1 changed files with 26 additions and 22 deletions
				
			
		| 
						 | 
					@ -143,9 +143,8 @@ LIST_HEAD(tty_drivers);			/* linked list of tty drivers */
 | 
				
			||||||
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 file *, char __user *, size_t, loff_t *);
 | 
				
			||||||
static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
 | 
					static ssize_t tty_write(struct kiocb *, struct iov_iter *);
 | 
				
			||||||
ssize_t redirected_tty_write(struct file *, const char __user *,
 | 
					ssize_t redirected_tty_write(struct kiocb *, struct iov_iter *);
 | 
				
			||||||
							size_t, loff_t *);
 | 
					 | 
				
			||||||
static __poll_t tty_poll(struct file *, poll_table *);
 | 
					static __poll_t tty_poll(struct file *, poll_table *);
 | 
				
			||||||
static int tty_open(struct inode *, struct file *);
 | 
					static int tty_open(struct inode *, struct file *);
 | 
				
			||||||
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 | 
					long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 | 
				
			||||||
| 
						 | 
					@ -478,7 +477,8 @@ 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		= tty_read,
 | 
				
			||||||
	.write		= tty_write,
 | 
						.write_iter	= tty_write,
 | 
				
			||||||
 | 
						.splice_write	= iter_file_splice_write,
 | 
				
			||||||
	.poll		= tty_poll,
 | 
						.poll		= tty_poll,
 | 
				
			||||||
	.unlocked_ioctl	= tty_ioctl,
 | 
						.unlocked_ioctl	= tty_ioctl,
 | 
				
			||||||
	.compat_ioctl	= tty_compat_ioctl,
 | 
						.compat_ioctl	= tty_compat_ioctl,
 | 
				
			||||||
| 
						 | 
					@ -491,7 +491,8 @@ 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		= tty_read,
 | 
				
			||||||
	.write		= redirected_tty_write,
 | 
						.write_iter	= redirected_tty_write,
 | 
				
			||||||
 | 
						.splice_write	= iter_file_splice_write,
 | 
				
			||||||
	.poll		= tty_poll,
 | 
						.poll		= tty_poll,
 | 
				
			||||||
	.unlocked_ioctl	= tty_ioctl,
 | 
						.unlocked_ioctl	= tty_ioctl,
 | 
				
			||||||
	.compat_ioctl	= tty_compat_ioctl,
 | 
						.compat_ioctl	= tty_compat_ioctl,
 | 
				
			||||||
| 
						 | 
					@ -607,9 +608,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
 | 
				
			||||||
	/* This breaks for file handles being sent over AF_UNIX sockets ? */
 | 
						/* This breaks for file handles being sent over AF_UNIX sockets ? */
 | 
				
			||||||
	list_for_each_entry(priv, &tty->tty_files, list) {
 | 
						list_for_each_entry(priv, &tty->tty_files, list) {
 | 
				
			||||||
		filp = priv->file;
 | 
							filp = priv->file;
 | 
				
			||||||
		if (filp->f_op->write == redirected_tty_write)
 | 
							if (filp->f_op->write_iter == redirected_tty_write)
 | 
				
			||||||
			cons_filp = filp;
 | 
								cons_filp = filp;
 | 
				
			||||||
		if (filp->f_op->write != tty_write)
 | 
							if (filp->f_op->write_iter != tty_write)
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		closecount++;
 | 
							closecount++;
 | 
				
			||||||
		__tty_fasync(-1, filp, 0);	/* can't block */
 | 
							__tty_fasync(-1, filp, 0);	/* can't block */
 | 
				
			||||||
| 
						 | 
					@ -902,9 +903,9 @@ static inline ssize_t do_tty_write(
 | 
				
			||||||
	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
 | 
						ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
 | 
				
			||||||
	struct tty_struct *tty,
 | 
						struct tty_struct *tty,
 | 
				
			||||||
	struct file *file,
 | 
						struct file *file,
 | 
				
			||||||
	const char __user *buf,
 | 
						struct iov_iter *from)
 | 
				
			||||||
	size_t count)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						size_t count = iov_iter_count(from);
 | 
				
			||||||
	ssize_t ret, written = 0;
 | 
						ssize_t ret, written = 0;
 | 
				
			||||||
	unsigned int chunk;
 | 
						unsigned int chunk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -956,14 +957,20 @@ static inline ssize_t do_tty_write(
 | 
				
			||||||
		size_t size = count;
 | 
							size_t size = count;
 | 
				
			||||||
		if (size > chunk)
 | 
							if (size > chunk)
 | 
				
			||||||
			size = chunk;
 | 
								size = chunk;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = -EFAULT;
 | 
							ret = -EFAULT;
 | 
				
			||||||
		if (copy_from_user(tty->write_buf, buf, size))
 | 
							if (copy_from_iter(tty->write_buf, size, from) != size)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ret = write(tty, file, tty->write_buf, size);
 | 
							ret = write(tty, file, tty->write_buf, size);
 | 
				
			||||||
		if (ret <= 0)
 | 
							if (ret <= 0)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* FIXME! Have Al check this! */
 | 
				
			||||||
 | 
							if (ret != size)
 | 
				
			||||||
 | 
								iov_iter_revert(from, size-ret);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		written += ret;
 | 
							written += ret;
 | 
				
			||||||
		buf += ret;
 | 
					 | 
				
			||||||
		count -= ret;
 | 
							count -= ret;
 | 
				
			||||||
		if (!count)
 | 
							if (!count)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					@ -1023,9 +1030,9 @@ void tty_write_message(struct tty_struct *tty, char *msg)
 | 
				
			||||||
 *	write method will not be invoked in parallel for each device.
 | 
					 *	write method will not be invoked in parallel for each device.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static ssize_t tty_write(struct file *file, const char __user *buf,
 | 
					static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
 | 
				
			||||||
						size_t count, loff_t *ppos)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						struct file *file = iocb->ki_filp;
 | 
				
			||||||
	struct tty_struct *tty = file_tty(file);
 | 
						struct tty_struct *tty = file_tty(file);
 | 
				
			||||||
 	struct tty_ldisc *ld;
 | 
					 	struct tty_ldisc *ld;
 | 
				
			||||||
	ssize_t ret;
 | 
						ssize_t ret;
 | 
				
			||||||
| 
						 | 
					@ -1038,18 +1045,15 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
 | 
				
			||||||
	if (tty->ops->write_room == NULL)
 | 
						if (tty->ops->write_room == NULL)
 | 
				
			||||||
		tty_err(tty, "missing write_room method\n");
 | 
							tty_err(tty, "missing write_room method\n");
 | 
				
			||||||
	ld = tty_ldisc_ref_wait(tty);
 | 
						ld = tty_ldisc_ref_wait(tty);
 | 
				
			||||||
	if (!ld)
 | 
						if (!ld || !ld->ops->write)
 | 
				
			||||||
		return hung_up_tty_write(file, buf, count, ppos);
 | 
					 | 
				
			||||||
	if (!ld->ops->write)
 | 
					 | 
				
			||||||
		ret = -EIO;
 | 
							ret = -EIO;
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
		ret = do_tty_write(ld->ops->write, tty, file, buf, count);
 | 
							ret = do_tty_write(ld->ops->write, tty, file, from);
 | 
				
			||||||
	tty_ldisc_deref(ld);
 | 
						tty_ldisc_deref(ld);
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ssize_t redirected_tty_write(struct file *file, const char __user *buf,
 | 
					ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
 | 
				
			||||||
						size_t count, loff_t *ppos)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct file *p = NULL;
 | 
						struct file *p = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1060,11 +1064,11 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (p) {
 | 
						if (p) {
 | 
				
			||||||
		ssize_t res;
 | 
							ssize_t res;
 | 
				
			||||||
		res = vfs_write(p, buf, count, &p->f_pos);
 | 
							res = vfs_iocb_iter_write(p, iocb, iter);
 | 
				
			||||||
		fput(p);
 | 
							fput(p);
 | 
				
			||||||
		return res;
 | 
							return res;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return tty_write(file, buf, count, ppos);
 | 
						return tty_write(iocb, iter);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -2293,7 +2297,7 @@ static int tioccons(struct file *file)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!capable(CAP_SYS_ADMIN))
 | 
						if (!capable(CAP_SYS_ADMIN))
 | 
				
			||||||
		return -EPERM;
 | 
							return -EPERM;
 | 
				
			||||||
	if (file->f_op->write == redirected_tty_write) {
 | 
						if (file->f_op->write_iter == redirected_tty_write) {
 | 
				
			||||||
		struct file *f;
 | 
							struct file *f;
 | 
				
			||||||
		spin_lock(&redirect_lock);
 | 
							spin_lock(&redirect_lock);
 | 
				
			||||||
		f = redirect;
 | 
							f = redirect;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue