Blob Blame History Raw
From 78105a206a21133f87f5982f29d7aa3c4cc72b0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 16 Mar 2022 09:28:46 +0100
Subject: [PATCH] shared/install: skip unnecessary chasing of symlinks in
 disable

We use the symlink source name and destination names to decide whether to remove
the symlink. But if the source name is enough to decide to remove the symlink,
we'd still look up the destination for no good reason. This is a slow operation,
let's skip it.

(cherry picked from commit 7a6c73dabf6451d6ef22d0cdfbb1749a77450d5b)

Related: #2082131
---
 src/shared/install.c | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/shared/install.c b/src/shared/install.c
index ad0238ab50..08a9892260 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -599,8 +599,7 @@ static int remove_marked_symlinks_fd(
                                 r = q;
 
                 } else if (de->d_type == DT_LNK) {
-                        _cleanup_free_ char *p = NULL, *dest = NULL;
-                        const char *rp;
+                        _cleanup_free_ char *p = NULL;
                         bool found;
                         int q;
 
@@ -612,24 +611,32 @@ static int remove_marked_symlinks_fd(
                                 return -ENOMEM;
                         path_simplify(p);
 
-                        q = chase_symlinks(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
-                        if (q == -ENOENT)
-                                continue;
-                        if (q < 0) {
-                                log_debug_errno(q, "Failed to resolve symlink \"%s\": %m", p);
-                                unit_file_changes_add(changes, n_changes, q, p, NULL);
+                        /* We remove all links pointing to a file or path that is marked, as well as all
+                         * files sharing the same name as a file that is marked. Do path chasing only if
+                         * we don't already know that we want to remove the symlink. */
+                        found = set_contains(remove_symlinks_to, de->d_name);
 
-                                if (r == 0)
-                                        r = q;
-                                continue;
-                        }
+                        if (!found) {
+                                _cleanup_free_ char *dest = NULL;
+
+
+                                q = chase_symlinks(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
+                                if (q == -ENOENT)
+                                        continue;
+                                if (q < 0) {
+                                        log_debug_errno(q, "Failed to resolve symlink \"%s\": %m", p);
+                                        unit_file_changes_add(changes, n_changes, q, p, NULL);
 
-                        /* We remove all links pointing to a file or path that is marked, as well as all files sharing
-                         * the same name as a file that is marked. */
+                                        if (r == 0)
+                                                r = q;
+                                        continue;
+                                }
+
+                                found = set_contains(remove_symlinks_to, dest) ||
+                                        set_contains(remove_symlinks_to, basename(dest));
+
+                        }
 
-                        found = set_contains(remove_symlinks_to, dest) ||
-                                set_contains(remove_symlinks_to, basename(dest)) ||
-                                set_contains(remove_symlinks_to, de->d_name);
 
                         if (!found)
                                 continue;
@@ -650,7 +657,7 @@ static int remove_marked_symlinks_fd(
                         /* Now, remember the full path (but with the root prefix removed) of
                          * the symlink we just removed, and remove any symlinks to it, too. */
 
-                        rp = skip_root(lp->root_dir, p);
+                        const char *rp = skip_root(lp->root_dir, p);
                         q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
                         if (q < 0)
                                 return q;