]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-dst.c
hashmap, set: remove unused functions
[thirdparty/systemd.git] / src / shared / time-dst.c
CommitLineData
b7f1542c
KS
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"
11f22867 41#include "util.h"
b7f1542c
KS
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"
52struct 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
64struct 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
72struct leap {
73 time_t transition; /* Time the transition takes effect. */
74 long int change; /* Seconds of correction to apply. */
75};
76
77static inline int decode(const void *ptr) {
78 return be32toh(*(int *)ptr);
79}
80
81static inline int64_t decode64(const void *ptr) {
82 return be64toh(*(int64_t *)ptr);
83}
84
85int time_get_dst(time_t date, const char *tzfile,
86 time_t *switch_cur, char **zone_cur, bool *dst_cur,
2311eb2f 87 time_t *switch_next, int *delta_next, char **zone_next, bool *dst_next) {
b7f1542c
KS
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;
b7f1542c
KS
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;
11f22867
ZJS
103 size_t num_transitions = 0;
104 _cleanup_free_ time_t *transitions = NULL;
105 _cleanup_fclose_ FILE *f;
b7f1542c
KS
106
107 f = fopen(tzfile, "re");
108 if (f == NULL)
109 return -errno;
110
11f22867
ZJS
111 if (fstat(fileno(f), &st) < 0)
112 return -errno;
b7f1542c
KS
113
114read_again:
115 if (fread((void *)&tzhead, sizeof(tzhead), 1, f) != 1 ||
116 memcmp(tzhead.tzh_magic, TZ_MAGIC, sizeof(tzhead.tzh_magic)) != 0)
11f22867 117 return -EINVAL;
b7f1542c
KS
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)
11f22867 139 return -EINVAL;
b7f1542c
KS
140
141 goto read_again;
142 }
143
144 if (num_transitions > ((SIZE_MAX - (__alignof__(struct ttinfo) - 1)) / (sizeof(time_t) + 1)))
11f22867 145 return -EINVAL;
b7f1542c
KS
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))
11f22867 151 return -EINVAL;
b7f1542c
KS
152
153 total_size += num_types * sizeof(struct ttinfo);
154 if (chars > SIZE_MAX - total_size)
11f22867 155 return -EINVAL;
b7f1542c
KS
156
157 total_size += chars;
158 if (__alignof__(struct leap) - 1 > SIZE_MAX - total_size)
11f22867 159 return -EINVAL;
b7f1542c
KS
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))
11f22867 163 return -EINVAL;
b7f1542c
KS
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))
11f22867 171 return -EINVAL;
b7f1542c
KS
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)
11f22867 174 return -EINVAL;
b7f1542c
KS
175 tzspec_len -= num_leaps * 12;
176 if (tzspec_len < num_isstd)
11f22867 177 return -EINVAL;
b7f1542c
KS
178 tzspec_len -= num_isstd;
179 if (tzspec_len == 0 || tzspec_len - 1 < num_isgmt)
11f22867 180 return -EINVAL;
b7f1542c
KS
181 tzspec_len -= num_isgmt + 1;
182 if (SIZE_MAX - total_size < tzspec_len)
11f22867 183 return -EINVAL;
b7f1542c
KS
184 }
185
955d98c9 186 transitions = malloc0(total_size + tzspec_len);
b7f1542c 187 if (transitions == NULL)
11f22867 188 return -EINVAL;
b7f1542c
KS
189
190 type_idxs = (unsigned char *)transitions + (num_transitions
191 * sizeof(time_t));
192 types = (struct ttinfo *)((char *)transitions + types_idx);
193 zone_names = (char *)types + num_types * sizeof(struct ttinfo);
194
195 if (sizeof(time_t) == 4 || trans_width == 8) {
196 if (fread(transitions, trans_width + 1, num_transitions, f) != num_transitions)
11f22867 197 return -EINVAL;
b7f1542c
KS
198 } else {
199 if (fread(transitions, 4, num_transitions, f) != num_transitions ||
200 fread(type_idxs, 1, num_transitions, f) != num_transitions)
11f22867 201 return -EINVAL;
b7f1542c
KS
202 }
203
204 /* Check for bogus indices in the data file, so we can hereafter
205 safely use type_idxs[T] as indices into `types' and never crash. */
206 for (i = 0; i < num_transitions; ++i)
207 if (type_idxs[i] >= num_types)
11f22867 208 return -EINVAL;
b7f1542c 209
4f4b92ba
LP
210 if (__BYTE_ORDER == __BIG_ENDIAN ? sizeof(time_t) == 8 && trans_width == 4
211 : sizeof(time_t) == 4 || trans_width == 4) {
b7f1542c
KS
212 /* Decode the transition times, stored as 4-byte integers in
213 network (big-endian) byte order. We work from the end of
214 the array so as not to clobber the next element to be
215 processed when sizeof (time_t) > 4. */
216 i = num_transitions;
217 while (i-- > 0)
218 transitions[i] = decode((char *)transitions + i * 4);
4f4b92ba 219 } else if (__BYTE_ORDER != __BIG_ENDIAN && sizeof(time_t) == 8) {
b7f1542c
KS
220 /* Decode the transition times, stored as 8-byte integers in
221 network (big-endian) byte order. */
222 for (i = 0; i < num_transitions; ++i)
223 transitions[i] = decode64((char *)transitions + i * 8);
224 }
225
226 for (i = 0; i < num_types; ++i) {
227 unsigned char x[4];
228 int c;
229
230 if (fread(x, 1, sizeof(x), f) != sizeof(x))
11f22867 231 return -EINVAL;
b7f1542c
KS
232 c = getc(f);
233 if ((unsigned int)c > 1u)
11f22867 234 return -EINVAL;
b7f1542c
KS
235 types[i].isdst = c;
236 c = getc(f);
237 if ((size_t) c > chars)
238 /* Bogus index in data file. */
11f22867 239 return -EINVAL;
b7f1542c
KS
240 types[i].idx = c;
241 types[i].offset = (long int)decode(x);
242 }
243
244 if (fread(zone_names, 1, chars, f) != chars)
11f22867 245 return -EINVAL;
b7f1542c
KS
246
247 for (i = 0; i < num_isstd; ++i) {
248 int c = getc(f);
249 if (c == EOF)
11f22867 250 return -EINVAL;
b7f1542c
KS
251 types[i].isstd = c != 0;
252 }
253
254 while (i < num_types)
255 types[i++].isstd = 0;
256
257 for (i = 0; i < num_isgmt; ++i) {
258 int c = getc(f);
259 if (c == EOF)
11f22867 260 return -EINVAL;
b7f1542c
KS
261 types[i].isgmt = c != 0;
262 }
263
264 while (i < num_types)
265 types[i++].isgmt = 0;
266
267 if (num_transitions == 0)
11f22867 268 return -EINVAL;
b7f1542c
KS
269
270 if (date < transitions[0] || date >= transitions[num_transitions - 1])
11f22867 271 return -EINVAL;
b7f1542c
KS
272
273 /* Find the first transition after TIMER, and
274 then pick the type of the transition before it. */
275 lo = 0;
276 hi = num_transitions - 1;
277
278 /* Assume that DST is changing twice a year and guess initial
279 search spot from it.
280 Half of a gregorian year has on average 365.2425 * 86400 / 2
281 = 15778476 seconds. */
282 i = (transitions[num_transitions - 1] - date) / 15778476;
283 if (i < num_transitions) {
284 i = num_transitions - 1 - i;
285 if (date < transitions[i]) {
286 if (i < 10 || date >= transitions[i - 10]) {
287 /* Linear search. */
288 while (date < transitions[i - 1])
289 i--;
290 goto found;
291 }
292 hi = i - 10;
293 } else {
294 if (i + 10 >= num_transitions || date < transitions[i + 10]) {
295 /* Linear search. */
296 while (date >= transitions[i])
297 i++;
298 goto found;
299 }
300 lo = i + 10;
301 }
302 }
303
304 /* Binary search. */
305 while (lo + 1 < hi) {
306 i = (lo + hi) / 2;
307 if (date < transitions[i])
308 hi = i;
309 else
310 lo = i;
311 }
312 i = hi;
313
314found:
315 if (switch_cur)
316 *switch_cur = transitions[i-1];
317 if (zone_cur)
318 *zone_cur = strdup(&zone_names[types[type_idxs[i - 1]].idx]);
319 if (dst_cur)
320 *dst_cur = types[type_idxs[i-1]].isdst;
2311eb2f 321
b7f1542c
KS
322 if (switch_next)
323 *switch_next = transitions[i];
2311eb2f
KS
324 if (delta_next)
325 *delta_next = (types[type_idxs[i]].offset - types[type_idxs[i-1]].offset) / 60;
b7f1542c
KS
326 if (zone_next)
327 *zone_next = strdup(&zone_names[types[type_idxs[i]].idx]);
328 if (dst_next)
329 *dst_next = types[type_idxs[i]].isdst;
330
b7f1542c 331 return 0;
b7f1542c 332}