summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2016-10-20 15:40:07 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2016-10-20 15:40:34 -0400
commit37ecf07d398ce8d69958d637349683a9516e0b88 (patch)
tree7413b935d47d5c61f0e3b901962578424103b802
parent9345bf08c923c5b1b3c4e5bf0720384691bd77ab (diff)
downloadpostgresql-37ecf07d398ce8d69958d637349683a9516e0b88.tar.gz
Sync our copy of the timezone library with IANA release tzcode2016h.
This absorbs a fix for a symlink-manipulation bug in zic that was introduced in 2016g. It probably isn't interesting for our use-case, but I'm not quite sure, so let's update while we're at it.
-rw-r--r--src/timezone/zic.c76
1 files changed, 56 insertions, 20 deletions
diff --git a/src/timezone/zic.c b/src/timezone/zic.c
index 4ff819931a..612133772e 100644
--- a/src/timezone/zic.c
+++ b/src/timezone/zic.c
@@ -801,6 +801,56 @@ namecheck(const char *name)
return componentcheck(name, component, cp);
}
+/*
+ * Create symlink contents suitable for symlinking FROM to TO, as a
+ * freshly allocated string. FROM should be a relative file name, and
+ * is relative to the global variable DIRECTORY. TO can be either
+ * relative or absolute.
+ */
+#ifdef HAVE_SYMLINK
+static char *
+relname(char const * from, char const * to)
+{
+ size_t i,
+ taillen,
+ dotdotetcsize;
+ size_t dir_len = 0,
+ dotdots = 0,
+ linksize = SIZE_MAX;
+ char const *f = from;
+ char *result = NULL;
+
+ if (*to == '/')
+ {
+ /* Make F absolute too. */
+ size_t len = strlen(directory);
+ bool needslash = len && directory[len - 1] != '/';
+
+ linksize = len + needslash + strlen(from) + 1;
+ f = result = emalloc(linksize);
+ strcpy(result, directory);
+ result[len] = '/';
+ strcpy(result + len + needslash, from);
+ }
+ for (i = 0; f[i] && f[i] == to[i]; i++)
+ if (f[i] == '/')
+ dir_len = i + 1;
+ for (; f[i]; i++)
+ dotdots += f[i] == '/' && f[i - 1] != '/';
+ taillen = i - dir_len;
+ dotdotetcsize = 3 * dotdots + taillen + 1;
+ if (dotdotetcsize <= linksize)
+ {
+ if (!result)
+ result = emalloc(dotdotetcsize);
+ for (i = 0; i < dotdots; i++)
+ memcpy(result + 3 * i, "../", 3);
+ memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
+ }
+ return result;
+}
+#endif /* HAVE_SYMLINK */
+
static void
dolink(char const * fromfield, char const * tofield, bool staysymlink)
{
@@ -844,31 +894,17 @@ dolink(char const * fromfield, char const * tofield, bool staysymlink)
if (link_errno != 0)
{
#ifdef HAVE_SYMLINK
- const char *s = fromfield;
- const char *t;
- char *p;
- size_t dotdots = 0;
- char *symlinkcontents;
- int symlink_errno;
+ bool absolute = *fromfield == '/';
+ char *linkalloc = absolute ? NULL : relname(fromfield, tofield);
+ char const *contents = absolute ? fromfield : linkalloc;
+ int symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
- do
- t = s;
- while ((s = strchr(s, '/'))
- && strncmp(fromfield, tofield, ++s - fromfield) == 0);
-
- for (s = tofield + (t - fromfield); *s; s++)
- dotdots += *s == '/';
- symlinkcontents = emalloc(3 * dotdots + strlen(t) + 1);
- for (p = symlinkcontents; dotdots-- != 0; p += 3)
- memcpy(p, "../", 3);
- strcpy(p, t);
- symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
if (symlink_errno == ENOENT && !todirs_made)
{
mkdirs(tofield, true);
- symlink_errno = symlink(symlinkcontents, tofield) == 0 ? 0 : errno;
+ symlink_errno = symlink(contents, tofield) == 0 ? 0 : errno;
}
- free(symlinkcontents);
+ free(linkalloc);
if (symlink_errno == 0)
{
if (link_errno != ENOTSUP)