Index: sys/amd64/linux/linux_dummy.c =================================================================== --- sys/amd64/linux/linux_dummy.c +++ sys/amd64/linux/linux_dummy.c @@ -59,7 +59,6 @@ UNIMPLEMENTED(uselib); UNIMPLEMENTED(vserver); -DUMMY(sendfile); DUMMY(syslog); DUMMY(setfsuid); DUMMY(setfsgid); Index: sys/amd64/linux32/linux32_dummy.c =================================================================== --- sys/amd64/linux32/linux32_dummy.c +++ sys/amd64/linux32/linux32_dummy.c @@ -73,7 +73,6 @@ DUMMY(quotactl); DUMMY(bdflush); DUMMY(sysfs); -DUMMY(sendfile); DUMMY(setfsuid); DUMMY(setfsgid); DUMMY(pivot_root); Index: sys/amd64/linux32/linux32_proto.h =================================================================== --- sys/amd64/linux32/linux32_proto.h +++ sys/amd64/linux32/linux32_proto.h @@ -604,7 +604,10 @@ char uoss_l_[PADL_(l_stack_t *)]; l_stack_t * uoss; char uoss_r_[PADR_(l_stack_t *)]; }; struct linux_sendfile_args { - register_t dummy; + char out_l_[PADL_(l_int)]; l_int out; char out_r_[PADR_(l_int)]; + char in_l_[PADL_(l_int)]; l_int in; char in_r_[PADR_(l_int)]; + char offset_l_[PADL_(l_long *)]; l_long * offset; char offset_r_[PADR_(l_long *)]; + char count_l_[PADL_(l_size_t)]; l_size_t count; char count_r_[PADR_(l_size_t)]; }; struct linux_vfork_args { register_t dummy; Index: sys/amd64/linux32/linux32_sysent.c =================================================================== --- sys/amd64/linux32/linux32_sysent.c +++ sys/amd64/linux32/linux32_sysent.c @@ -204,7 +204,7 @@ { AS(linux_capget_args), (sy_call_t *)linux_capget, AUE_CAPGET, NULL, 0, 0, 0, SY_THR_STATIC }, /* 184 = linux_capget */ { AS(linux_capset_args), (sy_call_t *)linux_capset, AUE_CAPSET, NULL, 0, 0, 0, SY_THR_STATIC }, /* 185 = linux_capset */ { AS(linux_sigaltstack_args), (sy_call_t *)linux_sigaltstack, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC }, /* 186 = linux_sigaltstack */ - { 0, (sy_call_t *)linux_sendfile, AUE_SENDFILE, NULL, 0, 0, 0, SY_THR_STATIC }, /* 187 = linux_sendfile */ + { AS(linux_sendfile_args), (sy_call_t *)linux_sendfile, AUE_SENDFILE, NULL, 0, 0, 0, SY_THR_STATIC }, /* 187 = linux_sendfile */ { 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 188 = getpmsg */ { 0, (sy_call_t *)nosys, AUE_NULL, NULL, 0, 0, 0, SY_THR_ABSENT }, /* 189 = putpmsg */ { 0, (sy_call_t *)linux_vfork, AUE_VFORK, NULL, 0, 0, 0, SY_THR_STATIC }, /* 190 = linux_vfork */ Index: sys/amd64/linux32/syscalls.master =================================================================== --- sys/amd64/linux32/syscalls.master +++ sys/amd64/linux32/syscalls.master @@ -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_long *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, \ Index: sys/compat/linux/linux_socket.h =================================================================== --- sys/compat/linux/linux_socket.h +++ sys/compat/linux/linux_socket.h @@ -172,6 +172,7 @@ #define LINUX_ACCEPT4 18 #define LINUX_RECVMMSG 19 #define LINUX_SENDMMSG 20 +#define LINUX_SENDFILE 21 #endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */ /* Socket defines */ Index: sys/compat/linux/linux_socket.c =================================================================== --- sys/compat/linux/linux_socket.c +++ sys/compat/linux/linux_socket.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,8 @@ #include #include +#include + static int linux_to_bsd_domain(int); static int linux_sendmsg_common(struct thread *, l_int, struct l_msghdr *, l_uint); @@ -1693,6 +1696,97 @@ return (error); } +int +linux_sendfile(struct thread *td, struct linux_sendfile_args *arg){ + struct stat sb; + struct tcp_info ti; + off_t bytes_read; + int ret; + l_long offset; + struct file *fp; + socklen_t size_val; + + /* XXX: The differences between freebsd and linux sendfile: + * - linux_sendfile doesn't send anything when count is 0 + * whereas freebsd_sendfile sends the whole file. However, + * in linux_sendfile given fds are still checked for if they + * are valid or not when count is 0. + * - linux_sendfile can send to any fd whereas freebsd_sendfile + * only sends to a socket stream. Note that only socket streams + * are implemented here. Also note that the current offset into + * the fdin shouldn't change before and after sendfile. Meaning + * 'lseek(fd, 0, SET_CUR)' should return same before and after. + * - linux_sendfile doesn't have equivalents of flags and sf_hdtr of + * freebsd_sendfile. + * - linux_sendfile takes in an offset pointer and updates it to where it + * was read until. freebsd_sendfile 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_sendfile + * returns 0. We use the 'bytes read' parameter to get this value. + */ + + /* fstat to get info on target fd */ + ret = kern_fstat(td, arg->out, &sb); + if (ret < 0) + return (ret); + + /* non-socket descriptor not implemented */ + if (!S_ISSOCK(sb.st_mode)) { + linux_msg(td, + "sendfile is only implemented for sending to a stream socket"); + return (ENOSYS); + } + + size_val = sizeof(ti); + ret = kern_getsockopt(td, arg->out, IPPROTO_TCP, TCP_INFO, + &ti, UIO_SYSSPACE, &size_val); + + /* datagram socket not implemented */ + if (ret != 0) { + linux_msg(td, + "sendfile is only implemented for sending to a stream socket"); + return (ENOSYS); + } + + /* offset is assumed as 0 when no pointer is given in linux_sendfile */ + offset = 0; + if (arg->offset != NULL) { + ret = copyin(arg->offset, &offset, sizeof(arg->offset)); + if (ret < 0) + return (ret); + } + + if (offset < 0) + return (EINVAL); + + bytes_read = 0; + + AUDIT_ARG_FD(arg->in); + /* Checks if fdin is valid */ + if ((ret = fget_read(td, arg->in, &cap_pread_rights, &fp)) != 0) + return (ret); + + /* Call real sendfile iff count != 0 */ + if (arg->count != 0) { + ret = fo_sendfile(fp, arg->out, NULL, NULL, offset, arg->count, + &bytes_read, 0, td); + if (ret < 0) + return (ret); + + offset += bytes_read; + } + + if (arg->offset != NULL) { + ret = copyout(&offset, arg->offset, sizeof(offset)); + if (ret < 0) + return (ret); + } + + td->td_retval[0] = (ssize_t) bytes_read; + return 0; +} + #if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) /* Argument list sizes for linux_socketcall */ @@ -1707,7 +1801,7 @@ 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)) @@ -1776,6 +1870,8 @@ 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);