On Mon, Nov 9, 2020 at 8:00 PM Amir Goldstein <amir73il@xxxxxxxxx> wrote: > > A filesystem view is a subtree of a filesystem accessible from a specific > mount point. When marking an FS view, user expects to get events on all > inodes that are accessible from the marked mount, even if the events > were generated from another mount. > > In particular, the events such as FAN_CREATE, FAN_MOVE, FAN_DELETE that > are not delivered to a mount mark can be delivered to an FS view mark. > > One example of a filesystem view is btrfs subvolume, which cannot be > marked with a regular filesystem mark. > > Another example of a filesystem view is a bind mount, not on the root of > the filesystem, such as the bind mounts used for containers. > > A filesystem view mark is composed of a heads sb mark and an sb_view mark. > The filesystem view mark is connected to the head sb mark and the head > sb mark is connected to the sb object. The mask of the head sb mask is > a cumulative mask of all the associated sb_view mark masks. > > Filesystem view marks cannot co-exist with a regular filesystem mark on > the same filesystem. > > When an event is generated on the head sb mark, fsnotify iterates the > list of associated sb_view marks and filter events that happen outside > of the sb_view mount's root. > > Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> > --- > > Jan, > > I started to play with ideas of filtering events by subtree. > My first approach was to do that in userspace. > > This becomes quite easy if you are able to create a bind mount on the > subtree of interest - you use a 'mount_fd' from within the bind mount > for open_by_handle_at() of the dirfid and the magic symlink in /proc > resolves that fd to / for dirs outside the bind mount path. > > Having done that, it seems pretty silly to go to userspace and back to > kernel for the filtering, so many wasted cycles when we can do the same > filtering much sooner. > > The name "fs view" is inspired by Mark's proposal of the same name [1]. > We do not have to restrict the "fs view" points to mount points, but it > created conventient semantics and I pretty much like the idea that > FAN_MARK_MOUNT|FAN_MARK_FILESYSTEM gives you this new hybrid thing. > > This is just a POC that I sketched with obvious missing pieces, but at > least with a single fs view mark that I tested it works. > > Thoughts? > > Amir. > > > [1] https://lore.kernel.org/linux-fsdevel/20180508180436.716-2-mfasheh@xxxxxxx/ > > fs/notify/fanotify/fanotify_user.c | 115 ++++++++++++++++++++++++++--- > fs/notify/fdinfo.c | 9 +++ > fs/notify/fsnotify.c | 37 +++++++++- > fs/notify/fsnotify.h | 8 ++ > fs/notify/group.c | 2 +- > fs/notify/mark.c | 11 ++- > include/linux/fanotify.h | 1 + > include/linux/fsnotify_backend.h | 44 +++++++++-- > include/uapi/linux/fanotify.h | 2 + > 9 files changed, 207 insertions(+), 22 deletions(-) > > diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c > index 3e01d8f2ab90..1a46898a1bc8 100644 > --- a/fs/notify/fanotify/fanotify_user.c > +++ b/fs/notify/fanotify/fanotify_user.c > @@ -760,7 +760,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, > > removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, > umask, &destroy_mark); > - if (removed & fsnotify_conn_mask(fsn_mark->connector)) > + if (!mask || (removed & fsnotify_conn_mask(fsn_mark->connector))) > fsnotify_recalc_mask(fsn_mark->connector); > if (destroy_mark) > fsnotify_detach_mark(fsn_mark); > @@ -781,6 +781,35 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, > mask, flags, umask); > } > > +static int fanotify_remove_sb_view_mark(struct fsnotify_group *group, > + struct vfsmount *mnt, __u32 mask, > + unsigned int flags, __u32 umask) > +{ > + struct fsnotify_mark *sb_mark; > + int ret; > + > + /* Find the head sb mark */ > + mutex_lock(&group->mark_mutex); > + sb_mark = fsnotify_find_mark(&mnt->mnt_sb->s_fsnotify_marks, group); > + mutex_unlock(&group->mark_mutex); > + /* Cannot have both sb mark and sb_view marks on the same sb */ > + if (!sb_mark || !(sb_mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD)) { > + if (sb_mark) > + fsnotify_put_mark(sb_mark); > + return -ENOENT; > + } > + > + /* Update or destroy the sb_view mark */ > + ret = fanotify_remove_mark(group, &fsnotify_sbv_mark(sb_mark)->sbv_marks, > + mask, flags, umask); > + fsnotify_put_mark(sb_mark); > + if (ret) > + return ret; > + > + /* Remove mask 0 to update or destroy the head sb mark */ > + return fanotify_remove_mark(group, &mnt->mnt_sb->s_fsnotify_marks, 0, flags, umask); > +} > + > static int fanotify_remove_sb_mark(struct fsnotify_group *group, > struct super_block *sb, __u32 mask, > unsigned int flags, __u32 umask) > @@ -833,6 +862,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group, > return ERR_PTR(-ENOMEM); > > fsnotify_init_mark(mark, group); > + fsnotify_sbv_mark(mark)->mnt = NULL; > ret = fsnotify_add_mark_locked(mark, connp, type, 0, fsid); > if (ret) { > fsnotify_put_mark(mark); > @@ -843,13 +873,19 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group, > } > > > -static int fanotify_add_mark(struct fsnotify_group *group, > +/* Returns fsnotify_mark with elevated refcount */ > +static struct fsnotify_mark *__fanotify_add_mark(struct fsnotify_group *group, > fsnotify_connp_t *connp, unsigned int type, > __u32 mask, unsigned int flags, > - __kernel_fsid_t *fsid) > + __kernel_fsid_t *fsid, struct vfsmount *mnt) > { > struct fsnotify_mark *fsn_mark; > __u32 added; > + unsigned int sb_view_head = 0; > + > + if (type == FSNOTIFY_OBJ_TYPE_SB && > + (flags & FAN_MARK_FS_VIEW) == FAN_MARK_FS_VIEW) > + sb_view_head = FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD; > > mutex_lock(&group->mark_mutex); > fsn_mark = fsnotify_find_mark(connp, group); > @@ -857,14 +893,38 @@ static int fanotify_add_mark(struct fsnotify_group *group, > fsn_mark = fanotify_add_new_mark(group, connp, type, fsid); > if (IS_ERR(fsn_mark)) { > mutex_unlock(&group->mark_mutex); > - return PTR_ERR(fsn_mark); > + return fsn_mark; > } > + if (type == FSNOTIFY_OBJ_TYPE_SB_VIEW) > + fsnotify_sbv_mark(fsn_mark)->mnt = mnt; > + else > + fsn_mark->flags |= sb_view_head; > + > + } else if ((fsn_mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD) != sb_view_head) { > + /* Cannot have both sb mark and sb_view marks on the same sb */ > + mutex_unlock(&group->mark_mutex); > + fsnotify_put_mark(fsn_mark); > + return ERR_PTR(-EEXIST); > } > added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); > - if (added & ~fsnotify_conn_mask(fsn_mark->connector)) > + if (!mask || (added & ~fsnotify_conn_mask(fsn_mark->connector))) > fsnotify_recalc_mask(fsn_mark->connector); > mutex_unlock(&group->mark_mutex); > > + return fsn_mark; > +} > + > +static int fanotify_add_mark(struct fsnotify_group *group, > + fsnotify_connp_t *connp, unsigned int type, > + __u32 mask, unsigned int flags, > + __kernel_fsid_t *fsid, struct vfsmount *mnt) > +{ > + struct fsnotify_mark *fsn_mark; > + > + fsn_mark = __fanotify_add_mark(group, connp, type, mask, flags, fsid, mnt); > + if (IS_ERR(fsn_mark)) > + return PTR_ERR(fsn_mark); > + > fsnotify_put_mark(fsn_mark); > return 0; > } > @@ -874,7 +934,31 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, > unsigned int flags, __kernel_fsid_t *fsid) > { > return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks, > - FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid); > + FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid, NULL); > +} > + > +static int fanotify_add_sb_view_mark(struct fsnotify_group *group, > + struct vfsmount *mnt, __u32 mask, > + unsigned int flags, __kernel_fsid_t *fsid) > +{ > + struct fsnotify_mark *sb_mark; > + int ret; > + > + sb_mark = __fanotify_add_mark(group, &mnt->mnt_sb->s_fsnotify_marks, > + FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid, mnt); > + if (IS_ERR(sb_mark)) > + return PTR_ERR(sb_mark); > + > + /* Add to sb_view mark */ > + ret = fanotify_add_mark(group, &fsnotify_sbv_mark(sb_mark)->sbv_marks, > + FSNOTIFY_OBJ_TYPE_SB_VIEW, mask, flags, fsid, mnt); > + fsnotify_put_mark(sb_mark); > + if (ret) > + return ret; > + > + /* Add mask 0 to re-calc the sb object mask after updating the sb view head mark mask */ > + return fanotify_add_mark(group, &mnt->mnt_sb->s_fsnotify_marks, FSNOTIFY_OBJ_TYPE_SB, 0, > + flags, fsid, mnt); > } > > static int fanotify_add_sb_mark(struct fsnotify_group *group, > @@ -882,7 +966,7 @@ static int fanotify_add_sb_mark(struct fsnotify_group *group, > unsigned int flags, __kernel_fsid_t *fsid) > { > return fanotify_add_mark(group, &sb->s_fsnotify_marks, > - FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid); > + FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid, NULL); > } > > static int fanotify_add_inode_mark(struct fsnotify_group *group, > @@ -902,7 +986,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, > return 0; > > return fanotify_add_mark(group, &inode->i_fsnotify_marks, > - FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid); > + FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid, NULL); > } > > static struct fsnotify_event *fanotify_alloc_overflow_event(void) > @@ -1116,7 +1200,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, > struct path path; > __kernel_fsid_t __fsid, *fsid = NULL; > u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS; > - unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; > + unsigned int mark_type = FANOTIFY_MARK_TYPE(flags); > bool ignored = flags & FAN_MARK_IGNORED_MASK; > unsigned int obj_type, fid_mode; > u32 umask = 0; > @@ -1139,6 +1223,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, > case FAN_MARK_MOUNT: > obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT; > break; > + case FAN_MARK_FS_VIEW: > + obj_type = FSNOTIFY_OBJ_TYPE_SB_VIEW; > + break; > case FAN_MARK_FILESYSTEM: > obj_type = FSNOTIFY_OBJ_TYPE_SB; > break; > @@ -1205,6 +1292,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, > ret = 0; > if (mark_type == FAN_MARK_MOUNT) > fsnotify_clear_vfsmount_marks_by_group(group); > + else if (mark_type == FAN_MARK_FS_VIEW) > + fsnotify_clear_sb_view_marks_by_group(group); > else if (mark_type == FAN_MARK_FILESYSTEM) > fsnotify_clear_sb_marks_by_group(group); > else > @@ -1256,6 +1345,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, > if (mark_type == FAN_MARK_MOUNT) > ret = fanotify_add_vfsmount_mark(group, mnt, mask, > flags, fsid); > + else if (mark_type == FAN_MARK_FS_VIEW) > + ret = fanotify_add_sb_view_mark(group, mnt, mask, > + flags, fsid); > else if (mark_type == FAN_MARK_FILESYSTEM) > ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask, > flags, fsid); > @@ -1267,6 +1359,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, > if (mark_type == FAN_MARK_MOUNT) > ret = fanotify_remove_vfsmount_mark(group, mnt, mask, > flags, umask); > + else if (mark_type == FAN_MARK_FS_VIEW) > + ret = fanotify_remove_sb_view_mark(group, mnt, mask, > + flags, umask); > else if (mark_type == FAN_MARK_FILESYSTEM) > ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask, > flags, umask); > @@ -1318,7 +1413,7 @@ static int __init fanotify_user_setup(void) > BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10); > BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9); > > - fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, > + fanotify_mark_cache = KMEM_CACHE(fsnotify_sb_view_mark, > SLAB_PANIC|SLAB_ACCOUNT); > fanotify_fid_event_cachep = KMEM_CACHE(fanotify_fid_event, > SLAB_PANIC); > diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c > index f0d6b54be412..4b00575e9bd1 100644 > --- a/fs/notify/fdinfo.c > +++ b/fs/notify/fdinfo.c > @@ -115,6 +115,9 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) > > if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY) > mflags |= FAN_MARK_IGNORED_SURV_MODIFY; > + if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB_VIEW || > + (mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD)) > + mflags |= FAN_MARK_FS_VIEW; > > if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) { > inode = igrab(fsnotify_conn_inode(mark->connector)); > @@ -131,6 +134,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) > > seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", > mnt->mnt_id, mflags, mark->mask, mark->ignored_mask); > + } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB_VIEW) { > + struct vfsmount *mnt = fsnotify_sbv_mark(mark)->mnt; > + > + seq_printf(m, "fanotify sdev:%x mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", > + mnt->mnt_sb->s_dev, real_mount(mnt)->mnt_id, mflags, mark->mask, > + mark->ignored_mask); > } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) { > struct super_block *sb = fsnotify_conn_sb(mark->connector); > > diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c > index 8d3ad5ef2925..7af2132372fc 100644 > --- a/fs/notify/fsnotify.c > +++ b/fs/notify/fsnotify.c > @@ -275,6 +275,9 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask, > return ops->handle_inode_event(child_mark, mask, inode, NULL, NULL); > } > > +static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector **connp); > +static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark); > + > static int send_to_group(__u32 mask, const void *data, int data_type, > struct inode *dir, const struct qstr *file_name, > u32 cookie, struct fsnotify_iter_info *iter_info) > @@ -305,9 +308,39 @@ static int send_to_group(__u32 mask, const void *data, int data_type, > if (!fsnotify_iter_should_report_type(iter_info, type)) > continue; > mark = iter_info->marks[type]; > + if (!mark) > + continue; > + > /* does the object mark tell us to do something? */ > - if (mark) { > - group = mark->group; > + group = mark->group; > + if (type == FSNOTIFY_OBJ_TYPE_SB && mark && > + (mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD)) { > + const struct path *path = fsnotify_data_path(data, data_type); > + struct fsnotify_sb_view_mark *sbv_mark = (void *)mark; > + > + /* TODO: pass FSNOTIFY_EVENT_DENTRY data type for dir modify events */ > + if (!path || !sbv_mark->sbv_marks) > + continue; > + > + /* > + * Iterate sb_view marks and apply masks if victim is under mnt root. > + * We already have rcu read lock and d_ancestor is accurate enough Yeh, we don't have rcu read lock. We need to either take it or use the more strict is_subdir() check. > + * for our needs - if any of the ancestors have been moved in or out > + * of the mnt root path, we may either send the event or not. > + * The important thing is that if ancestry was always under mnt root > + * we will send the event. > + */ > + for (sbv_mark = (void *)fsnotify_first_mark(&sbv_mark->sbv_marks); > + sbv_mark; sbv_mark = (void *)fsnotify_next_mark((void *)sbv_mark)) { > + if ((!(test_mask & sbv_mark->fsn_mark.mask) && > + !(test_mask & sbv_mark->fsn_mark.ignored_mask)) || > + !d_ancestor(sbv_mark->mnt->mnt_root, path->dentry)) > + continue; > + > + marks_mask |= sbv_mark->fsn_mark.mask; > + marks_ignored_mask |= sbv_mark->fsn_mark.ignored_mask; > + } > + } else { > marks_mask |= mark->mask; > marks_ignored_mask |= mark->ignored_mask; > } > diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h > index ff2063ec6b0f..5aaabf2bee31 100644 > --- a/fs/notify/fsnotify.h > +++ b/fs/notify/fsnotify.h > @@ -21,6 +21,12 @@ static inline struct mount *fsnotify_conn_mount( > return container_of(conn->obj, struct mount, mnt_fsnotify_marks); > } > > +static inline struct fsnotify_sb_view_mark *fsnotify_conn_sb_view_mark( > + struct fsnotify_mark_connector *conn) > +{ > + return container_of(conn->obj, struct fsnotify_sb_view_mark, sbv_marks); > +} > + > static inline struct super_block *fsnotify_conn_sb( > struct fsnotify_mark_connector *conn) > { > @@ -48,11 +54,13 @@ static inline void fsnotify_clear_marks_by_inode(struct inode *inode) > static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) > { > fsnotify_destroy_marks(&real_mount(mnt)->mnt_fsnotify_marks); > + /* TODO: clear sb_view marks associated with this mnt */ > } > /* run the list of all marks associated with sb and destroy them */ > static inline void fsnotify_clear_marks_by_sb(struct super_block *sb) > { > fsnotify_destroy_marks(&sb->s_fsnotify_marks); > + /* TODO: clear sb_view marks associated with this sb */ > } > > /* > diff --git a/fs/notify/group.c b/fs/notify/group.c > index a4a4b1c64d32..a5367b66f33a 100644 > --- a/fs/notify/group.c > +++ b/fs/notify/group.c > @@ -58,7 +58,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group) > fsnotify_group_stop_queueing(group); > > /* Clear all marks for this group and queue them for destruction */ > - fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK); > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK, 0); > > /* > * Some marks can still be pinned when waiting for response from > diff --git a/fs/notify/mark.c b/fs/notify/mark.c > index 8387937b9d01..81825de6a6a9 100644 > --- a/fs/notify/mark.c > +++ b/fs/notify/mark.c > @@ -103,6 +103,8 @@ static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn) > return &fsnotify_conn_inode(conn)->i_fsnotify_mask; > else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) > return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask; > + else if (conn->type == FSNOTIFY_OBJ_TYPE_SB_VIEW) > + return &fsnotify_conn_sb_view_mark(conn)->fsn_mark.mask; > else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) > return &fsnotify_conn_sb(conn)->s_fsnotify_mask; > return NULL; > @@ -185,6 +187,8 @@ static void *fsnotify_detach_connector_from_object( > atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs); > } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { > fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; > + } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB_VIEW) { > + fsnotify_conn_sb_view_mark(conn)->fsn_mark.mask = 0; > } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { > fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; > } > @@ -720,9 +724,9 @@ struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp, > } > EXPORT_SYMBOL_GPL(fsnotify_find_mark); > > -/* Clear any marks in a group with given type mask */ > +/* Clear any marks in a group with given type mask or given flags */ > void fsnotify_clear_marks_by_group(struct fsnotify_group *group, > - unsigned int type_mask) > + unsigned int type_mask, unsigned int flags_mask) > { > struct fsnotify_mark *lmark, *mark; > LIST_HEAD(to_free); > @@ -744,7 +748,8 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group, > */ > mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING); > list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { > - if ((1U << mark->connector->type) & type_mask) > + if (((1U << mark->connector->type) & type_mask) || > + (mark->flags & flags_mask)) > list_move(&mark->g_list, &to_free); > } > mutex_unlock(&group->mark_mutex); > diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h > index 3e9c56ee651f..6f36bece5518 100644 > --- a/include/linux/fanotify.h > +++ b/include/linux/fanotify.h > @@ -27,6 +27,7 @@ > > #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ > FAN_MARK_FILESYSTEM) > +#define FANOTIFY_MARK_TYPE(flags) ((flags) & FANOTIFY_MARK_TYPE_BITS) > > #define FANOTIFY_MARK_FLAGS (FANOTIFY_MARK_TYPE_BITS | \ > FAN_MARK_ADD | \ > diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h > index f8529a3a2923..441b3d5d9241 100644 > --- a/include/linux/fsnotify_backend.h > +++ b/include/linux/fsnotify_backend.h > @@ -279,6 +279,7 @@ enum fsnotify_obj_type { > FSNOTIFY_OBJ_TYPE_INODE, > FSNOTIFY_OBJ_TYPE_CHILD, > FSNOTIFY_OBJ_TYPE_VFSMOUNT, > + FSNOTIFY_OBJ_TYPE_SB_VIEW, > FSNOTIFY_OBJ_TYPE_SB, > FSNOTIFY_OBJ_TYPE_COUNT, > FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT > @@ -287,6 +288,7 @@ enum fsnotify_obj_type { > #define FSNOTIFY_OBJ_TYPE_INODE_FL (1U << FSNOTIFY_OBJ_TYPE_INODE) > #define FSNOTIFY_OBJ_TYPE_CHILD_FL (1U << FSNOTIFY_OBJ_TYPE_CHILD) > #define FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL (1U << FSNOTIFY_OBJ_TYPE_VFSMOUNT) > +#define FSNOTIFY_OBJ_TYPE_SB_VIEW_FL (1U << FSNOTIFY_OBJ_TYPE_SB_VIEW) > #define FSNOTIFY_OBJ_TYPE_SB_FL (1U << FSNOTIFY_OBJ_TYPE_SB) > #define FSNOTIFY_OBJ_ALL_TYPES_MASK ((1U << FSNOTIFY_OBJ_TYPE_COUNT) - 1) > > @@ -403,9 +405,30 @@ struct fsnotify_mark { > #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x01 > #define FSNOTIFY_MARK_FLAG_ALIVE 0x02 > #define FSNOTIFY_MARK_FLAG_ATTACHED 0x04 > +#define FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD 0x08 > unsigned int flags; /* flags [mark->lock] */ > }; > > +/* > + * The head sb_view mark is connected to an sb objects and the rest of the > + * sb_view marks are connected to the head mark. > + * The mask on the head mark is the cumulative mask of all sb_view marks. > + */ > +struct fsnotify_sb_view_mark { > + struct fsnotify_mark fsn_mark; > + union { > + /* sb_view marks connected to this head sb mark */ > + struct fsnotify_mark_connector __rcu *sbv_marks; > + /* mntpoint associated with this sb view mark */ > + struct vfsmount *mnt; > + }; > +}; > + > +static inline struct fsnotify_sb_view_mark *fsnotify_sbv_mark(struct fsnotify_mark *fsn_mark) > +{ > + return container_of(fsn_mark, struct fsnotify_sb_view_mark, fsn_mark); > +} > + > #ifdef CONFIG_FSNOTIFY > > /* called from the vfs helpers */ > @@ -552,22 +575,31 @@ extern void fsnotify_detach_mark(struct fsnotify_mark *mark); > extern void fsnotify_free_mark(struct fsnotify_mark *mark); > /* Wait until all marks queued for destruction are destroyed */ > extern void fsnotify_wait_marks_destroyed(void); > -/* run all the marks in a group, and clear all of the marks attached to given object type */ > -extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group, unsigned int type); > +/* run all the marks in a group, and clear all of the marks by object type and flags */ > +extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group, unsigned int type, > + unsigned int flags); > /* run all the marks in a group, and clear all of the vfsmount marks */ > static inline void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group) > { > - fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL); > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL, 0); > } > /* run all the marks in a group, and clear all of the inode marks */ > static inline void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group) > { > - fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE_FL); > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE_FL, 0); > +} > +/* run all the marks in a group, and clear all of the sb view marks */ > +static inline void fsnotify_clear_sb_view_marks_by_group(struct fsnotify_group *group) > +{ > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_VIEW_FL, 0); > + /* Clear all sb marks associated with sb view marks */ > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL, > + FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD); > } > -/* run all the marks in a group, and clear all of the sn marks */ > +/* run all the marks in a group, and clear all of the sb marks */ > static inline void fsnotify_clear_sb_marks_by_group(struct fsnotify_group *group) > { > - fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL); > + fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL, 0); > } > extern void fsnotify_get_mark(struct fsnotify_mark *mark); > extern void fsnotify_put_mark(struct fsnotify_mark *mark); > diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h > index fbf9c5c7dd59..c8e43c0ed89b 100644 > --- a/include/uapi/linux/fanotify.h > +++ b/include/uapi/linux/fanotify.h > @@ -79,6 +79,8 @@ > #define FAN_MARK_INODE 0x00000000 > #define FAN_MARK_MOUNT 0x00000010 > #define FAN_MARK_FILESYSTEM 0x00000100 > +/* FS View is a subtree of a filesystem accessible from a specific mount point */ > +#define FAN_MARK_FS_VIEW (FAN_MARK_FILESYSTEM | FAN_MARK_MOUNT) > > /* Deprecated - do not use this in programs and do not add new flags here! */ > #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ > -- > 2.25.1 >