From ee19edbb9f3455db3f750089082f3e5a925e3a0c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 19 May 2025 12:58:52 +0200 Subject: [PATCH 1/3] path-util: add flavour of path_startswith() that leaves a leading slash in place --- src/basic/fs-util.c | 2 +- src/basic/mkdir.c | 2 +- src/basic/path-util.c | 39 ++++++++++++++++++++++++++++----------- src/basic/path-util.h | 10 ++++++++-- src/test/test-path-util.c | 16 ++++++++++++++++ 5 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d306cc6d11..fd2312b543 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -67,7 +67,7 @@ int rmdir_parents(const char *path, const char *stop) { assert(*slash == '/'); *slash = '\0'; - if (path_startswith_full(stop, p, /* accept_dot_dot= */ false)) + if (path_startswith_full(stop, p, /* flags= */ 0)) return 0; if (rmdir(p) < 0 && errno != ENOENT) diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index a44add67de..9c04942bd1 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -151,7 +151,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui assert(_mkdirat != mkdirat); if (prefix) { - p = path_startswith_full(path, prefix, /* accept_dot_dot= */ false); + p = path_startswith_full(path, prefix, /* flags= */ 0); if (!p) return -EINVAL; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 64e42b2a23..5dd9b589f6 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -433,8 +433,8 @@ int path_simplify_alloc(const char *path, char **ret) { return 0; } -char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) { - assert(path); +char* path_startswith_full(const char *original_path, const char *prefix, PathStartWithFlags flags) { + assert(original_path); assert(prefix); /* Returns a pointer to the start of the first component after the parts matched by @@ -447,28 +447,45 @@ char* path_startswith_full(const char *path, const char *prefix, bool accept_dot * Returns NULL otherwise. */ + const char *path = original_path; + if ((path[0] == '/') != (prefix[0] == '/')) return NULL; for (;;) { const char *p, *q; - int r, k; + int m, n; - r = path_find_first_component(&path, accept_dot_dot, &p); - if (r < 0) + m = path_find_first_component(&path, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &p); + if (m < 0) return NULL; - k = path_find_first_component(&prefix, accept_dot_dot, &q); - if (k < 0) + n = path_find_first_component(&prefix, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &q); + if (n < 0) return NULL; - if (k == 0) - return (char*) (p ?: path); + if (n == 0) { + if (!p) + p = path; - if (r != k) + if (FLAGS_SET(flags, PATH_STARTSWITH_RETURN_LEADING_SLASH)) { + + if (p <= original_path) + return NULL; + + p--; + + if (*p != '/') + return NULL; + } + + return (char*) p; + } + + if (m != n) return NULL; - if (!strneq(p, q, r)) + if (!strneq(p, q, m)) return NULL; } } diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 671c1363c4..172248577e 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -43,9 +43,15 @@ int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from, const char *to, char **ret); int path_make_relative_parent(const char *from_child, const char *to, char **ret); -char* path_startswith_full(const char *path, const char *prefix, bool accept_dot_dot) _pure_; + +typedef enum PathStartWithFlags { + PATH_STARTSWITH_ACCEPT_DOT_DOT = 1U << 0, + PATH_STARTSWITH_RETURN_LEADING_SLASH = 1U << 1, +} PathStartWithFlags; + +char* path_startswith_full(const char *path, const char *prefix, PathStartWithFlags flags) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { - return path_startswith_full(path, prefix, true); + return path_startswith_full(path, prefix, PATH_STARTSWITH_ACCEPT_DOT_DOT); } int path_compare(const char *a, const char *b) _pure_; diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index aa4350e967..5928b2520d 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -756,6 +756,22 @@ TEST(path_startswith) { test_path_startswith_one("/foo/bar/barfoo/", "/fo", NULL, NULL); } +static void test_path_startswith_return_leading_slash_one(const char *path, const char *prefix, const char *expected) { + const char *p; + + log_debug("/* %s(%s, %s) */", __func__, path, prefix); + + p = path_startswith_full(path, prefix, PATH_STARTSWITH_RETURN_LEADING_SLASH); + ASSERT_STREQ(p, expected); +} + +TEST(path_startswith_return_leading_slash) { + test_path_startswith_return_leading_slash_one("/foo/bar", "/", "/foo/bar"); + test_path_startswith_return_leading_slash_one("/foo/bar", "/foo", "/bar"); + test_path_startswith_return_leading_slash_one("/foo/bar", "/foo/bar", NULL); + test_path_startswith_return_leading_slash_one("/foo/bar/", "/foo/bar", "/"); +} + static void test_prefix_root_one(const char *r, const char *p, const char *expected) { _cleanup_free_ char *s = NULL; From 482107724f64cb8dd24db3e65b6ea3151a330301 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 22 May 2025 18:35:25 +0200 Subject: [PATCH 2/3] cgroup: port some code over to path_startswith_full() --- src/basic/cgroup-util.c | 10 ++-------- src/core/cgroup.c | 14 ++++---------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index d8e94c3ed9..b68cab68a0 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -959,13 +959,12 @@ int cg_get_root_path(char **ret_path) { } int cg_shift_path(const char *cgroup, const char *root, const char **ret_shifted) { - _cleanup_free_ char *rt = NULL; - char *p; int r; assert(cgroup); assert(ret_shifted); + _cleanup_free_ char *rt = NULL; if (!root) { /* If the root was specified let's use that, otherwise * let's determine it from PID 1 */ @@ -977,12 +976,7 @@ int cg_shift_path(const char *cgroup, const char *root, const char **ret_shifted root = rt; } - p = path_startswith(cgroup, root); - if (p && p > cgroup) - *ret_shifted = p - 1; - else - *ret_shifted = cgroup; - + *ret_shifted = path_startswith_full(cgroup, root, PATH_STARTSWITH_RETURN_LEADING_SLASH) ?: cgroup; return 0; } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 61a034641b..e9dade8eb3 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -2808,19 +2808,13 @@ static int unit_prune_cgroup_via_bus(Unit *u) { return -EOWNERDEAD; /* Determine this unit's cgroup path relative to our cgroup root */ - const char *pp = path_startswith(crt->cgroup_path, u->manager->cgroup_root); + const char *pp = path_startswith_full( + crt->cgroup_path, + u->manager->cgroup_root, + PATH_STARTSWITH_RETURN_LEADING_SLASH); if (!pp) return -EINVAL; - _cleanup_free_ char *absolute = NULL; - if (!path_is_absolute(pp)) { /* RemoveSubgroupFromUnit() wants an absolute path */ - absolute = strjoin("/", pp); - if (!absolute) - return -ENOMEM; - - pp = absolute; - } - r = bus_call_method(u->manager->system_bus, bus_systemd_mgr, "RemoveSubgroupFromUnit", From ceed11e465f1c8efff1931412a85924d9de7c08d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 23 May 2025 06:45:40 +0200 Subject: [PATCH 3/3] path-util: invert PATH_STARTSWITH_ACCEPT_DOT_DOT flag As requested: https://github.com/systemd/systemd/pull/37572#pullrequestreview-2861928094 --- src/basic/cgroup-util.c | 2 +- src/basic/fs-util.c | 2 +- src/basic/mkdir.c | 2 +- src/basic/path-util.c | 4 ++-- src/basic/path-util.h | 4 ++-- src/core/cgroup.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index b68cab68a0..e11a6a3e49 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -976,7 +976,7 @@ int cg_shift_path(const char *cgroup, const char *root, const char **ret_shifted root = rt; } - *ret_shifted = path_startswith_full(cgroup, root, PATH_STARTSWITH_RETURN_LEADING_SLASH) ?: cgroup; + *ret_shifted = path_startswith_full(cgroup, root, PATH_STARTSWITH_RETURN_LEADING_SLASH|PATH_STARTSWITH_REFUSE_DOT_DOT) ?: cgroup; return 0; } diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index fd2312b543..5b83c78b24 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -67,7 +67,7 @@ int rmdir_parents(const char *path, const char *stop) { assert(*slash == '/'); *slash = '\0'; - if (path_startswith_full(stop, p, /* flags= */ 0)) + if (path_startswith_full(stop, p, PATH_STARTSWITH_REFUSE_DOT_DOT)) return 0; if (rmdir(p) < 0 && errno != ENOENT) diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 9c04942bd1..fc2d36b33d 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -151,7 +151,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui assert(_mkdirat != mkdirat); if (prefix) { - p = path_startswith_full(path, prefix, /* flags= */ 0); + p = path_startswith_full(path, prefix, PATH_STARTSWITH_REFUSE_DOT_DOT); if (!p) return -EINVAL; diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 5dd9b589f6..2fd568743a 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -456,11 +456,11 @@ char* path_startswith_full(const char *original_path, const char *prefix, PathSt const char *p, *q; int m, n; - m = path_find_first_component(&path, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &p); + m = path_find_first_component(&path, !FLAGS_SET(flags, PATH_STARTSWITH_REFUSE_DOT_DOT), &p); if (m < 0) return NULL; - n = path_find_first_component(&prefix, FLAGS_SET(flags, PATH_STARTSWITH_ACCEPT_DOT_DOT), &q); + n = path_find_first_component(&prefix, !FLAGS_SET(flags, PATH_STARTSWITH_REFUSE_DOT_DOT), &q); if (n < 0) return NULL; diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 172248577e..fabb0eac88 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -45,13 +45,13 @@ int path_make_relative(const char *from, const char *to, char **ret); int path_make_relative_parent(const char *from_child, const char *to, char **ret); typedef enum PathStartWithFlags { - PATH_STARTSWITH_ACCEPT_DOT_DOT = 1U << 0, + PATH_STARTSWITH_REFUSE_DOT_DOT = 1U << 0, PATH_STARTSWITH_RETURN_LEADING_SLASH = 1U << 1, } PathStartWithFlags; char* path_startswith_full(const char *path, const char *prefix, PathStartWithFlags flags) _pure_; static inline char* path_startswith(const char *path, const char *prefix) { - return path_startswith_full(path, prefix, PATH_STARTSWITH_ACCEPT_DOT_DOT); + return path_startswith_full(path, prefix, 0); } int path_compare(const char *a, const char *b) _pure_; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index e9dade8eb3..ed5d0f975d 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -2811,7 +2811,7 @@ static int unit_prune_cgroup_via_bus(Unit *u) { const char *pp = path_startswith_full( crt->cgroup_path, u->manager->cgroup_root, - PATH_STARTSWITH_RETURN_LEADING_SLASH); + PATH_STARTSWITH_RETURN_LEADING_SLASH|PATH_STARTSWITH_REFUSE_DOT_DOT); if (!pp) return -EINVAL;