xref: /linux/tools/testing/selftests/filesystems/mount-notify/mount-notify_test.c (revision 3191df0a4882c827cac29925e80ecb1775b904bd)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 // Copyright (c) 2025 Miklos Szeredi <miklos@szeredi.hu>
3 
4 #define _GNU_SOURCE
5 
6 // Needed for linux/fanotify.h
7 typedef struct {
8 	int	val[2];
9 } __kernel_fsid_t;
10 #define __kernel_fsid_t __kernel_fsid_t
11 
12 #include <fcntl.h>
13 #include <sched.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <sys/mount.h>
18 #include <unistd.h>
19 #include <sys/syscall.h>
20 #include <sys/fanotify.h>
21 
22 #include "../../kselftest_harness.h"
23 #include "../statmount/statmount.h"
24 #include "../utils.h"
25 
26 static const char root_mntpoint_templ[] = "/tmp/mount-notify_test_root.XXXXXX";
27 
28 static const int mark_cmds[] = {
29 	FAN_MARK_ADD,
30 	FAN_MARK_REMOVE,
31 	FAN_MARK_FLUSH
32 };
33 
34 #define NUM_FAN_FDS ARRAY_SIZE(mark_cmds)
35 
36 FIXTURE(fanotify) {
37 	int fan_fd[NUM_FAN_FDS];
38 	char buf[256];
39 	unsigned int rem;
40 	void *next;
41 	char root_mntpoint[sizeof(root_mntpoint_templ)];
42 	int orig_root;
43 	int ns_fd;
44 	uint64_t root_id;
45 };
46 
47 FIXTURE_SETUP(fanotify)
48 {
49 	int i, ret;
50 
51 	ASSERT_EQ(unshare(CLONE_NEWNS), 0);
52 
53 	self->ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
54 	ASSERT_GE(self->ns_fd, 0);
55 
56 	ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0);
57 
58 	strcpy(self->root_mntpoint, root_mntpoint_templ);
59 	ASSERT_NE(mkdtemp(self->root_mntpoint), NULL);
60 
61 	self->orig_root = open("/", O_PATH | O_CLOEXEC);
62 	ASSERT_GE(self->orig_root, 0);
63 
64 	ASSERT_EQ(mount("tmpfs", self->root_mntpoint, "tmpfs", 0, NULL), 0);
65 
66 	ASSERT_EQ(chroot(self->root_mntpoint), 0);
67 
68 	ASSERT_EQ(chdir("/"), 0);
69 
70 	ASSERT_EQ(mkdir("a", 0700), 0);
71 
72 	ASSERT_EQ(mkdir("b", 0700), 0);
73 
74 	self->root_id = get_unique_mnt_id("/");
75 	ASSERT_NE(self->root_id, 0);
76 
77 	for (i = 0; i < NUM_FAN_FDS; i++) {
78 		self->fan_fd[i] = fanotify_init(FAN_REPORT_MNT | FAN_NONBLOCK,
79 						0);
80 		ASSERT_GE(self->fan_fd[i], 0);
81 		ret = fanotify_mark(self->fan_fd[i], FAN_MARK_ADD |
82 				    FAN_MARK_MNTNS,
83 				    FAN_MNT_ATTACH | FAN_MNT_DETACH,
84 				    self->ns_fd, NULL);
85 		ASSERT_EQ(ret, 0);
86 		// On fd[0] we do an extra ADD that changes nothing.
87 		// On fd[1]/fd[2] we REMOVE/FLUSH which removes the mark.
88 		ret = fanotify_mark(self->fan_fd[i], mark_cmds[i] |
89 				    FAN_MARK_MNTNS,
90 				    FAN_MNT_ATTACH | FAN_MNT_DETACH,
91 				    self->ns_fd, NULL);
92 		ASSERT_EQ(ret, 0);
93 	}
94 
95 	self->rem = 0;
96 }
97 
98 FIXTURE_TEARDOWN(fanotify)
99 {
100 	int i;
101 
102 	ASSERT_EQ(self->rem, 0);
103 	for (i = 0; i < NUM_FAN_FDS; i++)
104 		close(self->fan_fd[i]);
105 
106 	ASSERT_EQ(fchdir(self->orig_root), 0);
107 
108 	ASSERT_EQ(chroot("."), 0);
109 
110 	EXPECT_EQ(umount2(self->root_mntpoint, MNT_DETACH), 0);
111 	EXPECT_EQ(chdir(self->root_mntpoint), 0);
112 	EXPECT_EQ(chdir("/"), 0);
113 	EXPECT_EQ(rmdir(self->root_mntpoint), 0);
114 }
115 
116 static uint64_t expect_notify(struct __test_metadata *const _metadata,
117 			      FIXTURE_DATA(fanotify) *self,
118 			      uint64_t *mask)
119 {
120 	struct fanotify_event_metadata *meta;
121 	struct fanotify_event_info_mnt *mnt;
122 	unsigned int thislen;
123 
124 	if (!self->rem) {
125 		ssize_t len;
126 		int i;
127 
128 		for (i = NUM_FAN_FDS - 1; i >= 0; i--) {
129 			len = read(self->fan_fd[i], self->buf,
130 				   sizeof(self->buf));
131 			if (i > 0) {
132 				// Groups 1,2 should get EAGAIN
133 				ASSERT_EQ(len, -1);
134 				ASSERT_EQ(errno, EAGAIN);
135 			} else {
136 				// Group 0 should get events
137 				ASSERT_GT(len, 0);
138 			}
139 		}
140 
141 		self->rem = len;
142 		self->next = (void *) self->buf;
143 	}
144 
145 	meta = self->next;
146 	ASSERT_TRUE(FAN_EVENT_OK(meta, self->rem));
147 
148 	thislen = meta->event_len;
149 	self->rem -= thislen;
150 	self->next += thislen;
151 
152 	*mask = meta->mask;
153 	thislen -= sizeof(*meta);
154 
155 	mnt = ((void *) meta) + meta->event_len - thislen;
156 
157 	ASSERT_EQ(thislen, sizeof(*mnt));
158 
159 	return mnt->mnt_id;
160 }
161 
162 static void expect_notify_n(struct __test_metadata *const _metadata,
163 				 FIXTURE_DATA(fanotify) *self,
164 				 unsigned int n, uint64_t mask[], uint64_t mnts[])
165 {
166 	unsigned int i;
167 
168 	for (i = 0; i < n; i++)
169 		mnts[i] = expect_notify(_metadata, self, &mask[i]);
170 }
171 
172 static uint64_t expect_notify_mask(struct __test_metadata *const _metadata,
173 				   FIXTURE_DATA(fanotify) *self,
174 				   uint64_t expect_mask)
175 {
176 	uint64_t mntid, mask;
177 
178 	mntid = expect_notify(_metadata, self, &mask);
179 	ASSERT_EQ(expect_mask, mask);
180 
181 	return mntid;
182 }
183 
184 
185 static void expect_notify_mask_n(struct __test_metadata *const _metadata,
186 				 FIXTURE_DATA(fanotify) *self,
187 				 uint64_t mask, unsigned int n, uint64_t mnts[])
188 {
189 	unsigned int i;
190 
191 	for (i = 0; i < n; i++)
192 		mnts[i] = expect_notify_mask(_metadata, self, mask);
193 }
194 
195 static void verify_mount_ids(struct __test_metadata *const _metadata,
196 			     const uint64_t list1[], const uint64_t list2[],
197 			     size_t num)
198 {
199 	unsigned int i, j;
200 
201 	// Check that neither list has any duplicates
202 	for (i = 0; i < num; i++) {
203 		for (j = 0; j < num; j++) {
204 			if (i != j) {
205 				ASSERT_NE(list1[i], list1[j]);
206 				ASSERT_NE(list2[i], list2[j]);
207 			}
208 		}
209 	}
210 	// Check that all list1 memebers can be found in list2. Together with
211 	// the above it means that the list1 and list2 represent the same sets.
212 	for (i = 0; i < num; i++) {
213 		for (j = 0; j < num; j++) {
214 			if (list1[i] == list2[j])
215 				break;
216 		}
217 		ASSERT_NE(j, num);
218 	}
219 }
220 
221 static void check_mounted(struct __test_metadata *const _metadata,
222 			  const uint64_t mnts[], size_t num)
223 {
224 	ssize_t ret;
225 	uint64_t *list;
226 
227 	list = malloc((num + 1) * sizeof(list[0]));
228 	ASSERT_NE(list, NULL);
229 
230 	ret = listmount(LSMT_ROOT, 0, 0, list, num + 1, 0);
231 	ASSERT_EQ(ret, num);
232 
233 	verify_mount_ids(_metadata, mnts, list, num);
234 
235 	free(list);
236 }
237 
238 static void setup_mount_tree(struct __test_metadata *const _metadata,
239 			    int log2_num)
240 {
241 	int ret, i;
242 
243 	ret = mount("", "/", NULL, MS_SHARED, NULL);
244 	ASSERT_EQ(ret, 0);
245 
246 	for (i = 0; i < log2_num; i++) {
247 		ret = mount("/", "/", NULL, MS_BIND, NULL);
248 		ASSERT_EQ(ret, 0);
249 	}
250 }
251 
252 TEST_F(fanotify, bind)
253 {
254 	int ret;
255 	uint64_t mnts[2] = { self->root_id };
256 
257 	ret = mount("/", "/", NULL, MS_BIND, NULL);
258 	ASSERT_EQ(ret, 0);
259 
260 	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
261 	ASSERT_NE(mnts[0], mnts[1]);
262 
263 	check_mounted(_metadata, mnts, 2);
264 
265 	// Cleanup
266 	uint64_t detach_id;
267 	ret = umount("/");
268 	ASSERT_EQ(ret, 0);
269 
270 	detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
271 	ASSERT_EQ(detach_id, mnts[1]);
272 
273 	check_mounted(_metadata, mnts, 1);
274 }
275 
276 TEST_F(fanotify, move)
277 {
278 	int ret;
279 	uint64_t mnts[2] = { self->root_id };
280 	uint64_t move_id;
281 
282 	ret = mount("/", "/a", NULL, MS_BIND, NULL);
283 	ASSERT_EQ(ret, 0);
284 
285 	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
286 	ASSERT_NE(mnts[0], mnts[1]);
287 
288 	check_mounted(_metadata, mnts, 2);
289 
290 	ret = move_mount(AT_FDCWD, "/a", AT_FDCWD, "/b", 0);
291 	ASSERT_EQ(ret, 0);
292 
293 	move_id = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH);
294 	ASSERT_EQ(move_id, mnts[1]);
295 
296 	// Cleanup
297 	ret = umount("/b");
298 	ASSERT_EQ(ret, 0);
299 
300 	check_mounted(_metadata, mnts, 1);
301 }
302 
303 TEST_F(fanotify, propagate)
304 {
305 	const unsigned int log2_num = 4;
306 	const unsigned int num = (1 << log2_num);
307 	uint64_t mnts[num];
308 
309 	setup_mount_tree(_metadata, log2_num);
310 
311 	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, num - 1, mnts + 1);
312 
313 	mnts[0] = self->root_id;
314 	check_mounted(_metadata, mnts, num);
315 
316 	// Cleanup
317 	int ret;
318 	uint64_t mnts2[num];
319 	ret = umount2("/", MNT_DETACH);
320 	ASSERT_EQ(ret, 0);
321 
322 	ret = mount("", "/", NULL, MS_PRIVATE, NULL);
323 	ASSERT_EQ(ret, 0);
324 
325 	mnts2[0] = self->root_id;
326 	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, num - 1, mnts2 + 1);
327 	verify_mount_ids(_metadata, mnts, mnts2, num);
328 
329 	check_mounted(_metadata, mnts, 1);
330 }
331 
332 TEST_F(fanotify, fsmount)
333 {
334 	int ret, fs, mnt;
335 	uint64_t mnts[2] = { self->root_id };
336 
337 	fs = fsopen("tmpfs", 0);
338 	ASSERT_GE(fs, 0);
339 
340         ret = fsconfig(fs, FSCONFIG_CMD_CREATE, 0, 0, 0);
341 	ASSERT_EQ(ret, 0);
342 
343         mnt = fsmount(fs, 0, 0);
344 	ASSERT_GE(mnt, 0);
345 
346         close(fs);
347 
348 	ret = move_mount(mnt, "", AT_FDCWD, "/a", MOVE_MOUNT_F_EMPTY_PATH);
349 	ASSERT_EQ(ret, 0);
350 
351         close(mnt);
352 
353 	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
354 	ASSERT_NE(mnts[0], mnts[1]);
355 
356 	check_mounted(_metadata, mnts, 2);
357 
358 	// Cleanup
359 	uint64_t detach_id;
360 	ret = umount("/a");
361 	ASSERT_EQ(ret, 0);
362 
363 	detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH);
364 	ASSERT_EQ(detach_id, mnts[1]);
365 
366 	check_mounted(_metadata, mnts, 1);
367 }
368 
369 TEST_F(fanotify, reparent)
370 {
371 	uint64_t mnts[6] = { self->root_id };
372 	uint64_t dmnts[3];
373 	uint64_t masks[3];
374 	unsigned int i;
375 	int ret;
376 
377 	// Create setup with a[1] -> b[2] propagation
378 	ret = mount("/", "/a", NULL, MS_BIND, NULL);
379 	ASSERT_EQ(ret, 0);
380 
381 	ret = mount("", "/a", NULL, MS_SHARED, NULL);
382 	ASSERT_EQ(ret, 0);
383 
384 	ret = mount("/a", "/b", NULL, MS_BIND, NULL);
385 	ASSERT_EQ(ret, 0);
386 
387 	ret = mount("", "/b", NULL, MS_SLAVE, NULL);
388 	ASSERT_EQ(ret, 0);
389 
390 	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
391 
392 	check_mounted(_metadata, mnts, 3);
393 
394 	// Mount on a[3], which is propagated to b[4]
395 	ret = mount("/", "/a", NULL, MS_BIND, NULL);
396 	ASSERT_EQ(ret, 0);
397 
398 	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 3);
399 
400 	check_mounted(_metadata, mnts, 5);
401 
402 	// Mount on b[5], not propagated
403 	ret = mount("/", "/b", NULL, MS_BIND, NULL);
404 	ASSERT_EQ(ret, 0);
405 
406 	mnts[5] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
407 
408 	check_mounted(_metadata, mnts, 6);
409 
410 	// Umount a[3], which is propagated to b[4], but not b[5]
411 	// This will result in b[5] "falling" on b[2]
412 	ret = umount("/a");
413 	ASSERT_EQ(ret, 0);
414 
415 	expect_notify_n(_metadata, self, 3, masks, dmnts);
416 	verify_mount_ids(_metadata, mnts + 3, dmnts, 3);
417 
418 	for (i = 0; i < 3; i++) {
419 		if (dmnts[i] == mnts[5]) {
420 			ASSERT_EQ(masks[i], FAN_MNT_ATTACH | FAN_MNT_DETACH);
421 		} else {
422 			ASSERT_EQ(masks[i], FAN_MNT_DETACH);
423 		}
424 	}
425 
426 	mnts[3] = mnts[5];
427 	check_mounted(_metadata, mnts, 4);
428 
429 	// Cleanup
430 	ret = umount("/b");
431 	ASSERT_EQ(ret, 0);
432 
433 	ret = umount("/a");
434 	ASSERT_EQ(ret, 0);
435 
436 	ret = umount("/b");
437 	ASSERT_EQ(ret, 0);
438 
439 	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 3, dmnts);
440 	verify_mount_ids(_metadata, mnts + 1, dmnts, 3);
441 
442 	check_mounted(_metadata, mnts, 1);
443 }
444 
445 TEST_F(fanotify, rmdir)
446 {
447 	uint64_t mnts[3] = { self->root_id };
448 	int ret;
449 
450 	ret = mount("/", "/a", NULL, MS_BIND, NULL);
451 	ASSERT_EQ(ret, 0);
452 
453 	ret = mount("/", "/a/b", NULL, MS_BIND, NULL);
454 	ASSERT_EQ(ret, 0);
455 
456 	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1);
457 
458 	check_mounted(_metadata, mnts, 3);
459 
460 	ret = chdir("/a");
461 	ASSERT_EQ(ret, 0);
462 
463 	ret = fork();
464 	ASSERT_GE(ret, 0);
465 
466 	if (ret == 0) {
467 		chdir("/");
468 		unshare(CLONE_NEWNS);
469 		mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL);
470 		umount2("/a", MNT_DETACH);
471 		// This triggers a detach in the other namespace
472 		rmdir("/a");
473 		exit(0);
474 	}
475 	wait(NULL);
476 
477 	expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 2, mnts + 1);
478 	check_mounted(_metadata, mnts, 1);
479 
480 	// Cleanup
481 	ret = chdir("/");
482 	ASSERT_EQ(ret, 0);
483 }
484 
485 TEST_F(fanotify, pivot_root)
486 {
487 	uint64_t mnts[3] = { self->root_id };
488 	uint64_t mnts2[3];
489 	int ret;
490 
491 	ret = mount("tmpfs", "/a", "tmpfs", 0, NULL);
492 	ASSERT_EQ(ret, 0);
493 
494 	mnts[2] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
495 
496 	ret = mkdir("/a/new", 0700);
497 	ASSERT_EQ(ret, 0);
498 
499 	ret = mkdir("/a/old", 0700);
500 	ASSERT_EQ(ret, 0);
501 
502 	ret = mount("/a", "/a/new", NULL, MS_BIND, NULL);
503 	ASSERT_EQ(ret, 0);
504 
505 	mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH);
506 	check_mounted(_metadata, mnts, 3);
507 
508 	ret = syscall(SYS_pivot_root, "/a/new", "/a/new/old");
509 	ASSERT_EQ(ret, 0);
510 
511 	expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH, 2, mnts2);
512 	verify_mount_ids(_metadata, mnts, mnts2, 2);
513 	check_mounted(_metadata, mnts, 3);
514 
515 	// Cleanup
516 	ret = syscall(SYS_pivot_root, "/old", "/old/a/new");
517 	ASSERT_EQ(ret, 0);
518 
519 	ret = umount("/a/new");
520 	ASSERT_EQ(ret, 0);
521 
522 	ret = umount("/a");
523 	ASSERT_EQ(ret, 0);
524 
525 	check_mounted(_metadata, mnts, 1);
526 }
527 
528 TEST_HARNESS_MAIN
529