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