1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
233d3dfffSAndreas Gruenbacher #include <linux/fanotify.h>
311637e4bSEric Paris #include <linux/fcntl.h>
4af579bebSMatthew Bobrowski #include <linux/fdtable.h>
52a3edf86SEric Paris #include <linux/file.h>
611637e4bSEric Paris #include <linux/fs.h>
752c923ddSEric Paris #include <linux/anon_inodes.h>
811637e4bSEric Paris #include <linux/fsnotify_backend.h>
92a3edf86SEric Paris #include <linux/init.h>
10a1014f10SEric Paris #include <linux/mount.h>
112a3edf86SEric Paris #include <linux/namei.h>
12a1014f10SEric Paris #include <linux/poll.h>
1311637e4bSEric Paris #include <linux/security.h>
1411637e4bSEric Paris #include <linux/syscalls.h>
15e4e047a2STejun Heo #include <linux/slab.h>
162a3edf86SEric Paris #include <linux/types.h>
17a1014f10SEric Paris #include <linux/uaccess.h>
1891c2e0bcSAl Viro #include <linux/compat.h>
19174cd4b1SIngo Molnar #include <linux/sched/signal.h>
20d46eb14bSShakeel Butt #include <linux/memcontrol.h>
21a8b13aa2SAmir Goldstein #include <linux/statfs.h>
22a8b13aa2SAmir Goldstein #include <linux/exportfs.h>
23a1014f10SEric Paris
24a1014f10SEric Paris #include <asm/ioctls.h>
2511637e4bSEric Paris
26c63181e6SAl Viro #include "../../mount.h"
27be77196bSCyrill Gorcunov #include "../fdinfo.h"
287053aee2SJan Kara #include "fanotify.h"
29c63181e6SAl Viro
302529a0dfSEric Paris #define FANOTIFY_DEFAULT_MAX_EVENTS 16384
315b8fea65SAmir Goldstein #define FANOTIFY_OLD_DEFAULT_MAX_MARKS 8192
325b8fea65SAmir Goldstein #define FANOTIFY_DEFAULT_MAX_GROUPS 128
33734a1a5eSGabriel Krisman Bertazi #define FANOTIFY_DEFAULT_FEE_POOL_SIZE 32
345b8fea65SAmir Goldstein
355b8fea65SAmir Goldstein /*
365b8fea65SAmir Goldstein * Legacy fanotify marks limits (8192) is per group and we introduced a tunable
375b8fea65SAmir Goldstein * limit of marks per user, similar to inotify. Effectively, the legacy limit
385b8fea65SAmir Goldstein * of fanotify marks per user is <max marks per group> * <max groups per user>.
395b8fea65SAmir Goldstein * This default limit (1M) also happens to match the increased limit of inotify
405b8fea65SAmir Goldstein * max_user_watches since v5.10.
415b8fea65SAmir Goldstein */
425b8fea65SAmir Goldstein #define FANOTIFY_DEFAULT_MAX_USER_MARKS \
435b8fea65SAmir Goldstein (FANOTIFY_OLD_DEFAULT_MAX_MARKS * FANOTIFY_DEFAULT_MAX_GROUPS)
445b8fea65SAmir Goldstein
455b8fea65SAmir Goldstein /*
465b8fea65SAmir Goldstein * Most of the memory cost of adding an inode mark is pinning the marked inode.
475b8fea65SAmir Goldstein * The size of the filesystem inode struct is not uniform across filesystems,
485b8fea65SAmir Goldstein * so double the size of a VFS inode is used as a conservative approximation.
495b8fea65SAmir Goldstein */
505b8fea65SAmir Goldstein #define INODE_MARK_COST (2 * sizeof(struct inode))
515b8fea65SAmir Goldstein
525b8fea65SAmir Goldstein /* configurable via /proc/sys/fs/fanotify/ */
535b8fea65SAmir Goldstein static int fanotify_max_queued_events __read_mostly;
545b8fea65SAmir Goldstein
555b8fea65SAmir Goldstein #ifdef CONFIG_SYSCTL
565b8fea65SAmir Goldstein
575b8fea65SAmir Goldstein #include <linux/sysctl.h>
585b8fea65SAmir Goldstein
59f153c224SSven Schnelle static long ft_zero = 0;
60f153c224SSven Schnelle static long ft_int_max = INT_MAX;
61f153c224SSven Schnelle
627b9ad122SXiaoming Ni static struct ctl_table fanotify_table[] = {
635b8fea65SAmir Goldstein {
645b8fea65SAmir Goldstein .procname = "max_user_groups",
655b8fea65SAmir Goldstein .data = &init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS],
66f153c224SSven Schnelle .maxlen = sizeof(long),
675b8fea65SAmir Goldstein .mode = 0644,
68f153c224SSven Schnelle .proc_handler = proc_doulongvec_minmax,
69f153c224SSven Schnelle .extra1 = &ft_zero,
70f153c224SSven Schnelle .extra2 = &ft_int_max,
715b8fea65SAmir Goldstein },
725b8fea65SAmir Goldstein {
735b8fea65SAmir Goldstein .procname = "max_user_marks",
745b8fea65SAmir Goldstein .data = &init_user_ns.ucount_max[UCOUNT_FANOTIFY_MARKS],
75f153c224SSven Schnelle .maxlen = sizeof(long),
765b8fea65SAmir Goldstein .mode = 0644,
77f153c224SSven Schnelle .proc_handler = proc_doulongvec_minmax,
78f153c224SSven Schnelle .extra1 = &ft_zero,
79f153c224SSven Schnelle .extra2 = &ft_int_max,
805b8fea65SAmir Goldstein },
815b8fea65SAmir Goldstein {
825b8fea65SAmir Goldstein .procname = "max_queued_events",
835b8fea65SAmir Goldstein .data = &fanotify_max_queued_events,
845b8fea65SAmir Goldstein .maxlen = sizeof(int),
855b8fea65SAmir Goldstein .mode = 0644,
865b8fea65SAmir Goldstein .proc_handler = proc_dointvec_minmax,
875b8fea65SAmir Goldstein .extra1 = SYSCTL_ZERO
885b8fea65SAmir Goldstein },
895b8fea65SAmir Goldstein { }
905b8fea65SAmir Goldstein };
917b9ad122SXiaoming Ni
fanotify_sysctls_init(void)927b9ad122SXiaoming Ni static void __init fanotify_sysctls_init(void)
937b9ad122SXiaoming Ni {
947b9ad122SXiaoming Ni register_sysctl("fs/fanotify", fanotify_table);
957b9ad122SXiaoming Ni }
967b9ad122SXiaoming Ni #else
977b9ad122SXiaoming Ni #define fanotify_sysctls_init() do { } while (0)
985b8fea65SAmir Goldstein #endif /* CONFIG_SYSCTL */
992529a0dfSEric Paris
10048149e9dSHeinrich Schuchardt /*
10148149e9dSHeinrich Schuchardt * All flags that may be specified in parameter event_f_flags of fanotify_init.
10248149e9dSHeinrich Schuchardt *
10348149e9dSHeinrich Schuchardt * Internal and external open flags are stored together in field f_flags of
10448149e9dSHeinrich Schuchardt * struct file. Only external open flags shall be allowed in event_f_flags.
10548149e9dSHeinrich Schuchardt * Internal flags like FMODE_NONOTIFY, FMODE_EXEC, FMODE_NOCMTIME shall be
10648149e9dSHeinrich Schuchardt * excluded.
10748149e9dSHeinrich Schuchardt */
10848149e9dSHeinrich Schuchardt #define FANOTIFY_INIT_ALL_EVENT_F_BITS ( \
10948149e9dSHeinrich Schuchardt O_ACCMODE | O_APPEND | O_NONBLOCK | \
11048149e9dSHeinrich Schuchardt __O_SYNC | O_DSYNC | O_CLOEXEC | \
11148149e9dSHeinrich Schuchardt O_LARGEFILE | O_NOATIME )
11248149e9dSHeinrich Schuchardt
11333d3dfffSAndreas Gruenbacher extern const struct fsnotify_ops fanotify_fsnotify_ops;
11411637e4bSEric Paris
115054c636eSJan Kara struct kmem_cache *fanotify_mark_cache __read_mostly;
1167088f357SJan Kara struct kmem_cache *fanotify_fid_event_cachep __read_mostly;
1177088f357SJan Kara struct kmem_cache *fanotify_path_event_cachep __read_mostly;
118f083441bSJan Kara struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
1192a3edf86SEric Paris
1205e469c83SAmir Goldstein #define FANOTIFY_EVENT_ALIGN 4
121d3424c9bSMatthew Bobrowski #define FANOTIFY_FID_INFO_HDR_LEN \
12244d705b0SAmir Goldstein (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
123af579bebSMatthew Bobrowski #define FANOTIFY_PIDFD_INFO_HDR_LEN \
124af579bebSMatthew Bobrowski sizeof(struct fanotify_event_info_pidfd)
125130a3c74SGabriel Krisman Bertazi #define FANOTIFY_ERROR_INFO_LEN \
126130a3c74SGabriel Krisman Bertazi (sizeof(struct fanotify_event_info_error))
1275e469c83SAmir Goldstein
fanotify_fid_info_len(int fh_len,int name_len)12844d705b0SAmir Goldstein static int fanotify_fid_info_len(int fh_len, int name_len)
129d766b553SAmir Goldstein {
13044d705b0SAmir Goldstein int info_len = fh_len;
13144d705b0SAmir Goldstein
13244d705b0SAmir Goldstein if (name_len)
13344d705b0SAmir Goldstein info_len += name_len + 1;
13444d705b0SAmir Goldstein
135d3424c9bSMatthew Bobrowski return roundup(FANOTIFY_FID_INFO_HDR_LEN + info_len,
136d3424c9bSMatthew Bobrowski FANOTIFY_EVENT_ALIGN);
137d766b553SAmir Goldstein }
138d766b553SAmir Goldstein
1397326e382SAmir Goldstein /* FAN_RENAME may have one or two dir+name info records */
fanotify_dir_name_info_len(struct fanotify_event * event)1407326e382SAmir Goldstein static int fanotify_dir_name_info_len(struct fanotify_event *event)
1417326e382SAmir Goldstein {
1427326e382SAmir Goldstein struct fanotify_info *info = fanotify_event_info(event);
1437326e382SAmir Goldstein int dir_fh_len = fanotify_event_dir_fh_len(event);
1447326e382SAmir Goldstein int dir2_fh_len = fanotify_event_dir2_fh_len(event);
1457326e382SAmir Goldstein int info_len = 0;
1467326e382SAmir Goldstein
1477326e382SAmir Goldstein if (dir_fh_len)
1487326e382SAmir Goldstein info_len += fanotify_fid_info_len(dir_fh_len,
1497326e382SAmir Goldstein info->name_len);
1507326e382SAmir Goldstein if (dir2_fh_len)
1517326e382SAmir Goldstein info_len += fanotify_fid_info_len(dir2_fh_len,
1527326e382SAmir Goldstein info->name2_len);
1537326e382SAmir Goldstein
1547326e382SAmir Goldstein return info_len;
1557326e382SAmir Goldstein }
1567326e382SAmir Goldstein
fanotify_event_len(unsigned int info_mode,struct fanotify_event * event)157b9928e80SGabriel Krisman Bertazi static size_t fanotify_event_len(unsigned int info_mode,
158929943b3SAmir Goldstein struct fanotify_event *event)
1595e469c83SAmir Goldstein {
160b9928e80SGabriel Krisman Bertazi size_t event_len = FAN_EVENT_METADATA_LEN;
161b9928e80SGabriel Krisman Bertazi int fh_len;
162929943b3SAmir Goldstein int dot_len = 0;
163f454fa61SAmir Goldstein
164b9928e80SGabriel Krisman Bertazi if (!info_mode)
165b9928e80SGabriel Krisman Bertazi return event_len;
166b9928e80SGabriel Krisman Bertazi
167130a3c74SGabriel Krisman Bertazi if (fanotify_is_error_event(event->mask))
168130a3c74SGabriel Krisman Bertazi event_len += FANOTIFY_ERROR_INFO_LEN;
169130a3c74SGabriel Krisman Bertazi
1707326e382SAmir Goldstein if (fanotify_event_has_any_dir_fh(event)) {
1717326e382SAmir Goldstein event_len += fanotify_dir_name_info_len(event);
172d3424c9bSMatthew Bobrowski } else if ((info_mode & FAN_REPORT_NAME) &&
173d3424c9bSMatthew Bobrowski (event->mask & FAN_ONDIR)) {
174929943b3SAmir Goldstein /*
175929943b3SAmir Goldstein * With group flag FAN_REPORT_NAME, if name was not recorded in
176929943b3SAmir Goldstein * event on a directory, we will report the name ".".
177929943b3SAmir Goldstein */
178929943b3SAmir Goldstein dot_len = 1;
179929943b3SAmir Goldstein }
180afc894c7SJan Kara
181af579bebSMatthew Bobrowski if (info_mode & FAN_REPORT_PIDFD)
182b9928e80SGabriel Krisman Bertazi event_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
183af579bebSMatthew Bobrowski
1844bd5a5c8SGabriel Krisman Bertazi if (fanotify_event_has_object_fh(event)) {
1854bd5a5c8SGabriel Krisman Bertazi fh_len = fanotify_event_object_fh_len(event);
186b9928e80SGabriel Krisman Bertazi event_len += fanotify_fid_info_len(fh_len, dot_len);
1874bd5a5c8SGabriel Krisman Bertazi }
1885e469c83SAmir Goldstein
189b9928e80SGabriel Krisman Bertazi return event_len;
1905e469c83SAmir Goldstein }
1915e469c83SAmir Goldstein
192a1014f10SEric Paris /*
19394e00d28SAmir Goldstein * Remove an hashed event from merge hash table.
19494e00d28SAmir Goldstein */
fanotify_unhash_event(struct fsnotify_group * group,struct fanotify_event * event)19594e00d28SAmir Goldstein static void fanotify_unhash_event(struct fsnotify_group *group,
19694e00d28SAmir Goldstein struct fanotify_event *event)
19794e00d28SAmir Goldstein {
19894e00d28SAmir Goldstein assert_spin_locked(&group->notification_lock);
19994e00d28SAmir Goldstein
20094e00d28SAmir Goldstein pr_debug("%s: group=%p event=%p bucket=%u\n", __func__,
20194e00d28SAmir Goldstein group, event, fanotify_event_hash_bucket(group, event));
20294e00d28SAmir Goldstein
20394e00d28SAmir Goldstein if (WARN_ON_ONCE(hlist_unhashed(&event->merge_list)))
20494e00d28SAmir Goldstein return;
20594e00d28SAmir Goldstein
20694e00d28SAmir Goldstein hlist_del_init(&event->merge_list);
20794e00d28SAmir Goldstein }
20894e00d28SAmir Goldstein
20994e00d28SAmir Goldstein /*
2107088f357SJan Kara * Get an fanotify notification event if one exists and is small
211a1014f10SEric Paris * enough to fit in "count". Return an error pointer if the count
21240873284SJan Kara * is not large enough. When permission event is dequeued, its state is
21340873284SJan Kara * updated accordingly.
214a1014f10SEric Paris */
get_one_event(struct fsnotify_group * group,size_t count)2157088f357SJan Kara static struct fanotify_event *get_one_event(struct fsnotify_group *group,
216a1014f10SEric Paris size_t count)
217a1014f10SEric Paris {
218b9928e80SGabriel Krisman Bertazi size_t event_size;
2197088f357SJan Kara struct fanotify_event *event = NULL;
2206f73171eSAmir Goldstein struct fsnotify_event *fsn_event;
2210aca67bbSMatthew Bobrowski unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
222a1014f10SEric Paris
223a1014f10SEric Paris pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
224a1014f10SEric Paris
2258c554466SJan Kara spin_lock(&group->notification_lock);
2266f73171eSAmir Goldstein fsn_event = fsnotify_peek_first_event(group);
2276f73171eSAmir Goldstein if (!fsn_event)
2288c554466SJan Kara goto out;
229a1014f10SEric Paris
2306f73171eSAmir Goldstein event = FANOTIFY_E(fsn_event);
231b9928e80SGabriel Krisman Bertazi event_size = fanotify_event_len(info_mode, event);
2325e469c83SAmir Goldstein
2338c554466SJan Kara if (event_size > count) {
2347088f357SJan Kara event = ERR_PTR(-EINVAL);
2358c554466SJan Kara goto out;
2368c554466SJan Kara }
2376f73171eSAmir Goldstein
2386f73171eSAmir Goldstein /*
2396f73171eSAmir Goldstein * Held the notification_lock the whole time, so this is the
2406f73171eSAmir Goldstein * same event we peeked above.
2416f73171eSAmir Goldstein */
2426f73171eSAmir Goldstein fsnotify_remove_first_event(group);
2437088f357SJan Kara if (fanotify_is_perm_event(event->mask))
2447088f357SJan Kara FANOTIFY_PERM(event)->state = FAN_EVENT_REPORTED;
24594e00d28SAmir Goldstein if (fanotify_is_hashed_event(event->mask))
24694e00d28SAmir Goldstein fanotify_unhash_event(group, event);
2478c554466SJan Kara out:
2488c554466SJan Kara spin_unlock(&group->notification_lock);
2497088f357SJan Kara return event;
250a1014f10SEric Paris }
251a1014f10SEric Paris
create_fd(struct fsnotify_group * group,const struct path * path,struct file ** file)252d5bf8889SAl Viro static int create_fd(struct fsnotify_group *group, const struct path *path,
253352e3b24SAl Viro struct file **file)
254a1014f10SEric Paris {
255a1014f10SEric Paris int client_fd;
256a1014f10SEric Paris struct file *new_file;
257a1014f10SEric Paris
2580b37e097SYann Droneaud client_fd = get_unused_fd_flags(group->fanotify_data.f_flags);
259a1014f10SEric Paris if (client_fd < 0)
260a1014f10SEric Paris return client_fd;
261a1014f10SEric Paris
262a1014f10SEric Paris /*
263a1014f10SEric Paris * we need a new file handle for the userspace program so it can read even if it was
264a1014f10SEric Paris * originally opened O_WRONLY.
265a1014f10SEric Paris */
266a741c2feSJan Kara new_file = dentry_open(path,
267dccd8557SVasily Averin group->fanotify_data.f_flags | __FMODE_NONOTIFY,
268a1014f10SEric Paris current_cred());
269a1014f10SEric Paris if (IS_ERR(new_file)) {
270a1014f10SEric Paris put_unused_fd(client_fd);
271a1014f10SEric Paris client_fd = PTR_ERR(new_file);
272a1014f10SEric Paris } else {
273352e3b24SAl Viro *file = new_file;
274a1014f10SEric Paris }
275a1014f10SEric Paris
27622aa425dSAndreas Gruenbacher return client_fd;
277a1014f10SEric Paris }
278a1014f10SEric Paris
process_access_response_info(const char __user * info,size_t info_len,struct fanotify_response_info_audit_rule * friar)27970529a19SRichard Guy Briggs static int process_access_response_info(const char __user *info,
28070529a19SRichard Guy Briggs size_t info_len,
28170529a19SRichard Guy Briggs struct fanotify_response_info_audit_rule *friar)
28270529a19SRichard Guy Briggs {
28370529a19SRichard Guy Briggs if (info_len != sizeof(*friar))
28470529a19SRichard Guy Briggs return -EINVAL;
28570529a19SRichard Guy Briggs
28670529a19SRichard Guy Briggs if (copy_from_user(friar, info, sizeof(*friar)))
28770529a19SRichard Guy Briggs return -EFAULT;
28870529a19SRichard Guy Briggs
28970529a19SRichard Guy Briggs if (friar->hdr.type != FAN_RESPONSE_INFO_AUDIT_RULE)
29070529a19SRichard Guy Briggs return -EINVAL;
29170529a19SRichard Guy Briggs if (friar->hdr.pad != 0)
29270529a19SRichard Guy Briggs return -EINVAL;
29370529a19SRichard Guy Briggs if (friar->hdr.len != sizeof(*friar))
29470529a19SRichard Guy Briggs return -EINVAL;
29570529a19SRichard Guy Briggs
29670529a19SRichard Guy Briggs return info_len;
29770529a19SRichard Guy Briggs }
29870529a19SRichard Guy Briggs
29940873284SJan Kara /*
30040873284SJan Kara * Finish processing of permission event by setting it to ANSWERED state and
30140873284SJan Kara * drop group->notification_lock.
30240873284SJan Kara */
finish_permission_event(struct fsnotify_group * group,struct fanotify_perm_event * event,u32 response,struct fanotify_response_info_audit_rule * friar)30340873284SJan Kara static void finish_permission_event(struct fsnotify_group *group,
30470529a19SRichard Guy Briggs struct fanotify_perm_event *event, u32 response,
30570529a19SRichard Guy Briggs struct fanotify_response_info_audit_rule *friar)
30640873284SJan Kara __releases(&group->notification_lock)
30740873284SJan Kara {
308fabf7f29SJan Kara bool destroy = false;
309fabf7f29SJan Kara
31040873284SJan Kara assert_spin_locked(&group->notification_lock);
31170529a19SRichard Guy Briggs event->response = response & ~FAN_INFO;
31270529a19SRichard Guy Briggs if (response & FAN_INFO)
31370529a19SRichard Guy Briggs memcpy(&event->audit_rule, friar, sizeof(*friar));
31470529a19SRichard Guy Briggs
315fabf7f29SJan Kara if (event->state == FAN_EVENT_CANCELED)
316fabf7f29SJan Kara destroy = true;
317fabf7f29SJan Kara else
31840873284SJan Kara event->state = FAN_EVENT_ANSWERED;
31940873284SJan Kara spin_unlock(&group->notification_lock);
320fabf7f29SJan Kara if (destroy)
321fabf7f29SJan Kara fsnotify_destroy_event(group, &event->fae.fse);
32240873284SJan Kara }
32340873284SJan Kara
process_access_response(struct fsnotify_group * group,struct fanotify_response * response_struct,const char __user * info,size_t info_len)324b2d87909SEric Paris static int process_access_response(struct fsnotify_group *group,
32570529a19SRichard Guy Briggs struct fanotify_response *response_struct,
32670529a19SRichard Guy Briggs const char __user *info,
32770529a19SRichard Guy Briggs size_t info_len)
328b2d87909SEric Paris {
32933913997SAmir Goldstein struct fanotify_perm_event *event;
330f083441bSJan Kara int fd = response_struct->fd;
3312e0a5471SRichard Guy Briggs u32 response = response_struct->response;
33270529a19SRichard Guy Briggs int ret = info_len;
33370529a19SRichard Guy Briggs struct fanotify_response_info_audit_rule friar;
334b2d87909SEric Paris
33570529a19SRichard Guy Briggs pr_debug("%s: group=%p fd=%d response=%u buf=%p size=%zu\n", __func__,
33670529a19SRichard Guy Briggs group, fd, response, info, info_len);
337b2d87909SEric Paris /*
338b2d87909SEric Paris * make sure the response is valid, if invalid we do nothing and either
33925985edcSLucas De Marchi * userspace can send a valid response or we will clean it up after the
340b2d87909SEric Paris * timeout
341b2d87909SEric Paris */
34270529a19SRichard Guy Briggs if (response & ~FANOTIFY_RESPONSE_VALID_MASK)
34370529a19SRichard Guy Briggs return -EINVAL;
34470529a19SRichard Guy Briggs
34570529a19SRichard Guy Briggs switch (response & FANOTIFY_RESPONSE_ACCESS) {
346b2d87909SEric Paris case FAN_ALLOW:
347b2d87909SEric Paris case FAN_DENY:
348b2d87909SEric Paris break;
349b2d87909SEric Paris default:
350b2d87909SEric Paris return -EINVAL;
351b2d87909SEric Paris }
352b2d87909SEric Paris
35370529a19SRichard Guy Briggs if ((response & FAN_AUDIT) && !FAN_GROUP_FLAG(group, FAN_ENABLE_AUDIT))
354b2d87909SEric Paris return -EINVAL;
355b2d87909SEric Paris
35670529a19SRichard Guy Briggs if (response & FAN_INFO) {
35770529a19SRichard Guy Briggs ret = process_access_response_info(info, info_len, &friar);
35870529a19SRichard Guy Briggs if (ret < 0)
35970529a19SRichard Guy Briggs return ret;
36070529a19SRichard Guy Briggs if (fd == FAN_NOFD)
36170529a19SRichard Guy Briggs return ret;
36270529a19SRichard Guy Briggs } else {
36370529a19SRichard Guy Briggs ret = 0;
36470529a19SRichard Guy Briggs }
36570529a19SRichard Guy Briggs
36670529a19SRichard Guy Briggs if (fd < 0)
367de8cd83eSSteve Grubb return -EINVAL;
368de8cd83eSSteve Grubb
369af6a5113SJan Kara spin_lock(&group->notification_lock);
370af6a5113SJan Kara list_for_each_entry(event, &group->fanotify_data.access_list,
371af6a5113SJan Kara fae.fse.list) {
372af6a5113SJan Kara if (event->fd != fd)
373af6a5113SJan Kara continue;
374b2d87909SEric Paris
375af6a5113SJan Kara list_del_init(&event->fae.fse.list);
37670529a19SRichard Guy Briggs finish_permission_event(group, event, response, &friar);
377b2d87909SEric Paris wake_up(&group->fanotify_data.access_waitq);
37870529a19SRichard Guy Briggs return ret;
379b2d87909SEric Paris }
380af6a5113SJan Kara spin_unlock(&group->notification_lock);
381af6a5113SJan Kara
382af6a5113SJan Kara return -ENOENT;
383af6a5113SJan Kara }
384b2d87909SEric Paris
copy_error_info_to_user(struct fanotify_event * event,char __user * buf,int count)385130a3c74SGabriel Krisman Bertazi static size_t copy_error_info_to_user(struct fanotify_event *event,
386130a3c74SGabriel Krisman Bertazi char __user *buf, int count)
387130a3c74SGabriel Krisman Bertazi {
3883cf984e9SAmir Goldstein struct fanotify_event_info_error info = { };
389130a3c74SGabriel Krisman Bertazi struct fanotify_error_event *fee = FANOTIFY_EE(event);
390130a3c74SGabriel Krisman Bertazi
391130a3c74SGabriel Krisman Bertazi info.hdr.info_type = FAN_EVENT_INFO_TYPE_ERROR;
392130a3c74SGabriel Krisman Bertazi info.hdr.len = FANOTIFY_ERROR_INFO_LEN;
393130a3c74SGabriel Krisman Bertazi
394130a3c74SGabriel Krisman Bertazi if (WARN_ON(count < info.hdr.len))
395130a3c74SGabriel Krisman Bertazi return -EFAULT;
396130a3c74SGabriel Krisman Bertazi
397130a3c74SGabriel Krisman Bertazi info.error = fee->error;
398130a3c74SGabriel Krisman Bertazi info.error_count = fee->err_count;
399130a3c74SGabriel Krisman Bertazi
400130a3c74SGabriel Krisman Bertazi if (copy_to_user(buf, &info, sizeof(info)))
401130a3c74SGabriel Krisman Bertazi return -EFAULT;
402130a3c74SGabriel Krisman Bertazi
403130a3c74SGabriel Krisman Bertazi return info.hdr.len;
404130a3c74SGabriel Krisman Bertazi }
405130a3c74SGabriel Krisman Bertazi
copy_fid_info_to_user(__kernel_fsid_t * fsid,struct fanotify_fh * fh,int info_type,const char * name,size_t name_len,char __user * buf,size_t count)406d3424c9bSMatthew Bobrowski static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
407d3424c9bSMatthew Bobrowski int info_type, const char *name,
408d3424c9bSMatthew Bobrowski size_t name_len,
40944d705b0SAmir Goldstein char __user *buf, size_t count)
4105e469c83SAmir Goldstein {
4115e469c83SAmir Goldstein struct fanotify_event_info_fid info = { };
4125e469c83SAmir Goldstein struct file_handle handle = { };
413afc894c7SJan Kara unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *fh_buf;
414cacfb956SAmir Goldstein size_t fh_len = fh ? fh->len : 0;
41544d705b0SAmir Goldstein size_t info_len = fanotify_fid_info_len(fh_len, name_len);
41644d705b0SAmir Goldstein size_t len = info_len;
4175e469c83SAmir Goldstein
41844d705b0SAmir Goldstein pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n",
41944d705b0SAmir Goldstein __func__, fh_len, name_len, info_len, count);
42044d705b0SAmir Goldstein
42144d705b0SAmir Goldstein if (WARN_ON_ONCE(len < sizeof(info) || len > count))
4225e469c83SAmir Goldstein return -EFAULT;
4235e469c83SAmir Goldstein
42444d705b0SAmir Goldstein /*
42544d705b0SAmir Goldstein * Copy event info fid header followed by variable sized file handle
42644d705b0SAmir Goldstein * and optionally followed by variable sized filename.
42744d705b0SAmir Goldstein */
42883b7a598SAmir Goldstein switch (info_type) {
42983b7a598SAmir Goldstein case FAN_EVENT_INFO_TYPE_FID:
43083b7a598SAmir Goldstein case FAN_EVENT_INFO_TYPE_DFID:
43183b7a598SAmir Goldstein if (WARN_ON_ONCE(name_len))
43283b7a598SAmir Goldstein return -EFAULT;
43383b7a598SAmir Goldstein break;
43483b7a598SAmir Goldstein case FAN_EVENT_INFO_TYPE_DFID_NAME:
4357326e382SAmir Goldstein case FAN_EVENT_INFO_TYPE_OLD_DFID_NAME:
4367326e382SAmir Goldstein case FAN_EVENT_INFO_TYPE_NEW_DFID_NAME:
43783b7a598SAmir Goldstein if (WARN_ON_ONCE(!name || !name_len))
43883b7a598SAmir Goldstein return -EFAULT;
43983b7a598SAmir Goldstein break;
44083b7a598SAmir Goldstein default:
44183b7a598SAmir Goldstein return -EFAULT;
44283b7a598SAmir Goldstein }
44383b7a598SAmir Goldstein
44483b7a598SAmir Goldstein info.hdr.info_type = info_type;
4455e469c83SAmir Goldstein info.hdr.len = len;
446d766b553SAmir Goldstein info.fsid = *fsid;
4475e469c83SAmir Goldstein if (copy_to_user(buf, &info, sizeof(info)))
4485e469c83SAmir Goldstein return -EFAULT;
4495e469c83SAmir Goldstein
4505e469c83SAmir Goldstein buf += sizeof(info);
4515e469c83SAmir Goldstein len -= sizeof(info);
45244d705b0SAmir Goldstein if (WARN_ON_ONCE(len < sizeof(handle)))
45344d705b0SAmir Goldstein return -EFAULT;
45444d705b0SAmir Goldstein
455afc894c7SJan Kara handle.handle_type = fh->type;
4565e469c83SAmir Goldstein handle.handle_bytes = fh_len;
457936d6a38SGabriel Krisman Bertazi
458936d6a38SGabriel Krisman Bertazi /* Mangle handle_type for bad file_handle */
459936d6a38SGabriel Krisman Bertazi if (!fh_len)
460936d6a38SGabriel Krisman Bertazi handle.handle_type = FILEID_INVALID;
461936d6a38SGabriel Krisman Bertazi
4625e469c83SAmir Goldstein if (copy_to_user(buf, &handle, sizeof(handle)))
4635e469c83SAmir Goldstein return -EFAULT;
4645e469c83SAmir Goldstein
4655e469c83SAmir Goldstein buf += sizeof(handle);
4665e469c83SAmir Goldstein len -= sizeof(handle);
46744d705b0SAmir Goldstein if (WARN_ON_ONCE(len < fh_len))
46844d705b0SAmir Goldstein return -EFAULT;
46944d705b0SAmir Goldstein
470b2d22b6bSJan Kara /*
47144d705b0SAmir Goldstein * For an inline fh and inline file name, copy through stack to exclude
47244d705b0SAmir Goldstein * the copy from usercopy hardening protections.
473b2d22b6bSJan Kara */
474afc894c7SJan Kara fh_buf = fanotify_fh_buf(fh);
475b2d22b6bSJan Kara if (fh_len <= FANOTIFY_INLINE_FH_LEN) {
476afc894c7SJan Kara memcpy(bounce, fh_buf, fh_len);
477afc894c7SJan Kara fh_buf = bounce;
478b2d22b6bSJan Kara }
479afc894c7SJan Kara if (copy_to_user(buf, fh_buf, fh_len))
4805e469c83SAmir Goldstein return -EFAULT;
4815e469c83SAmir Goldstein
4825e469c83SAmir Goldstein buf += fh_len;
4835e469c83SAmir Goldstein len -= fh_len;
48444d705b0SAmir Goldstein
48544d705b0SAmir Goldstein if (name_len) {
48644d705b0SAmir Goldstein /* Copy the filename with terminating null */
48744d705b0SAmir Goldstein name_len++;
48844d705b0SAmir Goldstein if (WARN_ON_ONCE(len < name_len))
48944d705b0SAmir Goldstein return -EFAULT;
49044d705b0SAmir Goldstein
49144d705b0SAmir Goldstein if (copy_to_user(buf, name, name_len))
49244d705b0SAmir Goldstein return -EFAULT;
49344d705b0SAmir Goldstein
49444d705b0SAmir Goldstein buf += name_len;
49544d705b0SAmir Goldstein len -= name_len;
49644d705b0SAmir Goldstein }
49744d705b0SAmir Goldstein
49844d705b0SAmir Goldstein /* Pad with 0's */
4995e469c83SAmir Goldstein WARN_ON_ONCE(len < 0 || len >= FANOTIFY_EVENT_ALIGN);
5005e469c83SAmir Goldstein if (len > 0 && clear_user(buf, len))
5015e469c83SAmir Goldstein return -EFAULT;
5025e469c83SAmir Goldstein
50344d705b0SAmir Goldstein return info_len;
5045e469c83SAmir Goldstein }
5055e469c83SAmir Goldstein
copy_pidfd_info_to_user(int pidfd,char __user * buf,size_t count)506af579bebSMatthew Bobrowski static int copy_pidfd_info_to_user(int pidfd,
507af579bebSMatthew Bobrowski char __user *buf,
508af579bebSMatthew Bobrowski size_t count)
509af579bebSMatthew Bobrowski {
510af579bebSMatthew Bobrowski struct fanotify_event_info_pidfd info = { };
511af579bebSMatthew Bobrowski size_t info_len = FANOTIFY_PIDFD_INFO_HDR_LEN;
512af579bebSMatthew Bobrowski
513af579bebSMatthew Bobrowski if (WARN_ON_ONCE(info_len > count))
514af579bebSMatthew Bobrowski return -EFAULT;
515af579bebSMatthew Bobrowski
516af579bebSMatthew Bobrowski info.hdr.info_type = FAN_EVENT_INFO_TYPE_PIDFD;
517af579bebSMatthew Bobrowski info.hdr.len = info_len;
518af579bebSMatthew Bobrowski info.pidfd = pidfd;
519af579bebSMatthew Bobrowski
520af579bebSMatthew Bobrowski if (copy_to_user(buf, &info, info_len))
521af579bebSMatthew Bobrowski return -EFAULT;
522af579bebSMatthew Bobrowski
523af579bebSMatthew Bobrowski return info_len;
524af579bebSMatthew Bobrowski }
525af579bebSMatthew Bobrowski
copy_info_records_to_user(struct fanotify_event * event,struct fanotify_info * info,unsigned int info_mode,int pidfd,char __user * buf,size_t count)5260aca67bbSMatthew Bobrowski static int copy_info_records_to_user(struct fanotify_event *event,
5270aca67bbSMatthew Bobrowski struct fanotify_info *info,
528af579bebSMatthew Bobrowski unsigned int info_mode, int pidfd,
5290aca67bbSMatthew Bobrowski char __user *buf, size_t count)
5300aca67bbSMatthew Bobrowski {
5310aca67bbSMatthew Bobrowski int ret, total_bytes = 0, info_type = 0;
5320aca67bbSMatthew Bobrowski unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS;
533af579bebSMatthew Bobrowski unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
5340aca67bbSMatthew Bobrowski
5350aca67bbSMatthew Bobrowski /*
5367326e382SAmir Goldstein * Event info records order is as follows:
5377326e382SAmir Goldstein * 1. dir fid + name
5387326e382SAmir Goldstein * 2. (optional) new dir fid + new name
5397326e382SAmir Goldstein * 3. (optional) child fid
5400aca67bbSMatthew Bobrowski */
5414bd5a5c8SGabriel Krisman Bertazi if (fanotify_event_has_dir_fh(event)) {
5420aca67bbSMatthew Bobrowski info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
5430aca67bbSMatthew Bobrowski FAN_EVENT_INFO_TYPE_DFID;
5447326e382SAmir Goldstein
5457326e382SAmir Goldstein /* FAN_RENAME uses special info types */
5467326e382SAmir Goldstein if (event->mask & FAN_RENAME)
5477326e382SAmir Goldstein info_type = FAN_EVENT_INFO_TYPE_OLD_DFID_NAME;
5487326e382SAmir Goldstein
5490aca67bbSMatthew Bobrowski ret = copy_fid_info_to_user(fanotify_event_fsid(event),
5500aca67bbSMatthew Bobrowski fanotify_info_dir_fh(info),
5510aca67bbSMatthew Bobrowski info_type,
5520aca67bbSMatthew Bobrowski fanotify_info_name(info),
5530aca67bbSMatthew Bobrowski info->name_len, buf, count);
5540aca67bbSMatthew Bobrowski if (ret < 0)
5550aca67bbSMatthew Bobrowski return ret;
5560aca67bbSMatthew Bobrowski
5570aca67bbSMatthew Bobrowski buf += ret;
5580aca67bbSMatthew Bobrowski count -= ret;
5590aca67bbSMatthew Bobrowski total_bytes += ret;
5600aca67bbSMatthew Bobrowski }
5610aca67bbSMatthew Bobrowski
5627326e382SAmir Goldstein /* New dir fid+name may be reported in addition to old dir fid+name */
5637326e382SAmir Goldstein if (fanotify_event_has_dir2_fh(event)) {
5647326e382SAmir Goldstein info_type = FAN_EVENT_INFO_TYPE_NEW_DFID_NAME;
5657326e382SAmir Goldstein ret = copy_fid_info_to_user(fanotify_event_fsid(event),
5667326e382SAmir Goldstein fanotify_info_dir2_fh(info),
5677326e382SAmir Goldstein info_type,
5687326e382SAmir Goldstein fanotify_info_name2(info),
5697326e382SAmir Goldstein info->name2_len, buf, count);
5707326e382SAmir Goldstein if (ret < 0)
5717326e382SAmir Goldstein return ret;
5727326e382SAmir Goldstein
5737326e382SAmir Goldstein buf += ret;
5747326e382SAmir Goldstein count -= ret;
5757326e382SAmir Goldstein total_bytes += ret;
5767326e382SAmir Goldstein }
5777326e382SAmir Goldstein
5784bd5a5c8SGabriel Krisman Bertazi if (fanotify_event_has_object_fh(event)) {
5790aca67bbSMatthew Bobrowski const char *dot = NULL;
5800aca67bbSMatthew Bobrowski int dot_len = 0;
5810aca67bbSMatthew Bobrowski
5820aca67bbSMatthew Bobrowski if (fid_mode == FAN_REPORT_FID || info_type) {
5830aca67bbSMatthew Bobrowski /*
5840aca67bbSMatthew Bobrowski * With only group flag FAN_REPORT_FID only type FID is
5850aca67bbSMatthew Bobrowski * reported. Second info record type is always FID.
5860aca67bbSMatthew Bobrowski */
5870aca67bbSMatthew Bobrowski info_type = FAN_EVENT_INFO_TYPE_FID;
5880aca67bbSMatthew Bobrowski } else if ((fid_mode & FAN_REPORT_NAME) &&
5890aca67bbSMatthew Bobrowski (event->mask & FAN_ONDIR)) {
5900aca67bbSMatthew Bobrowski /*
5910aca67bbSMatthew Bobrowski * With group flag FAN_REPORT_NAME, if name was not
5920aca67bbSMatthew Bobrowski * recorded in an event on a directory, report the name
5930aca67bbSMatthew Bobrowski * "." with info type DFID_NAME.
5940aca67bbSMatthew Bobrowski */
5950aca67bbSMatthew Bobrowski info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
5960aca67bbSMatthew Bobrowski dot = ".";
5970aca67bbSMatthew Bobrowski dot_len = 1;
5980aca67bbSMatthew Bobrowski } else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
5990aca67bbSMatthew Bobrowski (event->mask & FAN_ONDIR)) {
6000aca67bbSMatthew Bobrowski /*
6010aca67bbSMatthew Bobrowski * With group flag FAN_REPORT_DIR_FID, a single info
6020aca67bbSMatthew Bobrowski * record has type DFID for directory entry modification
6030aca67bbSMatthew Bobrowski * event and for event on a directory.
6040aca67bbSMatthew Bobrowski */
6050aca67bbSMatthew Bobrowski info_type = FAN_EVENT_INFO_TYPE_DFID;
6060aca67bbSMatthew Bobrowski } else {
6070aca67bbSMatthew Bobrowski /*
6080aca67bbSMatthew Bobrowski * With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID,
6090aca67bbSMatthew Bobrowski * a single info record has type FID for event on a
6100aca67bbSMatthew Bobrowski * non-directory, when there is no directory to report.
6110aca67bbSMatthew Bobrowski * For example, on FAN_DELETE_SELF event.
6120aca67bbSMatthew Bobrowski */
6130aca67bbSMatthew Bobrowski info_type = FAN_EVENT_INFO_TYPE_FID;
6140aca67bbSMatthew Bobrowski }
6150aca67bbSMatthew Bobrowski
6160aca67bbSMatthew Bobrowski ret = copy_fid_info_to_user(fanotify_event_fsid(event),
6170aca67bbSMatthew Bobrowski fanotify_event_object_fh(event),
6180aca67bbSMatthew Bobrowski info_type, dot, dot_len,
6190aca67bbSMatthew Bobrowski buf, count);
6200aca67bbSMatthew Bobrowski if (ret < 0)
6210aca67bbSMatthew Bobrowski return ret;
6220aca67bbSMatthew Bobrowski
6230aca67bbSMatthew Bobrowski buf += ret;
6240aca67bbSMatthew Bobrowski count -= ret;
6250aca67bbSMatthew Bobrowski total_bytes += ret;
6260aca67bbSMatthew Bobrowski }
6270aca67bbSMatthew Bobrowski
628af579bebSMatthew Bobrowski if (pidfd_mode) {
629af579bebSMatthew Bobrowski ret = copy_pidfd_info_to_user(pidfd, buf, count);
630af579bebSMatthew Bobrowski if (ret < 0)
631af579bebSMatthew Bobrowski return ret;
632af579bebSMatthew Bobrowski
633af579bebSMatthew Bobrowski buf += ret;
634af579bebSMatthew Bobrowski count -= ret;
635af579bebSMatthew Bobrowski total_bytes += ret;
636af579bebSMatthew Bobrowski }
637af579bebSMatthew Bobrowski
638130a3c74SGabriel Krisman Bertazi if (fanotify_is_error_event(event->mask)) {
639130a3c74SGabriel Krisman Bertazi ret = copy_error_info_to_user(event, buf, count);
640130a3c74SGabriel Krisman Bertazi if (ret < 0)
641130a3c74SGabriel Krisman Bertazi return ret;
642130a3c74SGabriel Krisman Bertazi buf += ret;
643130a3c74SGabriel Krisman Bertazi count -= ret;
644130a3c74SGabriel Krisman Bertazi total_bytes += ret;
645130a3c74SGabriel Krisman Bertazi }
646130a3c74SGabriel Krisman Bertazi
6470aca67bbSMatthew Bobrowski return total_bytes;
6480aca67bbSMatthew Bobrowski }
6490aca67bbSMatthew Bobrowski
copy_event_to_user(struct fsnotify_group * group,struct fanotify_event * event,char __user * buf,size_t count)650a1014f10SEric Paris static ssize_t copy_event_to_user(struct fsnotify_group *group,
6517088f357SJan Kara struct fanotify_event *event,
6525b03a472SKees Cook char __user *buf, size_t count)
653a1014f10SEric Paris {
654bb2f7b45SAmir Goldstein struct fanotify_event_metadata metadata;
655d5bf8889SAl Viro const struct path *path = fanotify_event_path(event);
656f454fa61SAmir Goldstein struct fanotify_info *info = fanotify_event_info(event);
6570aca67bbSMatthew Bobrowski unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES);
658af579bebSMatthew Bobrowski unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
659eee3a0e9SChristian Brauner struct file *f = NULL, *pidfd_file = NULL;
660*ffe1766aSAmir Goldstein int ret, pidfd = -ESRCH, fd = -EBADF;
661a1014f10SEric Paris
6627088f357SJan Kara pr_debug("%s: group=%p event=%p\n", __func__, group, event);
663a1014f10SEric Paris
664b9928e80SGabriel Krisman Bertazi metadata.event_len = fanotify_event_len(info_mode, event);
665bb2f7b45SAmir Goldstein metadata.metadata_len = FAN_EVENT_METADATA_LEN;
666bb2f7b45SAmir Goldstein metadata.vers = FANOTIFY_METADATA_VERSION;
667bb2f7b45SAmir Goldstein metadata.reserved = 0;
668bb2f7b45SAmir Goldstein metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
669bb2f7b45SAmir Goldstein metadata.pid = pid_vnr(event->pid);
6707cea2a3cSAmir Goldstein /*
6717cea2a3cSAmir Goldstein * For an unprivileged listener, event->pid can be used to identify the
6727cea2a3cSAmir Goldstein * events generated by the listener process itself, without disclosing
6737cea2a3cSAmir Goldstein * the pids of other processes.
6747cea2a3cSAmir Goldstein */
675a8b98c80SAmir Goldstein if (FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
6767cea2a3cSAmir Goldstein task_tgid(current) != event->pid)
6777cea2a3cSAmir Goldstein metadata.pid = 0;
678a1014f10SEric Paris
679a8b98c80SAmir Goldstein /*
680a8b98c80SAmir Goldstein * For now, fid mode is required for an unprivileged listener and
681a8b98c80SAmir Goldstein * fid mode does not report fd in events. Keep this check anyway
682a8b98c80SAmir Goldstein * for safety in case fid mode requirement is relaxed in the future
683a8b98c80SAmir Goldstein * to allow unprivileged listener to get events with no fd and no fid.
684a8b98c80SAmir Goldstein */
685a8b98c80SAmir Goldstein if (!FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV) &&
686a8b98c80SAmir Goldstein path && path->mnt && path->dentry) {
687a741c2feSJan Kara fd = create_fd(group, path, &f);
688*ffe1766aSAmir Goldstein /*
689*ffe1766aSAmir Goldstein * Opening an fd from dentry can fail for several reasons.
690*ffe1766aSAmir Goldstein * For example, when tasks are gone and we try to open their
691*ffe1766aSAmir Goldstein * /proc files or we try to open a WRONLY file like in sysfs
692*ffe1766aSAmir Goldstein * or when trying to open a file that was deleted on the
693*ffe1766aSAmir Goldstein * remote network server.
694*ffe1766aSAmir Goldstein *
695*ffe1766aSAmir Goldstein * For a group with FAN_REPORT_FD_ERROR, we will send the
696*ffe1766aSAmir Goldstein * event with the error instead of the open fd, otherwise
697*ffe1766aSAmir Goldstein * Userspace may not get the error at all.
698*ffe1766aSAmir Goldstein * In any case, userspace will not know which file failed to
699*ffe1766aSAmir Goldstein * open, so add a debug print for further investigation.
700*ffe1766aSAmir Goldstein */
701*ffe1766aSAmir Goldstein if (fd < 0) {
702*ffe1766aSAmir Goldstein pr_debug("fanotify: create_fd(%pd2) failed err=%d\n",
703*ffe1766aSAmir Goldstein path->dentry, fd);
704*ffe1766aSAmir Goldstein if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR)) {
705*ffe1766aSAmir Goldstein /*
706*ffe1766aSAmir Goldstein * Historically, we've handled EOPENSTALE in a
707*ffe1766aSAmir Goldstein * special way and silently dropped such
708*ffe1766aSAmir Goldstein * events. Now we have to keep it to maintain
709*ffe1766aSAmir Goldstein * backward compatibility...
710*ffe1766aSAmir Goldstein */
711*ffe1766aSAmir Goldstein if (fd == -EOPENSTALE)
712*ffe1766aSAmir Goldstein fd = 0;
713bb2f7b45SAmir Goldstein return fd;
714a741c2feSJan Kara }
715*ffe1766aSAmir Goldstein }
716*ffe1766aSAmir Goldstein }
717*ffe1766aSAmir Goldstein if (FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR))
718bb2f7b45SAmir Goldstein metadata.fd = fd;
719*ffe1766aSAmir Goldstein else
720*ffe1766aSAmir Goldstein metadata.fd = fd >= 0 ? fd : FAN_NOFD;
721bb2f7b45SAmir Goldstein
722af579bebSMatthew Bobrowski if (pidfd_mode) {
723af579bebSMatthew Bobrowski /*
724af579bebSMatthew Bobrowski * Complain if the FAN_REPORT_PIDFD and FAN_REPORT_TID mutual
725af579bebSMatthew Bobrowski * exclusion is ever lifted. At the time of incoporating pidfd
726af579bebSMatthew Bobrowski * support within fanotify, the pidfd API only supported the
727af579bebSMatthew Bobrowski * creation of pidfds for thread-group leaders.
728af579bebSMatthew Bobrowski */
729af579bebSMatthew Bobrowski WARN_ON_ONCE(FAN_GROUP_FLAG(group, FAN_REPORT_TID));
730af579bebSMatthew Bobrowski
731af579bebSMatthew Bobrowski /*
732af579bebSMatthew Bobrowski * The PIDTYPE_TGID check for an event->pid is performed
733af579bebSMatthew Bobrowski * preemptively in an attempt to catch out cases where the event
734af579bebSMatthew Bobrowski * listener reads events after the event generating process has
735*ffe1766aSAmir Goldstein * already terminated. Depending on flag FAN_REPORT_FD_ERROR,
736*ffe1766aSAmir Goldstein * report either -ESRCH or FAN_NOPIDFD to the event listener in
737*ffe1766aSAmir Goldstein * those cases with all other pidfd creation errors reported as
738*ffe1766aSAmir Goldstein * the error code itself or as FAN_EPIDFD.
739af579bebSMatthew Bobrowski */
740*ffe1766aSAmir Goldstein if (metadata.pid && pid_has_task(event->pid, PIDTYPE_TGID))
741eee3a0e9SChristian Brauner pidfd = pidfd_prepare(event->pid, 0, &pidfd_file);
742*ffe1766aSAmir Goldstein
743*ffe1766aSAmir Goldstein if (!FAN_GROUP_FLAG(group, FAN_REPORT_FD_ERROR) && pidfd < 0)
744*ffe1766aSAmir Goldstein pidfd = pidfd == -ESRCH ? FAN_NOPIDFD : FAN_EPIDFD;
745af579bebSMatthew Bobrowski }
746af579bebSMatthew Bobrowski
747352e3b24SAl Viro ret = -EFAULT;
7485b03a472SKees Cook /*
7495b03a472SKees Cook * Sanity check copy size in case get_one_event() and
750c5e443cbSFabian Frederick * event_len sizes ever get out of sync.
7515b03a472SKees Cook */
752bb2f7b45SAmir Goldstein if (WARN_ON_ONCE(metadata.event_len > count))
753352e3b24SAl Viro goto out_close_fd;
754352e3b24SAl Viro
7555e469c83SAmir Goldstein if (copy_to_user(buf, &metadata, FAN_EVENT_METADATA_LEN))
756bb2f7b45SAmir Goldstein goto out_close_fd;
757bb2f7b45SAmir Goldstein
75844d705b0SAmir Goldstein buf += FAN_EVENT_METADATA_LEN;
75944d705b0SAmir Goldstein count -= FAN_EVENT_METADATA_LEN;
76044d705b0SAmir Goldstein
7610aca67bbSMatthew Bobrowski if (info_mode) {
762af579bebSMatthew Bobrowski ret = copy_info_records_to_user(event, info, info_mode, pidfd,
763d3424c9bSMatthew Bobrowski buf, count);
7645e469c83SAmir Goldstein if (ret < 0)
765f644bc44SMatthew Bobrowski goto out_close_fd;
7665e469c83SAmir Goldstein }
7675e469c83SAmir Goldstein
768ee125951SDan Carpenter if (f)
769ee125951SDan Carpenter fd_install(fd, f);
770ee125951SDan Carpenter
771eee3a0e9SChristian Brauner if (pidfd_file)
772eee3a0e9SChristian Brauner fd_install(pidfd, pidfd_file);
773eee3a0e9SChristian Brauner
774*ffe1766aSAmir Goldstein if (fanotify_is_perm_event(event->mask))
775*ffe1766aSAmir Goldstein FANOTIFY_PERM(event)->fd = fd;
776*ffe1766aSAmir Goldstein
777bb2f7b45SAmir Goldstein return metadata.event_len;
778b2d87909SEric Paris
779b2d87909SEric Paris out_close_fd:
780*ffe1766aSAmir Goldstein if (f) {
781352e3b24SAl Viro put_unused_fd(fd);
782352e3b24SAl Viro fput(f);
783352e3b24SAl Viro }
784af579bebSMatthew Bobrowski
785*ffe1766aSAmir Goldstein if (pidfd_file) {
786eee3a0e9SChristian Brauner put_unused_fd(pidfd);
787eee3a0e9SChristian Brauner fput(pidfd_file);
788eee3a0e9SChristian Brauner }
789af579bebSMatthew Bobrowski
790b2d87909SEric Paris return ret;
791a1014f10SEric Paris }
792a1014f10SEric Paris
793a1014f10SEric Paris /* intofiy userspace file descriptor functions */
fanotify_poll(struct file * file,poll_table * wait)794076ccb76SAl Viro static __poll_t fanotify_poll(struct file *file, poll_table *wait)
795a1014f10SEric Paris {
796a1014f10SEric Paris struct fsnotify_group *group = file->private_data;
797076ccb76SAl Viro __poll_t ret = 0;
798a1014f10SEric Paris
799a1014f10SEric Paris poll_wait(file, &group->notification_waitq, wait);
800c21dbe20SJan Kara spin_lock(&group->notification_lock);
801a1014f10SEric Paris if (!fsnotify_notify_queue_is_empty(group))
802a9a08845SLinus Torvalds ret = EPOLLIN | EPOLLRDNORM;
803c21dbe20SJan Kara spin_unlock(&group->notification_lock);
804a1014f10SEric Paris
805a1014f10SEric Paris return ret;
806a1014f10SEric Paris }
807a1014f10SEric Paris
fanotify_read(struct file * file,char __user * buf,size_t count,loff_t * pos)808a1014f10SEric Paris static ssize_t fanotify_read(struct file *file, char __user *buf,
809a1014f10SEric Paris size_t count, loff_t *pos)
810a1014f10SEric Paris {
811a1014f10SEric Paris struct fsnotify_group *group;
8127088f357SJan Kara struct fanotify_event *event;
813a1014f10SEric Paris char __user *start;
814a1014f10SEric Paris int ret;
815536ebe9cSPeter Zijlstra DEFINE_WAIT_FUNC(wait, woken_wake_function);
816a1014f10SEric Paris
817a1014f10SEric Paris start = buf;
818a1014f10SEric Paris group = file->private_data;
819a1014f10SEric Paris
820a1014f10SEric Paris pr_debug("%s: group=%p\n", __func__, group);
821a1014f10SEric Paris
822536ebe9cSPeter Zijlstra add_wait_queue(&group->notification_waitq, &wait);
823a1014f10SEric Paris while (1) {
82447aaabdeSJan Kara /*
82547aaabdeSJan Kara * User can supply arbitrarily large buffer. Avoid softlockups
82647aaabdeSJan Kara * in case there are lots of available events.
82747aaabdeSJan Kara */
82847aaabdeSJan Kara cond_resched();
8297088f357SJan Kara event = get_one_event(group, count);
8307088f357SJan Kara if (IS_ERR(event)) {
8317088f357SJan Kara ret = PTR_ERR(event);
832a1014f10SEric Paris break;
833d8aaab4fSJan Kara }
834d8aaab4fSJan Kara
8357088f357SJan Kara if (!event) {
836d8aaab4fSJan Kara ret = -EAGAIN;
837d8aaab4fSJan Kara if (file->f_flags & O_NONBLOCK)
838d8aaab4fSJan Kara break;
839d8aaab4fSJan Kara
840d8aaab4fSJan Kara ret = -ERESTARTSYS;
841d8aaab4fSJan Kara if (signal_pending(current))
842d8aaab4fSJan Kara break;
843d8aaab4fSJan Kara
844d8aaab4fSJan Kara if (start != buf)
845d8aaab4fSJan Kara break;
846536ebe9cSPeter Zijlstra
847536ebe9cSPeter Zijlstra wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
848d8aaab4fSJan Kara continue;
849d8aaab4fSJan Kara }
850d8aaab4fSJan Kara
8517088f357SJan Kara ret = copy_event_to_user(group, event, buf, count);
8524ff33aafSAmir Goldstein
85385816794SJan Kara /*
854d8aaab4fSJan Kara * Permission events get queued to wait for response. Other
855d8aaab4fSJan Kara * events can be destroyed now.
85685816794SJan Kara */
8577088f357SJan Kara if (!fanotify_is_perm_event(event->mask)) {
8587088f357SJan Kara fsnotify_destroy_event(group, &event->fse);
859d507816bSJan Kara } else {
860*ffe1766aSAmir Goldstein if (ret <= 0 || FANOTIFY_PERM(event)->fd < 0) {
86140873284SJan Kara spin_lock(&group->notification_lock);
86240873284SJan Kara finish_permission_event(group,
86370529a19SRichard Guy Briggs FANOTIFY_PERM(event), FAN_DENY, NULL);
864d507816bSJan Kara wake_up(&group->fanotify_data.access_waitq);
8654ff33aafSAmir Goldstein } else {
866073f6552SJan Kara spin_lock(&group->notification_lock);
8677088f357SJan Kara list_add_tail(&event->fse.list,
868d507816bSJan Kara &group->fanotify_data.access_list);
869073f6552SJan Kara spin_unlock(&group->notification_lock);
8704ff33aafSAmir Goldstein }
871d507816bSJan Kara }
8724ff33aafSAmir Goldstein if (ret < 0)
8734ff33aafSAmir Goldstein break;
874a1014f10SEric Paris buf += ret;
875a1014f10SEric Paris count -= ret;
876a1014f10SEric Paris }
877536ebe9cSPeter Zijlstra remove_wait_queue(&group->notification_waitq, &wait);
878a1014f10SEric Paris
879a1014f10SEric Paris if (start != buf && ret != -EFAULT)
880a1014f10SEric Paris ret = buf - start;
881a1014f10SEric Paris return ret;
882a1014f10SEric Paris }
883a1014f10SEric Paris
fanotify_write(struct file * file,const char __user * buf,size_t count,loff_t * pos)884b2d87909SEric Paris static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
885b2d87909SEric Paris {
88670529a19SRichard Guy Briggs struct fanotify_response response;
887b2d87909SEric Paris struct fsnotify_group *group;
888b2d87909SEric Paris int ret;
88970529a19SRichard Guy Briggs const char __user *info_buf = buf + sizeof(struct fanotify_response);
89070529a19SRichard Guy Briggs size_t info_len;
891b2d87909SEric Paris
8926685df31SMiklos Szeredi if (!IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS))
8936685df31SMiklos Szeredi return -EINVAL;
8946685df31SMiklos Szeredi
895b2d87909SEric Paris group = file->private_data;
896b2d87909SEric Paris
89770529a19SRichard Guy Briggs pr_debug("%s: group=%p count=%zu\n", __func__, group, count);
89870529a19SRichard Guy Briggs
8995e23663bSFabian Frederick if (count < sizeof(response))
9005e23663bSFabian Frederick return -EINVAL;
9015e23663bSFabian Frederick
90270529a19SRichard Guy Briggs if (copy_from_user(&response, buf, sizeof(response)))
903b2d87909SEric Paris return -EFAULT;
904b2d87909SEric Paris
90570529a19SRichard Guy Briggs info_len = count - sizeof(response);
90670529a19SRichard Guy Briggs
90770529a19SRichard Guy Briggs ret = process_access_response(group, &response, info_buf, info_len);
908b2d87909SEric Paris if (ret < 0)
909b2d87909SEric Paris count = ret;
91070529a19SRichard Guy Briggs else
91170529a19SRichard Guy Briggs count = sizeof(response) + ret;
912b2d87909SEric Paris
913b2d87909SEric Paris return count;
914b2d87909SEric Paris }
915b2d87909SEric Paris
fanotify_release(struct inode * ignored,struct file * file)91652c923ddSEric Paris static int fanotify_release(struct inode *ignored, struct file *file)
91752c923ddSEric Paris {
91852c923ddSEric Paris struct fsnotify_group *group = file->private_data;
9196f73171eSAmir Goldstein struct fsnotify_event *fsn_event;
92019ba54f4SAndrew Morton
9215838d444SJan Kara /*
92296d41019SJan Kara * Stop new events from arriving in the notification queue. since
92396d41019SJan Kara * userspace cannot use fanotify fd anymore, no event can enter or
92496d41019SJan Kara * leave access_list by now either.
92596d41019SJan Kara */
92696d41019SJan Kara fsnotify_group_stop_queueing(group);
92796d41019SJan Kara
92896d41019SJan Kara /*
92996d41019SJan Kara * Process all permission events on access_list and notification queue
93096d41019SJan Kara * and simulate reply from userspace.
9315838d444SJan Kara */
932073f6552SJan Kara spin_lock(&group->notification_lock);
933ca6f8699SJan Kara while (!list_empty(&group->fanotify_data.access_list)) {
9347088f357SJan Kara struct fanotify_perm_event *event;
9357088f357SJan Kara
936ca6f8699SJan Kara event = list_first_entry(&group->fanotify_data.access_list,
937ca6f8699SJan Kara struct fanotify_perm_event, fae.fse.list);
938f083441bSJan Kara list_del_init(&event->fae.fse.list);
93970529a19SRichard Guy Briggs finish_permission_event(group, event, FAN_ALLOW, NULL);
94040873284SJan Kara spin_lock(&group->notification_lock);
9412eebf582SEric Paris }
9422eebf582SEric Paris
9435838d444SJan Kara /*
94496d41019SJan Kara * Destroy all non-permission events. For permission events just
94596d41019SJan Kara * dequeue them and set the response. They will be freed once the
94696d41019SJan Kara * response is consumed and fanotify_get_response() returns.
9475838d444SJan Kara */
9486f73171eSAmir Goldstein while ((fsn_event = fsnotify_remove_first_event(group))) {
9496f73171eSAmir Goldstein struct fanotify_event *event = FANOTIFY_E(fsn_event);
9507088f357SJan Kara
9517088f357SJan Kara if (!(event->mask & FANOTIFY_PERM_EVENTS)) {
952c21dbe20SJan Kara spin_unlock(&group->notification_lock);
9536f73171eSAmir Goldstein fsnotify_destroy_event(group, fsn_event);
9546685df31SMiklos Szeredi } else {
9557088f357SJan Kara finish_permission_event(group, FANOTIFY_PERM(event),
95670529a19SRichard Guy Briggs FAN_ALLOW, NULL);
95796d41019SJan Kara }
95840873284SJan Kara spin_lock(&group->notification_lock);
9596685df31SMiklos Szeredi }
960c21dbe20SJan Kara spin_unlock(&group->notification_lock);
96196d41019SJan Kara
96296d41019SJan Kara /* Response for all permission events it set, wakeup waiters */
9632eebf582SEric Paris wake_up(&group->fanotify_data.access_waitq);
9640a6b6bd5SEric Paris
96552c923ddSEric Paris /* matches the fanotify_init->fsnotify_alloc_group */
966d8153d4dSLino Sanfilippo fsnotify_destroy_group(group);
96752c923ddSEric Paris
96852c923ddSEric Paris return 0;
96952c923ddSEric Paris }
97052c923ddSEric Paris
fanotify_ioctl(struct file * file,unsigned int cmd,unsigned long arg)971a1014f10SEric Paris static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
972a1014f10SEric Paris {
973a1014f10SEric Paris struct fsnotify_group *group;
9747053aee2SJan Kara struct fsnotify_event *fsn_event;
975a1014f10SEric Paris void __user *p;
976a1014f10SEric Paris int ret = -ENOTTY;
977a1014f10SEric Paris size_t send_len = 0;
978a1014f10SEric Paris
979a1014f10SEric Paris group = file->private_data;
980a1014f10SEric Paris
981a1014f10SEric Paris p = (void __user *) arg;
982a1014f10SEric Paris
983a1014f10SEric Paris switch (cmd) {
984a1014f10SEric Paris case FIONREAD:
985c21dbe20SJan Kara spin_lock(&group->notification_lock);
9867053aee2SJan Kara list_for_each_entry(fsn_event, &group->notification_list, list)
987a1014f10SEric Paris send_len += FAN_EVENT_METADATA_LEN;
988c21dbe20SJan Kara spin_unlock(&group->notification_lock);
989a1014f10SEric Paris ret = put_user(send_len, (int __user *) p);
990a1014f10SEric Paris break;
991a1014f10SEric Paris }
992a1014f10SEric Paris
993a1014f10SEric Paris return ret;
994a1014f10SEric Paris }
995a1014f10SEric Paris
99652c923ddSEric Paris static const struct file_operations fanotify_fops = {
997be77196bSCyrill Gorcunov .show_fdinfo = fanotify_show_fdinfo,
998a1014f10SEric Paris .poll = fanotify_poll,
999a1014f10SEric Paris .read = fanotify_read,
1000b2d87909SEric Paris .write = fanotify_write,
100152c923ddSEric Paris .fasync = NULL,
100252c923ddSEric Paris .release = fanotify_release,
1003a1014f10SEric Paris .unlocked_ioctl = fanotify_ioctl,
10041832f2d8SArnd Bergmann .compat_ioctl = compat_ptr_ioctl,
10056038f373SArnd Bergmann .llseek = noop_llseek,
100652c923ddSEric Paris };
100752c923ddSEric Paris
fanotify_find_path(int dfd,const char __user * filename,struct path * path,unsigned int flags,__u64 mask,unsigned int obj_type)10082a3edf86SEric Paris static int fanotify_find_path(int dfd, const char __user *filename,
1009ac5656d8SAaron Goidel struct path *path, unsigned int flags, __u64 mask,
1010ac5656d8SAaron Goidel unsigned int obj_type)
10112a3edf86SEric Paris {
10122a3edf86SEric Paris int ret;
10132a3edf86SEric Paris
10142a3edf86SEric Paris pr_debug("%s: dfd=%d filename=%p flags=%x\n", __func__,
10152a3edf86SEric Paris dfd, filename, flags);
10162a3edf86SEric Paris
10172a3edf86SEric Paris if (filename == NULL) {
10182903ff01SAl Viro struct fd f = fdget(dfd);
10192a3edf86SEric Paris
10202a3edf86SEric Paris ret = -EBADF;
10212903ff01SAl Viro if (!f.file)
10222a3edf86SEric Paris goto out;
10232a3edf86SEric Paris
10242a3edf86SEric Paris ret = -ENOTDIR;
10252a3edf86SEric Paris if ((flags & FAN_MARK_ONLYDIR) &&
1026496ad9aaSAl Viro !(S_ISDIR(file_inode(f.file)->i_mode))) {
10272903ff01SAl Viro fdput(f);
10282a3edf86SEric Paris goto out;
10292a3edf86SEric Paris }
10302a3edf86SEric Paris
10312903ff01SAl Viro *path = f.file->f_path;
10322a3edf86SEric Paris path_get(path);
10332903ff01SAl Viro fdput(f);
10342a3edf86SEric Paris } else {
10352a3edf86SEric Paris unsigned int lookup_flags = 0;
10362a3edf86SEric Paris
10372a3edf86SEric Paris if (!(flags & FAN_MARK_DONT_FOLLOW))
10382a3edf86SEric Paris lookup_flags |= LOOKUP_FOLLOW;
10392a3edf86SEric Paris if (flags & FAN_MARK_ONLYDIR)
10402a3edf86SEric Paris lookup_flags |= LOOKUP_DIRECTORY;
10412a3edf86SEric Paris
10422a3edf86SEric Paris ret = user_path_at(dfd, filename, lookup_flags, path);
10432a3edf86SEric Paris if (ret)
10442a3edf86SEric Paris goto out;
10452a3edf86SEric Paris }
10462a3edf86SEric Paris
10472a3edf86SEric Paris /* you can only watch an inode if you have read permissions on it */
104802f92b38SChristian Brauner ret = path_permission(path, MAY_READ);
1049ac5656d8SAaron Goidel if (ret) {
1050ac5656d8SAaron Goidel path_put(path);
1051ac5656d8SAaron Goidel goto out;
1052ac5656d8SAaron Goidel }
1053ac5656d8SAaron Goidel
1054ac5656d8SAaron Goidel ret = security_path_notify(path, mask, obj_type);
10552a3edf86SEric Paris if (ret)
10562a3edf86SEric Paris path_put(path);
1057ac5656d8SAaron Goidel
10582a3edf86SEric Paris out:
10592a3edf86SEric Paris return ret;
10602a3edf86SEric Paris }
10612a3edf86SEric Paris
fanotify_mark_remove_from_mask(struct fsnotify_mark * fsn_mark,__u32 mask,unsigned int flags,__u32 umask,int * destroy)1062b9e4e3bdSEric Paris static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
10634ed6814aSAmir Goldstein __u32 mask, unsigned int flags,
10644ed6814aSAmir Goldstein __u32 umask, int *destroy)
1065088b09b0SAndreas Gruenbacher {
10664f0b903dSAmir Goldstein __u32 oldmask, newmask;
1067088b09b0SAndreas Gruenbacher
10684ed6814aSAmir Goldstein /* umask bits cannot be removed by user */
10694ed6814aSAmir Goldstein mask &= ~umask;
1070088b09b0SAndreas Gruenbacher spin_lock(&fsn_mark->lock);
10714f0b903dSAmir Goldstein oldmask = fsnotify_calc_mask(fsn_mark);
1072e252f2edSAmir Goldstein if (!(flags & FANOTIFY_MARK_IGNORE_BITS)) {
1073a72fd224SAmir Goldstein fsn_mark->mask &= ~mask;
1074b9e4e3bdSEric Paris } else {
107531a371e4SAmir Goldstein fsn_mark->ignore_mask &= ~mask;
1076b9e4e3bdSEric Paris }
10774f0b903dSAmir Goldstein newmask = fsnotify_calc_mask(fsn_mark);
10784ed6814aSAmir Goldstein /*
10794ed6814aSAmir Goldstein * We need to keep the mark around even if remaining mask cannot
10804ed6814aSAmir Goldstein * result in any events (e.g. mask == FAN_ONDIR) to support incremenal
10814ed6814aSAmir Goldstein * changes to the mask.
10824ed6814aSAmir Goldstein * Destroy mark when only umask bits remain.
10834ed6814aSAmir Goldstein */
108431a371e4SAmir Goldstein *destroy = !((fsn_mark->mask | fsn_mark->ignore_mask) & ~umask);
1085088b09b0SAndreas Gruenbacher spin_unlock(&fsn_mark->lock);
1086088b09b0SAndreas Gruenbacher
10874f0b903dSAmir Goldstein return oldmask & ~newmask;
1088088b09b0SAndreas Gruenbacher }
1089088b09b0SAndreas Gruenbacher
fanotify_remove_mark(struct fsnotify_group * group,fsnotify_connp_t * connp,__u32 mask,unsigned int flags,__u32 umask)1090eaa2c6b0SAmir Goldstein static int fanotify_remove_mark(struct fsnotify_group *group,
1091eaa2c6b0SAmir Goldstein fsnotify_connp_t *connp, __u32 mask,
10924ed6814aSAmir Goldstein unsigned int flags, __u32 umask)
109388826276SEric Paris {
109488826276SEric Paris struct fsnotify_mark *fsn_mark = NULL;
1095088b09b0SAndreas Gruenbacher __u32 removed;
10966dfbd149SLino Sanfilippo int destroy_mark;
109788826276SEric Paris
1098e79719a2SAmir Goldstein fsnotify_group_lock(group);
1099eaa2c6b0SAmir Goldstein fsn_mark = fsnotify_find_mark(connp, group);
11007b18527cSLino Sanfilippo if (!fsn_mark) {
1101e79719a2SAmir Goldstein fsnotify_group_unlock(group);
110288826276SEric Paris return -ENOENT;
11037b18527cSLino Sanfilippo }
110488826276SEric Paris
11056dfbd149SLino Sanfilippo removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
11064ed6814aSAmir Goldstein umask, &destroy_mark);
11073ac70bfcSAmir Goldstein if (removed & fsnotify_conn_mask(fsn_mark->connector))
11083ac70bfcSAmir Goldstein fsnotify_recalc_mask(fsn_mark->connector);
11096dfbd149SLino Sanfilippo if (destroy_mark)
11104712e722SJan Kara fsnotify_detach_mark(fsn_mark);
1111e79719a2SAmir Goldstein fsnotify_group_unlock(group);
11124712e722SJan Kara if (destroy_mark)
11134712e722SJan Kara fsnotify_free_mark(fsn_mark);
11147b18527cSLino Sanfilippo
1115b1362edfSJan Kara /* matches the fsnotify_find_mark() */
11162a3edf86SEric Paris fsnotify_put_mark(fsn_mark);
11172a3edf86SEric Paris return 0;
11182a3edf86SEric Paris }
11192a3edf86SEric Paris
fanotify_remove_vfsmount_mark(struct fsnotify_group * group,struct vfsmount * mnt,__u32 mask,unsigned int flags,__u32 umask)1120eaa2c6b0SAmir Goldstein static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
1121eaa2c6b0SAmir Goldstein struct vfsmount *mnt, __u32 mask,
11224ed6814aSAmir Goldstein unsigned int flags, __u32 umask)
1123eaa2c6b0SAmir Goldstein {
1124eaa2c6b0SAmir Goldstein return fanotify_remove_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
11254ed6814aSAmir Goldstein mask, flags, umask);
1126eaa2c6b0SAmir Goldstein }
1127eaa2c6b0SAmir Goldstein
fanotify_remove_sb_mark(struct fsnotify_group * group,struct super_block * sb,__u32 mask,unsigned int flags,__u32 umask)1128d54f4fbaSAmir Goldstein static int fanotify_remove_sb_mark(struct fsnotify_group *group,
1129d54f4fbaSAmir Goldstein struct super_block *sb, __u32 mask,
11304ed6814aSAmir Goldstein unsigned int flags, __u32 umask)
1131d54f4fbaSAmir Goldstein {
11324ed6814aSAmir Goldstein return fanotify_remove_mark(group, &sb->s_fsnotify_marks, mask,
11334ed6814aSAmir Goldstein flags, umask);
1134d54f4fbaSAmir Goldstein }
1135d54f4fbaSAmir Goldstein
fanotify_remove_inode_mark(struct fsnotify_group * group,struct inode * inode,__u32 mask,unsigned int flags,__u32 umask)1136eaa2c6b0SAmir Goldstein static int fanotify_remove_inode_mark(struct fsnotify_group *group,
1137eaa2c6b0SAmir Goldstein struct inode *inode, __u32 mask,
11384ed6814aSAmir Goldstein unsigned int flags, __u32 umask)
1139eaa2c6b0SAmir Goldstein {
1140eaa2c6b0SAmir Goldstein return fanotify_remove_mark(group, &inode->i_fsnotify_marks, mask,
11414ed6814aSAmir Goldstein flags, umask);
1142eaa2c6b0SAmir Goldstein }
1143eaa2c6b0SAmir Goldstein
fanotify_mark_update_flags(struct fsnotify_mark * fsn_mark,unsigned int fan_flags)11448998d110SAmir Goldstein static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
11458998d110SAmir Goldstein unsigned int fan_flags)
114604e317baSAmir Goldstein {
11477d5e005dSAmir Goldstein bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
1148e252f2edSAmir Goldstein unsigned int ignore = fan_flags & FANOTIFY_MARK_IGNORE_BITS;
11498998d110SAmir Goldstein bool recalc = false;
115004e317baSAmir Goldstein
115104e317baSAmir Goldstein /*
1152e252f2edSAmir Goldstein * When using FAN_MARK_IGNORE for the first time, mark starts using
1153e252f2edSAmir Goldstein * independent event flags in ignore mask. After that, trying to
1154e252f2edSAmir Goldstein * update the ignore mask with the old FAN_MARK_IGNORED_MASK API
1155e252f2edSAmir Goldstein * will result in EEXIST error.
1156e252f2edSAmir Goldstein */
1157e252f2edSAmir Goldstein if (ignore == FAN_MARK_IGNORE)
1158e252f2edSAmir Goldstein fsn_mark->flags |= FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS;
1159e252f2edSAmir Goldstein
1160e252f2edSAmir Goldstein /*
116104e317baSAmir Goldstein * Setting FAN_MARK_IGNORED_SURV_MODIFY for the first time may lead to
116204e317baSAmir Goldstein * the removal of the FS_MODIFY bit in calculated mask if it was set
116331a371e4SAmir Goldstein * because of an ignore mask that is now going to survive FS_MODIFY.
116404e317baSAmir Goldstein */
1165e252f2edSAmir Goldstein if (ignore && (fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
116604e317baSAmir Goldstein !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) {
116704e317baSAmir Goldstein fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
116804e317baSAmir Goldstein if (!(fsn_mark->mask & FS_MODIFY))
11698998d110SAmir Goldstein recalc = true;
117004e317baSAmir Goldstein }
117104e317baSAmir Goldstein
11727d5e005dSAmir Goldstein if (fsn_mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE ||
11737d5e005dSAmir Goldstein want_iref == !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
11748998d110SAmir Goldstein return recalc;
11757d5e005dSAmir Goldstein
11767d5e005dSAmir Goldstein /*
11777d5e005dSAmir Goldstein * NO_IREF may be removed from a mark, but not added.
11787d5e005dSAmir Goldstein * When removed, fsnotify_recalc_mask() will take the inode ref.
11797d5e005dSAmir Goldstein */
11807d5e005dSAmir Goldstein WARN_ON_ONCE(!want_iref);
11817d5e005dSAmir Goldstein fsn_mark->flags &= ~FSNOTIFY_MARK_FLAG_NO_IREF;
11827d5e005dSAmir Goldstein
11837d5e005dSAmir Goldstein return true;
11848998d110SAmir Goldstein }
11858998d110SAmir Goldstein
fanotify_mark_add_to_mask(struct fsnotify_mark * fsn_mark,__u32 mask,unsigned int fan_flags)11868998d110SAmir Goldstein static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
11878998d110SAmir Goldstein __u32 mask, unsigned int fan_flags)
1188912ee394SAndreas Gruenbacher {
11898998d110SAmir Goldstein bool recalc;
1190912ee394SAndreas Gruenbacher
1191912ee394SAndreas Gruenbacher spin_lock(&fsn_mark->lock);
1192e252f2edSAmir Goldstein if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS))
1193a72fd224SAmir Goldstein fsn_mark->mask |= mask;
11948998d110SAmir Goldstein else
119531a371e4SAmir Goldstein fsn_mark->ignore_mask |= mask;
11968998d110SAmir Goldstein
11978998d110SAmir Goldstein recalc = fsnotify_calc_mask(fsn_mark) &
11988998d110SAmir Goldstein ~fsnotify_conn_mask(fsn_mark->connector);
11998998d110SAmir Goldstein
12008998d110SAmir Goldstein recalc |= fanotify_mark_update_flags(fsn_mark, fan_flags);
1201912ee394SAndreas Gruenbacher spin_unlock(&fsn_mark->lock);
1202912ee394SAndreas Gruenbacher
12038998d110SAmir Goldstein return recalc;
1204912ee394SAndreas Gruenbacher }
1205912ee394SAndreas Gruenbacher
fanotify_add_new_mark(struct fsnotify_group * group,fsnotify_connp_t * connp,unsigned int obj_type,unsigned int fan_flags,__kernel_fsid_t * fsid)12065e9c070cSLino Sanfilippo static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
1207b812a9f5SAmir Goldstein fsnotify_connp_t *connp,
1208ad69cd99SAmir Goldstein unsigned int obj_type,
12097d5e005dSAmir Goldstein unsigned int fan_flags,
121077115225SAmir Goldstein __kernel_fsid_t *fsid)
12115e9c070cSLino Sanfilippo {
12125b8fea65SAmir Goldstein struct ucounts *ucounts = group->fanotify_data.ucounts;
12135e9c070cSLino Sanfilippo struct fsnotify_mark *mark;
12145e9c070cSLino Sanfilippo int ret;
12155e9c070cSLino Sanfilippo
12165b8fea65SAmir Goldstein /*
12175b8fea65SAmir Goldstein * Enforce per user marks limits per user in all containing user ns.
12185b8fea65SAmir Goldstein * A group with FAN_UNLIMITED_MARKS does not contribute to mark count
12195b8fea65SAmir Goldstein * in the limited groups account.
12205b8fea65SAmir Goldstein */
12215b8fea65SAmir Goldstein if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS) &&
12225b8fea65SAmir Goldstein !inc_ucount(ucounts->ns, ucounts->uid, UCOUNT_FANOTIFY_MARKS))
12235e9c070cSLino Sanfilippo return ERR_PTR(-ENOSPC);
12245e9c070cSLino Sanfilippo
12255e9c070cSLino Sanfilippo mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
12265b8fea65SAmir Goldstein if (!mark) {
12275b8fea65SAmir Goldstein ret = -ENOMEM;
12285b8fea65SAmir Goldstein goto out_dec_ucounts;
12295b8fea65SAmir Goldstein }
12305e9c070cSLino Sanfilippo
1231054c636eSJan Kara fsnotify_init_mark(mark, group);
12327d5e005dSAmir Goldstein if (fan_flags & FAN_MARK_EVICTABLE)
12337d5e005dSAmir Goldstein mark->flags |= FSNOTIFY_MARK_FLAG_NO_IREF;
12347d5e005dSAmir Goldstein
1235ad69cd99SAmir Goldstein ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0, fsid);
12365e9c070cSLino Sanfilippo if (ret) {
12375e9c070cSLino Sanfilippo fsnotify_put_mark(mark);
12385b8fea65SAmir Goldstein goto out_dec_ucounts;
12395e9c070cSLino Sanfilippo }
12405e9c070cSLino Sanfilippo
12415e9c070cSLino Sanfilippo return mark;
12425b8fea65SAmir Goldstein
12435b8fea65SAmir Goldstein out_dec_ucounts:
12445b8fea65SAmir Goldstein if (!FAN_GROUP_FLAG(group, FAN_UNLIMITED_MARKS))
12455b8fea65SAmir Goldstein dec_ucount(ucounts, UCOUNT_FANOTIFY_MARKS);
12465b8fea65SAmir Goldstein return ERR_PTR(ret);
12475e9c070cSLino Sanfilippo }
12485e9c070cSLino Sanfilippo
fanotify_group_init_error_pool(struct fsnotify_group * group)1249734a1a5eSGabriel Krisman Bertazi static int fanotify_group_init_error_pool(struct fsnotify_group *group)
1250734a1a5eSGabriel Krisman Bertazi {
1251734a1a5eSGabriel Krisman Bertazi if (mempool_initialized(&group->fanotify_data.error_events_pool))
1252734a1a5eSGabriel Krisman Bertazi return 0;
1253734a1a5eSGabriel Krisman Bertazi
1254734a1a5eSGabriel Krisman Bertazi return mempool_init_kmalloc_pool(&group->fanotify_data.error_events_pool,
1255734a1a5eSGabriel Krisman Bertazi FANOTIFY_DEFAULT_FEE_POOL_SIZE,
1256734a1a5eSGabriel Krisman Bertazi sizeof(struct fanotify_error_event));
1257734a1a5eSGabriel Krisman Bertazi }
12585e9c070cSLino Sanfilippo
fanotify_may_update_existing_mark(struct fsnotify_mark * fsn_mark,unsigned int fan_flags)12598afd7215SAmir Goldstein static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
12608afd7215SAmir Goldstein unsigned int fan_flags)
12618afd7215SAmir Goldstein {
12628afd7215SAmir Goldstein /*
12638afd7215SAmir Goldstein * Non evictable mark cannot be downgraded to evictable mark.
12648afd7215SAmir Goldstein */
12658afd7215SAmir Goldstein if (fan_flags & FAN_MARK_EVICTABLE &&
12668afd7215SAmir Goldstein !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
12678afd7215SAmir Goldstein return -EEXIST;
12688afd7215SAmir Goldstein
1269e252f2edSAmir Goldstein /*
1270e252f2edSAmir Goldstein * New ignore mask semantics cannot be downgraded to old semantics.
1271e252f2edSAmir Goldstein */
1272e252f2edSAmir Goldstein if (fan_flags & FAN_MARK_IGNORED_MASK &&
1273e252f2edSAmir Goldstein fsn_mark->flags & FSNOTIFY_MARK_FLAG_HAS_IGNORE_FLAGS)
1274e252f2edSAmir Goldstein return -EEXIST;
1275e252f2edSAmir Goldstein
1276e252f2edSAmir Goldstein /*
1277e252f2edSAmir Goldstein * An ignore mask that survives modify could never be downgraded to not
1278e252f2edSAmir Goldstein * survive modify. With new FAN_MARK_IGNORE semantics we make that rule
1279e252f2edSAmir Goldstein * explicit and return an error when trying to update the ignore mask
1280e252f2edSAmir Goldstein * without the original FAN_MARK_IGNORED_SURV_MODIFY value.
1281e252f2edSAmir Goldstein */
1282e252f2edSAmir Goldstein if (fan_flags & FAN_MARK_IGNORE &&
1283e252f2edSAmir Goldstein !(fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
1284e252f2edSAmir Goldstein fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
1285e252f2edSAmir Goldstein return -EEXIST;
1286e252f2edSAmir Goldstein
12878afd7215SAmir Goldstein return 0;
12888afd7215SAmir Goldstein }
12898afd7215SAmir Goldstein
fanotify_add_mark(struct fsnotify_group * group,fsnotify_connp_t * connp,unsigned int obj_type,__u32 mask,unsigned int fan_flags,__kernel_fsid_t * fsid)1290eaa2c6b0SAmir Goldstein static int fanotify_add_mark(struct fsnotify_group *group,
1291ad69cd99SAmir Goldstein fsnotify_connp_t *connp, unsigned int obj_type,
12928998d110SAmir Goldstein __u32 mask, unsigned int fan_flags,
129377115225SAmir Goldstein __kernel_fsid_t *fsid)
12942a3edf86SEric Paris {
12952a3edf86SEric Paris struct fsnotify_mark *fsn_mark;
12968998d110SAmir Goldstein bool recalc;
1297734a1a5eSGabriel Krisman Bertazi int ret = 0;
129888826276SEric Paris
1299e79719a2SAmir Goldstein fsnotify_group_lock(group);
1300b812a9f5SAmir Goldstein fsn_mark = fsnotify_find_mark(connp, group);
130188826276SEric Paris if (!fsn_mark) {
13027d5e005dSAmir Goldstein fsn_mark = fanotify_add_new_mark(group, connp, obj_type,
13037d5e005dSAmir Goldstein fan_flags, fsid);
13045e9c070cSLino Sanfilippo if (IS_ERR(fsn_mark)) {
1305e79719a2SAmir Goldstein fsnotify_group_unlock(group);
13065e9c070cSLino Sanfilippo return PTR_ERR(fsn_mark);
130788826276SEric Paris }
13087b18527cSLino Sanfilippo }
1309734a1a5eSGabriel Krisman Bertazi
1310734a1a5eSGabriel Krisman Bertazi /*
13118afd7215SAmir Goldstein * Check if requested mark flags conflict with an existing mark flags.
13127d5e005dSAmir Goldstein */
13138afd7215SAmir Goldstein ret = fanotify_may_update_existing_mark(fsn_mark, fan_flags);
13148afd7215SAmir Goldstein if (ret)
13157d5e005dSAmir Goldstein goto out;
13167d5e005dSAmir Goldstein
13177d5e005dSAmir Goldstein /*
1318734a1a5eSGabriel Krisman Bertazi * Error events are pre-allocated per group, only if strictly
1319734a1a5eSGabriel Krisman Bertazi * needed (i.e. FAN_FS_ERROR was requested).
1320734a1a5eSGabriel Krisman Bertazi */
1321e252f2edSAmir Goldstein if (!(fan_flags & FANOTIFY_MARK_IGNORE_BITS) &&
1322e252f2edSAmir Goldstein (mask & FAN_FS_ERROR)) {
1323734a1a5eSGabriel Krisman Bertazi ret = fanotify_group_init_error_pool(group);
1324734a1a5eSGabriel Krisman Bertazi if (ret)
1325734a1a5eSGabriel Krisman Bertazi goto out;
1326734a1a5eSGabriel Krisman Bertazi }
1327734a1a5eSGabriel Krisman Bertazi
13288998d110SAmir Goldstein recalc = fanotify_mark_add_to_mask(fsn_mark, mask, fan_flags);
13298998d110SAmir Goldstein if (recalc)
13303ac70bfcSAmir Goldstein fsnotify_recalc_mask(fsn_mark->connector);
1331734a1a5eSGabriel Krisman Bertazi
1332734a1a5eSGabriel Krisman Bertazi out:
1333e79719a2SAmir Goldstein fsnotify_group_unlock(group);
13345e9c070cSLino Sanfilippo
1335fa218ab9SLino Sanfilippo fsnotify_put_mark(fsn_mark);
1336734a1a5eSGabriel Krisman Bertazi return ret;
133788826276SEric Paris }
133888826276SEric Paris
fanotify_add_vfsmount_mark(struct fsnotify_group * group,struct vfsmount * mnt,__u32 mask,unsigned int flags,__kernel_fsid_t * fsid)1339eaa2c6b0SAmir Goldstein static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
1340eaa2c6b0SAmir Goldstein struct vfsmount *mnt, __u32 mask,
134177115225SAmir Goldstein unsigned int flags, __kernel_fsid_t *fsid)
1342eaa2c6b0SAmir Goldstein {
1343eaa2c6b0SAmir Goldstein return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
134477115225SAmir Goldstein FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
1345eaa2c6b0SAmir Goldstein }
1346eaa2c6b0SAmir Goldstein
fanotify_add_sb_mark(struct fsnotify_group * group,struct super_block * sb,__u32 mask,unsigned int flags,__kernel_fsid_t * fsid)1347d54f4fbaSAmir Goldstein static int fanotify_add_sb_mark(struct fsnotify_group *group,
1348d54f4fbaSAmir Goldstein struct super_block *sb, __u32 mask,
134977115225SAmir Goldstein unsigned int flags, __kernel_fsid_t *fsid)
1350d54f4fbaSAmir Goldstein {
1351d54f4fbaSAmir Goldstein return fanotify_add_mark(group, &sb->s_fsnotify_marks,
135277115225SAmir Goldstein FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
1353d54f4fbaSAmir Goldstein }
1354d54f4fbaSAmir Goldstein
fanotify_add_inode_mark(struct fsnotify_group * group,struct inode * inode,__u32 mask,unsigned int flags,__kernel_fsid_t * fsid)135552202dfbSAndreas Gruenbacher static int fanotify_add_inode_mark(struct fsnotify_group *group,
1356b9e4e3bdSEric Paris struct inode *inode, __u32 mask,
135777115225SAmir Goldstein unsigned int flags, __kernel_fsid_t *fsid)
135888826276SEric Paris {
135988826276SEric Paris pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
13602a3edf86SEric Paris
13615322a59fSEric Paris /*
13625322a59fSEric Paris * If some other task has this inode open for write we should not add
136331a371e4SAmir Goldstein * an ignore mask, unless that ignore mask is supposed to survive
13645322a59fSEric Paris * modification changes anyway.
13655322a59fSEric Paris */
1366e252f2edSAmir Goldstein if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
13675322a59fSEric Paris !(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
1368ac9498d6SNikolay Borisov inode_is_open_for_write(inode))
13695322a59fSEric Paris return 0;
13705322a59fSEric Paris
1371eaa2c6b0SAmir Goldstein return fanotify_add_mark(group, &inode->i_fsnotify_marks,
137277115225SAmir Goldstein FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
13732a3edf86SEric Paris }
13742a3edf86SEric Paris
fanotify_alloc_overflow_event(void)1375b8a6c3a2SAmir Goldstein static struct fsnotify_event *fanotify_alloc_overflow_event(void)
1376b8a6c3a2SAmir Goldstein {
1377b8a6c3a2SAmir Goldstein struct fanotify_event *oevent;
1378b8a6c3a2SAmir Goldstein
1379b8a6c3a2SAmir Goldstein oevent = kmalloc(sizeof(*oevent), GFP_KERNEL_ACCOUNT);
1380b8a6c3a2SAmir Goldstein if (!oevent)
1381b8a6c3a2SAmir Goldstein return NULL;
1382b8a6c3a2SAmir Goldstein
1383b8a6c3a2SAmir Goldstein fanotify_init_event(oevent, 0, FS_Q_OVERFLOW);
1384b8a6c3a2SAmir Goldstein oevent->type = FANOTIFY_EVENT_TYPE_OVERFLOW;
1385b8a6c3a2SAmir Goldstein
1386b8a6c3a2SAmir Goldstein return &oevent->fse;
1387b8a6c3a2SAmir Goldstein }
1388b8a6c3a2SAmir Goldstein
fanotify_alloc_merge_hash(void)138994e00d28SAmir Goldstein static struct hlist_head *fanotify_alloc_merge_hash(void)
139094e00d28SAmir Goldstein {
139194e00d28SAmir Goldstein struct hlist_head *hash;
139294e00d28SAmir Goldstein
139394e00d28SAmir Goldstein hash = kmalloc(sizeof(struct hlist_head) << FANOTIFY_HTABLE_BITS,
139494e00d28SAmir Goldstein GFP_KERNEL_ACCOUNT);
139594e00d28SAmir Goldstein if (!hash)
139694e00d28SAmir Goldstein return NULL;
139794e00d28SAmir Goldstein
139894e00d28SAmir Goldstein __hash_init(hash, FANOTIFY_HTABLE_SIZE);
139994e00d28SAmir Goldstein
140094e00d28SAmir Goldstein return hash;
140194e00d28SAmir Goldstein }
140294e00d28SAmir Goldstein
140352c923ddSEric Paris /* fanotify syscalls */
SYSCALL_DEFINE2(fanotify_init,unsigned int,flags,unsigned int,event_f_flags)140408ae8938SEric Paris SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
140511637e4bSEric Paris {
140652c923ddSEric Paris struct fsnotify_group *group;
140752c923ddSEric Paris int f_flags, fd;
140883b7a598SAmir Goldstein unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
140983b7a598SAmir Goldstein unsigned int class = flags & FANOTIFY_CLASS_BITS;
1410a8b98c80SAmir Goldstein unsigned int internal_flags = 0;
141152c923ddSEric Paris
141296a71f21SAmir Goldstein pr_debug("%s: flags=%x event_f_flags=%x\n",
141308ae8938SEric Paris __func__, flags, event_f_flags);
141452c923ddSEric Paris
14157cea2a3cSAmir Goldstein if (!capable(CAP_SYS_ADMIN)) {
14167cea2a3cSAmir Goldstein /*
14177cea2a3cSAmir Goldstein * An unprivileged user can setup an fanotify group with
14187cea2a3cSAmir Goldstein * limited functionality - an unprivileged group is limited to
14197cea2a3cSAmir Goldstein * notification events with file handles and it cannot use
14207cea2a3cSAmir Goldstein * unlimited queue/marks.
14217cea2a3cSAmir Goldstein */
14227cea2a3cSAmir Goldstein if ((flags & FANOTIFY_ADMIN_INIT_FLAGS) || !fid_mode)
1423a2f13ad0SAndreas Gruenbacher return -EPERM;
1424a8b98c80SAmir Goldstein
1425a8b98c80SAmir Goldstein /*
1426a8b98c80SAmir Goldstein * Setting the internal flag FANOTIFY_UNPRIV on the group
1427a8b98c80SAmir Goldstein * prevents setting mount/filesystem marks on this group and
1428a8b98c80SAmir Goldstein * prevents reporting pid and open fd in events.
1429a8b98c80SAmir Goldstein */
1430a8b98c80SAmir Goldstein internal_flags |= FANOTIFY_UNPRIV;
14317cea2a3cSAmir Goldstein }
143252c923ddSEric Paris
1433de8cd83eSSteve Grubb #ifdef CONFIG_AUDITSYSCALL
143423c9deebSAmir Goldstein if (flags & ~(FANOTIFY_INIT_FLAGS | FAN_ENABLE_AUDIT))
1435de8cd83eSSteve Grubb #else
143623c9deebSAmir Goldstein if (flags & ~FANOTIFY_INIT_FLAGS)
1437de8cd83eSSteve Grubb #endif
143852c923ddSEric Paris return -EINVAL;
143952c923ddSEric Paris
1440af579bebSMatthew Bobrowski /*
1441af579bebSMatthew Bobrowski * A pidfd can only be returned for a thread-group leader; thus
1442af579bebSMatthew Bobrowski * FAN_REPORT_PIDFD and FAN_REPORT_TID need to remain mutually
1443af579bebSMatthew Bobrowski * exclusive.
1444af579bebSMatthew Bobrowski */
1445af579bebSMatthew Bobrowski if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID))
1446af579bebSMatthew Bobrowski return -EINVAL;
1447af579bebSMatthew Bobrowski
144848149e9dSHeinrich Schuchardt if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS)
144948149e9dSHeinrich Schuchardt return -EINVAL;
145048149e9dSHeinrich Schuchardt
145148149e9dSHeinrich Schuchardt switch (event_f_flags & O_ACCMODE) {
145248149e9dSHeinrich Schuchardt case O_RDONLY:
145348149e9dSHeinrich Schuchardt case O_RDWR:
145448149e9dSHeinrich Schuchardt case O_WRONLY:
145548149e9dSHeinrich Schuchardt break;
145648149e9dSHeinrich Schuchardt default:
145748149e9dSHeinrich Schuchardt return -EINVAL;
145848149e9dSHeinrich Schuchardt }
145948149e9dSHeinrich Schuchardt
146083b7a598SAmir Goldstein if (fid_mode && class != FAN_CLASS_NOTIF)
1461a8b13aa2SAmir Goldstein return -EINVAL;
1462a8b13aa2SAmir Goldstein
1463929943b3SAmir Goldstein /*
1464929943b3SAmir Goldstein * Child name is reported with parent fid so requires dir fid.
1465691d9763SAmir Goldstein * We can report both child fid and dir fid with or without name.
1466929943b3SAmir Goldstein */
1467691d9763SAmir Goldstein if ((fid_mode & FAN_REPORT_NAME) && !(fid_mode & FAN_REPORT_DIR_FID))
146883b7a598SAmir Goldstein return -EINVAL;
146983b7a598SAmir Goldstein
1470d61fd650SAmir Goldstein /*
1471d61fd650SAmir Goldstein * FAN_REPORT_TARGET_FID requires FAN_REPORT_NAME and FAN_REPORT_FID
1472d61fd650SAmir Goldstein * and is used as an indication to report both dir and child fid on all
1473d61fd650SAmir Goldstein * dirent events.
1474d61fd650SAmir Goldstein */
1475d61fd650SAmir Goldstein if ((fid_mode & FAN_REPORT_TARGET_FID) &&
1476d61fd650SAmir Goldstein (!(fid_mode & FAN_REPORT_NAME) || !(fid_mode & FAN_REPORT_FID)))
1477d61fd650SAmir Goldstein return -EINVAL;
1478d61fd650SAmir Goldstein
1479dccd8557SVasily Averin f_flags = O_RDWR | __FMODE_NONOTIFY;
148052c923ddSEric Paris if (flags & FAN_CLOEXEC)
148152c923ddSEric Paris f_flags |= O_CLOEXEC;
148252c923ddSEric Paris if (flags & FAN_NONBLOCK)
148352c923ddSEric Paris f_flags |= O_NONBLOCK;
148452c923ddSEric Paris
148552c923ddSEric Paris /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */
1486867a448dSAmir Goldstein group = fsnotify_alloc_group(&fanotify_fsnotify_ops,
1487e79719a2SAmir Goldstein FSNOTIFY_GROUP_USER | FSNOTIFY_GROUP_NOFS);
148826379198SEric Paris if (IS_ERR(group)) {
148952c923ddSEric Paris return PTR_ERR(group);
149026379198SEric Paris }
149152c923ddSEric Paris
14925b8fea65SAmir Goldstein /* Enforce groups limits per user in all containing user ns */
14935b8fea65SAmir Goldstein group->fanotify_data.ucounts = inc_ucount(current_user_ns(),
14945b8fea65SAmir Goldstein current_euid(),
14955b8fea65SAmir Goldstein UCOUNT_FANOTIFY_GROUPS);
14965b8fea65SAmir Goldstein if (!group->fanotify_data.ucounts) {
14975b8fea65SAmir Goldstein fd = -EMFILE;
14985b8fea65SAmir Goldstein goto out_destroy_group;
14995b8fea65SAmir Goldstein }
15005b8fea65SAmir Goldstein
1501a8b98c80SAmir Goldstein group->fanotify_data.flags = flags | internal_flags;
1502d46eb14bSShakeel Butt group->memcg = get_mem_cgroup_from_mm(current->mm);
15034afeff85SEric Paris
150494e00d28SAmir Goldstein group->fanotify_data.merge_hash = fanotify_alloc_merge_hash();
150594e00d28SAmir Goldstein if (!group->fanotify_data.merge_hash) {
150694e00d28SAmir Goldstein fd = -ENOMEM;
150794e00d28SAmir Goldstein goto out_destroy_group;
150894e00d28SAmir Goldstein }
150994e00d28SAmir Goldstein
1510b8a6c3a2SAmir Goldstein group->overflow_event = fanotify_alloc_overflow_event();
1511b8a6c3a2SAmir Goldstein if (unlikely(!group->overflow_event)) {
1512ff57cd58SJan Kara fd = -ENOMEM;
1513ff57cd58SJan Kara goto out_destroy_group;
1514ff57cd58SJan Kara }
1515ff57cd58SJan Kara
15161e2ee49fSWill Woods if (force_o_largefile())
15171e2ee49fSWill Woods event_f_flags |= O_LARGEFILE;
151880af2588SEric Paris group->fanotify_data.f_flags = event_f_flags;
15199e66e423SEric Paris init_waitqueue_head(&group->fanotify_data.access_waitq);
15209e66e423SEric Paris INIT_LIST_HEAD(&group->fanotify_data.access_list);
152183b7a598SAmir Goldstein switch (class) {
15224231a235SEric Paris case FAN_CLASS_NOTIF:
15234231a235SEric Paris group->priority = FS_PRIO_0;
15244231a235SEric Paris break;
15254231a235SEric Paris case FAN_CLASS_CONTENT:
15264231a235SEric Paris group->priority = FS_PRIO_1;
15274231a235SEric Paris break;
15284231a235SEric Paris case FAN_CLASS_PRE_CONTENT:
15294231a235SEric Paris group->priority = FS_PRIO_2;
15304231a235SEric Paris break;
15314231a235SEric Paris default:
15324231a235SEric Paris fd = -EINVAL;
1533d8153d4dSLino Sanfilippo goto out_destroy_group;
15344231a235SEric Paris }
1535cb2d429fSEric Paris
15365dd03f55SEric Paris if (flags & FAN_UNLIMITED_QUEUE) {
15375dd03f55SEric Paris fd = -EPERM;
15385dd03f55SEric Paris if (!capable(CAP_SYS_ADMIN))
1539d8153d4dSLino Sanfilippo goto out_destroy_group;
15405dd03f55SEric Paris group->max_events = UINT_MAX;
15415dd03f55SEric Paris } else {
15425b8fea65SAmir Goldstein group->max_events = fanotify_max_queued_events;
15435dd03f55SEric Paris }
15442529a0dfSEric Paris
1545ac7e22dcSEric Paris if (flags & FAN_UNLIMITED_MARKS) {
1546ac7e22dcSEric Paris fd = -EPERM;
1547ac7e22dcSEric Paris if (!capable(CAP_SYS_ADMIN))
1548d8153d4dSLino Sanfilippo goto out_destroy_group;
1549ac7e22dcSEric Paris }
1550e7099d8aSEric Paris
1551de8cd83eSSteve Grubb if (flags & FAN_ENABLE_AUDIT) {
1552de8cd83eSSteve Grubb fd = -EPERM;
1553de8cd83eSSteve Grubb if (!capable(CAP_AUDIT_WRITE))
1554de8cd83eSSteve Grubb goto out_destroy_group;
1555de8cd83eSSteve Grubb }
1556de8cd83eSSteve Grubb
155752c923ddSEric Paris fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
155852c923ddSEric Paris if (fd < 0)
1559d8153d4dSLino Sanfilippo goto out_destroy_group;
156052c923ddSEric Paris
156152c923ddSEric Paris return fd;
156252c923ddSEric Paris
1563d8153d4dSLino Sanfilippo out_destroy_group:
1564d8153d4dSLino Sanfilippo fsnotify_destroy_group(group);
156552c923ddSEric Paris return fd;
156611637e4bSEric Paris }
1567bbaa4168SEric Paris
fanotify_test_fsid(struct dentry * dentry,__kernel_fsid_t * fsid)15688299212cSGabriel Krisman Bertazi static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
1569a8b13aa2SAmir Goldstein {
157073072283SAmir Goldstein __kernel_fsid_t root_fsid;
1571a8b13aa2SAmir Goldstein int err;
1572a8b13aa2SAmir Goldstein
1573a8b13aa2SAmir Goldstein /*
15748299212cSGabriel Krisman Bertazi * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse).
1575a8b13aa2SAmir Goldstein */
15768299212cSGabriel Krisman Bertazi err = vfs_get_fsid(dentry, fsid);
1577a8b13aa2SAmir Goldstein if (err)
1578a8b13aa2SAmir Goldstein return err;
1579a8b13aa2SAmir Goldstein
158073072283SAmir Goldstein if (!fsid->val[0] && !fsid->val[1])
1581a8b13aa2SAmir Goldstein return -ENODEV;
1582a8b13aa2SAmir Goldstein
1583a8b13aa2SAmir Goldstein /*
15848299212cSGabriel Krisman Bertazi * Make sure dentry is not of a filesystem subvolume (e.g. btrfs)
1585a8b13aa2SAmir Goldstein * which uses a different fsid than sb root.
1586a8b13aa2SAmir Goldstein */
15878299212cSGabriel Krisman Bertazi err = vfs_get_fsid(dentry->d_sb->s_root, &root_fsid);
1588a8b13aa2SAmir Goldstein if (err)
1589a8b13aa2SAmir Goldstein return err;
1590a8b13aa2SAmir Goldstein
159173072283SAmir Goldstein if (root_fsid.val[0] != fsid->val[0] ||
159273072283SAmir Goldstein root_fsid.val[1] != fsid->val[1])
1593a8b13aa2SAmir Goldstein return -EXDEV;
1594a8b13aa2SAmir Goldstein
15958299212cSGabriel Krisman Bertazi return 0;
15968299212cSGabriel Krisman Bertazi }
15978299212cSGabriel Krisman Bertazi
15988299212cSGabriel Krisman Bertazi /* Check if filesystem can encode a unique fid */
fanotify_test_fid(struct dentry * dentry,unsigned int flags)159997ac4897SAmir Goldstein static int fanotify_test_fid(struct dentry *dentry, unsigned int flags)
16008299212cSGabriel Krisman Bertazi {
160197ac4897SAmir Goldstein unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
160297ac4897SAmir Goldstein const struct export_operations *nop = dentry->d_sb->s_export_op;
160397ac4897SAmir Goldstein
1604a8b13aa2SAmir Goldstein /*
160597ac4897SAmir Goldstein * We need to make sure that the filesystem supports encoding of
160697ac4897SAmir Goldstein * file handles so user can use name_to_handle_at() to compare fids
160797ac4897SAmir Goldstein * reported with events to the file handle of watched objects.
1608a8b13aa2SAmir Goldstein */
160997ac4897SAmir Goldstein if (!nop)
161097ac4897SAmir Goldstein return -EOPNOTSUPP;
161197ac4897SAmir Goldstein
161297ac4897SAmir Goldstein /*
161397ac4897SAmir Goldstein * For sb/mount mark, we also need to make sure that the filesystem
161497ac4897SAmir Goldstein * supports decoding file handles, so user has a way to map back the
161597ac4897SAmir Goldstein * reported fids to filesystem objects.
161697ac4897SAmir Goldstein */
161797ac4897SAmir Goldstein if (mark_type != FAN_MARK_INODE && !nop->fh_to_dentry)
1618a8b13aa2SAmir Goldstein return -EOPNOTSUPP;
1619a8b13aa2SAmir Goldstein
1620a8b13aa2SAmir Goldstein return 0;
1621a8b13aa2SAmir Goldstein }
1622a8b13aa2SAmir Goldstein
fanotify_events_supported(struct fsnotify_group * group,const struct path * path,__u64 mask,unsigned int flags)16238698e3baSAmir Goldstein static int fanotify_events_supported(struct fsnotify_group *group,
1624d5bf8889SAl Viro const struct path *path, __u64 mask,
16258698e3baSAmir Goldstein unsigned int flags)
16260b3b094aSJan Kara {
16278698e3baSAmir Goldstein unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
16288698e3baSAmir Goldstein /* Strict validation of events in non-dir inode mask with v5.17+ APIs */
16298698e3baSAmir Goldstein bool strict_dir_events = FAN_GROUP_FLAG(group, FAN_REPORT_TARGET_FID) ||
1630e252f2edSAmir Goldstein (mask & FAN_RENAME) ||
1631e252f2edSAmir Goldstein (flags & FAN_MARK_IGNORE);
16328698e3baSAmir Goldstein
16330b3b094aSJan Kara /*
16340b3b094aSJan Kara * Some filesystems such as 'proc' acquire unusual locks when opening
16350b3b094aSJan Kara * files. For them fanotify permission events have high chances of
16360b3b094aSJan Kara * deadlocking the system - open done when reporting fanotify event
16370b3b094aSJan Kara * blocks on this "unusual" lock while another process holding the lock
16380b3b094aSJan Kara * waits for fanotify permission event to be answered. Just disallow
16390b3b094aSJan Kara * permission events for such filesystems.
16400b3b094aSJan Kara */
16410b3b094aSJan Kara if (mask & FANOTIFY_PERM_EVENTS &&
16420b3b094aSJan Kara path->mnt->mnt_sb->s_type->fs_flags & FS_DISALLOW_NOTIFY_PERM)
16430b3b094aSJan Kara return -EINVAL;
16448698e3baSAmir Goldstein
16458698e3baSAmir Goldstein /*
164669562eb0SAmir Goldstein * mount and sb marks are not allowed on kernel internal pseudo fs,
164769562eb0SAmir Goldstein * like pipe_mnt, because that would subscribe to events on all the
164869562eb0SAmir Goldstein * anonynous pipes in the system.
164969562eb0SAmir Goldstein *
165069562eb0SAmir Goldstein * SB_NOUSER covers all of the internal pseudo fs whose objects are not
165169562eb0SAmir Goldstein * exposed to user's mount namespace, but there are other SB_KERNMOUNT
165269562eb0SAmir Goldstein * fs, like nsfs, debugfs, for which the value of allowing sb and mount
165369562eb0SAmir Goldstein * mark is questionable. For now we leave them alone.
165469562eb0SAmir Goldstein */
165569562eb0SAmir Goldstein if (mark_type != FAN_MARK_INODE &&
165669562eb0SAmir Goldstein path->mnt->mnt_sb->s_flags & SB_NOUSER)
165769562eb0SAmir Goldstein return -EINVAL;
165869562eb0SAmir Goldstein
165969562eb0SAmir Goldstein /*
16608698e3baSAmir Goldstein * We shouldn't have allowed setting dirent events and the directory
16618698e3baSAmir Goldstein * flags FAN_ONDIR and FAN_EVENT_ON_CHILD in mask of non-dir inode,
16628698e3baSAmir Goldstein * but because we always allowed it, error only when using new APIs.
16638698e3baSAmir Goldstein */
16648698e3baSAmir Goldstein if (strict_dir_events && mark_type == FAN_MARK_INODE &&
16658698e3baSAmir Goldstein !d_is_dir(path->dentry) && (mask & FANOTIFY_DIRONLY_EVENT_BITS))
16668698e3baSAmir Goldstein return -ENOTDIR;
16678698e3baSAmir Goldstein
16680b3b094aSJan Kara return 0;
16690b3b094aSJan Kara }
16700b3b094aSJan Kara
do_fanotify_mark(int fanotify_fd,unsigned int flags,__u64 mask,int dfd,const char __user * pathname)1671183caa3cSDominik Brodowski static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
1672183caa3cSDominik Brodowski int dfd, const char __user *pathname)
1673bbaa4168SEric Paris {
16740ff21db9SEric Paris struct inode *inode = NULL;
16750ff21db9SEric Paris struct vfsmount *mnt = NULL;
16762a3edf86SEric Paris struct fsnotify_group *group;
16772903ff01SAl Viro struct fd f;
16782a3edf86SEric Paris struct path path;
167973072283SAmir Goldstein __kernel_fsid_t __fsid, *fsid = NULL;
1680bdd5a46fSAmir Goldstein u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
168123c9deebSAmir Goldstein unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
16828afd7215SAmir Goldstein unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
1683e252f2edSAmir Goldstein unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS;
1684d809daf1SAmir Goldstein unsigned int obj_type, fid_mode;
168585af5d92SAmir Goldstein u32 umask = 0;
16862903ff01SAl Viro int ret;
16872a3edf86SEric Paris
16882a3edf86SEric Paris pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
16892a3edf86SEric Paris __func__, fanotify_fd, flags, dfd, pathname, mask);
16902a3edf86SEric Paris
16912a3edf86SEric Paris /* we only use the lower 32 bits as of right now. */
169222d483b9SChristian Brauner if (upper_32_bits(mask))
16932a3edf86SEric Paris return -EINVAL;
16942a3edf86SEric Paris
169523c9deebSAmir Goldstein if (flags & ~FANOTIFY_MARK_FLAGS)
169688380fe6SAndreas Gruenbacher return -EINVAL;
1697d54f4fbaSAmir Goldstein
1698d54f4fbaSAmir Goldstein switch (mark_type) {
1699d54f4fbaSAmir Goldstein case FAN_MARK_INODE:
1700ac5656d8SAaron Goidel obj_type = FSNOTIFY_OBJ_TYPE_INODE;
1701ac5656d8SAaron Goidel break;
1702d54f4fbaSAmir Goldstein case FAN_MARK_MOUNT:
1703ac5656d8SAaron Goidel obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
1704ac5656d8SAaron Goidel break;
1705d54f4fbaSAmir Goldstein case FAN_MARK_FILESYSTEM:
1706ac5656d8SAaron Goidel obj_type = FSNOTIFY_OBJ_TYPE_SB;
1707d54f4fbaSAmir Goldstein break;
1708d54f4fbaSAmir Goldstein default:
1709d54f4fbaSAmir Goldstein return -EINVAL;
1710d54f4fbaSAmir Goldstein }
1711d54f4fbaSAmir Goldstein
17128afd7215SAmir Goldstein switch (mark_cmd) {
1713df561f66SGustavo A. R. Silva case FAN_MARK_ADD:
171488380fe6SAndreas Gruenbacher case FAN_MARK_REMOVE:
17151734dee4SLino Sanfilippo if (!mask)
17161734dee4SLino Sanfilippo return -EINVAL;
1717cc299a98SHeinrich Schuchardt break;
17184d92604cSEric Paris case FAN_MARK_FLUSH:
171923c9deebSAmir Goldstein if (flags & ~(FANOTIFY_MARK_TYPE_BITS | FAN_MARK_FLUSH))
1720cc299a98SHeinrich Schuchardt return -EINVAL;
172188380fe6SAndreas Gruenbacher break;
172288380fe6SAndreas Gruenbacher default:
172388380fe6SAndreas Gruenbacher return -EINVAL;
172488380fe6SAndreas Gruenbacher }
17258fcd6528SEric Paris
17266685df31SMiklos Szeredi if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS))
172723c9deebSAmir Goldstein valid_mask |= FANOTIFY_PERM_EVENTS;
17286685df31SMiklos Szeredi
17296685df31SMiklos Szeredi if (mask & ~valid_mask)
17302a3edf86SEric Paris return -EINVAL;
17312a3edf86SEric Paris
1732e252f2edSAmir Goldstein
1733e252f2edSAmir Goldstein /* We don't allow FAN_MARK_IGNORE & FAN_MARK_IGNORED_MASK together */
1734e252f2edSAmir Goldstein if (ignore == (FAN_MARK_IGNORE | FAN_MARK_IGNORED_MASK))
1735e252f2edSAmir Goldstein return -EINVAL;
1736e252f2edSAmir Goldstein
173731a371e4SAmir Goldstein /*
173831a371e4SAmir Goldstein * Event flags (FAN_ONDIR, FAN_EVENT_ON_CHILD) have no effect with
173931a371e4SAmir Goldstein * FAN_MARK_IGNORED_MASK.
174031a371e4SAmir Goldstein */
1741e252f2edSAmir Goldstein if (ignore == FAN_MARK_IGNORED_MASK) {
17423ef86653SAmir Goldstein mask &= ~FANOTIFY_EVENT_FLAGS;
1743e252f2edSAmir Goldstein umask = FANOTIFY_EVENT_FLAGS;
1744e252f2edSAmir Goldstein }
17453ef86653SAmir Goldstein
17462903ff01SAl Viro f = fdget(fanotify_fd);
17472903ff01SAl Viro if (unlikely(!f.file))
17482a3edf86SEric Paris return -EBADF;
17492a3edf86SEric Paris
17502a3edf86SEric Paris /* verify that this is indeed an fanotify instance */
17512a3edf86SEric Paris ret = -EINVAL;
17522903ff01SAl Viro if (unlikely(f.file->f_op != &fanotify_fops))
17532a3edf86SEric Paris goto fput_and_out;
17542903ff01SAl Viro group = f.file->private_data;
17554231a235SEric Paris
17564231a235SEric Paris /*
1757a8b98c80SAmir Goldstein * An unprivileged user is not allowed to setup mount nor filesystem
1758a8b98c80SAmir Goldstein * marks. This also includes setting up such marks by a group that
1759a8b98c80SAmir Goldstein * was initialized by an unprivileged user.
17607cea2a3cSAmir Goldstein */
17617cea2a3cSAmir Goldstein ret = -EPERM;
1762a8b98c80SAmir Goldstein if ((!capable(CAP_SYS_ADMIN) ||
1763a8b98c80SAmir Goldstein FAN_GROUP_FLAG(group, FANOTIFY_UNPRIV)) &&
17647cea2a3cSAmir Goldstein mark_type != FAN_MARK_INODE)
17657cea2a3cSAmir Goldstein goto fput_and_out;
17667cea2a3cSAmir Goldstein
17677cea2a3cSAmir Goldstein /*
17684231a235SEric Paris * group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF. These are not
17694231a235SEric Paris * allowed to set permissions events.
17704231a235SEric Paris */
17714231a235SEric Paris ret = -EINVAL;
177223c9deebSAmir Goldstein if (mask & FANOTIFY_PERM_EVENTS &&
17734231a235SEric Paris group->priority == FS_PRIO_0)
17744231a235SEric Paris goto fput_and_out;
17752a3edf86SEric Paris
17769709bd54SGabriel Krisman Bertazi if (mask & FAN_FS_ERROR &&
17779709bd54SGabriel Krisman Bertazi mark_type != FAN_MARK_FILESYSTEM)
17789709bd54SGabriel Krisman Bertazi goto fput_and_out;
17799709bd54SGabriel Krisman Bertazi
1780235328d1SAmir Goldstein /*
17817d5e005dSAmir Goldstein * Evictable is only relevant for inode marks, because only inode object
17827d5e005dSAmir Goldstein * can be evicted on memory pressure.
17837d5e005dSAmir Goldstein */
17847d5e005dSAmir Goldstein if (flags & FAN_MARK_EVICTABLE &&
17857d5e005dSAmir Goldstein mark_type != FAN_MARK_INODE)
17867d5e005dSAmir Goldstein goto fput_and_out;
17877d5e005dSAmir Goldstein
17887d5e005dSAmir Goldstein /*
17894fe595cfSGabriel Krisman Bertazi * Events that do not carry enough information to report
17904fe595cfSGabriel Krisman Bertazi * event->fd require a group that supports reporting fid. Those
17914fe595cfSGabriel Krisman Bertazi * events are not supported on a mount mark, because they do not
17924fe595cfSGabriel Krisman Bertazi * carry enough information (i.e. path) to be filtered by mount
17934fe595cfSGabriel Krisman Bertazi * point.
1794235328d1SAmir Goldstein */
1795d809daf1SAmir Goldstein fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
17964fe595cfSGabriel Krisman Bertazi if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) &&
1797d809daf1SAmir Goldstein (!fid_mode || mark_type == FAN_MARK_MOUNT))
1798235328d1SAmir Goldstein goto fput_and_out;
1799235328d1SAmir Goldstein
18008cc3b1ccSAmir Goldstein /*
18018cc3b1ccSAmir Goldstein * FAN_RENAME uses special info type records to report the old and
18028cc3b1ccSAmir Goldstein * new parent+name. Reporting only old and new parent id is less
18038cc3b1ccSAmir Goldstein * useful and was not implemented.
18048cc3b1ccSAmir Goldstein */
18058cc3b1ccSAmir Goldstein if (mask & FAN_RENAME && !(fid_mode & FAN_REPORT_NAME))
18068cc3b1ccSAmir Goldstein goto fput_and_out;
18078cc3b1ccSAmir Goldstein
18088afd7215SAmir Goldstein if (mark_cmd == FAN_MARK_FLUSH) {
18090a8dd2dbSHeinrich Schuchardt ret = 0;
1810d54f4fbaSAmir Goldstein if (mark_type == FAN_MARK_MOUNT)
18110a8dd2dbSHeinrich Schuchardt fsnotify_clear_vfsmount_marks_by_group(group);
1812d54f4fbaSAmir Goldstein else if (mark_type == FAN_MARK_FILESYSTEM)
1813d54f4fbaSAmir Goldstein fsnotify_clear_sb_marks_by_group(group);
18140a8dd2dbSHeinrich Schuchardt else
18150a8dd2dbSHeinrich Schuchardt fsnotify_clear_inode_marks_by_group(group);
18160a8dd2dbSHeinrich Schuchardt goto fput_and_out;
18170a8dd2dbSHeinrich Schuchardt }
18180a8dd2dbSHeinrich Schuchardt
1819ac5656d8SAaron Goidel ret = fanotify_find_path(dfd, pathname, &path, flags,
1820ac5656d8SAaron Goidel (mask & ALL_FSNOTIFY_EVENTS), obj_type);
18212a3edf86SEric Paris if (ret)
18222a3edf86SEric Paris goto fput_and_out;
18232a3edf86SEric Paris
18248afd7215SAmir Goldstein if (mark_cmd == FAN_MARK_ADD) {
18258698e3baSAmir Goldstein ret = fanotify_events_supported(group, &path, mask, flags);
18260b3b094aSJan Kara if (ret)
18270b3b094aSJan Kara goto path_put_and_out;
18280b3b094aSJan Kara }
18290b3b094aSJan Kara
1830d809daf1SAmir Goldstein if (fid_mode) {
18318299212cSGabriel Krisman Bertazi ret = fanotify_test_fsid(path.dentry, &__fsid);
18328299212cSGabriel Krisman Bertazi if (ret)
18338299212cSGabriel Krisman Bertazi goto path_put_and_out;
18348299212cSGabriel Krisman Bertazi
183597ac4897SAmir Goldstein ret = fanotify_test_fid(path.dentry, flags);
1836a8b13aa2SAmir Goldstein if (ret)
1837a8b13aa2SAmir Goldstein goto path_put_and_out;
183877115225SAmir Goldstein
183973072283SAmir Goldstein fsid = &__fsid;
1840a8b13aa2SAmir Goldstein }
1841a8b13aa2SAmir Goldstein
18422a3edf86SEric Paris /* inode held in place by reference to path; group by fget on fd */
1843d54f4fbaSAmir Goldstein if (mark_type == FAN_MARK_INODE)
18442a3edf86SEric Paris inode = path.dentry->d_inode;
18450ff21db9SEric Paris else
18460ff21db9SEric Paris mnt = path.mnt;
18472a3edf86SEric Paris
1848e252f2edSAmir Goldstein ret = mnt ? -EINVAL : -EISDIR;
1849e252f2edSAmir Goldstein /* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */
1850e252f2edSAmir Goldstein if (mark_cmd == FAN_MARK_ADD && ignore == FAN_MARK_IGNORE &&
1851e252f2edSAmir Goldstein (mnt || S_ISDIR(inode->i_mode)) &&
1852e252f2edSAmir Goldstein !(flags & FAN_MARK_IGNORED_SURV_MODIFY))
1853e252f2edSAmir Goldstein goto path_put_and_out;
1854e252f2edSAmir Goldstein
185585af5d92SAmir Goldstein /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */
185685af5d92SAmir Goldstein if (mnt || !S_ISDIR(inode->i_mode)) {
185785af5d92SAmir Goldstein mask &= ~FAN_EVENT_ON_CHILD;
185885af5d92SAmir Goldstein umask = FAN_EVENT_ON_CHILD;
185951280637SAmir Goldstein /*
186051280637SAmir Goldstein * If group needs to report parent fid, register for getting
186151280637SAmir Goldstein * events with parent/name info for non-directory.
186251280637SAmir Goldstein */
186351280637SAmir Goldstein if ((fid_mode & FAN_REPORT_DIR_FID) &&
186431a371e4SAmir Goldstein (flags & FAN_MARK_ADD) && !ignore)
186551280637SAmir Goldstein mask |= FAN_EVENT_ON_CHILD;
186685af5d92SAmir Goldstein }
186785af5d92SAmir Goldstein
18682a3edf86SEric Paris /* create/update an inode mark */
18698afd7215SAmir Goldstein switch (mark_cmd) {
1870c6223f46SAndreas Gruenbacher case FAN_MARK_ADD:
1871d54f4fbaSAmir Goldstein if (mark_type == FAN_MARK_MOUNT)
187277115225SAmir Goldstein ret = fanotify_add_vfsmount_mark(group, mnt, mask,
187377115225SAmir Goldstein flags, fsid);
1874d54f4fbaSAmir Goldstein else if (mark_type == FAN_MARK_FILESYSTEM)
187577115225SAmir Goldstein ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
187677115225SAmir Goldstein flags, fsid);
18770ff21db9SEric Paris else
187877115225SAmir Goldstein ret = fanotify_add_inode_mark(group, inode, mask,
187977115225SAmir Goldstein flags, fsid);
1880c6223f46SAndreas Gruenbacher break;
1881c6223f46SAndreas Gruenbacher case FAN_MARK_REMOVE:
1882d54f4fbaSAmir Goldstein if (mark_type == FAN_MARK_MOUNT)
188377115225SAmir Goldstein ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
188485af5d92SAmir Goldstein flags, umask);
1885d54f4fbaSAmir Goldstein else if (mark_type == FAN_MARK_FILESYSTEM)
188677115225SAmir Goldstein ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
188785af5d92SAmir Goldstein flags, umask);
1888f3640192SAndreas Gruenbacher else
188977115225SAmir Goldstein ret = fanotify_remove_inode_mark(group, inode, mask,
189085af5d92SAmir Goldstein flags, umask);
1891c6223f46SAndreas Gruenbacher break;
1892c6223f46SAndreas Gruenbacher default:
1893c6223f46SAndreas Gruenbacher ret = -EINVAL;
1894c6223f46SAndreas Gruenbacher }
18952a3edf86SEric Paris
1896a8b13aa2SAmir Goldstein path_put_and_out:
18972a3edf86SEric Paris path_put(&path);
18982a3edf86SEric Paris fput_and_out:
18992903ff01SAl Viro fdput(f);
19002a3edf86SEric Paris return ret;
1901bbaa4168SEric Paris }
19022a3edf86SEric Paris
19032ca408d9SBrian Gerst #ifndef CONFIG_ARCH_SPLIT_ARG64
SYSCALL_DEFINE5(fanotify_mark,int,fanotify_fd,unsigned int,flags,__u64,mask,int,dfd,const char __user *,pathname)1904183caa3cSDominik Brodowski SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags,
1905183caa3cSDominik Brodowski __u64, mask, int, dfd,
1906183caa3cSDominik Brodowski const char __user *, pathname)
1907183caa3cSDominik Brodowski {
1908183caa3cSDominik Brodowski return do_fanotify_mark(fanotify_fd, flags, mask, dfd, pathname);
1909183caa3cSDominik Brodowski }
19102ca408d9SBrian Gerst #endif
1911183caa3cSDominik Brodowski
19122ca408d9SBrian Gerst #if defined(CONFIG_ARCH_SPLIT_ARG64) || defined(CONFIG_COMPAT)
SYSCALL32_DEFINE6(fanotify_mark,int,fanotify_fd,unsigned int,flags,SC_ARG64 (mask),int,dfd,const char __user *,pathname)19132ca408d9SBrian Gerst SYSCALL32_DEFINE6(fanotify_mark,
191491c2e0bcSAl Viro int, fanotify_fd, unsigned int, flags,
19152ca408d9SBrian Gerst SC_ARG64(mask), int, dfd,
191691c2e0bcSAl Viro const char __user *, pathname)
191791c2e0bcSAl Viro {
19182ca408d9SBrian Gerst return do_fanotify_mark(fanotify_fd, flags, SC_VAL64(__u64, mask),
191991c2e0bcSAl Viro dfd, pathname);
192091c2e0bcSAl Viro }
192191c2e0bcSAl Viro #endif
192291c2e0bcSAl Viro
19232a3edf86SEric Paris /*
1924ae0e47f0SJustin P. Mattock * fanotify_user_setup - Our initialization function. Note that we cannot return
19252a3edf86SEric Paris * error because we have compiled-in VFS hooks. So an (unlikely) failure here
19262a3edf86SEric Paris * must result in panic().
19272a3edf86SEric Paris */
fanotify_user_setup(void)19282a3edf86SEric Paris static int __init fanotify_user_setup(void)
19292a3edf86SEric Paris {
19305b8fea65SAmir Goldstein struct sysinfo si;
19315b8fea65SAmir Goldstein int max_marks;
19325b8fea65SAmir Goldstein
19335b8fea65SAmir Goldstein si_meminfo(&si);
19345b8fea65SAmir Goldstein /*
19355b8fea65SAmir Goldstein * Allow up to 1% of addressable memory to be accounted for per user
19365b8fea65SAmir Goldstein * marks limited to the range [8192, 1048576]. mount and sb marks are
19375b8fea65SAmir Goldstein * a lot cheaper than inode marks, but there is no reason for a user
19385b8fea65SAmir Goldstein * to have many of those, so calculate by the cost of inode marks.
19395b8fea65SAmir Goldstein */
19405b8fea65SAmir Goldstein max_marks = (((si.totalram - si.totalhigh) / 100) << PAGE_SHIFT) /
19415b8fea65SAmir Goldstein INODE_MARK_COST;
19425b8fea65SAmir Goldstein max_marks = clamp(max_marks, FANOTIFY_OLD_DEFAULT_MAX_MARKS,
19435b8fea65SAmir Goldstein FANOTIFY_DEFAULT_MAX_USER_MARKS);
19445b8fea65SAmir Goldstein
1945a8b98c80SAmir Goldstein BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
1946*ffe1766aSAmir Goldstein BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13);
1947e252f2edSAmir Goldstein BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11);
1948bdd5a46fSAmir Goldstein
1949d46eb14bSShakeel Butt fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
1950d46eb14bSShakeel Butt SLAB_PANIC|SLAB_ACCOUNT);
19517088f357SJan Kara fanotify_fid_event_cachep = KMEM_CACHE(fanotify_fid_event,
19527088f357SJan Kara SLAB_PANIC);
19537088f357SJan Kara fanotify_path_event_cachep = KMEM_CACHE(fanotify_path_event,
19547088f357SJan Kara SLAB_PANIC);
19556685df31SMiklos Szeredi if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) {
19566685df31SMiklos Szeredi fanotify_perm_event_cachep =
195733913997SAmir Goldstein KMEM_CACHE(fanotify_perm_event, SLAB_PANIC);
19586685df31SMiklos Szeredi }
19592a3edf86SEric Paris
19605b8fea65SAmir Goldstein fanotify_max_queued_events = FANOTIFY_DEFAULT_MAX_EVENTS;
19615b8fea65SAmir Goldstein init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS] =
19625b8fea65SAmir Goldstein FANOTIFY_DEFAULT_MAX_GROUPS;
19635b8fea65SAmir Goldstein init_user_ns.ucount_max[UCOUNT_FANOTIFY_MARKS] = max_marks;
19647b9ad122SXiaoming Ni fanotify_sysctls_init();
19655b8fea65SAmir Goldstein
19662a3edf86SEric Paris return 0;
19672a3edf86SEric Paris }
19682a3edf86SEric Paris device_initcall(fanotify_user_setup);
1969