Commit bf78f301 authored by Alexander Viro's avatar Alexander Viro Committed by Linus Torvalds

[PATCH] TIOCCONS fix

This fixes the TIOCCONS race; it's the same as the 2.4 patch except for
a fix for a brown-paperbag bug in it.
parent 6a7b3d00
......@@ -132,12 +132,6 @@ extern struct tty_driver pts_driver; /* Unix98 pty slaves; for /dev/ptmx */
extern void disable_early_printk(void);
/*
* redirect is the pseudo-tty that console output
* is redirected to if asked by TIOCCONS.
*/
static struct tty_struct *redirect;
static void initialize_tty_struct(struct tty_struct *tty);
static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
......@@ -400,6 +394,8 @@ static struct file_operations hung_up_tty_fops = {
.release = tty_release,
};
static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;
static struct file *redirect;
/*
* This can be called by the "eventd" kernel thread. That is process synchronous,
* but doesn't hold any locks, so we need to make sure we have the appropriate
......@@ -409,7 +405,7 @@ void do_tty_hangup(void *data)
{
struct tty_struct *tty = (struct tty_struct *) data;
struct file * cons_filp = NULL;
struct file *filp;
struct file *filp, *f = NULL;
struct task_struct *p;
struct pid *pid;
int closecount = 0, n;
......@@ -420,6 +416,15 @@ void do_tty_hangup(void *data)
/* inuse_filps is protected by the single kernel lock */
lock_kernel();
spin_lock(&redirect_lock);
if (redirect && redirect->private_data == tty) {
f = redirect;
redirect = NULL;
}
spin_unlock(&redirect_lock);
if (f)
fput(f);
check_tty_count(tty, "do_tty_hangup");
file_list_lock();
list_for_each_entry(filp, &tty->tty_files, f_list) {
......@@ -726,39 +731,40 @@ static ssize_t tty_write(struct file * file, const char * buf, size_t count,
{
int is_console;
struct tty_struct * tty;
struct inode *inode;
/* Can't seek (pwrite) on ttys. */
if (ppos != &file->f_pos)
return -ESPIPE;
struct inode *inode = file->f_dentry->d_inode;
/*
* For now, we redirect writes from /dev/console as
* well as /dev/tty0.
*/
inode = file->f_dentry->d_inode;
is_console = IS_SYSCONS_DEV(inode->i_rdev) ||
IS_CONSOLE_DEV(inode->i_rdev);
if (is_console && redirect)
tty = redirect;
else
/* Can't seek (pwrite) on ttys. */
if (ppos != &file->f_pos)
return -ESPIPE;
if (is_console) {
struct file *p = NULL;
spin_lock(&redirect_lock);
if (redirect) {
get_file(redirect);
p = redirect;
}
spin_unlock(&redirect_lock);
if (p) {
ssize_t res = vfs_write(p, buf, count, &p->f_pos);
fput(p);
return res;
}
}
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
return -EIO;
if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
#if 0
if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
(current->tty == tty) && (tty->pgrp != current->pgrp)) {
if (is_orphaned_pgrp(current->pgrp))
return -EIO;
if (!is_ignored(SIGTTOU)) {
(void) kill_pg(current->pgrp, SIGTTOU, 1);
return -ERESTARTSYS;
}
}
#endif
if (!tty->ldisc.write)
return -EIO;
return do_tty_write(tty->ldisc.write, tty, file,
......@@ -1221,7 +1227,7 @@ static void release_dev(struct file * filp)
/*
* If _either_ side is closing, make sure there aren't any
* processes that still think tty or o_tty is their controlling
* tty. Also, clear redirect if it points to either tty.
* tty.
*/
if (tty_closing || o_tty_closing) {
struct task_struct *p;
......@@ -1235,9 +1241,6 @@ static void release_dev(struct file * filp)
for_each_task_pid(o_tty->session, PIDTYPE_SID, p,l, pid)
p->tty = NULL;
read_unlock(&tasklist_lock);
if (redirect == tty || (o_tty && redirect == o_tty))
redirect = NULL;
}
/* check whether both sides are closing ... */
......@@ -1526,19 +1529,29 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
return 0;
}
static int tioccons(struct inode *inode,
struct tty_struct *tty, struct tty_struct *real_tty)
static int tioccons(struct inode *inode, struct file *file)
{
if (IS_SYSCONS_DEV(inode->i_rdev) ||
IS_CONSOLE_DEV(inode->i_rdev)) {
struct file *f;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
spin_lock(&redirect_lock);
f = redirect;
redirect = NULL;
spin_unlock(&redirect_lock);
if (f)
fput(f);
return 0;
}
if (redirect)
spin_lock(&redirect_lock);
if (redirect) {
spin_unlock(&redirect_lock);
return -EBUSY;
redirect = real_tty;
}
get_file(file);
redirect = file;
spin_unlock(&redirect_lock);
return 0;
}
......@@ -1786,7 +1799,7 @@ int tty_ioctl(struct inode * inode, struct file * file,
case TIOCSWINSZ:
return tiocswinsz(tty, real_tty, (struct winsize *) arg);
case TIOCCONS:
return tioccons(inode, tty, real_tty);
return real_tty!=tty ? -EINVAL : tioccons(inode, file);
case FIONBIO:
return fionbio(file, (int *) arg);
case TIOCEXCL:
......
......@@ -345,7 +345,6 @@ struct tty_struct {
extern void tty_write_flush(struct tty_struct *);
extern struct termios tty_std_termios;
extern struct tty_struct * redirect;
extern struct tty_ldisc ldiscs[];
extern int fg_console, last_console, want_console;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment