Commit bec14d79 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fsnotify_for_v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

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
parents af07685b feee1ce4
...@@ -295,12 +295,13 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, ...@@ -295,12 +295,13 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
const void *data, int data_type, const void *data, int data_type,
struct inode *dir) 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 | __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
FANOTIFY_EVENT_FLAGS; FANOTIFY_EVENT_FLAGS;
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
bool ondir = event_mask & FAN_ONDIR;
int type; int type;
pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", 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, ...@@ -315,19 +316,21 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
return 0; return 0;
} else if (!(fid_mode & FAN_REPORT_FID)) { } else if (!(fid_mode & FAN_REPORT_FID)) {
/* Do we have a directory inode to report? */ /* Do we have a directory inode to report? */
if (!dir && !(event_mask & FS_ISDIR)) if (!dir && !ondir)
return 0; return 0;
} }
fsnotify_foreach_iter_mark_type(iter_info, mark, type) { 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 * Send the event depending on event flags in mark mask.
* events on dir, don't send it!
*/ */
if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR)) if (!fsnotify_mask_applicable(mark->mask, ondir, type))
continue; continue;
marks_mask |= mark->mask; marks_mask |= mark->mask;
...@@ -336,7 +339,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, ...@@ -336,7 +339,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
*match_mask |= 1U << type; *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 * For dirent modification events (create/delete/move) that do not carry
......
...@@ -499,6 +499,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark) ...@@ -499,6 +499,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY; mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF) if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
mflags |= FAN_MARK_EVICTABLE; mflags |= FAN_MARK_EVICTABLE;
if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
mflags |= FAN_MARK_IGNORE;
return mflags; return mflags;
} }
...@@ -1009,10 +1009,10 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, ...@@ -1009,10 +1009,10 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
mask &= ~umask; mask &= ~umask;
spin_lock(&fsn_mark->lock); spin_lock(&fsn_mark->lock);
oldmask = fsnotify_calc_mask(fsn_mark); oldmask = fsnotify_calc_mask(fsn_mark);
if (!(flags & FAN_MARK_IGNORED_MASK)) { if (!(flags & FANOTIFY_MARK_IGNORE_BITS)) {
fsn_mark->mask &= ~mask; fsn_mark->mask &= ~mask;
} else { } else {
fsn_mark->ignored_mask &= ~mask; fsn_mark->ignore_mask &= ~mask;
} }
newmask = fsnotify_calc_mask(fsn_mark); newmask = fsnotify_calc_mask(fsn_mark);
/* /*
...@@ -1021,7 +1021,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, ...@@ -1021,7 +1021,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
* changes to the mask. * changes to the mask.
* Destroy mark when only umask bits remain. * 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); spin_unlock(&fsn_mark->lock);
return oldmask & ~newmask; return oldmask & ~newmask;
...@@ -1085,15 +1085,24 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark, ...@@ -1085,15 +1085,24 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
unsigned int fan_flags) unsigned int fan_flags)
{ {
bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE); bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
unsigned int ignore = fan_flags & FANOTIFY_MARK_IGNORE_BITS;
bool recalc = false; 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 * 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 * 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) && if (ignore && (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
(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)) {
fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY; fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
if (!(fsn_mark->mask & FS_MODIFY)) if (!(fsn_mark->mask & FS_MODIFY))
...@@ -1120,10 +1129,10 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, ...@@ -1120,10 +1129,10 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
bool recalc; bool recalc;
spin_lock(&fsn_mark->lock); spin_lock(&fsn_mark->lock);
if (!(fan_flags & FAN_MARK_IGNORED_MASK)) if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
fsn_mark->mask |= mask; fsn_mark->mask |= mask;
else else
fsn_mark->ignored_mask |= mask; fsn_mark->ignore_mask |= mask;
recalc = fsnotify_calc_mask(fsn_mark) & recalc = fsnotify_calc_mask(fsn_mark) &
~fsnotify_conn_mask(fsn_mark->connector); ~fsnotify_conn_mask(fsn_mark->connector);
...@@ -1187,6 +1196,37 @@ static int fanotify_group_init_error_pool(struct fsnotify_group *group) ...@@ -1187,6 +1196,37 @@ static int fanotify_group_init_error_pool(struct fsnotify_group *group)
sizeof(struct fanotify_error_event)); 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, static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, unsigned int obj_type, fsnotify_connp_t *connp, unsigned int obj_type,
__u32 mask, unsigned int fan_flags, __u32 mask, unsigned int fan_flags,
...@@ -1208,19 +1248,18 @@ static int fanotify_add_mark(struct fsnotify_group *group, ...@@ -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 && ret = fanotify_may_update_existing_mark(fsn_mark, fan_flags);
!(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) { if (ret)
ret = -EEXIST;
goto out; goto out;
}
/* /*
* Error events are pre-allocated per group, only if strictly * Error events are pre-allocated per group, only if strictly
* needed (i.e. FAN_FS_ERROR was requested). * 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); ret = fanotify_group_init_error_pool(group);
if (ret) if (ret)
goto out; goto out;
...@@ -1261,10 +1300,10 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, ...@@ -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 * 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. * modification changes anyway.
*/ */
if ((flags & FAN_MARK_IGNORED_MASK) && if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
!(flags & FAN_MARK_IGNORED_SURV_MODIFY) && !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
inode_is_open_for_write(inode)) inode_is_open_for_write(inode))
return 0; return 0;
...@@ -1520,7 +1559,8 @@ static int fanotify_events_supported(struct fsnotify_group *group, ...@@ -1520,7 +1559,8 @@ static int fanotify_events_supported(struct fsnotify_group *group,
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
/* Strict validation of events in non-dir inode mask with v5.17+ APIs */ /* 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) || 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 * 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, ...@@ -1557,7 +1597,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
__kernel_fsid_t __fsid, *fsid = NULL; __kernel_fsid_t __fsid, *fsid = NULL;
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS; u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS; 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; unsigned int obj_type, fid_mode;
u32 umask = 0; u32 umask = 0;
int ret; int ret;
...@@ -1586,7 +1627,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1586,7 +1627,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
return -EINVAL; return -EINVAL;
} }
switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { switch (mark_cmd) {
case FAN_MARK_ADD: case FAN_MARK_ADD:
case FAN_MARK_REMOVE: case FAN_MARK_REMOVE:
if (!mask) if (!mask)
...@@ -1606,9 +1647,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1606,9 +1647,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
if (mask & ~valid_mask) if (mask & ~valid_mask)
return -EINVAL; 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; mask &= ~FANOTIFY_EVENT_FLAGS;
umask = FANOTIFY_EVENT_FLAGS;
}
f = fdget(fanotify_fd); f = fdget(fanotify_fd);
if (unlikely(!f.file)) if (unlikely(!f.file))
...@@ -1672,7 +1723,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -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)) if (mask & FAN_RENAME && !(fid_mode & FAN_REPORT_NAME))
goto fput_and_out; goto fput_and_out;
if (flags & FAN_MARK_FLUSH) { if (mark_cmd == FAN_MARK_FLUSH) {
ret = 0; ret = 0;
if (mark_type == FAN_MARK_MOUNT) if (mark_type == FAN_MARK_MOUNT)
fsnotify_clear_vfsmount_marks_by_group(group); fsnotify_clear_vfsmount_marks_by_group(group);
...@@ -1688,7 +1739,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1688,7 +1739,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
if (ret) if (ret)
goto fput_and_out; goto fput_and_out;
if (flags & FAN_MARK_ADD) { if (mark_cmd == FAN_MARK_ADD) {
ret = fanotify_events_supported(group, &path, mask, flags); ret = fanotify_events_supported(group, &path, mask, flags);
if (ret) if (ret)
goto path_put_and_out; goto path_put_and_out;
...@@ -1712,6 +1763,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1712,6 +1763,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
else else
mnt = path.mnt; 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 */ /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */
if (mnt || !S_ISDIR(inode->i_mode)) { if (mnt || !S_ISDIR(inode->i_mode)) {
mask &= ~FAN_EVENT_ON_CHILD; mask &= ~FAN_EVENT_ON_CHILD;
...@@ -1721,12 +1779,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -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. * events with parent/name info for non-directory.
*/ */
if ((fid_mode & FAN_REPORT_DIR_FID) && if ((fid_mode & FAN_REPORT_DIR_FID) &&
(flags & FAN_MARK_ADD) && !ignored) (flags & FAN_MARK_ADD) && !ignore)
mask |= FAN_EVENT_ON_CHILD; mask |= FAN_EVENT_ON_CHILD;
} }
/* create/update an inode mark */ /* create/update an inode mark */
switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { switch (mark_cmd) {
case FAN_MARK_ADD: case FAN_MARK_ADD:
if (mark_type == FAN_MARK_MOUNT) if (mark_type == FAN_MARK_MOUNT)
ret = fanotify_add_vfsmount_mark(group, mnt, mask, ret = fanotify_add_vfsmount_mark(group, mnt, mask,
...@@ -1804,7 +1862,7 @@ static int __init fanotify_user_setup(void) ...@@ -1804,7 +1862,7 @@ static int __init fanotify_user_setup(void)
BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12); 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, fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
SLAB_PANIC|SLAB_ACCOUNT); SLAB_PANIC|SLAB_ACCOUNT);
......
...@@ -113,7 +113,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) ...@@ -113,7 +113,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
return; return;
seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ", seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
inode->i_ino, inode->i_sb->s_dev, 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); show_mark_fhandle(m, inode);
seq_putc(m, '\n'); seq_putc(m, '\n');
iput(inode); iput(inode);
...@@ -121,12 +121,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) ...@@ -121,12 +121,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
struct mount *mnt = fsnotify_conn_mount(mark->connector); struct mount *mnt = fsnotify_conn_mount(mark->connector);
seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n", 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) { } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) {
struct super_block *sb = fsnotify_conn_sb(mark->connector); struct super_block *sb = fsnotify_conn_sb(mark->connector);
seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", 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);
} }
} }
......
...@@ -100,7 +100,7 @@ void fsnotify_sb_delete(struct super_block *sb) ...@@ -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 * 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 * 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 * 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. * 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) 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, ...@@ -324,7 +324,8 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
struct fsnotify_group *group = NULL; struct fsnotify_group *group = NULL;
__u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS); __u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
__u32 marks_mask = 0; __u32 marks_mask = 0;
__u32 marks_ignored_mask = 0; __u32 marks_ignore_mask = 0;
bool is_dir = mask & FS_ISDIR;
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
int type; int type;
...@@ -336,7 +337,7 @@ static int send_to_group(__u32 mask, const void *data, int data_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) { fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
if (!(mark->flags & if (!(mark->flags &
FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) 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, ...@@ -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) { fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
group = mark->group; group = mark->group;
marks_mask |= mark->mask; 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", 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_ignored_mask, __func__, group, mask, marks_mask, marks_ignore_mask,
data, data_type, dir, cookie); data, data_type, dir, cookie);
if (!(test_mask & marks_mask & ~marks_ignored_mask)) if (!(test_mask & marks_mask & ~marks_ignore_mask))
return 0; return 0;
if (group->ops->handle_event) { if (group->ops->handle_event) {
...@@ -423,7 +425,8 @@ static bool fsnotify_iter_select_report_types( ...@@ -423,7 +425,8 @@ static bool fsnotify_iter_select_report_types(
* But is *this mark* watching children? * But is *this mark* watching children?
*/ */
if (type == FSNOTIFY_ITER_TYPE_PARENT && 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; continue;
fsnotify_iter_set_report_type(iter_info, type); 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, ...@@ -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. * If this is a modify event we may need to clear some ignore masks.
* In that case, the object with ignored masks will have the FS_MODIFY * In that case, the object with ignore masks will have the FS_MODIFY
* event in its mask. * event in its mask.
* Otherwise, return if none of the marks care about this type of event. * Otherwise, return if none of the marks care about this type of event.
*/ */
......
...@@ -136,7 +136,7 @@ static inline u32 inotify_mask_to_arg(__u32 mask) ...@@ -136,7 +136,7 @@ static inline u32 inotify_mask_to_arg(__u32 mask)
IN_Q_OVERFLOW); IN_Q_OVERFLOW);
} }
/* intofiy userspace file descriptor functions */ /* inotify userspace file descriptor functions */
static __poll_t inotify_poll(struct file *file, poll_table *wait) static __poll_t inotify_poll(struct file *file, poll_table *wait)
{ {
struct fsnotify_group *group = file->private_data; struct fsnotify_group *group = file->private_data;
......
...@@ -59,15 +59,19 @@ ...@@ -59,15 +59,19 @@
#define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \
FAN_MARK_FILESYSTEM) 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 | \ #define FANOTIFY_MARK_FLAGS (FANOTIFY_MARK_TYPE_BITS | \
FAN_MARK_ADD | \ FANOTIFY_MARK_CMD_BITS | \
FAN_MARK_REMOVE | \ FANOTIFY_MARK_IGNORE_BITS | \
FAN_MARK_DONT_FOLLOW | \ FAN_MARK_DONT_FOLLOW | \
FAN_MARK_ONLYDIR | \ FAN_MARK_ONLYDIR | \
FAN_MARK_IGNORED_MASK | \
FAN_MARK_IGNORED_SURV_MODIFY | \ FAN_MARK_IGNORED_SURV_MODIFY | \
FAN_MARK_EVICTABLE | \ FAN_MARK_EVICTABLE)
FAN_MARK_FLUSH)
/* /*
* Events that can be reported with data type FSNOTIFY_EVENT_PATH. * Events that can be reported with data type FSNOTIFY_EVENT_PATH.
......
...@@ -518,8 +518,8 @@ struct fsnotify_mark { ...@@ -518,8 +518,8 @@ struct fsnotify_mark {
struct hlist_node obj_list; struct hlist_node obj_list;
/* Head of list of marks for an object [mark ref] */ /* Head of list of marks for an object [mark ref] */
struct fsnotify_mark_connector *connector; struct fsnotify_mark_connector *connector;
/* Events types to ignore [mark->lock, group->mark_mutex] */ /* Events types and flags to ignore [mark->lock, group->mark_mutex] */
__u32 ignored_mask; __u32 ignore_mask;
/* General fsnotify mark flags */ /* General fsnotify mark flags */
#define FSNOTIFY_MARK_FLAG_ALIVE 0x0001 #define FSNOTIFY_MARK_FLAG_ALIVE 0x0001
#define FSNOTIFY_MARK_FLAG_ATTACHED 0x0002 #define FSNOTIFY_MARK_FLAG_ATTACHED 0x0002
...@@ -529,6 +529,7 @@ struct fsnotify_mark { ...@@ -529,6 +529,7 @@ struct fsnotify_mark {
/* fanotify mark flags */ /* fanotify mark flags */
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x0100
#define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200 #define FSNOTIFY_MARK_FLAG_NO_IREF 0x0200
#define FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS 0x0400
unsigned int flags; /* flags [mark->lock] */ unsigned int flags; /* flags [mark->lock] */
}; };
...@@ -655,15 +656,91 @@ extern void fsnotify_remove_queued_event(struct fsnotify_group *group, ...@@ -655,15 +656,91 @@ extern void fsnotify_remove_queued_event(struct fsnotify_group *group,
/* functions used to manipulate the marks attached to inodes */ /* 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) static inline __u32 fsnotify_calc_mask(struct fsnotify_mark *mark)
{ {
__u32 mask = mark->mask; __u32 mask = mark->mask;
if (!mark->ignored_mask) if (!fsnotify_ignored_events(mark))
return mask; 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)) if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
mask |= FS_MODIFY; mask |= FS_MODIFY;
...@@ -671,7 +748,7 @@ static inline __u32 fsnotify_calc_mask(struct fsnotify_mark *mark) ...@@ -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 * If mark is interested in ignoring events on children, the object must
* show interest in those events for fsnotify_parent() to notice it. * 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 */ /* Get mask of events for a list of marks */
......
...@@ -83,12 +83,20 @@ ...@@ -83,12 +83,20 @@
#define FAN_MARK_FLUSH 0x00000080 #define FAN_MARK_FLUSH 0x00000080
/* FAN_MARK_FILESYSTEM is 0x00000100 */ /* FAN_MARK_FILESYSTEM is 0x00000100 */
#define FAN_MARK_EVICTABLE 0x00000200 #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. */ /* These are NOT bitwise flags. Both bits can be used togther. */
#define FAN_MARK_INODE 0x00000000 #define FAN_MARK_INODE 0x00000000
#define FAN_MARK_MOUNT 0x00000010 #define FAN_MARK_MOUNT 0x00000010
#define FAN_MARK_FILESYSTEM 0x00000100 #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! */ /* Deprecated - do not use this in programs and do not add new flags here! */
#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
FAN_MARK_REMOVE |\ FAN_MARK_REMOVE |\
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment