diff options
| author | Alexander Viro <viro@www.linux.org.uk> | 2003-05-24 21:40:43 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2003-05-24 21:40:43 -0700 |
| commit | bf78f301f7d0fa9f0a96e29bc2a1b8796c5c43bc (patch) | |
| tree | e29356dd59472a2164e65f59bd5a38492cbf9105 | |
| parent | 6a7b3d0041d73f9fa628591181cf6e46a18c8b3f (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.c | 89 | ||||
| -rw-r--r-- | include/linux/tty.h | 1 |
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; |
