[LTP] [PATCH STAGING v2 09/16] fchroot06: test path walks under failfs root
Andrea Cervesato <[email protected]>
| Newsgroups | it.linux.lists.ltp |
|---|---|
| Message-ID | <[email protected]> |
From: Andrea Cervesato <[email protected]> Verify that once the root is in failfs only lookups anchored at a file descriptor keep working: relative lookups from the working directory and from a pre-opened directory fd succeed, absolute symlinks fail with EOPNOTSUPP and ".." walks clamp at the top of the mount tree, landing on the real root. Signed-off-by: Andrea Cervesato <[email protected]> --- runtest/staging | 1 + testcases/kernel/syscalls/fchroot/.gitignore | 1 + testcases/kernel/syscalls/fchroot/fchroot06.c | 117 ++++++++++++++++++++++++++ 3 files changed, 119 insertions(+) diff --git a/runtest/staging b/runtest/staging index 49f51d637..8b6b1ecd9 100644 --- a/runtest/staging +++ b/runtest/staging @@ -5,3 +5,4 @@ fchroot02 fchroot02 fchroot03 fchroot03 fchroot04 fchroot04 fchroot05 fchroot05 +fchroot06 fchroot06 diff --git a/testcases/kernel/syscalls/fchroot/.gitignore b/testcases/kernel/syscalls/fchroot/.gitignore index 0697f10eb..12151270a 100644 --- a/testcases/kernel/syscalls/fchroot/.gitignore +++ b/testcases/kernel/syscalls/fchroot/.gitignore @@ -3,3 +3,4 @@ fchroot02 fchroot03 fchroot04 fchroot05 +fchroot06 diff --git a/testcases/kernel/syscalls/fchroot/fchroot06.c b/testcases/kernel/syscalls/fchroot/fchroot06.c new file mode 100644 index 000000000..bf6c4fea4 --- /dev/null +++ b/testcases/kernel/syscalls/fchroot/fchroot06.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2026 SUSE LLC Andrea Cervesato <[email protected]> + */ + +/*\ + * Test path walks under the failfs root. + * + * Once :manpage:`fchroot(2)` moved the process root into failfs, only + * lookups anchored at a file descriptor keep working: + * + * - lookups relative to the working directory, which stays in the real + * filesystem, keep working + * - lookups anchored at a pre-opened directory fd keep working, including + * resolution of relative symlinks + * - absolute symlinks restart the walk at the failfs root and fail with + * ``EOPNOTSUPP`` + * - ".." walks clamp at the top of the mount tree, not at the failfs + * root, so walking up from the working directory lands on the real + * root + * + * Root is required because entering failfs with the ``FD_FAILFS_ROOT`` + * sentinel requires ``CAP_SYS_CHROOT``. + * + * The test runs in a forked child so the root of the parent process is + * left untouched. + */ + +#define _GNU_SOURCE +#include <fcntl.h> +#include <sys/stat.h> +#include "tst_test.h" +#include "lapi/fcntl.h" +#include "lapi/syscalls.h" +#include "tst_safe_file_at.h" + +#define RELDIR "rel" +#define ABSDIR "abs" + +static char upwards[PATH_MAX]; + +static void run(void) +{ + if (SAFE_FORK()) + return; + + struct stat realroot, st; + int dfd, fd; + + SAFE_STAT("/", &realroot); + dfd = SAFE_OPEN(".", O_RDONLY | O_DIRECTORY); + + TST_EXP_PASS(tst_syscall(__NR_fchroot, FD_FAILFS_ROOT, 0), + "fchroot() with the FD_FAILFS_ROOT sentinel"); + + fd = SAFE_OPENAT(AT_FDCWD, ".", O_RDONLY | O_DIRECTORY); + SAFE_CLOSE(fd); + + fd = SAFE_OPENAT(dfd, "canary", O_WRONLY | O_CREAT, 0600); + SAFE_WRITE(SAFE_WRITE_ALL, fd, "x", 1); + SAFE_CLOSE(fd); + + fd = SAFE_OPENAT(dfd, RELDIR, O_RDONLY); + SAFE_CLOSE(fd); + + TST_EXP_FAIL2(openat(dfd, ABSDIR, O_RDONLY), EOPNOTSUPP, + "resolution of an absolute symlink"); + + fd = SAFE_OPENAT(AT_FDCWD, upwards, O_PATH); + SAFE_FSTAT(fd, &st); + SAFE_CLOSE(fd); + + TST_EXP_EXPR(st.st_dev == realroot.st_dev && + st.st_ino == realroot.st_ino, + "'..' walk clamps at the top of the mount tree"); + + SAFE_CLOSE(dfd); + + exit(0); +} + +static void setup(void) +{ + char *tmpdir; + char abs_path[PATH_MAX]; + struct stat root_st, st; + int fd, off; + + tmpdir = tst_tmpdir_path(); + snprintf(abs_path, sizeof(abs_path), "%s/%s", tmpdir, "target"); + + SAFE_TOUCH("target", 0644, NULL); + SAFE_SYMLINK("target", RELDIR); + SAFE_SYMLINK(abs_path, ABSDIR); + + SAFE_STAT("/", &root_st); + + off = snprintf(upwards, sizeof(upwards), ".."); + while (1) { + fd = SAFE_OPENAT(AT_FDCWD, upwards, O_PATH); + SAFE_FSTAT(fd, &st); + SAFE_CLOSE(fd); + + if (st.st_dev == root_st.st_dev && st.st_ino == root_st.st_ino) + break; + + off += snprintf(upwards + off, sizeof(upwards) - off, "/.."); + } +} + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_root = 1, + .needs_tmpdir = 1, + .forks_child = 1, +}; -- 2.51.0 -- Mailing list info: https://lists.linux.it/listinfo/ltp