Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/compat/linux/common More complete sendmsg(2) and recvmsg...
details: https://anonhg.NetBSD.org/src/rev/bcacd1a9860e
branches: trunk
changeset: 550004:bcacd1a9860e
user: jdolecek <jdolecek%NetBSD.org@localhost>
date: Sun Jul 27 19:30:03 2003 +0000
description:
More complete sendmsg(2) and recvmsg(2) emulation:
* translate MSG_* flags
* rewrite CMSG level/type to appropriate NetBSD value on input, and to Linux
value on output
* handle different CMSG_DATA alignment for some archs
This fixes SCM_RIGHTS passing. Other SCM_* types are not supported -
the set is different on NetBSD and Linux. SCM_TIMESTAMP doesn't seem
to be actually implemented in Linux 2.5.15, so it's not supported
for Linux binaries either (for now).
PR: 21577 by Todd Vierling
diffstat:
sys/compat/linux/common/linux_socket.c | 464 ++++++++++++++++++++++++++++++--
sys/compat/linux/common/linux_socket.h | 52 +++-
2 files changed, 480 insertions(+), 36 deletions(-)
diffs (truncated from 618 to 300 lines):
diff -r ecaed21467e7 -r bcacd1a9860e sys/compat/linux/common/linux_socket.c
--- a/sys/compat/linux/common/linux_socket.c Sun Jul 27 19:08:03 2003 +0000
+++ b/sys/compat/linux/common/linux_socket.c Sun Jul 27 19:30:03 2003 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: linux_socket.c,v 1.48 2003/07/27 05:04:02 mrg Exp $ */
+/* $NetBSD: linux_socket.c,v 1.49 2003/07/27 19:30:03 jdolecek Exp $ */
/*-
* Copyright (c) 1995, 1998 The NetBSD Foundation, Inc.
@@ -39,15 +39,10 @@
/*
* Functions in multiarch:
* linux_sys_socketcall : linux_socketcall.c
- *
- * XXX Note: Linux CMSG_ALIGN() uses (sizeof(long)-1). For architectures
- * where our CMSG_ALIGN() differs (like powerpc, sparc, sparc64), the passed
- * control structure would need to be adjusted accordingly in sendmsg() and
- * recvmsg().
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.48 2003/07/27 05:04:02 mrg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: linux_socket.c,v 1.49 2003/07/27 19:30:03 jdolecek Exp $");
#if defined(_KERNEL_OPT)
#include "opt_inet.h"
@@ -77,6 +72,7 @@
#include <sys/protosw.h>
#include <sys/mbuf.h>
#include <sys/syslog.h>
+#include <sys/exec.h>
#include <sys/sa.h>
#include <sys/syscallargs.h>
@@ -121,6 +117,8 @@
static int linux_sa_get __P((struct proc *, caddr_t *sgp, struct sockaddr **sap,
const struct osockaddr *osa, int *osalen));
static int linux_sa_put __P((struct osockaddr *osa));
+static int linux_to_bsd_msg_flags __P((int));
+static int bsd_to_linux_msg_flags __P((int));
static const int linux_to_bsd_domain_[LINUX_AF_MAX] = {
AF_UNSPEC,
@@ -183,6 +181,27 @@
-1, /* pseudo_AF_HDRCMPLT */
};
+static const int bsd_to_linux_msg_flags_[] = {
+ MSG_OOB, LINUX_MSG_OOB,
+ MSG_PEEK, LINUX_MSG_PEEK,
+ MSG_DONTROUTE, LINUX_MSG_DONTROUTE,
+ MSG_EOR, LINUX_MSG_EOR,
+ MSG_TRUNC, LINUX_MSG_TRUNC,
+ MSG_CTRUNC, LINUX_MSG_CTRUNC,
+ MSG_WAITALL, LINUX_MSG_WAITALL,
+ MSG_DONTWAIT, LINUX_MSG_DONTWAIT,
+ MSG_BCAST, 0, /* not supported, clear */
+ MSG_MCAST, 0, /* not supported, clear */
+ -1, /* not supp */ LINUX_MSG_PROBE,
+ -1, /* not supp */ LINUX_MSG_FIN,
+ -1, /* not supp */ LINUX_MSG_SYN,
+ -1, /* not supp */ LINUX_MSG_CONFIRM,
+ -1, /* not supp */ LINUX_MSG_RST,
+ -1, /* not supp */ LINUX_MSG_ERRQUEUE,
+ -1, /* not supp */ LINUX_MSG_NOSIGNAL,
+ -1, /* not supp */ LINUX_MSG_MORE,
+};
+
/*
* Convert between Linux and BSD socket domain values
*/
@@ -209,6 +228,62 @@
return bsd_to_linux_domain_[bdom];
}
+static int
+linux_to_bsd_msg_flags(lflag)
+ int lflag;
+{
+ int i, lfl, bfl;
+ int bflag = 0;
+
+ if (lflag == 0)
+ return (0);
+
+ for(i=0; i < sizeof(bsd_to_linux_msg_flags_)/2; i += 2) {
+ bfl = bsd_to_linux_msg_flags_[i];
+ lfl = bsd_to_linux_msg_flags_[i+1];
+
+ if (lfl == 0)
+ continue;
+
+ if (lflag & lfl) {
+ if (bfl < 0)
+ return (-1);
+
+ bflag |= bfl;
+ }
+ }
+
+ return (bflag);
+}
+
+static int
+bsd_to_linux_msg_flags(bflag)
+ int bflag;
+{
+ int i, lfl, bfl;
+ int lflag = 0;
+
+ if (bflag == 0)
+ return (0);
+
+ for(i=0; i < sizeof(bsd_to_linux_msg_flags_)/2; i += 2) {
+ bfl = bsd_to_linux_msg_flags_[i];
+ lfl = bsd_to_linux_msg_flags_[i+1];
+
+ if (bfl <= 0)
+ continue;
+
+ if (bflag & bfl) {
+ if (lfl < 0)
+ return (-1);
+
+ lflag |= lfl;
+ }
+ }
+
+ return (lflag);
+}
+
int
linux_sys_socket(l, v, retval)
struct lwp *l;
@@ -336,48 +411,212 @@
struct proc *p = l->l_proc;
struct msghdr msg;
int error;
- struct sys_sendmsg_args bsa;
- struct msghdr *nmsg = (struct msghdr *)SCARG(uap, msg);
+ struct iovec aiov[UIO_SMALLIOV], *iov;
+ caddr_t sg = 0;
+ int bflags;
+ u_int8_t *control=NULL;
error = copyin(SCARG(uap, msg), (caddr_t)&msg, sizeof(msg));
if (error)
return (error);
+ if ((unsigned int)msg.msg_iovlen > UIO_SMALLIOV) {
+ if ((unsigned int)msg.msg_iovlen > IOV_MAX)
+ return (EMSGSIZE);
+ iov = malloc(sizeof(struct iovec) * msg.msg_iovlen,
+ M_IOV, M_WAITOK);
+ } else
+ iov = aiov;
+ if ((unsigned int)msg.msg_iovlen > 0) {
+ error = copyin((caddr_t)msg.msg_iov, (caddr_t)iov,
+ (size_t)(msg.msg_iovlen * sizeof(struct iovec)));
+ if (error)
+ goto done;
+ }
+ msg.msg_iov = iov;
+ msg.msg_flags = 0;
+ /* Convert the sockaddr if necessary */
if (msg.msg_name) {
struct sockaddr *sa;
- caddr_t sg = stackgap_init(p, 0);
-
- nmsg = (struct msghdr *) stackgap_alloc(p, &sg,
- sizeof(struct msghdr));
- if (!nmsg)
- return (ENOMEM);
+ sg = stackgap_init(p, 0);
error = linux_sa_get(p, &sg, &sa,
(struct osockaddr *) msg.msg_name, &msg.msg_namelen);
if (error)
- return (error);
+ goto done;
+ msg.msg_name = sa;
+ }
- msg.msg_name = (struct sockaddr *) sa;
- if ((error = copyout(&msg, nmsg, sizeof(struct msghdr))))
- return (error);
+ /*
+ * Translate message flags.
+ */
+ bflags = linux_to_bsd_msg_flags(SCARG(uap, flags));
+ if (bflags < 0) {
+ /* Some supported flag */
+ error = EINVAL;
+ goto done;
}
/*
- * XXX handle different alignment of cmsg data on architectures where
- * the Linux alignment is different (powerpc, sparc, sparc64).
+ * Handle cmsg if there is any.
*/
+ if (CMSG_FIRSTHDR(&msg)) {
+ struct cmsghdr cmsg, *cc;
+ int changed = 0;
+ ssize_t resid = msg.msg_controllen;
+ size_t clen, cidx = 0, cspace;
+
+ /*
+ * Limit the size even more than what sockargs() would do,
+ * We need to fit into stackgap space.
+ */
+ if (msg.msg_controllen > (STACKGAPLEN / 2)) {
+ /* Sorry guys! */
+ error = EMSGSIZE;
+ goto done;
+ }
+
+ control = malloc((clen = msg.msg_controllen), M_TEMP, M_WAITOK);
+ if (!control) {
+ error = ENOMEM;
+ goto done;
+ }
+
+ cc = CMSG_FIRSTHDR(&msg);
+ do {
+ error = copyin(cc, &cmsg, sizeof(cmsg));
+ if (error)
+ goto done;
+
+ /*
+ * Sanity check the control message length.
+ */
+ if (cmsg.cmsg_len > resid
+ || cmsg.cmsg_len < sizeof(struct cmsghdr)) {
+ error = EINVAL;
+ goto done;
+ }
+
+ /*
+ * Refuse unsupported control messages, and
+ * translate fields as appropriate.
+ */
+ switch (cmsg.cmsg_level) {
+ case LINUX_SOL_SOCKET:
+ /* It only differs on some archs */
+ if (LINUX_SOL_SOCKET != SOL_SOCKET) {
+ cmsg.cmsg_level = SOL_SOCKET;
+ changed = 1;
+ }
+
+ switch(cmsg.cmsg_type) {
+ case LINUX_SCM_RIGHTS:
+ /* Linux SCM_RIGHTS is same as NetBSD */
+ break;
+
+ default:
+ /* other types not supported */
+ error = EINVAL;
+ goto done;
+ }
+ break;
+ default:
+ /* pray and leave intact */
+ break;
+ }
+
+ cspace = CMSG_SPACE(cmsg.cmsg_len - sizeof(cmsg));
+
+ /* Check the buffer is big enough */
+ if (__predict_false(cidx + cspace > clen)) {
+ u_int8_t *nc;
- SCARG(&bsa, s) = SCARG(uap, s);
- SCARG(&bsa, msg) = nmsg;
- SCARG(&bsa, flags) = SCARG(uap, flags);
+ clen = cidx + cspace;
+ nc = realloc(control, clen, M_TEMP, M_WAITOK);
+ if (!nc) {
+ error = ENOMEM;
+ goto done;
+ }
+ control = nc;
+ }
+
+ /* Copy header */
+ memcpy(&control[cidx], &cmsg, sizeof(cmsg));
+
+ /* Zero are between header and data */
+ memset(&control[cidx+sizeof(cmsg)], 0,
+ CMSG_ALIGN(sizeof(cmsg)) - sizeof(cmsg));
+
+ /* Copyin the data */
+ error = copyin(LINUX_CMSG_DATA(cc),
+ CMSG_DATA(control),
+ cmsg.cmsg_len - sizeof(cmsg));
+ if (error)
+ goto done;
+
+ /*
+ * If there is alignment difference, we changed
Home |
Main Index |
Thread Index |
Old Index