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