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