• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *   http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <stdint.h>
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/mman.h>
21 #include "libc.h"
22 #include "lock.h"
23 #include "time_impl.h"
24 #ifdef OHOS_ENABLE_PARAMETER
25 #include "sys_param.h"
26 #define SYSPARAM_LENGTH 40
27 #endif
28 
29 #define __TZ_VERSION__ '2'
30 
31 long  __timezone = 0;
32 int   __daylight = 0;
33 char *__tzname[2] = { 0, 0 };
34 
35 weak_alias(__timezone, timezone);
36 weak_alias(__daylight, daylight);
37 weak_alias(__tzname, tzname);
38 
39 static char std_name[TZNAME_MAX+1];
40 static char dst_name[TZNAME_MAX+1];
41 const char __utc[] = "UTC";
42 const char __gmt[] = "GMT";
43 
44 static int dst_off;
45 static int r0[5], r1[5];
46 
47 static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end;
48 static size_t map_size;
49 
50 static char old_tz_buf[32];
51 static char *old_tz = old_tz_buf;
52 static size_t old_tz_size = sizeof old_tz_buf;
53 
54 static volatile int lock[1];
55 
getint(const char ** p)56 static int getint(const char **p)
57 {
58     unsigned x;
59     for (x=0; **p-'0'<10U; (*p)++) {
60         x = **p-'0' + 10*x;
61     }
62     return x;
63 }
64 
getoff(const char ** p)65 static int getoff(const char **p)
66 {
67     int neg = 0;
68     if (**p == '-') {
69         ++*p;
70         neg = 1;
71     } else if (**p == '+') {
72         ++*p;
73     }
74     int off = 3600*getint(p);
75     if (**p == ':') {
76         ++*p;
77         off += 60*getint(p);
78         if (**p == ':') {
79             ++*p;
80             off += getint(p);
81         }
82     }
83     return neg ? -off : off;
84 }
85 
getrule(const char ** p,int rule[5])86 static void getrule(const char **p, int rule[5])
87 {
88     int r = rule[0] = **p;
89 
90     if (r!='M') {
91         if (r=='J') {
92             ++*p;
93         } else {
94             rule[0] = 0;
95         }
96         rule[1] = getint(p);
97     } else {
98         ++*p;
99         rule[1] = getint(p);
100         ++*p;
101         rule[2] = getint(p);
102         ++*p;
103         rule[3] = getint(p);
104     }
105 
106     if (**p=='/') {
107         ++*p;
108         rule[4] = getoff(p);
109     } else {
110         rule[4] = 7200;
111     }
112 }
113 
getname(char * d,const char ** p)114 static void getname(char *d, const char **p)
115 {
116     int i;
117     if (**p == '<') {
118         ++*p;
119         for (i=0; (*p)[i]!='>' && i<TZNAME_MAX; i++)
120             d[i] = (*p)[i];
121         ++*p;
122     } else {
123         for (i=0; ((*p)[i]|32)-'a'<26U && i<TZNAME_MAX; i++)
124             d[i] = (*p)[i];
125     }
126     *p += i;
127     d[i] = 0;
128 }
129 
130 #define VEC(...) ((const unsigned char[]) {__VA_ARGS__})
131 
zi_read32(const unsigned char * z)132 static uint32_t zi_read32(const unsigned char *z)
133 {
134     return (unsigned)(z[0]<<24) | (z[1]<<16) | (z[2]<<8) | z[3];
135 }
136 
zi_dotprod(const unsigned char * z,const unsigned char * v,size_t n)137 static size_t zi_dotprod(const unsigned char *z, const unsigned char *v, size_t n)
138 {
139     size_t y;
140     uint32_t x;
141     for (y=0; n; n--, z+=4, v++) {
142         x = zi_read32(z);
143         y += x * *v;
144     }
145     return y;
146 }
147 
do_tzset()148 static void do_tzset()
149 {
150     char buf[NAME_MAX+25], *pathname = buf+24;
151     const char *try, *s, *p;
152     const unsigned char *map = 0;
153     size_t i;
154     static const char search[] =
155         "/etc/zoneinfo/\0/usr/share/zoneinfo/\0/share/zoneinfo/\0";
156 
157     s = getenv("TZ");
158     if (!s) {
159 #ifdef OHOS_ENABLE_PARAMETER
160         static CachedHandle tz_param_handle = NULL;
161         if (tz_param_handle == NULL) {
162             tz_param_handle = CachedParameterCreate("persist.time.timezone", "/etc/localtime");
163         }
164         const char *tz_param_value = CachedParameterGet(tz_param_handle);
165         if (tz_param_value != NULL){
166             s = tz_param_value;
167         } else {
168             s = "/etc/localtime";
169         }
170 #else
171         s = "/etc/localtime";
172 #endif
173     }
174     if (!*s) {
175         s = __utc;
176     }
177 
178     if (old_tz && !strcmp(s, old_tz)) {
179         return;
180     }
181 
182     for (i=0; i<5; i++) {
183         r0[i] = r1[i] = 0;
184     }
185 
186     if (zi) {
187         __munmap((void *)zi, map_size);
188     }
189 
190     /* Cache the old value of TZ to check if it has changed. Avoid
191      * free so as not to pull it into static programs. Growth
192      * strategy makes it so free would have minimal benefit anyway. */
193     i = strlen(s);
194     if (i > PATH_MAX+1) s = __utc, i = 3;
195     if (i >= old_tz_size) {
196         old_tz_size *= 2;
197         if (i >= old_tz_size) old_tz_size = i+1;
198         if (old_tz_size > PATH_MAX+2) old_tz_size = PATH_MAX+2;
199         old_tz = malloc(old_tz_size);
200     }
201     if (old_tz) memcpy(old_tz, s, i+1);
202 
203     /* Non-suid can use an absolute tzfile pathname or a relative
204      * pathame beginning with "."; in secure mode, only the
205      * standard path will be searched. */
206     int flag = 1;
207     if (*s == ':' || ((p=strchr(s, '/')) && !memchr(s, ',', p-s))) {
208         if (*s == ':') s++;
209         if (*s == '/' || *s == '.') {
210             /* The path is invalid, use the default value. */
211             flag = 0;
212             if (!libc.secure || !strcmp(s, "/etc/localtime")) {
213                 map = __map_file(s, &map_size);
214             }
215         }
216     }
217 
218     if (flag) {
219         /* Adapt to time zone names, such as Asia/Shanghai or Shanghai*/
220         size_t l = strlen(s);
221         if (l <= NAME_MAX && !strchr(s, '.')) {
222             memcpy(pathname, s, l+1);
223             pathname[l] = 0;
224             for (try=search; !map && *try; try+=l+1) {
225                 l = strlen(try);
226                 memcpy(pathname-l, try, l);
227                 map = __map_file(pathname-l, &map_size);
228             }
229         }
230     }
231 
232     if (map && (map_size < 44 || memcmp(map, "TZif", 4))) {
233         __munmap((void *)map, map_size);
234         map = 0;
235         s = __utc;
236     }
237 
238     zi = map;
239     if (map) {
240         int scale = 2;
241 
242         /*
243          * map[0]-map[3]: magic, it is TZif
244          * map[4]:        version, '\0' or '2' or '3' as of 2013
245          * map[5]-map[19]: reserved; must be zero
246          * map[20]-map[23]: The number of UT/local indicators stored in the file.
247          * map[24]-map[27]: The number of standard/wall indicators stored in the file.
248          * map[24]-map[31]: The number of leap seconds for which data entries are stored in the file.
249          * map[32]-map[35]: The number of transition times for which data entries are stored in the file.
250          * map[36]-map[39]: The number of local time types for which data entries are
251          *                  stored in the file (must not be zero).
252          * map[40]-map[43]: The number of bytes of time zone abbreviation strings stored in the file.
253 
254          * If map[4] is '2' or greater, the above is followed by a second instance
255          * of tzhead and a second instance of the data in which each coded transition
256          * time uses 8 rather than 4 chars,
257          * then a POSIX-TZ-environment-variable-style string for use in handling
258          * instants after the last transition time stored in the file
259          * (with nothing between the newlines if there is no POSIX representation for
260          * such instants).
261 
262          * If map[4] is '3' or greater, the above is extended as follows.
263          * First, the POSIX TZ string's hour offset may range from -167
264          * through 167 as compared to the POSIX-required 0 through 24.
265          * Second, its DST start time may be January 1 at 00:00 and its stop
266          * time December 31 at 24:00 plus the difference between DST and
267          * standard time, indicating DST all year. */
268         if (sizeof(time_t) > 4 && map[4] >= __TZ_VERSION__) {
269             size_t skip = zi_dotprod(zi+20, VEC(1, 1, 8, 5, 6, 1), 6);
270             trans = zi+skip+44+44;
271             scale++;
272         } else {
273             trans = zi+44;
274         }
275         index = trans + (zi_read32(trans-12) << scale);
276         types = index + zi_read32(trans-12);
277         abbrevs = types + 6*zi_read32(trans-8);
278         abbrevs_end = abbrevs + zi_read32(trans-4);
279         if (zi[map_size-1] == '\n') {
280             for (s = (const char *)zi+map_size-2; *s!='\n'; s--);
281             s++;
282         } else {
283             const unsigned char *p;
284             __tzname[0] = __tzname[1] = 0;
285             __daylight = __timezone = dst_off = 0;
286             for (p=types; p<abbrevs; p+=6) {
287                 if (!p[4] && !__tzname[0]) {
288                     __tzname[0] = (char *)abbrevs + p[5];
289                     __timezone = -zi_read32(p);
290                 }
291                 if (p[4] && !__tzname[1]) {
292                     __tzname[1] = (char *)abbrevs + p[5];
293                     dst_off = -zi_read32(p);
294                     __daylight = 1;
295                 }
296             }
297             if (!__tzname[0]) __tzname[0] = __tzname[1];
298             if (!__tzname[0]) __tzname[0] = (char *)__utc;
299             if (!__daylight) {
300                 __tzname[1] = __tzname[0];
301                 dst_off = __timezone;
302             }
303             return;
304         }
305     }
306 
307     if (!s) s = __utc;
308     getname(std_name, &s);
309     __tzname[0] = std_name;
310     __timezone = getoff(&s);
311     getname(dst_name, &s);
312     __tzname[1] = dst_name;
313     if (dst_name[0]) {
314         __daylight = 1;
315         if (*s == '+' || *s=='-' || *s-'0'<10U)
316             dst_off = getoff(&s);
317         else
318             dst_off = __timezone - 3600;
319     } else {
320         __daylight = 0;
321         dst_off = __timezone;
322     }
323 
324     if (*s == ',') s++, getrule(&s, r0);
325     if (*s == ',') s++, getrule(&s, r1);
326 }
327 
328 /* Search zoneinfo rules to find the one that applies to the given time,
329  * and determine alternate opposite-DST-status rule that may be needed. */
330 
scan_trans(long long t,int local,size_t * alt)331 static size_t scan_trans(long long t, int local, size_t *alt)
332 {
333     int scale = 3 - (trans == zi+44);
334     uint64_t x;
335     int off = 0;
336 
337     size_t a = 0, n = (index-trans)>>scale, m;
338 
339     if (!n) {
340         if (alt) *alt = 0;
341         return 0;
342     }
343 
344     /* Binary search for 'most-recent rule before t'. */
345     while (n > 1) {
346         m = a + n/2;
347         x = zi_read32(trans + (m<<scale));
348         if (scale == 3) x = (x<<32) | zi_read32(trans + (m<<scale) + 4);
349         else x = (int32_t)x;
350         if (local) off = (int32_t)zi_read32(types + 6 * index[m-1]);
351         if (t - off < (int64_t)x) {
352             n /= 2;
353         } else {
354             a = m;
355             n -= n/2;
356         }
357     }
358 
359     /* First and last entry are special. First means to use lowest-index
360      * non-DST type. Last means to apply POSIX-style rule if available. */
361     n = (index-trans)>>scale;
362     if (a == n-1) return -1;
363     if (a == 0) {
364         x = zi_read32(trans + (a<<scale));
365         if (scale == 3) x = (x<<32) | zi_read32(trans + (a<<scale) + 4);
366         else x = (int32_t)x;
367         if (local) off = (int32_t)zi_read32(types + 6 * index[a-1]);
368         if (t - off < (int64_t)x) {
369             for (a=0; a<(abbrevs-types)/6; a++) {
370                 if (types[6*a+4] != types[4]) break;
371             }
372             if (a == (abbrevs-types)/6) a = 0;
373             if (types[6*a+4]) {
374                 *alt = a;
375                 return 0;
376             } else {
377                 *alt = 0;
378                 return a;
379             }
380         }
381     }
382 
383     /* Try to find a neighboring opposite-DST-status rule. */
384     if (alt) {
385         if (a && types[6*index[a-1]+4] != types[6*index[a]+4])
386             *alt = index[a-1];
387         else if (a+1<n && types[6*index[a+1]+4] != types[6*index[a]+4])
388             *alt = index[a+1];
389         else
390             *alt = index[a];
391     }
392 
393     return index[a];
394 }
395 
days_in_month(int m,int is_leap)396 static int days_in_month(int m, int is_leap)
397 {
398     if (m==2) return 28+is_leap;
399     else return 30+((0xad5>>(m-1))&1);
400 }
401 
402 /* Convert a POSIX DST rule plus year to seconds since epoch. */
403 
rule_to_secs(const int * rule,int year)404 static long long rule_to_secs(const int *rule, int year)
405 {
406     int is_leap;
407     long long t = __year_to_secs(year, &is_leap);
408     int x, m, n, d;
409     if (rule[0]!='M') {
410         x = rule[1];
411         if (rule[0]=='J' && (x < 60 || !is_leap)) x--;
412         t += 86400 * x;
413     } else {
414         m = rule[1];
415         n = rule[2];
416         d = rule[3];
417         t += __month_to_secs(m-1, is_leap);
418         int wday = (int)((t + 4*86400) % (7*86400)) / 86400;
419         int days = d - wday;
420         if (days < 0) days += 7;
421         if (n == 5 && days+28 >= days_in_month(m, is_leap)) n = 4;
422         t += 86400 * (days + 7*(n-1));
423     }
424     t += rule[4];
425     return t;
426 }
427 
428 /* Determine the time zone in effect for a given time in seconds since the
429  * epoch. It can be given in local or universal time. The results will
430  * indicate whether DST is in effect at the queried time, and will give both
431  * the GMT offset for the active zone/DST rule and the opposite DST. This
432  * enables a caller to efficiently adjust for the case where an explicit
433  * DST specification mismatches what would be in effect at the time. */
434 
__secs_to_zone(long long t,int local,int * isdst,long * offset,long * oppoff,const char ** zonename)435 void __secs_to_zone(long long t, int local, int *isdst, long *offset, long *oppoff, const char **zonename)
436 {
437     LOCK(lock);
438 
439     do_tzset();
440 
441     if (zi) {
442         size_t alt, i = scan_trans(t, local, &alt);
443         if (i != -1) {
444             *isdst = types[6*i+4];
445             *offset = (int32_t)zi_read32(types+6*i);
446             *zonename = (const char *)abbrevs + types[6*i+5];
447             if (oppoff) *oppoff = (int32_t)zi_read32(types+6*alt);
448             UNLOCK(lock);
449             return;
450         }
451     }
452 
453     if (!__daylight) goto std;
454 
455     long long y = t / 31556952 + 70;
456     while (__year_to_secs(y, 0) > t) y--;
457     while (__year_to_secs(y+1, 0) < t) y++;
458 
459     long long t0 = rule_to_secs(r0, y);
460     long long t1 = rule_to_secs(r1, y);
461 
462     if (!local) {
463         t0 += __timezone;
464         t1 += dst_off;
465     }
466     if (t0 < t1) {
467         if (t >= t0 && t < t1) goto dst;
468         goto std;
469     } else {
470         if (t >= t1 && t < t0) goto std;
471         goto dst;
472     }
473 std:
474     *isdst = 0;
475     *offset = -__timezone;
476     if (oppoff) {
477         *oppoff = -dst_off;
478     }
479     *zonename = __tzname[0];
480     UNLOCK(lock);
481     return;
482 dst:
483     *isdst = 1;
484     *offset = -dst_off;
485     if (oppoff) {
486         *oppoff = -__timezone;
487     }
488     *zonename = __tzname[1];
489     UNLOCK(lock);
490 }
491 
__tzset()492 static void __tzset()
493 {
494     LOCK(lock);
495     do_tzset();
496     UNLOCK(lock);
497 }
498 
499 weak_alias(__tzset, tzset);
500 
__tm_to_tzname(const struct tm * tm)501 const char *__tm_to_tzname(const struct tm *tm)
502 {
503     const void *p = tm->__tm_zone;
504     LOCK(lock);
505     do_tzset();
506     if (p != __utc && p != __tzname[0] && p != __tzname[1] &&
507         (!zi || (uintptr_t)p-(uintptr_t)abbrevs >= abbrevs_end - abbrevs)) {
508         p = "";
509     }
510     UNLOCK(lock);
511     return p;
512 }
513