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