Commit e252f2ed authored by Amir Goldstein's avatar Amir Goldstein Committed by Jan Kara

fanotify: introduce FAN_MARK_IGNORE

This flag is a new way to configure ignore mask which allows adding and
removing the event flags FAN_ONDIR and FAN_EVENT_ON_CHILD in ignore mask.

The legacy FAN_MARK_IGNORED_MASK flag would always ignore events on
directories and would ignore events on children depending on whether
the FAN_EVENT_ON_CHILD flag was set in the (non ignored) mask.

FAN_MARK_IGNORE can be used to ignore events on children without setting
FAN_EVENT_ON_CHILD in the mark's mask and will not ignore events on
directories unconditionally, only when FAN_ONDIR is set in ignore mask.

The new behavior is non-downgradable.  After calling fanotify_mark() with
FAN_MARK_IGNORE once, calling fanotify_mark() with FAN_MARK_IGNORED_MASK
on the same object will return EEXIST error.

Setting the event flags with FAN_MARK_IGNORE on a non-dir inode mark
has no meaning and will return ENOTDIR error.

The meaning of FAN_MARK_IGNORED_SURV_MODIFY is preserved with the new
FAN_MARK_IGNORE flag, but with a few semantic differences:

1. FAN_MARK_IGNORED_SURV_MODIFY is required for filesystem and mount
   marks and on an inode mark on a directory. Omitting this flag
   will return EINVAL or EISDIR error.

2. An ignore mask on a non-directory inode that survives modify could
   never be downgraded to an ignore mask that does not survive modify.
   With new FAN_MARK_IGNORE semantics we make that rule explicit -
   trying to update a surviving ignore mask without the flag
   FAN_MARK_IGNORED_SURV_MODIFY will return EEXIST error.

The conveniene macro FAN_MARK_IGNORE_SURV is added for
(FAN_MARK_IGNORE | FAN_MARK_IGNORED_SURV_MODIFY), because the
common case should use short constant names.

Link: https://lore.kernel.org/r/20220629144210.2983229-4-amir73il@gmail.comSigned-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 8afd7215
...@@ -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,7 +1009,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, ...@@ -1009,7 +1009,7 @@ 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->ignore_mask &= ~mask; fsn_mark->ignore_mask &= ~mask;
...@@ -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 ignore 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,7 +1129,7 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, ...@@ -1120,7 +1129,7 @@ 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->ignore_mask |= mask; fsn_mark->ignore_mask |= mask;
...@@ -1197,6 +1206,24 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark, ...@@ -1197,6 +1206,24 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
!(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
return -EEXIST; 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; return 0;
} }
...@@ -1231,7 +1258,8 @@ static int fanotify_add_mark(struct fsnotify_group *group, ...@@ -1231,7 +1258,8 @@ static int fanotify_add_mark(struct fsnotify_group *group,
* 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;
...@@ -1275,7 +1303,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, ...@@ -1275,7 +1303,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
* an ignore mask, unless that ignore mask 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;
...@@ -1531,7 +1559,8 @@ static int fanotify_events_supported(struct fsnotify_group *group, ...@@ -1531,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
...@@ -1569,7 +1598,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1569,7 +1598,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
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;
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
bool ignore = flags & FAN_MARK_IGNORED_MASK; 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;
...@@ -1618,12 +1647,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1618,12 +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;
/* 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 * Event flags (FAN_ONDIR, FAN_EVENT_ON_CHILD) have no effect with
* FAN_MARK_IGNORED_MASK. * FAN_MARK_IGNORED_MASK.
*/ */
if (ignore) 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))
...@@ -1727,6 +1763,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1727,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;
...@@ -1819,7 +1862,7 @@ static int __init fanotify_user_setup(void) ...@@ -1819,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);
......
...@@ -62,11 +62,14 @@ ...@@ -62,11 +62,14 @@
#define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \ #define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \
FAN_MARK_FLUSH) 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 | \
FANOTIFY_MARK_CMD_BITS | \ FANOTIFY_MARK_CMD_BITS | \
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)
......
...@@ -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