Commit bec14d79 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
Loading
Loading
Loading
Loading
+11 −8
Original line number Diff line number Diff line
@@ -295,12 +295,13 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
				     const void *data, int data_type,
				     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 |
				     FANOTIFY_EVENT_FLAGS;
	const struct path *path = fsnotify_data_path(data, data_type);
	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
	struct fsnotify_mark *mark;
	bool ondir = event_mask & FAN_ONDIR;
	int type;

	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;
	} else if (!(fid_mode & FAN_REPORT_FID)) {
		/* Do we have a directory inode to report? */
		if (!dir && !(event_mask & FS_ISDIR))
		if (!dir && !ondir)
			return 0;
	}

	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
		 * events on dir, don't send it!
		 * Send the event depending on event flags in mark mask.
		 */
		if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR))
		if (!fsnotify_mask_applicable(mark->mask, ondir, type))
			continue;

		marks_mask |= mark->mask;
@@ -336,7 +339,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
		*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
+2 −0
Original line number Diff line number Diff line
@@ -499,6 +499,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
		mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
	if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
		mflags |= FAN_MARK_EVICTABLE;
	if (mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
		mflags |= FAN_MARK_IGNORE;

	return mflags;
}
+84 −26
Original line number Diff line number Diff line
@@ -1009,10 +1009,10 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
	mask &= ~umask;
	spin_lock(&fsn_mark->lock);
	oldmask = fsnotify_calc_mask(fsn_mark);
	if (!(flags & FAN_MARK_IGNORED_MASK)) {
	if (!(flags & FANOTIFY_MARK_IGNORE_BITS)) {
		fsn_mark->mask &= ~mask;
	} else {
		fsn_mark->ignored_mask &= ~mask;
		fsn_mark->ignore_mask &= ~mask;
	}
	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.
	 * 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);

	return oldmask & ~newmask;
@@ -1085,15 +1085,24 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
				       unsigned int fan_flags)
{
	bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
	unsigned int ignore = fan_flags & FANOTIFY_MARK_IGNORE_BITS;
	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
	 * 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) &&
	    (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
	if (ignore && (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;
		if (!(fsn_mark->mask & FS_MODIFY))
@@ -1120,10 +1129,10 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
	bool recalc;

	spin_lock(&fsn_mark->lock);
	if (!(fan_flags & FAN_MARK_IGNORED_MASK))
	if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
		fsn_mark->mask |= mask;
	else
		fsn_mark->ignored_mask |= mask;
		fsn_mark->ignore_mask |= mask;

	recalc = fsnotify_calc_mask(fsn_mark) &
		~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));
}

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,
			     fsnotify_connp_t *connp, unsigned int obj_type,
			     __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 &&
	    !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) {
		ret = -EEXIST;
	ret = fanotify_may_update_existing_mark(fsn_mark, fan_flags);
	if (ret)
		goto out;
	}

	/*
	 * Error events are pre-allocated per group, only if strictly
	 * 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);
		if (ret)
			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
	 * 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.
	 */
	if ((flags & FAN_MARK_IGNORED_MASK) &&
	if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
	    !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
	    inode_is_open_for_write(inode))
		return 0;
@@ -1520,7 +1559,8 @@ static int fanotify_events_supported(struct fsnotify_group *group,
	unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
	/* 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) ||
				 (mask & FAN_RENAME);
				 (mask & FAN_RENAME) ||
				 (flags & FAN_MARK_IGNORE);

	/*
	 * 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;
	u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
	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;
	u32 umask = 0;
	int ret;
@@ -1586,7 +1627,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
		return -EINVAL;
	}

	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
	switch (mark_cmd) {
	case FAN_MARK_ADD:
	case FAN_MARK_REMOVE:
		if (!mask)
@@ -1606,9 +1647,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	if (mask & ~valid_mask)
		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;
		umask = FANOTIFY_EVENT_FLAGS;
	}

	f = fdget(fanotify_fd);
	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))
		goto fput_and_out;

	if (flags & FAN_MARK_FLUSH) {
	if (mark_cmd == FAN_MARK_FLUSH) {
		ret = 0;
		if (mark_type == FAN_MARK_MOUNT)
			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)
		goto fput_and_out;

	if (flags & FAN_MARK_ADD) {
	if (mark_cmd == FAN_MARK_ADD) {
		ret = fanotify_events_supported(group, &path, mask, flags);
		if (ret)
			goto path_put_and_out;
@@ -1712,6 +1763,13 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	else
		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 */
	if (mnt || !S_ISDIR(inode->i_mode)) {
		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.
		 */
		if ((fid_mode & FAN_REPORT_DIR_FID) &&
		    (flags & FAN_MARK_ADD) && !ignored)
		    (flags & FAN_MARK_ADD) && !ignore)
			mask |= FAN_EVENT_ON_CHILD;
	}

	/* create/update an inode mark */
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
	switch (mark_cmd) {
	case FAN_MARK_ADD:
		if (mark_type == FAN_MARK_MOUNT)
			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(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,
					 SLAB_PANIC|SLAB_ACCOUNT);
+3 −3
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
			return;
		seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
			   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);
		seq_putc(m, '\n');
		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);

		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) {
		struct super_block *sb = fsnotify_conn_sb(mark->connector);

		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);
	}
}

+13 −10
Original line number Diff line number Diff line
@@ -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
 * 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
 * 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.
 */
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;
	__u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
	__u32 marks_mask = 0;
	__u32 marks_ignored_mask = 0;
	__u32 marks_ignore_mask = 0;
	bool is_dir = mask & FS_ISDIR;
	struct fsnotify_mark *mark;
	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) {
			if (!(mark->flags &
			      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) {
		group = mark->group;
		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",
		 __func__, group, mask, marks_mask, marks_ignored_mask,
	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_ignore_mask,
		 data, data_type, dir, cookie);

	if (!(test_mask & marks_mask & ~marks_ignored_mask))
	if (!(test_mask & marks_mask & ~marks_ignore_mask))
		return 0;

	if (group->ops->handle_event) {
@@ -423,7 +425,8 @@ static bool fsnotify_iter_select_report_types(
			 * But is *this mark* watching children?
			 */
			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;

			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.
	 * In that case, the object with ignored masks will have the FS_MODIFY
	 * If this is a modify event we may need to clear some ignore masks.
	 * In that case, the object with ignore masks will have the FS_MODIFY
	 * event in its mask.
	 * Otherwise, return if none of the marks care about this type of event.
	 */
Loading