xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/fanotify/fanotify10.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2014 SUSE.  All Rights Reserved.
4  * Copyright (c) 2018 CTERA Networks.  All Rights Reserved.
5  *
6  * Started by Jan Kara <[email protected]>
7  * Forked from fanotify06.c by Amir Goldstein <[email protected]>
8  */
9 
10 /*\
11  * [Description]
12  * Check that fanotify properly merges ignore mask of a mount mark
13  * with a mask of an inode mark on the same group.  Unlike the
14  * prototype test fanotify06, do not use FAN_MODIFY event for the
15  * test mask, because it hides the bug.
16  */
17 
18 /*
19  * This is a regression test for commit:
20  *
21  *     9bdda4e9cf2d fsnotify: fix ignore mask logic in fsnotify()
22  *
23  * Test case #16 is a regression test for commit:
24  *
25  *     2f02fd3fa13e fanotify: fix ignore mask logic for events on child...
26  *
27  * Test cases with 'ignored_onchild' are regression tests for commit
28  * (from v5.9, unlikely to be backported thus not in .tags):
29  *
30  *     497b0c5a7c06 fsnotify: send event to parent and child with single...
31  */
32 
33 #define _GNU_SOURCE
34 #include "config.h"
35 
36 #include <stdio.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <errno.h>
41 #include <string.h>
42 #include <stdlib.h>
43 #include <sys/mount.h>
44 #include <sys/syscall.h>
45 #include "tst_test.h"
46 #include "tst_safe_stdio.h"
47 
48 #ifdef HAVE_SYS_FANOTIFY_H
49 #include "fanotify.h"
50 
51 #define EVENT_MAX 1024
52 /* size of the event structure, not counting name */
53 #define EVENT_SIZE  (sizeof(struct fanotify_event_metadata))
54 /* reasonable guess as to size of 1024 events */
55 #define EVENT_BUF_LEN        (EVENT_MAX * EVENT_SIZE)
56 
57 static unsigned int fanotify_class[] = {
58 	FAN_CLASS_PRE_CONTENT,
59 	FAN_CLASS_CONTENT,
60 	FAN_CLASS_NOTIF,
61 	/* Reporting dfid+name+fid merges events similar to reporting fd */
62 	FAN_REPORT_DFID_NAME_FID,
63 };
64 #define NUM_CLASSES ARRAY_SIZE(fanotify_class)
65 #define NUM_PRIORITIES 3
66 
67 #define GROUPS_PER_PRIO 3
68 
69 static int fd_notify[NUM_CLASSES][GROUPS_PER_PRIO];
70 static int fd_syncfs;
71 
72 static char event_buf[EVENT_BUF_LEN];
73 static int event_buf_pos, event_buf_len;
74 static int exec_events_unsupported;
75 static int fan_report_dfid_unsupported;
76 static int filesystem_mark_unsupported;
77 static int evictable_mark_unsupported;
78 static int ignore_mark_unsupported;
79 
80 #define MOUNT_PATH "fs_mnt"
81 #define MNT2_PATH "mntpoint"
82 #define DIR_NAME "testdir"
83 #define FILE_NAME "testfile"
84 #define FILE2_NAME "testfile2"
85 #define SUBDIR_NAME "testdir2"
86 #define TEST_APP "fanotify_child"
87 #define TEST_APP2 "fanotify_child2"
88 #define DIR_PATH MOUNT_PATH"/"DIR_NAME
89 #define DIR_PATH_MULTI DIR_PATH"%d"
90 #define FILE_PATH DIR_PATH"/"FILE_NAME
91 #define FILE_PATH_MULTI FILE_PATH"%d"
92 #define FILE_PATH_MULTIDIR DIR_PATH_MULTI"/"FILE_NAME
93 #define FILE2_PATH DIR_PATH"/"FILE2_NAME
94 #define SUBDIR_PATH DIR_PATH"/"SUBDIR_NAME
95 #define FILE_EXEC_PATH MOUNT_PATH"/"TEST_APP
96 #define FILE2_EXEC_PATH MOUNT_PATH"/"TEST_APP2
97 #define DIR_MNT2 MNT2_PATH"/"DIR_NAME
98 #define FILE_MNT2 DIR_MNT2"/"FILE_NAME
99 #define FILE2_MNT2 DIR_MNT2"/"FILE2_NAME
100 #define FILE_EXEC_PATH2 MNT2_PATH"/"TEST_APP
101 #define FILE2_EXEC_PATH2 MNT2_PATH"/"TEST_APP2
102 
103 #define DROP_CACHES_FILE "/proc/sys/vm/drop_caches"
104 #define CACHE_PRESSURE_FILE "/proc/sys/vm/vfs_cache_pressure"
105 
106 static int old_cache_pressure;
107 static pid_t child_pid;
108 static int bind_mount_created;
109 static unsigned int num_classes = NUM_CLASSES;
110 static int max_file_multi;
111 
112 enum {
113 	FANOTIFY_INODE,
114 	FANOTIFY_PARENT,
115 	FANOTIFY_SUBDIR,
116 	FANOTIFY_MOUNT,
117 	FANOTIFY_FILESYSTEM,
118 	FANOTIFY_EVICTABLE,
119 };
120 
121 static struct fanotify_mark_type fanotify_mark_types[] = {
122 	INIT_FANOTIFY_MARK_TYPE(INODE),
123 	INIT_FANOTIFY_MARK_TYPE(PARENT),
124 	INIT_FANOTIFY_MARK_TYPE(SUBDIR),
125 	INIT_FANOTIFY_MARK_TYPE(MOUNT),
126 	INIT_FANOTIFY_MARK_TYPE(FILESYSTEM),
127 	INIT_FANOTIFY_MARK_TYPE(EVICTABLE),
128 };
129 
130 static struct tcase {
131 	const char *tname;
132 	int mark_path_cnt;
133 	const char *mark_path_fmt;
134 	int mark_type;
135 	int ignore_path_cnt;
136 	const char *ignore_path_fmt;
137 	int ignore_mark_type;
138 	unsigned int ignored_flags;
139 	int event_path_cnt;
140 	const char *event_path_fmt;
141 	unsigned long long expected_mask_with_ignore;
142 	unsigned long long expected_mask_without_ignore;
143 } tcases[] = {
144 	{
145 		.tname = "ignore mount events created on a specific file",
146 		.mark_path_fmt = MOUNT_PATH,
147 		.mark_type = FANOTIFY_MOUNT,
148 		.ignore_path_fmt = FILE_MNT2,
149 		.ignore_mark_type = FANOTIFY_INODE,
150 		.event_path_fmt = FILE_PATH,
151 		.expected_mask_without_ignore = FAN_OPEN
152 	},
153 	{
154 		.tname = "ignore exec mount events created on a specific file",
155 		.mark_path_fmt = MOUNT_PATH,
156 		.mark_type = FANOTIFY_MOUNT,
157 		.ignore_path_fmt = FILE_EXEC_PATH2,
158 		.ignore_mark_type = FANOTIFY_INODE,
159 		.event_path_fmt = FILE_EXEC_PATH,
160 		.expected_mask_with_ignore = FAN_OPEN_EXEC,
161 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
162 	},
163 	{
164 		.tname = "don't ignore mount events created on another file",
165 		.mark_path_fmt = MOUNT_PATH,
166 		.mark_type = FANOTIFY_MOUNT,
167 		.ignore_path_fmt = FILE_PATH,
168 		.ignore_mark_type = FANOTIFY_INODE,
169 		.event_path_fmt = FILE2_PATH,
170 		.expected_mask_with_ignore = FAN_OPEN,
171 		.expected_mask_without_ignore = FAN_OPEN
172 	},
173 	{
174 		.tname = "don't ignore exec mount events created on another file",
175 		.mark_path_fmt = MOUNT_PATH,
176 		.mark_type = FANOTIFY_MOUNT,
177 		.ignore_path_fmt = FILE_EXEC_PATH,
178 		.ignore_mark_type = FANOTIFY_INODE,
179 		.event_path_fmt = FILE2_EXEC_PATH,
180 		.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
181 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
182 	},
183 	{
184 		.tname = "ignore inode events created on a specific mount point",
185 		.mark_path_fmt = FILE_PATH,
186 		.mark_type = FANOTIFY_INODE,
187 		.ignore_path_fmt = MNT2_PATH,
188 		.ignore_mark_type = FANOTIFY_MOUNT,
189 		.event_path_fmt = FILE_MNT2,
190 		.expected_mask_without_ignore = FAN_OPEN
191 	},
192 	{
193 		.tname = "ignore exec inode events created on a specific mount point",
194 		.mark_path_fmt = FILE_EXEC_PATH,
195 		.mark_type = FANOTIFY_INODE,
196 		.ignore_path_fmt = MNT2_PATH,
197 		.ignore_mark_type = FANOTIFY_MOUNT,
198 		.event_path_fmt = FILE_EXEC_PATH2,
199 		.expected_mask_with_ignore = FAN_OPEN_EXEC,
200 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
201 	},
202 	{
203 		.tname = "don't ignore inode events created on another mount point",
204 		.mark_path_fmt = FILE_MNT2,
205 		.mark_type = FANOTIFY_INODE,
206 		.ignore_path_fmt = MNT2_PATH,
207 		.ignore_mark_type = FANOTIFY_MOUNT,
208 		.event_path_fmt = FILE_PATH,
209 		.expected_mask_with_ignore = FAN_OPEN,
210 		.expected_mask_without_ignore = FAN_OPEN
211 	},
212 	{
213 		.tname = "don't ignore exec inode events created on another mount point",
214 		.mark_path_fmt = FILE_EXEC_PATH2,
215 		.mark_type = FANOTIFY_INODE,
216 		.ignore_path_fmt = MNT2_PATH,
217 		.ignore_mark_type = FANOTIFY_MOUNT,
218 		.event_path_fmt = FILE_EXEC_PATH,
219 		.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
220 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
221 	},
222 	{
223 		.tname = "ignore fs events created on a specific file",
224 		.mark_path_fmt = MOUNT_PATH,
225 		.mark_type = FANOTIFY_FILESYSTEM,
226 		.ignore_path_fmt = FILE_PATH,
227 		.ignore_mark_type = FANOTIFY_INODE,
228 		.event_path_fmt = FILE_PATH,
229 		.expected_mask_without_ignore = FAN_OPEN
230 	},
231 	{
232 		.tname = "ignore exec fs events created on a specific file",
233 		.mark_path_fmt = MOUNT_PATH,
234 		.mark_type = FANOTIFY_FILESYSTEM,
235 		.ignore_path_fmt = FILE_EXEC_PATH,
236 		.ignore_mark_type = FANOTIFY_INODE,
237 		.event_path_fmt = FILE_EXEC_PATH,
238 		.expected_mask_with_ignore = FAN_OPEN_EXEC,
239 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
240 	},
241 	{
242 		.tname = "don't ignore mount events created on another file",
243 		.mark_path_fmt = MOUNT_PATH,
244 		.mark_type = FANOTIFY_FILESYSTEM,
245 		.ignore_path_fmt = FILE_PATH,
246 		.ignore_mark_type = FANOTIFY_INODE,
247 		.event_path_fmt = FILE2_PATH,
248 		.expected_mask_with_ignore = FAN_OPEN,
249 		.expected_mask_without_ignore = FAN_OPEN
250 	},
251 	{
252 		.tname = "don't ignore exec mount events created on another file",
253 		.mark_path_fmt = MOUNT_PATH,
254 		.mark_type = FANOTIFY_FILESYSTEM,
255 		.ignore_path_fmt = FILE_EXEC_PATH,
256 		.ignore_mark_type = FANOTIFY_INODE,
257 		.event_path_fmt = FILE2_EXEC_PATH,
258 		.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
259 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
260 	},
261 	{
262 		.tname = "ignore fs events created on a specific mount point",
263 		.mark_path_fmt = MOUNT_PATH,
264 		.mark_type = FANOTIFY_FILESYSTEM,
265 		.ignore_path_fmt = MNT2_PATH,
266 		.ignore_mark_type = FANOTIFY_MOUNT,
267 		.event_path_fmt = FILE_MNT2,
268 		.expected_mask_without_ignore = FAN_OPEN
269 	},
270 	{
271 		.tname = "ignore exec fs events created on a specific mount point",
272 		.mark_path_fmt = MOUNT_PATH,
273 		.mark_type = FANOTIFY_FILESYSTEM,
274 		.ignore_path_fmt = MNT2_PATH,
275 		.ignore_mark_type = FANOTIFY_MOUNT,
276 		.event_path_fmt = FILE_EXEC_PATH2,
277 		.expected_mask_with_ignore = FAN_OPEN_EXEC,
278 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
279 	},
280 	{
281 		.tname = "don't ignore fs events created on another mount point",
282 		.mark_path_fmt = MOUNT_PATH,
283 		.mark_type = FANOTIFY_FILESYSTEM,
284 		.ignore_path_fmt = MNT2_PATH,
285 		.ignore_mark_type = FANOTIFY_MOUNT,
286 		.event_path_fmt = FILE_PATH,
287 		.expected_mask_with_ignore = FAN_OPEN,
288 		.expected_mask_without_ignore = FAN_OPEN
289 	},
290 	{
291 		.tname = "don't ignore exec fs events created on another mount point",
292 		.mark_path_fmt = MOUNT_PATH,
293 		.mark_type = FANOTIFY_FILESYSTEM,
294 		.ignore_path_fmt = MNT2_PATH,
295 		.ignore_mark_type = FANOTIFY_MOUNT,
296 		.event_path_fmt = FILE_EXEC_PATH,
297 		.expected_mask_with_ignore = FAN_OPEN | FAN_OPEN_EXEC,
298 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
299 	},
300 	{
301 		.tname = "ignore child exec events created on a specific mount point",
302 		.mark_path_fmt = MOUNT_PATH,
303 		.mark_type = FANOTIFY_PARENT,
304 		.ignore_path_fmt = MOUNT_PATH,
305 		.ignore_mark_type = FANOTIFY_MOUNT,
306 		.event_path_fmt = FILE_EXEC_PATH,
307 		.expected_mask_with_ignore = FAN_OPEN_EXEC,
308 		.expected_mask_without_ignore = FAN_OPEN | FAN_OPEN_EXEC
309 	},
310 	{
311 		.tname = "ignore events on children of directory created on a specific file",
312 		.mark_path_fmt = DIR_PATH,
313 		.mark_type = FANOTIFY_PARENT,
314 		.ignore_path_fmt = DIR_PATH,
315 		.ignore_mark_type = FANOTIFY_PARENT,
316 		.ignored_flags = FAN_EVENT_ON_CHILD,
317 		.event_path_fmt = FILE_PATH,
318 		.expected_mask_without_ignore = FAN_OPEN
319 	},
320 	{
321 		.tname = "ignore events on file created inside a parent watching children",
322 		.mark_path_fmt = FILE_PATH,
323 		.mark_type = FANOTIFY_INODE,
324 		.ignore_path_fmt = DIR_PATH,
325 		.ignore_mark_type = FANOTIFY_PARENT,
326 		.ignored_flags = FAN_EVENT_ON_CHILD,
327 		.event_path_fmt = FILE_PATH,
328 		.expected_mask_without_ignore = FAN_OPEN
329 	},
330 	{
331 		.tname = "don't ignore events on file created inside a parent not watching children",
332 		.mark_path_fmt = FILE_PATH,
333 		.mark_type = FANOTIFY_INODE,
334 		.ignore_path_fmt = DIR_PATH,
335 		.ignore_mark_type = FANOTIFY_PARENT,
336 		.event_path_fmt = FILE_PATH,
337 		.expected_mask_with_ignore = FAN_OPEN,
338 		.expected_mask_without_ignore = FAN_OPEN
339 	},
340 	{
341 		.tname = "ignore mount events created inside a parent watching children",
342 		.mark_path_fmt = FILE_PATH,
343 		.mark_type = FANOTIFY_MOUNT,
344 		.ignore_path_fmt = DIR_PATH,
345 		.ignore_mark_type = FANOTIFY_PARENT,
346 		.ignored_flags = FAN_EVENT_ON_CHILD,
347 		.event_path_fmt = FILE_PATH,
348 		.expected_mask_without_ignore = FAN_OPEN
349 	},
350 	{
351 		.tname = "don't ignore mount events created inside a parent not watching children",
352 		.mark_path_fmt = FILE_PATH,
353 		.mark_type = FANOTIFY_MOUNT,
354 		.ignore_path_fmt = DIR_PATH,
355 		.ignore_mark_type = FANOTIFY_PARENT,
356 		.event_path_fmt = FILE_PATH,
357 		.expected_mask_with_ignore = FAN_OPEN,
358 		.expected_mask_without_ignore = FAN_OPEN
359 	},
360 	{
361 		.tname = "ignore fs events created inside a parent watching children",
362 		.mark_path_fmt = FILE_PATH,
363 		.mark_type = FANOTIFY_FILESYSTEM,
364 		.ignore_path_fmt = DIR_PATH,
365 		.ignore_mark_type = FANOTIFY_PARENT,
366 		.ignored_flags = FAN_EVENT_ON_CHILD,
367 		.event_path_fmt = FILE_PATH,
368 		.expected_mask_without_ignore = FAN_OPEN
369 	},
370 	{
371 		.tname = "don't ignore fs events created inside a parent not watching children",
372 		.mark_path_fmt = FILE_PATH,
373 		.mark_type = FANOTIFY_FILESYSTEM,
374 		.ignore_path_fmt = DIR_PATH,
375 		.ignore_mark_type = FANOTIFY_PARENT,
376 		.event_path_fmt = FILE_PATH,
377 		.expected_mask_with_ignore = FAN_OPEN,
378 		.expected_mask_without_ignore = FAN_OPEN
379 	},
380 	/* Evictable ignore mark test cases */
381 	{
382 		.tname = "don't ignore mount events created on file with evicted ignore mark",
383 		.mark_path_fmt = MOUNT_PATH,
384 		.mark_type = FANOTIFY_MOUNT,
385 		.ignore_path_cnt = 16,
386 		.ignore_path_fmt = FILE_PATH_MULTI,
387 		.ignore_mark_type = FANOTIFY_EVICTABLE,
388 		.event_path_cnt = 16,
389 		.event_path_fmt = FILE_PATH_MULTI,
390 		.expected_mask_with_ignore = FAN_OPEN,
391 		.expected_mask_without_ignore = FAN_OPEN
392 	},
393 	{
394 		.tname = "don't ignore fs events created on a file with evicted ignore mark",
395 		.mark_path_fmt = MOUNT_PATH,
396 		.mark_type = FANOTIFY_FILESYSTEM,
397 		.ignore_path_cnt = 16,
398 		.ignore_path_fmt = FILE_PATH_MULTI,
399 		.ignore_mark_type = FANOTIFY_EVICTABLE,
400 		.event_path_cnt = 16,
401 		.event_path_fmt = FILE_PATH_MULTI,
402 		.expected_mask_with_ignore = FAN_OPEN,
403 		.expected_mask_without_ignore = FAN_OPEN
404 	},
405 	{
406 		.tname = "don't ignore mount events created inside a parent with evicted ignore mark",
407 		.mark_path_fmt = MOUNT_PATH,
408 		.mark_type = FANOTIFY_MOUNT,
409 		.ignore_path_cnt = 16,
410 		.ignore_path_fmt = DIR_PATH_MULTI,
411 		.ignore_mark_type = FANOTIFY_EVICTABLE,
412 		.ignored_flags = FAN_EVENT_ON_CHILD,
413 		.event_path_cnt = 16,
414 		.event_path_fmt = FILE_PATH_MULTIDIR,
415 		.expected_mask_with_ignore = FAN_OPEN,
416 		.expected_mask_without_ignore = FAN_OPEN
417 	},
418 	{
419 		.tname = "don't ignore fs events created inside a parent with evicted ignore mark",
420 		.mark_path_fmt = MOUNT_PATH,
421 		.mark_type = FANOTIFY_FILESYSTEM,
422 		.ignore_path_cnt = 16,
423 		.ignore_path_fmt = DIR_PATH_MULTI,
424 		.ignore_mark_type = FANOTIFY_EVICTABLE,
425 		.ignored_flags = FAN_EVENT_ON_CHILD,
426 		.event_path_cnt = 16,
427 		.event_path_fmt = FILE_PATH_MULTIDIR,
428 		.expected_mask_with_ignore = FAN_OPEN,
429 		.expected_mask_without_ignore = FAN_OPEN
430 	},
431 	/* FAN_MARK_IGNORE specific test cases */
432 	{
433 		.tname = "ignore events on subdir inside a parent watching subdirs",
434 		.mark_path_fmt = SUBDIR_PATH,
435 		.mark_type = FANOTIFY_SUBDIR,
436 		.ignore_path_fmt = DIR_PATH,
437 		.ignore_mark_type = FANOTIFY_PARENT,
438 		.ignored_flags = FAN_EVENT_ON_CHILD | FAN_ONDIR,
439 		.event_path_fmt = SUBDIR_PATH,
440 		.expected_mask_with_ignore = 0,
441 		.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
442 	},
443 	{
444 		.tname = "don't ignore events on subdir inside a parent not watching children",
445 		.mark_path_fmt = SUBDIR_PATH,
446 		.mark_type = FANOTIFY_SUBDIR,
447 		.ignore_path_fmt = DIR_PATH,
448 		.ignore_mark_type = FANOTIFY_PARENT,
449 		.ignored_flags = FAN_ONDIR,
450 		.event_path_fmt = SUBDIR_PATH,
451 		.expected_mask_with_ignore = FAN_OPEN | FAN_ONDIR,
452 		.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
453 	},
454 	{
455 		.tname = "don't ignore events on subdir inside a parent watching non-dir children",
456 		.mark_path_fmt = SUBDIR_PATH,
457 		.mark_type = FANOTIFY_SUBDIR,
458 		.ignore_path_fmt = DIR_PATH,
459 		.ignore_mark_type = FANOTIFY_PARENT,
460 		.ignored_flags = FAN_EVENT_ON_CHILD,
461 		.event_path_fmt = SUBDIR_PATH,
462 		.expected_mask_with_ignore = FAN_OPEN | FAN_ONDIR,
463 		.expected_mask_without_ignore = FAN_OPEN | FAN_ONDIR
464 	},
465 };
466 
format_path_check(char * buf,const char * fmt,int count,int i)467 static int format_path_check(char *buf, const char *fmt, int count, int i)
468 {
469 	int limit = count ? : 1;
470 
471 	if (i >= limit)
472 		return 0;
473 
474 	if (count)
475 		sprintf(buf, fmt, i);
476 	else
477 		strcpy(buf, fmt);
478 	return 1;
479 }
480 
481 #define foreach_path(tc, buf, pname) \
482 	for (int piter = 0; format_path_check((buf), (tc)->pname##_fmt,	\
483 					(tc)->pname##_cnt, piter); piter++)
484 
show_fanotify_ignore_marks(int fd,int min,int max)485 static void show_fanotify_ignore_marks(int fd, int min, int max)
486 {
487 	unsigned int mflags, mask, ignored_mask;
488 	char procfdinfo[100];
489 	char line[BUFSIZ];
490 	int marks = 0;
491 	FILE *f;
492 
493 	sprintf(procfdinfo, "/proc/%d/fdinfo/%d", (int)getpid(), fd);
494 	f = SAFE_FOPEN(procfdinfo, "r");
495 	while (fgets(line, BUFSIZ, f)) {
496 		if (sscanf(line, "fanotify ino:%*x sdev:%*x mflags: %x mask:%x ignored_mask:%x",
497 			   &mflags, &mask, &ignored_mask) == 3) {
498 			if (ignored_mask != 0)
499 				marks++;
500 		}
501 	}
502 	if (marks < min) {
503 		tst_res(TFAIL, "Found %d ignore marks but at least %d expected", marks, min);
504 		return;
505 	}
506 	if (marks > max) {
507 		tst_res(TFAIL, "Found %d ignore marks but at most %d expected", marks, max);
508 		return;
509 	}
510 	tst_res(TPASS, "Found %d ignore marks which is in expected range %d-%d", marks, min, max);
511 }
512 
drop_caches(void)513 static void drop_caches(void)
514 {
515 	if (syncfs(fd_syncfs) < 0)
516 		tst_brk(TBROK | TERRNO, "Unexpected error when syncing filesystem");
517 
518 	SAFE_FILE_PRINTF(DROP_CACHES_FILE, "3");
519 }
520 
create_fanotify_groups(unsigned int n)521 static int create_fanotify_groups(unsigned int n)
522 {
523 	struct tcase *tc = &tcases[n];
524 	struct fanotify_mark_type *mark, *ignore_mark;
525 	unsigned int mark_ignored, mask;
526 	unsigned int p, i;
527 	int evictable_ignored = (tc->ignore_mark_type == FANOTIFY_EVICTABLE);
528 	int ignore_mark_type;
529 	int ignored_onchild = tc->ignored_flags & FAN_EVENT_ON_CHILD;
530 	char path[PATH_MAX];
531 
532 	mark = &fanotify_mark_types[tc->mark_type];
533 	ignore_mark = &fanotify_mark_types[tc->ignore_mark_type];
534 	ignore_mark_type = ignore_mark->flag & FAN_MARK_TYPES;
535 
536 	/* Open fd for syncfs before creating groups to avoid the FAN_OPEN event */
537 	fd_syncfs = SAFE_OPEN(MOUNT_PATH, O_RDONLY);
538 
539 	for (p = 0; p < num_classes; p++) {
540 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
541 			fd_notify[p][i] = SAFE_FANOTIFY_INIT(fanotify_class[p] |
542 							     FAN_NONBLOCK, O_RDONLY);
543 
544 			/*
545 			 * Add mark for each group.
546 			 *
547 			 * FAN_EVENT_ON_CHILD has no effect on filesystem/mount
548 			 * or inode mark on non-directory.
549 			 */
550 			foreach_path(tc, path, mark_path)
551 				SAFE_FANOTIFY_MARK(fd_notify[p][i],
552 					    FAN_MARK_ADD | mark->flag,
553 					    tc->expected_mask_without_ignore |
554 					    FAN_EVENT_ON_CHILD | FAN_ONDIR,
555 					    AT_FDCWD, path);
556 
557 			/* Do not add ignore mark for first priority groups */
558 			if (p == 0)
559 				continue;
560 
561 			/*
562 			 * Run tests in two variants:
563 			 * 1. Legacy FAN_MARK_IGNORED_MASK
564 			 * 2. FAN_MARK_IGNORE
565 			 */
566 			mark_ignored = tst_variant ? FAN_MARK_IGNORE_SURV : FAN_MARK_IGNORED_SURV;
567 			mask = FAN_OPEN | tc->ignored_flags;
568 add_mark:
569 			foreach_path(tc, path, ignore_path)
570 				SAFE_FANOTIFY_MARK(fd_notify[p][i],
571 					    FAN_MARK_ADD | ignore_mark->flag | mark_ignored,
572 					    mask, AT_FDCWD, path);
573 
574 			/*
575 			 * FAN_MARK_IGNORE respects FAN_EVENT_ON_CHILD flag, but legacy
576 			 * FAN_MARK_IGNORED_MASK does not. When using legacy ignore mask,
577 			 * if ignored mask is on a parent watching children, we need to
578 			 * also set the event and flag FAN_EVENT_ON_CHILD in mark mask.
579 			 * This is needed to indicate that parent ignored mask
580 			 * should be applied to events on children.
581 			 */
582 			if (ignored_onchild && mark_ignored & FAN_MARK_IGNORED_MASK) {
583 				mark_ignored = 0;
584 				goto add_mark;
585 			}
586 
587 			/*
588 			 * When using FAN_MARK_IGNORE, verify that the FAN_EVENT_ON_CHILD
589 			 * flag in mark mask does not affect the ignore mask.
590 			 *
591 			 * If parent does not want to ignore FAN_OPEN events on children,
592 			 * set a mark mask to watch FAN_CLOSE_WRITE events on children
593 			 * to make sure we do not ignore FAN_OPEN events from children.
594 			 *
595 			 * If parent wants to ignore FAN_OPEN events on childern,
596 			 * set a mark mask to watch FAN_CLOSE events only on parent itself
597 			 * to make sure we do not get FAN_CLOSE events from children.
598 			 *
599 			 * If we had already set the FAN_EVENT_ON_CHILD in the parent
600 			 * mark mask (mark_type == FANOTIFY_PARENT), then FAN_CLOSE mask
601 			 * will apply also to childern, so we skip this verification.
602 			 */
603 			if (mark_ignored & FAN_MARK_IGNORE &&
604 			    tc->ignore_mark_type == FANOTIFY_PARENT) {
605 				if (!ignored_onchild)
606 					mask = FAN_CLOSE_WRITE | FAN_EVENT_ON_CHILD | FAN_ONDIR;
607 				else if (tc->mark_type == FANOTIFY_PARENT)
608 					continue;
609 				else if (tc->ignored_flags & FAN_ONDIR)
610 					mask = FAN_CLOSE | ignored_onchild;
611 				else
612 					mask = FAN_CLOSE | FAN_ONDIR;
613 				mark_ignored = 0;
614 				goto add_mark;
615 			}
616 		}
617 	}
618 
619 	/*
620 	 * Verify that first priority groups have no ignore inode marks and that
621 	 * drop_caches evicted the evictable ignore marks of other groups.
622 	 */
623 	if (evictable_ignored)
624 		drop_caches();
625 
626 	if (ignore_mark_type == FAN_MARK_INODE) {
627 		for (p = 0; p < num_classes; p++) {
628 			for (i = 0; i < GROUPS_PER_PRIO; i++) {
629 				if (fd_notify[p][i] > 0) {
630 					int minexp, maxexp;
631 
632 					if (p == 0) {
633 						minexp = maxexp = 0;
634 					} else if (evictable_ignored) {
635 						minexp = 0;
636 						/*
637 						 * Check at least half the
638 						 * marks get evicted by reclaim
639 						 */
640 						maxexp = tc->ignore_path_cnt / 2;
641 					} else {
642 						minexp = maxexp = tc->ignore_path_cnt ? : 1;
643 					}
644 					show_fanotify_ignore_marks(fd_notify[p][i],
645 								   minexp, maxexp);
646 				}
647 			}
648 		}
649 	}
650 
651 	return 0;
652 }
653 
cleanup_fanotify_groups(void)654 static void cleanup_fanotify_groups(void)
655 {
656 	unsigned int i, p;
657 
658 	for (p = 0; p < num_classes; p++) {
659 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
660 			if (fd_notify[p][i] > 0)
661 				SAFE_CLOSE(fd_notify[p][i]);
662 		}
663 	}
664 	if (fd_syncfs > 0)
665 		SAFE_CLOSE(fd_syncfs);
666 }
667 
668 /* Flush out all pending dirty inodes and destructing marks */
mount_cycle(void)669 static void mount_cycle(void)
670 {
671 	if (bind_mount_created)
672 		SAFE_UMOUNT(MNT2_PATH);
673 	SAFE_UMOUNT(MOUNT_PATH);
674 	SAFE_MOUNT(tst_device->dev, MOUNT_PATH, tst_device->fs_type, 0, NULL);
675 	SAFE_MOUNT(MOUNT_PATH, MNT2_PATH, "none", MS_BIND, NULL);
676 	bind_mount_created = 1;
677 }
678 
verify_event(int p,int group,struct fanotify_event_metadata * event,unsigned long long expected_mask)679 static int verify_event(int p, int group, struct fanotify_event_metadata *event,
680 			 unsigned long long expected_mask)
681 {
682 	/* Only FAN_REPORT_FID reports the FAN_ONDIR flag in events on dirs */
683 	if (!(fanotify_class[p] & FAN_REPORT_FID))
684 		expected_mask &= ~FAN_ONDIR;
685 
686 	if (event->mask != expected_mask) {
687 		tst_res(TFAIL, "group %d (%x) got event: mask %llx (expected %llx) "
688 			"pid=%u fd=%u", group, fanotify_class[p],
689 			(unsigned long long) event->mask,
690 			(unsigned long long) expected_mask,
691 			(unsigned int)event->pid, event->fd);
692 		return 0;
693 	} else if (event->pid != child_pid) {
694 		tst_res(TFAIL, "group %d (%x) got event: mask %llx pid=%u "
695 			"(expected %u) fd=%u", group, fanotify_class[p],
696 			(unsigned long long)event->mask, (unsigned int)event->pid,
697 			(unsigned int)child_pid, event->fd);
698 		return 0;
699 	}
700 	return 1;
701 }
702 
generate_event(struct tcase * tc,unsigned long long expected_mask)703 static int generate_event(struct tcase *tc, unsigned long long expected_mask)
704 {
705 	int fd, status;
706 
707 	child_pid = SAFE_FORK();
708 
709 	if (child_pid == 0) {
710 		char path[PATH_MAX];
711 
712 		foreach_path(tc, path, event_path) {
713 			if (expected_mask & FAN_OPEN_EXEC) {
714 				SAFE_EXECL(path, path, NULL);
715 			} else {
716 				fd = SAFE_OPEN(path, O_RDONLY);
717 
718 				if (fd > 0)
719 					SAFE_CLOSE(fd);
720 			}
721 		}
722 
723 		exit(0);
724 	}
725 
726 	SAFE_WAITPID(child_pid, &status, 0);
727 
728 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
729 		return 1;
730 	return 0;
731 }
732 
fetch_event(int fd,int * retp)733 static struct fanotify_event_metadata *fetch_event(int fd, int *retp)
734 {
735 	int ret;
736 	struct fanotify_event_metadata *event;
737 
738 	*retp = 0;
739 	if (event_buf_pos >= event_buf_len) {
740 		event_buf_pos = 0;
741 		ret = read(fd, event_buf, EVENT_BUF_LEN);
742 		if (ret < 0) {
743 			if (errno == EAGAIN)
744 				return NULL;
745 			tst_brk(TBROK | TERRNO, "reading fanotify events failed");
746 		}
747 		event_buf_len = ret;
748 	}
749 	if (event_buf_len - event_buf_pos < (int)FAN_EVENT_METADATA_LEN) {
750 		tst_brk(TBROK,
751 			"too short event when reading fanotify events (%d < %d)",
752 			event_buf_len - event_buf_pos,
753 			(int)FAN_EVENT_METADATA_LEN);
754 	}
755 	event = (struct fanotify_event_metadata *)(event_buf + event_buf_pos);
756 	event_buf_pos += event->event_len;
757 	return event;
758 }
759 
test_fanotify(unsigned int n)760 static void test_fanotify(unsigned int n)
761 {
762 	struct tcase *tc = &tcases[n];
763 	struct fanotify_mark_type *mark, *ignore_mark;
764 	int ret;
765 	unsigned int p, i;
766 	struct fanotify_event_metadata *event;
767 	int event_count;
768 
769 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
770 
771 	if (exec_events_unsupported && tc->expected_mask_with_ignore & FAN_OPEN_EXEC) {
772 		tst_res(TCONF, "FAN_OPEN_EXEC not supported in kernel?");
773 		return;
774 	}
775 
776 	if (filesystem_mark_unsupported && tc->mark_type == FANOTIFY_FILESYSTEM) {
777 		tst_res(TCONF, "FAN_MARK_FILESYSTEM not supported in kernel?");
778 		return;
779 	}
780 
781 	if (evictable_mark_unsupported && tc->ignore_mark_type == FANOTIFY_EVICTABLE) {
782 		tst_res(TCONF, "FAN_MARK_EVICTABLE not supported in kernel?");
783 		return;
784 	}
785 
786 	if (ignore_mark_unsupported && tst_variant) {
787 		tst_res(TCONF, "FAN_MARK_IGNORE not supported in kernel?");
788 		return;
789 	}
790 
791 	if (tc->ignored_flags & FAN_EVENT_ON_CHILD && tst_kvercmp(5, 9, 0) < 0) {
792 		tst_res(TCONF, "ignored mask in combination with flag FAN_EVENT_ON_CHILD"
793 				" has undefined behavior on kernel < 5.9");
794 		return;
795 	}
796 
797 	if (tc->ignored_flags && tc->ignore_mark_type == FANOTIFY_PARENT &&
798 			!tst_variant && tc->mark_type == FANOTIFY_SUBDIR) {
799 		tst_res(TCONF, "flags FAN_EVENT_ON_CHILD and FAN_ONDIR do not take effect"
800 				" with legacy FAN_MARK_IGNORED_MASK");
801 		return;
802 	}
803 
804 	if (create_fanotify_groups(n) != 0)
805 		goto cleanup;
806 
807 	mark = &fanotify_mark_types[tc->mark_type];
808 	ignore_mark = &fanotify_mark_types[tc->ignore_mark_type];
809 
810 	/* Generate event in child process */
811 	if (!generate_event(tc, tc->expected_mask_with_ignore))
812 		tst_brk(TBROK, "Child process terminated incorrectly");
813 
814 	/* First verify all groups without matching ignore mask got the event */
815 	for (p = 0; p < num_classes; p++) {
816 		if (p > 0 && !tc->expected_mask_with_ignore)
817 			break;
818 
819 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
820 			event_count = 0;
821 			event_buf_pos = event_buf_len = 0;
822 			while ((event = fetch_event(fd_notify[p][i], &ret))) {
823 				event_count++;
824 				if (!verify_event(p, i, event, p == 0 ?
825 						tc->expected_mask_without_ignore :
826 						tc->expected_mask_with_ignore))
827 					break;
828 				if (event->fd != FAN_NOFD)
829 					SAFE_CLOSE(event->fd);
830 			}
831 			if (ret < 0)
832 				continue;
833 			if (event_count != (tc->event_path_cnt ? : 1)) {
834 				tst_res(TFAIL, "group %d (%x) with %s "
835 					"got unexpected number of events (%d != %d)",
836 					i, fanotify_class[p], mark->name,
837 					event_count, tc->event_path_cnt);
838 			} else {
839 				tst_res(TPASS, "group %d (%x) got %d events: mask %llx pid=%u",
840 					i, fanotify_class[p], event_count,
841 					(unsigned long long)(p == 0 ?
842 					tc->expected_mask_without_ignore :
843 					tc->expected_mask_with_ignore),
844 					(unsigned int)child_pid);
845 			}
846 		}
847 	}
848 	/* Then verify all groups with matching ignore mask did got the event */
849 	for (p = 1; p < num_classes && !tc->expected_mask_with_ignore; p++) {
850 		for (i = 0; i < GROUPS_PER_PRIO; i++) {
851 			event_count = 0;
852 			event_buf_pos = event_buf_len = 0;
853 			while ((event = fetch_event(fd_notify[p][i], &ret))) {
854 				event_count++;
855 				if (event->fd != FAN_NOFD)
856 					SAFE_CLOSE(event->fd);
857 			}
858 			if (ret < 0)
859 				continue;
860 			if (event_count > tc->event_path_cnt / 2)
861 				tst_res(TFAIL, "group %d (%x) with %s and "
862 					"%s ignore mask got unexpectedly many events (%d > %d)",
863 					i, fanotify_class[p], mark->name,
864 					ignore_mark->name, event_count,
865 					tc->event_path_cnt / 2);
866 		}
867 	}
868 cleanup:
869 	cleanup_fanotify_groups();
870 	mount_cycle();
871 }
872 
setup(void)873 static void setup(void)
874 {
875 	int i;
876 
877 	exec_events_unsupported = fanotify_flags_supported_on_fs(FAN_CLASS_CONTENT,
878 					0, FAN_OPEN_EXEC, MOUNT_PATH);
879 	filesystem_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_FILESYSTEM,
880 								    MOUNT_PATH);
881 	evictable_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_EVICTABLE,
882 								   MOUNT_PATH);
883 	ignore_mark_unsupported = fanotify_mark_supported_on_fs(FAN_MARK_IGNORE_SURV,
884 								MOUNT_PATH);
885 	fan_report_dfid_unsupported = fanotify_flags_supported_on_fs(FAN_REPORT_DFID_NAME,
886 								     FAN_MARK_MOUNT,
887 								     FAN_OPEN, MOUNT_PATH);
888 	if (fan_report_dfid_unsupported) {
889 		FANOTIFY_INIT_FLAGS_ERR_MSG(FAN_REPORT_DFID_NAME, fan_report_dfid_unsupported);
890 		/* Limit tests to legacy priority classes */
891 		num_classes = NUM_PRIORITIES;
892 	}
893 
894 	SAFE_MKDIR(DIR_PATH, 0755);
895 	SAFE_MKDIR(SUBDIR_PATH, 0755);
896 	SAFE_FILE_PRINTF(FILE_PATH, "1");
897 	for (i = 0; i < (int)ARRAY_SIZE(tcases); i++) {
898 		if (tcases[i].mark_path_cnt > max_file_multi)
899 			max_file_multi = tcases[i].mark_path_cnt;
900 		if (tcases[i].ignore_path_cnt > max_file_multi)
901 			max_file_multi = tcases[i].ignore_path_cnt;
902 		if (tcases[i].event_path_cnt > max_file_multi)
903 			max_file_multi = tcases[i].event_path_cnt;
904 	}
905 	for (i = 0; i < max_file_multi; i++) {
906 		char path[PATH_MAX];
907 
908 		sprintf(path, FILE_PATH_MULTI, i);
909 		SAFE_FILE_PRINTF(path, "1");
910 		sprintf(path, DIR_PATH_MULTI, i);
911 		SAFE_MKDIR(path, 0755);
912 		sprintf(path, FILE_PATH_MULTIDIR, i);
913 		SAFE_FILE_PRINTF(path, "1");
914 	}
915 
916 	SAFE_CP(TEST_APP, FILE_EXEC_PATH);
917 	SAFE_CP(TEST_APP, FILE2_EXEC_PATH);
918 
919 	/* Create another bind mount at another path for generating events */
920 	SAFE_MKDIR(MNT2_PATH, 0755);
921 	mount_cycle();
922 
923 	SAFE_FILE_SCANF(CACHE_PRESSURE_FILE, "%d", &old_cache_pressure);
924 	/* Set high priority for evicting inodes */
925 	SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "500");
926 }
927 
cleanup(void)928 static void cleanup(void)
929 {
930 	int i;
931 
932 	cleanup_fanotify_groups();
933 
934 	if (bind_mount_created)
935 		SAFE_UMOUNT(MNT2_PATH);
936 
937 	SAFE_FILE_PRINTF(CACHE_PRESSURE_FILE, "%d", old_cache_pressure);
938 
939 	for (i = 0; i < max_file_multi; i++) {
940 		char path[PATH_MAX];
941 
942 		sprintf(path, FILE_PATH_MULTIDIR, i);
943 		SAFE_UNLINK(path);
944 		sprintf(path, DIR_PATH_MULTI, i);
945 		SAFE_RMDIR(path);
946 		sprintf(path, FILE_PATH_MULTI, i);
947 		SAFE_UNLINK(path);
948 	}
949 	SAFE_UNLINK(FILE_PATH);
950 	SAFE_RMDIR(SUBDIR_PATH);
951 	SAFE_RMDIR(DIR_PATH);
952 	SAFE_RMDIR(MNT2_PATH);
953 }
954 
955 static struct tst_test test = {
956 	.test = test_fanotify,
957 	.tcnt = ARRAY_SIZE(tcases),
958 	.test_variants = 2,
959 	.setup = setup,
960 	.cleanup = cleanup,
961 	.mount_device = 1,
962 	.mntpoint = MOUNT_PATH,
963 	.needs_root = 1,
964 	.forks_child = 1,
965 	.resource_files = (const char *const []) {
966 		TEST_APP,
967 		NULL
968 	},
969 	.tags = (const struct tst_tag[]) {
970 		{"linux-git", "9bdda4e9cf2d"},
971 		{"linux-git", "2f02fd3fa13e"},
972 		{}
973 	}
974 };
975 
976 #else
977 	TST_TEST_TCONF("system doesn't have required fanotify support");
978 #endif
979