diff options
Diffstat (limited to 'src/timezone/pgtz.c')
-rw-r--r-- | src/timezone/pgtz.c | 58 |
1 files changed, 44 insertions, 14 deletions
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c index 4fa3d0da89..846f8898ff 100644 --- a/src/timezone/pgtz.c +++ b/src/timezone/pgtz.c @@ -3,7 +3,7 @@ * pgtz.c * Timezone Library Integration Functions * - * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group * * IDENTIFICATION * src/timezone/pgtz.c @@ -17,6 +17,7 @@ #include <sys/stat.h> #include <time.h> +#include "datatype/timestamp.h" #include "miscadmin.h" #include "pgtz.h" #include "storage/fd.h" @@ -79,12 +80,38 @@ pg_open_tzfile(const char *name, char *canonname) int fullnamelen; int orignamelen; + /* Initialize fullname with base name of tzdata directory */ + strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); + orignamelen = fullnamelen = strlen(fullname); + + if (fullnamelen + 1 + strlen(name) >= MAXPGPATH) + return -1; /* not gonna fit */ + + /* + * If the caller doesn't need the canonical spelling, first just try to + * open the name as-is. This can be expected to succeed if the given name + * is already case-correct, or if the filesystem is case-insensitive; and + * we don't need to distinguish those situations if we aren't tasked with + * reporting the canonical spelling. + */ + if (canonname == NULL) + { + int result; + + fullname[fullnamelen] = '/'; + /* test above ensured this will fit: */ + strcpy(fullname + fullnamelen + 1, name); + result = open(fullname, O_RDONLY | PG_BINARY, 0); + if (result >= 0) + return result; + /* If that didn't work, fall through to do it the hard way */ + fullname[fullnamelen] = '\0'; + } + /* * Loop to split the given name into directory levels; for each level, * search using scan_directory_ci(). */ - strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); - orignamelen = fullnamelen = strlen(fullname); fname = name; for (;;) { @@ -96,8 +123,6 @@ pg_open_tzfile(const char *name, char *canonname) fnamelen = slashptr - fname; else fnamelen = strlen(fname); - if (fullnamelen + 1 + fnamelen >= MAXPGPATH) - return -1; /* not gonna fit */ if (!scan_directory_ci(fullname, fname, fnamelen, fullname + fullnamelen + 1, MAXPGPATH - fullnamelen - 1)) @@ -308,14 +333,14 @@ pg_tzset_offset(long gmtoffset) char tzname[128]; snprintf(offsetstr, sizeof(offsetstr), - "%02ld", absoffset / SECSPERHOUR); - absoffset %= SECSPERHOUR; + "%02ld", absoffset / SECS_PER_HOUR); + absoffset %= SECS_PER_HOUR; if (absoffset != 0) { snprintf(offsetstr + strlen(offsetstr), sizeof(offsetstr) - strlen(offsetstr), - ":%02ld", absoffset / SECSPERMIN); - absoffset %= SECSPERMIN; + ":%02ld", absoffset / SECS_PER_MINUTE); + absoffset %= SECS_PER_MINUTE; if (absoffset != 0) snprintf(offsetstr + strlen(offsetstr), sizeof(offsetstr) - strlen(offsetstr), @@ -412,7 +437,7 @@ pg_tzenumerate_next(pg_tzenum *dir) while (dir->depth >= 0) { struct dirent *direntry; - char fullname[MAXPGPATH]; + char fullname[MAXPGPATH * 2]; struct stat statbuf; direntry = ReadDir(dir->dirdesc[dir->depth], dir->dirname[dir->depth]); @@ -429,7 +454,7 @@ pg_tzenumerate_next(pg_tzenum *dir) if (direntry->d_name[0] == '.') continue; - snprintf(fullname, MAXPGPATH, "%s/%s", + snprintf(fullname, sizeof(fullname), "%s/%s", dir->dirname[dir->depth], direntry->d_name); if (stat(fullname, &statbuf) != 0) ereport(ERROR, @@ -457,10 +482,11 @@ pg_tzenumerate_next(pg_tzenum *dir) /* * Load this timezone using tzload() not pg_tzset(), so we don't fill - * the cache + * the cache. Also, don't ask for the canonical spelling: we already + * know it, and pg_open_tzfile's way of finding it out is pretty + * inefficient. */ - if (tzload(fullname + dir->baselen, dir->tz.TZname, &dir->tz.state, - true) != 0) + if (tzload(fullname + dir->baselen, NULL, &dir->tz.state, true) != 0) { /* Zone could not be loaded, ignore it */ continue; @@ -472,6 +498,10 @@ pg_tzenumerate_next(pg_tzenum *dir) continue; } + /* OK, return the canonical zone name spelling. */ + strlcpy(dir->tz.TZname, fullname + dir->baselen, + sizeof(dir->tz.TZname)); + /* Timezone loaded OK. */ return &dir->tz; } |