From 003ccba65072a74c18e3e45d4c1f8ea7346b2f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 21 Sep 2023 16:52:54 +0200 Subject: [PATCH] basic/path-util: make path_simplify() skip leading '/..' 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 | 16 +++++++++++---- src/test/test-path-util.c | 42 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 187fe2886b..b036191144 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -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; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index e8d6ff39e8..96a4fce630 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -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);