Merge tag 'fsnotify_for_v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Aug 2022 15:50:39 +0000 (08:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Aug 2022 15:50:39 +0000 (08:50 -0700)
Pull fsnotify updates from Jan Kara:

 - support for FAN_MARK_IGNORE which untangles some of the not well
   defined corner cases with fanotify ignore masks

 - small cleanups

* tag 'fsnotify_for_v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: Fix comment typo
  fanotify: introduce FAN_MARK_IGNORE
  fanotify: cleanups for fanotify_mark() input validations
  fanotify: prepare for setting event flags in ignore mask
  fs: inotify: Fix typo in inotify comment

fs/notify/fanotify/fanotify.c
fs/notify/fanotify/fanotify.h
fs/notify/fanotify/fanotify_user.c
fs/notify/fdinfo.c
fs/notify/fsnotify.c
fs/notify/inotify/inotify_user.c
include/linux/fanotify.h
include/linux/fsnotify_backend.h
include/uapi/linux/fanotify.h

index 4f897e1..cd7d09a 100644 (file)
@@ -295,12 +295,13 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
                                     const void *data, int data_type,
                                     struct inode *dir)
 {
-       __u32 marks_mask = 0, marks_ignored_mask = 0;
+       __u32 marks_mask = 0, marks_ignore_mask = 0;
        __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
                                     FANOTIFY_EVENT_FLAGS;
        const struct path *path = fsnotify_data_path(data, data_type);
        unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
        struct fsnotify_mark *mark;
+       bool ondir = event_mask & FAN_ONDIR;
        int type;
 
        pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
@@ -315,19 +316,21 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
                        return 0;
        } else if (!(fid_mode & FAN_REPORT_FID)) {
                /* Do we have a directory inode to report? */
-               if (!dir && !(event_mask & FS_ISDIR))
+               if (!dir && !ondir)
                        return 0;
        }
 
        fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
-               /* Apply ignore mask regardless of mark's ISDIR flag */
-               marks_ignored_mask |= mark->ignored_mask;
+               /*
+                * Apply ignore mask depending on event flags in ignore mask.
+                */
+               marks_ignore_mask |=
+                       fsnotify_effective_ignore_mask(mark, ondir, type);
 
                /*
-                * If the event is on dir and this mark doesn't care about
-                * events on dir, don't send it!
+                * Send the event depending on event flags in mark mask.
                 */
-               if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR))
+               if (!fsnotify_mask_applicable(mark->mask, ondir, type))
                        continue;
 
                marks_mask |= mark->mask;
@@ -336,7 +339,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
                *match_mask |= 1U << type;
        }
 
-       test_mask = event_mask & marks_mask & ~marks_ignored_mask;
+       test_mask = event_mask & marks_mask & ~marks_ignore_mask;
 
        /*
         * For dirent modification events (create/delete/move) that do not carry
index 80e0ec9..1d9f112 100644 (file)
@@ -499,6 +499,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
                mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
        if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
                mflags |= FAN_MARK_EVICTABLE;
+       if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
+               mflags |= FAN_MARK_IGNORE;
 
        return mflags;
 }
index b08ce0d..f0e49a4 100644 (file)
@@ -1009,10 +1009,10 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
        mask &= ~umask;
        spin_lock(&fsn_mark->lock);
        oldmask = fsnotify_calc_mask(fsn_mark);
-       if (!(flags & FAN_MARK_IGNORED_MASK)) {
+       if (!(flags & FANOTIFY_MARK_IGNORE_BITS)) {
                fsn_mark->mask &= ~mask;
        } else {
-               fsn_mark->ignored_mask &= ~mask;
+               fsn_mark->ignore_mask &= ~mask;
        }
        newmask = fsnotify_calc_mask(fsn_mark);
        /*
@@ -1021,7 +1021,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
         * changes to the mask.
         * Destroy mark when only umask bits remain.
         */
-       *destroy = !((fsn_mark->mask | fsn_mark->ignored_mask) & ~umask);
+       *destroy = !((fsn_mark->mask | fsn_mark->ignore_mask) & ~umask);
        spin_unlock(&fsn_mark->lock);
 
        return oldmask & ~newmask;
@@ -1085,15 +1085,24 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
                                       unsigned int fan_flags)
 {
        bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
+       unsigned int ignore = fan_flags & FANOTIFY_MARK_IGNORE_BITS;
        bool recalc = false;
 
+       /*
+        * When using FAN_MARK_IGNORE for the first time, mark starts using
+        * independent event flags in ignore mask.  After that, trying to
+        * update the ignore mask with the old FAN_MARK_IGNORED_MASK API
+        * will result in EEXIST error.
+        */
+       if (ignore == FAN_MARK_IGNORE)
+               fsn_mark->flags |= FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS;
+
        /*
         * Setting FAN_MARK_IGNORED_SURV_MODIFY for the first time may lead to
         * the removal of the FS_MODIFY bit in calculated mask if it was set
-        * because of an ignored mask that is now going to survive FS_MODIFY.
+        * because of an ignore mask that is now going to survive FS_MODIFY.
         */
-       if ((fan_flags & FAN_MARK_IGNORED_MASK) &&
-           (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
+       if (ignore && (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
            !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) {
                fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
                if (!(fsn_mark->mask & FS_MODIFY))
@@ -1120,10 +1129,10 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
        bool recalc;
 
        spin_lock(&fsn_mark->lock);
-       if (!(fan_flags & FAN_MARK_IGNORED_MASK))
+       if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
                fsn_mark->mask |= mask;
        else
-               fsn_mark->ignored_mask |= mask;
+               fsn_mark->ignore_mask |= mask;
 
        recalc = fsnotify_calc_mask(fsn_mark) &
                ~fsnotify_conn_mask(fsn_mark->connector);
@@ -1187,6 +1196,37 @@ static int fanotify_group_init_error_pool(struct fsnotify_group *group)
                                         sizeof(struct fanotify_error_event));
 }
 
+static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
+                                             unsigned int fan_flags)
+{
+       /*
+        * Non evictable mark cannot be downgraded to evictable mark.
+        */
+       if (fan_flags & FAN_MARK_EVICTABLE &&
+           !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
+               return -EEXIST;
+
+       /*
+        * New ignore mask semantics cannot be downgraded to old semantics.
+        */
+       if (fan_flags & FAN_MARK_IGNORED_MASK &&
+           fsn_mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
+               return -EEXIST;
+
+       /*
+        * An ignore mask that survives modify could never be downgraded to not
+        * survive modify.  With new FAN_MARK_IGNORE semantics we make that rule
+        * explicit and return an error when trying to update the ignore mask
+        * without the original FAN_MARK_IGNORED_SURV_MODIFY value.
+        */
+       if (fan_flags & FAN_MARK_IGNORE &&
+           !(fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
+           fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
+               return -EEXIST;
+
+       return 0;
+}
+
 static int fanotify_add_mark(struct fsnotify_group *group,
                             fsnotify_connp_t *connp, unsigned int obj_type,
                             __u32 mask, unsigned int fan_flags,
@@ -1208,19 +1248,18 @@ static int fanotify_add_mark(struct fsnotify_group *group,
        }
 
        /*
-        * Non evictable mark cannot be downgraded to evictable mark.
+        * Check if requested mark flags conflict with an existing mark flags.
         */
-       if (fan_flags & FAN_MARK_EVICTABLE &&
-           !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) {
-               ret = -EEXIST;
+       ret = fanotify_may_update_existing_mark(fsn_mark, fan_flags);
+       if (ret)
                goto out;
-       }
 
        /*
         * Error events are pre-allocated per group, only if strictly
         * needed (i.e. FAN_FS_ERROR was requested).
         */
-       if (!(fan_flags & FAN_MARK_IGNORED_MASK) && (mask & FAN_FS_ERROR)) {
+       if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS) &&
+           (mask & FAN_FS_ERROR)) {
                ret = fanotify_group_init_error_pool(group);
                if (ret)
                        goto out;
@@ -1261,10 +1300,10 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
 
        /*
         * If some other task has this inode open for write we should not add
-        * an ignored mark, unless that ignored mark is supposed to survive
+        * an ignore mask, unless that ignore mask is supposed to survive
         * modification changes anyway.
         */
-       if ((flags & FAN_MARK_IGNORED_MASK) &&
+       if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
            !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
            inode_is_open_for_write(inode))
                return 0;
@@ -1520,7 +1559,8 @@ static int fanotify_events_supported(struct fsnotify_group *group,
        unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
        /* Strict validation of events in non-dir inode mask with v5.17+ APIs */
        bool strict_dir_events = FAN_GROUP_FLAG(group, FAN_REPORT_TARGET_FID) ||
-                                (mask & FAN_RENAME);
+                                (mask & FAN_RENAME) ||
+                                (flags & FAN_MARK_IGNORE);
 
        /*
         * Some filesystems such as 'proc' acquire unusual locks when opening
@@ -1557,7 +1597,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        __kernel_fsid_t __fsid, *fsid = NULL;
        u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
        unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
-       bool ignored = flags & FAN_MARK_IGNORED_MASK;
+       unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
+       unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS;
        unsigned int obj_type, fid_mode;
        u32 umask = 0;
        int ret;
@@ -1586,7 +1627,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
                return -EINVAL;
        }
 
-       switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
+       switch (mark_cmd) {
        case FAN_MARK_ADD:
        case FAN_MARK_REMOVE:
                if (!mask)
@@ -1606,9 +1647,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        if (mask & ~valid_mask)
                return -EINVAL;
 
-       /* Event flags (ONDIR, ON_CHILD) are meaningless in ignored mask */
-       if (ignored)
+
+       /* We don't allow FAN_MARK_IGNORE & FAN_MARK_IGNORED_MASK together */
+       if (ignore == (FAN_MARK_IGNORE | FAN_MARK_IGNORED_MASK))
+               return -EINVAL;
+
+       /*
+        * Event flags (FAN_ONDIR, FAN_EVENT_ON_CHILD) have no effect with
+        * FAN_MARK_IGNORED_MASK.
+        */
+       if (ignore == FAN_MARK_IGNORED_MASK) {
                mask &= ~FANOTIFY_EVENT_FLAGS;
+               umask = FANOTIFY_EVENT_FLAGS;
+       }
 
        f = fdget(fanotify_fd);
        if (unlikely(!f.file))
@@ -1672,7 +1723,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        if (mask & FAN_RENAME && !(fid_mode & FAN_REPORT_NAME))
                goto fput_and_out;
 
-       if (flags & FAN_MARK_FLUSH) {
+       if (mark_cmd == FAN_MARK_FLUSH) {
                ret = 0;
                if (mark_type == FAN_MARK_MOUNT)
                        fsnotify_clear_vfsmount_marks_by_group(group);
@@ -1688,7 +1739,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        if (ret)
                goto fput_and_out;
 
-       if (flags & FAN_MARK_ADD) {
+       if (mark_cmd == FAN_MARK_ADD) {
                ret = fanotify_events_supported(group, &path, mask, flags);
                if (ret)
                        goto path_put_and_out;
@@ -1712,6 +1763,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
        else
                mnt = path.mnt;
 
+       ret = mnt ? -EINVAL : -EISDIR;
+       /* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */
+       if (mark_cmd == FAN_MARK_ADD && ignore == FAN_MARK_IGNORE &&
+           (mnt || S_ISDIR(inode->i_mode)) &&
+           !(flags & FAN_MARK_IGNORED_SURV_MODIFY))
+               goto path_put_and_out;
+
        /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */
        if (mnt || !S_ISDIR(inode->i_mode)) {
                mask &= ~FAN_EVENT_ON_CHILD;
@@ -1721,12 +1779,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
                 * events with parent/name info for non-directory.
                 */
                if ((fid_mode & FAN_REPORT_DIR_FID) &&
-                   (flags & FAN_MARK_ADD) && !ignored)
+                   (flags & FAN_MARK_ADD) && !ignore)
                        mask |= FAN_EVENT_ON_CHILD;
        }
 
        /* create/update an inode mark */
-       switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
+       switch (mark_cmd) {
        case FAN_MARK_ADD:
                if (mark_type == FAN_MARK_MOUNT)
                        ret = fanotify_add_vfsmount_mark(group, mnt, mask,
@@ -1804,7 +1862,7 @@ static int __init fanotify_user_setup(void)
 
        BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
        BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12);
-       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 10);
+       BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
 
        fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
                                         SLAB_PANIC|SLAB_ACCOUNT);
index 59fb40a..55081ae 100644 (file)
@@ -113,7 +113,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
                        return;
                seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
                           inode->i_ino, inode->i_sb->s_dev,
-                          mflags, mark->mask, mark->ignored_mask);
+                          mflags, mark->mask, mark->ignore_mask);
                show_mark_fhandle(m, inode);
                seq_putc(m, '\n');
                iput(inode);
@@ -121,12 +121,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
                struct mount *mnt = fsnotify_conn_mount(mark->connector);
 
                seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
-                          mnt->mnt_id, mflags, mark->mask, mark->ignored_mask);
+                          mnt->mnt_id, mflags, mark->mask, mark->ignore_mask);
        } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) {
                struct super_block *sb = fsnotify_conn_sb(mark->connector);
 
                seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n",
-                          sb->s_dev, mflags, mark->mask, mark->ignored_mask);
+                          sb->s_dev, mflags, mark->mask, mark->ignore_mask);
        }
 }
 
index 0b3e749..7974e91 100644 (file)
@@ -100,7 +100,7 @@ void fsnotify_sb_delete(struct super_block *sb)
  * Given an inode, first check if we care what happens to our children.  Inotify
  * and dnotify both tell their parents about events.  If we care about any event
  * on a child we run all of our children and set a dentry flag saying that the
- * parent cares.  Thus when an event happens on a child it can quickly tell if
+ * parent cares.  Thus when an event happens on a child it can quickly tell
  * if there is a need to find a parent and send the event to the parent.
  */
 void __fsnotify_update_child_dentry_flags(struct inode *inode)
@@ -324,7 +324,8 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
        struct fsnotify_group *group = NULL;
        __u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
        __u32 marks_mask = 0;
-       __u32 marks_ignored_mask = 0;
+       __u32 marks_ignore_mask = 0;
+       bool is_dir = mask & FS_ISDIR;
        struct fsnotify_mark *mark;
        int type;
 
@@ -336,7 +337,7 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
                fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
                        if (!(mark->flags &
                              FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
-                               mark->ignored_mask = 0;
+                               mark->ignore_mask = 0;
                }
        }
 
@@ -344,14 +345,15 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
        fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
                group = mark->group;
                marks_mask |= mark->mask;
-               marks_ignored_mask |= mark->ignored_mask;
+               marks_ignore_mask |=
+                       fsnotify_effective_ignore_mask(mark, is_dir, type);
        }
 
-       pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
-                __func__, group, mask, marks_mask, marks_ignored_mask,
+       pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignore_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
+                __func__, group, mask, marks_mask, marks_ignore_mask,
                 data, data_type, dir, cookie);
 
-       if (!(test_mask & marks_mask & ~marks_ignored_mask))
+       if (!(test_mask & marks_mask & ~marks_ignore_mask))
                return 0;
 
        if (group->ops->handle_event) {
@@ -423,7 +425,8 @@ static bool fsnotify_iter_select_report_types(
                         * But is *this mark* watching children?
                         */
                        if (type == FSNOTIFY_ITER_TYPE_PARENT &&
-                           !(mark->mask & FS_EVENT_ON_CHILD))
+                           !(mark->mask & FS_EVENT_ON_CHILD) &&
+                           !(fsnotify_ignore_mask(mark) & FS_EVENT_ON_CHILD))
                                continue;
 
                        fsnotify_iter_set_report_type(iter_info, type);
@@ -532,8 +535,8 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
 
 
        /*
-        * If this is a modify event we may need to clear some ignored masks.
-        * In that case, the object with ignored masks will have the FS_MODIFY
+        * If this is a modify event we may need to clear some ignore masks.
+        * In that case, the object with ignore masks will have the FS_MODIFY
         * event in its mask.
         * Otherwise, return if none of the marks care about this type of event.
         */
index ed42a18..1c4bfda 100644 (file)
@@ -136,7 +136,7 @@ static inline u32 inotify_mask_to_arg(__u32 mask)
                       IN_Q_OVERFLOW);
 }
 
-/* intofiy userspace file descriptor functions */
+/* inotify userspace file descriptor functions */
 static __poll_t inotify_poll(struct file *file, poll_table *wait)
 {
        struct fsnotify_group *group = file->private_data;
index e517dbc..8ad743d 100644 (file)
 #define FANOTIFY_MARK_TYPE_BITS        (FAN_MARK_INODE | FAN_MARK_MOUNT | \
                                 FAN_MARK_FILESYSTEM)
 
+#define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \
+                                FAN_MARK_FLUSH)
+
+#define FANOTIFY_MARK_IGNORE_BITS (FAN_MARK_IGNORED_MASK | \
+                                  FAN_MARK_IGNORE)
+
 #define FANOTIFY_MARK_FLAGS    (FANOTIFY_MARK_TYPE_BITS | \
-                                FAN_MARK_ADD | \
-                                FAN_MARK_REMOVE | \
+                                FANOTIFY_MARK_CMD_BITS | \
+                                FANOTIFY_MARK_IGNORE_BITS | \
                                 FAN_MARK_DONT_FOLLOW | \
                                 FAN_MARK_ONLYDIR | \
-                                FAN_MARK_IGNORED_MASK | \
                                 FAN_MARK_IGNORED_SURV_MODIFY | \
-                                FAN_MARK_EVICTABLE | \
-                                FAN_MARK_FLUSH)
+                                FAN_MARK_EVICTABLE)
 
 /*
  * Events that can be reported with data type FSNOTIFY_EVENT_PATH.
index 9560734..d7d96c8 100644 (file)
@@ -518,8 +518,8 @@ struct fsnotify_mark {
        struct hlist_node obj_list;
        /* Head of list of marks for an object [mark ref] */
        struct fsnotify_mark_connector *connector;
-       /* Events types to ignore [mark->lock, group->mark_mutex] */
-       __u32 ignored_mask;
+       /* Events types and flags to ignore [mark->lock, group->mark_mutex] */
+       __u32 ignore_mask;
        /* General fsnotify mark flags */
 #define FSNOTIFY_MARK_FLAG_ALIVE               0x0001
 #define FSNOTIFY_MARK_FLAG_ATTACHED            0x0002
@@ -529,6 +529,7 @@ struct fsnotify_mark {
        /* fanotify mark flags */
 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
 #define FSNOTIFY_MARK_FLAG_NO_IREF             0x0200
+#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS    0x0400
        unsigned int flags;             /* flags [mark->lock] */
 };
 
@@ -655,15 +656,91 @@ extern void fsnotify_remove_queued_event(struct fsnotify_group *group,
 
 /* functions used to manipulate the marks attached to inodes */
 
-/* Get mask for calculating object interest taking ignored mask into account */
+/*
+ * Canonical "ignore mask" including event flags.
+ *
+ * Note the subtle semantic difference from the legacy ->ignored_mask.
+ * ->ignored_mask traditionally only meant which events should be ignored,
+ * while ->ignore_mask also includes flags regarding the type of objects on
+ * which events should be ignored.
+ */
+static inline __u32 fsnotify_ignore_mask(struct fsnotify_mark *mark)
+{
+       __u32 ignore_mask = mark->ignore_mask;
+
+       /* The event flags in ignore mask take effect */
+       if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
+               return ignore_mask;
+
+       /*
+        * Legacy behavior:
+        * - Always ignore events on dir
+        * - Ignore events on child if parent is watching children
+        */
+       ignore_mask |= FS_ISDIR;
+       ignore_mask &= ~FS_EVENT_ON_CHILD;
+       ignore_mask |= mark->mask & FS_EVENT_ON_CHILD;
+
+       return ignore_mask;
+}
+
+/* Legacy ignored_mask - only event types to ignore */
+static inline __u32 fsnotify_ignored_events(struct fsnotify_mark *mark)
+{
+       return mark->ignore_mask & ALL_FSNOTIFY_EVENTS;
+}
+
+/*
+ * Check if mask (or ignore mask) should be applied depending if victim is a
+ * directory and whether it is reported to a watching parent.
+ */
+static inline bool fsnotify_mask_applicable(__u32 mask, bool is_dir,
+                                           int iter_type)
+{
+       /* Should mask be applied to a directory? */
+       if (is_dir && !(mask & FS_ISDIR))
+               return false;
+
+       /* Should mask be applied to a child? */
+       if (iter_type == FSNOTIFY_ITER_TYPE_PARENT &&
+           !(mask & FS_EVENT_ON_CHILD))
+               return false;
+
+       return true;
+}
+
+/*
+ * Effective ignore mask taking into account if event victim is a
+ * directory and whether it is reported to a watching parent.
+ */
+static inline __u32 fsnotify_effective_ignore_mask(struct fsnotify_mark *mark,
+                                                  bool is_dir, int iter_type)
+{
+       __u32 ignore_mask = fsnotify_ignored_events(mark);
+
+       if (!ignore_mask)
+               return 0;
+
+       /* For non-dir and non-child, no need to consult the event flags */
+       if (!is_dir && iter_type != FSNOTIFY_ITER_TYPE_PARENT)
+               return ignore_mask;
+
+       ignore_mask = fsnotify_ignore_mask(mark);
+       if (!fsnotify_mask_applicable(ignore_mask, is_dir, iter_type))
+               return 0;
+
+       return ignore_mask & ALL_FSNOTIFY_EVENTS;
+}
+
+/* Get mask for calculating object interest taking ignore mask into account */
 static inline __u32 fsnotify_calc_mask(struct fsnotify_mark *mark)
 {
        __u32 mask = mark->mask;
 
-       if (!mark->ignored_mask)
+       if (!fsnotify_ignored_events(mark))
                return mask;
 
-       /* Interest in FS_MODIFY may be needed for clearing ignored mask */
+       /* Interest in FS_MODIFY may be needed for clearing ignore mask */
        if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
                mask |= FS_MODIFY;
 
@@ -671,7 +748,7 @@ static inline __u32 fsnotify_calc_mask(struct fsnotify_mark *mark)
         * If mark is interested in ignoring events on children, the object must
         * show interest in those events for fsnotify_parent() to notice it.
         */
-       return mask | (mark->ignored_mask & ALL_FSNOTIFY_EVENTS);
+       return mask | mark->ignore_mask;
 }
 
 /* Get mask of events for a list of marks */
index f1f8913..d8536d7 100644 (file)
 #define FAN_MARK_FLUSH         0x00000080
 /* FAN_MARK_FILESYSTEM is      0x00000100 */
 #define FAN_MARK_EVICTABLE     0x00000200
+/* This bit is mutually exclusive with FAN_MARK_IGNORED_MASK bit */
+#define FAN_MARK_IGNORE                0x00000400
 
 /* These are NOT bitwise flags.  Both bits can be used togther.  */
 #define FAN_MARK_INODE         0x00000000
 #define FAN_MARK_MOUNT         0x00000010
 #define FAN_MARK_FILESYSTEM    0x00000100
 
+/*
+ * Convenience macro - FAN_MARK_IGNORE requires FAN_MARK_IGNORED_SURV_MODIFY
+ * for non-inode mark types.
+ */
+#define FAN_MARK_IGNORE_SURV   (FAN_MARK_IGNORE | FAN_MARK_IGNORED_SURV_MODIFY)
+
 /* Deprecated - do not use this in programs and do not add new flags here! */
 #define FAN_ALL_MARK_FLAGS     (FAN_MARK_ADD |\
                                 FAN_MARK_REMOVE |\