Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / fs / compat.c
index c580c32..f2944ac 100644 (file)
 #include <linux/nfs4_mount.h>
 #include <linux/syscalls.h>
 #include <linux/ctype.h>
-#include <linux/module.h>
 #include <linux/dirent.h>
 #include <linux/fsnotify.h>
 #include <linux/highuid.h>
-#include <linux/nfsd/syscall.h>
 #include <linux/personality.h>
 #include <linux/rwsem.h>
 #include <linux/tsacct_kern.h>
@@ -132,41 +130,35 @@ asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_tim
 
 static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
 {
-       compat_ino_t ino = stat->ino;
-       typeof(ubuf->st_uid) uid = 0;
-       typeof(ubuf->st_gid) gid = 0;
-       int err;
+       struct compat_stat tmp;
 
-       SET_UID(uid, stat->uid);
-       SET_GID(gid, stat->gid);
+       if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
+               return -EOVERFLOW;
 
-       if ((u64) stat->size > MAX_NON_LFS ||
-           !old_valid_dev(stat->dev) ||
-           !old_valid_dev(stat->rdev))
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.st_dev = old_encode_dev(stat->dev);
+       tmp.st_ino = stat->ino;
+       if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
                return -EOVERFLOW;
-       if (sizeof(ino) < sizeof(stat->ino) && ino != stat->ino)
+       tmp.st_mode = stat->mode;
+       tmp.st_nlink = stat->nlink;
+       if (tmp.st_nlink != stat->nlink)
                return -EOVERFLOW;
-
-       if (clear_user(ubuf, sizeof(*ubuf)))
-               return -EFAULT;
-
-       err  = __put_user(old_encode_dev(stat->dev), &ubuf->st_dev);
-       err |= __put_user(ino, &ubuf->st_ino);
-       err |= __put_user(stat->mode, &ubuf->st_mode);
-       err |= __put_user(stat->nlink, &ubuf->st_nlink);
-       err |= __put_user(uid, &ubuf->st_uid);
-       err |= __put_user(gid, &ubuf->st_gid);
-       err |= __put_user(old_encode_dev(stat->rdev), &ubuf->st_rdev);
-       err |= __put_user(stat->size, &ubuf->st_size);
-       err |= __put_user(stat->atime.tv_sec, &ubuf->st_atime);
-       err |= __put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec);
-       err |= __put_user(stat->mtime.tv_sec, &ubuf->st_mtime);
-       err |= __put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec);
-       err |= __put_user(stat->ctime.tv_sec, &ubuf->st_ctime);
-       err |= __put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec);
-       err |= __put_user(stat->blksize, &ubuf->st_blksize);
-       err |= __put_user(stat->blocks, &ubuf->st_blocks);
-       return err;
+       SET_UID(tmp.st_uid, stat->uid);
+       SET_GID(tmp.st_gid, stat->gid);
+       tmp.st_rdev = old_encode_dev(stat->rdev);
+       if ((u64) stat->size > MAX_NON_LFS)
+               return -EOVERFLOW;
+       tmp.st_size = stat->size;
+       tmp.st_atime = stat->atime.tv_sec;
+       tmp.st_atime_nsec = stat->atime.tv_nsec;
+       tmp.st_mtime = stat->mtime.tv_sec;
+       tmp.st_mtime_nsec = stat->mtime.tv_nsec;
+       tmp.st_ctime = stat->ctime.tv_sec;
+       tmp.st_ctime_nsec = stat->ctime.tv_nsec;
+       tmp.st_blocks = stat->blocks;
+       tmp.st_blksize = stat->blksize;
+       return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
 }
 
 asmlinkage long compat_sys_newstat(const char __user * filename,
@@ -247,50 +239,31 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
            __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
            __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
            __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
-           __put_user(0, &ubuf->f_spare[0]) || 
-           __put_user(0, &ubuf->f_spare[1]) || 
-           __put_user(0, &ubuf->f_spare[2]) || 
-           __put_user(0, &ubuf->f_spare[3]) || 
-           __put_user(0, &ubuf->f_spare[4]))
+           __put_user(kbuf->f_flags, &ubuf->f_flags) ||
+           __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare)))
                return -EFAULT;
        return 0;
 }
 
 /*
- * The following statfs calls are copies of code from fs/open.c and
+ * The following statfs calls are copies of code from fs/statfs.c and
  * should be checked against those from time to time
  */
 asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf)
 {
-       struct path path;
-       int error;
-
-       error = user_path(pathname, &path);
-       if (!error) {
-               struct kstatfs tmp;
-               error = vfs_statfs(&path, &tmp);
-               if (!error)
-                       error = put_compat_statfs(buf, &tmp);
-               path_put(&path);
-       }
+       struct kstatfs tmp;
+       int error = user_statfs(pathname, &tmp);
+       if (!error)
+               error = put_compat_statfs(buf, &tmp);
        return error;
 }
 
 asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf)
 {
-       struct file * file;
        struct kstatfs tmp;
-       int error;
-
-       error = -EBADF;
-       file = fget(fd);
-       if (!file)
-               goto out;
-       error = vfs_statfs(&file->f_path, &tmp);
+       int error = fd_statfs(fd, &tmp);
        if (!error)
                error = put_compat_statfs(buf, &tmp);
-       fput(file);
-out:
        return error;
 }
 
@@ -320,48 +293,38 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
            __put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
            __put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
            __put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
-           __put_user(kbuf->f_frsize, &ubuf->f_frsize))
+           __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
+           __put_user(kbuf->f_flags, &ubuf->f_flags) ||
+           __clear_user(ubuf->f_spare, sizeof(ubuf->f_spare)))
                return -EFAULT;
        return 0;
 }
 
 asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf)
 {
-       struct path path;
+       struct kstatfs tmp;
        int error;
 
        if (sz != sizeof(*buf))
                return -EINVAL;
 
-       error = user_path(pathname, &path);
-       if (!error) {
-               struct kstatfs tmp;
-               error = vfs_statfs(&path, &tmp);
-               if (!error)
-                       error = put_compat_statfs64(buf, &tmp);
-               path_put(&path);
-       }
+       error = user_statfs(pathname, &tmp);
+       if (!error)
+               error = put_compat_statfs64(buf, &tmp);
        return error;
 }
 
 asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user *buf)
 {
-       struct file * file;
        struct kstatfs tmp;
        int error;
 
        if (sz != sizeof(*buf))
                return -EINVAL;
 
-       error = -EBADF;
-       file = fget(fd);
-       if (!file)
-               goto out;
-       error = vfs_statfs(&file->f_path, &tmp);
+       error = fd_statfs(fd, &tmp);
        if (!error)
                error = put_compat_statfs64(buf, &tmp);
-       fput(file);
-out:
        return error;
 }
 
@@ -372,16 +335,9 @@ out:
  */
 asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u)
 {
-       struct super_block *sb;
        struct compat_ustat tmp;
        struct kstatfs sbuf;
-       int err;
-
-       sb = user_get_super(new_decode_dev(dev));
-       if (!sb)
-               return -EINVAL;
-       err = statfs_by_dentry(sb->s_root, &sbuf);
-       drop_super(sb);
+       int err = vfs_ustat(new_decode_dev(dev), &sbuf);
        if (err)
                return err;
 
@@ -576,7 +532,7 @@ out:
 ssize_t compat_rw_copy_check_uvector(int type,
                const struct compat_iovec __user *uvector, unsigned long nr_segs,
                unsigned long fast_segs, struct iovec *fast_pointer,
-               struct iovec **ret_pointer)
+               struct iovec **ret_pointer, int check_access)
 {
        compat_ssize_t tot_len;
        struct iovec *iov = *ret_pointer = fast_pointer;
@@ -597,10 +553,8 @@ ssize_t compat_rw_copy_check_uvector(int type,
        if (nr_segs > fast_segs) {
                ret = -ENOMEM;
                iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
-               if (iov == NULL) {
-                       *ret_pointer = fast_pointer;
+               if (iov == NULL)
                        goto out;
-               }
        }
        *ret_pointer = iov;
 
@@ -625,7 +579,8 @@ ssize_t compat_rw_copy_check_uvector(int type,
                }
                if (len < 0)    /* size_t not fitting in compat_ssize_t .. */
                        goto out;
-               if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
+               if (check_access &&
+                   !access_ok(vrfy_dir(type), compat_ptr(buf), len)) {
                        ret = -EFAULT;
                        goto out;
                }
@@ -1139,7 +1094,7 @@ static ssize_t compat_do_readv_writev(int type, struct file *file,
                goto out;
 
        tot_len = compat_rw_copy_check_uvector(type, uvector, nr_segs,
-                                              UIO_FASTIOV, iovstack, &iov);
+                                              UIO_FASTIOV, iovstack, &iov, 1);
        if (tot_len == 0) {
                ret = 0;
                goto out;
@@ -1215,10 +1170,9 @@ compat_sys_readv(unsigned long fd, const struct compat_iovec __user *vec,
 }
 
 asmlinkage ssize_t
-compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec,
-                 unsigned long vlen, u32 pos_low, u32 pos_high)
+compat_sys_preadv64(unsigned long fd, const struct compat_iovec __user *vec,
+                   unsigned long vlen, loff_t pos)
 {
-       loff_t pos = ((loff_t)pos_high << 32) | pos_low;
        struct file *file;
        int fput_needed;
        ssize_t ret;
@@ -1228,11 +1182,21 @@ compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec,
        file = fget_light(fd, &fput_needed);
        if (!file)
                return -EBADF;
-       ret = compat_readv(file, vec, vlen, &pos);
+       ret = -ESPIPE;
+       if (file->f_mode & FMODE_PREAD)
+               ret = compat_readv(file, vec, vlen, &pos);
        fput_light(file, fput_needed);
        return ret;
 }
 
+asmlinkage ssize_t
+compat_sys_preadv(unsigned long fd, const struct compat_iovec __user *vec,
+                 unsigned long vlen, u32 pos_low, u32 pos_high)
+{
+       loff_t pos = ((loff_t)pos_high << 32) | pos_low;
+       return compat_sys_preadv64(fd, vec, vlen, pos);
+}
+
 static size_t compat_writev(struct file *file,
                            const struct compat_iovec __user *vec,
                            unsigned long vlen, loff_t *pos)
@@ -1272,10 +1236,9 @@ compat_sys_writev(unsigned long fd, const struct compat_iovec __user *vec,
 }
 
 asmlinkage ssize_t
-compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec,
-                  unsigned long vlen, u32 pos_low, u32 pos_high)
+compat_sys_pwritev64(unsigned long fd, const struct compat_iovec __user *vec,
+                    unsigned long vlen, loff_t pos)
 {
-       loff_t pos = ((loff_t)pos_high << 32) | pos_low;
        struct file *file;
        int fput_needed;
        ssize_t ret;
@@ -1285,11 +1248,21 @@ compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec,
        file = fget_light(fd, &fput_needed);
        if (!file)
                return -EBADF;
-       ret = compat_writev(file, vec, vlen, &pos);
+       ret = -ESPIPE;
+       if (file->f_mode & FMODE_PWRITE)
+               ret = compat_writev(file, vec, vlen, &pos);
        fput_light(file, fput_needed);
        return ret;
 }
 
+asmlinkage ssize_t
+compat_sys_pwritev(unsigned long fd, const struct compat_iovec __user *vec,
+                  unsigned long vlen, u32 pos_low, u32 pos_high)
+{
+       loff_t pos = ((loff_t)pos_high << 32) | pos_low;
+       return compat_sys_pwritev64(fd, vec, vlen, pos);
+}
+
 asmlinkage long
 compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
                    unsigned int nr_segs, unsigned int flags)
@@ -1315,7 +1288,7 @@ compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32,
  * O_LARGEFILE flag.
  */
 asmlinkage long
-compat_sys_open(const char __user *filename, int flags, int mode)
+compat_sys_open(const char __user *filename, int flags, umode_t mode)
 {
        return do_sys_open(AT_FDCWD, filename, flags, mode);
 }
@@ -1325,244 +1298,11 @@ compat_sys_open(const char __user *filename, int flags, int mode)
  * O_LARGEFILE flag.
  */
 asmlinkage long
-compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, int mode)
+compat_sys_openat(unsigned int dfd, const char __user *filename, int flags, umode_t mode)
 {
        return do_sys_open(dfd, filename, flags, mode);
 }
 
-/*
- * compat_count() counts the number of arguments/envelopes. It is basically
- * a copy of count() from fs/exec.c, except that it works with 32 bit argv
- * and envp pointers.
- */
-static int compat_count(compat_uptr_t __user *argv, int max)
-{
-       int i = 0;
-
-       if (argv != NULL) {
-               for (;;) {
-                       compat_uptr_t p;
-
-                       if (get_user(p, argv))
-                               return -EFAULT;
-                       if (!p)
-                               break;
-                       argv++;
-                       if (i++ >= max)
-                               return -E2BIG;
-               }
-       }
-       return i;
-}
-
-/*
- * compat_copy_strings() is basically a copy of copy_strings() from fs/exec.c
- * except that it works with 32 bit argv and envp pointers.
- */
-static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
-                               struct linux_binprm *bprm)
-{
-       struct page *kmapped_page = NULL;
-       char *kaddr = NULL;
-       unsigned long kpos = 0;
-       int ret;
-
-       while (argc-- > 0) {
-               compat_uptr_t str;
-               int len;
-               unsigned long pos;
-
-               if (get_user(str, argv+argc) ||
-                   !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) {
-                       ret = -EFAULT;
-                       goto out;
-               }
-
-               if (len > MAX_ARG_STRLEN) {
-                       ret = -E2BIG;
-                       goto out;
-               }
-
-               /* We're going to work our way backwords. */
-               pos = bprm->p;
-               str += len;
-               bprm->p -= len;
-
-               while (len > 0) {
-                       int offset, bytes_to_copy;
-
-                       offset = pos % PAGE_SIZE;
-                       if (offset == 0)
-                               offset = PAGE_SIZE;
-
-                       bytes_to_copy = offset;
-                       if (bytes_to_copy > len)
-                               bytes_to_copy = len;
-
-                       offset -= bytes_to_copy;
-                       pos -= bytes_to_copy;
-                       str -= bytes_to_copy;
-                       len -= bytes_to_copy;
-
-                       if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
-                               struct page *page;
-
-#ifdef CONFIG_STACK_GROWSUP
-                               ret = expand_stack_downwards(bprm->vma, pos);
-                               if (ret < 0) {
-                                       /* We've exceed the stack rlimit. */
-                                       ret = -E2BIG;
-                                       goto out;
-                               }
-#endif
-                               ret = get_user_pages(current, bprm->mm, pos,
-                                                    1, 1, 1, &page, NULL);
-                               if (ret <= 0) {
-                                       /* We've exceed the stack rlimit. */
-                                       ret = -E2BIG;
-                                       goto out;
-                               }
-
-                               if (kmapped_page) {
-                                       flush_kernel_dcache_page(kmapped_page);
-                                       kunmap(kmapped_page);
-                                       put_page(kmapped_page);
-                               }
-                               kmapped_page = page;
-                               kaddr = kmap(kmapped_page);
-                               kpos = pos & PAGE_MASK;
-                               flush_cache_page(bprm->vma, kpos,
-                                                page_to_pfn(kmapped_page));
-                       }
-                       if (copy_from_user(kaddr+offset, compat_ptr(str),
-                                               bytes_to_copy)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-               }
-       }
-       ret = 0;
-out:
-       if (kmapped_page) {
-               flush_kernel_dcache_page(kmapped_page);
-               kunmap(kmapped_page);
-               put_page(kmapped_page);
-       }
-       return ret;
-}
-
-/*
- * compat_do_execve() is mostly a copy of do_execve(), with the exception
- * that it processes 32 bit argv and envp pointers.
- */
-int compat_do_execve(char * filename,
-       compat_uptr_t __user *argv,
-       compat_uptr_t __user *envp,
-       struct pt_regs * regs)
-{
-       struct linux_binprm *bprm;
-       struct file *file;
-       struct files_struct *displaced;
-       bool clear_in_exec;
-       int retval;
-
-       retval = unshare_files(&displaced);
-       if (retval)
-               goto out_ret;
-
-       retval = -ENOMEM;
-       bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
-       if (!bprm)
-               goto out_files;
-
-       retval = prepare_bprm_creds(bprm);
-       if (retval)
-               goto out_free;
-
-       retval = check_unsafe_exec(bprm);
-       if (retval < 0)
-               goto out_free;
-       clear_in_exec = retval;
-       current->in_execve = 1;
-
-       file = open_exec(filename);
-       retval = PTR_ERR(file);
-       if (IS_ERR(file))
-               goto out_unmark;
-
-       sched_exec();
-
-       bprm->file = file;
-       bprm->filename = filename;
-       bprm->interp = filename;
-
-       retval = bprm_mm_init(bprm);
-       if (retval)
-               goto out_file;
-
-       bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
-       if ((retval = bprm->argc) < 0)
-               goto out;
-
-       bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
-       if ((retval = bprm->envc) < 0)
-               goto out;
-
-       retval = prepare_binprm(bprm);
-       if (retval < 0)
-               goto out;
-
-       retval = copy_strings_kernel(1, &bprm->filename, bprm);
-       if (retval < 0)
-               goto out;
-
-       bprm->exec = bprm->p;
-       retval = compat_copy_strings(bprm->envc, envp, bprm);
-       if (retval < 0)
-               goto out;
-
-       retval = compat_copy_strings(bprm->argc, argv, bprm);
-       if (retval < 0)
-               goto out;
-
-       retval = search_binary_handler(bprm, regs);
-       if (retval < 0)
-               goto out;
-
-       /* execve succeeded */
-       current->fs->in_exec = 0;
-       current->in_execve = 0;
-       acct_update_integrals(current);
-       free_bprm(bprm);
-       if (displaced)
-               put_files_struct(displaced);
-       return retval;
-
-out:
-       if (bprm->mm)
-               mmput(bprm->mm);
-
-out_file:
-       if (bprm->file) {
-               allow_write_access(bprm->file);
-               fput(bprm->file);
-       }
-
-out_unmark:
-       if (clear_in_exec)
-               current->fs->in_exec = 0;
-       current->in_execve = 0;
-
-out_free:
-       free_bprm(bprm);
-
-out_files:
-       if (displaced)
-               reset_files_struct(displaced);
-out_ret:
-       return retval;
-}
-
 #define __COMPAT_NFDBITS       (8 * sizeof(compat_ulong_t))
 
 static int poll_select_copy_remaining(struct timespec *end_time, void __user *p,
@@ -1693,9 +1433,6 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset,
  * Update: ERESTARTSYS breaks at least the xview clock binary, so
  * I'm trying ERESTARTNOHAND which restart only when you want to.
  */
-#define MAX_SELECT_SECONDS \
-       ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1)
-
 int compat_core_sys_select(int n, compat_ulong_t __user *inp,
        compat_ulong_t __user *outp, compat_ulong_t __user *exp,
        struct timespec *end_time)
@@ -1935,257 +1672,6 @@ asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds,
 }
 #endif /* HAVE_SET_RESTORE_SIGMASK */
 
-#if (defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)) && !defined(CONFIG_NFSD_DEPRECATED)
-/* Stuff for NFS server syscalls... */
-struct compat_nfsctl_svc {
-       u16                     svc32_port;
-       s32                     svc32_nthreads;
-};
-
-struct compat_nfsctl_client {
-       s8                      cl32_ident[NFSCLNT_IDMAX+1];
-       s32                     cl32_naddr;
-       struct in_addr          cl32_addrlist[NFSCLNT_ADDRMAX];
-       s32                     cl32_fhkeytype;
-       s32                     cl32_fhkeylen;
-       u8                      cl32_fhkey[NFSCLNT_KEYMAX];
-};
-
-struct compat_nfsctl_export {
-       char            ex32_client[NFSCLNT_IDMAX+1];
-       char            ex32_path[NFS_MAXPATHLEN+1];
-       compat_dev_t    ex32_dev;
-       compat_ino_t    ex32_ino;
-       compat_int_t    ex32_flags;
-       __compat_uid_t  ex32_anon_uid;
-       __compat_gid_t  ex32_anon_gid;
-};
-
-struct compat_nfsctl_fdparm {
-       struct sockaddr         gd32_addr;
-       s8                      gd32_path[NFS_MAXPATHLEN+1];
-       compat_int_t            gd32_version;
-};
-
-struct compat_nfsctl_fsparm {
-       struct sockaddr         gd32_addr;
-       s8                      gd32_path[NFS_MAXPATHLEN+1];
-       compat_int_t            gd32_maxlen;
-};
-
-struct compat_nfsctl_arg {
-       compat_int_t            ca32_version;   /* safeguard */
-       union {
-               struct compat_nfsctl_svc        u32_svc;
-               struct compat_nfsctl_client     u32_client;
-               struct compat_nfsctl_export     u32_export;
-               struct compat_nfsctl_fdparm     u32_getfd;
-               struct compat_nfsctl_fsparm     u32_getfs;
-       } u;
-#define ca32_svc       u.u32_svc
-#define ca32_client    u.u32_client
-#define ca32_export    u.u32_export
-#define ca32_getfd     u.u32_getfd
-#define ca32_getfs     u.u32_getfs
-};
-
-union compat_nfsctl_res {
-       __u8                    cr32_getfh[NFS_FHSIZE];
-       struct knfsd_fh         cr32_getfs;
-};
-
-static int compat_nfs_svc_trans(struct nfsctl_arg *karg,
-                               struct compat_nfsctl_arg __user *arg)
-{
-       if (!access_ok(VERIFY_READ, &arg->ca32_svc, sizeof(arg->ca32_svc)) ||
-               get_user(karg->ca_version, &arg->ca32_version) ||
-               __get_user(karg->ca_svc.svc_port, &arg->ca32_svc.svc32_port) ||
-               __get_user(karg->ca_svc.svc_nthreads,
-                               &arg->ca32_svc.svc32_nthreads))
-               return -EFAULT;
-       return 0;
-}
-
-static int compat_nfs_clnt_trans(struct nfsctl_arg *karg,
-                               struct compat_nfsctl_arg __user *arg)
-{
-       if (!access_ok(VERIFY_READ, &arg->ca32_client,
-                       sizeof(arg->ca32_client)) ||
-               get_user(karg->ca_version, &arg->ca32_version) ||
-               __copy_from_user(&karg->ca_client.cl_ident[0],
-                               &arg->ca32_client.cl32_ident[0],
-                               NFSCLNT_IDMAX) ||
-               __get_user(karg->ca_client.cl_naddr,
-                               &arg->ca32_client.cl32_naddr) ||
-               __copy_from_user(&karg->ca_client.cl_addrlist[0],
-                               &arg->ca32_client.cl32_addrlist[0],
-                               (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)) ||
-               __get_user(karg->ca_client.cl_fhkeytype,
-                               &arg->ca32_client.cl32_fhkeytype) ||
-               __get_user(karg->ca_client.cl_fhkeylen,
-                               &arg->ca32_client.cl32_fhkeylen) ||
-               __copy_from_user(&karg->ca_client.cl_fhkey[0],
-                               &arg->ca32_client.cl32_fhkey[0],
-                               NFSCLNT_KEYMAX))
-               return -EFAULT;
-
-       return 0;
-}
-
-static int compat_nfs_exp_trans(struct nfsctl_arg *karg,
-                               struct compat_nfsctl_arg __user *arg)
-{
-       if (!access_ok(VERIFY_READ, &arg->ca32_export,
-                               sizeof(arg->ca32_export)) ||
-               get_user(karg->ca_version, &arg->ca32_version) ||
-               __copy_from_user(&karg->ca_export.ex_client[0],
-                               &arg->ca32_export.ex32_client[0],
-                               NFSCLNT_IDMAX) ||
-               __copy_from_user(&karg->ca_export.ex_path[0],
-                               &arg->ca32_export.ex32_path[0],
-                               NFS_MAXPATHLEN) ||
-               __get_user(karg->ca_export.ex_dev,
-                               &arg->ca32_export.ex32_dev) ||
-               __get_user(karg->ca_export.ex_ino,
-                               &arg->ca32_export.ex32_ino) ||
-               __get_user(karg->ca_export.ex_flags,
-                               &arg->ca32_export.ex32_flags) ||
-               __get_user(karg->ca_export.ex_anon_uid,
-                               &arg->ca32_export.ex32_anon_uid) ||
-               __get_user(karg->ca_export.ex_anon_gid,
-                               &arg->ca32_export.ex32_anon_gid))
-               return -EFAULT;
-       SET_UID(karg->ca_export.ex_anon_uid, karg->ca_export.ex_anon_uid);
-       SET_GID(karg->ca_export.ex_anon_gid, karg->ca_export.ex_anon_gid);
-
-       return 0;
-}
-
-static int compat_nfs_getfd_trans(struct nfsctl_arg *karg,
-                               struct compat_nfsctl_arg __user *arg)
-{
-       if (!access_ok(VERIFY_READ, &arg->ca32_getfd,
-                       sizeof(arg->ca32_getfd)) ||
-               get_user(karg->ca_version, &arg->ca32_version) ||
-               __copy_from_user(&karg->ca_getfd.gd_addr,
-                               &arg->ca32_getfd.gd32_addr,
-                               (sizeof(struct sockaddr))) ||
-               __copy_from_user(&karg->ca_getfd.gd_path,
-                               &arg->ca32_getfd.gd32_path,
-                               (NFS_MAXPATHLEN+1)) ||
-               __get_user(karg->ca_getfd.gd_version,
-                               &arg->ca32_getfd.gd32_version))
-               return -EFAULT;
-
-       return 0;
-}
-
-static int compat_nfs_getfs_trans(struct nfsctl_arg *karg,
-                               struct compat_nfsctl_arg __user *arg)
-{
-       if (!access_ok(VERIFY_READ,&arg->ca32_getfs,sizeof(arg->ca32_getfs)) ||
-               get_user(karg->ca_version, &arg->ca32_version) ||
-               __copy_from_user(&karg->ca_getfs.gd_addr,
-                               &arg->ca32_getfs.gd32_addr,
-                               (sizeof(struct sockaddr))) ||
-               __copy_from_user(&karg->ca_getfs.gd_path,
-                               &arg->ca32_getfs.gd32_path,
-                               (NFS_MAXPATHLEN+1)) ||
-               __get_user(karg->ca_getfs.gd_maxlen,
-                               &arg->ca32_getfs.gd32_maxlen))
-               return -EFAULT;
-
-       return 0;
-}
-
-/* This really doesn't need translations, we are only passing
- * back a union which contains opaque nfs file handle data.
- */
-static int compat_nfs_getfh_res_trans(union nfsctl_res *kres,
-                               union compat_nfsctl_res __user *res)
-{
-       int err;
-
-       err = copy_to_user(res, kres, sizeof(*res));
-
-       return (err) ? -EFAULT : 0;
-}
-
-asmlinkage long compat_sys_nfsservctl(int cmd,
-                               struct compat_nfsctl_arg __user *arg,
-                               union compat_nfsctl_res __user *res)
-{
-       struct nfsctl_arg *karg;
-       union nfsctl_res *kres;
-       mm_segment_t oldfs;
-       int err;
-
-       karg = kmalloc(sizeof(*karg), GFP_USER);
-       kres = kmalloc(sizeof(*kres), GFP_USER);
-       if(!karg || !kres) {
-               err = -ENOMEM;
-               goto done;
-       }
-
-       switch(cmd) {
-       case NFSCTL_SVC:
-               err = compat_nfs_svc_trans(karg, arg);
-               break;
-
-       case NFSCTL_ADDCLIENT:
-               err = compat_nfs_clnt_trans(karg, arg);
-               break;
-
-       case NFSCTL_DELCLIENT:
-               err = compat_nfs_clnt_trans(karg, arg);
-               break;
-
-       case NFSCTL_EXPORT:
-       case NFSCTL_UNEXPORT:
-               err = compat_nfs_exp_trans(karg, arg);
-               break;
-
-       case NFSCTL_GETFD:
-               err = compat_nfs_getfd_trans(karg, arg);
-               break;
-
-       case NFSCTL_GETFS:
-               err = compat_nfs_getfs_trans(karg, arg);
-               break;
-
-       default:
-               err = -EINVAL;
-               break;
-       }
-
-       if (err)
-               goto done;
-
-       oldfs = get_fs();
-       set_fs(KERNEL_DS);
-       /* The __user pointer casts are valid because of the set_fs() */
-       err = sys_nfsservctl(cmd, (void __user *) karg, (void __user *) kres);
-       set_fs(oldfs);
-
-       if (err)
-               goto done;
-
-       if((cmd == NFSCTL_GETFD) ||
-          (cmd == NFSCTL_GETFS))
-               err = compat_nfs_getfh_res_trans(kres, res);
-
-done:
-       kfree(karg);
-       kfree(kres);
-       return err;
-}
-#else /* !NFSD */
-long asmlinkage compat_sys_nfsservctl(int cmd, void *notused, void *notused2)
-{
-       return sys_ni_syscall();
-}
-#endif
-
 #ifdef CONFIG_EPOLL
 
 #ifdef HAVE_SET_RESTORE_SIGMASK
@@ -2306,3 +1792,16 @@ asmlinkage long compat_sys_timerfd_gettime(int ufd,
 }
 
 #endif /* CONFIG_TIMERFD */
+
+#ifdef CONFIG_FHANDLE
+/*
+ * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
+ * doesn't set the O_LARGEFILE flag.
+ */
+asmlinkage long
+compat_sys_open_by_handle_at(int mountdirfd,
+                            struct file_handle __user *handle, int flags)
+{
+       return do_handle_open(mountdirfd, handle, flags);
+}
+#endif