@@ -162,6 +162,15 @@ libc_hidden_proto (__libc_sa_len)
# define SA_LEN(_x) __libc_sa_len((_x)->sa_family)
#endif
+/* Used on y2038 emulation on 64-bit time_t binaries running on older
+ kernel without 64-bit time_t support. */
+#define SCM_TIMESTAMP_OLD SO_TIMESTAMP_OLD
+#define SCM_TIMESTAMPNS_OLD SO_TIMESTAMPNS_OLD
+#define SCM_TIMESTAMPING_OLD SO_TIMESTAMPING_OLD
+#define SCM_TIMESTAMP_NEW SO_TIMESTAMP_NEW
+#define SCM_TIMESTAMPNS_NEW SO_TIMESTAMPNS_NEW
+#define SCM_TIMESTAMPING_NEW SO_TIMESTAMPING_NEW
+
libc_hidden_proto (__cmsg_nxthdr)
#endif
@@ -72,6 +72,18 @@ getsockopt32 (int fd, int level, int optname, void *optval,
*tv64 = valid_timeval32_to_timeval64 (tv32);
*len = sizeof (*tv64);
}
+ break;
+
+ case SO_TIMESTAMP_NEW:
+ case SO_TIMESTAMPNS_NEW:
+ {
+ if (optname == SO_TIMESTAMP_NEW)
+ optname = SO_TIMESTAMP_OLD;
+ if (optname == SO_TIMESTAMPNS_NEW)
+ optname = SO_TIMESTAMPNS_OLD;
+ r = getsockopt_syscall (fd, level, optname, optval, len);
+ }
+ break;
}
return r;
@@ -21,14 +21,97 @@
#include <socketcall.h>
#include <shlib-compat.h>
+#ifndef __ASSUME_TIME64_SYSCALLS
+/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and
+ appends it to the control buffer. The 32-bit time field is kept as-is.
+
+ Calls with __TIMESIZE=32 will see the converted 64-bit time control
+ messages as spurious control message of unknown type.
+
+ Calls with __TIMESIZE=64 running on pre-time64 kernels will see the
+ original message as a spurious control ones of unknown typ while running
+ on kernel with native 64-bit time support will only see the time64 version
+ of the control message. */
+static void
+convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize)
+{
+ if (msg->msg_control == NULL || msg->msg_controllen == 0)
+ return;
+
+ /* The returnted control message format for SO_TIMESTAMP_NEW is a
+ 'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a
+ 'struct __kernel_timespec'. In both case it is essentially two
+ uint64_t members. */
+ uint64_t tvts[2];
+
+ struct cmsghdr *cmsg, *last = NULL;
+ int type = 0;
+
+ for (cmsg = CMSG_FIRSTHDR (msg);
+ cmsg != NULL;
+ cmsg = CMSG_NXTHDR (msg, cmsg))
+ {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ switch (cmsg->cmsg_type)
+ {
+ case SCM_TIMESTAMP_OLD:
+ if (type != 0)
+ break;
+ type = SCM_TIMESTAMP_NEW;
+ goto common;
+
+ case SCM_TIMESTAMPNS_OLD:
+ type = SCM_TIMESTAMPNS_NEW;
+
+ /* fallthrough */
+ common:
+ memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts));
+ break;
+ }
+
+ last = cmsg;
+ }
+
+ if (last == NULL || type == 0)
+ return;
+
+ if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen)
+ {
+ msg->msg_flags |= MSG_CTRUNC;
+ return;
+ }
+
+ msg->msg_controllen += CMSG_SPACE (sizeof tvts);
+ cmsg = CMSG_NXTHDR(msg, last);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = type;
+ cmsg->cmsg_len = CMSG_LEN (sizeof tvts);
+ memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts);
+}
+#endif
+
ssize_t
__libc_recvmsg (int fd, struct msghdr *msg, int flags)
{
-# ifdef __ASSUME_RECVMSG_SYSCALL
- return SYSCALL_CANCEL (recvmsg, fd, msg, flags);
-# else
- return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
-# endif
+ ssize_t r;
+#ifndef __ASSUME_TIME64_SYSCALLS
+ socklen_t orig_controllen = msg->msg_controllen;
+#endif
+
+#ifdef __ASSUME_RECVMSG_SYSCALL
+ r = SYSCALL_CANCEL (recvmsg, fd, msg, flags);
+#else
+ r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags);
+#endif
+
+#ifndef __ASSUME_TIME64_SYSCALLS
+ if (r >= 0)
+ convert_scm_timestamps (msg, orig_controllen);
+#endif
+
+ return r;
}
weak_alias (__libc_recvmsg, recvmsg)
weak_alias (__libc_recvmsg, __recvmsg)
@@ -74,6 +74,18 @@ setsockopt32 (int fd, int level, int optname, const void *optval,
r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32));
}
+ break;
+
+ case SO_TIMESTAMP_NEW:
+ case SO_TIMESTAMPNS_NEW:
+ {
+ if (optname == SO_TIMESTAMP_NEW)
+ optname = SO_TIMESTAMP_OLD;
+ if (optname == SO_TIMESTAMPNS_NEW)
+ optname = SO_TIMESTAMPNS_OLD;
+ r = setsockopt_syscall (fd, level, optname, NULL, 0);
+ }
+ break;
}
return r;