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
This commit is contained in:
@@ -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,
|
|||||||
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,
|
|||||||
*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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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)
|
|||||||
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,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* 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,
|
|||||||
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,
|
|||||||
__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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
* 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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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(
|
|||||||
* 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,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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)
|
|||||||
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 @@
|
|||||||
#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 {
|
|||||||
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 {
|
|||||||
/* 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,
|
|||||||
|
|
||||||
/* 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)
|
|||||||
* 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 @@
|
|||||||
#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 |\
|
||||||
|
|||||||
Reference in New Issue
Block a user