From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nikolay Amiantov <ab@fmap.me>
Date: Tue, 11 Oct 2016 13:12:08 +0300
Subject: [PATCH] Change /usr/share/zoneinfo to /etc/zoneinfo

NixOS uses this path.
---
 man/localtime.xml         |  4 ++--
 src/basic/time-util.c     |  8 ++++----
 src/firstboot/firstboot.c |  9 +++------
 src/nspawn/nspawn.c       |  4 ++--
 src/timedate/timedated.c  | 15 +++++++++------
 5 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/man/localtime.xml b/man/localtime.xml
index 3a13e04a27..4fd58068a1 100644
--- a/man/localtime.xml
+++ b/man/localtime.xml
@@ -20,7 +20,7 @@
   </refnamediv>
 
   <refsynopsisdiv>
-    <para><filename>/etc/localtime</filename> -&gt; <filename>../usr/share/zoneinfo/…</filename></para>
+    <para><filename>/etc/localtime</filename> -&gt; <filename>zoneinfo/…</filename></para>
   </refsynopsisdiv>
 
   <refsect1>
@@ -30,7 +30,7 @@
     system-wide timezone of the local system that is used by
     applications for presentation to the user. It should be an
     absolute or relative symbolic link pointing to
-    <filename>/usr/share/zoneinfo/</filename>, followed by a timezone
+    <filename>/etc/zoneinfo/</filename>, followed by a timezone
     identifier such as <literal>Europe/Berlin</literal> or
     <literal>Etc/UTC</literal>. The resulting link should lead to the
     corresponding binary
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index eb74de32c2..6906462b36 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -1456,7 +1456,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
 
         assert(ret);
 
-        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
+        f = fopen("/etc/zoneinfo/zone1970.tab", "re");
         if (!f)
                 return -errno;
 
@@ -1501,7 +1501,7 @@ static int get_timezones_from_tzdata_zi(char ***ret) {
 
         assert(ret);
 
-        f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
+        f = fopen("/etc/zoneinfo/tzdata.zi", "re");
         if (!f)
                 return -errno;
 
@@ -1616,7 +1616,7 @@ int verify_timezone(const char *name, int log_level) {
         if (p - name >= PATH_MAX)
                 return -ENAMETOOLONG;
 
-        t = strjoina("/usr/share/zoneinfo/", name);
+        t = strjoina("/etc/zoneinfo/", name);
 
         fd = open(t, O_RDONLY|O_CLOEXEC);
         if (fd < 0)
@@ -1688,7 +1688,7 @@ int get_timezone(char **ret) {
         if (r < 0)
                 return r; /* Return EINVAL if not a symlink */
 
-        const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
+        const char *e = PATH_STARTSWITH_SET(t, "/etc/zoneinfo/", "../etc/zoneinfo/");
         if (!e)
                 return -EINVAL;
         if (!timezone_is_valid(e, LOG_DEBUG))
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index e922abdd63..986a9e25d1 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -601,7 +601,7 @@ static int prompt_timezone(int rfd, sd_varlink **mute_console_link) {
 
 static int process_timezone(int rfd, sd_varlink **mute_console_link) {
         _cleanup_close_ int pfd = -EBADF;
-        _cleanup_free_ char *f = NULL, *relpath = NULL;
+        _cleanup_free_ char *f = NULL;
         const char *e;
         int r;
 
@@ -647,12 +647,9 @@ static int process_timezone(int rfd, sd_varlink **mute_console_link) {
         if (isempty(arg_timezone))
                 return 0;
 
-        e = strjoina("/usr/share/zoneinfo/", arg_timezone);
-        r = path_make_relative_parent(etc_localtime(), e, &relpath);
-        if (r < 0)
-                return r;
+        e = strjoina("zoneinfo/", arg_timezone);
 
-        r = symlinkat_atomic_full(relpath, pfd, f, SYMLINK_LABEL);
+        r = symlinkat_atomic_full(e, pfd, f, SYMLINK_LABEL);
         if (r < 0)
                 return log_error_errno(r, "Failed to create /etc/localtime symlink: %m");
 
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 7f7ceac318..5f3faa7bf4 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1757,8 +1757,8 @@ int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid
 static const char *timezone_from_path(const char *path) {
         return PATH_STARTSWITH_SET(
                         path,
-                        "../usr/share/zoneinfo/",
-                        "/usr/share/zoneinfo/");
+                        "../etc/zoneinfo/",
+                        "/etc/zoneinfo/");
 }
 
 static bool etc_writable(void) {
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 43cf3fddb9..dc23550500 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -268,7 +268,7 @@ static int context_read_data(Context *c) {
 
         r = get_timezone(&t);
         if (r == -EINVAL)
-                log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
+                log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /etc/zoneinfo/.");
         else if (r < 0)
                 log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
 
@@ -292,7 +292,7 @@ static int context_write_data_timezone(Context *c) {
 
         if (isempty(c->zone) || streq(c->zone, "UTC")) {
 
-                if (access("/usr/share/zoneinfo/UTC", F_OK) < 0) {
+                if (access("/etc/zoneinfo/UTC", F_OK) < 0) {
 
                         if (unlink(etc_localtime()) < 0 && errno != ENOENT)
                                 return -errno;
@@ -300,17 +300,20 @@ static int context_write_data_timezone(Context *c) {
                         return 0;
                 }
 
-                source = "/usr/share/zoneinfo/UTC";
+                source = "/etc/zoneinfo/UTC";
         } else {
-                p = path_join("/usr/share/zoneinfo", c->zone);
+                p = path_join("/etc/zoneinfo", c->zone);
                 if (!p)
                         return -ENOMEM;
 
                 source = p;
         }
 
-        return symlinkat_atomic_full(source, AT_FDCWD, etc_localtime(),
-                                     !secure_getenv("SYSTEMD_ETC_LOCALTIME"));
+        /* With zoneinfo being at /etc/zoneinfo instead of /usr/share/zoneinfo on NixOS, and etc_localtime() left untouched
+         * at /etc/localtime, a relative symlink would look like zoneinfo/UTC instead of ../usr/share/zoneinfo/UTC. This is
+         * an issue because not just systemd itself (src/basic/time-util.c @@ get_timezone), but also others like icu::TimeZone
+         * reject such symlinks. Forcing the symlink to be absolute (/etc/zoneinfo/UTC) takes care of at least both of them. */
+        return symlinkat_atomic_full(source, AT_FDCWD, etc_localtime(), /* flags */ 0);
 }
 
 static const char* etc_adjtime(void) {
