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