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