diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-05-04 19:47:21 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-05-04 19:47:21 +0000 |
commit | 642f2603a073324180f95c0876410fb840b84236 (patch) | |
tree | 570092278a3ea4c3acb3d058cdc8f94f2e9fa3a0 | |
parent | 5a8ae43fd4929960d6d09a974597aca8f7542425 (diff) | |
download | illumos-joyent-642f2603a073324180f95c0876410fb840b84236.tar.gz |
OS-5357 datadog doesn't work correctly in LX zone
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/lx_brand.c | 8 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/common/socket.c | 358 | ||||
-rw-r--r-- | usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_syscall.c | 8 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/sys/lx_syscalls.h | 4 | ||||
-rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_socket.c | 225 |
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 */ }; |