[1/1] Y2038: make __mktime_internal compatible with __time64_t

Message ID e3f1ae91-1a11-7aea-aafd-25331c816d70@cs.ucla.edu
State New, archived
Headers

Commit Message

Paul Eggert Nov. 21, 2018, 5:49 p.m. UTC
  Albert ARIBAUD (3ADEV) wrote:
> +#if __TIMESIZE != 64
> +
> +/* The 32-bit-time wrapper.  */
> +time_t
> +mktime (struct tm *tp)

When this code is used in Gnulib in the obvious way, it defines another instance 
of 'mktime' which won't work. Also, this new instance of mktime uses a function 
fits_in_time_t, which isn't defined outside glibc. There is a similar problem 
with timegm.

A quick fix would be to change '#if __TIMESIZE != 64' to '#if defined _LIBC && 
__TIMESIZE != 64'. But we can take this opportunity to simplify mktime.c as per 
the attached patches. The first is for glibc and assumes the patches you already 
sent, and the second (for the record) is the resulting combined patch to Gnulib.
  

Patch

From c0b58031ef8b98741f67d74662ba9128d43c5d03 Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Wed, 21 Nov 2018 09:18:10 -0800
Subject: [PATCH] mktime: adjust to glibc 64-bit changes

* lib/mktime-internal.h (__gmtime64_r): Rename from __gmtime_r.
(__localtime64_r): Rename from __localtime_r.
(__mktime64, __time64_t, __timegm64): New macros.
* lib/mktime.c, lib/timegm.c: Sync from glibc, with changes
needed for Gnulib.
---
 ChangeLog             |  7 +++++
 lib/mktime-internal.h | 14 ++++++---
 lib/mktime.c          | 68 ++++++++++++++++++++++++++-----------------
 lib/timegm.c          | 21 +++++++++++--
 4 files changed, 77 insertions(+), 33 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 15630fe23..457b21d6f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@ 
 2018-11-21  Paul Eggert  <eggert@cs.ucla.edu>
 
+	mktime: adjust to glibc 64-bit changes
+	* lib/mktime-internal.h (__gmtime64_r): Rename from __gmtime_r.
+	(__localtime64_r): Rename from __localtime_r.
+	(__mktime64, __time64_t, __timegm64): New macros.
+	* lib/mktime.c, lib/timegm.c: Sync from glibc, with changes
+	needed for Gnulib.
+
 	mktime: add libc-config dependency
 	I missed this when we synced from glibc.
 	* modules/mktime (Depends-on): Add libc-config.
diff --git a/lib/mktime-internal.h b/lib/mktime-internal.h
index 3e64156e8..fde7bfa4c 100644
--- a/lib/mktime-internal.h
+++ b/lib/mktime-internal.h
@@ -45,9 +45,15 @@  time_t mktime_internal (struct tm *,
    Similarly for gmtime_r.  See the gnulib time_r module for one way
    to implement this.  */
 
-#undef __gmtime_r
-#undef __localtime_r
-#define __gmtime_r gmtime_r
-#define __localtime_r localtime_r
+#undef __gmtime64_r
+#undef __localtime64_r
+#undef __mktime64
+#undef __time64_t
+#undef __timegm64
+#define __gmtime64_r gmtime_r
+#define __localtime64_r localtime_r
+#define __mktime64 mktime
+#define __time64_t time_t
+#define __timegm64 timegm
 
 #define __mktime_internal mktime_internal
diff --git a/lib/mktime.c b/lib/mktime.c
index 8faa9bc93..f6d911da4 100644
--- a/lib/mktime.c
+++ b/lib/mktime.c
@@ -112,11 +112,11 @@  my_tzset (void)
    added to them, and then with another timestamp added, without
    worrying about overflow.
 
-   Much of the code uses long_int to represent time_t values, to
-   lessen the hassle of dealing with platforms where time_t is
+   Much of the code uses long_int to represent __time64_t values, to
+   lessen the hassle of dealing with platforms where __time64_t is
    unsigned, and because long_int should suffice to represent all
-   time_t values that mktime can generate even on platforms where
-   time_t is excessively wide.  */
+   __time64_t values that mktime can generate even on platforms where
+   __time64_t is excessively wide.  */
 
 #if INT_MAX <= LONG_MAX / 4 / 366 / 24 / 60 / 60
 typedef long int long_int;
@@ -144,16 +144,17 @@  shr (long_int a, int b)
 	  : a / (one << b) - (a % (one << b) < 0));
 }
 
-/* Bounds for the intersection of time_t and long_int.  */
+/* Bounds for the intersection of __time64_t and long_int.  */
 
 static long_int const mktime_min
-  = ((TYPE_SIGNED (time_t) && TYPE_MINIMUM (time_t) < TYPE_MINIMUM (long_int))
-     ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (time_t));
+  = ((TYPE_SIGNED (__time64_t)
+      && TYPE_MINIMUM (__time64_t) < TYPE_MINIMUM (long_int))
+     ? TYPE_MINIMUM (long_int) : TYPE_MINIMUM (__time64_t));
 static long_int const mktime_max
-  = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (time_t)
-     ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (time_t));
+  = (TYPE_MAXIMUM (long_int) < TYPE_MAXIMUM (__time64_t)
+     ? TYPE_MAXIMUM (long_int) : TYPE_MAXIMUM (__time64_t));
 
-verify (TYPE_IS_INTEGER (time_t));
+verify (TYPE_IS_INTEGER (__time64_t));
 
 #define EPOCH_YEAR 1970
 #define TM_YEAR_BASE 1900
@@ -252,23 +253,23 @@  tm_diff (long_int year, long_int yday, int hour, int min, int sec,
 }
 
 /* Use CONVERT to convert T to a struct tm value in *TM.  T must be in
-   range for time_t.  Return TM if successful, NULL (setting errno) on
+   range for __time64_t.  Return TM if successful, NULL (setting errno) on
    failure.  */
 static struct tm *
-convert_time (struct tm *(*convert) (const time_t *, struct tm *),
+convert_time (struct tm *(*convert) (const __time64_t *, struct tm *),
 	      long_int t, struct tm *tm)
 {
-  time_t x = t;
+  __time64_t x = t;
   return convert (&x, tm);
 }
 
 /* Use CONVERT to convert *T to a broken down time in *TP.
    If *T is out of range for conversion, adjust it so that
    it is the nearest in-range value and then convert that.
-   A value is in range if it fits in both time_t and long_int.
+   A value is in range if it fits in both __time64_t and long_int.
    Return TP on success, NULL (setting errno) on failure.  */
 static struct tm *
-ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
+ranged_convert (struct tm *(*convert) (const __time64_t *, struct tm *),
 		long_int *t, struct tm *tp)
 {
   long_int t1 = (*t < mktime_min ? mktime_min
@@ -310,7 +311,7 @@  ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
 }
 
 
-/* Convert *TP to a time_t value, inverting
+/* Convert *TP to a __time64_t value, inverting
    the monotonic and mostly-unit-linear conversion function CONVERT.
    Use *OFFSET to keep track of a guess at the offset of the result,
    compared to what the result would be for UTC without leap seconds.
@@ -318,9 +319,9 @@  ranged_convert (struct tm *(*convert) (const time_t *, struct tm *),
    If successful, set *TP to the canonicalized struct tm;
    otherwise leave *TP alone, return ((time_t) -1) and set errno.
    This function is external because it is used also by timegm.c.  */
-time_t
+__time64_t
 __mktime_internal (struct tm *tp,
-		   struct tm *(*convert) (const time_t *, struct tm *),
+		   struct tm *(*convert) (const __time64_t *, struct tm *),
 		   mktime_offset_t *offset)
 {
   struct tm tm;
@@ -520,9 +521,9 @@  __mktime_internal (struct tm *tp,
 
 #if defined _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS
 
-/* Convert *TP to a time_t value.  */
-time_t
-mktime (struct tm *tp)
+/* Convert *TP to a __time64_t value.  */
+__time64_t
+__mktime64 (struct tm *tp)
 {
   /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
      time zone names contained in the external variable 'tzname' shall
@@ -531,19 +532,34 @@  mktime (struct tm *tp)
 
 # if defined _LIBC || NEED_MKTIME_WORKING
   static mktime_offset_t localtime_offset;
-  return __mktime_internal (tp, __localtime_r, &localtime_offset);
+  return __mktime_internal (tp, __localtime64_r, &localtime_offset);
 # else
 #  undef mktime
   return mktime (tp);
 # endif
 }
+
+weak_alias (__mktime64, __timelocal64)
+libc_hidden_def (__mktime64)
+libc_hidden_weak (__timelocal64)
+
 #endif /* _LIBC || NEED_MKTIME_WORKING || NEED_MKTIME_WINDOWS */
 
-#ifdef weak_alias
-weak_alias (mktime, timelocal)
-#endif
+#if defined _LIBC && __TIMESIZE != 64
 
-#ifdef _LIBC
+/* The 32-bit-time wrapper.  */
+time_t
+mktime (struct tm *tp)
+{
+  __time64_t t64 = __mktime64 (tp);
+  if (fits_in_time_t (t64))
+    return t64;
+  __set_errno (EOVERFLOW);
+  return -1;
+}
+
+weak_alias (mktime, timelocal)
 libc_hidden_def (mktime)
 libc_hidden_weak (timelocal)
+
 #endif
diff --git a/lib/timegm.c b/lib/timegm.c
index 229fff23c..ec7f8eb16 100644
--- a/lib/timegm.c
+++ b/lib/timegm.c
@@ -24,11 +24,26 @@ 
 #include <time.h>
 
 #include "mktime-internal.h"
+#include <errno.h>
+
+__time64_t
+__timegm64 (struct tm *tmp)
+{
+  static long int gmtime_offset;
+  tmp->tm_isdst = 0;
+  return __mktime_internal (tmp, __gmtime64_r, &gmtime_offset);
+}
+
+#if defined _LIBC &&  __TIMESIZE != 64
 
 time_t
 timegm (struct tm *tmp)
 {
-  static mktime_offset_t gmtime_offset;
-  tmp->tm_isdst = 0;
-  return __mktime_internal (tmp, __gmtime_r, &gmtime_offset);
+  __time64_t t64 = __timegm64 (tmp);
+  if (fits_in_time_t (t64))
+    return t64;
+  __set_errno (EOVERFLOW);
+  return -1;
 }
+
+#endif
-- 
2.17.1