summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/lx_brand.c8
-rw-r--r--usr/src/lib/brand/lx/lx_brand/common/socket.c358
-rw-r--r--usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h4
-rw-r--r--usr/src/uts/common/brand/lx/os/lx_syscall.c8
-rw-r--r--usr/src/uts/common/brand/lx/sys/lx_syscalls.h4
-rw-r--r--usr/src/uts/common/brand/lx/syscall/lx_socket.c225
6 files changed, 238 insertions, 369 deletions
diff --git a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
index 7024a5d567..3f64d3e806 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c
@@ -1006,7 +1006,7 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_sendfile64, /* 40: sendfile */
NULL, /* 41: socket */
NULL, /* 42: connect */
- lx_accept, /* 43: accept */
+ NULL, /* 43: accept */
NULL, /* 44: sendto */
NULL, /* 45: recvfrom */
NULL, /* 46: sendmsg */
@@ -1014,8 +1014,8 @@ static lx_syscall_handler_t lx_handlers[] = {
lx_shutdown, /* 48: shutdown */
NULL, /* 49: bind */
lx_listen, /* 50: listen */
- lx_getsockname, /* 51: getsockname */
- lx_getpeername, /* 52: getpeername */
+ NULL, /* 51: getsockname */
+ NULL, /* 52: getpeername */
lx_socketpair, /* 53: socketpair */
NULL, /* 54: setsockopt */
NULL, /* 55: getsockopt */
@@ -1251,7 +1251,7 @@ static lx_syscall_handler_t lx_handlers[] = {
NULL, /* 285: fallocate */
lx_timerfd_settime, /* 286: timerfd_settime */
lx_timerfd_gettime, /* 287: timerfd_gettime */
- lx_accept4, /* 288: accept4 */
+ NULL, /* 288: accept4 */
lx_signalfd4, /* 289: signalfd4 */
lx_eventfd2, /* 290: eventfd2 */
NULL, /* 291: epoll_create1 */
diff --git a/usr/src/lib/brand/lx/lx_brand/common/socket.c b/usr/src/lib/brand/lx/lx_brand/common/socket.c
index 20e30b9a52..e8659cf9ba 100644
--- a/usr/src/lib/brand/lx/lx_brand/common/socket.c
+++ b/usr/src/lib/brand/lx/lx_brand/common/socket.c
@@ -22,7 +22,7 @@
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
- * Copyright 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
*/
#include <unistd.h>
@@ -56,29 +56,11 @@
#include <sys/lx_misc.h>
#include <netpacket/packet.h>
-/*
- * This string is used to prefix all abstract namespace Unix sockets, ie all
- * abstract namespace sockets are converted to regular sockets in the /tmp
- * directory with .ABSK_ prefixed to their names.
- */
-#define ABST_PRFX "/tmp/.ABSK_"
-#define ABST_PRFX_LEN 11
-
-typedef enum {
- lxa_none,
- lxa_abstract,
- lxa_devlog
-} lx_addr_type_t;
-
#ifdef __i386
static int lx_listen32(ulong_t *);
-static int lx_accept32(ulong_t *);
-static int lx_getsockname32(ulong_t *);
-static int lx_getpeername32(ulong_t *);
static int lx_socketpair32(ulong_t *);
static int lx_shutdown32(ulong_t *);
-static int lx_accept4_32(ulong_t *);
static int lx_recvmmsg32(ulong_t *);
static int lx_sendmmsg32(ulong_t *);
@@ -92,9 +74,9 @@ static struct {
NULL, 3,
NULL, 3,
lx_listen32, 2,
- lx_accept32, 3,
- lx_getsockname32, 3,
- lx_getpeername32, 3,
+ NULL, 3,
+ NULL, 3,
+ NULL, 3,
lx_socketpair32, 4,
NULL, 4,
NULL, 4,
@@ -105,7 +87,7 @@ static struct {
NULL, 5,
NULL, 3,
NULL, 3,
- lx_accept4_32, 4,
+ NULL, 4,
lx_recvmmsg32, 5,
lx_sendmmsg32, 4
};
@@ -199,62 +181,6 @@ convert_pkt_proto(int protocol)
}
static int
-stol_sockaddr(struct sockaddr *addr, socklen_t *len,
- struct sockaddr *inaddr, socklen_t inlen, socklen_t orig)
-{
- int size = inlen;
-
- switch (inaddr->sa_family) {
- case AF_INET:
- if (inlen > sizeof (struct sockaddr))
- return (EINVAL);
- break;
-
- case AF_INET6:
- if (inlen != sizeof (struct sockaddr_in6))
- return (EINVAL);
- /*
- * The linux sockaddr_in6 is shorter than illumos.
- * We just truncate the extra field on the way out
- */
- size = (sizeof (lx_sockaddr_in6_t));
- inlen = (sizeof (lx_sockaddr_in6_t));
- break;
-
- case AF_UNIX:
- if (inlen > sizeof (struct sockaddr_un))
- return (EINVAL);
- break;
-
- case (sa_family_t)AF_NOTSUPPORTED:
- return (EPROTONOSUPPORT);
-
- case (sa_family_t)AF_INVAL:
- return (EAFNOSUPPORT);
-
- default:
- break;
- }
-
- inaddr->sa_family = STOL_FAMILY(inaddr->sa_family);
-
- /*
- * If inlen is larger than orig, copy out the maximum amount of
- * data possible and then update *len to indicate the actual
- * size of all the data that it wanted to copy out.
- */
- size = (orig > 0 && orig < size) ? orig : size;
-
- if (uucopy(inaddr, addr, size) < 0)
- return (errno);
-
- if (uucopy(&inlen, len, sizeof (socklen_t)) < 0)
- return (errno);
-
- return (0);
-}
-
-static int
convert_sock_args(int in_dom, int in_type, int in_protocol, int *out_dom,
int *out_type, int *out_options, int *out_protocol)
{
@@ -315,193 +241,6 @@ lx_listen(int sockfd, int backlog)
}
long
-lx_accept(int sockfd, void *name, int *nlp)
-{
- socklen_t namelen = 0, origlen;
- struct sockaddr *saddr;
- int r, err;
- int size;
-
- lx_debug("\taccept(%d, 0x%p, 0x%p", sockfd, (struct sockaddr *)name,
- nlp);
-
- /*
- * The Linux man page says that -1 is returned and errno is set to
- * EFAULT if the "name" address is bad, but it is silent on what to
- * set errno to if the "namelen" address is bad. Experimentation
- * shows that Linux (at least the 2.4.21 kernel in CentOS) actually
- * sets errno to EINVAL in both cases.
- *
- * Note that we must first check the name pointer, as the Linux
- * docs state nothing is copied out if the "name" pointer is NULL.
- * If it is NULL, we don't care about the namelen pointer's value
- * or about dereferencing it.
- *
- * Happily, illumos' accept(3SOCKET) treats NULL name pointers and
- * zero namelens the same way.
- */
- if ((name != NULL) &&
- (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0))
- return ((errno == EFAULT) ? -EINVAL : -errno);
- origlen = namelen;
-
- if (name != NULL) {
- /*
- * Use sizeof (struct sockaddr_in6) as the minimum temporary
- * name allocation. This will allow families such as AF_INET6
- * to work properly when their namelen differs between LX and
- * illumos.
- */
- size = sizeof (struct sockaddr_in6);
- if (namelen > size)
- size = namelen;
-
- saddr = SAFE_ALLOCA(size);
- if (saddr == NULL)
- return (-EINVAL);
- bzero(saddr, size);
- } else {
- saddr = NULL;
- }
-
- lx_debug("\taccept namelen = %d", namelen);
-
- if ((r = accept(sockfd, saddr, &namelen)) < 0)
- return ((errno == EFAULT) ? -EINVAL : -errno);
-
- lx_debug("\taccept namelen returned %d bytes", namelen);
-
- /*
- * In Linux, accept()ed sockets do not inherit anything set by
- * fcntl(), so filter those out.
- */
- if (fcntl(r, F_SETFL, 0) < 0)
- return (-errno);
-
- /*
- * Once again, a bad "namelen" address sets errno to EINVAL, not
- * EFAULT. If namelen was zero, there's no need to copy a zero back
- * out.
- *
- * Logic might dictate that we should check if we can write to
- * the namelen pointer earlier so we don't accept a pending connection
- * only to fail the call because we can't write the namelen value back
- * out. However, testing shows Linux does indeed fail the call after
- * accepting the connection so we must behave in a compatible manner.
- */
- if ((name != NULL) && (namelen != 0)) {
- err = stol_sockaddr((struct sockaddr *)name, (socklen_t *)nlp,
- saddr, namelen, origlen);
- if (err != 0) {
- close(r);
- return ((err == EFAULT) ? -EINVAL : -err);
- }
- }
-
- return (r);
-}
-
-long
-lx_getsockname(int sockfd, void *np, int *nlp)
-{
- struct sockaddr *name = NULL;
- socklen_t namelen, namelen_orig;
- struct stat sb;
- int err;
-
- if (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0)
- return (-errno);
- namelen_orig = namelen;
-
- lx_debug("\tgetsockname(%d, 0x%p, 0x%p (=%d))", sockfd,
- (struct sockaddr *)np, nlp, namelen);
-
- if (fstat(sockfd, &sb) == 0 && !S_ISSOCK(sb.st_mode))
- return (-ENOTSOCK);
-
- /*
- * Use sizeof (struct sockaddr_in6) as the minimum temporary
- * name allocation. This will allow families such as AF_INET6
- * to work properly when their namelen differs between LX and
- * illumos.
- */
- if (namelen <= 0)
- return (-EBADF);
- else if (namelen < sizeof (struct sockaddr_in6))
- namelen = sizeof (struct sockaddr_in6);
-
- if ((name = SAFE_ALLOCA(namelen)) == NULL)
- return (-ENOMEM);
- bzero(name, namelen);
-
- if (getsockname(sockfd, name, &namelen) < 0)
- return (-errno);
-
- /*
- * If the name that getsockname() wants to return is larger
- * than namelen, getsockname() will copy out the maximum amount
- * of data possible and then update namelen to indicate the
- * actually size of all the data that it wanted to copy out.
- */
- err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp, name,
- namelen, namelen_orig);
- return ((err != 0) ? -err : 0);
-}
-
-long
-lx_getpeername(int sockfd, void *np, int *nlp)
-{
- struct sockaddr *name;
- socklen_t namelen, namelen_orig;
- int err;
-
- if (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0)
- return (-errno);
- namelen_orig = namelen;
-
- lx_debug("\tgetpeername(%d, 0x%p, 0x%p (=%d))", sockfd,
- (struct sockaddr *)np, nlp, namelen);
-
- /* LTP can pass -1 but we'll limit the allocation to a page */
- if ((uint32_t)namelen > 4096)
- return (-EINVAL);
-
- /*
- * Linux returns EFAULT in this case, even if the namelen parameter
- * is 0 (some test cases use -1, so we check for that too). This check
- * will not catch other illegal addresses, but the benefit catching a
- * non-null illegal address here is not worth the cost of another
- * system call.
- */
- if (np == NULL || np == (void *)-1)
- return (-EFAULT);
-
- /*
- * Use sizeof (struct sockaddr_in6) as the minimum temporary
- * name allocation. This will allow families such as AF_INET6
- * to work properly when their namelen differs between LX and
- * illumos.
- */
- if (namelen < sizeof (struct sockaddr_in6))
- namelen = sizeof (struct sockaddr_in6);
-
- name = SAFE_ALLOCA(namelen);
- if (name == NULL)
- return (-EINVAL);
- bzero(name, namelen);
-
- if ((getpeername(sockfd, name, &namelen)) < 0)
- return (-errno);
-
- err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp,
- name, namelen, namelen_orig);
- if (err != 0)
- return (-err);
-
- return (0);
-}
-
-long
lx_socketpair(int domain, int type, int protocol, int *sv)
{
int options;
@@ -544,65 +283,6 @@ lx_shutdown(int sockfd, int how)
return ((r < 0) ? -errno : r);
}
-/*
- * Based on the lx_accept code with the addition of the flags handling.
- * See internal comments in that function for more explanation.
- */
-long
-lx_accept4(int sockfd, void *np, int *nlp, int lx_flags)
-{
- socklen_t namelen, namelen_orig;
- struct sockaddr *name = NULL;
- int flags = 0;
- int r, err;
-
- lx_debug("\taccept4(%d, 0x%p, 0x%p 0x%x", sockfd, np, nlp, lx_flags);
-
- if ((np != NULL) &&
- (uucopy((void *)nlp, &namelen, sizeof (socklen_t)) != 0))
- return ((errno == EFAULT) ? -EINVAL : -errno);
-
- namelen_orig = namelen;
- lx_debug("\taccept4 namelen = %d", namelen);
-
- if (np != NULL) {
- /*
- * Use sizeof (struct sockaddr_in6) as the minimum temporary
- * name allocation. This will allow families such as AF_INET6
- * to work properly when their namelen differs between LX and
- * illumos.
- */
- if (namelen < sizeof (struct sockaddr_in6))
- namelen = sizeof (struct sockaddr_in6);
-
- name = SAFE_ALLOCA(namelen);
- if (name == NULL)
- return (-EINVAL);
- bzero(name, namelen);
- }
-
- if (lx_flags & LX_SOCK_NONBLOCK)
- flags |= SOCK_NONBLOCK;
-
- if (lx_flags & LX_SOCK_CLOEXEC)
- flags |= SOCK_CLOEXEC;
-
- if ((r = accept4(sockfd, name, &namelen, flags)) < 0)
- return ((errno == EFAULT) ? -EINVAL : -errno);
-
- lx_debug("\taccept4 namelen returned %d bytes", namelen);
-
- if (np != NULL && namelen != 0) {
- err = stol_sockaddr((struct sockaddr *)np, (socklen_t *)nlp,
- name, namelen, namelen_orig);
- if (err != 0) {
- close(r);
- return ((err == EFAULT) ? -EINVAL : -err);
- }
- }
- return (r);
-}
-
#ifdef __i386
static int
@@ -612,27 +292,6 @@ lx_listen32(ulong_t *args)
}
static int
-lx_accept32(ulong_t *args)
-{
- return (lx_accept((int)args[0], (struct sockaddr *)args[1],
- (int *)args[2]));
-}
-
-static int
-lx_getsockname32(ulong_t *args)
-{
- return (lx_getsockname((int)args[0], (struct sockaddr *)args[1],
- (int *)args[2]));
-}
-
-static int
-lx_getpeername32(ulong_t *args)
-{
- return (lx_getpeername((int)args[0], (struct sockaddr *)args[1],
- (int *)args[2]));
-}
-
-static int
lx_socketpair32(ulong_t *args)
{
return (lx_socketpair((int)args[0], (int)args[1], (int)args[2],
@@ -646,13 +305,6 @@ lx_shutdown32(ulong_t *args)
}
static int
-lx_accept4_32(ulong_t *args)
-{
- return (lx_accept4((int)args[0], (struct sockaddr *)args[1],
- (int *)args[2], (int)args[3]));
-}
-
-static int
lx_recvmmsg32(ulong_t *args)
{
lx_unsupported("Unsupported socketcall: recvmmsg\n.");
diff --git a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
index 4b2e23def8..20d027beaf 100644
--- a/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
+++ b/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h
@@ -75,10 +75,6 @@ extern long lx_fadvise64(uintptr_t, off64_t, uintptr_t, uintptr_t);
extern long lx_fadvise64_64(uintptr_t, off64_t, off64_t, uintptr_t);
extern long lx_socketcall(uintptr_t, uintptr_t);
-extern long lx_accept(int, void *, int *);
-extern long lx_accept4(int, void *, int *, int);
-extern long lx_getpeername(int, void *, int *);
-extern long lx_getsockname(int, void *, int *);
extern long lx_listen(int, int);
extern long lx_shutdown(int, int);
extern long lx_socketpair(int, int, int, int *);
diff --git a/usr/src/uts/common/brand/lx/os/lx_syscall.c b/usr/src/uts/common/brand/lx/os/lx_syscall.c
index 23010b8234..ddb89e71b8 100644
--- a/usr/src/uts/common/brand/lx/os/lx_syscall.c
+++ b/usr/src/uts/common/brand/lx/os/lx_syscall.c
@@ -1030,7 +1030,7 @@ lx_sysent_t lx_sysent64[] = {
{"sendfile", NULL, 0, 4}, /* 40 */
{"socket", lx_socket, 0, 3}, /* 41 */
{"connect", lx_connect, 0, 3}, /* 42 */
- {"accept", NULL, 0, 3}, /* 43 */
+ {"accept", lx_accept, 0, 3}, /* 43 */
{"sendto", lx_sendto, 0, 6}, /* 44 */
{"recvfrom", lx_recvfrom, 0, 6}, /* 45 */
{"sendmsg", lx_sendmsg, 0, 3}, /* 46 */
@@ -1038,8 +1038,8 @@ lx_sysent_t lx_sysent64[] = {
{"shutdown", NULL, 0, 2}, /* 48 */
{"bind", lx_bind, 0, 3}, /* 49 */
{"listen", NULL, 0, 2}, /* 50 */
- {"getsockname", NULL, 0, 3}, /* 51 */
- {"getpeername", NULL, 0, 3}, /* 52 */
+ {"getsockname", lx_getsockname, 0, 3}, /* 51 */
+ {"getpeername", lx_getpeername, 0, 3}, /* 52 */
{"socketpair", NULL, 0, 4}, /* 53 */
{"setsockopt", lx_setsockopt, 0, 5}, /* 54 */
{"getsockopt", lx_getsockopt, 0, 5}, /* 55 */
@@ -1275,7 +1275,7 @@ lx_sysent_t lx_sysent64[] = {
{"fallocate", lx_fallocate, 0, 4}, /* 285 */
{"timerfd_settime", NULL, 0, 4}, /* 286 */
{"timerfd_gettime", NULL, 0, 2}, /* 287 */
- {"accept4", NULL, 0, 4}, /* 288 */
+ {"accept4", lx_accept4, 0, 4}, /* 288 */
{"signalfd4", NULL, 0, 4}, /* 289 */
{"eventfd2", NULL, 0, 2}, /* 290 */
{"epoll_create1", lx_epoll_create1, 0, 1}, /* 291 */
diff --git a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
index 001be273cd..c1694d7bcf 100644
--- a/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
+++ b/usr/src/uts/common/brand/lx/sys/lx_syscalls.h
@@ -34,6 +34,8 @@ extern "C" {
#ifdef _KERNEL
+extern long lx_accept();
+extern long lx_accept4();
extern long lx_arch_prctl();
extern long lx_bind();
extern long lx_brk();
@@ -74,6 +76,8 @@ extern long lx_getcwd();
extern long lx_getdents_32();
extern long lx_getdents_64();
extern long lx_getdents64();
+extern long lx_getpeername();
+extern long lx_getsockname();
extern long lx_getpid();
extern long lx_getppid();
extern long lx_getrandom();
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
index d6229bc679..d714a8865e 100644
--- a/usr/src/uts/common/brand/lx/syscall/lx_socket.c
+++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c
@@ -89,6 +89,12 @@ typedef enum lx_xlate_dir {
LX_TO_SUNOS
} lx_xlate_dir_t;
+/* enum for getpeername/getsockname handling */
+typedef enum lx_getname_type {
+ LX_GETPEERNAME,
+ LX_GETSOCKNAME
+} lx_getname_type_t;
+
/*
* What follows are a series of tables we use to translate Linux constants
* into equivalent Illumos constants and back again. I wish this were
@@ -564,6 +570,19 @@ stol_sockaddr_copyout(struct sockaddr *inaddr, socklen_t inlen,
if (inlen > sizeof (struct sockaddr_un)) {
return (EINVAL);
}
+
+ /*
+ * On Linux an empty AF_UNIX address is returned as NULL, which
+ * means setting the returned length to only encompass the
+ * address family part of the buffer. However, some code also
+ * references the address portion of the buffer and uses it,
+ * even though the returned length has been shortened. Thus, we
+ * clear the buffer to ensure that the address portion is NULL.
+ */
+ if (inaddr->sa_data[0] == '\0') {
+ bzero(&buf, sizeof (buf));
+ inlen = sizeof (inaddr->sa_family);
+ }
break;
case (sa_family_t)AF_NOTSUPPORTED:
@@ -3389,6 +3408,204 @@ lx_getsockopt(int sock, int level, int optname, void *optval,
return (0);
}
+long
+lx_getname_common(lx_getname_type_t type, int sockfd, void *np, int *nlp)
+{
+ struct sockaddr_storage buf;
+ struct sockaddr *name = (struct sockaddr *)&buf;
+ socklen_t namelen, namelen_orig;
+ int err, tmp;
+ struct sonode *so;
+
+ /* We need to validate the name address up front to pass LTP. */
+ if (copyin(np, &tmp, sizeof (tmp)) != 0)
+ return (set_errno(EFAULT));
+
+ if (copyin(nlp, &namelen, sizeof (socklen_t)) != 0)
+ return (set_errno(EFAULT));
+ namelen_orig = namelen;
+
+ /* LTP can pass -1 */
+ if ((int)namelen < 0)
+ return (set_errno(EINVAL));
+
+ if ((so = getsonode(sockfd, &err, NULL)) == NULL)
+ return (set_errno(err));
+
+ bzero(&buf, sizeof (buf));
+ namelen = sizeof (struct sockaddr_storage);
+ if (type == LX_GETPEERNAME) {
+ err = socket_getpeername(so, name, &namelen, B_FALSE, CRED());
+ } else {
+ err = socket_getsockname(so, name, &namelen, CRED());
+ }
+
+ if (err == 0) {
+ ASSERT(namelen <= so->so_max_addr_len);
+ err = stol_sockaddr_copyout(name, namelen,
+ (struct sockaddr *)np, (socklen_t *)nlp, namelen_orig);
+ }
+
+ releasef(sockfd);
+ return (err != 0 ? set_errno(err) : 0);
+}
+
+long
+lx_getpeername(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETPEERNAME, sockfd, np, nlp));
+}
+
+long
+lx_getsockname(int sockfd, void *np, int *nlp)
+{
+ return (lx_getname_common(LX_GETSOCKNAME, sockfd, np, nlp));
+}
+
+static int
+lx_accept_common(int sock, struct sockaddr *name, socklen_t *nlp, int flags)
+{
+ struct sonode *so;
+ file_t *fp;
+ int error;
+ socklen_t namelen;
+ struct sonode *nso;
+ struct vnode *nvp;
+ struct file *nfp;
+ int nfd;
+ int arg;
+
+ if (flags & ~(LX_SOCK_CLOEXEC | LX_SOCK_NONBLOCK)) {
+ return (set_errno(EINVAL));
+ }
+
+ if ((so = getsonode(sock, &error, &fp)) == NULL)
+ return (set_errno(error));
+
+ if (name != NULL) {
+ /*
+ * The Linux man page says that -1 is returned and errno is set
+ * to EFAULT if the "name" address is bad, but it is silent on
+ * what to set errno to if the "namelen" address is bad.
+ * LTP expects EINVAL.
+ *
+ * Note that we must first check the name pointer, as the Linux
+ * docs state nothing is copied out if the "name" pointer is
+ * NULL. If it is NULL, we don't care about the namelen
+ * pointer's value or about dereferencing it.
+ */
+ if (copyin(nlp, &namelen, sizeof (namelen))) {
+ releasef(sock);
+ return (set_errno(EINVAL));
+ }
+ if (namelen == 0) {
+ name = NULL;
+ }
+ } else {
+ namelen = 0;
+ }
+
+ /*
+ * Allocate the user fd before socket_accept() in order to
+ * catch EMFILE errors before calling socket_accept().
+ */
+ if ((error = falloc(NULL, FWRITE|FREAD, &nfp, &nfd)) != 0) {
+ eprintsoline(so, EMFILE);
+ releasef(sock);
+ return (set_errno(error));
+ }
+ if ((error = socket_accept(so, fp->f_flag, CRED(), &nso)) != 0) {
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ nvp = SOTOV(nso);
+
+ if (namelen != 0) {
+ socklen_t addrlen = sizeof (struct sockaddr_storage);
+ struct sockaddr_storage buf;
+ struct sockaddr *addrp = (struct sockaddr *)&buf;
+
+ if ((error = socket_getpeername(nso, addrp, &addrlen, B_TRUE,
+ CRED())) == 0) {
+ error = stol_sockaddr_copyout(addrp, addrlen,
+ name, nlp, namelen);
+ /*
+ * Logic might dictate that we should check if we can
+ * write to the namelen pointer earlier so we don't
+ * accept a pending connection only to fail the call
+ * because we can't write the namelen value back out.
+ * However, testing shows Linux does indeed fail the
+ * call after accepting the connection so we must
+ * behave in a compatible manner.
+ */
+ } else {
+ ASSERT(error == EINVAL || error == ENOTCONN);
+ error = ECONNABORTED;
+ }
+ }
+
+ if (error != 0) {
+ setf(nfd, NULL);
+ unfalloc(nfp);
+ (void) socket_close(nso, 0, CRED());
+ socket_destroy(nso);
+ releasef(sock);
+ return (set_errno(error));
+ }
+
+ /* Fill in the entries that falloc reserved */
+ nfp->f_vnode = nvp;
+ mutex_exit(&nfp->f_tlock);
+ setf(nfd, nfp);
+
+ /* Act on LX_SOCK_CLOEXEC from flags */
+ if (flags & LX_SOCK_CLOEXEC) {
+ f_setfd(nfd, FD_CLOEXEC);
+ }
+
+ /*
+ * In Linux, accept()ed sockets do not inherit anything set by fcntl(),
+ * so either explicitly set the flags or filter those out.
+ *
+ * The VOP_SETFL code is a simplification of the F_SETFL code in
+ * fcntl(). Ignore any errors from VOP_SETFL.
+ */
+ arg = 0;
+ if (flags & LX_SOCK_NONBLOCK)
+ arg |= FNONBLOCK;
+
+ error = VOP_SETFL(nvp, nfp->f_flag, arg, nfp->f_cred, NULL);
+ if (error != 0) {
+ eprintsoline(so, error);
+ error = 0;
+ } else {
+ mutex_enter(&nfp->f_tlock);
+ nfp->f_flag &= ~FMASK | (FREAD|FWRITE);
+ nfp->f_flag |= arg;
+ mutex_exit(&nfp->f_tlock);
+ }
+
+ releasef(sock);
+ return (nfd);
+}
+
+long
+lx_accept(int sockfd, void *np, int *nlp)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, 0));
+}
+
+long
+lx_accept4(int sockfd, void *np, int *nlp, int flags)
+{
+ return (lx_accept_common(sockfd, (struct sockaddr *)np,
+ (socklen_t *)nlp, flags));
+}
+
#if defined(_SYSCALL32_IMPL)
#define LX_SYS_SOCKETCALL 102
@@ -3404,9 +3621,9 @@ static struct {
lx_bind, 3, /* bind */
lx_connect, 3, /* connect */
NULL, 2, /* listen */
- NULL, 3, /* accept */
- NULL, 3, /* getsockname */
- NULL, 3, /* getpeername */
+ lx_accept, 3, /* accept */
+ lx_getsockname, 3, /* getsockname */
+ lx_getpeername, 3, /* getpeername */
NULL, 4, /* socketpair */
lx_send, 4, /* send */
lx_recv, 4, /* recv */
@@ -3417,7 +3634,7 @@ static struct {
lx_getsockopt, 5, /* getsockopt */
lx_sendmsg, 3, /* sendmsg */
lx_recvmsg, 3, /* recvmsg */
- NULL, 4, /* accept4 */
+ lx_accept4, 4, /* accept4 */
NULL, 5, /* recvmmsg */
NULL, 4 /* sendmmsg */
};