Commit 14bd41e4 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull fsnotify updates from Jan Kara:
 "A few fsnotify fixes from Amir fixing fallout from big fsnotify
  overhaul a few months back and an improvement of defaults limiting
  maximum number of inotify watches from Waiman"

* tag 'fsnotify_for_v5.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: fix events reported to watching parent and child
  inotify: convert to handle_inode_event() interface
  fsnotify: generalize handle_inode_event()
  inotify: Increase default inotify.max_user_watches limit to 1048576
parents d652d5f1 fecc4559
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -600,7 +600,7 @@ static struct notifier_block nfsd_file_lease_notifier = {
static int
nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
				struct inode *inode, struct inode *dir,
				const struct qstr *name)
				const struct qstr *name, u32 cookie)
{
	trace_nfsd_file_fsnotify_handle_event(inode, mask);

+1 −1
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark)
 */
static int dnotify_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
				struct inode *inode, struct inode *dir,
				const struct qstr *name)
				const struct qstr *name, u32 cookie)
{
	struct dnotify_mark *dn_mark;
	struct dnotify_struct *dn;
+3 −4
Original line number Diff line number Diff line
@@ -268,11 +268,10 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
			continue;

		/*
		 * If the event is for a child and this mark is on a parent not
		 * If the event is on a child and this mark is on a parent not
		 * watching children, don't send it!
		 */
		if (event_mask & FS_EVENT_ON_CHILD &&
		    type == FSNOTIFY_OBJ_TYPE_INODE &&
		if (type == FSNOTIFY_OBJ_TYPE_PARENT &&
		    !(mark->mask & FS_EVENT_ON_CHILD))
			continue;

+71 −36
Original line number Diff line number Diff line
@@ -152,6 +152,13 @@ static bool fsnotify_event_needs_parent(struct inode *inode, struct mount *mnt,
	if (mask & FS_ISDIR)
		return false;

	/*
	 * All events that are possible on child can also may be reported with
	 * parent/name info to inode/sb/mount.  Otherwise, a watching parent
	 * could result in events reported with unexpected name info to sb/mount.
	 */
	BUILD_BUG_ON(FS_EVENTS_POSS_ON_CHILD & ~FS_EVENTS_POSS_TO_PARENT);

	/* Did either inode/sb/mount subscribe for events with parent/name? */
	marks_mask |= fsnotify_parent_needed_mask(inode->i_fsnotify_mask);
	marks_mask |= fsnotify_parent_needed_mask(inode->i_sb->s_fsnotify_mask);
@@ -232,47 +239,76 @@ notify:
}
EXPORT_SYMBOL_GPL(__fsnotify_parent);

static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
				 const void *data, int data_type,
static int fsnotify_handle_inode_event(struct fsnotify_group *group,
				       struct fsnotify_mark *inode_mark,
				       u32 mask, const void *data, int data_type,
				       struct inode *dir, const struct qstr *name,
				 u32 cookie, struct fsnotify_iter_info *iter_info)
				       u32 cookie)
{
	struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
	struct fsnotify_mark *child_mark = fsnotify_iter_child_mark(iter_info);
	const struct path *path = fsnotify_data_path(data, data_type);
	struct inode *inode = fsnotify_data_inode(data, data_type);
	const struct fsnotify_ops *ops = group->ops;
	int ret;

	if (WARN_ON_ONCE(!ops->handle_inode_event))
		return 0;

	if ((inode_mark->mask & FS_EXCL_UNLINK) &&
	    path && d_unlinked(path->dentry))
		return 0;

	/* Check interest of this mark in case event was sent with two marks */
	if (!(mask & inode_mark->mask & ALL_FSNOTIFY_EVENTS))
		return 0;

	return ops->handle_inode_event(inode_mark, mask, inode, dir, name, cookie);
}

static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
				 const void *data, int data_type,
				 struct inode *dir, const struct qstr *name,
				 u32 cookie, struct fsnotify_iter_info *iter_info)
{
	struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
	struct fsnotify_mark *parent_mark = fsnotify_iter_parent_mark(iter_info);
	int ret;

	if (WARN_ON_ONCE(fsnotify_iter_sb_mark(iter_info)) ||
	    WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info)))
		return 0;

	if (parent_mark) {
		/*
	 * An event can be sent on child mark iterator instead of inode mark
	 * iterator because of other groups that have interest of this inode
	 * and have marks on both parent and child.  We can simplify this case.
		 * parent_mark indicates that the parent inode is watching
		 * children and interested in this event, which is an event
		 * possible on child. But is *this mark* watching children and
		 * interested in this event?
		 */
	if (!inode_mark) {
		inode_mark = child_mark;
		child_mark = NULL;
		dir = NULL;
		name = NULL;
	}

	ret = ops->handle_inode_event(inode_mark, mask, inode, dir, name);
	if (ret || !child_mark)
		if (parent_mark->mask & FS_EVENT_ON_CHILD) {
			ret = fsnotify_handle_inode_event(group, parent_mark, mask,
							  data, data_type, dir, name, 0);
			if (ret)
				return ret;
		}
		if (!inode_mark)
			return 0;
	}

	if (mask & FS_EVENT_ON_CHILD) {
		/*
		 * Some events can be sent on both parent dir and child marks
	 * (e.g. FS_ATTRIB).  If both parent dir and child are watching,
	 * report the event once to parent dir with name and once to child
	 * without name.
		 * (e.g. FS_ATTRIB).  If both parent dir and child are
		 * watching, report the event once to parent dir with name (if
		 * interested) and once to child without name (if interested).
		 * The child watcher is expecting an event without a file name
		 * and without the FS_EVENT_ON_CHILD flag.
		 */
	return ops->handle_inode_event(child_mark, mask, inode, NULL, NULL);
		mask &= ~FS_EVENT_ON_CHILD;
		dir = NULL;
		name = NULL;
	}

	return fsnotify_handle_inode_event(group, inode_mark, mask, data, data_type,
					   dir, name, cookie);
}

static int send_to_group(__u32 mask, const void *data, int data_type,
@@ -430,7 +466,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
	struct fsnotify_iter_info iter_info = {};
	struct super_block *sb;
	struct mount *mnt = NULL;
	struct inode *child = NULL;
	struct inode *parent = NULL;
	int ret = 0;
	__u32 test_mask, marks_mask;

@@ -442,11 +478,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
		inode = dir;
	} else if (mask & FS_EVENT_ON_CHILD) {
		/*
		 * Event on child - report on TYPE_INODE to dir if it is
		 * watching children and on TYPE_CHILD to child.
		 * Event on child - report on TYPE_PARENT to dir if it is
		 * watching children and on TYPE_INODE to child.
		 */
		child = inode;
		inode = dir;
		parent = dir;
	}
	sb = inode->i_sb;

@@ -460,7 +495,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
	if (!sb->s_fsnotify_marks &&
	    (!mnt || !mnt->mnt_fsnotify_marks) &&
	    (!inode || !inode->i_fsnotify_marks) &&
	    (!child || !child->i_fsnotify_marks))
	    (!parent || !parent->i_fsnotify_marks))
		return 0;

	marks_mask = sb->s_fsnotify_mask;
@@ -468,8 +503,8 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
		marks_mask |= mnt->mnt_fsnotify_mask;
	if (inode)
		marks_mask |= inode->i_fsnotify_mask;
	if (child)
		marks_mask |= child->i_fsnotify_mask;
	if (parent)
		marks_mask |= parent->i_fsnotify_mask;


	/*
@@ -492,9 +527,9 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
		iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
			fsnotify_first_mark(&inode->i_fsnotify_marks);
	}
	if (child) {
		iter_info.marks[FSNOTIFY_OBJ_TYPE_CHILD] =
			fsnotify_first_mark(&child->i_fsnotify_marks);
	if (parent) {
		iter_info.marks[FSNOTIFY_OBJ_TYPE_PARENT] =
			fsnotify_first_mark(&parent->i_fsnotify_marks);
	}

	/*
+4 −5
Original line number Diff line number Diff line
@@ -24,11 +24,10 @@ static inline struct inotify_event_info *INOTIFY_E(struct fsnotify_event *fse)

extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
					   struct fsnotify_group *group);
extern int inotify_handle_event(struct fsnotify_group *group, u32 mask,
				const void *data, int data_type,
extern int inotify_handle_inode_event(struct fsnotify_mark *inode_mark,
				      u32 mask, struct inode *inode,
				      struct inode *dir,
				const struct qstr *file_name, u32 cookie,
				struct fsnotify_iter_info *iter_info);
				      const struct qstr *name, u32 cookie);

extern const struct fsnotify_ops inotify_fsnotify_ops;
extern struct kmem_cache *inotify_inode_mark_cachep;
Loading