From: Albert ARIBAUD (3ADEV) Date: Thu, 7 Sep 2017 22:41:33 +0000 (+0200) Subject: Y2038: add struct __timespec64 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eaf0f523fb03866d27d2175b7b280a825b91a8c1;p=thirdparty%2Fglibc.git Y2038: add struct __timespec64 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 --- diff --git a/include/bits/types/struct___timespec64.h b/include/bits/types/struct___timespec64.h new file mode 100644 index 00000000000..b168c7708bd --- /dev/null +++ b/include/bits/types/struct___timespec64.h @@ -0,0 +1,36 @@ +#ifndef __timespec64_defined +#define __timespec64_defined 1 + +#include +#include +#include + +/* 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 diff --git a/include/time.h b/include/time.h index 5e88f2a58c5..f95f16ed464 100644 --- a/include/time.h +++ b/include/time.h @@ -3,6 +3,7 @@ #ifndef _ISOMAC # include +# include # include 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