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