18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/fanotify.h> 38c2ecf20Sopenharmony_ci#include <linux/fdtable.h> 48c2ecf20Sopenharmony_ci#include <linux/fsnotify_backend.h> 58c2ecf20Sopenharmony_ci#include <linux/init.h> 68c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* UINT_MAX */ 88c2ecf20Sopenharmony_ci#include <linux/mount.h> 98c2ecf20Sopenharmony_ci#include <linux/sched.h> 108c2ecf20Sopenharmony_ci#include <linux/sched/user.h> 118c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/wait.h> 148c2ecf20Sopenharmony_ci#include <linux/audit.h> 158c2ecf20Sopenharmony_ci#include <linux/sched/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/statfs.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "fanotify.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic bool fanotify_path_equal(struct path *p1, struct path *p2) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci return p1->mnt == p2->mnt && p1->dentry == p2->dentry; 238c2ecf20Sopenharmony_ci} 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1, 268c2ecf20Sopenharmony_ci __kernel_fsid_t *fsid2) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1]; 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic bool fanotify_fh_equal(struct fanotify_fh *fh1, 328c2ecf20Sopenharmony_ci struct fanotify_fh *fh2) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci if (fh1->type != fh2->type || fh1->len != fh2->len) 358c2ecf20Sopenharmony_ci return false; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return !fh1->len || 388c2ecf20Sopenharmony_ci !memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, 428c2ecf20Sopenharmony_ci struct fanotify_fid_event *ffe2) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci /* Do not merge fid events without object fh */ 458c2ecf20Sopenharmony_ci if (!ffe1->object_fh.len) 468c2ecf20Sopenharmony_ci return false; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci return fanotify_fsid_equal(&ffe1->fsid, &ffe2->fsid) && 498c2ecf20Sopenharmony_ci fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh); 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic bool fanotify_info_equal(struct fanotify_info *info1, 538c2ecf20Sopenharmony_ci struct fanotify_info *info2) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci if (info1->dir_fh_totlen != info2->dir_fh_totlen || 568c2ecf20Sopenharmony_ci info1->file_fh_totlen != info2->file_fh_totlen || 578c2ecf20Sopenharmony_ci info1->name_len != info2->name_len) 588c2ecf20Sopenharmony_ci return false; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (info1->dir_fh_totlen && 618c2ecf20Sopenharmony_ci !fanotify_fh_equal(fanotify_info_dir_fh(info1), 628c2ecf20Sopenharmony_ci fanotify_info_dir_fh(info2))) 638c2ecf20Sopenharmony_ci return false; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (info1->file_fh_totlen && 668c2ecf20Sopenharmony_ci !fanotify_fh_equal(fanotify_info_file_fh(info1), 678c2ecf20Sopenharmony_ci fanotify_info_file_fh(info2))) 688c2ecf20Sopenharmony_ci return false; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return !info1->name_len || 718c2ecf20Sopenharmony_ci !memcmp(fanotify_info_name(info1), fanotify_info_name(info2), 728c2ecf20Sopenharmony_ci info1->name_len); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic bool fanotify_name_event_equal(struct fanotify_name_event *fne1, 768c2ecf20Sopenharmony_ci struct fanotify_name_event *fne2) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct fanotify_info *info1 = &fne1->info; 798c2ecf20Sopenharmony_ci struct fanotify_info *info2 = &fne2->info; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Do not merge name events without dir fh */ 828c2ecf20Sopenharmony_ci if (!info1->dir_fh_totlen) 838c2ecf20Sopenharmony_ci return false; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (!fanotify_fsid_equal(&fne1->fsid, &fne2->fsid)) 868c2ecf20Sopenharmony_ci return false; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return fanotify_info_equal(info1, info2); 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic bool fanotify_should_merge(struct fsnotify_event *old_fsn, 928c2ecf20Sopenharmony_ci struct fsnotify_event *new_fsn) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct fanotify_event *old, *new; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn); 978c2ecf20Sopenharmony_ci old = FANOTIFY_E(old_fsn); 988c2ecf20Sopenharmony_ci new = FANOTIFY_E(new_fsn); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (old_fsn->objectid != new_fsn->objectid || 1018c2ecf20Sopenharmony_ci old->type != new->type || old->pid != new->pid) 1028c2ecf20Sopenharmony_ci return false; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* 1058c2ecf20Sopenharmony_ci * We want to merge many dirent events in the same dir (i.e. 1068c2ecf20Sopenharmony_ci * creates/unlinks/renames), but we do not want to merge dirent 1078c2ecf20Sopenharmony_ci * events referring to subdirs with dirent events referring to 1088c2ecf20Sopenharmony_ci * non subdirs, otherwise, user won't be able to tell from a 1098c2ecf20Sopenharmony_ci * mask FAN_CREATE|FAN_DELETE|FAN_ONDIR if it describes mkdir+ 1108c2ecf20Sopenharmony_ci * unlink pair or rmdir+create pair of events. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR)) 1138c2ecf20Sopenharmony_ci return false; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci switch (old->type) { 1168c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_PATH: 1178c2ecf20Sopenharmony_ci return fanotify_path_equal(fanotify_event_path(old), 1188c2ecf20Sopenharmony_ci fanotify_event_path(new)); 1198c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_FID: 1208c2ecf20Sopenharmony_ci return fanotify_fid_event_equal(FANOTIFY_FE(old), 1218c2ecf20Sopenharmony_ci FANOTIFY_FE(new)); 1228c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_FID_NAME: 1238c2ecf20Sopenharmony_ci return fanotify_name_event_equal(FANOTIFY_NE(old), 1248c2ecf20Sopenharmony_ci FANOTIFY_NE(new)); 1258c2ecf20Sopenharmony_ci default: 1268c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return false; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/* Limit event merges to limit CPU overhead per event */ 1338c2ecf20Sopenharmony_ci#define FANOTIFY_MAX_MERGE_EVENTS 128 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/* and the list better be locked by something too! */ 1368c2ecf20Sopenharmony_cistatic int fanotify_merge(struct list_head *list, struct fsnotify_event *event) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct fsnotify_event *test_event; 1398c2ecf20Sopenharmony_ci struct fanotify_event *new; 1408c2ecf20Sopenharmony_ci int i = 0; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci pr_debug("%s: list=%p event=%p\n", __func__, list, event); 1438c2ecf20Sopenharmony_ci new = FANOTIFY_E(event); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * Don't merge a permission event with any other event so that we know 1478c2ecf20Sopenharmony_ci * the event structure we have created in fanotify_handle_event() is the 1488c2ecf20Sopenharmony_ci * one we should check for permission response. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci if (fanotify_is_perm_event(new->mask)) 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci list_for_each_entry_reverse(test_event, list, list) { 1548c2ecf20Sopenharmony_ci if (++i > FANOTIFY_MAX_MERGE_EVENTS) 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci if (fanotify_should_merge(test_event, event)) { 1578c2ecf20Sopenharmony_ci FANOTIFY_E(test_event)->mask |= new->mask; 1588c2ecf20Sopenharmony_ci return 1; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* 1668c2ecf20Sopenharmony_ci * Wait for response to permission event. The function also takes care of 1678c2ecf20Sopenharmony_ci * freeing the permission event (or offloads that in case the wait is canceled 1688c2ecf20Sopenharmony_ci * by a signal). The function returns 0 in case access got allowed by userspace, 1698c2ecf20Sopenharmony_ci * -EPERM in case userspace disallowed the access, and -ERESTARTSYS in case 1708c2ecf20Sopenharmony_ci * the wait got interrupted by a signal. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic int fanotify_get_response(struct fsnotify_group *group, 1738c2ecf20Sopenharmony_ci struct fanotify_perm_event *event, 1748c2ecf20Sopenharmony_ci struct fsnotify_iter_info *iter_info) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci pr_debug("%s: group=%p event=%p\n", __func__, group, event); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = wait_event_killable(group->fanotify_data.access_waitq, 1818c2ecf20Sopenharmony_ci event->state == FAN_EVENT_ANSWERED); 1828c2ecf20Sopenharmony_ci /* Signal pending? */ 1838c2ecf20Sopenharmony_ci if (ret < 0) { 1848c2ecf20Sopenharmony_ci spin_lock(&group->notification_lock); 1858c2ecf20Sopenharmony_ci /* Event reported to userspace and no answer yet? */ 1868c2ecf20Sopenharmony_ci if (event->state == FAN_EVENT_REPORTED) { 1878c2ecf20Sopenharmony_ci /* Event will get freed once userspace answers to it */ 1888c2ecf20Sopenharmony_ci event->state = FAN_EVENT_CANCELED; 1898c2ecf20Sopenharmony_ci spin_unlock(&group->notification_lock); 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci /* Event not yet reported? Just remove it. */ 1938c2ecf20Sopenharmony_ci if (event->state == FAN_EVENT_INIT) 1948c2ecf20Sopenharmony_ci fsnotify_remove_queued_event(group, &event->fae.fse); 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * Event may be also answered in case signal delivery raced 1978c2ecf20Sopenharmony_ci * with wakeup. In that case we have nothing to do besides 1988c2ecf20Sopenharmony_ci * freeing the event and reporting error. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci spin_unlock(&group->notification_lock); 2018c2ecf20Sopenharmony_ci goto out; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* userspace responded, convert to something usable */ 2058c2ecf20Sopenharmony_ci switch (event->response & ~FAN_AUDIT) { 2068c2ecf20Sopenharmony_ci case FAN_ALLOW: 2078c2ecf20Sopenharmony_ci ret = 0; 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci case FAN_DENY: 2108c2ecf20Sopenharmony_ci default: 2118c2ecf20Sopenharmony_ci ret = -EPERM; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Check if the response should be audited */ 2158c2ecf20Sopenharmony_ci if (event->response & FAN_AUDIT) 2168c2ecf20Sopenharmony_ci audit_fanotify(event->response & ~FAN_AUDIT); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, 2198c2ecf20Sopenharmony_ci group, event, ret); 2208c2ecf20Sopenharmony_ciout: 2218c2ecf20Sopenharmony_ci fsnotify_destroy_event(group, &event->fae.fse); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return ret; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* 2278c2ecf20Sopenharmony_ci * This function returns a mask for an event that only contains the flags 2288c2ecf20Sopenharmony_ci * that have been specifically requested by the user. Flags that may have 2298c2ecf20Sopenharmony_ci * been included within the event mask, but have not been explicitly 2308c2ecf20Sopenharmony_ci * requested by the user, will not be present in the returned mask. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistatic u32 fanotify_group_event_mask(struct fsnotify_group *group, 2338c2ecf20Sopenharmony_ci struct fsnotify_iter_info *iter_info, 2348c2ecf20Sopenharmony_ci u32 event_mask, const void *data, 2358c2ecf20Sopenharmony_ci int data_type, struct inode *dir) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci __u32 marks_mask = 0, marks_ignored_mask = 0; 2388c2ecf20Sopenharmony_ci __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS | 2398c2ecf20Sopenharmony_ci FANOTIFY_EVENT_FLAGS; 2408c2ecf20Sopenharmony_ci const struct path *path = fsnotify_data_path(data, data_type); 2418c2ecf20Sopenharmony_ci unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); 2428c2ecf20Sopenharmony_ci struct fsnotify_mark *mark; 2438c2ecf20Sopenharmony_ci int type; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", 2468c2ecf20Sopenharmony_ci __func__, iter_info->report_mask, event_mask, data, data_type); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (!fid_mode) { 2498c2ecf20Sopenharmony_ci /* Do we have path to open a file descriptor? */ 2508c2ecf20Sopenharmony_ci if (!path) 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci /* Path type events are only relevant for files and dirs */ 2538c2ecf20Sopenharmony_ci if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry)) 2548c2ecf20Sopenharmony_ci return 0; 2558c2ecf20Sopenharmony_ci } else if (!(fid_mode & FAN_REPORT_FID)) { 2568c2ecf20Sopenharmony_ci /* Do we have a directory inode to report? */ 2578c2ecf20Sopenharmony_ci if (!dir && !(event_mask & FS_ISDIR)) 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci fsnotify_foreach_obj_type(type) { 2628c2ecf20Sopenharmony_ci if (!fsnotify_iter_should_report_type(iter_info, type)) 2638c2ecf20Sopenharmony_ci continue; 2648c2ecf20Sopenharmony_ci mark = iter_info->marks[type]; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Apply ignore mask regardless of ISDIR and ON_CHILD flags */ 2678c2ecf20Sopenharmony_ci marks_ignored_mask |= mark->ignored_mask; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* 2708c2ecf20Sopenharmony_ci * If the event is on dir and this mark doesn't care about 2718c2ecf20Sopenharmony_ci * events on dir, don't send it! 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR)) 2748c2ecf20Sopenharmony_ci continue; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* 2778c2ecf20Sopenharmony_ci * If the event is on a child and this mark is on a parent not 2788c2ecf20Sopenharmony_ci * watching children, don't send it! 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci if (type == FSNOTIFY_OBJ_TYPE_PARENT && 2818c2ecf20Sopenharmony_ci !(mark->mask & FS_EVENT_ON_CHILD)) 2828c2ecf20Sopenharmony_ci continue; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci marks_mask |= mark->mask; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci test_mask = event_mask & marks_mask & ~marks_ignored_mask; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * For dirent modification events (create/delete/move) that do not carry 2918c2ecf20Sopenharmony_ci * the child entry name information, we report FAN_ONDIR for mkdir/rmdir 2928c2ecf20Sopenharmony_ci * so user can differentiate them from creat/unlink. 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * For backward compatibility and consistency, do not report FAN_ONDIR 2958c2ecf20Sopenharmony_ci * to user in legacy fanotify mode (reporting fd) and report FAN_ONDIR 2968c2ecf20Sopenharmony_ci * to user in fid mode for all event types. 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * We never report FAN_EVENT_ON_CHILD to user, but we do pass it in to 2998c2ecf20Sopenharmony_ci * fanotify_alloc_event() when group is reporting fid as indication 3008c2ecf20Sopenharmony_ci * that event happened on child. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_ci if (fid_mode) { 3038c2ecf20Sopenharmony_ci /* Do not report event flags without any event */ 3048c2ecf20Sopenharmony_ci if (!(test_mask & ~FANOTIFY_EVENT_FLAGS)) 3058c2ecf20Sopenharmony_ci return 0; 3068c2ecf20Sopenharmony_ci } else { 3078c2ecf20Sopenharmony_ci user_mask &= ~FANOTIFY_EVENT_FLAGS; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return test_mask & user_mask; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* 3148c2ecf20Sopenharmony_ci * Check size needed to encode fanotify_fh. 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * Return size of encoded fh without fanotify_fh header. 3178c2ecf20Sopenharmony_ci * Return 0 on failure to encode. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_cistatic int fanotify_encode_fh_len(struct inode *inode) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int dwords = 0; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (!inode) 3248c2ecf20Sopenharmony_ci return 0; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return dwords << 2; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* 3328c2ecf20Sopenharmony_ci * Encode fanotify_fh. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Return total size of encoded fh including fanotify_fh header. 3358c2ecf20Sopenharmony_ci * Return 0 on failure to encode. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_cistatic int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, 3388c2ecf20Sopenharmony_ci unsigned int fh_len, gfp_t gfp) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int dwords, type = 0; 3418c2ecf20Sopenharmony_ci char *ext_buf = NULL; 3428c2ecf20Sopenharmony_ci void *buf = fh->buf; 3438c2ecf20Sopenharmony_ci int err; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci fh->type = FILEID_ROOT; 3468c2ecf20Sopenharmony_ci fh->len = 0; 3478c2ecf20Sopenharmony_ci fh->flags = 0; 3488c2ecf20Sopenharmony_ci if (!inode) 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 3528c2ecf20Sopenharmony_ci * !gpf means preallocated variable size fh, but fh_len could 3538c2ecf20Sopenharmony_ci * be zero in that case if encoding fh len failed. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_ci err = -ENOENT; 3568c2ecf20Sopenharmony_ci if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4)) 3578c2ecf20Sopenharmony_ci goto out_err; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci /* No external buffer in a variable size allocated fh */ 3608c2ecf20Sopenharmony_ci if (gfp && fh_len > FANOTIFY_INLINE_FH_LEN) { 3618c2ecf20Sopenharmony_ci /* Treat failure to allocate fh as failure to encode fh */ 3628c2ecf20Sopenharmony_ci err = -ENOMEM; 3638c2ecf20Sopenharmony_ci ext_buf = kmalloc(fh_len, gfp); 3648c2ecf20Sopenharmony_ci if (!ext_buf) 3658c2ecf20Sopenharmony_ci goto out_err; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci *fanotify_fh_ext_buf_ptr(fh) = ext_buf; 3688c2ecf20Sopenharmony_ci buf = ext_buf; 3698c2ecf20Sopenharmony_ci fh->flags |= FANOTIFY_FH_FLAG_EXT_BUF; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci dwords = fh_len >> 2; 3738c2ecf20Sopenharmony_ci type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL); 3748c2ecf20Sopenharmony_ci err = -EINVAL; 3758c2ecf20Sopenharmony_ci if (!type || type == FILEID_INVALID || fh_len != dwords << 2) 3768c2ecf20Sopenharmony_ci goto out_err; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci fh->type = type; 3798c2ecf20Sopenharmony_ci fh->len = fh_len; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci return FANOTIFY_FH_HDR_LEN + fh_len; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ciout_err: 3848c2ecf20Sopenharmony_ci pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n", 3858c2ecf20Sopenharmony_ci type, fh_len, err); 3868c2ecf20Sopenharmony_ci kfree(ext_buf); 3878c2ecf20Sopenharmony_ci *fanotify_fh_ext_buf_ptr(fh) = NULL; 3888c2ecf20Sopenharmony_ci /* Report the event without a file identifier on encode error */ 3898c2ecf20Sopenharmony_ci fh->type = FILEID_INVALID; 3908c2ecf20Sopenharmony_ci fh->len = 0; 3918c2ecf20Sopenharmony_ci return 0; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* 3958c2ecf20Sopenharmony_ci * The inode to use as identifier when reporting fid depends on the event. 3968c2ecf20Sopenharmony_ci * Report the modified directory inode on dirent modification events. 3978c2ecf20Sopenharmony_ci * Report the "victim" inode otherwise. 3988c2ecf20Sopenharmony_ci * For example: 3998c2ecf20Sopenharmony_ci * FS_ATTRIB reports the child inode even if reported on a watched parent. 4008c2ecf20Sopenharmony_ci * FS_CREATE reports the modified dir inode and not the created inode. 4018c2ecf20Sopenharmony_ci */ 4028c2ecf20Sopenharmony_cistatic struct inode *fanotify_fid_inode(u32 event_mask, const void *data, 4038c2ecf20Sopenharmony_ci int data_type, struct inode *dir) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) 4068c2ecf20Sopenharmony_ci return dir; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return fsnotify_data_inode(data, data_type); 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/* 4128c2ecf20Sopenharmony_ci * The inode to use as identifier when reporting dir fid depends on the event. 4138c2ecf20Sopenharmony_ci * Report the modified directory inode on dirent modification events. 4148c2ecf20Sopenharmony_ci * Report the "victim" inode if "victim" is a directory. 4158c2ecf20Sopenharmony_ci * Report the parent inode if "victim" is not a directory and event is 4168c2ecf20Sopenharmony_ci * reported to parent. 4178c2ecf20Sopenharmony_ci * Otherwise, do not report dir fid. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_cistatic struct inode *fanotify_dfid_inode(u32 event_mask, const void *data, 4208c2ecf20Sopenharmony_ci int data_type, struct inode *dir) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct inode *inode = fsnotify_data_inode(data, data_type); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) 4258c2ecf20Sopenharmony_ci return dir; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (S_ISDIR(inode->i_mode)) 4288c2ecf20Sopenharmony_ci return inode; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci return dir; 4318c2ecf20Sopenharmony_ci} 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic struct fanotify_event *fanotify_alloc_path_event(const struct path *path, 4348c2ecf20Sopenharmony_ci gfp_t gfp) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct fanotify_path_event *pevent; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci pevent = kmem_cache_alloc(fanotify_path_event_cachep, gfp); 4398c2ecf20Sopenharmony_ci if (!pevent) 4408c2ecf20Sopenharmony_ci return NULL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH; 4438c2ecf20Sopenharmony_ci pevent->path = *path; 4448c2ecf20Sopenharmony_ci path_get(path); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return &pevent->fae; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic struct fanotify_event *fanotify_alloc_perm_event(const struct path *path, 4508c2ecf20Sopenharmony_ci gfp_t gfp) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct fanotify_perm_event *pevent; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci pevent = kmem_cache_alloc(fanotify_perm_event_cachep, gfp); 4558c2ecf20Sopenharmony_ci if (!pevent) 4568c2ecf20Sopenharmony_ci return NULL; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH_PERM; 4598c2ecf20Sopenharmony_ci pevent->response = 0; 4608c2ecf20Sopenharmony_ci pevent->state = FAN_EVENT_INIT; 4618c2ecf20Sopenharmony_ci pevent->path = *path; 4628c2ecf20Sopenharmony_ci path_get(path); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return &pevent->fae; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic struct fanotify_event *fanotify_alloc_fid_event(struct inode *id, 4688c2ecf20Sopenharmony_ci __kernel_fsid_t *fsid, 4698c2ecf20Sopenharmony_ci gfp_t gfp) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct fanotify_fid_event *ffe; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp); 4748c2ecf20Sopenharmony_ci if (!ffe) 4758c2ecf20Sopenharmony_ci return NULL; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ffe->fae.type = FANOTIFY_EVENT_TYPE_FID; 4788c2ecf20Sopenharmony_ci ffe->fsid = *fsid; 4798c2ecf20Sopenharmony_ci fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id), 4808c2ecf20Sopenharmony_ci gfp); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return &ffe->fae; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic struct fanotify_event *fanotify_alloc_name_event(struct inode *id, 4868c2ecf20Sopenharmony_ci __kernel_fsid_t *fsid, 4878c2ecf20Sopenharmony_ci const struct qstr *file_name, 4888c2ecf20Sopenharmony_ci struct inode *child, 4898c2ecf20Sopenharmony_ci gfp_t gfp) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct fanotify_name_event *fne; 4928c2ecf20Sopenharmony_ci struct fanotify_info *info; 4938c2ecf20Sopenharmony_ci struct fanotify_fh *dfh, *ffh; 4948c2ecf20Sopenharmony_ci unsigned int dir_fh_len = fanotify_encode_fh_len(id); 4958c2ecf20Sopenharmony_ci unsigned int child_fh_len = fanotify_encode_fh_len(child); 4968c2ecf20Sopenharmony_ci unsigned int size; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len; 4998c2ecf20Sopenharmony_ci if (child_fh_len) 5008c2ecf20Sopenharmony_ci size += FANOTIFY_FH_HDR_LEN + child_fh_len; 5018c2ecf20Sopenharmony_ci if (file_name) 5028c2ecf20Sopenharmony_ci size += file_name->len + 1; 5038c2ecf20Sopenharmony_ci fne = kmalloc(size, gfp); 5048c2ecf20Sopenharmony_ci if (!fne) 5058c2ecf20Sopenharmony_ci return NULL; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; 5088c2ecf20Sopenharmony_ci fne->fsid = *fsid; 5098c2ecf20Sopenharmony_ci info = &fne->info; 5108c2ecf20Sopenharmony_ci fanotify_info_init(info); 5118c2ecf20Sopenharmony_ci dfh = fanotify_info_dir_fh(info); 5128c2ecf20Sopenharmony_ci info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0); 5138c2ecf20Sopenharmony_ci if (child_fh_len) { 5148c2ecf20Sopenharmony_ci ffh = fanotify_info_file_fh(info); 5158c2ecf20Sopenharmony_ci info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci if (file_name) 5188c2ecf20Sopenharmony_ci fanotify_info_copy_name(info, file_name); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n", 5218c2ecf20Sopenharmony_ci __func__, id->i_ino, size, dir_fh_len, child_fh_len, 5228c2ecf20Sopenharmony_ci info->name_len, info->name_len, fanotify_info_name(info)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci return &fne->fae; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, 5288c2ecf20Sopenharmony_ci u32 mask, const void *data, 5298c2ecf20Sopenharmony_ci int data_type, struct inode *dir, 5308c2ecf20Sopenharmony_ci const struct qstr *file_name, 5318c2ecf20Sopenharmony_ci __kernel_fsid_t *fsid) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct fanotify_event *event = NULL; 5348c2ecf20Sopenharmony_ci gfp_t gfp = GFP_KERNEL_ACCOUNT; 5358c2ecf20Sopenharmony_ci struct inode *id = fanotify_fid_inode(mask, data, data_type, dir); 5368c2ecf20Sopenharmony_ci struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); 5378c2ecf20Sopenharmony_ci const struct path *path = fsnotify_data_path(data, data_type); 5388c2ecf20Sopenharmony_ci unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); 5398c2ecf20Sopenharmony_ci struct mem_cgroup *old_memcg; 5408c2ecf20Sopenharmony_ci struct inode *child = NULL; 5418c2ecf20Sopenharmony_ci bool name_event = false; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) { 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we 5468c2ecf20Sopenharmony_ci * report the child fid for events reported on a non-dir child 5478c2ecf20Sopenharmony_ci * in addition to reporting the parent fid and maybe child name. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_ci if ((fid_mode & FAN_REPORT_FID) && 5508c2ecf20Sopenharmony_ci id != dirid && !(mask & FAN_ONDIR)) 5518c2ecf20Sopenharmony_ci child = id; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci id = dirid; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * We record file name only in a group with FAN_REPORT_NAME 5578c2ecf20Sopenharmony_ci * and when we have a directory inode to report. 5588c2ecf20Sopenharmony_ci * 5598c2ecf20Sopenharmony_ci * For directory entry modification event, we record the fid of 5608c2ecf20Sopenharmony_ci * the directory and the name of the modified entry. 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * For event on non-directory that is reported to parent, we 5638c2ecf20Sopenharmony_ci * record the fid of the parent and the name of the child. 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Even if not reporting name, we need a variable length 5668c2ecf20Sopenharmony_ci * fanotify_name_event if reporting both parent and child fids. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci if (!(fid_mode & FAN_REPORT_NAME)) { 5698c2ecf20Sopenharmony_ci name_event = !!child; 5708c2ecf20Sopenharmony_ci file_name = NULL; 5718c2ecf20Sopenharmony_ci } else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || 5728c2ecf20Sopenharmony_ci !(mask & FAN_ONDIR)) { 5738c2ecf20Sopenharmony_ci name_event = true; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci /* 5788c2ecf20Sopenharmony_ci * For queues with unlimited length lost events are not expected and 5798c2ecf20Sopenharmony_ci * can possibly have security implications. Avoid losing events when 5808c2ecf20Sopenharmony_ci * memory is short. For the limited size queues, avoid OOM killer in the 5818c2ecf20Sopenharmony_ci * target monitoring memcg as it may have security repercussion. 5828c2ecf20Sopenharmony_ci */ 5838c2ecf20Sopenharmony_ci if (group->max_events == UINT_MAX) 5848c2ecf20Sopenharmony_ci gfp |= __GFP_NOFAIL; 5858c2ecf20Sopenharmony_ci else 5868c2ecf20Sopenharmony_ci gfp |= __GFP_RETRY_MAYFAIL; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* Whoever is interested in the event, pays for the allocation. */ 5898c2ecf20Sopenharmony_ci old_memcg = set_active_memcg(group->memcg); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci if (fanotify_is_perm_event(mask)) { 5928c2ecf20Sopenharmony_ci event = fanotify_alloc_perm_event(path, gfp); 5938c2ecf20Sopenharmony_ci } else if (name_event && (file_name || child)) { 5948c2ecf20Sopenharmony_ci event = fanotify_alloc_name_event(id, fsid, file_name, child, 5958c2ecf20Sopenharmony_ci gfp); 5968c2ecf20Sopenharmony_ci } else if (fid_mode) { 5978c2ecf20Sopenharmony_ci event = fanotify_alloc_fid_event(id, fsid, gfp); 5988c2ecf20Sopenharmony_ci } else { 5998c2ecf20Sopenharmony_ci event = fanotify_alloc_path_event(path, gfp); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (!event) 6038c2ecf20Sopenharmony_ci goto out; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * Use the victim inode instead of the watching inode as the id for 6078c2ecf20Sopenharmony_ci * event queue, so event reported on parent is merged with event 6088c2ecf20Sopenharmony_ci * reported on child when both directory and child watches exist. 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci fanotify_init_event(event, (unsigned long)id, mask); 6118c2ecf20Sopenharmony_ci if (FAN_GROUP_FLAG(group, FAN_REPORT_TID)) 6128c2ecf20Sopenharmony_ci event->pid = get_pid(task_pid(current)); 6138c2ecf20Sopenharmony_ci else 6148c2ecf20Sopenharmony_ci event->pid = get_pid(task_tgid(current)); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciout: 6178c2ecf20Sopenharmony_ci set_active_memcg(old_memcg); 6188c2ecf20Sopenharmony_ci return event; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci/* 6228c2ecf20Sopenharmony_ci * Get cached fsid of the filesystem containing the object from any connector. 6238c2ecf20Sopenharmony_ci * All connectors are supposed to have the same fsid, but we do not verify that 6248c2ecf20Sopenharmony_ci * here. 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_cistatic __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci int type; 6298c2ecf20Sopenharmony_ci __kernel_fsid_t fsid = {}; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci fsnotify_foreach_obj_type(type) { 6328c2ecf20Sopenharmony_ci struct fsnotify_mark_connector *conn; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (!fsnotify_iter_should_report_type(iter_info, type)) 6358c2ecf20Sopenharmony_ci continue; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci conn = READ_ONCE(iter_info->marks[type]->connector); 6388c2ecf20Sopenharmony_ci /* Mark is just getting destroyed or created? */ 6398c2ecf20Sopenharmony_ci if (!conn) 6408c2ecf20Sopenharmony_ci continue; 6418c2ecf20Sopenharmony_ci if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID)) 6428c2ecf20Sopenharmony_ci continue; 6438c2ecf20Sopenharmony_ci /* Pairs with smp_wmb() in fsnotify_add_mark_list() */ 6448c2ecf20Sopenharmony_ci smp_rmb(); 6458c2ecf20Sopenharmony_ci fsid = conn->fsid; 6468c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1])) 6478c2ecf20Sopenharmony_ci continue; 6488c2ecf20Sopenharmony_ci return fsid; 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return fsid; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic int fanotify_handle_event(struct fsnotify_group *group, u32 mask, 6558c2ecf20Sopenharmony_ci const void *data, int data_type, 6568c2ecf20Sopenharmony_ci struct inode *dir, 6578c2ecf20Sopenharmony_ci const struct qstr *file_name, u32 cookie, 6588c2ecf20Sopenharmony_ci struct fsnotify_iter_info *iter_info) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci int ret = 0; 6618c2ecf20Sopenharmony_ci struct fanotify_event *event; 6628c2ecf20Sopenharmony_ci struct fsnotify_event *fsn_event; 6638c2ecf20Sopenharmony_ci __kernel_fsid_t fsid = {}; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); 6668c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); 6678c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_ATTRIB != FS_ATTRIB); 6688c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); 6698c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE); 6708c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_OPEN != FS_OPEN); 6718c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_MOVED_TO != FS_MOVED_TO); 6728c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_MOVED_FROM != FS_MOVED_FROM); 6738c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_CREATE != FS_CREATE); 6748c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_DELETE != FS_DELETE); 6758c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_DELETE_SELF != FS_DELETE_SELF); 6768c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_MOVE_SELF != FS_MOVE_SELF); 6778c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); 6788c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); 6798c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM); 6808c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM); 6818c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR); 6828c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC); 6838c2ecf20Sopenharmony_ci BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci mask = fanotify_group_event_mask(group, iter_info, mask, data, 6888c2ecf20Sopenharmony_ci data_type, dir); 6898c2ecf20Sopenharmony_ci if (!mask) 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci pr_debug("%s: group=%p mask=%x\n", __func__, group, mask); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (fanotify_is_perm_event(mask)) { 6958c2ecf20Sopenharmony_ci /* 6968c2ecf20Sopenharmony_ci * fsnotify_prepare_user_wait() fails if we race with mark 6978c2ecf20Sopenharmony_ci * deletion. Just let the operation pass in that case. 6988c2ecf20Sopenharmony_ci */ 6998c2ecf20Sopenharmony_ci if (!fsnotify_prepare_user_wait(iter_info)) 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) { 7048c2ecf20Sopenharmony_ci fsid = fanotify_get_fsid(iter_info); 7058c2ecf20Sopenharmony_ci /* Racing with mark destruction or creation? */ 7068c2ecf20Sopenharmony_ci if (!fsid.val[0] && !fsid.val[1]) 7078c2ecf20Sopenharmony_ci return 0; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci event = fanotify_alloc_event(group, mask, data, data_type, dir, 7118c2ecf20Sopenharmony_ci file_name, &fsid); 7128c2ecf20Sopenharmony_ci ret = -ENOMEM; 7138c2ecf20Sopenharmony_ci if (unlikely(!event)) { 7148c2ecf20Sopenharmony_ci /* 7158c2ecf20Sopenharmony_ci * We don't queue overflow events for permission events as 7168c2ecf20Sopenharmony_ci * there the access is denied and so no event is in fact lost. 7178c2ecf20Sopenharmony_ci */ 7188c2ecf20Sopenharmony_ci if (!fanotify_is_perm_event(mask)) 7198c2ecf20Sopenharmony_ci fsnotify_queue_overflow(group); 7208c2ecf20Sopenharmony_ci goto finish; 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci fsn_event = &event->fse; 7248c2ecf20Sopenharmony_ci ret = fsnotify_add_event(group, fsn_event, fanotify_merge); 7258c2ecf20Sopenharmony_ci if (ret) { 7268c2ecf20Sopenharmony_ci /* Permission events shouldn't be merged */ 7278c2ecf20Sopenharmony_ci BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS); 7288c2ecf20Sopenharmony_ci /* Our event wasn't used in the end. Free it. */ 7298c2ecf20Sopenharmony_ci fsnotify_destroy_event(group, fsn_event); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ret = 0; 7328c2ecf20Sopenharmony_ci } else if (fanotify_is_perm_event(mask)) { 7338c2ecf20Sopenharmony_ci ret = fanotify_get_response(group, FANOTIFY_PERM(event), 7348c2ecf20Sopenharmony_ci iter_info); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_cifinish: 7378c2ecf20Sopenharmony_ci if (fanotify_is_perm_event(mask)) 7388c2ecf20Sopenharmony_ci fsnotify_finish_user_wait(iter_info); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci return ret; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic void fanotify_free_group_priv(struct fsnotify_group *group) 7448c2ecf20Sopenharmony_ci{ 7458c2ecf20Sopenharmony_ci struct user_struct *user; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci user = group->fanotify_data.user; 7488c2ecf20Sopenharmony_ci atomic_dec(&user->fanotify_listeners); 7498c2ecf20Sopenharmony_ci free_uid(user); 7508c2ecf20Sopenharmony_ci} 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_cistatic void fanotify_free_path_event(struct fanotify_event *event) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci path_put(fanotify_event_path(event)); 7558c2ecf20Sopenharmony_ci kmem_cache_free(fanotify_path_event_cachep, FANOTIFY_PE(event)); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic void fanotify_free_perm_event(struct fanotify_event *event) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci path_put(fanotify_event_path(event)); 7618c2ecf20Sopenharmony_ci kmem_cache_free(fanotify_perm_event_cachep, FANOTIFY_PERM(event)); 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic void fanotify_free_fid_event(struct fanotify_event *event) 7658c2ecf20Sopenharmony_ci{ 7668c2ecf20Sopenharmony_ci struct fanotify_fid_event *ffe = FANOTIFY_FE(event); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci if (fanotify_fh_has_ext_buf(&ffe->object_fh)) 7698c2ecf20Sopenharmony_ci kfree(fanotify_fh_ext_buf(&ffe->object_fh)); 7708c2ecf20Sopenharmony_ci kmem_cache_free(fanotify_fid_event_cachep, ffe); 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic void fanotify_free_name_event(struct fanotify_event *event) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci kfree(FANOTIFY_NE(event)); 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic void fanotify_free_event(struct fsnotify_event *fsn_event) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci struct fanotify_event *event; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci event = FANOTIFY_E(fsn_event); 7838c2ecf20Sopenharmony_ci put_pid(event->pid); 7848c2ecf20Sopenharmony_ci switch (event->type) { 7858c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_PATH: 7868c2ecf20Sopenharmony_ci fanotify_free_path_event(event); 7878c2ecf20Sopenharmony_ci break; 7888c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_PATH_PERM: 7898c2ecf20Sopenharmony_ci fanotify_free_perm_event(event); 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_FID: 7928c2ecf20Sopenharmony_ci fanotify_free_fid_event(event); 7938c2ecf20Sopenharmony_ci break; 7948c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_FID_NAME: 7958c2ecf20Sopenharmony_ci fanotify_free_name_event(event); 7968c2ecf20Sopenharmony_ci break; 7978c2ecf20Sopenharmony_ci case FANOTIFY_EVENT_TYPE_OVERFLOW: 7988c2ecf20Sopenharmony_ci kfree(event); 7998c2ecf20Sopenharmony_ci break; 8008c2ecf20Sopenharmony_ci default: 8018c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci} 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic void fanotify_free_mark(struct fsnotify_mark *fsn_mark) 8068c2ecf20Sopenharmony_ci{ 8078c2ecf20Sopenharmony_ci kmem_cache_free(fanotify_mark_cache, fsn_mark); 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ciconst struct fsnotify_ops fanotify_fsnotify_ops = { 8118c2ecf20Sopenharmony_ci .handle_event = fanotify_handle_event, 8128c2ecf20Sopenharmony_ci .free_group_priv = fanotify_free_group_priv, 8138c2ecf20Sopenharmony_ci .free_event = fanotify_free_event, 8148c2ecf20Sopenharmony_ci .free_mark = fanotify_free_mark, 8158c2ecf20Sopenharmony_ci}; 816