eb6853
From b864dc9ef4aa00191dfa81accbe93f9ed0382261 Mon Sep 17 00:00:00 2001
eb6853
From: Michal Sekletar <msekleta@redhat.com>
eb6853
Date: Tue, 9 Mar 2021 17:22:32 +0100
eb6853
Subject: [PATCH] install: refactor find_symlinks() and don't search for
eb6853
 symlinks recursively
eb6853
eb6853
After all we are only interested in symlinks either in top-level config
eb6853
directory or in .wants and .requires sub-directories.
eb6853
eb6853
As a bonus this should speed up ListUnitFiles() roughly 3-4x on systems
eb6853
with a lot of units that use drop-ins (e.g. SSH jump hosts with a lot of
eb6853
user session scopes).
eb6853
eb6853
(cherry picked from commit 43b4e3058c106e663bbd5413e7bd106e55d6fd79)
eb6853
eb6853
Resolves: #1828758
eb6853
---
eb6853
 src/shared/install.c | 207 ++++++++++++++++++++-----------------------
eb6853
 1 file changed, 96 insertions(+), 111 deletions(-)
eb6853
eb6853
diff --git a/src/shared/install.c b/src/shared/install.c
eb6853
index f2f968e329..761bf8074e 100644
eb6853
--- a/src/shared/install.c
eb6853
+++ b/src/shared/install.c
eb6853
@@ -503,137 +503,88 @@ static int remove_marked_symlinks(
eb6853
         return r;
eb6853
 }
eb6853
 
eb6853
-static int find_symlinks_fd(
eb6853
+static int find_symlinks_in_directory(
eb6853
+                DIR *dir,
eb6853
+                const char *dir_path,
eb6853
                 const char *root_dir,
eb6853
                 const char *name,
eb6853
-                int fd,
eb6853
-                const char *path,
eb6853
                 const char *config_path,
eb6853
                 bool *same_name_link) {
eb6853
 
eb6853
+        struct dirent *de;
eb6853
         int r = 0;
eb6853
-        _cleanup_closedir_ DIR *d = NULL;
eb6853
 
eb6853
-        assert(name);
eb6853
-        assert(fd >= 0);
eb6853
-        assert(path);
eb6853
-        assert(config_path);
eb6853
-        assert(same_name_link);
eb6853
-
eb6853
-        d = fdopendir(fd);
eb6853
-        if (!d) {
eb6853
-                safe_close(fd);
eb6853
-                return -errno;
eb6853
-        }
eb6853
-
eb6853
-        for (;;) {
eb6853
-                struct dirent *de;
eb6853
-
eb6853
-                errno = 0;
eb6853
-                de = readdir(d);
eb6853
-                if (!de && errno != 0)
eb6853
-                        return -errno;
eb6853
+        FOREACH_DIRENT(de, dir, return -errno) {
eb6853
+                _cleanup_free_ char *p = NULL, *dest = NULL;
eb6853
+                bool found_path = false, found_dest, b = false;
eb6853
+                int q;
eb6853
 
eb6853
-                if (!de)
eb6853
-                        return r;
eb6853
+                dirent_ensure_type(dir, de);
eb6853
 
eb6853
-                if (hidden_file(de->d_name))
eb6853
+                if (de->d_type != DT_LNK)
eb6853
                         continue;
eb6853
 
eb6853
-                dirent_ensure_type(d, de);
eb6853
-
eb6853
-                if (de->d_type == DT_DIR) {
eb6853
-                        int nfd, q;
eb6853
-                        _cleanup_free_ char *p = NULL;
eb6853
-
eb6853
-                        nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
eb6853
-                        if (nfd < 0) {
eb6853
-                                if (errno == ENOENT)
eb6853
-                                        continue;
eb6853
-
eb6853
-                                if (r == 0)
eb6853
-                                        r = -errno;
eb6853
-                                continue;
eb6853
-                        }
eb6853
-
eb6853
-                        p = path_make_absolute(de->d_name, path);
eb6853
-                        if (!p) {
eb6853
-                                safe_close(nfd);
eb6853
-                                return -ENOMEM;
eb6853
-                        }
eb6853
+                /* Acquire symlink name */
eb6853
+                p = path_make_absolute(de->d_name, dir_path);
eb6853
+                if (!p)
eb6853
+                        return -ENOMEM;
eb6853
 
eb6853
-                        /* This will close nfd, regardless whether it succeeds or not */
eb6853
-                        q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link);
eb6853
-                        if (q > 0)
eb6853
-                                return 1;
eb6853
+                /* Acquire symlink destination */
eb6853
+                q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
eb6853
+                if (q == -ENOENT)
eb6853
+                        continue;
eb6853
+                if (q < 0) {
eb6853
                         if (r == 0)
eb6853
                                 r = q;
eb6853
+                        continue;
eb6853
+                }
eb6853
 
eb6853
-                } else if (de->d_type == DT_LNK) {
eb6853
-                        _cleanup_free_ char *p = NULL, *dest = NULL;
eb6853
-                        bool found_path, found_dest, b = false;
eb6853
-                        int q;
eb6853
+                /* Make absolute */
eb6853
+                if (!path_is_absolute(dest)) {
eb6853
+                        char *x;
eb6853
 
eb6853
-                        /* Acquire symlink name */
eb6853
-                        p = path_make_absolute(de->d_name, path);
eb6853
-                        if (!p)
eb6853
+                        x = path_join(dir_path, dest, NULL);
eb6853
+                        if (!x)
eb6853
                                 return -ENOMEM;
eb6853
 
eb6853
-                        /* Acquire symlink destination */
eb6853
-                        q = readlink_malloc(p, &dest);
eb6853
-                        if (q < 0) {
eb6853
-                                if (q == -ENOENT)
eb6853
-                                        continue;
eb6853
+                        free(dest);
eb6853
+                        dest = x;
eb6853
 
eb6853
-                                if (r == 0)
eb6853
-                                        r = q;
eb6853
-                                continue;
eb6853
-                        }
eb6853
-
eb6853
-                        /* Make absolute */
eb6853
-                        if (!path_is_absolute(dest)) {
eb6853
-                                char *x;
eb6853
-
eb6853
-                                x = prefix_root(root_dir, dest);
eb6853
-                                if (!x)
eb6853
-                                        return -ENOMEM;
eb6853
-
eb6853
-                                free(dest);
eb6853
-                                dest = x;
eb6853
-                        }
eb6853
+                }
eb6853
 
eb6853
-                        /* Check if the symlink itself matches what we
eb6853
-                         * are looking for */
eb6853
-                        if (path_is_absolute(name))
eb6853
-                                found_path = path_equal(p, name);
eb6853
-                        else
eb6853
-                                found_path = streq(de->d_name, name);
eb6853
+                /* Check if the symlink itself matches what we
eb6853
+                        * are looking for */
eb6853
+                if (path_is_absolute(name))
eb6853
+                        found_path = path_equal(p, name);
eb6853
+                else
eb6853
+                        found_path = streq(de->d_name, name);
eb6853
 
eb6853
-                        /* Check if what the symlink points to
eb6853
-                         * matches what we are looking for */
eb6853
-                        if (path_is_absolute(name))
eb6853
-                                found_dest = path_equal(dest, name);
eb6853
-                        else
eb6853
-                                found_dest = streq(basename(dest), name);
eb6853
+                /* Check if what the symlink points to
eb6853
+                        * matches what we are looking for */
eb6853
+                if (path_is_absolute(name))
eb6853
+                        found_dest = path_equal(dest, name);
eb6853
+                else
eb6853
+                        found_dest = streq(basename(dest), name);
eb6853
 
eb6853
-                        if (found_path && found_dest) {
eb6853
-                                _cleanup_free_ char *t = NULL;
eb6853
+                if (found_path && found_dest) {
eb6853
+                        _cleanup_free_ char *t = NULL;
eb6853
 
eb6853
-                                /* Filter out same name links in the main
eb6853
-                                 * config path */
eb6853
-                                t = path_make_absolute(name, config_path);
eb6853
-                                if (!t)
eb6853
-                                        return -ENOMEM;
eb6853
-
eb6853
-                                b = path_equal(t, p);
eb6853
-                        }
eb6853
+                        /* Filter out same name links in the main
eb6853
+                                * config path */
eb6853
+                        t = path_make_absolute(name, config_path);
eb6853
+                        if (!t)
eb6853
+                                return -ENOMEM;
eb6853
 
eb6853
-                        if (b)
eb6853
-                                *same_name_link = true;
eb6853
-                        else if (found_path || found_dest)
eb6853
-                                return 1;
eb6853
+                        b = path_equal(p, t);
eb6853
                 }
eb6853
+
eb6853
+                if (b)
eb6853
+                        *same_name_link = true;
eb6853
+                else if (found_path || found_dest)
eb6853
+                        return 1;
eb6853
         }
eb6853
+
eb6853
+        return r;
eb6853
 }
eb6853
 
eb6853
 static int find_symlinks(
eb6853
@@ -642,21 +593,55 @@ static int find_symlinks(
eb6853
                 const char *config_path,
eb6853
                 bool *same_name_link) {
eb6853
 
eb6853
-        int fd;
eb6853
+        _cleanup_closedir_ DIR *config_dir = NULL;
eb6853
+        struct dirent *de;
eb6853
+        int r = 0;
eb6853
 
eb6853
         assert(name);
eb6853
         assert(config_path);
eb6853
         assert(same_name_link);
eb6853
 
eb6853
-        fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
eb6853
-        if (fd < 0) {
eb6853
-                if (errno == ENOENT)
eb6853
+        config_dir = opendir(config_path);
eb6853
+        if (!config_dir) {
eb6853
+                if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
eb6853
                         return 0;
eb6853
                 return -errno;
eb6853
         }
eb6853
 
eb6853
-        /* This takes possession of fd and closes it */
eb6853
-        return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link);
eb6853
+        FOREACH_DIRENT(de, config_dir, return -errno) {
eb6853
+                const char *suffix;
eb6853
+                _cleanup_free_ const char *path = NULL;
eb6853
+                _cleanup_closedir_ DIR *d = NULL;
eb6853
+
eb6853
+                dirent_ensure_type(config_dir, de);
eb6853
+
eb6853
+                if (de->d_type != DT_DIR)
eb6853
+                        continue;
eb6853
+
eb6853
+                suffix = strrchr(de->d_name, '.');
eb6853
+                if (!streq(suffix, ".wants") && !streq(suffix, ".requires"))
eb6853
+                        continue;
eb6853
+
eb6853
+                path = path_join(config_path, de->d_name, NULL);
eb6853
+                if (!path)
eb6853
+                        return -ENOMEM;
eb6853
+
eb6853
+                d = opendir(path);
eb6853
+                if (!d) {
eb6853
+                        log_error_errno(errno, "Failed to open directory '%s' while scanning for symlinks, ignoring: %m", path);
eb6853
+                        continue;
eb6853
+                }
eb6853
+
eb6853
+                r = find_symlinks_in_directory(d, path, root_dir, name, config_path, same_name_link);
eb6853
+                if (r > 0)
eb6853
+                        return 1;
eb6853
+                else if (r < 0)
eb6853
+                        log_debug_errno(r, "Failed to lookup for symlinks in '%s': %m", path);
eb6853
+        }
eb6853
+
eb6853
+        /* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
eb6853
+        rewinddir(config_dir);
eb6853
+        return find_symlinks_in_directory(config_dir, config_path, root_dir, name, config_path, same_name_link);
eb6853
 }
eb6853
 
eb6853
 static int find_symlinks_in_scope(