fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / compat.c
index 7973687..0ec70e3 100644 (file)
@@ -44,7 +44,9 @@
 #include <linux/nfsd/syscall.h>
 #include <linux/personality.h>
 #include <linux/rwsem.h>
-#include <linux/acct.h>
+#include <linux/tsacct_kern.h>
+#include <linux/highmem.h>
+#include <linux/poll.h>
 #include <linux/mm.h>
 
 #include <net/sock.h>          /* siocdevprivate_ioctl */
@@ -52,8 +54,7 @@
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
 #include <asm/ioctls.h>
-
-extern void sigset_from_compat(sigset_t *set, compat_sigset_t *compat);
+#include "internal.h"
 
 int compat_log = 1;
 
@@ -69,6 +70,8 @@ int compat_printk(const char *fmt, ...)
        return ret;
 }
 
+#include "read_write.h"
+
 /*
  * Not all architectures have sys_utime, so implement this in terms
  * of sys_utimes.
@@ -229,7 +232,7 @@ asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user
        file = fget(fd);
        if (!file)
                goto out;
-       error = vfs_statfs(file->f_dentry, &tmp);
+       error = vfs_statfs(file->f_path.dentry, &tmp);
        if (!error)
                error = put_compat_statfs(buf, &tmp);
        fput(file);
@@ -300,7 +303,7 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c
        file = fget(fd);
        if (!file)
                goto out;
-       error = vfs_statfs(file->f_dentry, &tmp);
+       error = vfs_statfs(file->f_path.dentry, &tmp);
        if (!error)
                error = put_compat_statfs64(buf, &tmp);
        fput(file);
@@ -313,9 +316,6 @@ out:
 #define IOCTL_HASHSIZE 256
 static struct ioctl_trans *ioctl32_hash_table[IOCTL_HASHSIZE];
 
-extern struct ioctl_trans ioctl_start[];
-extern int ioctl_table_size;
-
 static inline unsigned long ioctl32_hash(unsigned long cmd)
 {
        return (((cmd >> 6) ^ (cmd >> 4) ^ cmd)) % IOCTL_HASHSIZE;
@@ -365,7 +365,7 @@ static void compat_ioctl_error(struct file *filp, unsigned int fd,
        /* find the name of the device. */
        path = (char *)__get_free_page(GFP_KERNEL);
        if (path) {
-               fn = d_path(filp->f_dentry, filp->f_vfsmnt, path, PAGE_SIZE);
+               fn = d_path(filp->f_path.dentry, filp->f_path.mnt, path, PAGE_SIZE);
                if (IS_ERR(fn))
                        fn = "?";
        }
@@ -416,7 +416,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
        case FIBMAP:
        case FIGETBSZ:
        case FIONREAD:
-               if (S_ISREG(filp->f_dentry->d_inode->i_mode))
+               if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))
                        break;
                /*FALL THROUGH*/
 
@@ -438,7 +438,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd,
                        goto found_handler;
        }
 
-       if (S_ISSOCK(filp->f_dentry->d_inode->i_mode) &&
+       if (S_ISSOCK(filp->f_path.dentry->d_inode->i_mode) &&
            cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
                error = siocdevprivate_ioctl(fd, cmd, arg);
        } else {
@@ -838,8 +838,6 @@ static int do_nfs4_super_data_conv(void *raw_data)
        return 0;
 }
 
-extern int copy_mount_options (const void __user *, unsigned long *);
-
 #define SMBFS_NAME      "smbfs"
 #define NCPFS_NAME      "ncpfs"
 #define NFS4_NAME      "nfs4"
@@ -1146,7 +1144,9 @@ asmlinkage long compat_sys_getdents64(unsigned int fd,
        lastdirent = buf.previous;
        if (lastdirent) {
                typeof(lastdirent->d_off) d_off = file->f_pos;
-               __put_user_unaligned(d_off, &lastdirent->d_off);
+               error = -EFAULT;
+               if (__put_user_unaligned(d_off, &lastdirent->d_off))
+                       goto out_putf;
                error = count - buf.count;
        }
 
@@ -1161,9 +1161,6 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
                               const struct compat_iovec __user *uvector,
                               unsigned long nr_segs, loff_t *pos)
 {
-       typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *);
-       typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *);
-
        compat_ssize_t tot_len;
        struct iovec iovstack[UIO_FASTIOV];
        struct iovec *iov=iovstack, *vector;
@@ -1246,44 +1243,23 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
        fnv = NULL;
        if (type == READ) {
                fn = file->f_op->read;
-               fnv = file->f_op->readv;
+               fnv = file->f_op->aio_read;
        } else {
                fn = (io_fn_t)file->f_op->write;
-               fnv = file->f_op->writev;
-       }
-       if (fnv) {
-               ret = fnv(file, iov, nr_segs, pos);
-               goto out;
+               fnv = file->f_op->aio_write;
        }
 
-       /* Do it by hand, with file-ops */
-       ret = 0;
-       vector = iov;
-       while (nr_segs > 0) {
-               void __user * base;
-               size_t len;
-               ssize_t nr;
-
-               base = vector->iov_base;
-               len = vector->iov_len;
-               vector++;
-               nr_segs--;
-
-               nr = fn(file, base, len, pos);
+       if (fnv)
+               ret = do_sync_readv_writev(file, iov, nr_segs, tot_len,
+                                               pos, fnv);
+       else
+               ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn);
 
-               if (nr < 0) {
-                       if (!ret) ret = nr;
-                       break;
-               }
-               ret += nr;
-               if (nr != len)
-                       break;
-       }
 out:
        if (iov != iovstack)
                kfree(iov);
        if ((ret + (type == READ)) > 0) {
-               struct dentry *dentry = file->f_dentry;
+               struct dentry *dentry = file->f_path.dentry;
                if (type == READ)
                        fsnotify_access(dentry);
                else
@@ -1306,7 +1282,7 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec, unsign
                goto out;
 
        ret = -EINVAL;
-       if (!file->f_op || (!file->f_op->readv && !file->f_op->read))
+       if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read))
                goto out;
 
        ret = compat_do_readv_writev(READ, file, vec, vlen, &file->f_pos);
@@ -1329,7 +1305,7 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec, unsig
                goto out;
 
        ret = -EINVAL;
-       if (!file->f_op || (!file->f_op->writev && !file->f_op->write))
+       if (!file->f_op || (!file->f_op->aio_write && !file->f_op->write))
                goto out;
 
        ret = compat_do_readv_writev(WRITE, file, vec, vlen, &file->f_pos);
@@ -1344,7 +1320,7 @@ compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
                    unsigned int nr_segs, unsigned int flags)
 {
        unsigned i;
-       struct iovec *iov;
+       struct iovec __user *iov;
        if (nr_segs > UIO_MAXIOV)
                return -EINVAL;
        iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec));
@@ -1637,14 +1613,14 @@ int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
                nr &= ~1UL;
                while (nr) {
                        unsigned long h, l;
-                       __get_user(l, ufdset);
-                       __get_user(h, ufdset+1);
+                       if (__get_user(l, ufdset) || __get_user(h, ufdset+1))
+                               return -EFAULT;
                        ufdset += 2;
                        *fdset++ = h << 32 | l;
                        nr -= 2;
                }
-               if (odd)
-                       __get_user(*fdset, ufdset);
+               if (odd && __get_user(*fdset, ufdset))
+                       return -EFAULT;
        } else {
                /* Tricky, must clear full unsigned long in the
                 * kernel fdset at the end, this makes sure that
@@ -1656,14 +1632,14 @@ int compat_get_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
 }
 
 static
-void compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
-                       unsigned long *fdset)
+int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
+                     unsigned long *fdset)
 {
        unsigned long odd;
        nr = ROUND_UP(nr, __COMPAT_NFDBITS);
 
        if (!ufdset)
-               return;
+               return 0;
 
        odd = nr & 1UL;
        nr &= ~1UL;
@@ -1671,13 +1647,14 @@ void compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
                unsigned long h, l;
                l = *fdset++;
                h = l >> 32;
-               __put_user(l, ufdset);
-               __put_user(h, ufdset+1);
+               if (__put_user(l, ufdset) || __put_user(h, ufdset+1))
+                       return -EFAULT;
                ufdset += 2;
                nr -= 2;
        }
-       if (odd)
-               __put_user(*fdset, ufdset);
+       if (odd && __put_user(*fdset, ufdset))
+               return -EFAULT;
+       return 0;
 }
 
 
@@ -1702,19 +1679,19 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
 {
        fd_set_bits fds;
        char *bits;
-       int size, max_fdset, ret = -EINVAL;
+       int size, max_fds, ret = -EINVAL;
        struct fdtable *fdt;
 
        if (n < 0)
                goto out_nofds;
 
-       /* max_fdset can increase, so grab it once to avoid race */
+       /* max_fds can increase, so grab it once to avoid race */
        rcu_read_lock();
        fdt = files_fdtable(current->files);
-       max_fdset = fdt->max_fdset;
+       max_fds = fdt->max_fds;
        rcu_read_unlock();
-       if (n > max_fdset)
-               n = max_fdset;
+       if (n > max_fds)
+               n = max_fds;
 
        /*
         * We need 6 bitmaps (in/out/ex for both incoming and outgoing),
@@ -1752,10 +1729,10 @@ int compat_core_sys_select(int n, compat_ulong_t __user *inp,
                ret = 0;
        }
 
-       compat_set_fd_set(n, inp, fds.res_in);
-       compat_set_fd_set(n, outp, fds.res_out);
-       compat_set_fd_set(n, exp, fds.res_ex);
-
+       if (compat_set_fd_set(n, inp, fds.res_in) ||
+           compat_set_fd_set(n, outp, fds.res_out) ||
+           compat_set_fd_set(n, exp, fds.res_ex))
+               ret = -EFAULT;
 out:
        kfree(bits);
 out_nofds:
@@ -1863,9 +1840,12 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
 
        } while (!ret && !timeout && tsp && (ts.tv_sec || ts.tv_nsec));
 
-       if (tsp && !(current->personality & STICKY_TIMEOUTS)) {
+       if (tsp) {
                struct compat_timespec rts;
 
+               if (current->personality & STICKY_TIMEOUTS)
+                       goto sticky;
+
                rts.tv_sec = timeout / HZ;
                rts.tv_nsec = (timeout % HZ) * (NSEC_PER_SEC/HZ);
                if (rts.tv_nsec >= NSEC_PER_SEC) {
@@ -1874,7 +1854,19 @@ asmlinkage long compat_sys_pselect7(int n, compat_ulong_t __user *inp,
                }
                if (compat_timespec_compare(&rts, &ts) >= 0)
                        rts = ts;
-               copy_to_user(tsp, &rts, sizeof(rts));
+               if (copy_to_user(tsp, &rts, sizeof(rts))) {
+sticky:
+                       /*
+                        * If an application puts its timeval in read-only
+                        * memory, we don't want the Linux-specific update to
+                        * the timeval to cause a fault after the select has
+                        * completed successfully. However, because we're not
+                        * updating the timeval, we can't restart the system
+                        * call.
+                        */
+                       if (ret == -ERESTARTNOHAND)
+                               ret = -EINTR;
+               }
        }
 
        if (ret == -ERESTARTNOHAND) {