From: Martin Pitt <martin.pitt@ubuntu.com>
Date: Sat, 26 Apr 2014 23:49:32 +0200
Subject: Support system-image read-only /etc

On Ubuntu Phone with readonly /etc we symlink
/etc/{adjtime,localtime,timezone,hostname,machine-info} to /etc/writable/, so
we need to update those files instead if the original files are symlinks into
/etc/writable/.

Forwarded: OMGno, this is a rather nasty hack until we get a clean solution for an /etc overlay
Bug-Ubuntu: https://launchpad.net/bugs/1227520
---
 src/hostname/hostnamed.c | 27 +++++++++++++++++++++++----
 src/timedate/timedated.c | 35 +++++++++++++++++++++++++++--------
 2 files changed, 50 insertions(+), 12 deletions(-)

diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index 7cd4a1d..7546f30 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -76,6 +76,25 @@ static void context_free(Context *c) {
         bus_verify_polkit_async_registry_free(c->polkit_registry);
 }
 
+/* Hack for Ubuntu phone: check if path is an existing symlink to
+ * /etc/writable; if it is, update that instead */
+static const char* writable_filename(const char *path) {
+        ssize_t r;
+        static char realfile_buf[PATH_MAX];
+        _cleanup_free_ char *realfile = NULL;
+        const char *result = path;
+        int orig_errno = errno;
+
+        r = readlink_and_make_absolute(path, &realfile);
+        if (r >= 0 && startswith(realfile, "/etc/writable")) {
+                snprintf(realfile_buf, sizeof(realfile_buf), "%s", realfile);
+                result = realfile_buf;
+        }
+
+        errno = orig_errno;
+        return result;
+}
+
 static int context_read_data(Context *c) {
         int r;
         struct utsname u;
@@ -300,12 +319,12 @@ static int context_write_data_static_hostname(Context *c) {
 
         if (isempty(c->data[PROP_STATIC_HOSTNAME])) {
 
-                if (unlink("/etc/hostname") < 0)
+                if (unlink(writable_filename("/etc/hostname")) < 0)
                         return errno == ENOENT ? 0 : -errno;
 
                 return 0;
         }
-        return write_string_file_atomic_label("/etc/hostname", c->data[PROP_STATIC_HOSTNAME]);
+        return write_string_file_atomic_label(writable_filename("/etc/hostname"), c->data[PROP_STATIC_HOSTNAME]);
 }
 
 static int context_write_data_machine_info(Context *c) {
@@ -351,13 +370,13 @@ static int context_write_data_machine_info(Context *c) {
         }
 
         if (strv_isempty(l)) {
-                if (unlink("/etc/machine-info") < 0)
+                if (unlink(writable_filename("/etc/machine-info")) < 0)
                         return errno == ENOENT ? 0 : -errno;
 
                 return 0;
         }
 
-        return write_env_file_label("/etc/machine-info", l);
+        return write_env_file_label(writable_filename("/etc/machine-info"), l);
 }
 
 static int property_get_icon_name(
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
index 972396f..fa23fa1 100644
--- a/src/timedate/timedated.c
+++ b/src/timedate/timedated.c
@@ -157,13 +157,32 @@ static int symlink_or_copy_atomic(const char *from, const char *to) {
         return r;
 }
 
+/* Hack for Ubuntu phone: check if path is an existing symlink to
+ * /etc/writable; if it is, update that instead */
+static const char* writable_filename(const char *path) {
+        ssize_t r;
+        static char realfile_buf[PATH_MAX];
+        _cleanup_free_ char *realfile = NULL;
+        const char *result = path;
+        int orig_errno = errno;
+
+        r = readlink_and_make_absolute(path, &realfile);
+        if (r >= 0 && startswith(realfile, "/etc/writable")) {
+                snprintf(realfile_buf, sizeof(realfile_buf), "%s", realfile);
+                result = realfile_buf;
+        }
+
+        errno = orig_errno;
+        return result;
+}
+
 static int context_read_data(Context *c) {
         _cleanup_free_ char *t = NULL;
         int r;
 
         assert(c);
 
-        r = readlink_malloc("/etc/localtime", &t);
+        r = readlink_malloc(writable_filename("/etc/localtime"), &t);
         if (r < 0) {
                 if (r == -EINVAL)
                         log_debug("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/.");
@@ -187,7 +206,7 @@ static int context_read_data(Context *c) {
                 }
         }
 
-        r = read_one_line_file("/etc/timezone", &c->zone);
+        r = read_one_line_file(writable_filename("/etc/timezone"), &c->zone);
         if (r < 0) {
                 if (r != -ENOENT)
                         log_warning("Failed to read /etc/timezone: %s", strerror(-r));
@@ -212,10 +231,10 @@ static int context_write_data_timezone(Context *c) {
         assert(c);
 
         if (isempty(c->zone)) {
-                if (unlink("/etc/localtime") < 0 && errno != ENOENT)
+                if (unlink(writable_filename("/etc/localtime")) < 0 && errno != ENOENT)
                         r = -errno;
 
-                if (unlink("/etc/timezone") < 0 && errno != ENOENT)
+                if (unlink(writable_filename("/etc/timezone")) < 0 && errno != ENOENT)
                         r = -errno;
 
                 return r;
@@ -225,12 +244,12 @@ static int context_write_data_timezone(Context *c) {
         if (!p)
                 return log_oom();
 
-        r = symlink_or_copy_atomic(p, "/etc/localtime");
+        r = symlink_or_copy_atomic(p, writable_filename("/etc/localtime"));
         if (r < 0)
                 return r;
 
-        if (stat("/etc/timezone", &st) == 0 && S_ISREG(st.st_mode)) {
-                r = write_string_file_atomic("/etc/timezone", c->zone);
+        if (stat(writable_filename("/etc/timezone"), &st) == 0 && S_ISREG(st.st_mode)) {
+                r = write_string_file_atomic(writable_filename("/etc/timezone"), c->zone);
                 if (r < 0)
                         return r;
         }
@@ -282,7 +301,7 @@ static int context_write_data_local_rtc(Context *c) {
                 *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0;
 
                 if (streq(w, NULL_ADJTIME_UTC)) {
-                        if (unlink("/etc/adjtime") < 0)
+                        if (unlink(writable_filename("/etc/adjtime")) < 0)
                                 if (errno != ENOENT)
                                         return -errno;
 
