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

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

Pull fsnotify updates from Jan Kara:
 "A couple of small improvements for fsnotify subsystem.

  The most interesting is probably Amir's change modifying the meaning
  of fsnotify fmode bits (and I spell it out specifically because I know
  you care about those). There's no change for the common cases of no
  fsnotify watches or no permission event watches. But when there are
  permission watches (either for open or for pre-content events) but no
  FAN_ACCESS_PERM watch (which nobody uses in practice) we are now able
  optimize away unnecessary cache loads from the read path"

* tag 'fsnotify_for_v6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: optimize FMODE_NONOTIFY_PERM for the common cases
  fsnotify: merge file_set_fsnotify_mode_from_watchers() with open perm hook
  samples: fix building fs-monitor on musl systems
  fanotify: sanitize handle_type values when reporting fid
parents 440e6d7e 0d4c4d4e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -199,7 +199,7 @@ static int init_file(struct file *f, int flags, const struct cred *cred)
	file_ref_init(&f->f_ref, 1);
	/*
	 * Disable permission and pre-content events for all files by default.
	 * They may be enabled later by file_set_fsnotify_mode_from_watchers().
	 * They may be enabled later by fsnotify_open_perm_and_set_mode().
	 */
	file_set_fsnotify_mode(f, FMODE_NONOTIFY_PERM);
	return 0;
+7 −1
Original line number Diff line number Diff line
@@ -454,7 +454,13 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
	dwords = fh_len >> 2;
	type = exportfs_encode_fid(inode, buf, &dwords);
	err = -EINVAL;
	if (type <= 0 || type == FILEID_INVALID || fh_len != dwords << 2)
	/*
	 * Unlike file_handle, type and len of struct fanotify_fh are u8.
	 * Traditionally, filesystem return handle_type < 0xff, but there
	 * is no enforecement for that in vfs.
	 */
	BUILD_BUG_ON(MAX_HANDLE_SZ > 0xff || FILEID_INVALID > 0xff);
	if (type <= 0 || type >= FILEID_INVALID || fh_len != dwords << 2)
		goto out_err;

	fh->type = type;
+53 −34
Original line number Diff line number Diff line
@@ -199,7 +199,7 @@ static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask,
}

/* Are there any inode/mount/sb objects that watch for these events? */
static inline bool fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
static inline __u32 fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
					    __u32 mask)
{
	__u32 marks_mask = READ_ONCE(inode->i_fsnotify_mask) | mnt_mask |
@@ -656,20 +656,20 @@ EXPORT_SYMBOL_GPL(fsnotify);

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
/*
 * At open time we check fsnotify_sb_has_priority_watchers() and set the
 * FMODE_NONOTIFY_ mode bits accordignly.
 * At open time we check fsnotify_sb_has_priority_watchers(), call the open perm
 * hook and set the FMODE_NONOTIFY_ mode bits accordignly.
 * Later, fsnotify permission hooks do not check if there are permission event
 * watches, but that there were permission event watches at open time.
 */
void file_set_fsnotify_mode_from_watchers(struct file *file)
int fsnotify_open_perm_and_set_mode(struct file *file)
{
	struct dentry *dentry = file->f_path.dentry, *parent;
	struct super_block *sb = dentry->d_sb;
	__u32 mnt_mask, p_mask;
	__u32 mnt_mask, p_mask = 0;

	/* Is it a file opened by fanotify? */
	if (FMODE_FSNOTIFY_NONE(file->f_mode))
		return;
		return 0;

	/*
	 * Permission events is a super set of pre-content events, so if there
@@ -679,45 +679,64 @@ void file_set_fsnotify_mode_from_watchers(struct file *file)
	if (likely(!fsnotify_sb_has_priority_watchers(sb,
						FSNOTIFY_PRIO_CONTENT))) {
		file_set_fsnotify_mode(file, FMODE_NONOTIFY_PERM);
		return;
		return 0;
	}

	/*
	 * If there are permission event watchers but no pre-content event
	 * watchers, set FMODE_NONOTIFY | FMODE_NONOTIFY_PERM to indicate that.
	 * OK, there are some permission event watchers. Check if anybody is
	 * watching for permission events on *this* file.
	 */
	if ((!d_is_dir(dentry) && !d_is_reg(dentry)) ||
	    likely(!fsnotify_sb_has_priority_watchers(sb,
						FSNOTIFY_PRIO_PRE_CONTENT))) {
		file_set_fsnotify_mode(file, FMODE_NONOTIFY | FMODE_NONOTIFY_PERM);
		return;
	mnt_mask = READ_ONCE(real_mount(file->f_path.mnt)->mnt_fsnotify_mask);
	p_mask = fsnotify_object_watched(d_inode(dentry), mnt_mask,
					 ALL_FSNOTIFY_PERM_EVENTS);
	if (dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED) {
		parent = dget_parent(dentry);
		p_mask |= fsnotify_inode_watches_children(d_inode(parent));
		dput(parent);
	}

	/*
	 * OK, there are some pre-content watchers. Check if anybody is
	 * watching for pre-content events on *this* file.
	 * Legacy FAN_ACCESS_PERM events have very high performance overhead,
	 * so unlikely to be used in the wild. If they are used there will be
	 * no optimizations at all.
	 */
	mnt_mask = READ_ONCE(real_mount(file->f_path.mnt)->mnt_fsnotify_mask);
	if (unlikely(fsnotify_object_watched(d_inode(dentry), mnt_mask,
				     FSNOTIFY_PRE_CONTENT_EVENTS))) {
		/* Enable pre-content events */
	if (unlikely(p_mask & FS_ACCESS_PERM)) {
		/* Enable all permission and pre-content events */
		file_set_fsnotify_mode(file, 0);
		return;
		goto open_perm;
	}

	/* Is parent watching for pre-content events on this file? */
	if (dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED) {
		parent = dget_parent(dentry);
		p_mask = fsnotify_inode_watches_children(d_inode(parent));
		dput(parent);
		if (p_mask & FSNOTIFY_PRE_CONTENT_EVENTS) {
			/* Enable pre-content events */
			file_set_fsnotify_mode(file, 0);
			return;
	/*
	 * Pre-content events are only supported on regular files.
	 * If there are pre-content event watchers and no permission access
	 * watchers, set FMODE_NONOTIFY | FMODE_NONOTIFY_PERM to indicate that.
	 * That is the common case with HSM service.
	 */
	if (d_is_reg(dentry) && (p_mask & FSNOTIFY_PRE_CONTENT_EVENTS)) {
		file_set_fsnotify_mode(file, FMODE_NONOTIFY |
					     FMODE_NONOTIFY_PERM);
		goto open_perm;
	}

	/* Nobody watching permission and pre-content events on this file */
	file_set_fsnotify_mode(file, FMODE_NONOTIFY_PERM);

open_perm:
	/*
	 * Send open perm events depending on object masks and regardless of
	 * FMODE_NONOTIFY_PERM.
	 */
	if (file->f_flags & __FMODE_EXEC && p_mask & FS_OPEN_EXEC_PERM) {
		int ret = fsnotify_path(&file->f_path, FS_OPEN_EXEC_PERM);

		if (ret)
			return ret;
	}
	/* Nobody watching for pre-content events from this file */
	file_set_fsnotify_mode(file, FMODE_NONOTIFY | FMODE_NONOTIFY_PERM);

	if (p_mask & FS_OPEN_PERM)
		return fsnotify_path(&file->f_path, FS_OPEN_PERM);

	return 0;
}
#endif

+3 −3
Original line number Diff line number Diff line
@@ -944,12 +944,12 @@ static int do_dentry_open(struct file *f,
		goto cleanup_all;

	/*
	 * Set FMODE_NONOTIFY_* bits according to existing permission watches.
	 * Call fsnotify open permission hook and set FMODE_NONOTIFY_* bits
	 * according to existing permission watches.
	 * If FMODE_NONOTIFY mode was already set for an fanotify fd or for a
	 * pseudo file, this call will not change the mode.
	 */
	file_set_fsnotify_mode_from_watchers(f);
	error = fsnotify_open_perm(f);
	error = fsnotify_open_perm_and_set_mode(f);
	if (error)
		goto cleanup_all;

+6 −6
Original line number Diff line number Diff line
@@ -200,12 +200,12 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,

/*
 * The two FMODE_NONOTIFY* define which fsnotify events should not be generated
 * for a file. These are the possible values of (f->f_mode &
 * FMODE_FSNOTIFY_MASK) and their meaning:
 * for an open file. These are the possible values of
 * (f->f_mode & FMODE_FSNOTIFY_MASK) and their meaning:
 *
 * FMODE_NONOTIFY - suppress all (incl. non-permission) events.
 * FMODE_NONOTIFY_PERM - suppress permission (incl. pre-content) events.
 * FMODE_NONOTIFY | FMODE_NONOTIFY_PERM - suppress only pre-content events.
 * FMODE_NONOTIFY | FMODE_NONOTIFY_PERM - suppress only FAN_ACCESS_PERM.
 */
#define FMODE_FSNOTIFY_MASK \
	(FMODE_NONOTIFY | FMODE_NONOTIFY_PERM)
@@ -213,13 +213,13 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
#define FMODE_FSNOTIFY_NONE(mode) \
	((mode & FMODE_FSNOTIFY_MASK) == FMODE_NONOTIFY)
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
#define FMODE_FSNOTIFY_PERM(mode) \
#define FMODE_FSNOTIFY_HSM(mode) \
	((mode & FMODE_FSNOTIFY_MASK) == 0 || \
	 (mode & FMODE_FSNOTIFY_MASK) == (FMODE_NONOTIFY | FMODE_NONOTIFY_PERM))
#define FMODE_FSNOTIFY_HSM(mode) \
#define FMODE_FSNOTIFY_ACCESS_PERM(mode) \
	((mode & FMODE_FSNOTIFY_MASK) == 0)
#else
#define FMODE_FSNOTIFY_PERM(mode)	0
#define FMODE_FSNOTIFY_ACCESS_PERM(mode) 0
#define FMODE_FSNOTIFY_HSM(mode)	0
#endif

Loading