Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1792f17b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-linus' of git://git.infradead.org/users/eparis/notify

* 'for-linus' of git://git.infradead.org/users/eparis/notify: (22 commits)
  Ensure FMODE_NONOTIFY is not set by userspace
  make fanotify_read() restartable across signals
  fsnotify: remove alignment padding from fsnotify_mark on 64 bit builds
  fs/notify/fanotify/fanotify_user.c: fix warnings
  fanotify: Fix FAN_CLOSE comments
  fanotify: do not recalculate the mask if the ignored mask changed
  fanotify: ignore events on directories unless specifically requested
  fsnotify: rename FS_IN_ISDIR to FS_ISDIR
  fanotify: do not send events for irregular files
  fanotify: limit number of listeners per user
  fanotify: allow userspace to override max marks
  fanotify: limit the number of marks in a single fanotify group
  fanotify: allow userspace to override max queue depth
  fsnotify: implement a default maximum queue depth
  fanotify: ignore fanotify ignore marks if open writers
  fanotify: allow userspace to flush all marks
  fsnotify: call fsnotify_parent in perm events
  fsnotify: correctly handle return codes from listeners
  fanotify: use __aligned_u64 in fanotify userspace metadata
  fanotify: implement fanotify listener ordering
  ...
parents f02a38d8 6bff7ecc
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,4 +3,4 @@ config FSNOTIFY

source "fs/notify/dnotify/Kconfig"
source "fs/notify/inotify/Kconfig"
#source "fs/notify/fanotify/Kconfig"
source "fs/notify/fanotify/Kconfig"
+21 −6
Original line number Diff line number Diff line
@@ -131,6 +131,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
	BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
	BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
	BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
	BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);

	pr_debug("%s: group=%p event=%p\n", __func__, group, event);

@@ -160,20 +161,21 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
				       __u32 event_mask, void *data, int data_type)
{
	__u32 marks_mask, marks_ignored_mask;
	struct path *path = data;

	pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p "
		 "mask=%x data=%p data_type=%d\n", __func__, group, to_tell,
		 inode_mark, vfsmnt_mark, event_mask, data, data_type);

	/* sorry, fanotify only gives a damn about files and dirs */
	if (!S_ISREG(to_tell->i_mode) &&
	    !S_ISDIR(to_tell->i_mode))
		return false;

	/* if we don't have enough info to send an event to userspace say no */
	if (data_type != FSNOTIFY_EVENT_PATH)
		return false;

	/* sorry, fanotify only gives a damn about files and dirs */
	if (!S_ISREG(path->dentry->d_inode->i_mode) &&
	    !S_ISDIR(path->dentry->d_inode->i_mode))
		return false;

	if (inode_mark && vfsmnt_mark) {
		marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
		marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask);
@@ -194,16 +196,29 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
		BUG();
	}

	if (S_ISDIR(path->dentry->d_inode->i_mode) &&
	    (marks_ignored_mask & FS_ISDIR))
		return false;

	if (event_mask & marks_mask & ~marks_ignored_mask)
		return true;

	return false;
}

static void fanotify_free_group_priv(struct fsnotify_group *group)
{
	struct user_struct *user;

	user = group->fanotify_data.user;
	atomic_dec(&user->fanotify_listeners);
	free_uid(user);
}

const struct fsnotify_ops fanotify_fsnotify_ops = {
	.handle_event = fanotify_handle_event,
	.should_send_event = fanotify_should_send_event,
	.free_group_priv = NULL,
	.free_group_priv = fanotify_free_group_priv,
	.free_event_priv = NULL,
	.freeing_mark = NULL,
};
+90 −8
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

#include <asm/ioctls.h>

#define FANOTIFY_DEFAULT_MAX_EVENTS	16384
#define FANOTIFY_DEFAULT_MAX_MARKS	8192
#define FANOTIFY_DEFAULT_MAX_LISTENERS	128

extern const struct fsnotify_ops fanotify_fsnotify_ops;

static struct kmem_cache *fanotify_mark_cache __read_mostly;
@@ -326,7 +330,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
		ret = -EAGAIN;
		if (file->f_flags & O_NONBLOCK)
			break;
		ret = -EINTR;
		ret = -ERESTARTSYS;
		if (signal_pending(current))
			break;

@@ -372,11 +376,10 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t
static int fanotify_release(struct inode *ignored, struct file *file)
{
	struct fsnotify_group *group = file->private_data;
	struct fanotify_response_event *re, *lre;

	pr_debug("%s: file=%p group=%p\n", __func__, file, group);

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	struct fanotify_response_event *re, *lre;

	mutex_lock(&group->fanotify_data.access_mutex);

	group->fanotify_data.bypass_perm = true;
@@ -554,18 +557,24 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
				       __u32 mask,
				       unsigned int flags)
{
	__u32 oldmask;
	__u32 oldmask = -1;

	spin_lock(&fsn_mark->lock);
	if (!(flags & FAN_MARK_IGNORED_MASK)) {
		oldmask = fsn_mark->mask;
		fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
	} else {
		oldmask = fsn_mark->ignored_mask;
		fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask));
		__u32 tmask = fsn_mark->ignored_mask | mask;
		fsnotify_set_mark_ignored_mask_locked(fsn_mark, tmask);
		if (flags & FAN_MARK_IGNORED_SURV_MODIFY)
			fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
	}

	if (!(flags & FAN_MARK_ONDIR)) {
		__u32 tmask = fsn_mark->ignored_mask | FAN_ONDIR;
		fsnotify_set_mark_ignored_mask_locked(fsn_mark, tmask);
	}

	spin_unlock(&fsn_mark->lock);

	return mask & ~oldmask;
@@ -582,6 +591,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
	if (!fsn_mark) {
		int ret;

		if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks)
			return -ENOSPC;

		fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!fsn_mark)
			return -ENOMEM;
@@ -610,10 +622,23 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,

	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 ignored mark, unless that ignored mark is supposed to survive
	 * modification changes anyway.
	 */
	if ((flags & FAN_MARK_IGNORED_MASK) &&
	    !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
	    (atomic_read(&inode->i_writecount) > 0))
		return 0;

	fsn_mark = fsnotify_find_inode_mark(group, inode);
	if (!fsn_mark) {
		int ret;

		if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks)
			return -ENOSPC;

		fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!fsn_mark)
			return -ENOMEM;
@@ -637,6 +662,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
	struct fsnotify_group *group;
	int f_flags, fd;
	struct user_struct *user;

	pr_debug("%s: flags=%d event_f_flags=%d\n",
		__func__, flags, event_f_flags);
@@ -647,6 +673,12 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
	if (flags & ~FAN_ALL_INIT_FLAGS)
		return -EINVAL;

	user = get_current_user();
	if (atomic_read(&user->fanotify_listeners) > FANOTIFY_DEFAULT_MAX_LISTENERS) {
		free_uid(user);
		return -EMFILE;
	}

	f_flags = O_RDWR | FMODE_NONOTIFY;
	if (flags & FAN_CLOEXEC)
		f_flags |= O_CLOEXEC;
@@ -658,12 +690,47 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
	if (IS_ERR(group))
		return PTR_ERR(group);

	group->fanotify_data.user = user;
	atomic_inc(&user->fanotify_listeners);

	group->fanotify_data.f_flags = event_f_flags;
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	mutex_init(&group->fanotify_data.access_mutex);
	init_waitqueue_head(&group->fanotify_data.access_waitq);
	INIT_LIST_HEAD(&group->fanotify_data.access_list);
#endif
	switch (flags & FAN_ALL_CLASS_BITS) {
	case FAN_CLASS_NOTIF:
		group->priority = FS_PRIO_0;
		break;
	case FAN_CLASS_CONTENT:
		group->priority = FS_PRIO_1;
		break;
	case FAN_CLASS_PRE_CONTENT:
		group->priority = FS_PRIO_2;
		break;
	default:
		fd = -EINVAL;
		goto out_put_group;
	}

	if (flags & FAN_UNLIMITED_QUEUE) {
		fd = -EPERM;
		if (!capable(CAP_SYS_ADMIN))
			goto out_put_group;
		group->max_events = UINT_MAX;
	} else {
		group->max_events = FANOTIFY_DEFAULT_MAX_EVENTS;
	}

	if (flags & FAN_UNLIMITED_MARKS) {
		fd = -EPERM;
		if (!capable(CAP_SYS_ADMIN))
			goto out_put_group;
		group->fanotify_data.max_marks = UINT_MAX;
	} else {
		group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS;
	}

	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
	if (fd < 0)
@@ -704,6 +771,12 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
	default:
		return -EINVAL;
	}

	if (mask & FAN_ONDIR) {
		flags |= FAN_MARK_ONDIR;
		mask &= ~FAN_ONDIR;
	}

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD))
#else
@@ -719,6 +792,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
	ret = -EINVAL;
	if (unlikely(filp->f_op != &fanotify_fops))
		goto fput_and_out;
	group = filp->private_data;

	/*
	 * group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF.  These are not
	 * allowed to set permissions events.
	 */
	ret = -EINVAL;
	if (mask & FAN_ALL_PERM_EVENTS &&
	    group->priority == FS_PRIO_0)
		goto fput_and_out;

	ret = fanotify_find_path(dfd, pathname, &path, flags);
	if (ret)
@@ -729,7 +812,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
		inode = path.dentry->d_inode;
	else
		mnt = path.mnt;
	group = filp->private_data;

	/* create/update an inode mark */
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
+21 −14
Original line number Diff line number Diff line
@@ -84,16 +84,17 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
}

/* Notify this dentry's parent about a child's events. */
void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
{
	struct dentry *parent;
	struct inode *p_inode;
	int ret = 0;

	if (!dentry)
		dentry = path->dentry;

	if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
		return;
		return 0;

	parent = dget_parent(dentry);
	p_inode = parent->d_inode;
@@ -106,14 +107,16 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
		mask |= FS_EVENT_ON_CHILD;

		if (path)
			fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
			ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
				       dentry->d_name.name, 0);
		else
			fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
			ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
				       dentry->d_name.name, 0);
	}

	dput(parent);

	return ret;
}
EXPORT_SYMBOL_GPL(__fsnotify_parent);

@@ -252,20 +255,23 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,

		if (inode_group > vfsmount_group) {
			/* handle inode */
			send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
			ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
					    data_is, cookie, file_name, &event);
			/* we didn't use the vfsmount_mark */
			vfsmount_group = NULL;
		} else if (vfsmount_group > inode_group) {
			send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
			ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
					    data_is, cookie, file_name, &event);
			inode_group = NULL;
		} else {
			send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
			ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
					    mask, data, data_is, cookie, file_name,
					    &event);
		}

		if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
			goto out;

		if (inode_group)
			inode_node = srcu_dereference(inode_node->next,
						      &fsnotify_mark_srcu);
@@ -273,7 +279,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
			vfsmount_node = srcu_dereference(vfsmount_node->next,
							 &fsnotify_mark_srcu);
	}

	ret = 0;
out:
	srcu_read_unlock(&fsnotify_mark_srcu, idx);
	/*
	 * fsnotify_create_event() took a reference so the event can't be cleaned
+7 −2
Original line number Diff line number Diff line
@@ -177,7 +177,8 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
 * Attach an initialized mark to a given inode.
 * These marks may be used for the fsnotify backend to determine which
 * event types should be delivered to which group and for which inodes.  These
 * marks are ordered according to the group's location in memory.
 * marks are ordered according to priority, highest number first, and then by
 * the group's location in memory.
 */
int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
			    struct fsnotify_group *group, struct inode *inode,
@@ -211,7 +212,11 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
			goto out;
		}

		if (mark->group < lmark->group)
		if (mark->group->priority < lmark->group->priority)
			continue;

		if ((mark->group->priority == lmark->group->priority) &&
		    (mark->group < lmark->group))
			continue;

		hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
Loading