]> 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)
committerLennart Poettering <lennart@poettering.net>
Fri, 23 May 2025 04:50:30 +0000 (06:50 +0200)
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 d306cc6d11285168aba4c12f17e54e443c0e055e..fd2312b543b6e66aa016fa37413019a71a65ed66 100644 (file)
@@ -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)
index a44add67de890a9b15022c9dbb9291d635e2c01c..9c04942bd136dc3f815f4482d41bfc09400a305b 100644 (file)
@@ -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;
 
index 64e42b2a23e48e0353015e76f6fb6a71fc94d60a..5dd9b589f6a003e3b2d9aad0597ffc2a40e6d4ce 100644 (file)
@@ -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;
         }
 }
index 671c1363c41acee7044aa572d11b6526cfc2975e..172248577ee3fcd03de12c4cd46275c374ec3542 100644 (file)
@@ -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_;
index aa4350e9678f0e83d93e91f460c33c71fe108d59..5928b2520da8e252a956545e146c5c8c41ca97ce 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;