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