]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: check null_or_empty for masked units instead of /dev/null
authorAnita Zhang <the.anitazha@gmail.com>
Thu, 28 May 2020 19:09:32 +0000 (12:09 -0700)
committerAnita Zhang <the.anitazha@gmail.com>
Fri, 3 Jul 2020 09:33:50 +0000 (02:33 -0700)
There's some inconsistency in the what is considered a masked unit:
some places (i.e. load-fragment.c) use `null_or_empty()` while others
check if the file path is symlinked to "/dev/null". Since the latter
doesn't account for things like non-absolute symlinks to "/dev/null",
this commit switches the check for "/dev/null" to use `null_or_empty_path()`

src/core/unit.c
src/shared/install.c
src/test/test-install-root.c

index 18bf0cd52ab0df9bc6974fbb30b61f90d7cec69d..c739c0a561695ba2541329b2b8c4477e93e4c7cc 100644 (file)
@@ -5955,7 +5955,7 @@ const char *unit_label_path(const Unit *u) {
                 return NULL;
 
         /* If a unit is masked, then don't read the SELinux label of /dev/null, as that really makes no sense */
-        if (path_equal(p, "/dev/null"))
+        if (null_or_empty_path(p) > 0)
                 return NULL;
 
         return p;
index bb2eff7387945628764c94aa9e45115e352eafdf..0f2d407a36afd55780534145edd93bc4a0df42b6 100644 (file)
@@ -1324,26 +1324,27 @@ static int unit_file_load_or_readlink(
                 const char *path,
                 const char *root_dir,
                 SearchFlags flags) {
-
-        _cleanup_free_ char *target = NULL;
+        struct stat st;
         int r;
 
         r = unit_file_load(c, info, path, root_dir, flags);
         if (r != -ELOOP || (flags & SEARCH_DROPIN))
                 return r;
 
-        /* This is a symlink, let's read it. */
-
-        r = readlink_malloc(path, &target);
-        if (r < 0)
-                return r;
-
-        if (path_equal(target, "/dev/null"))
+        r = chase_symlinks_and_stat(path, root_dir, CHASE_WARN, NULL, &st, NULL);
+        if (r > 0 && null_or_empty(&st))
                 info->type = UNIT_FILE_TYPE_MASKED;
         else {
+                _cleanup_free_ char *target = NULL;
                 const char *bn;
                 UnitType a, b;
 
+                /* This is a symlink, let's read it. */
+
+                r = readlink_malloc(path, &target);
+                if (r < 0)
+                        return r;
+
                 bn = basename(target);
 
                 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
index 515f14b8ca9209ea46a65d0edd457e84b8a0e35f..d437686baebc214971b2b37347eb9504ddfaa03e 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "fileio.h"
+#include "fs-util.h"
 #include "install.h"
 #include "mkdir.h"
 #include "rm-rf.h"
@@ -23,6 +24,7 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) == -ENOENT);
 
         p = strjoina(root, "/usr/lib/systemd/system/a.service");
         assert_se(write_string_file(p,
@@ -150,6 +152,22 @@ static void test_basic_mask_and_enable(const char *root) {
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
         assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
+
+        /* Test masking with relative symlinks */
+
+        p = strjoina(root, "/usr/lib/systemd/system/e.service");
+        assert_se(symlink("../../../../../../dev/null", p) >= 0);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        assert_se(unlink(p) == 0);
+        assert_se(symlink("/usr/../dev/null", p) >= 0);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0);
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
+
+        assert_se(unlink(p) == 0);
 }
 
 static void test_linked_units(const char *root) {
@@ -1227,6 +1245,12 @@ int main(int argc, char *argv[]) {
         p = strjoina(root, "/usr/lib/systemd/system-preset/");
         assert_se(mkdir_p(p, 0755) >= 0);
 
+        p = strjoina(root, "/dev/");
+        assert_se(mkdir_p(p, 0755) >= 0);
+
+        p = strjoina(root, "/dev/null");
+        assert_se(touch(p) >= 0);
+
         test_basic_mask_and_enable(root);
         test_linked_units(root);
         test_default(root);