]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
basic/path-util: make path_simplify() skip leading '/..'
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 21 Sep 2023 14:52:54 +0000 (16:52 +0200)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 21 Sep 2023 16:01:03 +0000 (18:01 +0200)
I.e., /.. becomes /, /../foo becomes /foo, /../../bar becomes /bar, etc. We can
do this unconditionally, without access to the file system, because the parent
of the root directory always resolves to. /.. in other places is handled as
before, because resolving it properly would require access to the file system
which we don't want to do in path_simplify().

src/basic/path-util.c
src/test/test-path-util.c

index 187fe2886baf0c5045da7a6fcb2e4828de6efbed..b036191144666112e7651a23397453b58f79a635 100644 (file)
@@ -345,7 +345,7 @@ char** path_strv_resolve_uniq(char **l, const char *root) {
 }
 
 char* path_simplify_full(char *path, PathSimplifyFlags flags) {
-        bool add_slash = false, keep_trailing_slash;
+        bool add_slash = false, keep_trailing_slash, absolute, beginning = true;
         char *f = ASSERT_PTR(path);
         int r;
 
@@ -354,6 +354,8 @@ char* path_simplify_full(char *path, PathSimplifyFlags flags) {
          *
          * ///foo//./bar/.   becomes /foo/bar
          * .//./foo//./bar/. becomes foo/bar
+         * /../foo/bar       becomes /foo/bar
+         * /../foo/bar/..    becomes /foo/bar/..
          */
 
         if (isempty(path))
@@ -361,8 +363,8 @@ char* path_simplify_full(char *path, PathSimplifyFlags flags) {
 
         keep_trailing_slash = FLAGS_SET(flags, PATH_SIMPLIFY_KEEP_TRAILING_SLASH) && endswith(path, "/");
 
-        if (path_is_absolute(path))
-                f++;
+        absolute = path_is_absolute(path);
+        f += absolute;  /* Keep leading /, if present. */
 
         for (const char *p = f;;) {
                 const char *e;
@@ -371,11 +373,17 @@ char* path_simplify_full(char *path, PathSimplifyFlags flags) {
                 if (r == 0)
                         break;
 
+                if (r > 0 && absolute && beginning && path_startswith(e, ".."))
+                        /* If we're at the beginning of an absolute path, we can safely skip ".." */
+                        continue;
+
+                beginning = false;
+
                 if (add_slash)
                         *f++ = '/';
 
                 if (r < 0) {
-                        /* if path is invalid, then refuse to simplify remaining part. */
+                        /* if path is invalid, then refuse to simplify the remaining part. */
                         memmove(f, p, strlen(p) + 1);
                         return path;
                 }
index e8d6ff39e81cae792191848165d423a0db906e80..96a4fce630492ecfccb43726ab706569a481268c 100644 (file)
@@ -158,7 +158,7 @@ TEST(path_simplify) {
         test_path_simplify_one("///", "/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
         test_path_simplify_one("///.//", "/", 0);
         test_path_simplify_one("///.//.///", "/", 0);
-        test_path_simplify_one("////.././///../.", "/../..", 0);
+        test_path_simplify_one("////.././///../.", "/", 0);
         test_path_simplify_one(".", ".", 0);
         test_path_simplify_one("./", ".", 0);
         test_path_simplify_one("./", "./", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
@@ -174,6 +174,46 @@ TEST(path_simplify) {
                                "../aaa/.bbb/../c./d.dd/..eeee/..", 0);
         test_path_simplify_one("abc///", "abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
 
+        test_path_simplify_one("/../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../abc///", "/abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../abc///", "/abc", 0);
+        test_path_simplify_one("/../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../abc///..", "/abc/..", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../abc///../", "/abc/../", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../abc///../", "/abc/..", 0);
+
+        test_path_simplify_one("/../../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../../abc///", "/abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../../abc///", "/abc", 0);
+        test_path_simplify_one("/../../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../../abc///../..", "/abc/../..", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../../abc///../../", "/abc/../../", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/../../abc///../../", "/abc/../..", 0);
+
+        test_path_simplify_one("/.././../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.././../abc///", "/abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.././../abc///", "/abc", 0);
+        test_path_simplify_one("/.././../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.././../abc///../..", "/abc/../..", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.././../abc///../../", "/abc/../../", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.././../abc///../../", "/abc/../..", 0);
+
+        test_path_simplify_one("/./.././../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/./.././../abc///", "/abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/./.././../abc///", "/abc", 0);
+        test_path_simplify_one("/./.././../abc", "/abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/./.././../abc///../..", "/abc/../..", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/./.././../abc///../../", "/abc/../../", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/./.././../abc///../../", "/abc/../..", 0);
+
+        test_path_simplify_one("/.../abc", "/.../abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.../abc///", "/.../abc/", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.../abc///", "/.../abc", 0);
+        test_path_simplify_one("/.../abc", "/.../abc", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.../abc///...", "/.../abc/...", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.../abc///.../", "/.../abc/.../", PATH_SIMPLIFY_KEEP_TRAILING_SLASH);
+        test_path_simplify_one("/.../abc///.../", "/.../abc/...", 0);
+
         memset(foo, 'a', sizeof(foo) -1);
         char_array_0(foo);