VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / char / tty_io.c
index e4607d8..91f530c 100644 (file)
@@ -91,6 +91,7 @@
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <linux/device.h>
+#include <linux/idr.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -128,15 +129,17 @@ DECLARE_MUTEX(tty_sem);
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
 extern int pty_limit;          /* Config limit on Unix98 ptys */
+static DEFINE_IDR(allocated_ptys);
+static DECLARE_MUTEX(allocated_ptys_lock);
 #endif
 
 extern void disable_early_printk(void);
 
 static void initialize_tty_struct(struct tty_struct *tty);
 
-static ssize_t tty_read(struct file *, char *, size_t, loff_t *);
-static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);
-ssize_t redirected_tty_write(struct file *, const char *, 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 *);
+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 tty_release(struct inode *, struct file *);
@@ -339,21 +342,15 @@ int tty_check_change(struct tty_struct * tty)
 
 EXPORT_SYMBOL(tty_check_change);
 
-static ssize_t hung_up_tty_read(struct file * file, char * buf,
+static ssize_t hung_up_tty_read(struct file * file, char __user * buf,
                                size_t count, loff_t *ppos)
 {
-       /* Can't seek (pread) on ttys.  */
-       if (ppos != &file->f_pos)
-               return -ESPIPE;
        return 0;
 }
 
-static ssize_t hung_up_tty_write(struct file * file, const char * buf,
+static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,
                                 size_t count, loff_t *ppos)
 {
-       /* Can't seek (pwrite) on ttys.  */
-       if (ppos != &file->f_pos)
-               return -ESPIPE;
        return -EIO;
 }
 
@@ -638,17 +635,13 @@ void start_tty(struct tty_struct *tty)
 
 EXPORT_SYMBOL(start_tty);
 
-static ssize_t tty_read(struct file * file, char * buf, size_t count, 
+static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 
                        loff_t *ppos)
 {
        int i;
        struct tty_struct * tty;
        struct inode *inode;
 
-       /* Can't seek (pread) on ttys.  */
-       if (ppos != &file->f_pos)
-               return -ESPIPE;
-
        tty = (struct tty_struct *)file->private_data;
        inode = file->f_dentry->d_inode;
        if (tty_paranoia_check(tty, inode, "tty_read"))
@@ -672,10 +665,10 @@ static ssize_t tty_read(struct file * file, char * buf, size_t count,
  * denial-of-service type attacks
  */
 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 __user *, size_t),
        struct tty_struct *tty,
        struct file *file,
-       const unsigned char *buf,
+       const unsigned char __user *buf,
        size_t count)
 {
        ssize_t ret = 0, written = 0;
@@ -717,16 +710,12 @@ static inline ssize_t do_tty_write(
 }
 
 
-static ssize_t tty_write(struct file * file, const char * buf, size_t count,
+static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
                         loff_t *ppos)
 {
        struct tty_struct * tty;
        struct inode *inode = file->f_dentry->d_inode;
 
-       /* Can't seek (pwrite) on ttys.  */
-       if (ppos != &file->f_pos)
-               return -ESPIPE;
-
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_write"))
                return -EIO;
@@ -735,10 +724,10 @@ static ssize_t tty_write(struct file * file, const char * buf, size_t count,
        if (!tty->ldisc.write)
                return -EIO;
        return do_tty_write(tty->ldisc.write, tty, file,
-                           (const unsigned char *)buf, count);
+                           (const unsigned char __user *)buf, count);
 }
 
-ssize_t redirected_tty_write(struct file * file, const char * buf, size_t count,
+ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count,
                         loff_t *ppos)
 {
        struct file *p = NULL;
@@ -752,9 +741,6 @@ ssize_t redirected_tty_write(struct file * file, const char * buf, size_t count,
 
        if (p) {
                ssize_t res;
-               /* Can't seek (pwrite) on ttys.  */
-               if (ppos != &file->f_pos)
-                       return -ESPIPE;
                res = vfs_write(p, buf, count, &p->f_pos);
                fput(p);
                return res;
@@ -1065,6 +1051,7 @@ static void release_dev(struct file * filp)
 {
        struct tty_struct *tty, *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
+       int     devpts_master, devpts;
        int     idx;
        char    buf[64];
        
@@ -1079,6 +1066,8 @@ static void release_dev(struct file * filp)
        idx = tty->index;
        pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
                      tty->driver->subtype == PTY_TYPE_MASTER);
+       devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
+       devpts_master = pty_master && devpts;
        o_tty = tty->link;
 
 #ifdef TTY_PARANOIA_CHECK
@@ -1266,6 +1255,18 @@ static void release_dev(struct file * filp)
        printk(KERN_DEBUG "freeing tty structure...");
 #endif
 
+       /*
+        * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
+        * kill any delayed work.
+        */
+       clear_bit(TTY_DONT_FLIP, &tty->flags);
+       cancel_delayed_work(&tty->flip.work);
+
+       /*
+        * Wait for ->hangup_work and ->flip.work handlers to terminate
+        */
+       flush_scheduled_work();
+
        /*
         * Shutdown the current line discipline, and reset it to N_TTY.
         * N.B. why reset ldisc when we're releasing the memory??
@@ -1282,24 +1283,22 @@ static void release_dev(struct file * filp)
                module_put(o_tty->ldisc.owner);
                o_tty->ldisc = ldiscs[N_TTY];
        }
-       
-       /*
-        * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
-        * kill any delayed work.
-        */
-       clear_bit(TTY_DONT_FLIP, &tty->flags);
-       cancel_delayed_work(&tty->flip.work);
 
        /*
-        * Wait for ->hangup_work and ->flip.work handlers to terminate
-        */
-       flush_scheduled_work();
-
-       /* 
         * The release_mem function takes care of the details of clearing
         * the slots and preserving the termios structure.
         */
        release_mem(tty, idx);
+
+#ifdef CONFIG_UNIX98_PTYS
+       /* Make this pty number available for reallocation */
+       if (devpts) {
+               down(&allocated_ptys_lock);
+               idr_remove(&allocated_ptys, idx);
+               up(&allocated_ptys_lock);
+       }
+#endif
+
 }
 
 /*
@@ -1322,8 +1321,13 @@ static int tty_open(struct inode * inode, struct file * filp)
        int index;
        dev_t device = inode->i_rdev;
        unsigned short saved_flags = filp->f_flags;
+
+       nonseekable_open(inode, filp);
 retry_open:
        noctty = filp->f_flags & O_NOCTTY;
+       index  = -1;
+       retval = 0;
+
        if (device == MKDEV(TTYAUX_MAJOR,0)) {
                if (!current->signal->tty)
                        return -ENXIO;
@@ -1344,13 +1348,8 @@ retry_open:
        }
 #endif
        if (device == MKDEV(TTYAUX_MAJOR,1)) {
-               struct console *c = console_drivers;
-               for (c = console_drivers; c; c = c->next) {
-                       if (!c->device)
-                               continue;
-                       driver = c->device(c, &index);
-                       if (!driver)
-                               continue;
+               driver = console_device(&index);
+               if (driver) {
                        /* Don't let /dev/console block */
                        filp->f_flags |= O_NONBLOCK;
                        noctty = 1;
@@ -1361,24 +1360,40 @@ retry_open:
 
 #ifdef CONFIG_UNIX98_PTYS
        if (device == MKDEV(TTYAUX_MAJOR,2)) {
+               int idr_ret;
+
                /* find a device that is not in use. */
-               static int next_ptmx_dev = 0;
-               retval = -1;
+               down(&allocated_ptys_lock);
+               if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
+                       up(&allocated_ptys_lock);
+                       return -ENOMEM;
+               }
+               idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
+               if (idr_ret < 0) {
+                       up(&allocated_ptys_lock);
+                       if (idr_ret == -EAGAIN)
+                               return -ENOMEM;
+                       return -EIO;
+               }
+               if (index >= pty_limit) {
+                       idr_remove(&allocated_ptys, index);
+                       up(&allocated_ptys_lock);
+                       return -EIO;
+               }
+               up(&allocated_ptys_lock);
+
                driver = ptm_driver;
-               while (driver->refcount < pty_limit) {
-                       index = next_ptmx_dev;
-                       next_ptmx_dev = (next_ptmx_dev+1) % driver->num;
-                       if (!init_dev(driver, index, &tty))
-                               goto ptmx_found; /* ok! */
+               retval = init_dev(driver, index, &tty);
+               if (retval) {
+                       down(&allocated_ptys_lock);
+                       idr_remove(&allocated_ptys, index);
+                       up(&allocated_ptys_lock);
+                       return retval;
                }
-               return -EIO; /* no free ptys */
-       ptmx_found:
+
                set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
-               if (devpts_pty_new(tty->link)) {
-                       /* BADNESS - need to destroy both ptm and pts! */
-                       return -ENOMEM;
-               }
-               noctty = 1;
+               if (devpts_pty_new(tty->link))
+                       retval = -ENOMEM;
        } else
 #endif
        {
@@ -1400,10 +1415,12 @@ got_driver:
 #ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "opening %s...", tty->name);
 #endif
-       if (tty->driver->open)
-               retval = tty->driver->open(tty, filp);
-       else
-               retval = -ENODEV;
+       if (!retval) {
+               if (tty->driver->open)
+                       retval = tty->driver->open(tty, filp);
+               else
+                       retval = -ENODEV;
+       }
        filp->f_flags = saved_flags;
 
        if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
@@ -1415,6 +1432,14 @@ got_driver:
                       tty->name);
 #endif
 
+#ifdef CONFIG_UNIX98_PTYS
+               if (index != -1) {
+                       down(&allocated_ptys_lock);
+                       idr_remove(&allocated_ptys, index);
+                       up(&allocated_ptys_lock);
+               }
+#endif
+
                release_dev(filp);
                if (retval != -ERESTARTSYS)
                        return retval;
@@ -1490,19 +1515,19 @@ static int tty_fasync(int fd, struct file * filp, int on)
        return 0;
 }
 
-static int tiocsti(struct tty_struct *tty, char * arg)
+static int tiocsti(struct tty_struct *tty, char __user *p)
 {
        char ch, mbz = 0;
 
        if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
                return -EPERM;
-       if (get_user(ch, arg))
+       if (get_user(ch, p))
                return -EFAULT;
        tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
        return 0;
 }
 
-static int tiocgwinsz(struct tty_struct *tty, struct winsize * arg)
+static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
 {
        if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
                return -EFAULT;
@@ -1510,7 +1535,7 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize * arg)
 }
 
 static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
-       struct winsize * arg)
+       struct winsize __user * arg)
 {
        struct winsize tmp_ws;
 
@@ -1565,11 +1590,11 @@ static int tioccons(struct file *file)
 }
 
 
-static int fionbio(struct file *file, int *arg)
+static int fionbio(struct file *file, int __user *p)
 {
        int nonblock;
 
-       if (get_user(nonblock, arg))
+       if (get_user(nonblock, p))
                return -EFAULT;
 
        if (nonblock)
@@ -1620,7 +1645,7 @@ static int tiocsctty(struct tty_struct *tty, int arg)
        return 0;
 }
 
-static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
        /*
         * (tty == real_tty) is a cheap way of
@@ -1628,10 +1653,10 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
         */
        if (tty == real_tty && current->signal->tty != real_tty)
                return -ENOTTY;
-       return put_user(real_tty->pgrp, arg);
+       return put_user(real_tty->pgrp, p);
 }
 
-static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
        pid_t pgrp;
        int retval = tty_check_change(real_tty);
@@ -1644,7 +1669,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
            (current->signal->tty != real_tty) ||
            (real_tty->session != current->signal->session))
                return -ENOTTY;
-       if (get_user(pgrp, (pid_t *) arg))
+       if (get_user(pgrp, p))
                return -EFAULT;
        if (pgrp < 0)
                return -EINVAL;
@@ -1654,7 +1679,7 @@ static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
        return 0;
 }
 
-static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *arg)
+static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
 {
        /*
         * (tty == real_tty) is a cheap way of
@@ -1664,14 +1689,14 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t *
                return -ENOTTY;
        if (real_tty->session <= 0)
                return -ENOTTY;
-       return put_user(real_tty->session, arg);
+       return put_user(real_tty->session, p);
 }
 
-static int tiocsetd(struct tty_struct *tty, int *arg)
+static int tiocsetd(struct tty_struct *tty, int __user *p)
 {
        int ldisc;
 
-       if (get_user(ldisc, arg))
+       if (get_user(ldisc, p))
                return -EFAULT;
        return tty_set_ldisc(tty, ldisc);
 }
@@ -1690,7 +1715,7 @@ static int send_break(struct tty_struct *tty, int duration)
 }
 
 static int
-tty_tiocmget(struct tty_struct *tty, struct file *file, unsigned long arg)
+tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p)
 {
        int retval = -EINVAL;
 
@@ -1698,21 +1723,21 @@ tty_tiocmget(struct tty_struct *tty, struct file *file, unsigned long arg)
                retval = tty->driver->tiocmget(tty, file);
 
                if (retval >= 0)
-                       retval = put_user(retval, (int *)arg);
+                       retval = put_user(retval, p);
        }
        return retval;
 }
 
 static int
 tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
-            unsigned long arg)
+            unsigned __user *p)
 {
        int retval = -EINVAL;
 
        if (tty->driver->tiocmset) {
                unsigned int set, clear, val;
 
-               retval = get_user(val, (unsigned int *)arg);
+               retval = get_user(val, p);
                if (retval)
                        return retval;
 
@@ -1745,6 +1770,7 @@ int tty_ioctl(struct inode * inode, struct file * file,
              unsigned int cmd, unsigned long arg)
 {
        struct tty_struct *tty, *real_tty;
+       void __user *p = (void __user *)arg;
        int retval;
        
        tty = (struct tty_struct *)file->private_data;
@@ -1802,15 +1828,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
 
        switch (cmd) {
                case TIOCSTI:
-                       return tiocsti(tty, (char *)arg);
+                       return tiocsti(tty, p);
                case TIOCGWINSZ:
-                       return tiocgwinsz(tty, (struct winsize *) arg);
+                       return tiocgwinsz(tty, p);
                case TIOCSWINSZ:
-                       return tiocswinsz(tty, real_tty, (struct winsize *) arg);
+                       return tiocswinsz(tty, real_tty, p);
                case TIOCCONS:
                        return real_tty!=tty ? -EINVAL : tioccons(file);
                case FIONBIO:
-                       return fionbio(file, (int *) arg);
+                       return fionbio(file, p);
                case TIOCEXCL:
                        set_bit(TTY_EXCLUSIVE, &tty->flags);
                        return 0;
@@ -1829,15 +1855,15 @@ int tty_ioctl(struct inode * inode, struct file * file,
                case TIOCSCTTY:
                        return tiocsctty(tty, arg);
                case TIOCGPGRP:
-                       return tiocgpgrp(tty, real_tty, (pid_t *) arg);
+                       return tiocgpgrp(tty, real_tty, p);
                case TIOCSPGRP:
-                       return tiocspgrp(tty, real_tty, (pid_t *) arg);
+                       return tiocspgrp(tty, real_tty, p);
                case TIOCGSID:
-                       return tiocgsid(tty, real_tty, (pid_t *) arg);
+                       return tiocgsid(tty, real_tty, p);
                case TIOCGETD:
-                       return put_user(tty->ldisc.num, (int *) arg);
+                       return put_user(tty->ldisc.num, (int __user *)p);
                case TIOCSETD:
-                       return tiocsetd(tty, (int *) arg);
+                       return tiocsetd(tty, p);
 #ifdef CONFIG_VT
                case TIOCLINUX:
                        return tioclinux(tty, arg);
@@ -1865,12 +1891,12 @@ int tty_ioctl(struct inode * inode, struct file * file,
                        return send_break(tty, arg ? arg*(HZ/10) : HZ/4);
 
                case TIOCMGET:
-                       return tty_tiocmget(tty, file, arg);
+                       return tty_tiocmget(tty, file, p);
 
                case TIOCMSET:
                case TIOCMBIC:
                case TIOCMBIS:
-                       return tty_tiocmset(tty, file, cmd, arg);
+                       return tty_tiocmset(tty, file, cmd, p);
        }
        if (tty->driver->ioctl) {
                int retval = (tty->driver->ioctl)(tty, file, cmd, arg);