Author: emaste
Date: Wed Feb  5 16:53:02 2020
New Revision: 357577
URL: https://svnweb.freebsd.org/changeset/base/357577

Log:
  linuxulator: implement sendfile
  
  Submitted by: Bora Özarslan <borako.ozars...@gmail.com>
  Submitted by: Yang Wang <2...@outlook.jp>
  Reviewed by:  markj
  Sponsored by: The FreeBSD Foundation
  Differential Revision:        https://reviews.freebsd.org/D19917

Modified:
  head/sys/amd64/linux/linux_dummy.c
  head/sys/amd64/linux32/linux32_dummy.c
  head/sys/amd64/linux32/syscalls.master
  head/sys/arm/linux/syscalls.master
  head/sys/arm64/linux/linux_dummy.c
  head/sys/compat/linux/linux_socket.c
  head/sys/compat/linux/linux_socket.h
  head/sys/i386/linux/linux_dummy.c
  head/sys/i386/linux/syscalls.master

Modified: head/sys/amd64/linux/linux_dummy.c
==============================================================================
--- head/sys/amd64/linux/linux_dummy.c  Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/amd64/linux/linux_dummy.c  Wed Feb  5 16:53:02 2020        
(r357577)
@@ -59,7 +59,6 @@ UNIMPLEMENTED(set_thread_area);
 UNIMPLEMENTED(uselib);
 UNIMPLEMENTED(vserver);
 
-DUMMY(sendfile);
 DUMMY(setfsuid);
 DUMMY(setfsgid);
 DUMMY(sysfs);

Modified: head/sys/amd64/linux32/linux32_dummy.c
==============================================================================
--- head/sys/amd64/linux32/linux32_dummy.c      Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/amd64/linux32/linux32_dummy.c      Wed Feb  5 16:53:02 2020        
(r357577)
@@ -72,7 +72,6 @@ DUMMY(delete_module);
 DUMMY(quotactl);
 DUMMY(bdflush);
 DUMMY(sysfs);
-DUMMY(sendfile);
 DUMMY(setfsuid);
 DUMMY(setfsgid);
 DUMMY(pivot_root);

Modified: head/sys/amd64/linux32/syscalls.master
==============================================================================
--- head/sys/amd64/linux32/syscalls.master      Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/amd64/linux32/syscalls.master      Wed Feb  5 16:53:02 2020        
(r357577)
@@ -338,7 +338,8 @@
                                    struct l_user_cap_data *datap); }
 186    AUE_NULL        STD     { int linux_sigaltstack(l_stack_t *uss, \
                                    l_stack_t *uoss); }
-187    AUE_SENDFILE    STD     { int linux_sendfile(void); }
+187    AUE_SENDFILE    STD     { int linux_sendfile(l_int out, l_int in, \
+                                   l_long *offset, l_size_t count); }
 188    AUE_GETPMSG     UNIMPL  getpmsg
 189    AUE_PUTPMSG     UNIMPL  putpmsg
 190    AUE_VFORK       STD     { int linux_vfork(void); }
@@ -412,7 +413,8 @@
 236    AUE_NULL        STD     { int linux_lremovexattr(void); }
 237    AUE_NULL        STD     { int linux_fremovexattr(void); }
 238    AUE_NULL        STD     { int linux_tkill(int tid, int sig); }
-239    AUE_SENDFILE    UNIMPL  linux_sendfile64
+239    AUE_SENDFILE    STD     { int linux_sendfile64(l_int out, l_int in, \
+                                   l_loff_t *offset, l_size_t count); }
 240    AUE_NULL        STD     { int linux_sys_futex(void *uaddr, int op, 
uint32_t val, \
                                        struct l_timespec *timeout, uint32_t 
*uaddr2, uint32_t val3); }
 241    AUE_NULL        STD     { int linux_sched_setaffinity(l_pid_t pid, 
l_uint len, \

Modified: head/sys/arm/linux/syscalls.master
==============================================================================
--- head/sys/arm/linux/syscalls.master  Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/arm/linux/syscalls.master  Wed Feb  5 16:53:02 2020        
(r357577)
@@ -868,7 +868,12 @@
                );
        }
 187    AUE_SENDFILE    STD     {
-               int linux_sendfile(void);
+               int linux_sendfile(
+                       l_int out,
+                       l_int in,
+                       l_long *offset,
+                       l_size_t count
+               );
        }
 188    AUE_NULL        UNIMPL  ; was getpmsg
 189    AUE_NULL        UNIMPL  ; was putpmsg
@@ -1090,7 +1095,14 @@
                    int sig
                );
        }
-239    AUE_SENDFILE    UNIMPL  linux_sendfile64
+239    AUE_SENDFILE    STD     {
+               int linux_sendfile64(
+                       l_int out,
+                       l_int in,
+                       l_loff_t *offset,
+                       l_size_t count
+               );
+       }
 240    AUE_NULL        STD     {
                int linux_sys_futex(void *uaddr,
                    int op,

Modified: head/sys/arm64/linux/linux_dummy.c
==============================================================================
--- head/sys/arm64/linux/linux_dummy.c  Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/arm64/linux/linux_dummy.c  Wed Feb  5 16:53:02 2020        
(r357577)
@@ -64,7 +64,6 @@ UNIMPLEMENTED(tuxcall);
 UNIMPLEMENTED(uselib);
 UNIMPLEMENTED(vserver);
 
-DUMMY(sendfile);
 DUMMY(setfsuid);
 DUMMY(setfsgid);
 DUMMY(vhangup);

Modified: head/sys/compat/linux/linux_socket.c
==============================================================================
--- head/sys/compat/linux/linux_socket.c        Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/compat/linux/linux_socket.c        Wed Feb  5 16:53:02 2020        
(r357577)
@@ -49,9 +49,13 @@ __FBSDID("$FreeBSD$");
 #include <sys/socketvar.h>
 #include <sys/syscallsubr.h>
 #include <sys/uio.h>
+#include <sys/stat.h>
 #include <sys/syslog.h>
 #include <sys/un.h>
+#include <sys/unistd.h>
 
+#include <security/audit/audit.h>
+
 #include <net/if.h>
 #include <net/vnet.h>
 #include <netinet/in.h>
@@ -1581,8 +1585,135 @@ out:
        return (error);
 }
 
-#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+static int
+linux_sendfile_common(struct thread *td, l_int out, l_int in,
+    l_loff_t *offset, l_size_t count)
+{
+       off_t bytes_read;
+       int error;
+       l_loff_t current_offset;
+       struct file *fp;
 
+       AUDIT_ARG_FD(in);
+       error = fget_read(td, in, &cap_pread_rights, &fp);
+       if (error != 0)
+               return (error);
+
+       if (offset != NULL) {
+               current_offset = *offset;
+       } else {
+               error = (fp->f_ops->fo_flags & DFLAG_SEEKABLE) != 0 ?
+                   fo_seek(fp, 0, SEEK_CUR, td) : ESPIPE;
+               if (error != 0)
+                       goto drop;
+               current_offset = td->td_uretoff.tdu_off;
+       }
+
+       bytes_read = 0;
+
+       /* Linux cannot have 0 count. */
+       if (count <= 0 || current_offset < 0) {
+               error = EINVAL;
+               goto drop;
+       }
+
+       error = fo_sendfile(fp, out, NULL, NULL, current_offset, count,
+           &bytes_read, 0, td);
+       if (error != 0)
+               goto drop;
+       current_offset += bytes_read;
+
+       if (offset != NULL) {
+               *offset = current_offset;
+       } else {
+               error = fo_seek(fp, current_offset, SEEK_SET, td);
+               if (error != 0)
+                       goto drop;
+       }
+
+       td->td_retval[0] = (ssize_t)bytes_read;
+drop:
+       fdrop(fp, td);
+       return (error);
+}
+
+int
+linux_sendfile(struct thread *td, struct linux_sendfile_args *arg)
+{
+       /*
+        * Differences between FreeBSD and Linux sendfile:
+        * - Linux doesn't send anything when count is 0 (FreeBSD uses 0 to
+        *   mean send the whole file.)  In linux_sendfile given fds are still
+        *   checked for validity when the count is 0.
+        * - Linux can send to any fd whereas FreeBSD only supports sockets.
+        *   The same restriction follows for linux_sendfile.
+        * - Linux doesn't have an equivalent for FreeBSD's flags and sf_hdtr.
+        * - Linux takes an offset pointer and updates it to the read location.
+        *   FreeBSD takes in an offset and a 'bytes read' parameter which is
+        *   only filled if it isn't NULL.  We use this parameter to update the
+        *   offset pointer if it exists.
+        * - Linux sendfile returns bytes read on success while FreeBSD
+        *   returns 0.  We use the 'bytes read' parameter to get this value.
+        */
+
+       l_loff_t offset64;
+       l_long offset;
+       int ret;
+       int error;
+
+       if (arg->offset != NULL) {
+               error = copyin(arg->offset, &offset, sizeof(offset));
+               if (error != 0)
+                       return (error);
+               offset64 = (l_loff_t)offset;
+       }
+
+       ret = linux_sendfile_common(td, arg->out, arg->in,
+           arg->offset != NULL ? &offset64 : NULL, arg->count);
+
+       if (arg->offset != NULL) {
+#if defined(__i386__) || defined(__arm__) || \
+    (defined(__amd64__) && defined(COMPAT_LINUX32))
+               if (offset64 > INT32_MAX)
+                       return (EOVERFLOW);
+#endif
+               offset = (l_long)offset64;
+               error = copyout(&offset, arg->offset, sizeof(offset));
+               if (error != 0)
+                       return (error);
+       }
+
+       return (ret);
+}
+
+#if defined(__i386__) || defined(__arm__) || \
+    (defined(__amd64__) && defined(COMPAT_LINUX32))
+
+int
+linux_sendfile64(struct thread *td, struct linux_sendfile64_args *arg)
+{
+       l_loff_t offset;
+       int ret;
+       int error;
+
+       if (arg->offset != NULL) {
+               error = copyin(arg->offset, &offset, sizeof(offset));
+               if (error != 0)
+                       return (error);
+       }
+
+       ret = linux_sendfile_common(td, arg->out, arg->in,
+               arg->offset != NULL ? &offset : NULL, arg->count);
+
+       if (arg->offset != NULL) {
+               error = copyout(&offset, arg->offset, sizeof(offset));
+               if (error != 0)
+                       return (error);
+       }
+
+       return (ret);
+}
+
 /* Argument list sizes for linux_socketcall */
 static const unsigned char lxs_args_cnt[] = {
        0 /* unused*/,          3 /* socket */,
@@ -1595,7 +1726,7 @@ static const unsigned char lxs_args_cnt[] = {
        5 /* setsockopt */,     5 /* getsockopt */,
        3 /* sendmsg */,        3 /* recvmsg */,
        4 /* accept4 */,        5 /* recvmmsg */,
-       4 /* sendmmsg */
+       4 /* sendmmsg */,       4 /* sendfile */
 };
 #define        LINUX_ARGS_CNT          (nitems(lxs_args_cnt) - 1)
 #define        LINUX_ARG_SIZE(x)       (lxs_args_cnt[x] * sizeof(l_ulong))
@@ -1664,9 +1795,11 @@ linux_socketcall(struct thread *td, struct linux_socke
                return (linux_recvmmsg(td, arg));
        case LINUX_SENDMMSG:
                return (linux_sendmmsg(td, arg));
+       case LINUX_SENDFILE:
+               return (linux_sendfile(td, arg));
        }
 
        uprintf("LINUX: 'socket' typ=%d not implemented\n", args->what);
        return (ENOSYS);
 }
-#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+#endif /* __i386__ || __arm__ || (__amd64__ && COMPAT_LINUX32) */

Modified: head/sys/compat/linux/linux_socket.h
==============================================================================
--- head/sys/compat/linux/linux_socket.h        Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/compat/linux/linux_socket.h        Wed Feb  5 16:53:02 2020        
(r357577)
@@ -132,7 +132,9 @@ struct l_ucred {
        uint32_t        gid;
 };
 
-#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+#if defined(__i386__) || defined(__arm__) || \
+    (defined(__amd64__) && defined(COMPAT_LINUX32))
+
 struct linux_accept_args {
        register_t s;
        register_t addr;
@@ -162,7 +164,9 @@ int linux_accept(struct thread *td, struct linux_accep
 #define        LINUX_ACCEPT4           18
 #define        LINUX_RECVMMSG          19
 #define        LINUX_SENDMMSG          20
-#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+#define        LINUX_SENDFILE          21
+
+#endif /* __i386__ || __arm__ || (__amd64__ && COMPAT_LINUX32) */
 
 /* Socket defines */
 #define        LINUX_SOL_SOCKET        1

Modified: head/sys/i386/linux/linux_dummy.c
==============================================================================
--- head/sys/i386/linux/linux_dummy.c   Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/i386/linux/linux_dummy.c   Wed Feb  5 16:53:02 2020        
(r357577)
@@ -75,7 +75,6 @@ DUMMY(quotactl);
 DUMMY(bdflush);
 DUMMY(sysfs);
 DUMMY(vm86);
-DUMMY(sendfile);               /* different semantics */
 DUMMY(setfsuid);
 DUMMY(setfsgid);
 DUMMY(pivot_root);

Modified: head/sys/i386/linux/syscalls.master
==============================================================================
--- head/sys/i386/linux/syscalls.master Wed Feb  5 16:10:09 2020        
(r357576)
+++ head/sys/i386/linux/syscalls.master Wed Feb  5 16:53:02 2020        
(r357577)
@@ -341,7 +341,8 @@
                                    struct l_user_cap_data *datap); }
 186    AUE_NULL        STD     { int linux_sigaltstack(l_stack_t *uss, \
                                    l_stack_t *uoss); }
-187    AUE_SENDFILE    STD     { int linux_sendfile(void); }
+187    AUE_SENDFILE    STD     { int linux_sendfile(l_int out, l_int in, \
+                                   l_long *offset, l_size_t count); }
 188    AUE_GETPMSG     UNIMPL  getpmsg
 189    AUE_PUTPMSG     UNIMPL  putpmsg
 190    AUE_VFORK       STD     { int linux_vfork(void); }
@@ -415,7 +416,8 @@
 236    AUE_NULL        STD     { int linux_lremovexattr(void); }
 237    AUE_NULL        STD     { int linux_fremovexattr(void); }
 238    AUE_NULL        STD     { int linux_tkill(int tid, int sig); }
-239    AUE_SENDFILE    UNIMPL  linux_sendfile64
+239    AUE_SENDFILE    STD     { int linux_sendfile64(l_int out, l_int in, \
+                                   l_loff_t *offset, l_size_t count); }
 240    AUE_NULL        STD     { int linux_sys_futex(void *uaddr, int op, 
uint32_t val, \
                                        struct l_timespec *timeout, uint32_t 
*uaddr2, uint32_t val3); }
 241    AUE_NULL        STD     { int linux_sched_setaffinity(l_pid_t pid, 
l_uint len, \
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to