diff options
Diffstat (limited to 'sysdeps/unix/sysv/linux/getsockopt.c')
-rw-r--r-- | sysdeps/unix/sysv/linux/getsockopt.c | 70 |
1 files changed, 63 insertions, 7 deletions
diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c index 76ee8a94d6..688a7de087 100644 --- a/sysdeps/unix/sysv/linux/getsockopt.c +++ b/sysdeps/unix/sysv/linux/getsockopt.c @@ -15,16 +15,15 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ -#include <errno.h> -#include <signal.h> #include <sys/socket.h> - +#include <time.h> +#include <sysdep.h> #include <socketcall.h> -#include <kernel-features.h> -#include <sys/syscall.h> +#include <socket-constants-time64.h> -int -__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len) +static int +getsockopt_syscall (int fd, int level, int optname, void *optval, + socklen_t *len) { #ifdef __ASSUME_GETSOCKOPT_SYSCALL return INLINE_SYSCALL (getsockopt, 5, fd, level, optname, optval, len); @@ -32,4 +31,61 @@ __getsockopt (int fd, int level, int optname, void *optval, socklen_t *len) return SOCKETCALL (getsockopt, fd, level, optname, optval, len); #endif } + +#ifndef __ASSUME_TIME64_SYSCALLS +static int +getsockopt32 (int fd, int level, int optname, void *optval, + socklen_t *len) +{ + int r = -1; + + if (level != SOL_SOCKET) + return r; + + switch (optname) + { + case COMPAT_SO_RCVTIMEO_NEW: + case COMPAT_SO_SNDTIMEO_NEW: + { + if (optname == COMPAT_SO_RCVTIMEO_NEW) + optname = COMPAT_SO_RCVTIMEO_OLD; + if (optname == COMPAT_SO_SNDTIMEO_NEW) + optname = COMPAT_SO_SNDTIMEO_OLD; + + struct __timeval32 tv32; + r = getsockopt_syscall (fd, level, optname, &tv32, + (socklen_t[]) { sizeof tv32 }); + if (r < 0) + break; + + /* POSIX states that if the size of the option value is greater than + then option length, the option value argument shall be silently + truncated. */ + if (*len >= sizeof (struct __timeval64)) + { + struct __timeval64 *tv64 = (struct __timeval64 *) optval; + *tv64 = valid_timeval32_to_timeval64 (tv32); + *len = sizeof (*tv64); + } + else + memcpy (optval, &tv32, sizeof tv32); + } + } + + return r; +} +#endif + +int +__getsockopt (int fd, int level, int optname, void *optval, socklen_t *len) +{ + int r = getsockopt_syscall (fd, level, optname, optval, len); + +#ifndef __ASSUME_TIME64_SYSCALLS + if (r == -1 && errno == ENOPROTOOPT) + r = getsockopt32 (fd, level, optname, optval, len); +#endif + + return r; +} weak_alias (__getsockopt, getsockopt) |