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 Original line Diff line number Diff line
@@ -3,4 +3,4 @@ config FSNOTIFY


source "fs/notify/dnotify/Kconfig"
source "fs/notify/dnotify/Kconfig"
source "fs/notify/inotify/Kconfig"
source "fs/notify/inotify/Kconfig"
#source "fs/notify/fanotify/Kconfig"
source "fs/notify/fanotify/Kconfig"
+21 −6
Original line number Original line 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_Q_OVERFLOW != FS_Q_OVERFLOW);
	BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
	BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
	BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_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);
	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 event_mask, void *data, int data_type)
{
{
	__u32 marks_mask, marks_ignored_mask;
	__u32 marks_mask, marks_ignored_mask;
	struct path *path = data;


	pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p "
	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,
		 "mask=%x data=%p data_type=%d\n", __func__, group, to_tell,
		 inode_mark, vfsmnt_mark, event_mask, data, data_type);
		 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 we don't have enough info to send an event to userspace say no */
	if (data_type != FSNOTIFY_EVENT_PATH)
	if (data_type != FSNOTIFY_EVENT_PATH)
		return false;
		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) {
	if (inode_mark && vfsmnt_mark) {
		marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
		marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
		marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_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();
		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)
	if (event_mask & marks_mask & ~marks_ignored_mask)
		return true;
		return true;


	return false;
	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 = {
const struct fsnotify_ops fanotify_fsnotify_ops = {
	.handle_event = fanotify_handle_event,
	.handle_event = fanotify_handle_event,
	.should_send_event = fanotify_should_send_event,
	.should_send_event = fanotify_should_send_event,
	.free_group_priv = NULL,
	.free_group_priv = fanotify_free_group_priv,
	.free_event_priv = NULL,
	.free_event_priv = NULL,
	.freeing_mark = NULL,
	.freeing_mark = NULL,
};
};
+90 −8
Original line number Original line Diff line number Diff line
@@ -16,6 +16,10 @@


#include <asm/ioctls.h>
#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;
extern const struct fsnotify_ops fanotify_fsnotify_ops;


static struct kmem_cache *fanotify_mark_cache __read_mostly;
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;
		ret = -EAGAIN;
		if (file->f_flags & O_NONBLOCK)
		if (file->f_flags & O_NONBLOCK)
			break;
			break;
		ret = -EINTR;
		ret = -ERESTARTSYS;
		if (signal_pending(current))
		if (signal_pending(current))
			break;
			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)
static int fanotify_release(struct inode *ignored, struct file *file)
{
{
	struct fsnotify_group *group = file->private_data;
	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
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	struct fanotify_response_event *re, *lre;

	mutex_lock(&group->fanotify_data.access_mutex);
	mutex_lock(&group->fanotify_data.access_mutex);


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


	spin_lock(&fsn_mark->lock);
	spin_lock(&fsn_mark->lock);
	if (!(flags & FAN_MARK_IGNORED_MASK)) {
	if (!(flags & FAN_MARK_IGNORED_MASK)) {
		oldmask = fsn_mark->mask;
		oldmask = fsn_mark->mask;
		fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
		fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
	} else {
	} else {
		oldmask = fsn_mark->ignored_mask;
		__u32 tmask = fsn_mark->ignored_mask | mask;
		fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask));
		fsnotify_set_mark_ignored_mask_locked(fsn_mark, tmask);
		if (flags & FAN_MARK_IGNORED_SURV_MODIFY)
		if (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 (!(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);
	spin_unlock(&fsn_mark->lock);


	return mask & ~oldmask;
	return mask & ~oldmask;
@@ -582,6 +591,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
	if (!fsn_mark) {
	if (!fsn_mark) {
		int ret;
		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);
		fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!fsn_mark)
		if (!fsn_mark)
			return -ENOMEM;
			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);
	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);
	fsn_mark = fsnotify_find_inode_mark(group, inode);
	if (!fsn_mark) {
	if (!fsn_mark) {
		int ret;
		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);
		fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
		if (!fsn_mark)
		if (!fsn_mark)
			return -ENOMEM;
			return -ENOMEM;
@@ -637,6 +662,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
{
	struct fsnotify_group *group;
	struct fsnotify_group *group;
	int f_flags, fd;
	int f_flags, fd;
	struct user_struct *user;


	pr_debug("%s: flags=%d event_f_flags=%d\n",
	pr_debug("%s: flags=%d event_f_flags=%d\n",
		__func__, flags, event_f_flags);
		__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)
	if (flags & ~FAN_ALL_INIT_FLAGS)
		return -EINVAL;
		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;
	f_flags = O_RDWR | FMODE_NONOTIFY;
	if (flags & FAN_CLOEXEC)
	if (flags & FAN_CLOEXEC)
		f_flags |= O_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))
	if (IS_ERR(group))
		return PTR_ERR(group);
		return PTR_ERR(group);


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

	group->fanotify_data.f_flags = event_f_flags;
	group->fanotify_data.f_flags = event_f_flags;
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	mutex_init(&group->fanotify_data.access_mutex);
	mutex_init(&group->fanotify_data.access_mutex);
	init_waitqueue_head(&group->fanotify_data.access_waitq);
	init_waitqueue_head(&group->fanotify_data.access_waitq);
	INIT_LIST_HEAD(&group->fanotify_data.access_list);
	INIT_LIST_HEAD(&group->fanotify_data.access_list);
#endif
#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);
	fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
	if (fd < 0)
	if (fd < 0)
@@ -704,6 +771,12 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
	default:
	default:
		return -EINVAL;
		return -EINVAL;
	}
	}

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

#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
	if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD))
	if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD))
#else
#else
@@ -719,6 +792,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
	ret = -EINVAL;
	ret = -EINVAL;
	if (unlikely(filp->f_op != &fanotify_fops))
	if (unlikely(filp->f_op != &fanotify_fops))
		goto fput_and_out;
		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);
	ret = fanotify_find_path(dfd, pathname, &path, flags);
	if (ret)
	if (ret)
@@ -729,7 +812,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
		inode = path.dentry->d_inode;
		inode = path.dentry->d_inode;
	else
	else
		mnt = path.mnt;
		mnt = path.mnt;
	group = filp->private_data;


	/* create/update an inode mark */
	/* create/update an inode mark */
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
	switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
+21 −14
Original line number Original line 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. */
/* 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 dentry *parent;
	struct inode *p_inode;
	struct inode *p_inode;
	int ret = 0;


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


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


	parent = dget_parent(dentry);
	parent = dget_parent(dentry);
	p_inode = parent->d_inode;
	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;
		mask |= FS_EVENT_ON_CHILD;


		if (path)
		if (path)
			fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
			ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
				       dentry->d_name.name, 0);
				       dentry->d_name.name, 0);
		else
		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);
				       dentry->d_name.name, 0);
	}
	}


	dput(parent);
	dput(parent);

	return ret;
}
}
EXPORT_SYMBOL_GPL(__fsnotify_parent);
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) {
		if (inode_group > vfsmount_group) {
			/* handle inode */
			/* 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);
					    data_is, cookie, file_name, &event);
			/* we didn't use the vfsmount_mark */
			/* we didn't use the vfsmount_mark */
			vfsmount_group = NULL;
			vfsmount_group = NULL;
		} else if (vfsmount_group > inode_group) {
		} 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);
					    data_is, cookie, file_name, &event);
			inode_group = NULL;
			inode_group = NULL;
		} else {
		} 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,
					    mask, data, data_is, cookie, file_name,
					    &event);
					    &event);
		}
		}


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

		if (inode_group)
		if (inode_group)
			inode_node = srcu_dereference(inode_node->next,
			inode_node = srcu_dereference(inode_node->next,
						      &fsnotify_mark_srcu);
						      &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,
			vfsmount_node = srcu_dereference(vfsmount_node->next,
							 &fsnotify_mark_srcu);
							 &fsnotify_mark_srcu);
	}
	}

	ret = 0;
out:
	srcu_read_unlock(&fsnotify_mark_srcu, idx);
	srcu_read_unlock(&fsnotify_mark_srcu, idx);
	/*
	/*
	 * fsnotify_create_event() took a reference so the event can't be cleaned
	 * fsnotify_create_event() took a reference so the event can't be cleaned
+7 −2
Original line number Original line 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.
 * Attach an initialized mark to a given inode.
 * These marks may be used for the fsnotify backend to determine which
 * 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
 * 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,
int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
			    struct fsnotify_group *group, struct inode *inode,
			    struct fsnotify_group *group, struct inode *inode,
@@ -211,7 +212,11 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
			goto out;
			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;
			continue;


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