Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fanotify: prepare for setting event flags in ignore mask

Setting flags FAN_ONDIR FAN_EVENT_ON_CHILD in ignore mask has no effect.
The FAN_EVENT_ON_CHILD flag in mask implicitly applies to ignore mask and
ignore mask is always implicitly applied to events on directories.

Define a mark flag that replaces this legacy behavior with logic of
applying the ignore mask according to event flags in ignore mask.

Implement the new logic to prepare for supporting an ignore mask that
ignores events on children and ignore mask that does not ignore events
on directories.

To emphasize the change in terminology, also rename ignored_mask mark
member to ignore_mask and use accessors to get only the effective
ignored events or the ignored events and flags.

This change in terminology finally aligns with the "ignore mask"
language in man pages and in most of the comments.

Link: https://lore.kernel.org/r/20220629144210.2983229-2-amir73il@gmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Amir Goldstein and committed by
Jan Kara
31a371e4 c05787b4

+121 -35
+11 -8
fs/notify/fanotify/fanotify.c
··· 295 295 const void *data, int data_type, 296 296 struct inode *dir) 297 297 { 298 - __u32 marks_mask = 0, marks_ignored_mask = 0; 298 + __u32 marks_mask = 0, marks_ignore_mask = 0; 299 299 __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS | 300 300 FANOTIFY_EVENT_FLAGS; 301 301 const struct path *path = fsnotify_data_path(data, data_type); 302 302 unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); 303 303 struct fsnotify_mark *mark; 304 + bool ondir = event_mask & FAN_ONDIR; 304 305 int type; 305 306 306 307 pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", ··· 316 315 return 0; 317 316 } else if (!(fid_mode & FAN_REPORT_FID)) { 318 317 /* Do we have a directory inode to report? */ 319 - if (!dir && !(event_mask & FS_ISDIR)) 318 + if (!dir && !ondir) 320 319 return 0; 321 320 } 322 321 323 322 fsnotify_foreach_iter_mark_type(iter_info, mark, type) { 324 - /* Apply ignore mask regardless of mark's ISDIR flag */ 325 - marks_ignored_mask |= mark->ignored_mask; 323 + /* 324 + * Apply ignore mask depending on event flags in ignore mask. 325 + */ 326 + marks_ignore_mask |= 327 + fsnotify_effective_ignore_mask(mark, ondir, type); 326 328 327 329 /* 328 - * If the event is on dir and this mark doesn't care about 329 - * events on dir, don't send it! 330 + * Send the event depending on event flags in mark mask. 330 331 */ 331 - if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR)) 332 + if (!fsnotify_mask_applicable(mark->mask, ondir, type)) 332 333 continue; 333 334 334 335 marks_mask |= mark->mask; ··· 339 336 *match_mask |= 1U << type; 340 337 } 341 338 342 - test_mask = event_mask & marks_mask & ~marks_ignored_mask; 339 + test_mask = event_mask & marks_mask & ~marks_ignore_mask; 343 340 344 341 /* 345 342 * For dirent modification events (create/delete/move) that do not carry
+12 -9
fs/notify/fanotify/fanotify_user.c
··· 1012 1012 if (!(flags & FAN_MARK_IGNORED_MASK)) { 1013 1013 fsn_mark->mask &= ~mask; 1014 1014 } else { 1015 - fsn_mark->ignored_mask &= ~mask; 1015 + fsn_mark->ignore_mask &= ~mask; 1016 1016 } 1017 1017 newmask = fsnotify_calc_mask(fsn_mark); 1018 1018 /* ··· 1021 1021 * changes to the mask. 1022 1022 * Destroy mark when only umask bits remain. 1023 1023 */ 1024 - *destroy = !((fsn_mark->mask | fsn_mark->ignored_mask) & ~umask); 1024 + *destroy = !((fsn_mark->mask | fsn_mark->ignore_mask) & ~umask); 1025 1025 spin_unlock(&fsn_mark->lock); 1026 1026 1027 1027 return oldmask & ~newmask; ··· 1090 1090 /* 1091 1091 * Setting FAN_MARK_IGNORED_SURV_MODIFY for the first time may lead to 1092 1092 * the removal of the FS_MODIFY bit in calculated mask if it was set 1093 - * because of an ignored mask that is now going to survive FS_MODIFY. 1093 + * because of an ignore mask that is now going to survive FS_MODIFY. 1094 1094 */ 1095 1095 if ((fan_flags & FAN_MARK_IGNORED_MASK) && 1096 1096 (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) && ··· 1123 1123 if (!(fan_flags & FAN_MARK_IGNORED_MASK)) 1124 1124 fsn_mark->mask |= mask; 1125 1125 else 1126 - fsn_mark->ignored_mask |= mask; 1126 + fsn_mark->ignore_mask |= mask; 1127 1127 1128 1128 recalc = fsnotify_calc_mask(fsn_mark) & 1129 1129 ~fsnotify_conn_mask(fsn_mark->connector); ··· 1261 1261 1262 1262 /* 1263 1263 * If some other task has this inode open for write we should not add 1264 - * an ignored mark, unless that ignored mark is supposed to survive 1264 + * an ignore mask, unless that ignore mask is supposed to survive 1265 1265 * modification changes anyway. 1266 1266 */ 1267 1267 if ((flags & FAN_MARK_IGNORED_MASK) && ··· 1557 1557 __kernel_fsid_t __fsid, *fsid = NULL; 1558 1558 u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS; 1559 1559 unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; 1560 - bool ignored = flags & FAN_MARK_IGNORED_MASK; 1560 + bool ignore = flags & FAN_MARK_IGNORED_MASK; 1561 1561 unsigned int obj_type, fid_mode; 1562 1562 u32 umask = 0; 1563 1563 int ret; ··· 1606 1606 if (mask & ~valid_mask) 1607 1607 return -EINVAL; 1608 1608 1609 - /* Event flags (ONDIR, ON_CHILD) are meaningless in ignored mask */ 1610 - if (ignored) 1609 + /* 1610 + * Event flags (FAN_ONDIR, FAN_EVENT_ON_CHILD) have no effect with 1611 + * FAN_MARK_IGNORED_MASK. 1612 + */ 1613 + if (ignore) 1611 1614 mask &= ~FANOTIFY_EVENT_FLAGS; 1612 1615 1613 1616 f = fdget(fanotify_fd); ··· 1724 1721 * events with parent/name info for non-directory. 1725 1722 */ 1726 1723 if ((fid_mode & FAN_REPORT_DIR_FID) && 1727 - (flags & FAN_MARK_ADD) && !ignored) 1724 + (flags & FAN_MARK_ADD) && !ignore) 1728 1725 mask |= FAN_EVENT_ON_CHILD; 1729 1726 } 1730 1727
+3 -3
fs/notify/fdinfo.c
··· 113 113 return; 114 114 seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ", 115 115 inode->i_ino, inode->i_sb->s_dev, 116 - mflags, mark->mask, mark->ignored_mask); 116 + mflags, mark->mask, mark->ignore_mask); 117 117 show_mark_fhandle(m, inode); 118 118 seq_putc(m, '\n'); 119 119 iput(inode); ··· 121 121 struct mount *mnt = fsnotify_conn_mount(mark->connector); 122 122 123 123 seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", 124 - mnt->mnt_id, mflags, mark->mask, mark->ignored_mask); 124 + mnt->mnt_id, mflags, mark->mask, mark->ignore_mask); 125 125 } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) { 126 126 struct super_block *sb = fsnotify_conn_sb(mark->connector); 127 127 128 128 seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", 129 - sb->s_dev, mflags, mark->mask, mark->ignored_mask); 129 + sb->s_dev, mflags, mark->mask, mark->ignore_mask); 130 130 } 131 131 } 132 132
+12 -9
fs/notify/fsnotify.c
··· 324 324 struct fsnotify_group *group = NULL; 325 325 __u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS); 326 326 __u32 marks_mask = 0; 327 - __u32 marks_ignored_mask = 0; 327 + __u32 marks_ignore_mask = 0; 328 + bool is_dir = mask & FS_ISDIR; 328 329 struct fsnotify_mark *mark; 329 330 int type; 330 331 ··· 337 336 fsnotify_foreach_iter_mark_type(iter_info, mark, type) { 338 337 if (!(mark->flags & 339 338 FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 340 - mark->ignored_mask = 0; 339 + mark->ignore_mask = 0; 341 340 } 342 341 } 343 342 ··· 345 344 fsnotify_foreach_iter_mark_type(iter_info, mark, type) { 346 345 group = mark->group; 347 346 marks_mask |= mark->mask; 348 - marks_ignored_mask |= mark->ignored_mask; 347 + marks_ignore_mask |= 348 + fsnotify_effective_ignore_mask(mark, is_dir, type); 349 349 } 350 350 351 - pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d\n", 352 - __func__, group, mask, marks_mask, marks_ignored_mask, 351 + pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignore_mask=%x data=%p data_type=%d dir=%p cookie=%d\n", 352 + __func__, group, mask, marks_mask, marks_ignore_mask, 353 353 data, data_type, dir, cookie); 354 354 355 - if (!(test_mask & marks_mask & ~marks_ignored_mask)) 355 + if (!(test_mask & marks_mask & ~marks_ignore_mask)) 356 356 return 0; 357 357 358 358 if (group->ops->handle_event) { ··· 425 423 * But is *this mark* watching children? 426 424 */ 427 425 if (type == FSNOTIFY_ITER_TYPE_PARENT && 428 - !(mark->mask & FS_EVENT_ON_CHILD)) 426 + !(mark->mask & FS_EVENT_ON_CHILD) && 427 + !(fsnotify_ignore_mask(mark) & FS_EVENT_ON_CHILD)) 429 428 continue; 430 429 431 430 fsnotify_iter_set_report_type(iter_info, type); ··· 535 532 536 533 537 534 /* 538 - * If this is a modify event we may need to clear some ignored masks. 539 - * In that case, the object with ignored masks will have the FS_MODIFY 535 + * If this is a modify event we may need to clear some ignore masks. 536 + * In that case, the object with ignore masks will have the FS_MODIFY 540 537 * event in its mask. 541 538 * Otherwise, return if none of the marks care about this type of event. 542 539 */
+83 -6
include/linux/fsnotify_backend.h
··· 518 518 struct hlist_node obj_list; 519 519 /* Head of list of marks for an object [mark ref] */ 520 520 struct fsnotify_mark_connector *connector; 521 - /* Events types to ignore [mark->lock, group->mark_mutex] */ 522 - __u32 ignored_mask; 521 + /* Events types and flags to ignore [mark->lock, group->mark_mutex] */ 522 + __u32 ignore_mask; 523 523 /* General fsnotify mark flags */ 524 524 #define FSNOTIFY_MARK_FLAG_ALIVE 0x0001 525 525 #define FSNOTIFY_MARK_FLAG_ATTACHED 0x0002 ··· 529 529 /* fanotify mark flags */ 530 530 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100 531 531 #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200 532 + #define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400 532 533 unsigned int flags; /* flags [mark->lock] */ 533 534 }; 534 535 ··· 656 655 657 656 /* functions used to manipulate the marks attached to inodes */ 658 657 659 - /* Get mask for calculating object interest taking ignored mask into account */ 658 + /* 659 + * Canonical "ignore mask" including event flags. 660 + * 661 + * Note the subtle semantic difference from the legacy ->ignored_mask. 662 + * ->ignored_mask traditionally only meant which events should be ignored, 663 + * while ->ignore_mask also includes flags regarding the type of objects on 664 + * which events should be ignored. 665 + */ 666 + static inline __u32 fsnotify_ignore_mask(struct fsnotify_mark *mark) 667 + { 668 + __u32 ignore_mask = mark->ignore_mask; 669 + 670 + /* The event flags in ignore mask take effect */ 671 + if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS) 672 + return ignore_mask; 673 + 674 + /* 675 + * Legacy behavior: 676 + * - Always ignore events on dir 677 + * - Ignore events on child if parent is watching children 678 + */ 679 + ignore_mask |= FS_ISDIR; 680 + ignore_mask &= ~FS_EVENT_ON_CHILD; 681 + ignore_mask |= mark->mask & FS_EVENT_ON_CHILD; 682 + 683 + return ignore_mask; 684 + } 685 + 686 + /* Legacy ignored_mask - only event types to ignore */ 687 + static inline __u32 fsnotify_ignored_events(struct fsnotify_mark *mark) 688 + { 689 + return mark->ignore_mask & ALL_FSNOTIFY_EVENTS; 690 + } 691 + 692 + /* 693 + * Check if mask (or ignore mask) should be applied depending if victim is a 694 + * directory and whether it is reported to a watching parent. 695 + */ 696 + static inline bool fsnotify_mask_applicable(__u32 mask, bool is_dir, 697 + int iter_type) 698 + { 699 + /* Should mask be applied to a directory? */ 700 + if (is_dir && !(mask & FS_ISDIR)) 701 + return false; 702 + 703 + /* Should mask be applied to a child? */ 704 + if (iter_type == FSNOTIFY_ITER_TYPE_PARENT && 705 + !(mask & FS_EVENT_ON_CHILD)) 706 + return false; 707 + 708 + return true; 709 + } 710 + 711 + /* 712 + * Effective ignore mask taking into account if event victim is a 713 + * directory and whether it is reported to a watching parent. 714 + */ 715 + static inline __u32 fsnotify_effective_ignore_mask(struct fsnotify_mark *mark, 716 + bool is_dir, int iter_type) 717 + { 718 + __u32 ignore_mask = fsnotify_ignored_events(mark); 719 + 720 + if (!ignore_mask) 721 + return 0; 722 + 723 + /* For non-dir and non-child, no need to consult the event flags */ 724 + if (!is_dir && iter_type != FSNOTIFY_ITER_TYPE_PARENT) 725 + return ignore_mask; 726 + 727 + ignore_mask = fsnotify_ignore_mask(mark); 728 + if (!fsnotify_mask_applicable(ignore_mask, is_dir, iter_type)) 729 + return 0; 730 + 731 + return ignore_mask & ALL_FSNOTIFY_EVENTS; 732 + } 733 + 734 + /* Get mask for calculating object interest taking ignore mask into account */ 660 735 static inline __u32 fsnotify_calc_mask(struct fsnotify_mark *mark) 661 736 { 662 737 __u32 mask = mark->mask; 663 738 664 - if (!mark->ignored_mask) 739 + if (!fsnotify_ignored_events(mark)) 665 740 return mask; 666 741 667 - /* Interest in FS_MODIFY may be needed for clearing ignored mask */ 742 + /* Interest in FS_MODIFY may be needed for clearing ignore mask */ 668 743 if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 669 744 mask |= FS_MODIFY; 670 745 ··· 748 671 * If mark is interested in ignoring events on children, the object must 749 672 * show interest in those events for fsnotify_parent() to notice it. 750 673 */ 751 - return mask | (mark->ignored_mask & ALL_FSNOTIFY_EVENTS); 674 + return mask | mark->ignore_mask; 752 675 } 753 676 754 677 /* Get mask of events for a list of marks */