summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Viro <viro@www.linux.org.uk>2003-05-24 21:40:43 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-05-24 21:40:43 -0700
commitbf78f301f7d0fa9f0a96e29bc2a1b8796c5c43bc (patch)
treee29356dd59472a2164e65f59bd5a38492cbf9105
parent6a7b3d0041d73f9fa628591181cf6e46a18c8b3f (diff)
[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.
-rw-r--r--drivers/char/tty_io.c89
-rw-r--r--include/linux/tty.h1
2 files changed, 51 insertions, 39 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 01278bf90344..4deacb6d95ed 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -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;
@@ -419,6 +415,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();
@@ -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
- tty = (struct tty_struct *)file->private_data;
+ /* 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:
diff --git a/include/linux/tty.h b/include/linux/tty.h
index fa5529ad5c6e..47fa75d9ace0 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -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;