Re: [PATCH] selftests: add tests for mount notification

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Mar 07, 2025 at 09:40:45PM +0100, Miklos Szeredi wrote:
> Provide coverage for all mnt_notify_add() instances.
> 
> Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx>
> ---

Thank you! Tests are most excellent!

>  tools/testing/selftests/Makefile              |   1 +
>  .../filesystems/mount-notify/.gitignore       |   2 +
>  .../filesystems/mount-notify/Makefile         |   6 +
>  .../mount-notify/mount-notify_test.c          | 586 ++++++++++++++++++
>  .../filesystems/statmount/statmount.h         |   2 +-
>  5 files changed, 596 insertions(+), 1 deletion(-)
>  create mode 100644 tools/testing/selftests/filesystems/mount-notify/.gitignore
>  create mode 100644 tools/testing/selftests/filesystems/mount-notify/Makefile
>  create mode 100644 tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
> 
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index 8daac70c2f9d..2ebaf5e6942e 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -35,6 +35,7 @@ TARGETS += filesystems/epoll
>  TARGETS += filesystems/fat
>  TARGETS += filesystems/overlayfs
>  TARGETS += filesystems/statmount
> +TARGETS += filesystems/mount-notify
>  TARGETS += firmware
>  TARGETS += fpu
>  TARGETS += ftrace
> diff --git a/tools/testing/selftests/filesystems/mount-notify/.gitignore b/tools/testing/selftests/filesystems/mount-notify/.gitignore
> new file mode 100644
> index 000000000000..82a4846cbc4b
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/mount-notify/.gitignore
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +/*_test
> diff --git a/tools/testing/selftests/filesystems/mount-notify/Makefile b/tools/testing/selftests/filesystems/mount-notify/Makefile
> new file mode 100644
> index 000000000000..10be0227b5ae
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/mount-notify/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +
> +CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES)
> +TEST_GEN_PROGS := mount-notify_test
> +
> +include ../../lib.mk
> diff --git a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
> new file mode 100644
> index 000000000000..d39ff57bf163
> --- /dev/null
> +++ b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
> @@ -0,0 +1,586 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +// Copyright (c) 2025 Miklos Szeredi <miklos@xxxxxxxxxx>
> +
> +#define _GNU_SOURCE
> +#include <fcntl.h>
> +#include <sched.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/stat.h>
> +#include <sys/mount.h>
> +#include <linux/fanotify.h>
> +#include <unistd.h>
> +#include <sys/fanotify.h>
> +#include <sys/syscall.h>
> +
> +#include "../../kselftest_harness.h"
> +#include "../statmount/statmount.h"
> +
> +static char root_mntpoint[] = "/tmp/mount-notify_test_root.XXXXXX";
> +static int orig_root, ns_fd;
> +static uint64_t root_id;
> +
> +static uint64_t get_mnt_id(const char *path)
> +{
> +	struct statx sx;
> +	int ret;
> +
> +	ret = statx(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("retrieving mount ID");
> +
> +	if (!(sx.stx_mask & STATX_MNT_ID_UNIQUE))
> +		ksft_exit_fail_msg("no mount ID available\n");
> +
> +	return sx.stx_mnt_id;
> +}
> +
> +static void cleanup_namespace(void)
> +{
> +	int ret;
> +
> +	ret = fchdir(orig_root);
> +	if (ret == -1)
> +		ksft_perror("fchdir to original root");
> +
> +	ret = chroot(".");
> +	if (ret == -1)
> +		ksft_perror("chroot to original root");
> +
> +	umount2(root_mntpoint, MNT_DETACH);
> +	chdir(root_mntpoint);
> +	rmdir("a");
> +	rmdir("b");
> +	chdir("/");
> +	rmdir(root_mntpoint);
> +}
> +
> +static void setup_namespace(void)
> +{
> +	int ret;
> +
> +	ret = unshare(CLONE_NEWNS);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("unsharing mountns and userns");
> +
> +	ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
> +	if (ns_fd == -1)
> +		ksft_exit_fail_perror("opening /proc/self/ns/mnt");
> +
> +	ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("making mount tree private");
> +
> +	if (!mkdtemp(root_mntpoint))
> +		ksft_exit_fail_perror("creating temporary directory");
> +
> +	orig_root = open("/", O_PATH);
> +	if (orig_root == -1)
> +		ksft_exit_fail_perror("opening root directory");
> +
> +	atexit(cleanup_namespace);
> +
> +	ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("mounting temp root");
> +
> +	ret = chroot(root_mntpoint);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("chroot to temp root");
> +
> +	ret = chdir("/");
> +	if (ret == -1)
> +		ksft_exit_fail_perror("chdir to root");
> +
> +	ret = mkdir("a", 0700);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("mkdir(a)");
> +
> +	ret = mkdir("b", 0700);
> +	if (ret == -1)
> +		ksft_exit_fail_perror("mkdir(b)");
> +
> +	root_id = get_mnt_id("/");
> +}
> +
> +FIXTURE(fanotify) {
> +	int fan_fd;
> +	char buf[256];
> +	unsigned int rem;
> +	void *next;
> +};
> +
> +#define MAX_MNTS 256
> +#define MAX_PATH 256
> +
> +FIXTURE_SETUP(fanotify)
> +{
> +	uint64_t list[MAX_MNTS];
> +	ssize_t num;
> +	size_t bufsize = sizeof(struct statmount) + MAX_PATH;
> +	struct statmount *buf = alloca(bufsize);
> +	unsigned int i;
> +	int ret;
> +
> +	// Clean up mount tree
> +	ret = mount("", "/", NULL, MS_PRIVATE, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	num = listmount(LSMT_ROOT, 0, 0, list, MAX_MNTS, 0);
> +	ASSERT_GE(num, 1);
> +	ASSERT_LT(num, MAX_MNTS);
> +
> +	for (i = 0; i < num; i++) {
> +		if (list[i] == root_id)
> +			continue;
> +		ret = statmount(list[i], 0, STATMOUNT_MNT_POINT, buf, bufsize, 0);
> +		if (ret == 0 && buf->mask & STATMOUNT_MNT_POINT)
> +			umount2(buf->str + buf->mnt_point, MNT_DETACH);
> +	}
> +	num = listmount(LSMT_ROOT, 0, 0, list, 2, 0);
> +	ASSERT_EQ(num, 1);
> +	ASSERT_EQ(list[0], root_id);
> +
> +	mkdir("/a", 0700);
> +	mkdir("/b", 0700);
> +
> +	self->fan_fd = fanotify_init(FAN_REPORT_MNT, 0);
> +	ASSERT_GE(self->fan_fd, 0);
> +
> +	ret = fanotify_mark(self->fan_fd, FAN_MARK_ADD | FAN_MARK_MNTNS,
> +			    FAN_MNT_ATTACH | FAN_MNT_DETACH, ns_fd, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	self->rem = 0;
> +}
> +
> +FIXTURE_TEARDOWN(fanotify)
> +{
> +	ASSERT_EQ(self->rem, 0);
> +	close(self->fan_fd);
> +}
> +
> +static uint64_t expect_notify(struct __test_metadata *const _metadata,
> +			      FIXTURE_DATA(fanotify) *self,
> +			      uint64_t *mask)
> +{
> +	struct fanotify_event_metadata *meta;
> +	struct fanotify_event_info_mnt *mnt;
> +	unsigned int thislen;
> +
> +	if (!self->rem) {
> +		ssize_t len = read(self->fan_fd, self->buf, sizeof(self->buf));
> +		ASSERT_GT(len, 0);
> +
> +		self->rem = len;
> +		self->next = (void *) self->buf;
> +	}
> +
> +	meta = self->next;
> +	ASSERT_TRUE(FAN_EVENT_OK(meta, self->rem));
> +
> +	thislen = meta->event_len;
> +	self->rem -= thislen;
> +	self->next += thislen;
> +
> +	*mask = meta->mask;
> +	thislen -= sizeof(*meta);
> +
> +	mnt = ((void *) meta) + meta->event_len - thislen;
> +
> +	ASSERT_EQ(thislen, sizeof(*mnt));
> +
> +	return mnt->mnt_id;
> +}
> +
> +static void expect_notify_n(struct __test_metadata *const _metadata,
> +				 FIXTURE_DATA(fanotify) *self,
> +				 unsigned int n, uint64_t mask[], uint64_t mnts[])
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < n; i++)
> +		mnts[i] = expect_notify(_metadata, self, &mask[i]);
> +}
> +
> +static uint64_t expect_notify_mask(struct __test_metadata *const _metadata,
> +				   FIXTURE_DATA(fanotify) *self,
> +				   uint64_t expect_mask)
> +{
> +	uint64_t mntid, mask;
> +
> +	mntid = expect_notify(_metadata, self, &mask);
> +	ASSERT_EQ(expect_mask, mask);
> +
> +	return mntid;
> +}
> +
> +
> +static void expect_notify_mask_n(struct __test_metadata *const _metadata,
> +				 FIXTURE_DATA(fanotify) *self,
> +				 uint64_t mask, unsigned int n, uint64_t mnts[])
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < n; i++)
> +		mnts[i] = expect_notify_mask(_metadata, self, mask);
> +}
> +
> +
> +static void verify_mount_ids(struct __test_metadata *const _metadata,
> +			     const uint64_t list1[], const uint64_t list2[],
> +			     size_t num)
> +{
> +	unsigned int i, j;
> +
> +	// Check that neither list has any duplicates
> +	for (i = 0; i < num; i++) {
> +		for (j = 0; j < num; j++) {
> +			if (i != j) {
> +				ASSERT_NE(list1[i], list1[j]);
> +				ASSERT_NE(list2[i], list2[j]);
> +			}
> +		}
> +	}
> +	// Check that all list1 memebers can be found in list2. Together with
> +	// the above it means that the list1 and list2 represent the same sets.
> +	for (i = 0; i < num; i++) {
> +		for (j = 0; j < num; j++) {
> +			if (list1[i] == list2[j])
> +				break;
> +		}
> +		ASSERT_NE(j, num);
> +	}
> +}
> +
> +static void check_mounted(struct __test_metadata *const _metadata,
> +			  const uint64_t mnts[], size_t num)
> +{
> +	ssize_t ret;
> +	uint64_t *list;
> +
> +	list = malloc((num + 1) * sizeof(list[0]));
> +	ASSERT_NE(list, NULL);
> +
> +	ret = listmount(LSMT_ROOT, 0, 0, list, num + 1, 0);
> +	ASSERT_EQ(ret, num);
> +
> +	verify_mount_ids(_metadata, mnts, list, num);
> +
> +	free(list);
> +}
> +
> +static void setup_mount_tree(struct __test_metadata *const _metadata,
> +			    int log2_num)
> +{
> +	int ret, i;
> +
> +	ret = mount("", "/", NULL, MS_SHARED, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	for (i = 0; i < log2_num; i++) {
> +		ret = mount("/", "/", NULL, MS_BIND, NULL);
> +		ASSERT_EQ(ret, 0);
> +	}
> +}
> +
> +TEST_F(fanotify, bind)
> +{
> +	int ret;
> +	uint64_t mnts[2] = { root_id };
> +
> +	ret = mount("/", "/", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +	ASSERT_NE(mnts[0], mnts[1]);
> +
> +	check_mounted(_metadata, mnts, 2);
> +
> +	// Cleanup
> +	uint64_t detach_id;
> +	ret = umount("/");
> +	ASSERT_EQ(ret, 0);
> +
> +	detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
> +	ASSERT_EQ(detach_id, mnts[1]);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +TEST_F(fanotify, move)
> +{
> +	int ret;
> +	uint64_t mnts[2] = { root_id };
> +	uint64_t move_id;
> +
> +	ret = mount("/", "/a", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +	ASSERT_NE(mnts[0], mnts[1]);
> +
> +	check_mounted(_metadata, mnts, 2);
> +
> +	ret = move_mount(AT_FDCWD, "/a", AT_FDCWD, "/b", 0);
> +	ASSERT_EQ(ret, 0);
> +
> +	move_id = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH);
> +	ASSERT_EQ(move_id, mnts[1]);
> +
> +	// Cleanup
> +	ret = umount("/b");
> +	ASSERT_EQ(ret, 0);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +TEST_F(fanotify, propagate)
> +{
> +	const unsigned int log2_num = 4;
> +	const unsigned int num = (1 << log2_num);
> +	uint64_t mnts[num];
> +
> +	setup_mount_tree(_metadata, log2_num);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, num - 1, mnts + 1);
> +
> +	mnts[0] = root_id;
> +	check_mounted(_metadata, mnts, num);
> +
> +	// Cleanup
> +	int ret;
> +	uint64_t mnts2[num];
> +	ret = umount2("/", MNT_DETACH);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("", "/", NULL, MS_PRIVATE, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts2[0] = root_id;
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, num - 1, mnts2 + 1);
> +	verify_mount_ids(_metadata, mnts, mnts2, num);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +TEST_F(fanotify, fsmount)
> +{
> +	int ret, fs, mnt;
> +	uint64_t mnts[2] = { root_id };
> +
> +	fs = fsopen("tmpfs", 0);
> +	ASSERT_GE(fs, 0);
> +
> +        ret = fsconfig(fs, FSCONFIG_CMD_CREATE, 0, 0, 0);
> +	ASSERT_EQ(ret, 0);
> +
> +        mnt = fsmount(fs, 0, 0);
> +	ASSERT_GE(mnt, 0);
> +
> +        close(fs);
> +
> +	ret = move_mount(mnt, "", AT_FDCWD, "/a", MOVE_MOUNT_F_EMPTY_PATH);
> +	ASSERT_EQ(ret, 0);
> +
> +        close(mnt);
> +
> +	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +	ASSERT_NE(mnts[0], mnts[1]);
> +
> +	check_mounted(_metadata, mnts, 2);
> +
> +	// Cleanup
> +	uint64_t detach_id;
> +	ret = umount("/a");
> +	ASSERT_EQ(ret, 0);
> +
> +	detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
> +	ASSERT_EQ(detach_id, mnts[1]);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +TEST_F(fanotify, reparent)
> +{
> +	uint64_t mnts[6] = { root_id };
> +	uint64_t dmnts[3];
> +	uint64_t masks[3];
> +	unsigned int i;
> +	int ret;
> +
> +	// Create setup with a[1] -> b[2] propagation
> +	ret = mount("/", "/a", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("", "/a", NULL, MS_SHARED, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("/a", "/b", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("", "/b", NULL, MS_SLAVE, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
> +
> +	check_mounted(_metadata, mnts, 3);
> +
> +	// Mount on a[3], which is propagated to b[4]
> +	ret = mount("/", "/a", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 3);
> +
> +	check_mounted(_metadata, mnts, 5);
> +
> +	// Mount on b[5], not propagated
> +	ret = mount("/", "/b", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts[5] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +
> +	check_mounted(_metadata, mnts, 6);
> +
> +	// Umount a[3], which is propagated to b[4], but not b[5]
> +	// This will result in b[5] "falling" on b[2]
> +	ret = umount("/a");
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_n(_metadata, self, 3, masks, dmnts);
> +	verify_mount_ids(_metadata, mnts + 3, dmnts, 3);
> +
> +	for (i = 0; i < 3; i++) {
> +		if (dmnts[i] == mnts[5]) {
> +			ASSERT_EQ(masks[i], FAN_MNT_ATTACH | FAN_MNT_DETACH);
> +		} else {
> +			ASSERT_EQ(masks[i], FAN_MNT_DETACH);
> +		}
> +	}
> +
> +	mnts[3] = mnts[5];
> +	check_mounted(_metadata, mnts, 4);
> +
> +	// Cleanup
> +	ret = umount("/b");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = umount("/a");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = umount("/b");
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 3, dmnts);
> +	verify_mount_ids(_metadata, mnts + 1, dmnts, 3);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +TEST_F(fanotify, rmdir)
> +{
> +	uint64_t mnts[3] = { root_id };
> +	int ret;
> +
> +	ret = mount("/", "/a", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("/", "/a/b", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
> +
> +	check_mounted(_metadata, mnts, 3);
> +
> +	ret = chdir("/a");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = fork();
> +	ASSERT_GE(ret, 0);
> +
> +	if (ret == 0) {
> +		chdir("/");
> +		unshare(CLONE_NEWNS);
> +		mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
> +		umount2("/a", MNT_DETACH);
> +		// This triggers a detach in the other namespace
> +		rmdir("/a");
> +		exit(0);
> +	}
> +	wait(NULL);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 2, mnts + 1);
> +	check_mounted(_metadata, mnts, 1);
> +
> +	// Cleanup
> +	ret = chdir("/");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mkdir("a", 0700);
> +	ASSERT_EQ(ret, 0);
> +}
> +
> +TEST_F(fanotify, pivot_root)
> +{
> +	uint64_t mnts[3] = { root_id };
> +	uint64_t mnts2[3];
> +	int ret;
> +
> +	ret = mount("tmpfs", "/a", "tmpfs", 0, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts[2] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +
> +	ret = mkdir("/a/new", 0700);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mkdir("/a/old", 0700);
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = mount("/a", "/a/new", NULL, MS_BIND, NULL);
> +	ASSERT_EQ(ret, 0);
> +
> +	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
> +	check_mounted(_metadata, mnts, 3);
> +
> +	ret = syscall(SYS_pivot_root, "/a/new", "/a/new/old");
> +	ASSERT_EQ(ret, 0);
> +
> +	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH, 2, mnts2);
> +	verify_mount_ids(_metadata, mnts, mnts2, 2);
> +	check_mounted(_metadata, mnts, 3);
> +
> +	// Cleanup
> +	ret = syscall(SYS_pivot_root, "/old", "/old/a/new");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = umount("/a/new");
> +	ASSERT_EQ(ret, 0);
> +
> +	ret = umount("/a");
> +	ASSERT_EQ(ret, 0);
> +
> +	check_mounted(_metadata, mnts, 1);
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int ret;
> +
> +	ksft_print_header();
> +
> +	if (geteuid())
> +		ksft_exit_skip("mount notify requires root privileges\n");
> +
> +	ret = fanotify_init(FAN_REPORT_MNT, 0);
> +	if (ret == -1) {
> +		if (errno == EINVAL)
> +			ksft_exit_skip("FAN_REPORT_MNT not supported\n");
> +		ksft_exit_fail_perror("fanotify_init(FAN_REPORT_MNT, 0)");
> +	}
> +	close(ret);
> +
> +	setup_namespace();
> +
> +	return test_harness_run(argc, argv);
> +}

Thanks for using the TEST_* framework instead of the old one! It's way
easier to extend and has better behavior! But you don't need a main
function. You can just use:

TEST_HARNESS_MAIN

setup_namespace() can just be called on each FIXTURE_SETUP() invocation
and cleanup_namespace() on each FIXTURE_TEARDOWN(). They will always
start with a clean slate this way.

There's some build errors with missing defines when the kheaders aren't
installed. I don't like making this a test-run prerequisite so I'm
adding defines when FAN_MNT_ATTACH etc aren't defined.

I've fixed that all up. I'm appending the changes I folded in.
Afterwards I get:

user1@localhost:~/data/kernel/linux/tools/testing/selftests/filesystems/mount-notify$ sudo ./mount-notify_test
TAP version 13
1..7
# Starting 7 tests from 1 test cases.
#  RUN           fanotify.bind ...
#            OK  fanotify.bind
ok 1 fanotify.bind
#  RUN           fanotify.move ...
#            OK  fanotify.move
ok 2 fanotify.move
#  RUN           fanotify.propagate ...
#            OK  fanotify.propagate
ok 3 fanotify.propagate
#  RUN           fanotify.fsmount ...
#            OK  fanotify.fsmount
ok 4 fanotify.fsmount
#  RUN           fanotify.reparent ...
#            OK  fanotify.reparent
ok 5 fanotify.reparent
#  RUN           fanotify.rmdir ...
#            OK  fanotify.rmdir
ok 6 fanotify.rmdir
#  RUN           fanotify.pivot_root ...
#            OK  fanotify.pivot_root
ok 7 fanotify.pivot_root
# PASSED: 7 / 7 tests passed.
# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0

> diff --git a/tools/testing/selftests/filesystems/statmount/statmount.h b/tools/testing/selftests/filesystems/statmount/statmount.h
> index f4294bab9d73..a7a5289ddae9 100644
> --- a/tools/testing/selftests/filesystems/statmount/statmount.h
> +++ b/tools/testing/selftests/filesystems/statmount/statmount.h
> @@ -25,7 +25,7 @@ static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask,
>  	return syscall(__NR_statmount, &req, buf, bufsize, flags);
>  }
>  
> -static ssize_t listmount(uint64_t mnt_id, uint64_t mnt_ns_id,
> +static inline ssize_t listmount(uint64_t mnt_id, uint64_t mnt_ns_id,
>  			 uint64_t last_mnt_id, uint64_t list[], size_t num,
>  			 unsigned int flags)
>  {
> -- 
> 2.48.1
> 
commit d67dff33e14af26f37114d0cd3c846816360f809
Author:     Christian Brauner <brauner@xxxxxxxxxx>
AuthorDate: Sat Mar 8 12:47:29 2025 +0100
Commit:     Christian Brauner <brauner@xxxxxxxxxx>
CommitDate: Sat Mar 8 13:07:26 2025 +0100

    folded changes
    
    Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>

diff --git a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
index 7fa2e376e516..2f0bd360166d 100644
--- a/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
+++ b/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c
@@ -16,11 +16,11 @@
 #include "../../kselftest_harness.h"
 #include "../statmount/statmount.h"
 
-#ifndef FAN_PRE_ACCESS
-#define FAN_PRE_ACCESS 0x00100000 /* Pre-content access hook */
-#endif
-
 #ifndef FAN_MNT_ATTACH
+struct fanotify_event_info_mnt {
+	struct fanotify_event_info_header hdr;
+	__u64 mnt_id;
+};
 #define FAN_MNT_ATTACH 0x01000000 /* Mount was attached */
 #endif
 
@@ -32,91 +32,64 @@
 #define FAN_REPORT_MNT 0x00004000 /* Report mount events */
 #endif
 
+#ifndef FAN_MARK_MNTNS
+#define FAN_MARK_MNTNS 0x00000110
+#endif
+
 static char root_mntpoint[] = "/tmp/mount-notify_test_root.XXXXXX";
 static int orig_root, ns_fd;
 static uint64_t root_id;
 
-static uint64_t get_mnt_id(const char *path)
+static uint64_t get_mnt_id(struct __test_metadata *const _metadata,
+			   const char *path)
 {
 	struct statx sx;
-	int ret;
-
-	ret = statx(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx);
-	if (ret == -1)
-		ksft_exit_fail_perror("retrieving mount ID");
-
-	if (!(sx.stx_mask & STATX_MNT_ID_UNIQUE))
-		ksft_exit_fail_msg("no mount ID available\n");
 
+	ASSERT_EQ(statx(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx), 0);
+	ASSERT_TRUE(!!(sx.stx_mask & STATX_MNT_ID_UNIQUE));
 	return sx.stx_mnt_id;
 }
 
-static void cleanup_namespace(void)
+static void cleanup_namespace(struct __test_metadata *const _metadata)
 {
-	int ret;
-
-	ret = fchdir(orig_root);
-	if (ret == -1)
-		ksft_perror("fchdir to original root");
+	ASSERT_EQ(fchdir(orig_root), 0);
 
-	ret = chroot(".");
-	if (ret == -1)
-		ksft_perror("chroot to original root");
+	ASSERT_EQ(chroot("."), 0);
 
-	umount2(root_mntpoint, MNT_DETACH);
-	chdir(root_mntpoint);
-	rmdir("a");
-	rmdir("b");
-	chdir("/");
-	rmdir(root_mntpoint);
+	EXPECT_EQ(umount2(root_mntpoint, MNT_DETACH), 0);
+	EXPECT_EQ(chdir(root_mntpoint), 0);
+	EXPECT_EQ(rmdir("a"), 0);
+	EXPECT_EQ(rmdir("b"), 0);
+	EXPECT_EQ(chdir("/"), 0);
+	EXPECT_EQ(rmdir(root_mntpoint), 0);
 }
 
-static void setup_namespace(void)
+static void setup_namespace(struct __test_metadata *const _metadata)
 {
-	int ret;
-
-	ret = unshare(CLONE_NEWNS);
-	if (ret == -1)
-		ksft_exit_fail_perror("unsharing mountns and userns");
+	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
 
 	ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
-	if (ns_fd == -1)
-		ksft_exit_fail_perror("opening /proc/self/ns/mnt");
+	ASSERT_GE(ns_fd, 0);
 
-	ret = mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
-	if (ret == -1)
-		ksft_exit_fail_perror("making mount tree private");
+	ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0);
 
-	if (!mkdtemp(root_mntpoint))
-		ksft_exit_fail_perror("creating temporary directory");
+	ASSERT_NE(mkdtemp(root_mntpoint), NULL);
 
-	orig_root = open("/", O_PATH);
-	if (orig_root == -1)
-		ksft_exit_fail_perror("opening root directory");
+	orig_root = open("/", O_PATH | O_CLOEXEC);
+	ASSERT_GE(orig_root, 0);
 
-	atexit(cleanup_namespace);
+	ASSERT_EQ(mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL), 0);
 
-	ret = mount(root_mntpoint, root_mntpoint, NULL, MS_BIND, NULL);
-	if (ret == -1)
-		ksft_exit_fail_perror("mounting temp root");
+	ASSERT_EQ(chroot(root_mntpoint), 0);
 
-	ret = chroot(root_mntpoint);
-	if (ret == -1)
-		ksft_exit_fail_perror("chroot to temp root");
+	ASSERT_EQ(chdir("/"), 0);
 
-	ret = chdir("/");
-	if (ret == -1)
-		ksft_exit_fail_perror("chdir to root");
+	ASSERT_EQ(mkdir("a", 0700), 0);
 
-	ret = mkdir("a", 0700);
-	if (ret == -1)
-		ksft_exit_fail_perror("mkdir(a)");
-
-	ret = mkdir("b", 0700);
-	if (ret == -1)
-		ksft_exit_fail_perror("mkdir(b)");
+	ASSERT_EQ(mkdir("b", 0700), 0);
 
-	root_id = get_mnt_id("/");
+	root_id = get_mnt_id(_metadata, "/");
+	ASSERT_NE(root_id, 0);
 }
 
 FIXTURE(fanotify) {
@@ -138,6 +111,8 @@ FIXTURE_SETUP(fanotify)
 	unsigned int i;
 	int ret;
 
+	setup_namespace(_metadata);
+
 	// Clean up mount tree
 	ret = mount("", "/", NULL, MS_PRIVATE, NULL);
 	ASSERT_EQ(ret, 0);
@@ -172,6 +147,7 @@ FIXTURE_SETUP(fanotify)
 
 FIXTURE_TEARDOWN(fanotify)
 {
+	cleanup_namespace(_metadata);
 	ASSERT_EQ(self->rem, 0);
 	close(self->fan_fd);
 }
@@ -579,24 +555,4 @@ TEST_F(fanotify, pivot_root)
 	check_mounted(_metadata, mnts, 1);
 }
 
-int main(int argc, char *argv[])
-{
-	int ret;
-
-	ksft_print_header();
-
-	if (geteuid())
-		ksft_exit_skip("mount notify requires root privileges\n");
-
-	ret = fanotify_init(FAN_REPORT_MNT, 0);
-	if (ret == -1) {
-		if (errno == EINVAL)
-			ksft_exit_skip("FAN_REPORT_MNT not supported\n");
-		ksft_exit_fail_perror("fanotify_init(FAN_REPORT_MNT, 0)");
-	}
-	close(ret);
-
-	setup_namespace();
-
-	return test_harness_run(argc, argv);
-}
+TEST_HARNESS_MAIN

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux