84b277
From 022406c6dcaa5e9201a30b95f3e86328739e3892 Mon Sep 17 00:00:00 2001
84b277
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
84b277
Date: Thu, 24 Apr 2014 01:44:10 -0400
84b277
Subject: [PATCH] Make systemctl --root look for files in the proper places
84b277
84b277
Running systemctl enable/disable/set-default/... with the --root
84b277
option under strace reveals that it accessed various files and
84b277
directories in the main fs, and not underneath the specified root.
84b277
This can lead to correct results only when the layout and
84b277
configuration in the container are identical, which often is not the
84b277
case. Fix this by adding the specified root to all file access
84b277
operations.
84b277
84b277
This patch does not handle some corner cases: symlinks which point
84b277
outside of the specified root might be interpreted differently than
84b277
they would be by the kernel if the specified root was the real root.
84b277
But systemctl does not create such symlinks by itself, and I think
84b277
this is enough of a corner case not to be worth the additional
84b277
complexity of reimplementing link chasing in systemd.
84b277
84b277
Also, simplify the code in a few places and remove an hypothetical
84b277
memory leak on error.
84b277
84b277
Conflicts:
84b277
	TODO
84b277
	src/shared/install.c
84b277
84b277
(cherry picked from commit 12ed81d9c88406234c20e9261ae8c8b992d8bc4d)
84b277
84b277
Related: #1111199
84b277
---
84b277
 src/core/manager.c        |  2 ++
84b277
 src/shared/install.c      | 34 +++++++++++++++++++++-----------
84b277
 src/shared/path-lookup.c  | 12 ++++--------
84b277
 src/shared/path-lookup.h  |  8 +++++++-
84b277
 src/shared/path-util.c    | 49 ++++++++++++++++++++++++++++++++++++-----------
84b277
 src/systemctl/systemctl.c |  2 +-
84b277
 6 files changed, 75 insertions(+), 32 deletions(-)
84b277
84b277
diff --git a/src/core/manager.c b/src/core/manager.c
84b277
index 57a88b0..a2810b4 100644
84b277
--- a/src/core/manager.c
84b277
+++ b/src/core/manager.c
84b277
@@ -891,6 +891,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
84b277
 
84b277
         r = lookup_paths_init(
84b277
                         &m->lookup_paths, m->running_as, true,
84b277
+                        NULL,
84b277
                         m->generator_unit_path,
84b277
                         m->generator_unit_path_early,
84b277
                         m->generator_unit_path_late);
84b277
@@ -2383,6 +2384,7 @@ int manager_reload(Manager *m) {
84b277
 
84b277
         q = lookup_paths_init(
84b277
                         &m->lookup_paths, m->running_as, true,
84b277
+                        NULL,
84b277
                         m->generator_unit_path,
84b277
                         m->generator_unit_path_early,
84b277
                         m->generator_unit_path_late);
84b277
diff --git a/src/shared/install.c b/src/shared/install.c
84b277
index b9c85b7..672dcc2 100644
84b277
--- a/src/shared/install.c
84b277
+++ b/src/shared/install.c
84b277
@@ -47,7 +47,9 @@ typedef struct {
84b277
 #define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free)
84b277
 #define _cleanup_install_context_done_ _cleanup_(install_context_done)
84b277
 
84b277
-static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
84b277
+static int lookup_paths_init_from_scope(LookupPaths *paths,
84b277
+                                        UnitFileScope scope,
84b277
+                                        const char *root_dir) {
84b277
         assert(paths);
84b277
         assert(scope >= 0);
84b277
         assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
@@ -57,6 +59,7 @@ static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope)
84b277
         return lookup_paths_init(paths,
84b277
                                  scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
84b277
                                  scope == UNIT_FILE_USER,
84b277
+                                 root_dir,
84b277
                                  NULL, NULL, NULL);
84b277
 }
84b277
 
84b277
@@ -705,7 +708,7 @@ int unit_file_link(
84b277
         assert(scope >= 0);
84b277
         assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1476,7 +1479,7 @@ int unit_file_enable(
84b277
         assert(scope >= 0);
84b277
         assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1516,7 +1519,7 @@ int unit_file_disable(
84b277
         assert(scope >= 0);
84b277
         assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1578,7 +1581,7 @@ int unit_file_set_default(
84b277
         if (unit_name_to_type(file) != UNIT_TARGET)
84b277
                 return -EINVAL;
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1613,7 +1616,11 @@ int unit_file_get_default(
84b277
         char **p;
84b277
         int r;
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        assert(scope >= 0);
84b277
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
+        assert(name);
84b277
+
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1665,12 +1672,13 @@ UnitFileState unit_file_get_state(
84b277
         if (!unit_name_is_valid(name, true))
84b277
                 return -EINVAL;
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
         STRV_FOREACH(i, paths.unit_path) {
84b277
                 struct stat st;
84b277
+                char *partial;
84b277
 
84b277
                 free(path);
84b277
                 path = NULL;
84b277
@@ -1679,10 +1687,14 @@ UnitFileState unit_file_get_state(
84b277
                         asprintf(&path, "%s/%s/%s", root_dir, *i, name);
84b277
                 else
84b277
                         asprintf(&path, "%s/%s", *i, name);
84b277
-
84b277
                 if (!path)
84b277
                         return -ENOMEM;
84b277
 
84b277
+                if (root_dir)
84b277
+                        partial = path + strlen(root_dir) + 1;
84b277
+                else
84b277
+                        partial = path;
84b277
+
84b277
                 /*
84b277
                  * Search for a unit file in our default paths, to
84b277
                  * be sure, that there are no broken symlinks.
84b277
@@ -1714,7 +1726,7 @@ UnitFileState unit_file_get_state(
84b277
                 else if (r > 0)
84b277
                         return state;
84b277
 
84b277
-                r = unit_file_can_install(&paths, root_dir, path, true);
84b277
+                r = unit_file_can_install(&paths, root_dir, partial, true);
84b277
                 if (r < 0 && errno != ENOENT)
84b277
                         return r;
84b277
                 else if (r > 0)
84b277
@@ -1822,7 +1834,7 @@ int unit_file_preset(
84b277
         assert(scope >= 0);
84b277
         assert(scope < _UNIT_FILE_SCOPE_MAX);
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
@@ -1891,7 +1903,7 @@ int unit_file_get_list(
84b277
         if (root_dir && scope != UNIT_FILE_SYSTEM)
84b277
                 return -EINVAL;
84b277
 
84b277
-        r = lookup_paths_init_from_scope(&paths, scope);
84b277
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
84b277
         if (r < 0)
84b277
                 return r;
84b277
 
84b277
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
84b277
index 03c1380..b62f302 100644
84b277
--- a/src/shared/path-lookup.c
84b277
+++ b/src/shared/path-lookup.c
84b277
@@ -239,6 +239,7 @@ int lookup_paths_init(
84b277
                 LookupPaths *p,
84b277
                 SystemdRunningAs running_as,
84b277
                 bool personal,
84b277
+                const char *root_dir,
84b277
                 const char *generator,
84b277
                 const char *generator_early,
84b277
                 const char *generator_late) {
84b277
@@ -316,11 +317,9 @@ int lookup_paths_init(
84b277
                 }
84b277
         }
84b277
 
84b277
-        if (!path_strv_canonicalize_absolute(p->unit_path, NULL))
84b277
+        if (!path_strv_canonicalize_absolute_uniq(p->unit_path, root_dir))
84b277
                 return -ENOMEM;
84b277
 
84b277
-        strv_uniq(p->unit_path);
84b277
-
84b277
         if (!strv_isempty(p->unit_path)) {
84b277
                 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
84b277
                 if (!t)
84b277
@@ -372,15 +371,12 @@ int lookup_paths_init(
84b277
                                 return -ENOMEM;
84b277
                 }
84b277
 
84b277
-                if (!path_strv_canonicalize_absolute(p->sysvinit_path, NULL))
84b277
+                if (!path_strv_canonicalize_absolute_uniq(p->sysvinit_path, root_dir))
84b277
                         return -ENOMEM;
84b277
 
84b277
-                if (!path_strv_canonicalize_absolute(p->sysvrcnd_path, NULL))
84b277
+                if (!path_strv_canonicalize_absolute_uniq(p->sysvrcnd_path, root_dir))
84b277
                         return -ENOMEM;
84b277
 
84b277
-                strv_uniq(p->sysvinit_path);
84b277
-                strv_uniq(p->sysvrcnd_path);
84b277
-
84b277
                 if (!strv_isempty(p->sysvinit_path)) {
84b277
                         _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
84b277
                         if (!t)
84b277
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
84b277
index 9dee85f..0db9bfb 100644
84b277
--- a/src/shared/path-lookup.h
84b277
+++ b/src/shared/path-lookup.h
84b277
@@ -41,5 +41,11 @@ SystemdRunningAs systemd_running_as_from_string(const char *s) _pure_;
84b277
 
84b277
 int user_config_home(char **config_home);
84b277
 
84b277
-int lookup_paths_init(LookupPaths *p, SystemdRunningAs running_as, bool personal, const char *generator, const char *generator_early, const char *generator_late);
84b277
+int lookup_paths_init(LookupPaths *p,
84b277
+                      SystemdRunningAs running_as,
84b277
+                      bool personal,
84b277
+                      const char *root_dir,
84b277
+                      const char *generator,
84b277
+                      const char *generator_early,
84b277
+                      const char *generator_late);
84b277
 void lookup_paths_free(LookupPaths *p);
84b277
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
84b277
index de291a5..0aa5d61 100644
84b277
--- a/src/shared/path-util.c
84b277
+++ b/src/shared/path-util.c
84b277
@@ -179,36 +179,63 @@ char **path_strv_canonicalize_absolute(char **l, const char *prefix) {
84b277
 
84b277
         STRV_FOREACH(s, l) {
84b277
                 char *t, *u;
84b277
+                _cleanup_free_ char *orig = NULL;
84b277
 
84b277
-                if (!path_is_absolute(*s))
84b277
+                if (!path_is_absolute(*s)) {
84b277
+                        free(*s);
84b277
                         continue;
84b277
+                }
84b277
 
84b277
                 if (prefix) {
84b277
-                        t = strappend(prefix, *s);
84b277
-                        free(*s);
84b277
-                        *s = NULL;
84b277
-
84b277
+                        orig = *s;
84b277
+                        t = strappend(prefix, orig);
84b277
                         if (!t) {
84b277
                                 enomem = true;
84b277
                                 continue;
84b277
                         }
84b277
-                } else {
84b277
+                } else
84b277
                         t = *s;
84b277
-                        *s = NULL;
84b277
-                }
84b277
 
84b277
                 errno = 0;
84b277
                 u = canonicalize_file_name(t);
84b277
                 if (!u) {
84b277
-                        if (errno == ENOENT)
84b277
-                                u = t;
84b277
-                        else {
84b277
+                        if (errno == ENOENT) {
84b277
+                                if (prefix) {
84b277
+                                        u = orig;
84b277
+                                        orig = NULL;
84b277
+                                        free(t);
84b277
+                                } else
84b277
+                                        u = t;
84b277
+                        } else {
84b277
                                 free(t);
84b277
                                 if (errno == ENOMEM || errno == 0)
84b277
                                         enomem = true;
84b277
 
84b277
                                 continue;
84b277
                         }
84b277
+                } else if (prefix) {
84b277
+                        char *x;
84b277
+
84b277
+                        free(t);
84b277
+                        x = path_startswith(u, prefix);
84b277
+                        if (x) {
84b277
+                                /* restore the slash if it was lost */
84b277
+                                if (!startswith(x, "/"))
84b277
+                                        *(--x) = '/';
84b277
+
84b277
+                                t = strdup(x);
84b277
+                                free(u);
84b277
+                                if (!t) {
84b277
+                                        enomem = true;
84b277
+                                        continue;
84b277
+                                }
84b277
+                                u = t;
84b277
+                        } else {
84b277
+                                /* canonicalized path goes outside of
84b277
+                                 * prefix, keep the original path instead */
84b277
+                                u = orig;
84b277
+                                orig = NULL;
84b277
+                        }
84b277
                 } else
84b277
                         free(t);
84b277
 
84b277
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
84b277
index 1ca4fd3..1cbcd20 100644
84b277
--- a/src/systemctl/systemctl.c
84b277
+++ b/src/systemctl/systemctl.c
84b277
@@ -4258,7 +4258,7 @@ static int enable_sysv_units(const char *verb, char **args) {
84b277
         /* Processes all SysV units, and reshuffles the array so that
84b277
          * afterwards only the native units remain */
84b277
 
84b277
-        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL);
84b277
+        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
84b277
         if (r < 0)
84b277
                 return r;
84b277