[PATCH 4/4 v5] selftests: add devpts selftests
This adds tests to check: - bind-mounts from /dev/pts/ptmx to /dev/ptmx work - non-standard mounts of devpts work - bind-mounts of /dev/pts/ptmx to locations that do not resolve to a valid slave pty path under the originating devpts mount fail Signed-off-by: Christian Brauner--- ChangeLog v4->v5: * extend tests to verify failure on ptmx devices located outside the devpts mount without a common ancestor directory ChangeLog v3->v4: * patch unchanged ChangeLog v2->v3: * extend test for non-standard devpts mounts such as mount -t devpts e devpts /mnt ChangeLog v1->v2: * patch added ChangeLog v0->v1: * patch not present --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 2 +- tools/testing/selftests/filesystems/devpts_pts.c | 313 +++ 4 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/filesystems/devpts_pts.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7442dfb73b7f..dbda89c9d9b9 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -7,6 +7,7 @@ TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += efivarfs TARGETS += exec +TARGETS += filesystems TARGETS += firmware TARGETS += ftrace TARGETS += futex diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 31d6e426b6d4..8449cf6716ce 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -1 +1,2 @@ dnotify_test +devpts_pts diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index 13a73bf725b5..4e6d09fb166f 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := dnotify_test +TEST_PROGS := dnotify_test devpts_pts all: $(TEST_PROGS) include ../lib.mk diff --git a/tools/testing/selftests/filesystems/devpts_pts.c b/tools/testing/selftests/filesystems/devpts_pts.c new file mode 100644 index ..b9055e974289 --- /dev/null +++ b/tools/testing/selftests/filesystems/devpts_pts.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool terminal_dup2(int duplicate, int original) +{ + int ret; + + ret = dup2(duplicate, original); + if (ret < 0) + return false; + + return true; +} + +static int terminal_set_stdfds(int fd) +{ + int i; + + if (fd < 0) + return 0; + + for (i = 0; i < 3; i++) + if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO, + STDERR_FILENO}[i])) + return -1; + + return 0; +} + +static int login_pty(int fd) +{ + int ret; + + setsid(); + + ret = ioctl(fd, TIOCSCTTY, NULL); + if (ret < 0) + return -1; + + ret = terminal_set_stdfds(fd); + if (ret < 0) + return -1; + + if (fd > STDERR_FILENO) + close(fd); + + return 0; +} + +static int wait_for_pid(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, , 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + return -1; + } + if (ret != pid) + goto again; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +static int resolve_procfd_symlink(int fd, char *buf, size_t buflen) +{ + int ret; + char procfd[4096]; + + ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd); + if (ret < 0 || ret >= 4096) + return -1; + + ret = readlink(procfd, buf, buflen); + if (ret < 0 || (size_t)ret >= buflen) + return -1; + + buf[ret] = '\0'; + + return 0; +} + +static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents) +{ + int ret; + int master = -1, slave = -1, fret = -1; + + master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (master < 0) { + fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx, + strerror(errno)); + return -1; + } + + /* +* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also +* not really needed. +*/ + ret = unlockpt(master); + if (ret < 0) { + fprintf(stderr, "Failed to unlock terminal\n"); + goto do_cleanup; + } + +#ifdef TIOCGPTPEER + slave = ioctl(master, TIOCGPTPEER,
[PATCH 4/4 v5] selftests: add devpts selftests
This adds tests to check: - bind-mounts from /dev/pts/ptmx to /dev/ptmx work - non-standard mounts of devpts work - bind-mounts of /dev/pts/ptmx to locations that do not resolve to a valid slave pty path under the originating devpts mount fail Signed-off-by: Christian Brauner --- ChangeLog v4->v5: * extend tests to verify failure on ptmx devices located outside the devpts mount without a common ancestor directory ChangeLog v3->v4: * patch unchanged ChangeLog v2->v3: * extend test for non-standard devpts mounts such as mount -t devpts e devpts /mnt ChangeLog v1->v2: * patch added ChangeLog v0->v1: * patch not present --- tools/testing/selftests/Makefile | 1 + tools/testing/selftests/filesystems/.gitignore | 1 + tools/testing/selftests/filesystems/Makefile | 2 +- tools/testing/selftests/filesystems/devpts_pts.c | 313 +++ 4 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/filesystems/devpts_pts.c diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 7442dfb73b7f..dbda89c9d9b9 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -7,6 +7,7 @@ TARGETS += cpufreq TARGETS += cpu-hotplug TARGETS += efivarfs TARGETS += exec +TARGETS += filesystems TARGETS += firmware TARGETS += ftrace TARGETS += futex diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore index 31d6e426b6d4..8449cf6716ce 100644 --- a/tools/testing/selftests/filesystems/.gitignore +++ b/tools/testing/selftests/filesystems/.gitignore @@ -1 +1,2 @@ dnotify_test +devpts_pts diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile index 13a73bf725b5..4e6d09fb166f 100644 --- a/tools/testing/selftests/filesystems/Makefile +++ b/tools/testing/selftests/filesystems/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -TEST_PROGS := dnotify_test +TEST_PROGS := dnotify_test devpts_pts all: $(TEST_PROGS) include ../lib.mk diff --git a/tools/testing/selftests/filesystems/devpts_pts.c b/tools/testing/selftests/filesystems/devpts_pts.c new file mode 100644 index ..b9055e974289 --- /dev/null +++ b/tools/testing/selftests/filesystems/devpts_pts.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static bool terminal_dup2(int duplicate, int original) +{ + int ret; + + ret = dup2(duplicate, original); + if (ret < 0) + return false; + + return true; +} + +static int terminal_set_stdfds(int fd) +{ + int i; + + if (fd < 0) + return 0; + + for (i = 0; i < 3; i++) + if (!terminal_dup2(fd, (int[]){STDIN_FILENO, STDOUT_FILENO, + STDERR_FILENO}[i])) + return -1; + + return 0; +} + +static int login_pty(int fd) +{ + int ret; + + setsid(); + + ret = ioctl(fd, TIOCSCTTY, NULL); + if (ret < 0) + return -1; + + ret = terminal_set_stdfds(fd); + if (ret < 0) + return -1; + + if (fd > STDERR_FILENO) + close(fd); + + return 0; +} + +static int wait_for_pid(pid_t pid) +{ + int status, ret; + +again: + ret = waitpid(pid, , 0); + if (ret == -1) { + if (errno == EINTR) + goto again; + return -1; + } + if (ret != pid) + goto again; + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) + return -1; + + return 0; +} + +static int resolve_procfd_symlink(int fd, char *buf, size_t buflen) +{ + int ret; + char procfd[4096]; + + ret = snprintf(procfd, 4096, "/proc/self/fd/%d", fd); + if (ret < 0 || ret >= 4096) + return -1; + + ret = readlink(procfd, buf, buflen); + if (ret < 0 || (size_t)ret >= buflen) + return -1; + + buf[ret] = '\0'; + + return 0; +} + +static int do_tiocgptpeer(char *ptmx, char *expected_procfd_contents) +{ + int ret; + int master = -1, slave = -1, fret = -1; + + master = open(ptmx, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (master < 0) { + fprintf(stderr, "Failed to open \"%s\": %s\n", ptmx, + strerror(errno)); + return -1; + } + + /* +* grantpt() makes assumptions about /dev/pts/ so ignore it. It's also +* not really needed. +*/ + ret = unlockpt(master); + if (ret < 0) { + fprintf(stderr, "Failed to unlock terminal\n"); + goto do_cleanup; + } + +#ifdef TIOCGPTPEER + slave = ioctl(master, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);