From: Lennart Poettering Date: Mon, 19 May 2025 10:58:52 +0000 (+0200) Subject: path-util: add flavour of path_startswith() that leaves a leading slash in place X-Git-Tag: v258-rc1~512^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ee19edbb9f3455db3f750089082f3e5a925e3a0c;p=thirdparty%2Fsystemd.git path-util: add flavour of path_startswith() that leaves a leading slash in place --- diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index d306cc6d112..fd2312b543b 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 a44add67de8..9c04942bd13 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 64e42b2a23e..5dd9b589f6a 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 (FLAGS_SET(flags, PATH_STARTSWITH_RETURN_LEADING_SLASH)) { + + if (p <= original_path) + return NULL; + + p--; + + if (*p != '/') + return NULL; + } + + return (char*) p; + } - if (r != k) + 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 671c1363c41..172248577ee 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 aa4350e9678..5928b2520da 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;