]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
path-util: add flavour of path_startswith() that leaves a leading slash in place
authorLennart Poettering <lennart@poettering.net>
Mon, 19 May 2025 10:58:52 +0000 (12:58 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 27 Feb 2026 21:57:05 +0000 (21:57 +0000)
(cherry picked from commit ee19edbb9f3455db3f750089082f3e5a925e3a0c)

src/basic/fs-util.c
src/basic/mkdir.c
src/basic/path-util.c
src/basic/path-util.h
src/test/test-path-util.c

index 96bf2676bc3b8fdbea207e48d984fc750d595793..a1ae0f7cca243b7b37ed4e7d78618bc567a062f8 100644 (file)
@@ -66,7 +66,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)
index 3e1545a58bba3fad552a3a393fb536c87a970911..6fc2a79944bc71865aa50e6cb0396ac9e0166dcd 100644 (file)
@@ -149,7 +149,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;
 
index 62603c81e9e0ef1440535bbde56d2e50890bb1d5..24f3cbcc18ffd91758a32ad0fefc99003cdccb0c 100644 (file)
@@ -405,8 +405,8 @@ char* path_simplify_full(char *path, PathSimplifyFlags flags) {
         return path;
 }
 
-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
@@ -419,28 +419,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;
         }
 }
index ae9e46df050b1c79c95a8ac80e2293b0bd8c30a9..8a82e1af76e4ad804c12d4577ac1304f6a2231e3 100644 (file)
@@ -53,9 +53,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_;
index e02bd8c857553b4340b698060f6f1b605cd4f49a..8aa477bd69928cd07fa0657689ee324be5606e5e 100644 (file)
@@ -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;
         const char *t;