diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-12-28 16:47:20 +0100 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2021-12-28 17:29:17 +0100 |
commit | 7f304b856164a70b240d66d279fe66e7c8e8887d (patch) | |
tree | 299fad06ed7ec27f1b2155a2816ff5d701f4db34 | |
parent | core: do not touch /run/systemd/systemd-units-load from user session instances (diff) | |
download | systemd-7f304b856164a70b240d66d279fe66e7c8e8887d.tar.xz systemd-7f304b856164a70b240d66d279fe66e7c8e8887d.zip |
unti-file: fix symlinked drop-in directory handling
This fixes a bug introduced by 95ef0eaf0d5cd43fcc8e9eb541f2c342f25f8f2f.
Fixes #21920.
-rw-r--r-- | src/basic/unit-file.c | 84 |
1 files changed, 61 insertions, 23 deletions
diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c index 30c632dfce..faea92f66d 100644 --- a/src/basic/unit-file.c +++ b/src/basic/unit-file.c @@ -236,6 +236,31 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_ return updated == timestamp_hash; } +static int directory_name_is_valid(const char *name) { + const char *suffix; + + /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */ + + FOREACH_STRING(suffix, ".wants", ".requires", ".d") { + _cleanup_free_ char *chopped = NULL; + const char *e; + + e = endswith(name, suffix); + if (!e) + continue; + + chopped = strndup(name, e - name); + if (!chopped) + return log_oom(); + + if (unit_name_is_valid(chopped, UNIT_NAME_ANY) || + unit_type_from_string(chopped) >= 0) + return true; + } + + return false; +} + int unit_file_build_name_map( const LookupPaths *lp, uint64_t *cache_timestamp_hash, @@ -287,50 +312,61 @@ int unit_file_build_name_map( FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) { _unused_ _cleanup_free_ char *_filename_free = NULL; _cleanup_free_ char *simplified = NULL; + bool symlink_to_dir = false; const char *dst = NULL; char *filename; /* We only care about valid units and dirs with certain suffixes, let's ignore the * rest. */ - if (IN_SET(de->d_type, DT_REG, DT_LNK)) { + if (de->d_type == DT_REG) { + /* Accept a regular file whose name is a valid unit file name. */ if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; - /* Accept a regular file or symlink whose name is a valid unit file name. */ - } else if (de->d_type == DT_DIR) { - bool valid_dir_name = false; - const char *suffix; - - /* Also accept a directory whose name is a valid unit file name ending in - * .wants/, .requires/ or .d/ */ if (!paths) /* Skip directories early unless path_cache is requested */ continue; - FOREACH_STRING(suffix, ".wants", ".requires", ".d") { - _cleanup_free_ char *chopped = NULL; - const char *e; + r = directory_name_is_valid(de->d_name); + if (r < 0) + return r; + if (r == 0) + continue; + + } else if (de->d_type == DT_LNK) { + + /* Accept a symlink file whose name is a valid unit file name or + * ending in .wants/, .requires/ or .d/. */ - e = endswith(de->d_name, suffix); - if (!e) + if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) { + _cleanup_free_ char *target = NULL; + + if (!paths) /* Skip symlink to a directory early unless path_cache is requested */ continue; - chopped = strndup(de->d_name, e - de->d_name); - if (!chopped) - return log_oom(); + r = directory_name_is_valid(de->d_name); + if (r < 0) + return r; + if (r == 0) + continue; - if (unit_name_is_valid(chopped, UNIT_NAME_ANY) || - unit_type_from_string(chopped) >= 0) { - valid_dir_name = true; - break; + r = readlinkat_malloc(dirfd(d), de->d_name, &target); + if (r < 0) { + log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m", + *dir, de->d_name); + continue; } + + r = is_dir(target, /* follow = */ true); + if (r <= 0) + continue; + + symlink_to_dir = true; } - if (!valid_dir_name) - continue; } else continue; @@ -347,9 +383,11 @@ int unit_file_build_name_map( } else _filename_free = filename; /* Make sure we free the filename. */ - if (!IN_SET(de->d_type, DT_REG, DT_LNK)) + if (de->d_type == DT_DIR || (de->d_type == DT_LNK && symlink_to_dir)) continue; + assert(IN_SET(de->d_type, DT_REG, DT_LNK)); + /* search_path is ordered by priority (highest first). If the name is already mapped * to something (incl. itself), it means that we have already seen it, and we should * ignore it here. */ |