Commit 5af9d1cf authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull fsnotify updates from Jan Kara:

 - reduce overhead of fsnotify infrastructure when no permission events
   are in use

 - a few small cleanups

* tag 'fsnotify_for_v6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: fix UAF from FS_ERROR event on a shutting down filesystem
  fsnotify: optimize the case of no permission event watchers
  fsnotify: use an enum for group priority constants
  fsnotify: move s_fsnotify_connectors into fsnotify_sb_info
  fsnotify: lazy attach fsnotify_sb_info state to sb
  fsnotify: create helper fsnotify_update_sb_watchers()
  fsnotify: pass object pointer and type to fsnotify mark helpers
  fanotify: merge two checks regarding add of ignore mark
  fsnotify: create a wrapper fsnotify_find_inode_mark()
  fsnotify: create helpers to get sb and connp from object
  fsnotify: rename fsnotify_{get,put}_sb_connectors()
  fsnotify: Avoid -Wflex-array-member-not-at-end warning
  fanotify: remove unneeded sub-zero check for unsigned value
parents daa12112 795bb82d
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode)

	do {
		fsnotify_group_lock(nfsd_file_fsnotify_group);
		mark = fsnotify_find_mark(&inode->i_fsnotify_marks,
		mark = fsnotify_find_inode_mark(inode,
						nfsd_file_fsnotify_group);
		if (mark) {
			nfm = nfsd_file_mark_get(container_of(mark,
+2 −2
Original line number Diff line number Diff line
@@ -162,7 +162,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
	if (!S_ISDIR(inode->i_mode))
		return;

	fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group);
	fsn_mark = fsnotify_find_inode_mark(inode, dnotify_group);
	if (!fsn_mark)
		return;
	dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
@@ -326,7 +326,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg)
	fsnotify_group_lock(dnotify_group);

	/* add the new_fsn_mark or find an old one. */
	fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group);
	fsn_mark = fsnotify_find_inode_mark(inode, dnotify_group);
	if (fsn_mark) {
		dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
		spin_lock(&fsn_mark->lock);
+43 −100
Original line number Diff line number Diff line
@@ -502,7 +502,7 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
	}

	/* Pad with 0's */
	WARN_ON_ONCE(len < 0 || len >= FANOTIFY_EVENT_ALIGN);
	WARN_ON_ONCE(len >= FANOTIFY_EVENT_ALIGN);
	if (len > 0 && clear_user(buf, len))
		return -EFAULT;

@@ -1076,7 +1076,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
}

static int fanotify_remove_mark(struct fsnotify_group *group,
				fsnotify_connp_t *connp, __u32 mask,
				void *obj, unsigned int obj_type, __u32 mask,
				unsigned int flags, __u32 umask)
{
	struct fsnotify_mark *fsn_mark = NULL;
@@ -1084,7 +1084,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
	int destroy_mark;

	fsnotify_group_lock(group);
	fsn_mark = fsnotify_find_mark(connp, group);
	fsn_mark = fsnotify_find_mark(obj, obj_type, group);
	if (!fsn_mark) {
		fsnotify_group_unlock(group);
		return -ENOENT;
@@ -1105,30 +1105,6 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
	return 0;
}

static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
					 struct vfsmount *mnt, __u32 mask,
					 unsigned int flags, __u32 umask)
{
	return fanotify_remove_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
				    mask, flags, umask);
}

static int fanotify_remove_sb_mark(struct fsnotify_group *group,
				   struct super_block *sb, __u32 mask,
				   unsigned int flags, __u32 umask)
{
	return fanotify_remove_mark(group, &sb->s_fsnotify_marks, mask,
				    flags, umask);
}

static int fanotify_remove_inode_mark(struct fsnotify_group *group,
				      struct inode *inode, __u32 mask,
				      unsigned int flags, __u32 umask)
{
	return fanotify_remove_mark(group, &inode->i_fsnotify_marks, mask,
				    flags, umask);
}

static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
				       unsigned int fan_flags)
{
@@ -1249,7 +1225,7 @@ static int fanotify_set_mark_fsid(struct fsnotify_group *group,
}

static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
						   fsnotify_connp_t *connp,
						   void *obj,
						   unsigned int obj_type,
						   unsigned int fan_flags,
						   struct fan_fsid *fsid)
@@ -1288,7 +1264,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
		fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
	}

	ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0);
	ret = fsnotify_add_mark_locked(mark, obj, obj_type, 0);
	if (ret)
		goto out_put_mark;

@@ -1344,7 +1320,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
}

static int fanotify_add_mark(struct fsnotify_group *group,
			     fsnotify_connp_t *connp, unsigned int obj_type,
			     void *obj, unsigned int obj_type,
			     __u32 mask, unsigned int fan_flags,
			     struct fan_fsid *fsid)
{
@@ -1353,9 +1329,9 @@ static int fanotify_add_mark(struct fsnotify_group *group,
	int ret = 0;

	fsnotify_group_lock(group);
	fsn_mark = fsnotify_find_mark(connp, group);
	fsn_mark = fsnotify_find_mark(obj, obj_type, group);
	if (!fsn_mark) {
		fsn_mark = fanotify_add_new_mark(group, connp, obj_type,
		fsn_mark = fanotify_add_new_mark(group, obj, obj_type,
						 fan_flags, fsid);
		if (IS_ERR(fsn_mark)) {
			fsnotify_group_unlock(group);
@@ -1392,42 +1368,6 @@ static int fanotify_add_mark(struct fsnotify_group *group,
	return ret;
}

static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
				      struct vfsmount *mnt, __u32 mask,
				      unsigned int flags, struct fan_fsid *fsid)
{
	return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
}

static int fanotify_add_sb_mark(struct fsnotify_group *group,
				struct super_block *sb, __u32 mask,
				unsigned int flags, struct fan_fsid *fsid)
{
	return fanotify_add_mark(group, &sb->s_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
}

static int fanotify_add_inode_mark(struct fsnotify_group *group,
				   struct inode *inode, __u32 mask,
				   unsigned int flags, struct fan_fsid *fsid)
{
	pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);

	/*
	 * If some other task has this inode open for write we should not add
	 * an ignore mask, unless that ignore mask is supposed to survive
	 * modification changes anyway.
	 */
	if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
	    !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
	    inode_is_open_for_write(inode))
		return 0;

	return fanotify_add_mark(group, &inode->i_fsnotify_marks,
				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
}

static struct fsnotify_event *fanotify_alloc_overflow_event(void)
{
	struct fanotify_event *oevent;
@@ -1576,13 +1516,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
	INIT_LIST_HEAD(&group->fanotify_data.access_list);
	switch (class) {
	case FAN_CLASS_NOTIF:
		group->priority = FS_PRIO_0;
		group->priority = FSNOTIFY_PRIO_NORMAL;
		break;
	case FAN_CLASS_CONTENT:
		group->priority = FS_PRIO_1;
		group->priority = FSNOTIFY_PRIO_CONTENT;
		break;
	case FAN_CLASS_PRE_CONTENT:
		group->priority = FS_PRIO_2;
		group->priority = FSNOTIFY_PRIO_PRE_CONTENT;
		break;
	default:
		fd = -EINVAL;
@@ -1750,6 +1690,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
	unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS;
	unsigned int obj_type, fid_mode;
	void *obj;
	u32 umask = 0;
	int ret;

@@ -1833,12 +1774,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
		goto fput_and_out;

	/*
	 * group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF.  These are not
	 * allowed to set permissions events.
	 * Permission events require minimum priority FAN_CLASS_CONTENT.
	 */
	ret = -EINVAL;
	if (mask & FANOTIFY_PERM_EVENTS &&
	    group->priority == FS_PRIO_0)
	    group->priority < FSNOTIFY_PRIO_CONTENT)
		goto fput_and_out;

	if (mask & FAN_FS_ERROR &&
@@ -1908,17 +1848,34 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	}

	/* inode held in place by reference to path; group by fget on fd */
	if (mark_type == FAN_MARK_INODE)
	if (mark_type == FAN_MARK_INODE) {
		inode = path.dentry->d_inode;
	else
		obj = inode;
	} else {
		mnt = path.mnt;
		if (mark_type == FAN_MARK_MOUNT)
			obj = mnt;
		else
			obj = mnt->mnt_sb;
	}

	/*
	 * If some other task has this inode open for write we should not add
	 * an ignore mask, unless that ignore mask is supposed to survive
	 * modification changes anyway.
	 */
	if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) &&
	    !(flags & FAN_MARK_IGNORED_SURV_MODIFY)) {
		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))
		if (ignore == FAN_MARK_IGNORE &&
		    (mnt || S_ISDIR(inode->i_mode)))
			goto path_put_and_out;

		ret = 0;
		if (inode && inode_is_open_for_write(inode))
			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)) {
@@ -1936,26 +1893,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
	/* create/update an inode mark */
	switch (mark_cmd) {
	case FAN_MARK_ADD:
		if (mark_type == FAN_MARK_MOUNT)
			ret = fanotify_add_vfsmount_mark(group, mnt, mask,
							 flags, fsid);
		else if (mark_type == FAN_MARK_FILESYSTEM)
			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
						   flags, fsid);
		else
			ret = fanotify_add_inode_mark(group, inode, mask,
						      flags, fsid);
		ret = fanotify_add_mark(group, obj, obj_type, mask, flags,
					fsid);
		break;
	case FAN_MARK_REMOVE:
		if (mark_type == FAN_MARK_MOUNT)
			ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
							    flags, umask);
		else if (mark_type == FAN_MARK_FILESYSTEM)
			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
						      flags, umask);
		else
			ret = fanotify_remove_inode_mark(group, inode, mask,
							 flags, umask);
		ret = fanotify_remove_mark(group, obj, obj_type, mask, flags,
					   umask);
		break;
	default:
		ret = -EINVAL;
+8 −12
Original line number Diff line number Diff line
@@ -41,29 +41,25 @@ static void show_fdinfo(struct seq_file *m, struct file *f,
#if defined(CONFIG_EXPORTFS)
static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
{
	struct {
		struct file_handle handle;
		u8 pad[MAX_HANDLE_SZ];
	} f;
	DEFINE_FLEX(struct file_handle, f, f_handle, handle_bytes, MAX_HANDLE_SZ);
	int size, ret, i;

	f.handle.handle_bytes = sizeof(f.pad);
	size = f.handle.handle_bytes >> 2;
	size = f->handle_bytes >> 2;

	ret = exportfs_encode_fid(inode, (struct fid *)f.handle.f_handle, &size);
	ret = exportfs_encode_fid(inode, (struct fid *)f->f_handle, &size);
	if ((ret == FILEID_INVALID) || (ret < 0)) {
		WARN_ONCE(1, "Can't encode file handler for inotify: %d\n", ret);
		return;
	}

	f.handle.handle_type = ret;
	f.handle.handle_bytes = size * sizeof(u32);
	f->handle_type = ret;
	f->handle_bytes = size * sizeof(u32);

	seq_printf(m, "fhandle-bytes:%x fhandle-type:%x f_handle:",
		   f.handle.handle_bytes, f.handle.handle_type);
		   f->handle_bytes, f->handle_type);

	for (i = 0; i < f.handle.handle_bytes; i++)
		seq_printf(m, "%02x", (int)f.handle.f_handle[i]);
	for (i = 0; i < f->handle_bytes; i++)
		seq_printf(m, "%02x", (int)f->f_handle[i]);
}
#else
static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
+22 −5
Original line number Diff line number Diff line
@@ -89,11 +89,25 @@ static void fsnotify_unmount_inodes(struct super_block *sb)

void fsnotify_sb_delete(struct super_block *sb)
{
	struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);

	/* Were any marks ever added to any object on this sb? */
	if (!sbinfo)
		return;

	fsnotify_unmount_inodes(sb);
	fsnotify_clear_marks_by_sb(sb);
	/* Wait for outstanding object references from connectors */
	wait_var_event(&sb->s_fsnotify_connectors,
		       !atomic_long_read(&sb->s_fsnotify_connectors));
	wait_var_event(fsnotify_sb_watched_objects(sb),
		       !atomic_long_read(fsnotify_sb_watched_objects(sb)));
	WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
	WARN_ON(fsnotify_sb_has_priority_watchers(sb,
						  FSNOTIFY_PRIO_PRE_CONTENT));
}

void fsnotify_sb_free(struct super_block *sb)
{
	kfree(sb->s_fsnotify_info);
}

/*
@@ -489,6 +503,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
{
	const struct path *path = fsnotify_data_path(data, data_type);
	struct super_block *sb = fsnotify_data_sb(data, data_type);
	struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
	struct fsnotify_iter_info iter_info = {};
	struct mount *mnt = NULL;
	struct inode *inode2 = NULL;
@@ -525,7 +540,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
	 * SRCU because we have no references to any objects and do not
	 * need SRCU to keep them "alive".
	 */
	if (!sb->s_fsnotify_marks &&
	if ((!sbinfo || !sbinfo->sb_marks) &&
	    (!mnt || !mnt->mnt_fsnotify_marks) &&
	    (!inode || !inode->i_fsnotify_marks) &&
	    (!inode2 || !inode2->i_fsnotify_marks))
@@ -552,8 +567,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,

	iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);

	if (sbinfo) {
		iter_info.marks[FSNOTIFY_ITER_TYPE_SB] =
		fsnotify_first_mark(&sb->s_fsnotify_marks);
			fsnotify_first_mark(&sbinfo->sb_marks);
	}
	if (mnt) {
		iter_info.marks[FSNOTIFY_ITER_TYPE_VFSMOUNT] =
			fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
Loading