chase: fix corner case when using CHASE_PARENT with a path ending in ".."

If we use CHASE_PARENT on a path ending in ".." then things are a bit
weird, because we the last path we look at is actually the *parent* and not
the *child* of the preceeding path. Hence we cannot just return the 2nd
to last fd we look at. We have to correct it, by going *two* levels up,
to get to the actual parent, and make sure CHASE_PARENT does what it
should.

Example: for the path /a/b/c chase() with CHASE_PARENT will return
/a/b/c as path, and the fd returned points to /a/b. All good.  But now,
for the path /a/b/c/.. chase() with CHASE_PARENT would previously return
/a/b as path (which is OK) but the fd would point to /a/b/c, which is
*not* the parent of /a/b, after all! To get to the actual parent of
/a/b we have to go *two* levels up to get to /a.

Very confusing. But that's what we here for, no?

@mrc0mmand ran into this in https://github.com/systemd/systemd/pull/28891#issuecomment-1782833722
This commit is contained in:
Lennart Poettering
2023-11-01 14:43:24 +00:00
committed by Luca Boccassi
parent 7c2fd96dba
commit 9c21cfdd7d
2 changed files with 48 additions and 1 deletions
+27
View File
@@ -721,6 +721,33 @@ TEST(chaseat_prefix_root) {
assert_se(streq(ret, expected));
}
TEST(trailing_dot_dot) {
_cleanup_free_ char *path = NULL, *fdpath = NULL;
_cleanup_close_ int fd = -EBADF;
assert_se(chase("/usr/..", NULL, CHASE_PARENT, &path, &fd) >= 0);
assert_se(path_equal(path, "/"));
assert_se(fd_get_path(fd, &fdpath) >= 0);
assert_se(path_equal(fdpath, "/"));
path = mfree(path);
fdpath = mfree(fdpath);
fd = safe_close(fd);
_cleanup_(rm_rf_physical_and_freep) char *t = NULL;
assert_se(mkdtemp_malloc(NULL, &t) >= 0);
_cleanup_free_ char *sub = ASSERT_PTR(path_join(t, "a/b/c/d"));
assert_se(mkdir_p(sub, 0700) >= 0);
_cleanup_free_ char *suffixed = ASSERT_PTR(path_join(sub, ".."));
assert_se(chase(suffixed, NULL, CHASE_PARENT, &path, &fd) >= 0);
_cleanup_free_ char *expected1 = ASSERT_PTR(path_join(t, "a/b/c"));
_cleanup_free_ char *expected2 = ASSERT_PTR(path_join(t, "a/b"));
assert_se(path_equal(path, expected1));
assert_se(fd_get_path(fd, &fdpath) >= 0);
assert_se(path_equal(fdpath, expected2));
}
static int intro(void) {
arg_test_dir = saved_argv[1];
return EXIT_SUCCESS;