]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Y2038: add struct __timespec64
authorAlbert ARIBAUD (3ADEV) <albert.aribaud@3adev.fr>
Thu, 7 Sep 2017 22:41:33 +0000 (00:41 +0200)
committerAlbert ARIBAUD (3ADEV) <albert.aribaud@3adev.fr>
Wed, 24 Oct 2018 10:53:03 +0000 (12:53 +0200)
This type is a glibc-internal type similar to struct timespec
but whose tv_sec field is a _time64_t rather than a time_t,
which makes it Y2038-proof and useable to pass between user
code and Y2038-proof kernel syscalls.

On 64-bit architectures, and on X32, struct __timespec64 is
just an alias of struct timespec, which is already 64-bit.
On other architectures, it must be explicitly defined.

In order to match the Linux kernel struct, the tv_nsec field
of struct __timespec64 is also a signed 64-bit integer. The
kernel ensures that the higher half of tv_nsec is always 0,
which means glibc does not need to do it.

Note: the corresponding glibc public* type will differ from
this private type, as the user source code might depend on the
tv_nsec field being a 32-bit long, not a 64-bit int. To meet
this expectation, the public type will have a 32-bit tv_nsec
field plus 32-bit anonymous padding, ordered according to the
endianness of the architecture.

Tested with make xtests on x86_64 and ARM.

* include/bits/types/struct_timespec64.h:
  Include time/bits/types/struct_timespec64.h
* include/time.h (valid_timeval_to_timespec64): Add.
* include/time.h (valid_timespec_to_timespec64): Likewise.
* include/time.h (valid_timespec64_to_timespec): Likewise.
* include/time.h (valid_timespec64_to_timeval): Likewise.
* include/time.h (IS_VALID_NANOSECONDS): Likewise.
* include/time.h (timespec_to_timespec64): Likewise.
* include/time.h (timespec64_to_timespec): Likewise.
* include/time.h (timespec64_to_timeval): Likewise.
* io/fcntl.h: Include bits/types/struct_timespec64.h.
* io/sys/poll.h: Likewise.
* io/sys/stat.h: Likewise.
* misc/sys/select.h: Likewise.
* posix/sched.h: Likewise.
* posix/netdb.h: Likewise.
* rt/aio.h: Likewise.
* rt/mqueue.h: Likewise.
* signal/signal.h: Likewise.
* sysdeps/nptl/pthread.h: Likewise.
* sysdeps/pthread/semaphore.h: Likewise.
* sysdeps/unix/sysv/linux/hppa/pthread.h: Likewise.
* sysvipc/sys/sem.h: Likewise.
* time/Makefile: Add bits/types/struct_timespec64.h
* time/bits/types/struct_itimerspec.h:
  Include bits/types/struct_timespec64.h
* time/bits/types/struct_itimerspec64.h: Add.
* time/time.h: Include bits/types/struct_timespec64.h

include/bits/types/struct___timespec64.h [new file with mode: 0644]
include/time.h

diff --git a/include/bits/types/struct___timespec64.h b/include/bits/types/struct___timespec64.h
new file mode 100644 (file)
index 0000000..b168c77
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef __timespec64_defined
+#define __timespec64_defined 1
+
+#include <bits/types.h>
+#include <bits/timesize.h>
+#include <endian.h>
+
+/* The glibc-internal Y2038-proof structure for a time value.
+   To keep things Posix-ish, we keep the nanoseconds field a 32-bit
+   signed long, but since the Linux field is a 64-bit signed int, we
+   pad our tv_nsec with a 32-bit int, which should always be 0.
+   Note that this is the glibc-internal type; in the public type, the
+   padding is an anonymous 32-bit bitfield, so that source-code initializers
+   keep working.  */
+#if __TIMESIZE==64
+# define __timespec64 timespec
+#else
+#define TIMSPEC64_TV_PAD_DEFINED
+# if BYTE_ORDER == BIG_ENDIAN
+struct __timespec64
+{
+  __time64_t tv_sec;           /* Seconds */
+  int tv_pad: 32;              /* Padding named for checking/setting */
+  __syscall_slong_t tv_nsec;   /* Nanoseconds */
+};
+# else
+struct __timespec64
+{
+  __time64_t tv_sec;           /* Seconds */
+  __syscall_slong_t tv_nsec;   /* Nanoseconds */
+  int tv_pad: 32;              /* Padding named for checking/setting */
+};
+# endif
+#endif
+
+#endif
index 5e88f2a58c5d1c0887dc1f19758c75f4e930bd0c..f95f16ed46410af6f1eaff5882a061730bbc7550 100644 (file)
@@ -3,6 +3,7 @@
 
 #ifndef _ISOMAC
 # include <bits/types/locale_t.h>
+# include <bits/types/struct___timespec64.h>
 # include <stdbool.h>
 
 extern __typeof (strftime_l) __strftime_l;
@@ -166,5 +167,90 @@ fits_in_time_t (__time64_t t64)
   return t == t64;
 }
 
+/* Convert a known valid struct timeval into a struct __timespec64.  */
+static inline void
+valid_timeval_to_timespec64 (const struct timeval *tv32,
+                            struct __timespec64 *ts64)
+{
+  ts64->tv_sec = tv32->tv_sec;
+  ts64->tv_nsec = tv32->tv_usec * 1000;
+}
+
+/* Convert a known valid struct timespec into a struct __timespec64.  */
+static inline void
+valid_timespec_to_timespec64 (const struct timespec *ts32,
+                             struct __timespec64 *ts64)
+{
+  ts64->tv_sec = ts32->tv_sec;
+  ts64->tv_nsec = ts32->tv_nsec;
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+}
+
+/* Convert a known valid struct __timespec64 into a struct timespec.  */
+static inline void
+valid_timespec64_to_timespec (const struct __timespec64 *ts64,
+                             struct timespec *ts32)
+{
+  ts32->tv_sec = (time_t) ts64->tv_sec;
+  ts32->tv_nsec = ts64->tv_nsec;
+}
+
+/* Convert a known valid struct __timespec64 into a struct timeval.  */
+static inline void
+valid_timespec64_to_timeval (const struct __timespec64 *ts64,
+                            struct timeval *tv32)
+{
+  tv32->tv_sec = (time_t) ts64->tv_sec;
+  tv32->tv_usec = ts64->tv_nsec / 1000;
+}
+
+/* Check if a value lies with the valid nanoseconds range.  */
+#define IS_VALID_NANOSECONDS(ns) (ns >= 0 && ns <= 999999999)
+
+/* Check and convert a struct timespec into a struct __timespec64.  */
+static inline bool
+timespec_to_timespec64 (const struct timespec *ts32,
+                                          struct __timespec64 *ts64)
+{
+  /* Check that ts32 holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts32->tv_nsec))
+    return false;
+  /* All ts32 fields can fit in ts64, so copy them.  */
+  valid_timespec_to_timespec64 (ts32, ts64);
+  /* We only need to zero ts64->tv_pad if we pass it to the kernel.  */
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timespec.  */
+static inline bool
+timespec64_to_timespec (const struct __timespec64 *ts64,
+                                          struct timespec *ts32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! fits_in_time_t (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in ts32, so copy them.  */
+  valid_timespec64_to_timespec (ts64, ts32);
+  return true;
+}
+
+/* Check and convert a struct __timespec64 into a struct timeval.  */
+static inline bool
+timespec64_to_timeval (const struct __timespec64 *ts64,
+                                         struct timeval *tv32)
+{
+  /* Check that tv_nsec holds a valid count of nanoseconds.  */
+  if (! IS_VALID_NANOSECONDS (ts64->tv_nsec))
+    return false;
+  /* Check that tv_sec can fit in a __time_t.  */
+  if (! fits_in_time_t (ts64->tv_sec))
+    return false;
+  /* All ts64 fields can fit in tv32, so copy them.  */
+  valid_timespec64_to_timeval (ts64, tv32);
+  return true;
+}
 #endif
 #endif