Currently an fanotify group can subscribe to events FAN_MNT_ATTACH/DETACH only on mark type FAN_MARK_MNTNS and IFF the group was initialized with FAN_REPORT_MNT. Hence, mount events can not be mixed on the same group with filesystem events and mntns marks cannot be mixed with filesystem type marks. Define a new property for an fsnotify group called fsnotify_group_type which describes the category of events and mark types and use a distinct category FSNOTIFY_GROUP_TYPE_NAMESPACE for the mount event groups. Create helpers fsnotify_is_{filesystem,namespace}_watcher() and the macro FANOTIFY_NAMESPACE_INIT_FLAGS and use them to generalize some code that was checking the FAN_REPORT_MNT flag. FAN_REPORT_MNT is both a namespace group type selector and an information mode that controls the event metadata. Signed-off-by: Amir Goldstein --- fs/notify/fanotify/fanotify.c | 4 +++- fs/notify/fanotify/fanotify_user.c | 37 ++++++++++++++++++++++-------- fs/notify/group.c | 11 +++++---- include/linux/fanotify.h | 10 ++++++-- include/linux/fsnotify_backend.h | 26 +++++++++++++++++++++ 5 files changed, 72 insertions(+), 16 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 71b3a2361929c..bfaec0a4fc2f4 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -312,9 +312,11 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", __func__, iter_info->report_mask, event_mask, data, data_type); - if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { + if (fsnotify_is_namespace_watcher(group)) { if (data_type != FSNOTIFY_EVENT_MNT) return 0; + } else if (WARN_ON_ONCE(!fsnotify_is_filesystem_watcher(group))) { + return 0; } else if (!fid_mode) { /* Do we have path to open a file descriptor? */ if (!path) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 22c6424c334a0..6bad0f1a17eb2 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1582,8 +1582,10 @@ static struct hlist_head *fanotify_alloc_merge_hash(void) DEFINE_CLASS(fsnotify_group, struct fsnotify_group *, if (!IS_ERR_OR_NULL(_T)) fsnotify_destroy_group(_T), - fsnotify_alloc_group(ops, flags), - const struct fsnotify_ops *ops, int flags) + __fsnotify_alloc_group(ops, type, flags, gfp), + const struct fsnotify_ops *ops, + enum fsnotify_group_type type, + int flags, gfp_t gfp) /* fanotify syscalls */ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) @@ -1593,6 +1595,10 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) unsigned int fid_mode = flags & FANOTIFY_FID_BITS; unsigned int class = flags & FANOTIFY_CLASS_BITS; unsigned int internal_flags = 0; + /* A group is either for watching filesystems or namespaces */ + enum fsnotify_group_type type = + (flags & FANOTIFY_NAMESPACE_INIT_FLAGS) ? + FSNOTIFY_GROUP_TYPE_NAMESPACE : FSNOTIFY_GROUP_TYPE_FILESYSTEM; pr_debug("%s: flags=%x event_f_flags=%x\n", __func__, flags, event_f_flags); @@ -1604,7 +1610,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) * queue/marks. */ if (((flags & FANOTIFY_ADMIN_INIT_FLAGS) || - !(flags & (FANOTIFY_FID_BITS | FAN_REPORT_MNT))) && + !(flags & (FANOTIFY_FID_BITS | FANOTIFY_NAMESPACE_INIT_FLAGS))) && !capable(CAP_SYS_ADMIN)) return -EPERM; @@ -1624,8 +1630,11 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) #endif return -EINVAL; - /* Don't allow mixing mnt events with inode events for now */ - if (flags & FAN_REPORT_MNT) { + /* + * Namespace watchers do not support priority classes and do not + * support reporting file info event extensions. + */ + if (type == FSNOTIFY_GROUP_TYPE_NAMESPACE) { if (class != FAN_CLASS_NOTIF) return -EINVAL; if (flags & (FANOTIFY_FID_BITS | FAN_REPORT_FD_ERROR)) @@ -1669,8 +1678,9 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (flags & FAN_NONBLOCK) f_flags |= O_NONBLOCK; - CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops, - FSNOTIFY_GROUP_FLAG_USER); + CLASS(fsnotify_group, group)(&fanotify_fsnotify_ops, type, + FSNOTIFY_GROUP_FLAG_USER, + GFP_KERNEL_ACCOUNT); /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ if (IS_ERR(group)) return PTR_ERR(group); @@ -1873,6 +1883,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS; + enum fsnotify_group_type mark_group_type; unsigned int obj_type, fid_mode; void *obj = NULL; u32 umask = 0; @@ -1891,15 +1902,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, switch (mark_type) { case FAN_MARK_INODE: obj_type = FSNOTIFY_OBJ_TYPE_INODE; + mark_group_type = FSNOTIFY_GROUP_TYPE_FILESYSTEM; break; case FAN_MARK_MOUNT: obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT; + mark_group_type = FSNOTIFY_GROUP_TYPE_FILESYSTEM; break; case FAN_MARK_FILESYSTEM: obj_type = FSNOTIFY_OBJ_TYPE_SB; + mark_group_type = FSNOTIFY_GROUP_TYPE_FILESYSTEM; break; case FAN_MARK_MNTNS: obj_type = FSNOTIFY_OBJ_TYPE_MNTNS; + mark_group_type = FSNOTIFY_GROUP_TYPE_NAMESPACE; break; default: return -EINVAL; @@ -1948,6 +1963,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, return -EINVAL; group = fd_file(f)->private_data; + if (group->type != mark_group_type) + return -EINVAL; + /* Only report mount events on mnt namespace */ if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { if (mask & ~FANOTIFY_MOUNT_EVENTS) @@ -1993,14 +2011,15 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, return -EINVAL; /* - * Events that do not carry enough information to report + * Filesystem events that do not carry enough information to report * event->fd require a group that supports reporting fid. Those * events are not supported on a mount mark, because they do not * carry enough information (i.e. path) to be filtered by mount * point. */ fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); - if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_MOUNT_EVENTS|FANOTIFY_EVENT_FLAGS) && + if (fsnotify_is_filesystem_watcher(group) && + mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) && (!fid_mode || mark_type == FAN_MARK_MOUNT)) return -EINVAL; diff --git a/fs/notify/group.c b/fs/notify/group.c index cf445e270ba6a..071d34051da95 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -111,9 +111,9 @@ void fsnotify_put_group(struct fsnotify_group *group) } EXPORT_SYMBOL_GPL(fsnotify_put_group); -static struct fsnotify_group *__fsnotify_alloc_group( - const struct fsnotify_ops *ops, - int flags, gfp_t gfp) +struct fsnotify_group *__fsnotify_alloc_group(const struct fsnotify_ops *ops, + enum fsnotify_group_type type, + int flags, gfp_t gfp) { struct fsnotify_group *group; @@ -135,9 +135,11 @@ static struct fsnotify_group *__fsnotify_alloc_group( group->ops = ops; group->flags = flags; + group->type = type; return group; } +EXPORT_SYMBOL_GPL(__fsnotify_alloc_group); /* * Create a new fsnotify_group and hold a reference for the group returned. @@ -148,7 +150,8 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops, gfp_t gfp = (flags & FSNOTIFY_GROUP_FLAG_USER) ? GFP_KERNEL_ACCOUNT : GFP_KERNEL; - return __fsnotify_alloc_group(ops, flags, gfp); + return __fsnotify_alloc_group(ops, FSNOTIFY_GROUP_TYPE_FILESYSTEM, + flags, gfp); } EXPORT_SYMBOL_GPL(fsnotify_alloc_group); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 879cff5eccd4e..ddfcfd37b231c 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -25,7 +25,12 @@ #define FANOTIFY_FID_BITS (FAN_REPORT_DFID_NAME_TARGET) -#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD | FAN_REPORT_MNT) +/* fanotify_init() flags related to information reported with events */ +#define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD | \ + FAN_REPORT_MNT) + +/* fanotify_init() flags that create a namespace watcher */ +#define FANOTIFY_NAMESPACE_INIT_FLAGS (FAN_REPORT_MNT) /* * fanotify_init() flags that require CAP_SYS_ADMIN. @@ -47,7 +52,8 @@ * so one of the flags for reporting file handles is required. */ #define FANOTIFY_USER_INIT_FLAGS (FAN_CLASS_NOTIF | \ - FANOTIFY_FID_BITS | FAN_REPORT_MNT | \ + FANOTIFY_FID_BITS | \ + FANOTIFY_NAMESPACE_INIT_FLAGS | \ FAN_CLOEXEC | FAN_NONBLOCK) #define FANOTIFY_INIT_FLAGS (FANOTIFY_ADMIN_INIT_FLAGS | \ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index ad310de907819..68c001ae55071 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -204,6 +204,15 @@ enum fsnotify_group_prio { __FSNOTIFY_PRIO_NUM }; +/* + * Category of kernel objects watched by this group. + * Every category has its own mark types and event types. + */ +enum fsnotify_group_type { + FSNOTIFY_GROUP_TYPE_FILESYSTEM = 0, + FSNOTIFY_GROUP_TYPE_NAMESPACE, +}; + /* * A group is a "thing" that wants to receive notification about filesystem * events. The mask holds the subset of event types this group cares about. @@ -230,6 +239,7 @@ struct fsnotify_group { unsigned int q_len; /* events on the queue */ unsigned int max_events; /* maximum events allowed on the list */ enum fsnotify_group_prio priority; /* priority for sending events */ + enum fsnotify_group_type type; /* type of watched objects */ bool shutdown; /* group is being shut down, don't queue more events */ #define FSNOTIFY_GROUP_FLAG_USER 0x01 /* user allocated group */ @@ -280,6 +290,18 @@ struct fsnotify_group { }; }; +static inline bool fsnotify_is_filesystem_watcher( + const struct fsnotify_group *group) +{ + return group->type == FSNOTIFY_GROUP_TYPE_FILESYSTEM; +} + +static inline bool fsnotify_is_namespace_watcher( + const struct fsnotify_group *group) +{ + return group->type == FSNOTIFY_GROUP_TYPE_NAMESPACE; +} + /* * These helpers are used to prevent deadlock when reclaiming inodes with * evictable marks of the same group that is allocating a new mark. @@ -727,6 +749,10 @@ static inline void fsnotify_update_flags(struct dentry *dentry) /* called from fsnotify listeners, such as fanotify or dnotify */ /* create a new group */ +extern struct fsnotify_group *__fsnotify_alloc_group( + const struct fsnotify_ops *ops, + enum fsnotify_group_type type, + int flags, gfp_t gfp); extern struct fsnotify_group *fsnotify_alloc_group( const struct fsnotify_ops *ops, int flags); -- 2.55.0