]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
selftests: add tests for mntns iteration
authorChristian Brauner <brauner@kernel.org>
Thu, 12 Dec 2024 23:03:47 +0000 (00:03 +0100)
committerChristian Brauner <brauner@kernel.org>
Thu, 9 Jan 2025 15:58:53 +0000 (16:58 +0100)
Test that forward and backward iteration works correctly.

Link: https://lore.kernel.org/r/20241213-work-mount-rbtree-lockless-v3-8-6e3cdaf9b280@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
tools/testing/selftests/filesystems/nsfs/.gitignore
tools/testing/selftests/filesystems/nsfs/Makefile
tools/testing/selftests/filesystems/nsfs/iterate_mntns.c [new file with mode: 0644]

index ed79ebdf286e4d945cfbbf80fb072ba3e05c9112..92a8249006d1e0817800df0057183a94ef0f939d 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 owner
 pidns
+iterate_mntns
index c2f3ca6e488e9ddb49514e1b8e93909d5594259b..231aaa7dfd95c638c23e0a8e5a1d4f7f16f00f7b 100644 (file)
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
-TEST_GEN_PROGS := owner pidns
+TEST_GEN_PROGS := owner pidns iterate_mntns
 
 CFLAGS := -Wall -Werror
 
diff --git a/tools/testing/selftests/filesystems/nsfs/iterate_mntns.c b/tools/testing/selftests/filesystems/nsfs/iterate_mntns.c
new file mode 100644 (file)
index 0000000..457cf76
--- /dev/null
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (c) 2024 Christian Brauner <brauner@kernel.org>
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include "../../kselftest_harness.h"
+
+#define MNT_NS_COUNT 11
+#define MNT_NS_LAST_INDEX 10
+
+struct mnt_ns_info {
+       __u32 size;
+       __u32 nr_mounts;
+       __u64 mnt_ns_id;
+};
+
+#define MNT_NS_INFO_SIZE_VER0 16 /* size of first published struct */
+
+/* Get information about namespace. */
+#define NS_MNT_GET_INFO _IOR(0xb7, 10, struct mnt_ns_info)
+/* Get next namespace. */
+#define NS_MNT_GET_NEXT _IOR(0xb7, 11, struct mnt_ns_info)
+/* Get previous namespace. */
+#define NS_MNT_GET_PREV _IOR(0xb7, 12, struct mnt_ns_info)
+
+FIXTURE(iterate_mount_namespaces) {
+       int fd_mnt_ns[MNT_NS_COUNT];
+       __u64 mnt_ns_id[MNT_NS_COUNT];
+};
+
+FIXTURE_SETUP(iterate_mount_namespaces)
+{
+       for (int i = 0; i < MNT_NS_COUNT; i++)
+               self->fd_mnt_ns[i] = -EBADF;
+
+       /*
+        * Creating a new user namespace let's us guarantee that we only see
+        * mount namespaces that we did actually create.
+        */
+       ASSERT_EQ(unshare(CLONE_NEWUSER), 0);
+
+       for (int i = 0; i < MNT_NS_COUNT; i++) {
+               struct mnt_ns_info info = {};
+
+               ASSERT_EQ(unshare(CLONE_NEWNS), 0);
+               self->fd_mnt_ns[i] = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC);
+               ASSERT_GE(self->fd_mnt_ns[i], 0);
+               ASSERT_EQ(ioctl(self->fd_mnt_ns[i], NS_MNT_GET_INFO, &info), 0);
+               self->mnt_ns_id[i] = info.mnt_ns_id;
+       }
+}
+
+FIXTURE_TEARDOWN(iterate_mount_namespaces)
+{
+       for (int i = 0; i < MNT_NS_COUNT; i++) {
+               if (self->fd_mnt_ns[i] < 0)
+                       continue;
+               ASSERT_EQ(close(self->fd_mnt_ns[i]), 0);
+       }
+}
+
+TEST_F(iterate_mount_namespaces, iterate_all_forward)
+{
+       int fd_mnt_ns_cur, count = 0;
+
+       fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[0], F_DUPFD_CLOEXEC);
+       ASSERT_GE(fd_mnt_ns_cur, 0);
+
+       for (;; count++) {
+               struct mnt_ns_info info = {};
+               int fd_mnt_ns_next;
+
+               fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info);
+               if (fd_mnt_ns_next < 0 && errno == ENOENT)
+                       break;
+               ASSERT_GE(fd_mnt_ns_next, 0);
+               ASSERT_EQ(close(fd_mnt_ns_cur), 0);
+               fd_mnt_ns_cur = fd_mnt_ns_next;
+       }
+       ASSERT_EQ(count, MNT_NS_LAST_INDEX);
+}
+
+TEST_F(iterate_mount_namespaces, iterate_all_backwards)
+{
+       int fd_mnt_ns_cur, count = 0;
+
+       fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[MNT_NS_LAST_INDEX], F_DUPFD_CLOEXEC);
+       ASSERT_GE(fd_mnt_ns_cur, 0);
+
+       for (;; count++) {
+               struct mnt_ns_info info = {};
+               int fd_mnt_ns_prev;
+
+               fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info);
+               if (fd_mnt_ns_prev < 0 && errno == ENOENT)
+                       break;
+               ASSERT_GE(fd_mnt_ns_prev, 0);
+               ASSERT_EQ(close(fd_mnt_ns_cur), 0);
+               fd_mnt_ns_cur = fd_mnt_ns_prev;
+       }
+       ASSERT_EQ(count, MNT_NS_LAST_INDEX);
+}
+
+TEST_F(iterate_mount_namespaces, iterate_forward)
+{
+       int fd_mnt_ns_cur;
+
+       ASSERT_EQ(setns(self->fd_mnt_ns[0], CLONE_NEWNS), 0);
+
+       fd_mnt_ns_cur = self->fd_mnt_ns[0];
+       for (int i = 1; i < MNT_NS_COUNT; i++) {
+               struct mnt_ns_info info = {};
+               int fd_mnt_ns_next;
+
+               fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info);
+               ASSERT_GE(fd_mnt_ns_next, 0);
+               ASSERT_EQ(close(fd_mnt_ns_cur), 0);
+               fd_mnt_ns_cur = fd_mnt_ns_next;
+               ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]);
+       }
+}
+
+TEST_F(iterate_mount_namespaces, iterate_backward)
+{
+       int fd_mnt_ns_cur;
+
+       ASSERT_EQ(setns(self->fd_mnt_ns[MNT_NS_LAST_INDEX], CLONE_NEWNS), 0);
+
+       fd_mnt_ns_cur = self->fd_mnt_ns[MNT_NS_LAST_INDEX];
+       for (int i = MNT_NS_LAST_INDEX - 1; i >= 0; i--) {
+               struct mnt_ns_info info = {};
+               int fd_mnt_ns_prev;
+
+               fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info);
+               ASSERT_GE(fd_mnt_ns_prev, 0);
+               ASSERT_EQ(close(fd_mnt_ns_cur), 0);
+               fd_mnt_ns_cur = fd_mnt_ns_prev;
+               ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]);
+       }
+}
+
+TEST_HARNESS_MAIN