Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/unix/native/libjava/TimeZone_md.c
41119 views
1
/*
2
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <strings.h>
29
#include <time.h>
30
#include <limits.h>
31
#include <errno.h>
32
#include <stddef.h>
33
#include <sys/stat.h>
34
#include <sys/types.h>
35
#include <string.h>
36
#include <dirent.h>
37
#include <unistd.h>
38
39
#include "jvm.h"
40
#include "TimeZone_md.h"
41
42
static char *isFileIdentical(char* buf, size_t size, char *pathname);
43
44
#define SKIP_SPACE(p) while (*p == ' ' || *p == '\t') p++;
45
46
#define RESTARTABLE(_cmd, _result) do { \
47
do { \
48
_result = _cmd; \
49
} while((_result == -1) && (errno == EINTR)); \
50
} while(0)
51
52
#define fileopen fopen
53
#define filegets fgets
54
#define fileclose fclose
55
56
#if defined(_ALLBSD_SOURCE)
57
#define stat64 stat
58
#define lstat64 lstat
59
#define fstat64 fstat
60
#endif
61
62
#if defined(__linux__) || defined(_ALLBSD_SOURCE)
63
static const char *ETC_TIMEZONE_FILE = "/etc/timezone";
64
static const char *ZONEINFO_DIR = "/usr/share/zoneinfo";
65
static const char *DEFAULT_ZONEINFO_FILE = "/etc/localtime";
66
#else
67
static const char *SYS_INIT_FILE = "/etc/default/init";
68
static const char *ZONEINFO_DIR = "/usr/share/lib/zoneinfo";
69
static const char *DEFAULT_ZONEINFO_FILE = "/usr/share/lib/zoneinfo/localtime";
70
#endif /* defined(__linux__) || defined(_ALLBSD_SOURCE) */
71
72
static const char popularZones[][4] = {"UTC", "GMT"};
73
74
#if defined(_AIX)
75
static const char *ETC_ENVIRONMENT_FILE = "/etc/environment";
76
#endif
77
78
#if defined(__linux__) || defined(MACOSX)
79
80
/*
81
* Returns a pointer to the zone ID portion of the given zoneinfo file
82
* name, or NULL if the given string doesn't contain "zoneinfo/".
83
*/
84
static char *
85
getZoneName(char *str)
86
{
87
static const char *zidir = "zoneinfo/";
88
89
char *pos = strstr((const char *)str, zidir);
90
if (pos == NULL) {
91
return NULL;
92
}
93
return pos + strlen(zidir);
94
}
95
96
/*
97
* Returns a path name created from the given 'dir' and 'name' under
98
* UNIX. This function allocates memory for the pathname calling
99
* malloc(). NULL is returned if malloc() fails.
100
*/
101
static char *
102
getPathName(const char *dir, const char *name) {
103
char *path;
104
105
path = (char *) malloc(strlen(dir) + strlen(name) + 2);
106
if (path == NULL) {
107
return NULL;
108
}
109
return strcat(strcat(strcpy(path, dir), "/"), name);
110
}
111
112
/*
113
* Scans the specified directory and its subdirectories to find a
114
* zoneinfo file which has the same content as /etc/localtime on Linux
115
* or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'.
116
* If file is symbolic link, then the contents it points to are in buf.
117
* Returns a zone ID if found, otherwise, NULL is returned.
118
*/
119
static char *
120
findZoneinfoFile(char *buf, size_t size, const char *dir)
121
{
122
DIR *dirp = NULL;
123
struct dirent *dp = NULL;
124
char *pathname = NULL;
125
char *tz = NULL;
126
int res;
127
128
if (strcmp(dir, ZONEINFO_DIR) == 0) {
129
/* fast path for 1st iteration */
130
for (unsigned int i = 0; i < sizeof (popularZones) / sizeof (popularZones[0]); i++) {
131
pathname = getPathName(dir, popularZones[i]);
132
if (pathname == NULL) {
133
continue;
134
}
135
tz = isFileIdentical(buf, size, pathname);
136
free((void *) pathname);
137
pathname = NULL;
138
if (tz != NULL) {
139
return tz;
140
}
141
}
142
}
143
144
dirp = opendir(dir);
145
if (dirp == NULL) {
146
return NULL;
147
}
148
149
while ((dp = readdir(dirp)) != NULL) {
150
/*
151
* Skip '.' and '..' (and possibly other .* files)
152
*/
153
if (dp->d_name[0] == '.') {
154
continue;
155
}
156
157
/*
158
* Skip "ROC", "posixrules", and "localtime".
159
*/
160
if ((strcmp(dp->d_name, "ROC") == 0)
161
|| (strcmp(dp->d_name, "posixrules") == 0)
162
|| (strcmp(dp->d_name, "localtime") == 0)) {
163
continue;
164
}
165
166
pathname = getPathName(dir, dp->d_name);
167
if (pathname == NULL) {
168
break;
169
}
170
171
tz = isFileIdentical(buf, size, pathname);
172
free((void *) pathname);
173
pathname = NULL;
174
if (tz != NULL) {
175
break;
176
}
177
}
178
179
if (dirp != NULL) {
180
(void) closedir(dirp);
181
}
182
return tz;
183
}
184
185
/*
186
* Checks if the file pointed to by pathname matches
187
* the data contents in buf.
188
* Returns a representation of the timezone file name
189
* if file match is found, otherwise NULL.
190
*/
191
static char *
192
isFileIdentical(char *buf, size_t size, char *pathname)
193
{
194
char *possibleMatch = NULL;
195
struct stat64 statbuf;
196
char *dbuf = NULL;
197
int fd = -1;
198
int res;
199
200
RESTARTABLE(stat64(pathname, &statbuf), res);
201
if (res == -1) {
202
return NULL;
203
}
204
205
if (S_ISDIR(statbuf.st_mode)) {
206
possibleMatch = findZoneinfoFile(buf, size, pathname);
207
} else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) {
208
dbuf = (char *) malloc(size);
209
if (dbuf == NULL) {
210
return NULL;
211
}
212
RESTARTABLE(open(pathname, O_RDONLY), fd);
213
if (fd == -1) {
214
goto freedata;
215
}
216
RESTARTABLE(read(fd, dbuf, size), res);
217
if (res != (ssize_t) size) {
218
goto freedata;
219
}
220
if (memcmp(buf, dbuf, size) == 0) {
221
possibleMatch = getZoneName(pathname);
222
if (possibleMatch != NULL) {
223
possibleMatch = strdup(possibleMatch);
224
}
225
}
226
freedata:
227
free((void *) dbuf);
228
(void) close(fd);
229
}
230
return possibleMatch;
231
}
232
233
/*
234
* Performs Linux specific mapping and returns a zone ID
235
* if found. Otherwise, NULL is returned.
236
*/
237
static char *
238
getPlatformTimeZoneID()
239
{
240
struct stat64 statbuf;
241
char *tz = NULL;
242
FILE *fp;
243
int fd;
244
char *buf;
245
size_t size;
246
int res;
247
248
#if defined(__linux__)
249
/*
250
* Try reading the /etc/timezone file for Debian distros. There's
251
* no spec of the file format available. This parsing assumes that
252
* there's one line of an Olson tzid followed by a '\n', no
253
* leading or trailing spaces, no comments.
254
*/
255
if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) {
256
char line[256];
257
258
if (fgets(line, sizeof(line), fp) != NULL) {
259
char *p = strchr(line, '\n');
260
if (p != NULL) {
261
*p = '\0';
262
}
263
if (strlen(line) > 0) {
264
tz = strdup(line);
265
}
266
}
267
(void) fclose(fp);
268
if (tz != NULL) {
269
return tz;
270
}
271
}
272
#endif /* defined(__linux__) */
273
274
/*
275
* Next, try /etc/localtime to find the zone ID.
276
*/
277
RESTARTABLE(lstat64(DEFAULT_ZONEINFO_FILE, &statbuf), res);
278
if (res == -1) {
279
return NULL;
280
}
281
282
/*
283
* If it's a symlink, get the link name and its zone ID part. (The
284
* older versions of timeconfig created a symlink as described in
285
* the Red Hat man page. It was changed in 1999 to create a copy
286
* of a zoneinfo file. It's no longer possible to get the zone ID
287
* from /etc/localtime.)
288
*/
289
if (S_ISLNK(statbuf.st_mode)) {
290
char linkbuf[PATH_MAX+1];
291
int len;
292
293
if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) {
294
jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n",
295
DEFAULT_ZONEINFO_FILE);
296
return NULL;
297
}
298
linkbuf[len] = '\0';
299
tz = getZoneName(linkbuf);
300
if (tz != NULL) {
301
tz = strdup(tz);
302
return tz;
303
}
304
}
305
306
/*
307
* If it's a regular file, we need to find out the same zoneinfo file
308
* that has been copied as /etc/localtime.
309
* If initial symbolic link resolution failed, we should treat target
310
* file as a regular file.
311
*/
312
RESTARTABLE(open(DEFAULT_ZONEINFO_FILE, O_RDONLY), fd);
313
if (fd == -1) {
314
return NULL;
315
}
316
317
RESTARTABLE(fstat64(fd, &statbuf), res);
318
if (res == -1) {
319
(void) close(fd);
320
return NULL;
321
}
322
size = (size_t) statbuf.st_size;
323
buf = (char *) malloc(size);
324
if (buf == NULL) {
325
(void) close(fd);
326
return NULL;
327
}
328
329
RESTARTABLE(read(fd, buf, size), res);
330
if (res != (ssize_t) size) {
331
(void) close(fd);
332
free((void *) buf);
333
return NULL;
334
}
335
(void) close(fd);
336
337
tz = findZoneinfoFile(buf, size, ZONEINFO_DIR);
338
free((void *) buf);
339
return tz;
340
}
341
342
#elif defined(_AIX)
343
344
static char *
345
getPlatformTimeZoneID()
346
{
347
FILE *fp;
348
char *tz = NULL;
349
char *tz_key = "TZ=";
350
char line[256];
351
size_t tz_key_len = strlen(tz_key);
352
353
if ((fp = fopen(ETC_ENVIRONMENT_FILE, "r")) != NULL) {
354
while (fgets(line, sizeof(line), fp) != NULL) {
355
char *p = strchr(line, '\n');
356
if (p != NULL) {
357
*p = '\0';
358
}
359
if (0 == strncmp(line, tz_key, tz_key_len)) {
360
tz = strdup(line + tz_key_len);
361
break;
362
}
363
}
364
(void) fclose(fp);
365
}
366
367
return tz;
368
}
369
370
static char *
371
mapPlatformToJavaTimezone(const char *java_home_dir, const char *tz) {
372
FILE *tzmapf;
373
char mapfilename[PATH_MAX + 1];
374
char line[256];
375
int linecount = 0;
376
char *tz_buf = NULL;
377
char *temp_tz = NULL;
378
char *javatz = NULL;
379
size_t tz_len = 0;
380
381
/* On AIX, the TZ environment variable may end with a comma
382
* followed by modifier fields. These are ignored here. */
383
temp_tz = strchr(tz, ',');
384
tz_len = (temp_tz == NULL) ? strlen(tz) : temp_tz - tz;
385
tz_buf = (char *)malloc(tz_len + 1);
386
memcpy(tz_buf, tz, tz_len);
387
tz_buf[tz_len] = 0;
388
389
/* Open tzmappings file, with buffer overrun check */
390
if ((strlen(java_home_dir) + 15) > PATH_MAX) {
391
jio_fprintf(stderr, "Path %s/lib/tzmappings exceeds maximum path length\n", java_home_dir);
392
goto tzerr;
393
}
394
strcpy(mapfilename, java_home_dir);
395
strcat(mapfilename, "/lib/tzmappings");
396
if ((tzmapf = fopen(mapfilename, "r")) == NULL) {
397
jio_fprintf(stderr, "can't open %s\n", mapfilename);
398
goto tzerr;
399
}
400
401
while (fgets(line, sizeof(line), tzmapf) != NULL) {
402
char *p = line;
403
char *sol = line;
404
char *java;
405
int result;
406
407
linecount++;
408
/*
409
* Skip comments and blank lines
410
*/
411
if (*p == '#' || *p == '\n') {
412
continue;
413
}
414
415
/*
416
* Get the first field, platform zone ID
417
*/
418
while (*p != '\0' && *p != '\t') {
419
p++;
420
}
421
if (*p == '\0') {
422
/* mapping table is broken! */
423
jio_fprintf(stderr, "tzmappings: Illegal format at near line %d.\n", linecount);
424
break;
425
}
426
427
*p++ = '\0';
428
if ((result = strncmp(tz_buf, sol, tz_len)) == 0) {
429
/*
430
* If this is the current platform zone ID,
431
* take the Java time zone ID (2nd field).
432
*/
433
java = p;
434
while (*p != '\0' && *p != '\n') {
435
p++;
436
}
437
438
if (*p == '\0') {
439
/* mapping table is broken! */
440
jio_fprintf(stderr, "tzmappings: Illegal format at line %d.\n", linecount);
441
break;
442
}
443
444
*p = '\0';
445
javatz = strdup(java);
446
break;
447
} else if (result < 0) {
448
break;
449
}
450
}
451
(void) fclose(tzmapf);
452
453
tzerr:
454
if (tz_buf != NULL ) {
455
free((void *) tz_buf);
456
}
457
458
if (javatz == NULL) {
459
return getGMTOffsetID();
460
}
461
462
return javatz;
463
}
464
465
#endif /* defined(_AIX) */
466
467
/*
468
* findJavaTZ_md() maps platform time zone ID to Java time zone ID
469
* using <java_home>/lib/tzmappings. If the TZ value is not found, it
470
* trys some libc implementation dependent mappings. If it still
471
* can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
472
* form.
473
*/
474
/*ARGSUSED1*/
475
char *
476
findJavaTZ_md(const char *java_home_dir)
477
{
478
char *tz;
479
char *javatz = NULL;
480
char *freetz = NULL;
481
482
tz = getenv("TZ");
483
484
if (tz == NULL || *tz == '\0') {
485
tz = getPlatformTimeZoneID();
486
freetz = tz;
487
}
488
489
if (tz != NULL) {
490
/* Ignore preceding ':' */
491
if (*tz == ':') {
492
tz++;
493
}
494
#if defined(__linux__)
495
/* Ignore "posix/" prefix on Linux. */
496
if (strncmp(tz, "posix/", 6) == 0) {
497
tz += 6;
498
}
499
#endif
500
501
#if defined(_AIX)
502
/* On AIX do the platform to Java mapping. */
503
javatz = mapPlatformToJavaTimezone(java_home_dir, tz);
504
if (freetz != NULL) {
505
free((void *) freetz);
506
}
507
#else
508
if (freetz == NULL) {
509
/* strdup if we are still working on getenv result. */
510
javatz = strdup(tz);
511
} else if (freetz != tz) {
512
/* strdup and free the old buffer, if we moved the pointer. */
513
javatz = strdup(tz);
514
free((void *) freetz);
515
} else {
516
/* we are good if we already work on a freshly allocated buffer. */
517
javatz = tz;
518
}
519
#endif
520
}
521
522
return javatz;
523
}
524
525
/**
526
* Returns a GMT-offset-based zone ID. (e.g., "GMT-08:00")
527
*/
528
529
#if defined(MACOSX)
530
531
char *
532
getGMTOffsetID()
533
{
534
time_t offset;
535
char sign, buf[32];
536
struct tm local_tm;
537
time_t clock;
538
539
clock = time(NULL);
540
if (localtime_r(&clock, &local_tm) == NULL) {
541
return strdup("GMT");
542
}
543
offset = (time_t)local_tm.tm_gmtoff;
544
if (offset == 0) {
545
return strdup("GMT");
546
}
547
if (offset > 0) {
548
sign = '+';
549
} else {
550
offset = -offset;
551
sign = '-';
552
}
553
sprintf(buf, (const char *)"GMT%c%02d:%02d",
554
sign, (int)(offset/3600), (int)((offset%3600)/60));
555
return strdup(buf);
556
}
557
558
#else
559
560
char *
561
getGMTOffsetID()
562
{
563
time_t offset;
564
char sign, buf[32];
565
offset = timezone;
566
567
if (offset == 0) {
568
return strdup("GMT");
569
}
570
571
/* Note that the time offset direction is opposite. */
572
if (offset > 0) {
573
sign = '-';
574
} else {
575
offset = -offset;
576
sign = '+';
577
}
578
sprintf(buf, (const char *)"GMT%c%02d:%02d",
579
sign, (int)(offset/3600), (int)((offset%3600)/60));
580
return strdup(buf);
581
}
582
#endif /* MACOSX */
583
584