Blob Blame History Raw
From 43f62843fbc5e5d085874393c24cf52ebb6658eb 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 17:37:58 +0100
Subject: [PATCH] shared/install: when looking for symlinks in
 .wants/.requires, ignore symlink target
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

We'd say that file is enabled indirectly if we had a symlink like:
  foo@.service ← bar.target.wants/foo@one.service
but not when we had
  foo@one.service ← bar.target.wants/foo@one.service

The effect of both link types is the same. In fact we don't care
about the symlink target. (We'll warn if it is mismatched, but we honour
it anyway.)

So let's use the original match logic only for aliases.
For .wants/.requires we instead look for a matching source name,
or a source name that matches after stripping of instance.

(cherry picked from commit 466f6979c90aaee62c33723392cc49c6638a3f46)

Related: #2082131
---
 src/shared/install.c | 93 ++++++++++++++++++++++++++++----------------
 1 file changed, 60 insertions(+), 33 deletions(-)

diff --git a/src/shared/install.c b/src/shared/install.c
index 1a2b0ccf24..a864039f44 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -748,7 +748,8 @@ static int find_symlinks_in_directory(
                 const char *dir_path,
                 const char *root_dir,
                 const UnitFileInstallInfo *info,
-                bool match_aliases,
+                bool ignore_destination,
+                bool match_name,
                 bool ignore_same_name,
                 const char *config_path,
                 bool *same_name_link) {
@@ -756,51 +757,67 @@ static int find_symlinks_in_directory(
         int r = 0;
 
         FOREACH_DIRENT(de, dir, return -errno) {
-                _cleanup_free_ char *dest = NULL;
-                bool found_path = false, found_dest, b = false;
+                bool found_path = false, found_dest = false, b = false;
                 int q;
 
                 if (de->d_type != DT_LNK)
                         continue;
 
-                /* Acquire symlink destination */
-                q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
-                if (q == -ENOENT)
-                        continue;
-                if (q < 0) {
-                        if (r == 0)
-                                r = q;
-                        continue;
-                }
+                if (!ignore_destination) {
+                        _cleanup_free_ char *dest = NULL;
+
+                        /* Acquire symlink destination */
+                        q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
+                        if (q == -ENOENT)
+                                continue;
+                        if (q < 0) {
+                                if (r == 0)
+                                        r = q;
+                                continue;
+                        }
 
-                /* Make absolute */
-                if (!path_is_absolute(dest)) {
-                        char *x;
+                        /* Make absolute */
+                        if (!path_is_absolute(dest)) {
+                                char *x;
 
-                        x = path_join(dir_path, dest);
-                        if (!x)
-                                return -ENOMEM;
+                                x = path_join(dir_path, dest);
+                                if (!x)
+                                        return -ENOMEM;
 
-                        free_and_replace(dest, x);
+                                free_and_replace(dest, x);
+                        }
+
+                        /* Check if what the symlink points to matches what we are looking for */
+                        found_dest = streq(basename(dest), info->name);
                 }
 
                 assert(unit_name_is_valid(info->name, UNIT_NAME_ANY));
-                if (!ignore_same_name)
-                               /* Check if the symlink itself matches what we are looking for.
-                                *
-                                * If ignore_same_name is specified, we are in one of the directories which
-                                * have lower priority than the unit file, and even if a file or symlink with
-                                * this name was found, we should ignore it. */
-                                found_path = streq(de->d_name, info->name);
 
-                /* Check if what the symlink points to matches what we are looking for */
-                found_dest = streq(basename(dest), info->name);
+                /* Check if the symlink itself matches what we are looking for.
+                 *
+                 * If ignore_destination is specified, we only look at the source name.
+                 *
+                 * If ignore_same_name is specified, we are in one of the directories which
+                 * have lower priority than the unit file, and even if a file or symlink with
+                 * this name was found, we should ignore it. */
+
+                if (ignore_destination || !ignore_same_name)
+                        found_path = streq(de->d_name, info->name);
+
+                if (!found_path && ignore_destination) {
+                        _cleanup_free_ char *template = NULL;
+
+                        q = unit_name_template(de->d_name, &template);
+                        if (q < 0 && q != -EINVAL)
+                                return q;
+                        if (q >= 0)
+                                found_dest = streq(template, info->name);
+                }
 
                 if (found_path && found_dest) {
                         _cleanup_free_ char *p = NULL, *t = NULL;
 
-                        /* Filter out same name links in the main
-                         * config path */
+                        /* Filter out same name links in the main config path */
                         p = path_make_absolute(de->d_name, dir_path);
                         t = path_make_absolute(info->name, config_path);
 
@@ -813,7 +830,7 @@ static int find_symlinks_in_directory(
                 if (b)
                         *same_name_link = true;
                 else if (found_path || found_dest) {
-                        if (!match_aliases)
+                        if (!match_name)
                                 return 1;
 
                         /* Check if symlink name is in the set of names used by [Install] */
@@ -872,7 +889,12 @@ static int find_symlinks(
                         continue;
                 }
 
-                r = find_symlinks_in_directory(d, path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
+                r = find_symlinks_in_directory(d, path, root_dir, i,
+                                               /* ignore_destination= */ true,
+                                               /* match_name= */ match_name,
+                                               /* ignore_same_name= */ ignore_same_name,
+                                               config_path,
+                                               same_name_link);
                 if (r > 0)
                         return 1;
                 else if (r < 0)
@@ -881,7 +903,12 @@ static int find_symlinks(
 
         /* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
         rewinddir(config_dir);
-        return find_symlinks_in_directory(config_dir, config_path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
+        return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
+                                          /* ignore_destination= */ false,
+                                          /* match_name= */ match_name,
+                                          /* ignore_same_name= */ ignore_same_name,
+                                          config_path,
+                                          same_name_link);
 }
 
 static int find_symlinks_in_scope(