From df9991a2186d4236ba1e97e6638fa53b578bc6d7 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 6 Oct 2016 11:47:17 -0700 Subject: [PROPOSED PATCH] Do not assume TZDEFAULT is relative. Problem reported by Andreas Stieger in: http://mm.icann.org/pipermail/tz/2016-October/024280.html * NEWS: Document this. * zic.c (relname): New function. (dolink): Use it. --- NEWS | 7 +++++++ zic.c | 65 ++++++++++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index c694ea8..b8470e3 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,13 @@ Unreleased, experimental changes not at 02:00, and the spring 1994 transition was on March 20, not March 27. (Thanks to Kıvanç Yazan.) + Changes to code + + zic no longer mishandles relativizing file names when creating + symbolic links like /etc/localtime, when these symbolic links + are outside the usual directory hierarchy. This fixes a bug + introduced in 2016g. (Problem reported by Andreas Stieger.) + Changes to build procedure New rules 'traditional_tarballs' and 'traditional_signatures' for diff --git a/zic.c b/zic.c index 2505c11..eba223c 100644 --- a/zic.c +++ b/zic.c @@ -764,6 +764,44 @@ 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. */ +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; +} + static void dolink(char const *fromfield, char const *tofield, bool staysymlink) { @@ -800,30 +838,15 @@ dolink(char const *fromfield, char const *tofield, bool staysymlink) link_errno = link(fromfield, tofield) == 0 ? 0 : errno; } if (link_errno != 0) { - const char *s = fromfield; - const char *t; - char *p; - size_t dotdots = 0; - char *symlinkcontents; - int symlink_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; + 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; 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) warning(_("symbolic link used because hard link failed: %s"), -- 2.7.4