162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fs/inotify_user.c - inotify support for userspace
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *	John McCutchan	<ttb@tentacle.dhs.org>
762306a36Sopenharmony_ci *	Robert Love	<rml@novell.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Copyright (C) 2005 John McCutchan
1062306a36Sopenharmony_ci * Copyright 2006 Hewlett-Packard Development Company, L.P.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright (C) 2009 Eric Paris <Red Hat Inc>
1362306a36Sopenharmony_ci * inotify was largely rewriten to make use of the fsnotify infrastructure
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/file.h>
1762306a36Sopenharmony_ci#include <linux/fs.h> /* struct inode */
1862306a36Sopenharmony_ci#include <linux/fsnotify_backend.h>
1962306a36Sopenharmony_ci#include <linux/idr.h>
2062306a36Sopenharmony_ci#include <linux/init.h> /* fs_initcall */
2162306a36Sopenharmony_ci#include <linux/inotify.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h> /* roundup() */
2362306a36Sopenharmony_ci#include <linux/namei.h> /* LOOKUP_FOLLOW */
2462306a36Sopenharmony_ci#include <linux/sched/signal.h>
2562306a36Sopenharmony_ci#include <linux/slab.h> /* struct kmem_cache */
2662306a36Sopenharmony_ci#include <linux/syscalls.h>
2762306a36Sopenharmony_ci#include <linux/types.h>
2862306a36Sopenharmony_ci#include <linux/anon_inodes.h>
2962306a36Sopenharmony_ci#include <linux/uaccess.h>
3062306a36Sopenharmony_ci#include <linux/poll.h>
3162306a36Sopenharmony_ci#include <linux/wait.h>
3262306a36Sopenharmony_ci#include <linux/memcontrol.h>
3362306a36Sopenharmony_ci#include <linux/security.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "inotify.h"
3662306a36Sopenharmony_ci#include "../fdinfo.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#include <asm/ioctls.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * An inotify watch requires allocating an inotify_inode_mark structure as
4262306a36Sopenharmony_ci * well as pinning the watched inode. Doubling the size of a VFS inode
4362306a36Sopenharmony_ci * should be more than enough to cover the additional filesystem inode
4462306a36Sopenharmony_ci * size increase.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ci#define INOTIFY_WATCH_COST	(sizeof(struct inotify_inode_mark) + \
4762306a36Sopenharmony_ci				 2 * sizeof(struct inode))
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* configurable via /proc/sys/fs/inotify/ */
5062306a36Sopenharmony_cistatic int inotify_max_queued_events __read_mostly;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct kmem_cache *inotify_inode_mark_cachep __read_mostly;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include <linux/sysctl.h>
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic long it_zero = 0;
5962306a36Sopenharmony_cistatic long it_int_max = INT_MAX;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic struct ctl_table inotify_table[] = {
6262306a36Sopenharmony_ci	{
6362306a36Sopenharmony_ci		.procname	= "max_user_instances",
6462306a36Sopenharmony_ci		.data		= &init_user_ns.ucount_max[UCOUNT_INOTIFY_INSTANCES],
6562306a36Sopenharmony_ci		.maxlen		= sizeof(long),
6662306a36Sopenharmony_ci		.mode		= 0644,
6762306a36Sopenharmony_ci		.proc_handler	= proc_doulongvec_minmax,
6862306a36Sopenharmony_ci		.extra1		= &it_zero,
6962306a36Sopenharmony_ci		.extra2		= &it_int_max,
7062306a36Sopenharmony_ci	},
7162306a36Sopenharmony_ci	{
7262306a36Sopenharmony_ci		.procname	= "max_user_watches",
7362306a36Sopenharmony_ci		.data		= &init_user_ns.ucount_max[UCOUNT_INOTIFY_WATCHES],
7462306a36Sopenharmony_ci		.maxlen		= sizeof(long),
7562306a36Sopenharmony_ci		.mode		= 0644,
7662306a36Sopenharmony_ci		.proc_handler	= proc_doulongvec_minmax,
7762306a36Sopenharmony_ci		.extra1		= &it_zero,
7862306a36Sopenharmony_ci		.extra2		= &it_int_max,
7962306a36Sopenharmony_ci	},
8062306a36Sopenharmony_ci	{
8162306a36Sopenharmony_ci		.procname	= "max_queued_events",
8262306a36Sopenharmony_ci		.data		= &inotify_max_queued_events,
8362306a36Sopenharmony_ci		.maxlen		= sizeof(int),
8462306a36Sopenharmony_ci		.mode		= 0644,
8562306a36Sopenharmony_ci		.proc_handler	= proc_dointvec_minmax,
8662306a36Sopenharmony_ci		.extra1		= SYSCTL_ZERO
8762306a36Sopenharmony_ci	},
8862306a36Sopenharmony_ci	{ }
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void __init inotify_sysctls_init(void)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	register_sysctl("fs/inotify", inotify_table);
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#else
9762306a36Sopenharmony_ci#define inotify_sysctls_init() do { } while (0)
9862306a36Sopenharmony_ci#endif /* CONFIG_SYSCTL */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic inline __u32 inotify_arg_to_mask(struct inode *inode, u32 arg)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	__u32 mask;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * Everything should receive events when the inode is unmounted.
10662306a36Sopenharmony_ci	 * All directories care about children.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	mask = (FS_UNMOUNT);
10962306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode))
11062306a36Sopenharmony_ci		mask |= FS_EVENT_ON_CHILD;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* mask off the flags used to open the fd */
11362306a36Sopenharmony_ci	mask |= (arg & INOTIFY_USER_MASK);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return mask;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define INOTIFY_MARK_FLAGS \
11962306a36Sopenharmony_ci	(FSNOTIFY_MARK_FLAG_EXCL_UNLINK | FSNOTIFY_MARK_FLAG_IN_ONESHOT)
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline unsigned int inotify_arg_to_flags(u32 arg)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	unsigned int flags = 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (arg & IN_EXCL_UNLINK)
12662306a36Sopenharmony_ci		flags |= FSNOTIFY_MARK_FLAG_EXCL_UNLINK;
12762306a36Sopenharmony_ci	if (arg & IN_ONESHOT)
12862306a36Sopenharmony_ci		flags |= FSNOTIFY_MARK_FLAG_IN_ONESHOT;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return flags;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic inline u32 inotify_mask_to_arg(__u32 mask)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	return mask & (IN_ALL_EVENTS | IN_ISDIR | IN_UNMOUNT | IN_IGNORED |
13662306a36Sopenharmony_ci		       IN_Q_OVERFLOW);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* inotify userspace file descriptor functions */
14062306a36Sopenharmony_cistatic __poll_t inotify_poll(struct file *file, poll_table *wait)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct fsnotify_group *group = file->private_data;
14362306a36Sopenharmony_ci	__poll_t ret = 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	poll_wait(file, &group->notification_waitq, wait);
14662306a36Sopenharmony_ci	spin_lock(&group->notification_lock);
14762306a36Sopenharmony_ci	if (!fsnotify_notify_queue_is_empty(group))
14862306a36Sopenharmony_ci		ret = EPOLLIN | EPOLLRDNORM;
14962306a36Sopenharmony_ci	spin_unlock(&group->notification_lock);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	return ret;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic int round_event_name_len(struct fsnotify_event *fsn_event)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct inotify_event_info *event;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	event = INOTIFY_E(fsn_event);
15962306a36Sopenharmony_ci	if (!event->name_len)
16062306a36Sopenharmony_ci		return 0;
16162306a36Sopenharmony_ci	return roundup(event->name_len + 1, sizeof(struct inotify_event));
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/*
16562306a36Sopenharmony_ci * Get an inotify_kernel_event if one exists and is small
16662306a36Sopenharmony_ci * enough to fit in "count". Return an error pointer if
16762306a36Sopenharmony_ci * not large enough.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Called with the group->notification_lock held.
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cistatic struct fsnotify_event *get_one_event(struct fsnotify_group *group,
17262306a36Sopenharmony_ci					    size_t count)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	size_t event_size = sizeof(struct inotify_event);
17562306a36Sopenharmony_ci	struct fsnotify_event *event;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	event = fsnotify_peek_first_event(group);
17862306a36Sopenharmony_ci	if (!event)
17962306a36Sopenharmony_ci		return NULL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	pr_debug("%s: group=%p event=%p\n", __func__, group, event);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	event_size += round_event_name_len(event);
18462306a36Sopenharmony_ci	if (event_size > count)
18562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* held the notification_lock the whole time, so this is the
18862306a36Sopenharmony_ci	 * same event we peeked above */
18962306a36Sopenharmony_ci	fsnotify_remove_first_event(group);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return event;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * Copy an event to user space, returning how much we copied.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * We already checked that the event size is smaller than the
19862306a36Sopenharmony_ci * buffer we had in "get_one_event()" above.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic ssize_t copy_event_to_user(struct fsnotify_group *group,
20162306a36Sopenharmony_ci				  struct fsnotify_event *fsn_event,
20262306a36Sopenharmony_ci				  char __user *buf)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct inotify_event inotify_event;
20562306a36Sopenharmony_ci	struct inotify_event_info *event;
20662306a36Sopenharmony_ci	size_t event_size = sizeof(struct inotify_event);
20762306a36Sopenharmony_ci	size_t name_len;
20862306a36Sopenharmony_ci	size_t pad_name_len;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	pr_debug("%s: group=%p event=%p\n", __func__, group, fsn_event);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	event = INOTIFY_E(fsn_event);
21362306a36Sopenharmony_ci	name_len = event->name_len;
21462306a36Sopenharmony_ci	/*
21562306a36Sopenharmony_ci	 * round up name length so it is a multiple of event_size
21662306a36Sopenharmony_ci	 * plus an extra byte for the terminating '\0'.
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci	pad_name_len = round_event_name_len(fsn_event);
21962306a36Sopenharmony_ci	inotify_event.len = pad_name_len;
22062306a36Sopenharmony_ci	inotify_event.mask = inotify_mask_to_arg(event->mask);
22162306a36Sopenharmony_ci	inotify_event.wd = event->wd;
22262306a36Sopenharmony_ci	inotify_event.cookie = event->sync_cookie;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/* send the main event */
22562306a36Sopenharmony_ci	if (copy_to_user(buf, &inotify_event, event_size))
22662306a36Sopenharmony_ci		return -EFAULT;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	buf += event_size;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/*
23162306a36Sopenharmony_ci	 * fsnotify only stores the pathname, so here we have to send the pathname
23262306a36Sopenharmony_ci	 * and then pad that pathname out to a multiple of sizeof(inotify_event)
23362306a36Sopenharmony_ci	 * with zeros.
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	if (pad_name_len) {
23662306a36Sopenharmony_ci		/* copy the path name */
23762306a36Sopenharmony_ci		if (copy_to_user(buf, event->name, name_len))
23862306a36Sopenharmony_ci			return -EFAULT;
23962306a36Sopenharmony_ci		buf += name_len;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		/* fill userspace with 0's */
24262306a36Sopenharmony_ci		if (clear_user(buf, pad_name_len - name_len))
24362306a36Sopenharmony_ci			return -EFAULT;
24462306a36Sopenharmony_ci		event_size += pad_name_len;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return event_size;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic ssize_t inotify_read(struct file *file, char __user *buf,
25162306a36Sopenharmony_ci			    size_t count, loff_t *pos)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct fsnotify_group *group;
25462306a36Sopenharmony_ci	struct fsnotify_event *kevent;
25562306a36Sopenharmony_ci	char __user *start;
25662306a36Sopenharmony_ci	int ret;
25762306a36Sopenharmony_ci	DEFINE_WAIT_FUNC(wait, woken_wake_function);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	start = buf;
26062306a36Sopenharmony_ci	group = file->private_data;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	add_wait_queue(&group->notification_waitq, &wait);
26362306a36Sopenharmony_ci	while (1) {
26462306a36Sopenharmony_ci		spin_lock(&group->notification_lock);
26562306a36Sopenharmony_ci		kevent = get_one_event(group, count);
26662306a36Sopenharmony_ci		spin_unlock(&group->notification_lock);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		pr_debug("%s: group=%p kevent=%p\n", __func__, group, kevent);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		if (kevent) {
27162306a36Sopenharmony_ci			ret = PTR_ERR(kevent);
27262306a36Sopenharmony_ci			if (IS_ERR(kevent))
27362306a36Sopenharmony_ci				break;
27462306a36Sopenharmony_ci			ret = copy_event_to_user(group, kevent, buf);
27562306a36Sopenharmony_ci			fsnotify_destroy_event(group, kevent);
27662306a36Sopenharmony_ci			if (ret < 0)
27762306a36Sopenharmony_ci				break;
27862306a36Sopenharmony_ci			buf += ret;
27962306a36Sopenharmony_ci			count -= ret;
28062306a36Sopenharmony_ci			continue;
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		ret = -EAGAIN;
28462306a36Sopenharmony_ci		if (file->f_flags & O_NONBLOCK)
28562306a36Sopenharmony_ci			break;
28662306a36Sopenharmony_ci		ret = -ERESTARTSYS;
28762306a36Sopenharmony_ci		if (signal_pending(current))
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		if (start != buf)
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	remove_wait_queue(&group->notification_waitq, &wait);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (start != buf && ret != -EFAULT)
29862306a36Sopenharmony_ci		ret = buf - start;
29962306a36Sopenharmony_ci	return ret;
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int inotify_release(struct inode *ignored, struct file *file)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct fsnotify_group *group = file->private_data;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	pr_debug("%s: group=%p\n", __func__, group);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* free this group, matching get was inotify_init->fsnotify_obtain_group */
30962306a36Sopenharmony_ci	fsnotify_destroy_group(group);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	return 0;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic long inotify_ioctl(struct file *file, unsigned int cmd,
31562306a36Sopenharmony_ci			  unsigned long arg)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct fsnotify_group *group;
31862306a36Sopenharmony_ci	struct fsnotify_event *fsn_event;
31962306a36Sopenharmony_ci	void __user *p;
32062306a36Sopenharmony_ci	int ret = -ENOTTY;
32162306a36Sopenharmony_ci	size_t send_len = 0;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	group = file->private_data;
32462306a36Sopenharmony_ci	p = (void __user *) arg;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	pr_debug("%s: group=%p cmd=%u\n", __func__, group, cmd);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	switch (cmd) {
32962306a36Sopenharmony_ci	case FIONREAD:
33062306a36Sopenharmony_ci		spin_lock(&group->notification_lock);
33162306a36Sopenharmony_ci		list_for_each_entry(fsn_event, &group->notification_list,
33262306a36Sopenharmony_ci				    list) {
33362306a36Sopenharmony_ci			send_len += sizeof(struct inotify_event);
33462306a36Sopenharmony_ci			send_len += round_event_name_len(fsn_event);
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci		spin_unlock(&group->notification_lock);
33762306a36Sopenharmony_ci		ret = put_user(send_len, (int __user *) p);
33862306a36Sopenharmony_ci		break;
33962306a36Sopenharmony_ci#ifdef CONFIG_CHECKPOINT_RESTORE
34062306a36Sopenharmony_ci	case INOTIFY_IOC_SETNEXTWD:
34162306a36Sopenharmony_ci		ret = -EINVAL;
34262306a36Sopenharmony_ci		if (arg >= 1 && arg <= INT_MAX) {
34362306a36Sopenharmony_ci			struct inotify_group_private_data *data;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci			data = &group->inotify_data;
34662306a36Sopenharmony_ci			spin_lock(&data->idr_lock);
34762306a36Sopenharmony_ci			idr_set_cursor(&data->idr, (unsigned int)arg);
34862306a36Sopenharmony_ci			spin_unlock(&data->idr_lock);
34962306a36Sopenharmony_ci			ret = 0;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci		break;
35262306a36Sopenharmony_ci#endif /* CONFIG_CHECKPOINT_RESTORE */
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return ret;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic const struct file_operations inotify_fops = {
35962306a36Sopenharmony_ci	.show_fdinfo	= inotify_show_fdinfo,
36062306a36Sopenharmony_ci	.poll		= inotify_poll,
36162306a36Sopenharmony_ci	.read		= inotify_read,
36262306a36Sopenharmony_ci	.fasync		= fsnotify_fasync,
36362306a36Sopenharmony_ci	.release	= inotify_release,
36462306a36Sopenharmony_ci	.unlocked_ioctl	= inotify_ioctl,
36562306a36Sopenharmony_ci	.compat_ioctl	= inotify_ioctl,
36662306a36Sopenharmony_ci	.llseek		= noop_llseek,
36762306a36Sopenharmony_ci};
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci/*
37162306a36Sopenharmony_ci * find_inode - resolve a user-given path to a specific inode
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_cistatic int inotify_find_inode(const char __user *dirname, struct path *path,
37462306a36Sopenharmony_ci						unsigned int flags, __u64 mask)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	int error;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	error = user_path_at(AT_FDCWD, dirname, flags, path);
37962306a36Sopenharmony_ci	if (error)
38062306a36Sopenharmony_ci		return error;
38162306a36Sopenharmony_ci	/* you can only watch an inode if you have read permissions on it */
38262306a36Sopenharmony_ci	error = path_permission(path, MAY_READ);
38362306a36Sopenharmony_ci	if (error) {
38462306a36Sopenharmony_ci		path_put(path);
38562306a36Sopenharmony_ci		return error;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci	error = security_path_notify(path, mask,
38862306a36Sopenharmony_ci				FSNOTIFY_OBJ_TYPE_INODE);
38962306a36Sopenharmony_ci	if (error)
39062306a36Sopenharmony_ci		path_put(path);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return error;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock,
39662306a36Sopenharmony_ci			      struct inotify_inode_mark *i_mark)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	int ret;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
40162306a36Sopenharmony_ci	spin_lock(idr_lock);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = idr_alloc_cyclic(idr, i_mark, 1, 0, GFP_NOWAIT);
40462306a36Sopenharmony_ci	if (ret >= 0) {
40562306a36Sopenharmony_ci		/* we added the mark to the idr, take a reference */
40662306a36Sopenharmony_ci		i_mark->wd = ret;
40762306a36Sopenharmony_ci		fsnotify_get_mark(&i_mark->fsn_mark);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	spin_unlock(idr_lock);
41162306a36Sopenharmony_ci	idr_preload_end();
41262306a36Sopenharmony_ci	return ret < 0 ? ret : 0;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic struct inotify_inode_mark *inotify_idr_find_locked(struct fsnotify_group *group,
41662306a36Sopenharmony_ci								int wd)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci	struct idr *idr = &group->inotify_data.idr;
41962306a36Sopenharmony_ci	spinlock_t *idr_lock = &group->inotify_data.idr_lock;
42062306a36Sopenharmony_ci	struct inotify_inode_mark *i_mark;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	assert_spin_locked(idr_lock);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	i_mark = idr_find(idr, wd);
42562306a36Sopenharmony_ci	if (i_mark) {
42662306a36Sopenharmony_ci		struct fsnotify_mark *fsn_mark = &i_mark->fsn_mark;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		fsnotify_get_mark(fsn_mark);
42962306a36Sopenharmony_ci		/* One ref for being in the idr, one ref we just took */
43062306a36Sopenharmony_ci		BUG_ON(refcount_read(&fsn_mark->refcnt) < 2);
43162306a36Sopenharmony_ci	}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return i_mark;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic struct inotify_inode_mark *inotify_idr_find(struct fsnotify_group *group,
43762306a36Sopenharmony_ci							 int wd)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct inotify_inode_mark *i_mark;
44062306a36Sopenharmony_ci	spinlock_t *idr_lock = &group->inotify_data.idr_lock;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	spin_lock(idr_lock);
44362306a36Sopenharmony_ci	i_mark = inotify_idr_find_locked(group, wd);
44462306a36Sopenharmony_ci	spin_unlock(idr_lock);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	return i_mark;
44762306a36Sopenharmony_ci}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/*
45062306a36Sopenharmony_ci * Remove the mark from the idr (if present) and drop the reference
45162306a36Sopenharmony_ci * on the mark because it was in the idr.
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_cistatic void inotify_remove_from_idr(struct fsnotify_group *group,
45462306a36Sopenharmony_ci				    struct inotify_inode_mark *i_mark)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct idr *idr = &group->inotify_data.idr;
45762306a36Sopenharmony_ci	spinlock_t *idr_lock = &group->inotify_data.idr_lock;
45862306a36Sopenharmony_ci	struct inotify_inode_mark *found_i_mark = NULL;
45962306a36Sopenharmony_ci	int wd;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	spin_lock(idr_lock);
46262306a36Sopenharmony_ci	wd = i_mark->wd;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/*
46562306a36Sopenharmony_ci	 * does this i_mark think it is in the idr?  we shouldn't get called
46662306a36Sopenharmony_ci	 * if it wasn't....
46762306a36Sopenharmony_ci	 */
46862306a36Sopenharmony_ci	if (wd == -1) {
46962306a36Sopenharmony_ci		WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p\n",
47062306a36Sopenharmony_ci			__func__, i_mark, i_mark->wd, i_mark->fsn_mark.group);
47162306a36Sopenharmony_ci		goto out;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Lets look in the idr to see if we find it */
47562306a36Sopenharmony_ci	found_i_mark = inotify_idr_find_locked(group, wd);
47662306a36Sopenharmony_ci	if (unlikely(!found_i_mark)) {
47762306a36Sopenharmony_ci		WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p\n",
47862306a36Sopenharmony_ci			__func__, i_mark, i_mark->wd, i_mark->fsn_mark.group);
47962306a36Sopenharmony_ci		goto out;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/*
48362306a36Sopenharmony_ci	 * We found an mark in the idr at the right wd, but it's
48462306a36Sopenharmony_ci	 * not the mark we were told to remove.  eparis seriously
48562306a36Sopenharmony_ci	 * fucked up somewhere.
48662306a36Sopenharmony_ci	 */
48762306a36Sopenharmony_ci	if (unlikely(found_i_mark != i_mark)) {
48862306a36Sopenharmony_ci		WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p "
48962306a36Sopenharmony_ci			"found_i_mark=%p found_i_mark->wd=%d "
49062306a36Sopenharmony_ci			"found_i_mark->group=%p\n", __func__, i_mark,
49162306a36Sopenharmony_ci			i_mark->wd, i_mark->fsn_mark.group, found_i_mark,
49262306a36Sopenharmony_ci			found_i_mark->wd, found_i_mark->fsn_mark.group);
49362306a36Sopenharmony_ci		goto out;
49462306a36Sopenharmony_ci	}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * One ref for being in the idr
49862306a36Sopenharmony_ci	 * one ref grabbed by inotify_idr_find
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	if (unlikely(refcount_read(&i_mark->fsn_mark.refcnt) < 2)) {
50162306a36Sopenharmony_ci		printk(KERN_ERR "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p\n",
50262306a36Sopenharmony_ci			 __func__, i_mark, i_mark->wd, i_mark->fsn_mark.group);
50362306a36Sopenharmony_ci		/* we can't really recover with bad ref cnting.. */
50462306a36Sopenharmony_ci		BUG();
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	idr_remove(idr, wd);
50862306a36Sopenharmony_ci	/* Removed from the idr, drop that ref. */
50962306a36Sopenharmony_ci	fsnotify_put_mark(&i_mark->fsn_mark);
51062306a36Sopenharmony_ciout:
51162306a36Sopenharmony_ci	i_mark->wd = -1;
51262306a36Sopenharmony_ci	spin_unlock(idr_lock);
51362306a36Sopenharmony_ci	/* match the ref taken by inotify_idr_find_locked() */
51462306a36Sopenharmony_ci	if (found_i_mark)
51562306a36Sopenharmony_ci		fsnotify_put_mark(&found_i_mark->fsn_mark);
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci/*
51962306a36Sopenharmony_ci * Send IN_IGNORED for this wd, remove this wd from the idr.
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_civoid inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
52262306a36Sopenharmony_ci				    struct fsnotify_group *group)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct inotify_inode_mark *i_mark;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/* Queue ignore event for the watch */
52762306a36Sopenharmony_ci	inotify_handle_inode_event(fsn_mark, FS_IN_IGNORED, NULL, NULL, NULL,
52862306a36Sopenharmony_ci				   0);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
53162306a36Sopenharmony_ci	/* remove this mark from the idr */
53262306a36Sopenharmony_ci	inotify_remove_from_idr(group, i_mark);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	dec_inotify_watches(group->inotify_data.ucounts);
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_cistatic int inotify_update_existing_watch(struct fsnotify_group *group,
53862306a36Sopenharmony_ci					 struct inode *inode,
53962306a36Sopenharmony_ci					 u32 arg)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct fsnotify_mark *fsn_mark;
54262306a36Sopenharmony_ci	struct inotify_inode_mark *i_mark;
54362306a36Sopenharmony_ci	__u32 old_mask, new_mask;
54462306a36Sopenharmony_ci	int replace = !(arg & IN_MASK_ADD);
54562306a36Sopenharmony_ci	int create = (arg & IN_MASK_CREATE);
54662306a36Sopenharmony_ci	int ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
54962306a36Sopenharmony_ci	if (!fsn_mark)
55062306a36Sopenharmony_ci		return -ENOENT;
55162306a36Sopenharmony_ci	else if (create) {
55262306a36Sopenharmony_ci		ret = -EEXIST;
55362306a36Sopenharmony_ci		goto out;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	spin_lock(&fsn_mark->lock);
55962306a36Sopenharmony_ci	old_mask = fsn_mark->mask;
56062306a36Sopenharmony_ci	if (replace) {
56162306a36Sopenharmony_ci		fsn_mark->mask = 0;
56262306a36Sopenharmony_ci		fsn_mark->flags &= ~INOTIFY_MARK_FLAGS;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci	fsn_mark->mask |= inotify_arg_to_mask(inode, arg);
56562306a36Sopenharmony_ci	fsn_mark->flags |= inotify_arg_to_flags(arg);
56662306a36Sopenharmony_ci	new_mask = fsn_mark->mask;
56762306a36Sopenharmony_ci	spin_unlock(&fsn_mark->lock);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (old_mask != new_mask) {
57062306a36Sopenharmony_ci		/* more bits in old than in new? */
57162306a36Sopenharmony_ci		int dropped = (old_mask & ~new_mask);
57262306a36Sopenharmony_ci		/* more bits in this fsn_mark than the inode's mask? */
57362306a36Sopenharmony_ci		int do_inode = (new_mask & ~inode->i_fsnotify_mask);
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		/* update the inode with this new fsn_mark */
57662306a36Sopenharmony_ci		if (dropped || do_inode)
57762306a36Sopenharmony_ci			fsnotify_recalc_mask(inode->i_fsnotify_marks);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* return the wd */
58262306a36Sopenharmony_ci	ret = i_mark->wd;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciout:
58562306a36Sopenharmony_ci	/* match the get from fsnotify_find_mark() */
58662306a36Sopenharmony_ci	fsnotify_put_mark(fsn_mark);
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return ret;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic int inotify_new_watch(struct fsnotify_group *group,
59262306a36Sopenharmony_ci			     struct inode *inode,
59362306a36Sopenharmony_ci			     u32 arg)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	struct inotify_inode_mark *tmp_i_mark;
59662306a36Sopenharmony_ci	int ret;
59762306a36Sopenharmony_ci	struct idr *idr = &group->inotify_data.idr;
59862306a36Sopenharmony_ci	spinlock_t *idr_lock = &group->inotify_data.idr_lock;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL);
60162306a36Sopenharmony_ci	if (unlikely(!tmp_i_mark))
60262306a36Sopenharmony_ci		return -ENOMEM;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	fsnotify_init_mark(&tmp_i_mark->fsn_mark, group);
60562306a36Sopenharmony_ci	tmp_i_mark->fsn_mark.mask = inotify_arg_to_mask(inode, arg);
60662306a36Sopenharmony_ci	tmp_i_mark->fsn_mark.flags = inotify_arg_to_flags(arg);
60762306a36Sopenharmony_ci	tmp_i_mark->wd = -1;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	ret = inotify_add_to_idr(idr, idr_lock, tmp_i_mark);
61062306a36Sopenharmony_ci	if (ret)
61162306a36Sopenharmony_ci		goto out_err;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* increment the number of watches the user has */
61462306a36Sopenharmony_ci	if (!inc_inotify_watches(group->inotify_data.ucounts)) {
61562306a36Sopenharmony_ci		inotify_remove_from_idr(group, tmp_i_mark);
61662306a36Sopenharmony_ci		ret = -ENOSPC;
61762306a36Sopenharmony_ci		goto out_err;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* we are on the idr, now get on the inode */
62162306a36Sopenharmony_ci	ret = fsnotify_add_inode_mark_locked(&tmp_i_mark->fsn_mark, inode, 0);
62262306a36Sopenharmony_ci	if (ret) {
62362306a36Sopenharmony_ci		/* we failed to get on the inode, get off the idr */
62462306a36Sopenharmony_ci		inotify_remove_from_idr(group, tmp_i_mark);
62562306a36Sopenharmony_ci		goto out_err;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	/* return the watch descriptor for this new mark */
63062306a36Sopenharmony_ci	ret = tmp_i_mark->wd;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ciout_err:
63362306a36Sopenharmony_ci	/* match the ref from fsnotify_init_mark() */
63462306a36Sopenharmony_ci	fsnotify_put_mark(&tmp_i_mark->fsn_mark);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return ret;
63762306a36Sopenharmony_ci}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic int inotify_update_watch(struct fsnotify_group *group, struct inode *inode, u32 arg)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	int ret = 0;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	fsnotify_group_lock(group);
64462306a36Sopenharmony_ci	/* try to update and existing watch with the new arg */
64562306a36Sopenharmony_ci	ret = inotify_update_existing_watch(group, inode, arg);
64662306a36Sopenharmony_ci	/* no mark present, try to add a new one */
64762306a36Sopenharmony_ci	if (ret == -ENOENT)
64862306a36Sopenharmony_ci		ret = inotify_new_watch(group, inode, arg);
64962306a36Sopenharmony_ci	fsnotify_group_unlock(group);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	return ret;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic struct fsnotify_group *inotify_new_group(unsigned int max_events)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct fsnotify_group *group;
65762306a36Sopenharmony_ci	struct inotify_event_info *oevent;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	group = fsnotify_alloc_group(&inotify_fsnotify_ops,
66062306a36Sopenharmony_ci				     FSNOTIFY_GROUP_USER);
66162306a36Sopenharmony_ci	if (IS_ERR(group))
66262306a36Sopenharmony_ci		return group;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	oevent = kmalloc(sizeof(struct inotify_event_info), GFP_KERNEL_ACCOUNT);
66562306a36Sopenharmony_ci	if (unlikely(!oevent)) {
66662306a36Sopenharmony_ci		fsnotify_destroy_group(group);
66762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
66862306a36Sopenharmony_ci	}
66962306a36Sopenharmony_ci	group->overflow_event = &oevent->fse;
67062306a36Sopenharmony_ci	fsnotify_init_event(group->overflow_event);
67162306a36Sopenharmony_ci	oevent->mask = FS_Q_OVERFLOW;
67262306a36Sopenharmony_ci	oevent->wd = -1;
67362306a36Sopenharmony_ci	oevent->sync_cookie = 0;
67462306a36Sopenharmony_ci	oevent->name_len = 0;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	group->max_events = max_events;
67762306a36Sopenharmony_ci	group->memcg = get_mem_cgroup_from_mm(current->mm);
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	spin_lock_init(&group->inotify_data.idr_lock);
68062306a36Sopenharmony_ci	idr_init(&group->inotify_data.idr);
68162306a36Sopenharmony_ci	group->inotify_data.ucounts = inc_ucount(current_user_ns(),
68262306a36Sopenharmony_ci						 current_euid(),
68362306a36Sopenharmony_ci						 UCOUNT_INOTIFY_INSTANCES);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (!group->inotify_data.ucounts) {
68662306a36Sopenharmony_ci		fsnotify_destroy_group(group);
68762306a36Sopenharmony_ci		return ERR_PTR(-EMFILE);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return group;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci/* inotify syscalls */
69562306a36Sopenharmony_cistatic int do_inotify_init(int flags)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct fsnotify_group *group;
69862306a36Sopenharmony_ci	int ret;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* Check the IN_* constants for consistency.  */
70162306a36Sopenharmony_ci	BUILD_BUG_ON(IN_CLOEXEC != O_CLOEXEC);
70262306a36Sopenharmony_ci	BUILD_BUG_ON(IN_NONBLOCK != O_NONBLOCK);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
70562306a36Sopenharmony_ci		return -EINVAL;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */
70862306a36Sopenharmony_ci	group = inotify_new_group(inotify_max_queued_events);
70962306a36Sopenharmony_ci	if (IS_ERR(group))
71062306a36Sopenharmony_ci		return PTR_ERR(group);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	ret = anon_inode_getfd("inotify", &inotify_fops, group,
71362306a36Sopenharmony_ci				  O_RDONLY | flags);
71462306a36Sopenharmony_ci	if (ret < 0)
71562306a36Sopenharmony_ci		fsnotify_destroy_group(group);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	return ret;
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciSYSCALL_DEFINE1(inotify_init1, int, flags)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	return do_inotify_init(flags);
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ciSYSCALL_DEFINE0(inotify_init)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	return do_inotify_init(0);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ciSYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
73162306a36Sopenharmony_ci		u32, mask)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	struct fsnotify_group *group;
73462306a36Sopenharmony_ci	struct inode *inode;
73562306a36Sopenharmony_ci	struct path path;
73662306a36Sopenharmony_ci	struct fd f;
73762306a36Sopenharmony_ci	int ret;
73862306a36Sopenharmony_ci	unsigned flags = 0;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/*
74162306a36Sopenharmony_ci	 * We share a lot of code with fs/dnotify.  We also share
74262306a36Sopenharmony_ci	 * the bit layout between inotify's IN_* and the fsnotify
74362306a36Sopenharmony_ci	 * FS_*.  This check ensures that only the inotify IN_*
74462306a36Sopenharmony_ci	 * bits get passed in and set in watches/events.
74562306a36Sopenharmony_ci	 */
74662306a36Sopenharmony_ci	if (unlikely(mask & ~ALL_INOTIFY_BITS))
74762306a36Sopenharmony_ci		return -EINVAL;
74862306a36Sopenharmony_ci	/*
74962306a36Sopenharmony_ci	 * Require at least one valid bit set in the mask.
75062306a36Sopenharmony_ci	 * Without _something_ set, we would have no events to
75162306a36Sopenharmony_ci	 * watch for.
75262306a36Sopenharmony_ci	 */
75362306a36Sopenharmony_ci	if (unlikely(!(mask & ALL_INOTIFY_BITS)))
75462306a36Sopenharmony_ci		return -EINVAL;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	f = fdget(fd);
75762306a36Sopenharmony_ci	if (unlikely(!f.file))
75862306a36Sopenharmony_ci		return -EBADF;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
76162306a36Sopenharmony_ci	if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE))) {
76262306a36Sopenharmony_ci		ret = -EINVAL;
76362306a36Sopenharmony_ci		goto fput_and_out;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* verify that this is indeed an inotify instance */
76762306a36Sopenharmony_ci	if (unlikely(f.file->f_op != &inotify_fops)) {
76862306a36Sopenharmony_ci		ret = -EINVAL;
76962306a36Sopenharmony_ci		goto fput_and_out;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (!(mask & IN_DONT_FOLLOW))
77362306a36Sopenharmony_ci		flags |= LOOKUP_FOLLOW;
77462306a36Sopenharmony_ci	if (mask & IN_ONLYDIR)
77562306a36Sopenharmony_ci		flags |= LOOKUP_DIRECTORY;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ret = inotify_find_inode(pathname, &path, flags,
77862306a36Sopenharmony_ci			(mask & IN_ALL_EVENTS));
77962306a36Sopenharmony_ci	if (ret)
78062306a36Sopenharmony_ci		goto fput_and_out;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	/* inode held in place by reference to path; group by fget on fd */
78362306a36Sopenharmony_ci	inode = path.dentry->d_inode;
78462306a36Sopenharmony_ci	group = f.file->private_data;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* create/update an inode mark */
78762306a36Sopenharmony_ci	ret = inotify_update_watch(group, inode, mask);
78862306a36Sopenharmony_ci	path_put(&path);
78962306a36Sopenharmony_cifput_and_out:
79062306a36Sopenharmony_ci	fdput(f);
79162306a36Sopenharmony_ci	return ret;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ciSYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	struct fsnotify_group *group;
79762306a36Sopenharmony_ci	struct inotify_inode_mark *i_mark;
79862306a36Sopenharmony_ci	struct fd f;
79962306a36Sopenharmony_ci	int ret = -EINVAL;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	f = fdget(fd);
80262306a36Sopenharmony_ci	if (unlikely(!f.file))
80362306a36Sopenharmony_ci		return -EBADF;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* verify that this is indeed an inotify instance */
80662306a36Sopenharmony_ci	if (unlikely(f.file->f_op != &inotify_fops))
80762306a36Sopenharmony_ci		goto out;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	group = f.file->private_data;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	i_mark = inotify_idr_find(group, wd);
81262306a36Sopenharmony_ci	if (unlikely(!i_mark))
81362306a36Sopenharmony_ci		goto out;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	ret = 0;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	fsnotify_destroy_mark(&i_mark->fsn_mark, group);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	/* match ref taken by inotify_idr_find */
82062306a36Sopenharmony_ci	fsnotify_put_mark(&i_mark->fsn_mark);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ciout:
82362306a36Sopenharmony_ci	fdput(f);
82462306a36Sopenharmony_ci	return ret;
82562306a36Sopenharmony_ci}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci/*
82862306a36Sopenharmony_ci * inotify_user_setup - Our initialization function.  Note that we cannot return
82962306a36Sopenharmony_ci * error because we have compiled-in VFS hooks.  So an (unlikely) failure here
83062306a36Sopenharmony_ci * must result in panic().
83162306a36Sopenharmony_ci */
83262306a36Sopenharmony_cistatic int __init inotify_user_setup(void)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	unsigned long watches_max;
83562306a36Sopenharmony_ci	struct sysinfo si;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	si_meminfo(&si);
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * Allow up to 1% of addressable memory to be allocated for inotify
84062306a36Sopenharmony_ci	 * watches (per user) limited to the range [8192, 1048576].
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci	watches_max = (((si.totalram - si.totalhigh) / 100) << PAGE_SHIFT) /
84362306a36Sopenharmony_ci			INOTIFY_WATCH_COST;
84462306a36Sopenharmony_ci	watches_max = clamp(watches_max, 8192UL, 1048576UL);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	BUILD_BUG_ON(IN_ACCESS != FS_ACCESS);
84762306a36Sopenharmony_ci	BUILD_BUG_ON(IN_MODIFY != FS_MODIFY);
84862306a36Sopenharmony_ci	BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB);
84962306a36Sopenharmony_ci	BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE);
85062306a36Sopenharmony_ci	BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
85162306a36Sopenharmony_ci	BUILD_BUG_ON(IN_OPEN != FS_OPEN);
85262306a36Sopenharmony_ci	BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM);
85362306a36Sopenharmony_ci	BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO);
85462306a36Sopenharmony_ci	BUILD_BUG_ON(IN_CREATE != FS_CREATE);
85562306a36Sopenharmony_ci	BUILD_BUG_ON(IN_DELETE != FS_DELETE);
85662306a36Sopenharmony_ci	BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF);
85762306a36Sopenharmony_ci	BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF);
85862306a36Sopenharmony_ci	BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT);
85962306a36Sopenharmony_ci	BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW);
86062306a36Sopenharmony_ci	BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED);
86162306a36Sopenharmony_ci	BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	BUILD_BUG_ON(HWEIGHT32(ALL_INOTIFY_BITS) != 22);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark,
86662306a36Sopenharmony_ci					       SLAB_PANIC|SLAB_ACCOUNT);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	inotify_max_queued_events = 16384;
86962306a36Sopenharmony_ci	init_user_ns.ucount_max[UCOUNT_INOTIFY_INSTANCES] = 128;
87062306a36Sopenharmony_ci	init_user_ns.ucount_max[UCOUNT_INOTIFY_WATCHES] = watches_max;
87162306a36Sopenharmony_ci	inotify_sysctls_init();
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	return 0;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_cifs_initcall(inotify_user_setup);
876