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