This function is used by sparc, powerpc and arm64 for compat support.
The patch adds a generic implementation which calls do_sendfile()
directly and avoids set_fs().
The sparc architecture has wrappers for the sign extensions while
powerpc relies on the compiler to do the this. The patch adds wrappers
for powerpc to handle the u32->int type conversion.
compat_sys_sendfile64() can be replaced by a sys_sendfile() call since
compat_loff_t has the same size as off_t on a 64-bit system.
On powerpc, the patch also changes the 64-bit sendfile call from
sys_sendile64 to sys_sendfile.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: David S. Miller <davem@davemloft.net>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
 SYSCALL_SPU(capget)
 SYSCALL_SPU(capset)
 COMPAT_SYS(sigaltstack)
-SYSX_SPU(sys_sendfile64,compat_sys_sendfile,sys_sendfile)
+SYSX_SPU(sys_sendfile,compat_sys_sendfile_wrapper,sys_sendfile)
 SYSCALL(ni_syscall)
 SYSCALL(ni_syscall)
 PPC_SYS(vfork)
 COMPAT_SYS_SPU(sched_getaffinity)
 SYSCALL(ni_syscall)
 SYSCALL(ni_syscall)
-SYS32ONLY(sendfile64)
+SYSX(sys_ni_syscall,compat_sys_sendfile64_wrapper,sys_sendfile64)
 COMPAT_SYS_SPU(io_setup)
 SYSCALL_SPU(io_destroy)
 COMPAT_SYS_SPU(io_getevents)
 
 #define __ARCH_WANT_COMPAT_SYS_TIME
 #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
 #define __ARCH_WANT_SYS_NEWFSTATAT
+#define __ARCH_WANT_COMPAT_SYS_SENDFILE
 #endif
 
 /*
 
  * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode)
  * and the register representation of a signed int (msr in 64-bit mode) is performed.
  */
-asmlinkage long compat_sys_sendfile(u32 out_fd, u32 in_fd, compat_off_t __user * offset, u32 count)
+asmlinkage long compat_sys_sendfile_wrapper(u32 out_fd, u32 in_fd,
+                                           compat_off_t __user *offset, u32 count)
 {
-       mm_segment_t old_fs = get_fs();
-       int ret;
-       off_t of;
-       off_t __user *up;
-
-       if (offset && get_user(of, offset))
-               return -EFAULT;
-
-       /* The __user pointer cast is valid because of the set_fs() */          
-       set_fs(KERNEL_DS);
-       up = offset ? (off_t __user *) &of : NULL;
-       ret = sys_sendfile((int)out_fd, (int)in_fd, up, count);
-       set_fs(old_fs);
-       
-       if (offset && put_user(of, offset))
-               return -EFAULT;
-               
-       return ret;
+       return compat_sys_sendfile((int)out_fd, (int)in_fd, offset, count);
 }
 
-asmlinkage int compat_sys_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, s32 count)
+asmlinkage long compat_sys_sendfile64_wrapper(u32 out_fd, u32 in_fd,
+                                             compat_loff_t __user *offset, u32 count)
 {
-       mm_segment_t old_fs = get_fs();
-       int ret;
-       loff_t lof;
-       loff_t __user *up;
-       
-       if (offset && get_user(lof, offset))
-               return -EFAULT;
-               
-       /* The __user pointer cast is valid because of the set_fs() */          
-       set_fs(KERNEL_DS);
-       up = offset ? (loff_t __user *) &lof : NULL;
-       ret = sys_sendfile64(out_fd, in_fd, up, count);
-       set_fs(old_fs);
-       
-       if (offset && put_user(lof, offset))
-               return -EFAULT;
-               
-       return ret;
+       return sys_sendfile((int)out_fd, (int)in_fd,
+                           (off_t __user *)offset, count);
 }
 
 long compat_sys_execve(unsigned long a0, unsigned long a1, unsigned long a2,
 
 #else
 #define __ARCH_WANT_COMPAT_SYS_TIME
 #define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
+#define __ARCH_WANT_COMPAT_SYS_SENDFILE
 #endif
 
 /*
 
 SIGN3(sys32_futex, compat_sys_futex, %o1, %o2, %o5)
 SIGN1(sys32_sysfs, compat_sys_sysfs, %o0)
 SIGN2(sys32_sendfile, compat_sys_sendfile, %o0, %o1)
-SIGN2(sys32_sendfile64, compat_sys_sendfile64, %o0, %o1)
+SIGN2(sys32_sendfile64, sys_sendfile, %o0, %o1)
 SIGN1(sys32_prctl, sys_prctl, %o0)
 SIGN1(sys32_sched_rr_get_interval, compat_sys_sched_rr_get_interval, %o0)
 SIGN2(sys32_waitpid, sys_waitpid, %o0, %o2)
 
                                advice);
 }
 
-asmlinkage long compat_sys_sendfile(int out_fd, int in_fd,
-                                   compat_off_t __user *offset,
-                                   compat_size_t count)
-{
-       mm_segment_t old_fs = get_fs();
-       int ret;
-       off_t of;
-       
-       if (offset && get_user(of, offset))
-               return -EFAULT;
-               
-       set_fs(KERNEL_DS);
-       ret = sys_sendfile(out_fd, in_fd,
-                          offset ? (off_t __user *) &of : NULL,
-                          count);
-       set_fs(old_fs);
-       
-       if (offset && put_user(of, offset))
-               return -EFAULT;
-               
-       return ret;
-}
-
-asmlinkage long compat_sys_sendfile64(int out_fd, int in_fd,
-                                     compat_loff_t __user *offset,
-                                     compat_size_t count)
-{
-       mm_segment_t old_fs = get_fs();
-       int ret;
-       loff_t lof;
-       
-       if (offset && get_user(lof, offset))
-               return -EFAULT;
-               
-       set_fs(KERNEL_DS);
-       ret = sys_sendfile64(out_fd, in_fd,
-                            offset ? (loff_t __user *) &lof : NULL,
-                            count);
-       set_fs(old_fs);
-       
-       if (offset && put_user(lof, offset))
-               return -EFAULT;
-               
-       return ret;
-}
-
 /* This is just a version for 32-bit applications which does
  * not force O_LARGEFILE on.
  */
 
        return do_handle_open(mountdirfd, handle, flags);
 }
 #endif
+
+#ifdef __ARCH_WANT_COMPAT_SYS_SENDFILE
+asmlinkage long compat_sys_sendfile(int out_fd, int in_fd,
+                                   compat_off_t __user *offset, compat_size_t count)
+{
+       loff_t pos;
+       off_t off;
+       ssize_t ret;
+
+       if (offset) {
+               if (unlikely(get_user(off, offset)))
+                       return -EFAULT;
+               pos = off;
+               ret = do_sendfile(out_fd, in_fd, &pos, count, MAX_NON_LFS);
+               if (unlikely(put_user(pos, offset)))
+                       return -EFAULT;
+               return ret;
+       }
+
+       return do_sendfile(out_fd, in_fd, NULL, count, 0);
+}
+#endif /* __ARCH_WANT_COMPAT_SYS_SENDFILE */
 
        return ret;
 }
 
-static ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos,
-                          size_t count, loff_t max)
+ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count,
+                   loff_t max)
 {
        struct fd in, out;
        struct inode *in_inode, *out_inode;
 
                unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn);
 ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov,
                unsigned long nr_segs, loff_t *ppos, io_fn_t fn);
+ssize_t do_sendfile(int out_fd, int in_fd, loff_t *ppos, size_t count,
+                   loff_t max);
 
                unsigned long liovcnt, const struct compat_iovec __user *rvec,
                unsigned long riovcnt, unsigned long flags);
 
+asmlinkage long compat_sys_sendfile(int out_fd, int in_fd,
+                                   compat_off_t __user *offset, compat_size_t count);
+
 #else
 
 #define is_compat_task() (0)