f8452f
From 3569b29eb8b082229dd97b8aae60bbe4d2f96ef5 Mon Sep 17 00:00:00 2001
f8452f
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
f8452f
Date: Wed, 19 Dec 2018 23:05:48 +0100
f8452f
Subject: [PATCH] tmpfiles: fix crash with NULL in arg_root and other fixes and
f8452f
 tests
f8452f
f8452f
The function to replacement paths into the configuration file list was borked.
f8452f
Apart from the crash with empty root prefix, it would incorrectly handle the
f8452f
case where root *was* set, and the replacement file was supposed to override
f8452f
an existing file.
f8452f
f8452f
prefix_root is used instead of path_join because prefix_root removes duplicate
f8452f
slashes (when --root=dir/ is used).
f8452f
f8452f
A test is added.
f8452f
f8452f
Fixes #11124.
f8452f
f8452f
(cherry picked from commit 082bb1c59bd4300bcdc08488c94109680cfadf57)
f8452f
f8452f
Resolves: #1836024
f8452f
---
f8452f
 src/basic/conf-files.c     | 21 ++++++++-----
f8452f
 src/test/test-conf-files.c | 61 +++++++++++++++++++++++++++++++++++++-
f8452f
 2 files changed, 73 insertions(+), 9 deletions(-)
f8452f
f8452f
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
f8452f
index d6ef0e941e..5ca83091c9 100644
f8452f
--- a/src/basic/conf-files.c
f8452f
+++ b/src/basic/conf-files.c
f8452f
@@ -204,14 +204,17 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
f8452f
                 if (c == 0) {
f8452f
                         char **dir;
f8452f
 
f8452f
-                        /* Oh, we found our spot and it already contains something. */
f8452f
+                        /* Oh, there already is an entry with a matching name (the last component). */
f8452f
+
f8452f
                         STRV_FOREACH(dir, dirs) {
f8452f
+                                _cleanup_free_ char *rdir = NULL;
f8452f
                                 char *p1, *p2;
f8452f
 
f8452f
-                                p1 = path_startswith((*strv)[i], root);
f8452f
-                                if (p1)
f8452f
-                                        /* Skip "/" in *dir, because p1 is without "/" too */
f8452f
-                                        p1 = path_startswith(p1, *dir + 1);
f8452f
+                                rdir = prefix_root(root, *dir);
f8452f
+                                if (!rdir)
f8452f
+                                        return -ENOMEM;
f8452f
+
f8452f
+                                p1 = path_startswith((*strv)[i], rdir);
f8452f
                                 if (p1)
f8452f
                                         /* Existing entry with higher priority
f8452f
                                          * or same priority, no need to do anything. */
f8452f
@@ -220,7 +223,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
f8452f
                                 p2 = path_startswith(path, *dir);
f8452f
                                 if (p2) {
f8452f
                                         /* Our new entry has higher priority */
f8452f
-                                        t = path_join(root, path, NULL);
f8452f
+
f8452f
+                                        t = prefix_root(root, path);
f8452f
                                         if (!t)
f8452f
                                                 return log_oom();
f8452f
 
f8452f
@@ -236,7 +240,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
f8452f
                 /* … we are not there yet, let's continue */
f8452f
         }
f8452f
 
f8452f
-        t = path_join(root, path, NULL);
f8452f
+        /* The new file has lower priority than all the existing entries */
f8452f
+        t = prefix_root(root, path);
f8452f
         if (!t)
f8452f
                 return log_oom();
f8452f
 
f8452f
@@ -322,7 +327,7 @@ int conf_files_list_with_replacement(
f8452f
                 if (r < 0)
f8452f
                         return log_error_errno(r, "Failed to extend config file list: %m");
f8452f
 
f8452f
-                p = path_join(root, replacement, NULL);
f8452f
+                p = prefix_root(root, replacement);
f8452f
                 if (!p)
f8452f
                         return log_oom();
f8452f
         }
f8452f
diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c
f8452f
index 2ec2dfc261..5789767161 100644
f8452f
--- a/src/test/test-conf-files.c
f8452f
+++ b/src/test/test-conf-files.c
f8452f
@@ -13,6 +13,7 @@
f8452f
 #include "macro.h"
f8452f
 #include "mkdir.h"
f8452f
 #include "parse-util.h"
f8452f
+#include "path-util.h"
f8452f
 #include "rm-rf.h"
f8452f
 #include "string-util.h"
f8452f
 #include "strv.h"
f8452f
@@ -42,7 +43,7 @@ static void test_conf_files_list(bool use_root) {
f8452f
         _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL;
f8452f
         const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c, *mask;
f8452f
 
f8452f
-        log_debug("/* %s */", __func__);
f8452f
+        log_debug("/* %s(%s) */", __func__, yes_no(use_root));
f8452f
 
f8452f
         setup_test_dir(tmp_dir,
f8452f
                        "/dir1/a.conf",
f8452f
@@ -92,6 +93,60 @@ static void test_conf_files_list(bool use_root) {
f8452f
         assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
f8452f
 }
f8452f
 
f8452f
+static void test_conf_files_insert(const char *root) {
f8452f
+        _cleanup_strv_free_ char **s = NULL;
f8452f
+
f8452f
+        log_info("/* %s root=%s */", __func__, strempty(root));
f8452f
+
f8452f
+        char **dirs = STRV_MAKE("/dir1", "/dir2", "/dir3");
f8452f
+
f8452f
+        _cleanup_free_ const char
f8452f
+                *foo1 = prefix_root(root, "/dir1/foo.conf"),
f8452f
+                *foo2 = prefix_root(root, "/dir2/foo.conf"),
f8452f
+                *bar2 = prefix_root(root, "/dir2/bar.conf"),
f8452f
+                *zzz3 = prefix_root(root, "/dir3/zzz.conf"),
f8452f
+                *whatever = prefix_root(root, "/whatever.conf");
f8452f
+
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(foo2)));
f8452f
+
f8452f
+        /* The same file again, https://github.com/systemd/systemd/issues/11124 */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(foo2)));
f8452f
+
f8452f
+        /* Lower priority → new entry is ignored */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir3/foo.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(foo2)));
f8452f
+
f8452f
+        /* Higher priority → new entry replaces */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir1/foo.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(foo1)));
f8452f
+
f8452f
+        /* Earlier basename */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(bar2, foo1)));
f8452f
+
f8452f
+        /* Later basename */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
f8452f
+
f8452f
+        /* All lower priority → all ignored */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir3/zzz.conf") == 0);
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir2/bar.conf") == 0);
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir3/bar.conf") == 0);
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
f8452f
+
f8452f
+        /* Two entries that don't match any of the directories, but match basename */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/dir4/zzz.conf") == 0);
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/zzz.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, zzz3)));
f8452f
+
f8452f
+        /* An entry that doesn't match any of the directories, no match at all */
f8452f
+        assert_se(conf_files_insert(&s, root, dirs, "/whatever.conf") == 0);
f8452f
+        assert_se(strv_equal(s, STRV_MAKE(bar2, foo1, whatever, zzz3)));
f8452f
+}
f8452f
+
f8452f
 int main(int argc, char **argv) {
f8452f
         log_set_max_level(LOG_DEBUG);
f8452f
         log_parse_environment();
f8452f
@@ -99,5 +154,9 @@ int main(int argc, char **argv) {
f8452f
 
f8452f
         test_conf_files_list(false);
f8452f
         test_conf_files_list(true);
f8452f
+        test_conf_files_insert(NULL);
f8452f
+        test_conf_files_insert("/root");
f8452f
+        test_conf_files_insert("/root/");
f8452f
+
f8452f
         return 0;
f8452f
 }