diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index d8e94c3ed9..e11a6a3e49 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|PATH_STARTSWITH_REFUSE_DOT_DOT) ?: cgroup; return 0; } diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d306cc6d11..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, /* accept_dot_dot= */ false)) + 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 a44add67de..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, /* accept_dot_dot= */ false); + 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 64e42b2a23..2fd568743a 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_REFUSE_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_REFUSE_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..fabb0eac88 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_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, true); + 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 61a034641b..ed5d0f975d 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|PATH_STARTSWITH_REFUSE_DOT_DOT); 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", 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;