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