diff options
Diffstat (limited to 'src/third_party/timelib-2022.02/parse_zoneinfo.c')
-rw-r--r-- | src/third_party/timelib-2022.02/parse_zoneinfo.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/third_party/timelib-2022.02/parse_zoneinfo.c b/src/third_party/timelib-2022.02/parse_zoneinfo.c new file mode 100644 index 00000000000..35348f69413 --- /dev/null +++ b/src/third_party/timelib-2022.02/parse_zoneinfo.c @@ -0,0 +1,354 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2017-2018 MongoDB, Inc. + * Copyright (c) 2015 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "timelib.h" +#include "timelib_private.h" + +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> + +#ifdef _WIN32 +# include "win_dirent.h" +#endif + +#ifndef MAXPATHLEN +# ifdef _WIN32 +# define MAXPATHLEN 2048 +# elif PATH_MAX +# define MAXPATHLEN PATH_MAX +# elif defined(MAX_PATH) +# define MAXPATHLEN MAX_PATH +# else +# define MAXPATHLEN 256 +# endif +#endif + +#if _MSC_VER +# define TIMELIB_DIR_SEPARATOR "\\" +#else +# define TIMELIB_DIR_SEPARATOR "/" +#endif + +#define TIMELIB_NAME_SEPARATOR "/" + +/* Filter out some non-tzdata files and the posix/right databases, if + * present. */ +static int index_filter(const struct dirent *ent) +{ + return strcmp(ent->d_name, ".") != 0 + && strcmp(ent->d_name, "..") != 0 + && strcmp(ent->d_name, "posix") != 0 + && strcmp(ent->d_name, "posixrules") != 0 + && strcmp(ent->d_name, "right") != 0 + && strcmp(ent->d_name, "localtime") != 0 + && strstr(ent->d_name, ".list") == NULL + && strstr(ent->d_name, ".tab") == NULL; +} + +static int timelib_alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcmp((*a)->d_name, (*b)->d_name); +} + + +static int sysdbcmp(const void *first, const void *second) +{ + const timelib_tzdb_index_entry *alpha = first, *beta = second; + + return timelib_strcasecmp(alpha->id, beta->id); +} + +/* Returns true if the passed-in stat structure describes a + * probably-valid timezone file. */ +static int is_valid_tzfile(const struct stat *st, int fd) +{ + if (fd) { + char buf[20]; + if (read(fd, buf, 20) != 20) { + return 0; + } + lseek(fd, SEEK_SET, 0); + if (memcmp(buf, "TZif", 4)) { + return 0; + } + } + return S_ISREG(st->st_mode) && st->st_size > 20; +} + +/* Return the contents of the tzfile if found, else NULL. On success, the + * length of the mapped data is placed in *length. */ +static char *read_tzfile(const char *directory, const char *timezone, size_t *length) +{ + char *fname; + size_t fname_len; + char *buffer; + struct stat st; + int fd; +#ifdef _WIN32 + int read_bytes; +#else + ssize_t read_bytes; +#endif + + if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) { + return NULL; + } + + fname_len = strlen(directory) + strlen(TIMELIB_DIR_SEPARATOR) + strlen(timezone) + 1; + fname = timelib_malloc(fname_len); + if (snprintf(fname, fname_len, "%s%s%s", directory, TIMELIB_DIR_SEPARATOR, timezone) < 0) { + timelib_free(fname); + return NULL; + } + + /* O_BINARY is required to properly read the file on windows */ +#ifdef _WIN32 + fd = open(fname, O_RDONLY | O_BINARY); +#else + fd = open(fname, O_RDONLY); +#endif + timelib_free(fname); + + if (fd == -1) { + return NULL; + } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) { + close(fd); + return NULL; + } + + *length = st.st_size; + + buffer = timelib_calloc(1, *length + 1); + read_bytes = read(fd, buffer, *length); + close(fd); + + if (read_bytes == -1 || read_bytes != st.st_size) { + return NULL; + } + + return buffer; +} + +static int timelib_scandir(const char *directory_name, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **)) +{ + DIR *dir; + struct dirent **entries = NULL; + int entries_size = 0; + int entries_count = 0; + char entry_container[sizeof(struct dirent) + MAXPATHLEN]; + struct dirent *entry = (struct dirent *)&entry_container; + + dir = opendir(directory_name); + if (!dir) { + return -1; + } + + while ((entry = readdir(dir)) != NULL) { + int new_entry_size = 0; + struct dirent *new_entry = NULL; + + /* Skip if the filter matches */ + if (filter && (*filter)(entry) == 0) { + continue; + } + + /* Make sure our entries array is large enough */ + if (entries_count == entries_size) { + struct dirent **new_entries; + + if (entries_size == 0) { + entries_size = 16; + } else { + entries_size *= 2; + } + + new_entries = (struct dirent **) timelib_realloc(entries, entries_size * sizeof(struct dirent *)); + if (!new_entries) { + return -1; + } + entries = new_entries; + } + + /* Add our new entry to the list */ + new_entry_size = sizeof(struct dirent) + (strlen(entry->d_name) + 1); + new_entry = (struct dirent *) timelib_malloc(new_entry_size); + + if (new_entry == NULL) { + goto cleanup; + } + + entries[entries_count++] = (struct dirent *) memcpy(new_entry, entry, new_entry_size); + } + + closedir(dir); + + *namelist = entries; + + if (compar) { + qsort(*namelist, entries_count, sizeof(struct dirent *), (int (*) (const void *, const void *)) compar); + } + + return entries_count; + +cleanup: + while (entries_count-- > 0) { + timelib_free(entries[entries_count]); + } + timelib_free(entries); + return -1; +} + +/* Create the zone identifier index by trawling the filesystem. */ +static int create_zone_index(const char *directory, timelib_tzdb *db) +{ + size_t dirstack_size, dirstack_top; + size_t index_size, index_next; + timelib_tzdb_index_entry *db_index; + char **dirstack; + size_t data_size = 0; + unsigned char *tmp_data = NULL; + + /* LIFO stack to hold directory entries to scan; each slot is a + * directory name relative to the zoneinfo prefix. */ + dirstack_size = 32; + dirstack = timelib_malloc(dirstack_size * sizeof(*dirstack)); + dirstack_top = 1; + dirstack[0] = timelib_strdup(""); + + /* Index array. */ + index_size = 64; + db_index = timelib_malloc(index_size * sizeof(timelib_tzdb_index_entry)); + index_next = 0; + + do { + struct dirent **ents; + char name[PATH_MAX], *top; + int count; + + /* Pop the top stack entry, and iterate through its contents. */ + top = dirstack[--dirstack_top]; + snprintf(name, sizeof(name), "%s/%s", directory, top); + + count = timelib_scandir(name, &ents, index_filter, timelib_alphasort); + if (count == -1) { + timelib_free(dirstack); + timelib_free(db_index); + return -errno; + } + + while (count > 0) { + struct stat st; + const char *leaf = ents[count - 1]->d_name; + + snprintf(name, sizeof(name), "%s%s%s%s%s", directory, TIMELIB_NAME_SEPARATOR, top, TIMELIB_NAME_SEPARATOR, leaf); + + if (strlen(name) && stat(name, &st) == 0) { + /* Name, relative to the zoneinfo prefix. */ + const char *root = top; + + if (root[0] == '/') { + root++; + } + + snprintf(name, sizeof(name), "%s%s%s", root, *root ? TIMELIB_NAME_SEPARATOR : "", leaf); + + if (S_ISDIR(st.st_mode)) { + if (dirstack_top == dirstack_size) { + dirstack_size *= 2; + dirstack = timelib_realloc(dirstack, dirstack_size * sizeof(*dirstack)); + } + dirstack[dirstack_top++] = timelib_strdup(name); + } else { + if (index_next == index_size) { + index_size *= 2; + db_index = timelib_realloc(db_index, index_size * sizeof(timelib_tzdb_index_entry)); + } + + db_index[index_next].id = timelib_strdup(name); + + { + size_t length; + char *tzfile_data = read_tzfile(directory, name, &length); + + if (tzfile_data) { + tmp_data = timelib_realloc(tmp_data, data_size + length); + memcpy(tmp_data + data_size, tzfile_data, length); + db_index[index_next].pos = data_size; + data_size += length; + timelib_free(tzfile_data); + + index_next++; + } else { + timelib_free(db_index[index_next].id); + } + } + } + } + + timelib_free(ents[--count]); + } + + if (count != -1) { + timelib_free(ents); + } + timelib_free(top); + } while (dirstack_top); + + qsort(db_index, index_next, sizeof(*db_index), sysdbcmp); + + db->index = db_index; + db->index_size = index_next; + db->data = tmp_data; + + timelib_free(dirstack); + + return 0; +} + +timelib_tzdb *timelib_zoneinfo(const char *directory) +{ + timelib_tzdb *tmp = timelib_malloc(sizeof(timelib_tzdb)); + + tmp->version = "0.system"; + tmp->data = NULL; + if (create_zone_index(directory, tmp) < 0) { + timelib_free(tmp); + return NULL; + } + return tmp; +} + +void timelib_zoneinfo_dtor(timelib_tzdb *tzdb) +{ + int i; + + for (i = 0; i < tzdb->index_size; i++) { + timelib_free(tzdb->index[i].id); + } + timelib_free((timelib_tzdb_index_entry*) tzdb->index); + timelib_free((char*) tzdb->data); + timelib_free(tzdb); +} |