From 4a88b6aa86a7594555f31e39cf518a9ee486a87a Mon Sep 17 00:00:00 2001 From: Wang Siyuan Date: Fri, 26 Dec 2025 14:30:59 +0000 Subject: [PATCH] Add regression tests --- test/src/apps/pseudofs/pseudo_dentry.c | 59 ++++++++++++ test/src/apps/pseudofs/pseudo_file_cleanup.h | 21 +++++ test/src/apps/pseudofs/pseudo_file_create.h | 66 ++++++++++++++ test/src/apps/pseudofs/pseudo_inode.c | 95 ++++++-------------- test/src/apps/pseudofs/pseudo_mount.c | 60 +++++++++++++ test/src/apps/scripts/process.sh | 2 + 6 files changed, 234 insertions(+), 69 deletions(-) create mode 100644 test/src/apps/pseudofs/pseudo_dentry.c create mode 100644 test/src/apps/pseudofs/pseudo_file_cleanup.h create mode 100644 test/src/apps/pseudofs/pseudo_file_create.h create mode 100644 test/src/apps/pseudofs/pseudo_mount.c diff --git a/test/src/apps/pseudofs/pseudo_dentry.c b/test/src/apps/pseudofs/pseudo_dentry.c new file mode 100644 index 000000000..81746d7c3 --- /dev/null +++ b/test/src/apps/pseudofs/pseudo_dentry.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include "pseudo_file_create.h" + +static int readlink_check(int fd, const char *expect_prefix, int check_ino) +{ + char path[64]; + char buf[256]; + struct stat st; + + fd_path(fd, path, sizeof(path)); + ssize_t len = readlink(path, buf, sizeof(buf) - 1); + if (len < 0) + return -1; + buf[len] = '\0'; + + size_t prefix_len = strlen(expect_prefix); + if (strncmp(buf, expect_prefix, prefix_len) != 0) + return -1; + + if (check_ino) { + const char *ino_p = buf + prefix_len; + char *end = NULL; + + unsigned long ino = strtoul(ino_p, &end, 10); + if (end == ino_p || *end != ']' || *(end + 1) != '\0') + return -1; + if (fstat(fd, &st) < 0 || st.st_ino != ino) + return -1; + } + + return 0; +} + +FN_TEST(pseudo_dentry) +{ + TEST_RES(readlink_check(pipe_1[0], "pipe:[", 1), _ret == 0); + TEST_RES(readlink_check(pipe_1[1], "pipe:[", 1), _ret == 0); + + TEST_RES(readlink_check(sock[0], "socket:[", 1), _ret == 0); + TEST_RES(readlink_check(sock[1], "socket:[", 1), _ret == 0); + + TEST_RES(readlink_check(epoll_fd, "anon_inode:[eventpoll]", 0), + _ret == 0); + TEST_RES(readlink_check(event_fd, "anon_inode:[eventfd]", 0), + _ret == 0); + TEST_RES(readlink_check(timer_fd, "anon_inode:[timerfd]", 0), + _ret == 0); + TEST_RES(readlink_check(signal_fd, "anon_inode:[signalfd]", 0), + _ret == 0); + TEST_RES(readlink_check(inotify_fd, "anon_inode:inotify", 0), + _ret == 0); + TEST_RES(readlink_check(pid_fd, "anon_inode:[pidfd]", 0), _ret == 0); + + TEST_RES(readlink_check(mem_fd, "/memfd:test_memfd", 0), _ret == 0); +} +END_TEST() + +#include "pseudo_file_cleanup.h" diff --git a/test/src/apps/pseudofs/pseudo_file_cleanup.h b/test/src/apps/pseudofs/pseudo_file_cleanup.h new file mode 100644 index 000000000..3593662e3 --- /dev/null +++ b/test/src/apps/pseudofs/pseudo_file_cleanup.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MPL-2.0 + +FN_SETUP(cleanup) +{ + CHECK(close(pipe_1[0])); + CHECK(close(pipe_1[1])); + CHECK(close(pipe_2[0])); + CHECK(close(pipe_2[1])); + CHECK(close(sock[0])); + CHECK(close(sock[1])); + CHECK(close(epoll_fd)); + CHECK(close(event_fd)); + CHECK(close(timer_fd)); + CHECK(close(signal_fd)); + CHECK(close(inotify_fd)); + CHECK(close(pid_fd)); + CHECK(close(mem_fd)); + CHECK(kill(child, SIGKILL)); + CHECK(waitpid(child, NULL, 0)); +} +END_SETUP() \ No newline at end of file diff --git a/test/src/apps/pseudofs/pseudo_file_create.h b/test/src/apps/pseudofs/pseudo_file_create.h new file mode 100644 index 000000000..aefd9a460 --- /dev/null +++ b/test/src/apps/pseudofs/pseudo_file_create.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MPL-2.0 + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../test.h" + +int pipe_1[2], pipe_2[2]; +int sock[2]; +int epoll_fd, event_fd, timer_fd, signal_fd, inotify_fd, pid_fd, mem_fd; +pid_t child; + +FN_SETUP(create) +{ + CHECK(pipe(pipe_1)); + CHECK(pipe(pipe_2)); + CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sock)); + + epoll_fd = CHECK(epoll_create1(EPOLL_CLOEXEC)); + event_fd = CHECK(eventfd(0, EFD_CLOEXEC)); + timer_fd = CHECK(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)); + inotify_fd = CHECK(inotify_init1(0)); + mem_fd = CHECK(memfd_create("test_memfd", MFD_CLOEXEC)); + + sigset_t mask; + CHECK(sigemptyset(&mask)); + CHECK(sigaddset(&mask, SIGUSR1)); + signal_fd = CHECK(signalfd(-1, &mask, SFD_CLOEXEC)); + + child = CHECK(fork()); + if (child == 0) { + pause(); + exit(-1); + } + pid_fd = CHECK(syscall(SYS_pidfd_open, child, 0)); +} +END_SETUP() + +static void __attribute__((unused)) fd_path(int fd, char *buf, size_t buflen) +{ + CHECK_WITH(snprintf(buf, buflen, "/proc/self/fd/%d", fd), + _ret > 0 && _ret < buflen); +} + +static void __attribute__((unused)) +fdinfo_path(int fd, char *buf, size_t buflen) +{ + CHECK_WITH(snprintf(buf, buflen, "/proc/self/fdinfo/%d", fd), + _ret > 0 && _ret < buflen); +} diff --git a/test/src/apps/pseudofs/pseudo_inode.c b/test/src/apps/pseudofs/pseudo_inode.c index c188f4d79..d6a99b55a 100644 --- a/test/src/apps/pseudofs/pseudo_inode.c +++ b/test/src/apps/pseudofs/pseudo_inode.c @@ -1,28 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../test.h" - -static void fd_path(int fd, char *buf, size_t buflen) -{ - CHECK_WITH(snprintf(buf, buflen, "/proc/self/fd/%d", fd), - _ret > 0 && _ret < buflen); -} +#include "pseudo_file_create.h" static int get_mode(int fd) { @@ -46,29 +24,22 @@ static int set_mode(int fd, int mode) FN_TEST(pipe_ends_share_inode) { - int pipe1[2], pipe2[2]; - TEST_SUCC(pipe(pipe1)); - TEST_SUCC(pipe(pipe2)); + TEST_RES(get_mode(pipe_1[0]), _ret == 0600); + TEST_RES(get_mode(pipe_1[1]), _ret == 0600); + TEST_RES(get_mode(pipe_2[0]), _ret == 0600); + TEST_RES(get_mode(pipe_2[1]), _ret == 0600); - TEST_RES(get_mode(pipe1[0]), _ret == 0600); - TEST_RES(get_mode(pipe1[1]), _ret == 0600); - TEST_RES(get_mode(pipe2[0]), _ret == 0600); - TEST_RES(get_mode(pipe2[1]), _ret == 0600); + TEST_SUCC(set_mode(pipe_1[0], 0000)); - TEST_SUCC(set_mode(pipe1[0], 0000)); - - TEST_RES(get_mode(pipe1[0]), _ret == 0000); - TEST_RES(get_mode(pipe1[1]), _ret == 0000); - TEST_RES(get_mode(pipe2[0]), _ret == 0600); - TEST_RES(get_mode(pipe2[1]), _ret == 0600); + TEST_RES(get_mode(pipe_1[0]), _ret == 0000); + TEST_RES(get_mode(pipe_1[1]), _ret == 0000); + TEST_RES(get_mode(pipe_2[0]), _ret == 0600); + TEST_RES(get_mode(pipe_2[1]), _ret == 0600); } END_TEST() FN_TEST(sockets_do_not_share_inode) { - int sock[2]; - TEST_SUCC(socketpair(AF_UNIX, SOCK_STREAM, 0, sock)); - TEST_RES(get_mode(sock[0]), _ret == 0777); TEST_RES(get_mode(sock[1]), _ret == 0777); @@ -81,37 +52,23 @@ END_TEST() FN_TEST(anon_inodefs_share_inode) { - int fd; + struct fd_mode { + int fd; + mode_t modes[2]; + }; - // eventfd - fd = TEST_SUCC(eventfd(0, EFD_CLOEXEC)); - TEST_RES(get_mode(fd), _ret == 0600); - TEST_SUCC(set_mode(fd, 0000)); - TEST_RES(get_mode(fd), _ret == 0000); - TEST_SUCC(close(fd)); + struct fd_mode fds[] = { + { epoll_fd, { 0600, 0000 } }, { event_fd, { 0000, 0111 } }, + { timer_fd, { 0111, 0222 } }, { signal_fd, { 0222, 0333 } }, + { inotify_fd, { 0333, 0444 } }, { pid_fd, { 0444, 0600 } }, + }; - // timerfd - fd = TEST_SUCC(timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC)); - TEST_RES(get_mode(fd), _ret == 0000); - TEST_SUCC(set_mode(fd, 0111)); - TEST_RES(get_mode(fd), _ret == 0111); - TEST_SUCC(close(fd)); - - // signalfd - sigset_t mask; - TEST_SUCC(sigemptyset(&mask)); - TEST_SUCC(sigaddset(&mask, SIGUSR1)); - fd = TEST_SUCC(signalfd(-1, &mask, SFD_CLOEXEC)); - TEST_RES(get_mode(fd), _ret == 0111); - TEST_SUCC(set_mode(fd, 0222)); - TEST_RES(get_mode(fd), _ret == 0222); - TEST_SUCC(close(fd)); - - // epollfd - fd = TEST_SUCC(epoll_create1(EPOLL_CLOEXEC)); - TEST_RES(get_mode(fd), _ret == 0222); - TEST_SUCC(set_mode(fd, 0600)); - TEST_RES(get_mode(fd), _ret == 0600); - TEST_SUCC(close(fd)); + for (size_t i = 0; i < sizeof(fds) / sizeof(fds[0]); i++) { + TEST_RES(get_mode(fds[i].fd), _ret == fds[i].modes[0]); + TEST_SUCC(set_mode(fds[i].fd, fds[i].modes[1])); + TEST_RES(get_mode(fds[i].fd), _ret == fds[i].modes[1]); + } } END_TEST() + +#include "pseudo_file_cleanup.h" diff --git a/test/src/apps/pseudofs/pseudo_mount.c b/test/src/apps/pseudofs/pseudo_mount.c new file mode 100644 index 000000000..45580c5da --- /dev/null +++ b/test/src/apps/pseudofs/pseudo_mount.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include "pseudo_file_create.h" + +static int read_fdinfo_mnt_id(int fd) +{ + char path[64], line[256]; + FILE *f; + + fdinfo_path(fd, path, sizeof(path)); + f = fopen(path, "r"); + if (!f) + return -1; + + int mnt_id = -1; + while (fgets(line, sizeof(line), f)) { + if (CHECK(sscanf(line, "mnt_id:\t%d", &mnt_id)) == 1) + break; + } + CHECK(fclose(f)); + + return mnt_id; +} + +FN_TEST(pseudo_mount) +{ + int anon[] = { epoll_fd, event_fd, timer_fd, + signal_fd, inotify_fd, pid_fd }; + + struct fd_group { + int *fds; + int nr; + int mnt_id; + }; + + struct fd_group groups[] = { + { pipe_1, 2, -1 }, + { sock, 2, -1 }, + { anon, 6, -1 }, + { &mem_fd, 1, -1 }, + }; + + for (int i = 0; i < 4; i++) { + int base = TEST_SUCC(read_fdinfo_mnt_id(groups[i].fds[0])); + for (int j = 1; j < groups[i].nr; j++) { + TEST_RES(read_fdinfo_mnt_id(groups[i].fds[j]), + _ret == base); + } + groups[i].mnt_id = base; + } + + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 4; j++) { + TEST_RES(0, groups[i].mnt_id != groups[j].mnt_id); + } + } +} +END_TEST() + +#include "pseudo_file_cleanup.h" diff --git a/test/src/apps/scripts/process.sh b/test/src/apps/scripts/process.sh index eb19d7da9..adeb29332 100755 --- a/test/src/apps/scripts/process.sh +++ b/test/src/apps/scripts/process.sh @@ -51,7 +51,9 @@ process/pidfd process/wait4 procfs/dentry_cache procfs/pid_mem +pseudofs/pseudo_dentry pseudofs/pseudo_inode +pseudofs/pseudo_mount pseudofs/memfd_access_err pthread/pthread_test pty/close_pty