chiark / gitweb /
remove unused includes
[elogind.git] / src / shared / time-dst.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Timezone file reading code from glibc 2.16.
7
8   Copyright (C) 1991-2012 Free Software Foundation, Inc.
9   Copyright 2012 Kay Sievers
10
11   systemd is free software; you can redistribute it and/or modify it
12   under the terms of the GNU Lesser General Public License as published by
13   the Free Software Foundation; either version 2.1 of the License, or
14   (at your option) any later version.
15
16   systemd is distributed in the hope that it will be useful, but
17   WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19   Lesser General Public License for more details.
20
21   You should have received a copy of the GNU Lesser General Public License
22   along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 ***/
24 #include <errno.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <time.h>
29 #include <endian.h>
30 #include <stdint.h>
31 #include <stdbool.h>
32 #include <sys/stat.h>
33
34 #include "time-dst.h"
35 #include "util.h"
36
37 /*
38  * If tzh_version is '2' or greater, the above is followed by a second instance
39  * of tzhead and a second instance of the data in which each coded transition
40  * time uses 8 rather than 4 chars, then a POSIX-TZ-environment-variable-style
41  * string for use in handling instants after the last transition time stored in
42  * the file * (with nothing between the newlines if there is no POSIX
43  * representation for such instants).
44  */
45 #define TZ_MAGIC                "TZif"
46 struct tzhead {
47         char tzh_magic[4];      /* TZ_MAGIC */
48         char tzh_version[1];    /* '\0' or '2' as of 2005 */
49         char tzh_reserved[15];  /* reserved--must be zero */
50         char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
51         char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
52         char tzh_leapcnt[4];    /* coded number of leap seconds */
53         char tzh_timecnt[4];    /* coded number of transition times */
54         char tzh_typecnt[4];    /* coded number of local time types */
55         char tzh_charcnt[4];    /* coded number of abbr. chars */
56 };
57
58 struct ttinfo {
59         long int offset;        /* Seconds east of GMT.  */
60         unsigned char isdst;    /* Used to set tm_isdst.  */
61         unsigned char idx;      /* Index into `zone_names'.  */
62         unsigned char isstd;    /* Transition times are in standard time.  */
63         unsigned char isgmt;    /* Transition times are in GMT.  */
64 };
65
66 struct leap {
67         time_t transition;      /* Time the transition takes effect.  */
68         long int change;        /* Seconds of correction to apply.  */
69 };
70
71 static inline int decode(const void *ptr) {
72         return be32toh(*(int *)ptr);
73 }
74
75 static inline int64_t decode64(const void *ptr) {
76         return be64toh(*(int64_t *)ptr);
77 }
78
79 int time_get_dst(time_t date, const char *tzfile,
80                  time_t *switch_cur, char **zone_cur, bool *dst_cur,
81                  time_t *switch_next, int *delta_next, char **zone_next, bool *dst_next) {
82         unsigned char *type_idxs = 0;
83         size_t num_types = 0;
84         struct ttinfo *types = NULL;
85         char *zone_names = NULL;
86         struct stat st;
87         size_t num_isstd, num_isgmt;
88         struct tzhead tzhead;
89         size_t chars;
90         size_t i;
91         size_t total_size;
92         size_t types_idx;
93         int trans_width = 4;
94         size_t tzspec_len;
95         size_t num_leaps;
96         size_t lo, hi;
97         size_t num_transitions = 0;
98         _cleanup_free_ time_t *transitions = NULL;
99         _cleanup_fclose_ FILE *f;
100
101         f = fopen(tzfile, "re");
102         if (f == NULL)
103                 return -errno;
104
105         if (fstat(fileno(f), &st) < 0)
106                 return -errno;
107
108 read_again:
109         if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
110             memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
111                 return -EINVAL;
112
113         num_transitions = (size_t)decode(tzhead.tzh_timecnt);
114         num_types = (size_t)decode(tzhead.tzh_typecnt);
115         chars = (size_t)decode(tzhead.tzh_charcnt);
116         num_leaps = (size_t)decode(tzhead.tzh_leapcnt);
117         num_isstd = (size_t)decode(tzhead.tzh_ttisstdcnt);
118         num_isgmt = (size_t)decode(tzhead.tzh_ttisgmtcnt);
119
120         /* For platforms with 64-bit time_t we use the new format if available.  */
121         if (sizeof(time_t) == 8 && trans_width == 4 && tzhead.tzh_version[0] != '\0') {
122                 size_t to_skip;
123
124                 /* We use the 8-byte format.  */
125                 trans_width = 8;
126
127                 /* Position the stream before the second header.  */
128                 to_skip = (num_transitions * (4 + 1)
129                            + num_types * 6
130                            + chars
131                            + num_leaps * 8 + num_isstd + num_isgmt);
132                 if (fseek(f, to_skip, SEEK_CUR) != 0)
133                         return -EINVAL;
134
135                 goto read_again;
136         }
137
138         if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
139                  return -EINVAL;
140
141         total_size = num_transitions * (sizeof(time_t) + 1);
142         total_size = ((total_size + __alignof__(struct ttinfo) - 1) & ~(__alignof__(struct ttinfo) - 1));
143         types_idx = total_size;
144         if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct ttinfo))
145                 return -EINVAL;
146
147         total_size += num_types * sizeof(struct ttinfo);
148         if (chars > SIZE_MAX - total_size)
149                 return -EINVAL;
150
151         total_size += chars;
152         if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
153                  return -EINVAL;
154
155         total_size = ((total_size + __alignof__(struct leap) - 1) & ~(__alignof__(struct leap) - 1));
156         if (num_leaps > (SIZE_MAX - total_size) / sizeof(struct leap))
157                 return -EINVAL;
158
159         total_size += num_leaps * sizeof(struct leap);
160         tzspec_len = 0;
161         if (sizeof(time_t) == 8 && trans_width == 8) {
162                 off_t rem = st.st_size - ftello(f);
163
164                 if (rem < 0 || (size_t) rem < (num_transitions * (8 + 1) + num_types * 6 + chars))
165                         return -EINVAL;
166                 tzspec_len = (size_t) rem - (num_transitions * (8 + 1) + num_types * 6 + chars);
167                 if (num_leaps > SIZE_MAX / 12 || tzspec_len < num_leaps * 12)
168                         return -EINVAL;
169                 tzspec_len -= num_leaps * 12;
170                 if (tzspec_len < num_isstd)
171                         return -EINVAL;
172                 tzspec_len -= num_isstd;
173                 if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
174                         return -EINVAL;
175                 tzspec_len -= num_isgmt + 1;
176                 if (SIZE_MAX - total_size < tzspec_len)
177                         return -EINVAL;
178         }
179
180         /* leave space for additional zone_names zero terminator */
181         transitions = malloc0(total_size + tzspec_len + 1);
182         if (transitions == NULL)
183                 return -EINVAL;
184
185         type_idxs = (unsigned char *)transitions + (num_transitions
186                                                     * sizeof(time_t));
187         types = (struct ttinfo *)((char *)transitions + types_idx);
188         zone_names = (char *)types + num_types * sizeof(struct ttinfo);
189
190         if (sizeof(time_t) == 4 || trans_width == 8) {
191                 if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
192                         return -EINVAL;
193         } else {
194                 if (fread(transitions, 4, num_transitions, f) != num_transitions ||
195                     fread(type_idxs, 1, num_transitions, f) != num_transitions)
196                         return -EINVAL;
197         }
198
199         /* Check for bogus indices in the data file, so we can hereafter
200            safely use type_idxs[T] as indices into `types' and never crash.  */
201         for (i = 0; i < num_transitions; ++i)
202                 if (type_idxs[i] >= num_types)
203                         return -EINVAL;
204
205         if (__BYTE_ORDER == __BIG_ENDIAN ? sizeof(time_t) == 8 && trans_width == 4
206                                          : sizeof(time_t) == 4 || trans_width == 4) {
207                 /* Decode the transition times, stored as 4-byte integers in
208                    network (big-endian) byte order.  We work from the end of
209                    the array so as not to clobber the next element to be
210                    processed when sizeof (time_t) > 4.  */
211                 i = num_transitions;
212                 while (i-- > 0)
213                         transitions[i] = decode((char *)transitions + i * 4);
214         } else if (__BYTE_ORDER != __BIG_ENDIAN && sizeof(time_t) == 8) {
215                 /* Decode the transition times, stored as 8-byte integers in
216                    network (big-endian) byte order.  */
217                 for (i = 0; i < num_transitions; ++i)
218                         transitions[i] = decode64((char *)transitions + i * 8);
219         }
220
221         for (i = 0; i < num_types; ++i) {
222                 unsigned char x[4];
223                 int c;
224
225                 if (fread(x, 1, sizeof(x), f) != sizeof(x))
226                         return -EINVAL;
227                 c = getc(f);
228                 if ((unsigned int)c > 1u)
229                         return -EINVAL;
230                 types[i].isdst = c;
231                 c = getc(f);
232                 if ((size_t) c > chars)
233                         /* Bogus index in data file.  */
234                         return -EINVAL;
235                 types[i].idx = c;
236                 types[i].offset = (long int)decode(x);
237         }
238
239         if (fread(zone_names, 1, chars, f) != chars)
240                 return -EINVAL;
241
242         zone_names[chars] = '\0';
243
244         for (i = 0; i < num_isstd; ++i) {
245                 int c = getc(f);
246                 if (c == EOF)
247                         return -EINVAL;
248                 types[i].isstd = c != 0;
249         }
250
251         while (i < num_types)
252                 types[i++].isstd = 0;
253
254         for (i = 0; i < num_isgmt; ++i) {
255                 int c = getc(f);
256                 if (c == EOF)
257                         return -EINVAL;
258                 types[i].isgmt = c != 0;
259         }
260
261         while (i < num_types)
262                 types[i++].isgmt = 0;
263
264         if (num_transitions == 0)
265                return -EINVAL;
266
267         if (date < transitions[0] || date >= transitions[num_transitions - 1])
268                return -EINVAL;
269
270         /* Find the first transition after TIMER, and
271            then pick the type of the transition before it.  */
272         lo = 0;
273         hi = num_transitions - 1;
274
275         /* Assume that DST is changing twice a year and guess initial
276            search spot from it.
277            Half of a gregorian year has on average 365.2425 * 86400 / 2
278            = 15778476 seconds.  */
279         i = (transitions[num_transitions - 1] - date) / 15778476;
280         if (i < num_transitions) {
281                 i = num_transitions - 1 - i;
282                 if (date < transitions[i]) {
283                         if (i < 10 || date >= transitions[i - 10]) {
284                                 /* Linear search.  */
285                                 while (date < transitions[i - 1])
286                                         i--;
287                                 goto found;
288                         }
289                         hi = i - 10;
290                 } else {
291                         if (i + 10 >= num_transitions || date < transitions[i + 10]) {
292                                 /* Linear search.  */
293                                 while (date >= transitions[i])
294                                         i++;
295                                 goto found;
296                         }
297                         lo = i + 10;
298                 }
299         }
300
301         /* Binary search. */
302         while (lo + 1 < hi) {
303                 i = (lo + hi) / 2;
304                 if (date < transitions[i])
305                         hi = i;
306                 else
307                         lo = i;
308         }
309         i = hi;
310
311 found:
312         if (switch_cur)
313                 *switch_cur = transitions[i-1];
314         if (zone_cur)
315                 *zone_cur = strdup(&zone_names[types[type_idxs[i - 1]].idx]);
316         if (dst_cur)
317                 *dst_cur = types[type_idxs[i-1]].isdst;
318
319         if (switch_next)
320                 *switch_next = transitions[i];
321         if (delta_next)
322                 *delta_next = (types[type_idxs[i]].offset - types[type_idxs[i-1]].offset) / 60;
323         if (zone_next)
324                 *zone_next = strdup(&zone_names[types[type_idxs[i]].idx]);
325         if (dst_next)
326                 *dst_next = types[type_idxs[i]].isdst;
327
328         return 0;
329 }