| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | 
|---|
| 2 | /* | 
|---|
| 3 | * fs/inotify_user.c - inotify support for userspace | 
|---|
| 4 | * | 
|---|
| 5 | * Authors: | 
|---|
| 6 | *	John McCutchan	<ttb@tentacle.dhs.org> | 
|---|
| 7 | *	Robert Love	<rml@novell.com> | 
|---|
| 8 | * | 
|---|
| 9 | * Copyright (C) 2005 John McCutchan | 
|---|
| 10 | * Copyright 2006 Hewlett-Packard Development Company, L.P. | 
|---|
| 11 | * | 
|---|
| 12 | * Copyright (C) 2009 Eric Paris <Red Hat Inc> | 
|---|
| 13 | * inotify was largely rewritten to make use of the fsnotify infrastructure | 
|---|
| 14 | */ | 
|---|
| 15 |  | 
|---|
| 16 | #include <linux/dcache.h> /* d_unlinked */ | 
|---|
| 17 | #include <linux/fs.h> /* struct inode */ | 
|---|
| 18 | #include <linux/fsnotify_backend.h> | 
|---|
| 19 | #include <linux/inotify.h> | 
|---|
| 20 | #include <linux/path.h> /* struct path */ | 
|---|
| 21 | #include <linux/slab.h> /* kmem_* */ | 
|---|
| 22 | #include <linux/types.h> | 
|---|
| 23 | #include <linux/sched.h> | 
|---|
| 24 | #include <linux/sched/user.h> | 
|---|
| 25 | #include <linux/sched/mm.h> | 
|---|
| 26 |  | 
|---|
| 27 | #include "inotify.h" | 
|---|
| 28 |  | 
|---|
| 29 | /* | 
|---|
| 30 | * Check if 2 events contain the same information. | 
|---|
| 31 | */ | 
|---|
| 32 | static bool event_compare(struct fsnotify_event *old_fsn, | 
|---|
| 33 | struct fsnotify_event *new_fsn) | 
|---|
| 34 | { | 
|---|
| 35 | struct inotify_event_info *old, *new; | 
|---|
| 36 |  | 
|---|
| 37 | old = INOTIFY_E(fse: old_fsn); | 
|---|
| 38 | new = INOTIFY_E(fse: new_fsn); | 
|---|
| 39 | if (old->mask & FS_IN_IGNORED) | 
|---|
| 40 | return false; | 
|---|
| 41 | if ((old->mask == new->mask) && | 
|---|
| 42 | (old->wd == new->wd) && | 
|---|
| 43 | (old->name_len == new->name_len) && | 
|---|
| 44 | (!old->name_len || !strcmp(old->name, new->name))) | 
|---|
| 45 | return true; | 
|---|
| 46 | return false; | 
|---|
| 47 | } | 
|---|
| 48 |  | 
|---|
| 49 | static int inotify_merge(struct fsnotify_group *group, | 
|---|
| 50 | struct fsnotify_event *event) | 
|---|
| 51 | { | 
|---|
| 52 | struct list_head *list = &group->notification_list; | 
|---|
| 53 | struct fsnotify_event *last_event; | 
|---|
| 54 |  | 
|---|
| 55 | last_event = list_entry(list->prev, struct fsnotify_event, list); | 
|---|
| 56 | return event_compare(old_fsn: last_event, new_fsn: event); | 
|---|
| 57 | } | 
|---|
| 58 |  | 
|---|
| 59 | int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask, | 
|---|
| 60 | struct inode *inode, struct inode *dir, | 
|---|
| 61 | const struct qstr *name, u32 cookie) | 
|---|
| 62 | { | 
|---|
| 63 | struct inotify_inode_mark *i_mark; | 
|---|
| 64 | struct inotify_event_info *event; | 
|---|
| 65 | struct fsnotify_event *fsn_event; | 
|---|
| 66 | struct fsnotify_group *group = inode_mark->group; | 
|---|
| 67 | int ret; | 
|---|
| 68 | int len = 0, wd; | 
|---|
| 69 | int alloc_len = sizeof(struct inotify_event_info); | 
|---|
| 70 | struct mem_cgroup *old_memcg; | 
|---|
| 71 |  | 
|---|
| 72 | if (name) { | 
|---|
| 73 | len = name->len; | 
|---|
| 74 | alloc_len += len + 1; | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | pr_debug( "%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark, | 
|---|
| 78 | mask); | 
|---|
| 79 |  | 
|---|
| 80 | i_mark = container_of(inode_mark, struct inotify_inode_mark, | 
|---|
| 81 | fsn_mark); | 
|---|
| 82 |  | 
|---|
| 83 | /* | 
|---|
| 84 | * We can be racing with mark being detached. Don't report event with | 
|---|
| 85 | * invalid wd. | 
|---|
| 86 | */ | 
|---|
| 87 | wd = READ_ONCE(i_mark->wd); | 
|---|
| 88 | if (wd == -1) | 
|---|
| 89 | return 0; | 
|---|
| 90 | /* | 
|---|
| 91 | * Whoever is interested in the event, pays for the allocation. Do not | 
|---|
| 92 | * trigger OOM killer in the target monitoring memcg as it may have | 
|---|
| 93 | * security repercussion. | 
|---|
| 94 | */ | 
|---|
| 95 | old_memcg = set_active_memcg(group->memcg); | 
|---|
| 96 | event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); | 
|---|
| 97 | set_active_memcg(old_memcg); | 
|---|
| 98 |  | 
|---|
| 99 | if (unlikely(!event)) { | 
|---|
| 100 | /* | 
|---|
| 101 | * Treat lost event due to ENOMEM the same way as queue | 
|---|
| 102 | * overflow to let userspace know event was lost. | 
|---|
| 103 | */ | 
|---|
| 104 | fsnotify_queue_overflow(group); | 
|---|
| 105 | return -ENOMEM; | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | /* | 
|---|
| 109 | * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events | 
|---|
| 110 | * for fanotify. inotify never reported IN_ISDIR with those events. | 
|---|
| 111 | * It looks like an oversight, but to avoid the risk of breaking | 
|---|
| 112 | * existing inotify programs, mask the flag out from those events. | 
|---|
| 113 | */ | 
|---|
| 114 | if (mask & (IN_MOVE_SELF | IN_DELETE_SELF)) | 
|---|
| 115 | mask &= ~IN_ISDIR; | 
|---|
| 116 |  | 
|---|
| 117 | fsn_event = &event->fse; | 
|---|
| 118 | fsnotify_init_event(event: fsn_event); | 
|---|
| 119 | event->mask = mask; | 
|---|
| 120 | event->wd = wd; | 
|---|
| 121 | event->sync_cookie = cookie; | 
|---|
| 122 | event->name_len = len; | 
|---|
| 123 | if (len) | 
|---|
| 124 | strscpy(event->name, name->name, event->name_len + 1); | 
|---|
| 125 |  | 
|---|
| 126 | ret = fsnotify_add_event(group, event: fsn_event, merge: inotify_merge); | 
|---|
| 127 | if (ret) { | 
|---|
| 128 | /* Our event wasn't used in the end. Free it. */ | 
|---|
| 129 | fsnotify_destroy_event(group, event: fsn_event); | 
|---|
| 130 | } | 
|---|
| 131 |  | 
|---|
| 132 | if (inode_mark->flags & FSNOTIFY_MARK_FLAG_IN_ONESHOT) | 
|---|
| 133 | fsnotify_destroy_mark(mark: inode_mark, group); | 
|---|
| 134 |  | 
|---|
| 135 | return 0; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) | 
|---|
| 139 | { | 
|---|
| 140 | inotify_ignored_and_remove_idr(fsn_mark, group); | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | /* | 
|---|
| 144 | * This is NEVER supposed to be called.  Inotify marks should either have been | 
|---|
| 145 | * removed from the idr when the watch was removed or in the | 
|---|
| 146 | * fsnotify_destroy_mark_by_group() call when the inotify instance was being | 
|---|
| 147 | * torn down.  This is only called if the idr is about to be freed but there | 
|---|
| 148 | * are still marks in it. | 
|---|
| 149 | */ | 
|---|
| 150 | static int idr_callback(int id, void *p, void *data) | 
|---|
| 151 | { | 
|---|
| 152 | struct fsnotify_mark *fsn_mark; | 
|---|
| 153 | struct inotify_inode_mark *i_mark; | 
|---|
| 154 | static bool warned = false; | 
|---|
| 155 |  | 
|---|
| 156 | if (warned) | 
|---|
| 157 | return 0; | 
|---|
| 158 |  | 
|---|
| 159 | warned = true; | 
|---|
| 160 | fsn_mark = p; | 
|---|
| 161 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | 
|---|
| 162 |  | 
|---|
| 163 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " | 
|---|
| 164 | "idr.  Probably leaking memory\n", id, p, data); | 
|---|
| 165 |  | 
|---|
| 166 | /* | 
|---|
| 167 | * I'm taking the liberty of assuming that the mark in question is a | 
|---|
| 168 | * valid address and I'm dereferencing it.  This might help to figure | 
|---|
| 169 | * out why we got here and the panic is no worse than the original | 
|---|
| 170 | * BUG() that was here. | 
|---|
| 171 | */ | 
|---|
| 172 | if (fsn_mark) | 
|---|
| 173 | printk(KERN_WARNING "fsn_mark->group=%p wd=%d\n", | 
|---|
| 174 | fsn_mark->group, i_mark->wd); | 
|---|
| 175 | return 0; | 
|---|
| 176 | } | 
|---|
| 177 |  | 
|---|
| 178 | static void inotify_free_group_priv(struct fsnotify_group *group) | 
|---|
| 179 | { | 
|---|
| 180 | /* ideally the idr is empty and we won't hit the BUG in the callback */ | 
|---|
| 181 | idr_for_each(&group->inotify_data.idr, fn: idr_callback, data: group); | 
|---|
| 182 | idr_destroy(&group->inotify_data.idr); | 
|---|
| 183 | if (group->inotify_data.ucounts) | 
|---|
| 184 | dec_inotify_instances(ucounts: group->inotify_data.ucounts); | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | static void inotify_free_event(struct fsnotify_group *group, | 
|---|
| 188 | struct fsnotify_event *fsn_event) | 
|---|
| 189 | { | 
|---|
| 190 | kfree(objp: INOTIFY_E(fse: fsn_event)); | 
|---|
| 191 | } | 
|---|
| 192 |  | 
|---|
| 193 | /* ding dong the mark is dead */ | 
|---|
| 194 | static void inotify_free_mark(struct fsnotify_mark *fsn_mark) | 
|---|
| 195 | { | 
|---|
| 196 | struct inotify_inode_mark *i_mark; | 
|---|
| 197 |  | 
|---|
| 198 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | 
|---|
| 199 |  | 
|---|
| 200 | kmem_cache_free(s: inotify_inode_mark_cachep, objp: i_mark); | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | const struct fsnotify_ops inotify_fsnotify_ops = { | 
|---|
| 204 | .handle_inode_event = inotify_handle_inode_event, | 
|---|
| 205 | .free_group_priv = inotify_free_group_priv, | 
|---|
| 206 | .free_event = inotify_free_event, | 
|---|
| 207 | .freeing_mark = inotify_freeing_mark, | 
|---|
| 208 | .free_mark = inotify_free_mark, | 
|---|
| 209 | }; | 
|---|
| 210 |  | 
|---|