#include <linux/device.h>
#include <linux/idr.h>
#include <linux/wait.h>
+#include <linux/bitops.h>
#include <asm/uaccess.h>
#include <asm/system.h>
-#include <asm/bitops.h>
#include <linux/kbd_kern.h>
#include <linux/vt_kern.h>
extern int pty_limit; /* Config limit on Unix98 ptys */
static DEFINE_IDR(allocated_ptys);
static DECLARE_MUTEX(allocated_ptys_lock);
+static int ptmx_open(struct inode *, struct file *);
#endif
extern void disable_early_printk(void);
ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
-static int ptmx_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg);
static inline void free_tty_struct(struct tty_struct *tty)
{
+ kfree(tty->write_buf);
kfree(tty);
}
* callers who will do ldisc lookups and cannot sleep.
*/
-static spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(tty_ldisc_lock);
static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
static struct tty_ldisc tty_ldiscs[NR_LDISCS]; /* line disc dispatch table */
EXPORT_SYMBOL_GPL(tty_ldisc_put);
-void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
+static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
{
tty->ldisc = *ld;
tty->ldisc.refcount = 0;
/*
* This routine returns a tty driver structure, given a device number
*/
-struct tty_driver *get_tty_driver(dev_t device, int *index)
+static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;
.release = tty_release,
};
-static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(redirect_lock);
static struct file *redirect;
/**
* but doesn't hold any locks, so we need to make sure we have the appropriate
* locks for what we're doing..
*/
-void do_tty_hangup(void *data)
+static void do_tty_hangup(void *data)
{
struct tty_struct *tty = (struct tty_struct *) data;
struct file * cons_filp = NULL;
lock_kernel();
+ down(&tty_sem);
tty = current->signal->tty;
if (tty) {
tty_pgrp = tty->pgrp;
+ up(&tty_sem);
if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
} else {
kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);
kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);
}
+ up(&tty_sem);
unlock_kernel();
return;
}
kill_pg(tty_pgrp, SIGCONT, on_exit);
}
+ /* Must lock changes to tty_old_pgrp */
+ down(&tty_sem);
current->signal->tty_old_pgrp = 0;
tty->session = 0;
tty->pgrp = -1;
+ /* Now clear signal->tty under the lock */
read_lock(&tasklist_lock);
do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {
p->signal->tty = NULL;
} while_each_task_pid(current->signal->session, PIDTYPE_SID, p);
read_unlock(&tasklist_lock);
+ up(&tty_sem);
unlock_kernel();
}
tty_ldisc_deref(ld);
unlock_kernel();
if (i > 0)
- inode->i_atime = CURRENT_TIME;
+ inode->i_atime = current_fs_time(inode->i_sb);
return i;
}
* denial-of-service type attacks
*/
static inline ssize_t do_tty_write(
- ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t),
+ ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
- const unsigned char __user *buf,
+ const char __user *buf,
size_t count)
{
ssize_t ret = 0, written = 0;
+ unsigned int chunk;
if (down_interruptible(&tty->atomic_write)) {
return -ERESTARTSYS;
}
- if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {
+
+ /*
+ * We chunk up writes into a temporary buffer. This
+ * simplifies low-level drivers immensely, since they
+ * don't have locking issues and user mode accesses.
+ *
+ * But if TTY_NO_WRITE_SPLIT is set, we should use a
+ * big chunk-size..
+ *
+ * The default chunk-size is 2kB, because the NTTY
+ * layer has problems with bigger chunks. It will
+ * claim to be able to handle more characters than
+ * it actually does.
+ */
+ chunk = 2048;
+ if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
+ chunk = 65536;
+ if (count < chunk)
+ chunk = count;
+
+ /* write_buf/write_cnt is protected by the atomic_write semaphore */
+ if (tty->write_cnt < chunk) {
+ unsigned char *buf;
+
+ if (chunk < 1024)
+ chunk = 1024;
+
+ buf = kmalloc(chunk, GFP_KERNEL);
+ if (!buf) {
+ up(&tty->atomic_write);
+ return -ENOMEM;
+ }
+ kfree(tty->write_buf);
+ tty->write_cnt = chunk;
+ tty->write_buf = buf;
+ }
+
+ /* Do the write .. */
+ for (;;) {
+ size_t size = count;
+ if (size > chunk)
+ size = chunk;
+ ret = -EFAULT;
+ if (copy_from_user(tty->write_buf, buf, size))
+ break;
lock_kernel();
- written = write(tty, file, buf, count);
+ ret = write(tty, file, tty->write_buf, size);
unlock_kernel();
- } else {
- for (;;) {
- unsigned long size = max((unsigned long)PAGE_SIZE*2, 16384UL);
- if (size > count)
- size = count;
- lock_kernel();
- ret = write(tty, file, buf, size);
- unlock_kernel();
- if (ret <= 0)
- break;
- written += ret;
- buf += ret;
- count -= ret;
- if (!count)
- break;
- ret = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- cond_resched();
- }
+ if (ret <= 0)
+ break;
+ written += ret;
+ buf += ret;
+ count -= ret;
+ if (!count)
+ break;
+ ret = -ERESTARTSYS;
+ if (signal_pending(current))
+ break;
+ cond_resched();
}
if (written) {
- file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+ struct inode *inode = file->f_dentry->d_inode;
+ inode->i_mtime = current_fs_time(inode->i_sb);
ret = written;
}
up(&tty->atomic_write);
if (!ld->write)
ret = -EIO;
else
- ret = do_tty_write(ld->write, tty, file,
- (const unsigned char __user *)buf, count);
+ ret = do_tty_write(ld->write, tty, file, buf, count);
tty_ldisc_deref(ld);
return ret;
}
struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
int retval=0;
- /*
- * Check whether we need to acquire the tty semaphore to avoid
- * race conditions. For now, play it safe.
- */
- down(&tty_sem);
-
/* check whether we're reopening an existing tty */
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
tty = devpts_get_tty(idx);
/* All paths come through here to release the semaphore */
end_init:
- up(&tty_sem);
return retval;
/* Release locally allocated memory ... nothing placed in slots */
* each iteration we avoid any problems.
*/
while (1) {
+ /* Guard against races with tty->count changes elsewhere and
+ opens on /dev/tty */
+
+ down(&tty_sem);
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
+ up(&tty_sem);
do_sleep = 0;
if (tty_closing) {
* both sides, and we've completed the last operation that could
* block, so it's safe to proceed with closing.
*/
+
+ down(&tty_sem);
if (pty_master) {
if (--o_tty->count < 0) {
printk(KERN_WARNING "release_dev: bad pty slave count "
tty->count, tty_name(tty, buf));
tty->count = 0;
}
-
+ up(&tty_sem);
+
/*
* We've decremented tty->count, so we need to remove this file
* descriptor off the tty->tty_files list; this serves two
noctty = filp->f_flags & O_NOCTTY;
index = -1;
retval = 0;
+
+ down(&tty_sem);
if (device == MKDEV(TTYAUX_MAJOR,0)) {
- if (!current->signal->tty)
+ if (!current->signal->tty) {
+ up(&tty_sem);
return -ENXIO;
+ }
driver = current->signal->tty->driver;
index = current->signal->tty->index;
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
noctty = 1;
goto got_driver;
}
+ up(&tty_sem);
return -ENODEV;
}
driver = get_tty_driver(device, &index);
- if (!driver)
+ if (!driver) {
+ up(&tty_sem);
return -ENODEV;
+ }
got_driver:
retval = init_dev(driver, index, &tty);
+ up(&tty_sem);
if (retval)
return retval;
}
up(&allocated_ptys_lock);
+ down(&tty_sem);
retval = init_dev(ptm_driver, index, &tty);
+ up(&tty_sem);
+
if (retval)
goto out;
static int tioccons(struct file *file)
{
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
if (file->f_op->write == redirected_tty_write) {
struct file *f;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
spin_lock(&redirect_lock);
f = redirect;
redirect = NULL;
static int send_break(struct tty_struct *tty, int duration)
{
- set_current_state(TASK_INTERRUPTIBLE);
-
tty->driver->break_ctl(tty, -1);
- if (!signal_pending(current))
+ if (!signal_pending(current)) {
+ set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(duration);
+ }
tty->driver->break_ctl(tty, 0);
if (signal_pending(current))
return -EINTR;
tty_ldisc_deref(disc);
}
-/*
- * Call the ldisc flush directly from a driver. This function may
- * return an error and need retrying by the user.
- */
-
-int tty_push_data(struct tty_struct *tty, unsigned char *cp, unsigned char *fp, int count)
-{
- int ret = 0;
- struct tty_ldisc *disc;
-
- disc = tty_ldisc_ref(tty);
- if(test_bit(TTY_DONT_FLIP, &tty->flags))
- ret = -EAGAIN;
- else if(disc == NULL)
- ret = -EIO;
- else
- disc->receive_buf(tty, cp, fp, count);
- tty_ldisc_deref(disc);
- return ret;
-
-}
-
/*
* Routine which returns the baud rate of the tty
*
*/
static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
{
- tty->driver->write(tty, 0, &ch, 1);
+ tty->driver->write(tty, &ch, 1);
}
static struct class_simple *tty_class;
So I haven't moved it. dwmw2 */
rs_360_init();
#endif
- call = &__con_initcall_start;
- while (call < &__con_initcall_end) {
+ call = __con_initcall_start;
+ while (call < __con_initcall_end) {
(*call)();
call++;
}