linuxulator: implement sendfile

Submitted by:	Bora Özarslan <borako.ozarslan@gmail.com>
Submitted by:	Yang Wang <2333@outlook.jp>
Reviewed by:	markj
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D19917
This commit is contained in:
emaste 2020-02-05 16:53:02 +00:00
parent 4c3d743519
commit 77a92d8644
9 changed files with 164 additions and 15 deletions

View File

@ -59,7 +59,6 @@ UNIMPLEMENTED(set_thread_area);
UNIMPLEMENTED(uselib);
UNIMPLEMENTED(vserver);
DUMMY(sendfile);
DUMMY(setfsuid);
DUMMY(setfsgid);
DUMMY(sysfs);

View File

@ -72,7 +72,6 @@ DUMMY(delete_module);
DUMMY(quotactl);
DUMMY(bdflush);
DUMMY(sysfs);
DUMMY(sendfile);
DUMMY(setfsuid);
DUMMY(setfsgid);
DUMMY(pivot_root);

View File

@ -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, \

View File

@ -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,

View File

@ -64,7 +64,6 @@ UNIMPLEMENTED(tuxcall);
UNIMPLEMENTED(uselib);
UNIMPLEMENTED(vserver);
DUMMY(sendfile);
DUMMY(setfsuid);
DUMMY(setfsgid);
DUMMY(vhangup);

View File

@ -49,8 +49,12 @@ __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>
@ -1581,7 +1585,134 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
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[] = {
@ -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_socketcall_args *args)
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) */

View File

@ -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_accept_args *args);
#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

View File

@ -75,7 +75,6 @@ DUMMY(quotactl);
DUMMY(bdflush);
DUMMY(sysfs);
DUMMY(vm86);
DUMMY(sendfile); /* different semantics */
DUMMY(setfsuid);
DUMMY(setfsgid);
DUMMY(pivot_root);

View File

@ -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, \