8d419f
From d49d646d00078b201cdde2978b7941d20acb1d4b Mon Sep 17 00:00:00 2001
8d419f
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
8d419f
Date: Wed, 2 Mar 2022 16:53:54 +0100
8d419f
Subject: [PATCH] basic/unit-file: split out the subroutine for symlink
8d419f
 verification
8d419f
8d419f
The old logs used __func__, but this doesn't make sense now, because the
8d419f
low-level function will be used in other places. So those are adjusted to be
8d419f
more generic.
8d419f
8d419f
(cherry picked from commit 9825181143530af7003fc50567b814dbbee39046)
8d419f
8d419f
Related: #2082131
8d419f
---
8d419f
 src/basic/unit-file.c | 159 +++++++++++++++++++++++++-----------------
8d419f
 1 file changed, 96 insertions(+), 63 deletions(-)
8d419f
8d419f
diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c
8d419f
index 96826e2940..25abce932a 100644
8d419f
--- a/src/basic/unit-file.c
8d419f
+++ b/src/basic/unit-file.c
8d419f
@@ -260,6 +260,83 @@ static int directory_name_is_valid(const char *name) {
8d419f
         return false;
8d419f
 }
8d419f
 
8d419f
+static int unit_file_resolve_symlink(
8d419f
+                const char *root_dir,
8d419f
+                char **search_path,
8d419f
+                const char *dir,
8d419f
+                int dirfd,
8d419f
+                const char *filename,
8d419f
+                char **ret_destination) {
8d419f
+
8d419f
+        _cleanup_free_ char *target = NULL, *simplified = NULL, *dst = NULL;
8d419f
+        int r;
8d419f
+
8d419f
+        assert(dir);
8d419f
+        assert(dirfd >= 0);
8d419f
+        assert(filename);
8d419f
+        assert(ret_destination);
8d419f
+
8d419f
+        r = readlinkat_malloc(dirfd, filename, &target);
8d419f
+        if (r < 0)
8d419f
+                return log_warning_errno(r, "Failed to read symlink %s%s%s: %m",
8d419f
+                                         dir, dir ? "/" : "", filename);
8d419f
+
8d419f
+        bool is_abs = path_is_absolute(target);
8d419f
+        if (root_dir || !is_abs) {
8d419f
+                char *target_abs = path_join(is_abs ? root_dir : dir, target);
8d419f
+                if (!target_abs)
8d419f
+                        return log_oom();
8d419f
+
8d419f
+                free_and_replace(target, target_abs);
8d419f
+        }
8d419f
+
8d419f
+        /* Get rid of "." and ".." components in target path */
8d419f
+        r = chase_symlinks(target, root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
8d419f
+        if (r < 0)
8d419f
+                return log_warning_errno(r, "Failed to resolve symlink %s/%s pointing to %s: %m",
8d419f
+                                         dir, filename, target);
8d419f
+
8d419f
+        /* Check if the symlink goes outside of our search path.
8d419f
+         * If yes, it's a linked unit file or mask, and we don't care about the target name.
8d419f
+         * Let's just store the link source directly.
8d419f
+         * If not, let's verify that it's a good symlink. */
8d419f
+        const char *tail = path_startswith_strv(simplified, search_path);
8d419f
+        if (!tail) {
8d419f
+                log_debug("Linked unit file: %s/%s → %s", dir, filename, simplified);
8d419f
+
8d419f
+                dst = path_join(dir, filename);
8d419f
+                if (!dst)
8d419f
+                        return log_oom();
8d419f
+
8d419f
+        } else {
8d419f
+                r = path_extract_filename(simplified, &dst);
8d419f
+                if (r < 0)
8d419f
+                        return r;
8d419f
+
8d419f
+                bool self_alias = streq(dst, filename);
8d419f
+
8d419f
+                if (is_path(tail))
8d419f
+                        log_full(self_alias ? LOG_DEBUG : LOG_WARNING,
8d419f
+                                 "Suspicious symlink %s/%s→%s, treating as alias.",
8d419f
+                                 dir, filename, simplified);
8d419f
+
8d419f
+                r = unit_validate_alias_symlink_and_warn(filename, simplified);
8d419f
+                if (r < 0)
8d419f
+                        return r;
8d419f
+
8d419f
+                if (self_alias)
8d419f
+                        /* A self-alias that has no effect */
8d419f
+                        return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
8d419f
+                                               "Unit file self-alias: %s/%s → %s, ignoring.",
8d419f
+                                               dir, filename, dst);
8d419f
+
8d419f
+                log_debug("Unit file alias: %s/%s → %s", dir, filename, dst);
8d419f
+        }
8d419f
+
8d419f
+        *ret_destination = TAKE_PTR(dst);
8d419f
+        return 0;
8d419f
+}
8d419f
+
8d419f
 int unit_file_build_name_map(
8d419f
                 const LookupPaths *lp,
8d419f
                 uint64_t *cache_timestamp_hash,
8d419f
@@ -310,10 +387,9 @@ int unit_file_build_name_map(
8d419f
 
8d419f
                 FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
8d419f
                         _unused_ _cleanup_free_ char *_filename_free = NULL;
8d419f
-                        _cleanup_free_ char *simplified = NULL;
8d419f
-                        bool symlink_to_dir = false;
8d419f
-                        const char *dst = NULL;
8d419f
                         char *filename;
8d419f
+                        _cleanup_free_ char *dst = NULL;
8d419f
+                        bool symlink_to_dir = false;
8d419f
 
8d419f
                         /* We only care about valid units and dirs with certain suffixes, let's ignore the
8d419f
                          * rest. */
8d419f
@@ -397,77 +473,34 @@ int unit_file_build_name_map(
8d419f
                                 /* We don't explicitly check for alias loops here. unit_ids_map_get() which
8d419f
                                  * limits the number of hops should be used to access the map. */
8d419f
 
8d419f
-                                _cleanup_free_ char *target = NULL;
8d419f
-
8d419f
-                                r = readlinkat_malloc(dirfd(d), de->d_name, &target);
8d419f
-                                if (r < 0) {
8d419f
-                                        log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m",
8d419f
-                                                          *dir, de->d_name);
8d419f
+                                r = unit_file_resolve_symlink(lp->root_dir, lp->search_path,
8d419f
+                                                              *dir, dirfd(d), de->d_name,
8d419f
+                                                              &dst);
8d419f
+                                if (r == -ENOMEM)
8d419f
+                                        return r;
8d419f
+                                if (r < 0)  /* we ignore other errors here */
8d419f
                                         continue;
8d419f
-                                }
8d419f
 
8d419f
-                                const bool is_abs = path_is_absolute(target);
8d419f
-                                if (lp->root_dir || !is_abs) {
8d419f
-                                        char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target);
8d419f
-                                        if (!target_abs)
8d419f
+                        } else {
8d419f
+                                dst = TAKE_PTR(_filename_free); /* Grab the copy we made previously, if available. */
8d419f
+                                if (!dst) {
8d419f
+                                        dst = strdup(filename);
8d419f
+                                        if (!dst)
8d419f
                                                 return log_oom();
8d419f
-
8d419f
-                                        free_and_replace(target, target_abs);
8d419f
                                 }
8d419f
 
8d419f
-                                /* Get rid of "." and ".." components in target path */
8d419f
-                                r = chase_symlinks(target, lp->root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
8d419f
-                                if (r < 0) {
8d419f
-                                        log_warning_errno(r, "Failed to resolve symlink %s pointing to %s, ignoring: %m",
8d419f
-                                                          filename, target);
8d419f
-                                        continue;
8d419f
-                                }
8d419f
-
8d419f
-                                /* Check if the symlink goes outside of our search path.
8d419f
-                                 * If yes, it's a linked unit file or mask, and we don't care about the target name.
8d419f
-                                 * Let's just store the link source directly.
8d419f
-                                 * If not, let's verify that it's a good symlink. */
8d419f
-                                char *tail = path_startswith_strv(simplified, lp->search_path);
8d419f
-                                if (!tail) {
8d419f
-                                        log_debug("%s: linked unit file: %s → %s",
8d419f
-                                                  __func__, filename, simplified);
8d419f
-
8d419f
-                                        dst = filename;
8d419f
-                                } else {
8d419f
-
8d419f
-                                        bool self_alias;
8d419f
-
8d419f
-                                        dst = basename(simplified);
8d419f
-                                        self_alias = streq(dst, de->d_name);
8d419f
-
8d419f
-                                        if (is_path(tail))
8d419f
-                                                log_full(self_alias ? LOG_DEBUG : LOG_WARNING,
8d419f
-                                                         "Suspicious symlink %s→%s, treating as alias.",
8d419f
-                                                         filename, simplified);
8d419f
-
8d419f
-                                        r = unit_validate_alias_symlink_and_warn(filename, simplified);
8d419f
-                                        if (r < 0)
8d419f
-                                                continue;
8d419f
-
8d419f
-                                        if (self_alias) {
8d419f
-                                                /* A self-alias that has no effect */
8d419f
-                                                log_debug("%s: self-alias: %s/%s → %s, ignoring.",
8d419f
-                                                          __func__, *dir, de->d_name, dst);
8d419f
-                                                continue;
8d419f
-                                        }
8d419f
-
8d419f
-                                        log_debug("%s: alias: %s/%s → %s", __func__, *dir, de->d_name, dst);
8d419f
-                                }
8d419f
-
8d419f
-                        } else {
8d419f
-                                dst = filename;
8d419f
                                 log_debug("%s: normal unit file: %s", __func__, dst);
8d419f
                         }
8d419f
 
8d419f
-                        r = hashmap_put_strdup(&ids, de->d_name, dst);
8d419f
+                        _cleanup_free_ char *key = strdup(de->d_name);
8d419f
+                        if (!key)
8d419f
+                                return log_oom();
8d419f
+
8d419f
+                        r = hashmap_ensure_put(&ids, &string_hash_ops_free_free, key, dst);
8d419f
                         if (r < 0)
8d419f
                                 return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
8d419f
                                                          de->d_name, dst);
8d419f
+                        key = dst = NULL;
8d419f
                 }
8d419f
         }
8d419f