diff options
| author | Florian Weimer <fweimer@redhat.com> | 2017-03-09 16:33:57 +0100 |
|---|---|---|
| committer | Florian Weimer <fweimer@redhat.com> | 2017-03-09 16:34:11 +0100 |
| commit | 60a91a23d61a48162f428e6031d8e2c36ca3ea9f (patch) | |
| tree | 89d4cb34531757daec7d82c9e8750fe180c4aaf6 | |
| parent | c9611e6f0e631edbc043986e3030d57e70fafb90 (diff) | |
| download | glibc-fw/bug16145.tar.xz glibc-fw/bug16145.zip | |
WIP reorganization to improve scalability of localtimefw/bug16145
| -rw-r--r-- | include/time.h | 16 | ||||
| -rw-r--r-- | time/gmtime.c | 6 | ||||
| -rw-r--r-- | time/localtime.c | 6 | ||||
| -rw-r--r-- | time/time-private.h | 148 | ||||
| -rw-r--r-- | time/tzfile.c | 492 | ||||
| -rw-r--r-- | time/tzset.c | 240 |
6 files changed, 519 insertions, 389 deletions
diff --git a/include/time.h b/include/time.h index 08454efa5e..19a750b518 100644 --- a/include/time.h +++ b/include/time.h @@ -38,19 +38,6 @@ libc_hidden_proto (__time_isleap) /* Defined in tzset.c. */ extern char *__tzstring (const char *string); -extern int __use_tzfile attribute_hidden; - -extern void __tzfile_read (const char *file, size_t extra, - char **extrap); -extern void __tzfile_compute (time_t timer, int use_localtime, - long int *leap_correct, int *leap_hit, - struct tm *tp); -extern void __tzfile_default (const char *std, const char *dst, - long int stdoff, long int dstoff); -extern void __tzset_parse_tz (const char *tz); -extern void __tz_compute (time_t timer, struct tm *tm, int use_localtime) - __THROW internal_function; - /* Subroutine of `mktime'. Return the `time_t' representation of TP and normalize TP, given that a `struct tm *' maps to a `time_t' as performed by FUNC. Keep track of next guess for time_t offset in *OFFSET. */ @@ -76,9 +63,6 @@ extern int __offtime (const time_t *__timer, extern char *__asctime_r (const struct tm *__tp, char *__buf); extern void __tzset (void); -/* Prototype for the internal function to get information based on TZ. */ -extern struct tm *__tz_convert (const time_t *timer, int use_localtime, struct tm *tp); - extern int __nanosleep (const struct timespec *__requested_time, struct timespec *__remaining); libc_hidden_proto (__nanosleep) diff --git a/time/gmtime.c b/time/gmtime.c index 1fc129b8e8..8a4a0a4f28 100644 --- a/time/gmtime.c +++ b/time/gmtime.c @@ -18,13 +18,14 @@ #include <time.h> #include <time/time-variables.h> +#include <time/time-private.h> /* Return the `struct tm' representation of *T in UTC, using *TP to store the result. */ struct tm * __gmtime_r (const time_t *t, struct tm *tp) { - return __tz_convert (t, 0, tp); + return __tz_convert (t, /* reentrant */ true, /* localtime */ false, tp); } libc_hidden_def (__gmtime_r) weak_alias (__gmtime_r, gmtime_r) @@ -34,5 +35,6 @@ weak_alias (__gmtime_r, gmtime_r) struct tm * gmtime (const time_t *t) { - return __tz_convert (t, 0, &_tmbuf); + return __tz_convert (t, /* reentrant */ false, /* localtime */ false, + &_tmbuf); } diff --git a/time/localtime.c b/time/localtime.c index 3a38cde809..2a95994c72 100644 --- a/time/localtime.c +++ b/time/localtime.c @@ -18,6 +18,7 @@ #include <time.h> #include <time/time-variables.h> +#include <time/time-private.h> /* The C Standard says that localtime and gmtime return the same pointer. */ struct tm _tmbuf; @@ -28,7 +29,7 @@ struct tm _tmbuf; struct tm * __localtime_r (const time_t *t, struct tm *tp) { - return __tz_convert (t, 1, tp); + return __tz_convert (t, /* reentrant */ true, /* localtime */ true, tp); } weak_alias (__localtime_r, localtime_r) @@ -37,6 +38,7 @@ weak_alias (__localtime_r, localtime_r) struct tm * localtime (const time_t *t) { - return __tz_convert (t, 1, &_tmbuf); + return __tz_convert (t, /* reentrant */ false, /* localtime */ true, + &_tmbuf); } libc_hidden_def (localtime) diff --git a/time/time-private.h b/time/time-private.h new file mode 100644 index 0000000000..872fa54bf5 --- /dev/null +++ b/time/time-private.h @@ -0,0 +1,148 @@ +/* Private declarations for the time subsystem. + Copyright (C) 1991-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +#ifndef TIME_PRIVATE_H +#define TIME_PRIVATE_H + +#include <stdbool.h> +#include <sys/types.h> + +struct ttinfo +{ + long int offset; /* Seconds east of GMT. */ + unsigned char isdst; /* Used to set tm_isdst. */ + unsigned char idx; /* Index into `zone_names'. */ + unsigned char isstd; /* Transition times are in standard time. */ + unsigned char isgmt; /* Transition times are in GMT. */ +}; + +struct leap +{ + time_t transition; /* Time the transition takes effect. */ + long int change; /* Seconds of correction to apply. */ +}; + +/* This structure contains all the information about a + timezone given in the POSIX standard TZ envariable. */ +struct tz_rule +{ + const char *name; + + /* When to change. */ + enum { J0, J1, M } type; /* Interpretation of: */ + unsigned short int m, n, d; /* Month, week, day. */ + int secs; /* Time of day. */ + + long int offset; /* Seconds east of GMT (west if < 0). */ +}; + +struct tzdata +{ + /* Updated by tzfile.c. */ + + /* Used to recognize unchanged tzdata files. */ + dev_t tzfile_dev; + ino64_t tzfile_ino; + time_t tzfile_mtime; + + size_t num_transitions; + time_t *transitions; + unsigned char *type_idxs; + size_t num_types; + struct ttinfo *types; + char *zone_names; + long int rule_stdoff; + long int rule_dstoff; + size_t num_leaps; + struct leap *leaps; + char *tzspec; + bool use_tzfile; + + /* Updated by tzset.c. */ + + /* Used to recognize an unchanged TZ environment variable. */ + char *old_tz; + + /* tz_rules[0] is standard, tz_rules[1] is daylight. */ + struct tz_rule tz_rules[2]; + + /* The remaining members correspond to the global variables of the + same name (with a __ prefix within glibc). */ + + bool daylight; + int timezone; + char *tzname[2]; +}; + +/* Determine the actual location of the timezone file based on FILE. + Return a newly allocated string (which the caller should free), and + NULL on memory allocation failure. */ +char *__tzfile_determine_path (const char *file) + internal_function attribute_hidden; + +/* Return true if the timezone data in *TZDATA is up-to-date with + regards to the modification time of the file at PATH. */ +bool __tzfile_is_current (const char *path, const struct tzdata *tzdata) + internal_function attribute_hidden __nonnull ((1, 2)); + +/* Load the timezone data specified by FILE in *TZDATA. All errors + result in a tzdata object for the UTC time zone without any leap + seconds. */ +void __tzfile_read (struct tzdata *tzdata, const char *file, + size_t extra, char **extrap) + internal_function attribute_hidden __nonnull ((1)); + +/* Deallocate the data in the struct tzdata object, but not the object + itself. */ +void __tzdata_free (struct tzdata *tzdata) + internal_function attribute_hidden __nonnull ((1)); + +/* The user specified a hand-made timezone, but not its DST rules. We + will use the names and offsets from the user, and the rules from + the TZDEFRULES file. */ +void __tzfile_default (struct tzdata *tzdata, + const char *std, const char *dst, + long int stdoff, long int dstoff) + internal_function attribute_hidden __nonnull ((1, 2, 3)); + +/* Parse the POSIX TZ-style string. */ +void __tzset_parse_tz (struct tzdata *tzdata, const char *tz) + internal_function attribute_hidden __nonnull ((1, 2)); + +/* Figure out the correct timezone for TM and set TM->tm_zone, + TM->tm_isdst, and TM->tm_gmtoff accordingly. */ +void __tz_compute (const struct tzdata *tzdata, + time_t timer, struct tm *tm, int use_localtime) + internal_function attribute_hidden __nonnull ((1, 3)); + +/* Write the struct tm representation of *TIMER in the local timezone + to *TP. Use local time if USE_LOCALTIME, UTC otherwise. If + USE_REENTRANT, the global timezone variables are not updated. + Return TP. */ +struct tm *__tz_convert (const time_t *timer, bool use_reentrant, + bool use_localtime, struct tm *tp) + internal_function attribute_hidden __nonnull ((1, 4)); + +extern void __tzfile_compute (const struct tzdata *tzdata, + time_t timer, int use_localtime, + long int *leap_correct, int *leap_hit, + struct tm *tp); + internal_function attribute_hidden __nonnull ((1, 4, 5, 6)); + + +#endif /* TIME_PRIVATE_H */ diff --git a/time/tzfile.c b/time/tzfile.c index 9290dc620b..aa7f5034b4 100644 --- a/time/tzfile.c +++ b/time/tzfile.c @@ -26,41 +26,9 @@ #include <sys/stat.h> #include <stdint.h> -#include <time/time-variables.h> +#include <time/time-private.h> #include <timezone/tzfile.h> -int __use_tzfile; -static dev_t tzfile_dev; -static ino64_t tzfile_ino; -static time_t tzfile_mtime; - -struct ttinfo - { - long int offset; /* Seconds east of GMT. */ - unsigned char isdst; /* Used to set tm_isdst. */ - unsigned char idx; /* Index into `zone_names'. */ - unsigned char isstd; /* Transition times are in standard time. */ - unsigned char isgmt; /* Transition times are in GMT. */ - }; - -struct leap - { - time_t transition; /* Time the transition takes effect. */ - long int change; /* Seconds of correction to apply. */ - }; - -static size_t num_transitions; -libc_freeres_ptr (static time_t *transitions); -static unsigned char *type_idxs; -static size_t num_types; -static struct ttinfo *types; -static char *zone_names; -static long int rule_stdoff; -static long int rule_dstoff; -static size_t num_leaps; -static struct leap *leaps; -static char *tzspec; - #include <endian.h> #include <byteswap.h> @@ -96,35 +64,17 @@ decode64 (const void *ptr) return bswap_64 (*(const int64_t *) ptr); } - -void -__tzfile_read (const char *file, size_t extra, char **extrap) +char * +internal_function +__tzfile_determine_path (const char *file) { static const char default_tzdir[] = TZDIR; - size_t num_isstd, num_isgmt; - FILE *f; - struct tzhead tzhead; - size_t chars; - size_t i; - size_t total_size; - size_t types_idx; - size_t leaps_idx; - int was_using_tzfile = __use_tzfile; - int trans_width = 4; - size_t tzspec_len; - char *new = NULL; - - if (sizeof (time_t) != 4 && sizeof (time_t) != 8) - abort (); - - __use_tzfile = 0; - if (file == NULL) /* No user specification; use the site-wide default. */ file = TZDEFAULT; else if (*file == '\0') /* User specified the empty string; use UTC with no leap seconds. */ - goto ret_free_transitions; + ; else { /* We must not allow to read an arbitrary file in a setuid @@ -138,7 +88,7 @@ __tzfile_read (const char *file, size_t extra, char **extrap) || strstr (file, "../") != NULL)) /* This test is certainly a bit too restrictive but it should catch all critical cases. */ - goto ret_free_transitions; + file = ""; /* Use UTC. */ } if (*file != '/') @@ -148,18 +98,45 @@ __tzfile_read (const char *file, size_t extra, char **extrap) tzdir = getenv ("TZDIR"); if (tzdir == NULL || *tzdir == '\0') tzdir = default_tzdir; + char *new; if (__asprintf (&new, "%s/%s", tzdir, file) == -1) - goto ret_free_transitions; - file = new; + return NULL; + return new; } + return strdup (file); +} + +bool +internal_function +__tzfile_is_current (const char *path, const struct tzdata *tzdata) +{ /* If we were already using tzfile, check whether the file changed. */ struct stat64 st; - if (was_using_tzfile - && stat64 (file, &st) == 0 - && tzfile_ino == st.st_ino && tzfile_dev == st.st_dev - && tzfile_mtime == st.st_mtime) - goto done; /* Nothing to do. */ + return tzdata->use_tzfile + && stat64 (path, &st) == 0 + && tzdata->tzfile_ino == st.st_ino + && tzdata->tzfile_dev == st.st_dev + && tzdata->tzfile_mtime == st.st_mtime; +} + +void +internal_function +__tzfile_read (struct tzdata *tzdata, const char *file, + size_t extra, char **extrap) +{ + size_t num_isstd, num_isgmt; + FILE *f; + struct tzhead tzhead; + size_t chars; + size_t i; + size_t total_size; + size_t types_idx; + size_t leaps_idx; + int trans_width = 4; + size_t tzspec_len; + + tzdata->use_tzfile = false; /* Note the file is opened with cancellation in the I/O functions disabled and if available FD_CLOEXEC set. */ @@ -168,19 +145,20 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto ret_free_transitions; /* Get information about the file we are actually using. */ + struct stat64 st; if (fstat64 (__fileno (f), &st) != 0) { fclose (f); goto ret_free_transitions; } - free ((void *) transitions); - transitions = NULL; + free (tzdata->transitions); + tzdata->transitions = NULL; /* Remember the inode and device number and modification time. */ - tzfile_dev = st.st_dev; - tzfile_ino = st.st_ino; - tzfile_mtime = st.st_mtime; + tzdata->tzfile_dev = st.st_dev; + tzdata->tzfile_ino = st.st_ino; + tzdata->tzfile_mtime = st.st_mtime; /* No threads reading this stream. */ __fsetlocking (f, FSETLOCKING_BYCALLER); @@ -191,14 +169,15 @@ __tzfile_read (const char *file, size_t extra, char **extrap) || memcmp (tzhead.tzh_magic, TZ_MAGIC, sizeof (tzhead.tzh_magic)) != 0) goto lose; - num_transitions = (size_t) decode (tzhead.tzh_timecnt); - num_types = (size_t) decode (tzhead.tzh_typecnt); + tzdata->num_transitions = (size_t) decode (tzhead.tzh_timecnt); + tzdata->num_types = (size_t) decode (tzhead.tzh_typecnt); chars = (size_t) decode (tzhead.tzh_charcnt); - num_leaps = (size_t) decode (tzhead.tzh_leapcnt); + tzdata->num_leaps = (size_t) decode (tzhead.tzh_leapcnt); num_isstd = (size_t) decode (tzhead.tzh_ttisstdcnt); num_isgmt = (size_t) decode (tzhead.tzh_ttisgmtcnt); - if (__glibc_unlikely (num_isstd > num_types || num_isgmt > num_types)) + if (__glibc_unlikely (num_isstd > tzdata->num_types + || num_isgmt > tzdata->num_types)) goto lose; /* For platforms with 64-bit time_t we use the new format if available. */ @@ -209,10 +188,10 @@ __tzfile_read (const char *file, size_t extra, char **extrap) trans_width = 8; /* Position the stream before the second header. */ - size_t to_skip = (num_transitions * (4 + 1) - + num_types * 6 + size_t to_skip = (tzdata->num_transitions * (4 + 1) + + tzdata->num_types * 6 + chars - + num_leaps * 8 + + tzdata->num_leaps * 8 + num_isstd + num_isgmt); if (fseek (f, to_skip, SEEK_CUR) != 0) @@ -221,18 +200,18 @@ __tzfile_read (const char *file, size_t extra, char **extrap) goto read_again; } - if (__builtin_expect (num_transitions + if (__builtin_expect (tzdata->num_transitions > ((SIZE_MAX - (__alignof__ (struct ttinfo) - 1)) / (sizeof (time_t) + 1)), 0)) goto lose; - total_size = num_transitions * (sizeof (time_t) + 1); + total_size = tzdata->num_transitions * (sizeof (time_t) + 1); total_size = ((total_size + __alignof__ (struct ttinfo) - 1) & ~(__alignof__ (struct ttinfo) - 1)); types_idx = total_size; - if (__builtin_expect (num_types + if (__builtin_expect (tzdata->num_types > (SIZE_MAX - total_size) / sizeof (struct ttinfo), 0)) goto lose; - total_size += num_types * sizeof (struct ttinfo); + total_size += tzdata->num_types * sizeof (struct ttinfo); if (__glibc_unlikely (chars > SIZE_MAX - total_size)) goto lose; total_size += chars; @@ -242,26 +221,26 @@ __tzfile_read (const char *file, size_t extra, char **extrap) total_size = ((total_size + __alignof__ (struct leap) - 1) & ~(__alignof__ (struct leap) - 1)); leaps_idx = total_size; - if (__builtin_expect (num_leaps + if (__builtin_expect (tzdata->num_leaps > (SIZE_MAX - total_size) / sizeof (struct leap), 0)) goto lose; - total_size += num_leaps * sizeof (struct leap); + total_size += tzdata->num_leaps * sizeof (struct leap); tzspec_len = 0; if (sizeof (time_t) == 8 && trans_width == 8) { off_t rem = st.st_size - __ftello (f); if (__builtin_expect (rem < 0 - || (size_t) rem < (num_transitions * (8 + 1) - + num_types * 6 + || (size_t) rem < (tzdata->num_transitions * (8 + 1) + + tzdata->num_types * 6 + chars), 0)) goto lose; - tzspec_len = (size_t) rem - (num_transitions * (8 + 1) - + num_types * 6 + tzspec_len = (size_t) rem - (tzdata->num_transitions * (8 + 1) + + tzdata->num_types * 6 + chars); - if (__builtin_expect (num_leaps > SIZE_MAX / 12 - || tzspec_len < num_leaps * 12, 0)) + if (__builtin_expect (tzdata->num_leaps > SIZE_MAX / 12 + || tzspec_len < tzdata->num_leaps * 12, 0)) goto lose; - tzspec_len -= num_leaps * 12; + tzspec_len -= tzdata->num_leaps * 12; if (__glibc_unlikely (tzspec_len < num_isstd)) goto lose; tzspec_len -= num_isstd; @@ -277,43 +256,47 @@ __tzfile_read (const char *file, size_t extra, char **extrap) /* Allocate enough memory including the extra block requested by the caller. */ - transitions = (time_t *) malloc (total_size + tzspec_len + extra); - if (transitions == NULL) + tzdata->transitions = (time_t *) malloc (total_size + tzspec_len + extra); + if (tzdata->transitions == NULL) goto lose; - type_idxs = (unsigned char *) transitions + (num_transitions - * sizeof (time_t)); - types = (struct ttinfo *) ((char *) transitions + types_idx); - zone_names = (char *) types + num_types * sizeof (struct ttinfo); - leaps = (struct leap *) ((char *) transitions + leaps_idx); + tzdata->type_idxs = (unsigned char *) tzdata->transitions + + (tzdata->num_transitions * sizeof (time_t)); + tzdata->types = (struct ttinfo *) ((char *) tzdata->transitions + types_idx); + tzdata->zone_names = (char *) tzdata->types + + tzdata->num_types * sizeof (struct ttinfo); + tzdata->leaps = (struct leap *) ((char *) tzdata->transitions + leaps_idx); if (sizeof (time_t) == 8 && trans_width == 8) - tzspec = (char *) leaps + num_leaps * sizeof (struct leap) + extra; + tzdata->tzspec = (char *) tzdata->leaps + + tzdata->num_leaps * sizeof (struct leap) + extra; else - tzspec = NULL; + tzdata->tzspec = NULL; if (extra > 0) - *extrap = (char *) &leaps[num_leaps]; + *extrap = (char *) &tzdata->leaps[tzdata->num_leaps]; if (sizeof (time_t) == 4 || __builtin_expect (trans_width == 8, 1)) { - if (__builtin_expect (__fread_unlocked (transitions, trans_width + 1, - num_transitions, f) - != num_transitions, 0)) + if (__builtin_expect (__fread_unlocked + (tzdata->transitions, trans_width + 1, + tzdata->num_transitions, f) + != tzdata->num_transitions, 0)) goto lose; } else { - if (__builtin_expect (__fread_unlocked (transitions, 4, - num_transitions, f) - != num_transitions, 0) - || __builtin_expect (__fread_unlocked (type_idxs, 1, num_transitions, - f) != num_transitions, 0)) + if (__builtin_expect (__fread_unlocked (tzdata->transitions, 4, + tzdata->num_transitions, f) + != tzdata->num_transitions, 0) + || __builtin_expect (__fread_unlocked + (tzdata->type_idxs, 1, tzdata->num_transitions, + f) != tzdata->num_transitions, 0)) goto lose; } /* Check for bogus indices in the data file, so we can hereafter safely use type_idxs[T] as indices into `types' and never crash. */ - for (i = 0; i < num_transitions; ++i) - if (__glibc_unlikely (type_idxs[i] >= num_types)) + for (i = 0; i < tzdata->num_transitions; ++i) + if (__glibc_unlikely (tzdata->type_idxs[i] >= tzdata->num_types)) goto lose; if ((BYTE_ORDER != BIG_ENDIAN && (sizeof (time_t) == 4 || trans_width == 4)) @@ -324,19 +307,20 @@ __tzfile_read (const char *file, size_t extra, char **extrap) network (big-endian) byte order. We work from the end of the array so as not to clobber the next element to be processed when sizeof (time_t) > 4. */ - i = num_transitions; + i = tzdata->num_transitions; while (i-- > 0) - transitions[i] = decode ((char *) transitions + i * 4); + tzdata->transitions[i] = decode ((char *) tzdata->transitions + i * 4); } else if (BYTE_ORDER != BIG_ENDIAN && sizeof (time_t) == 8) { /* Decode the transition times, stored as 8-byte integers in network (big-endian) byte order. */ - for (i = 0; i < num_transitions; ++i) - transitions[i] = decode64 ((char *) transitions + i * 8); + for (i = 0; i < tzdata->num_transitions; ++i) + tzdata->transitions[i] + = decode64 ((char *) tzdata->transitions + i * 8); } - for (i = 0; i < num_types; ++i) + for (i = 0; i < tzdata->num_types; ++i) { unsigned char x[4]; int c; @@ -347,32 +331,33 @@ __tzfile_read (const char *file, size_t extra, char **extrap) c = getc_unlocked (f); if (__glibc_unlikely ((unsigned int) c > 1u)) goto lose; - types[i].isdst = c; + tzdata->types[i].isdst = c; c = getc_unlocked (f); if (__glibc_unlikely ((size_t) c > chars)) /* Bogus index in data file. */ goto lose; - types[i].idx = c; - types[i].offset = (long int) decode (x); + tzdata->types[i].idx = c; + tzdata->types[i].offset = (long int) decode (x); } - if (__glibc_unlikely (__fread_unlocked (zone_names, 1, chars, f) != chars)) + if (__glibc_unlikely (__fread_unlocked (tzdata->zone_names, 1, chars, f) + != chars)) goto lose; - for (i = 0; i < num_leaps; ++i) + for (i = 0; i < tzdata->num_leaps; ++i) { unsigned char x[8]; if (__builtin_expect (__fread_unlocked (x, 1, trans_width, f) != trans_width, 0)) goto lose; if (sizeof (time_t) == 4 || trans_width == 4) - leaps[i].transition = (time_t) decode (x); + tzdata->leaps[i].transition = (time_t) decode (x); else - leaps[i].transition = (time_t) decode64 (x); + tzdata->leaps[i].transition = (time_t) decode64 (x); if (__glibc_unlikely (__fread_unlocked (x, 1, 4, f) != 4)) goto lose; - leaps[i].change = (long int) decode (x); + tz |
