Commit | Line | Data |
---|---|---|
3e0a4e85 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
63c882a0 EP |
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 rewriten to make use of the fsnotify infrastructure | |
63c882a0 EP |
14 | */ |
15 | ||
8c1934c8 | 16 | #include <linux/dcache.h> /* d_unlinked */ |
63c882a0 EP |
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> | |
b3b38d84 | 23 | #include <linux/sched.h> |
5b825c3a | 24 | #include <linux/sched/user.h> |
d46eb14b | 25 | #include <linux/sched/mm.h> |
63c882a0 EP |
26 | |
27 | #include "inotify.h" | |
28 | ||
74766bbf | 29 | /* |
7053aee2 | 30 | * Check if 2 events contain the same information. |
74766bbf | 31 | */ |
7053aee2 JK |
32 | static bool event_compare(struct fsnotify_event *old_fsn, |
33 | struct fsnotify_event *new_fsn) | |
74766bbf | 34 | { |
7053aee2 JK |
35 | struct inotify_event_info *old, *new; |
36 | ||
7053aee2 JK |
37 | old = INOTIFY_E(old_fsn); |
38 | new = INOTIFY_E(new_fsn); | |
a0a92d26 AG |
39 | if (old->mask & FS_IN_IGNORED) |
40 | return false; | |
41 | if ((old->mask == new->mask) && | |
956235af | 42 | (old->wd == new->wd) && |
7053aee2 JK |
43 | (old->name_len == new->name_len) && |
44 | (!old->name_len || !strcmp(old->name, new->name))) | |
45 | return true; | |
74766bbf EP |
46 | return false; |
47 | } | |
48 | ||
94e00d28 AG |
49 | static int inotify_merge(struct fsnotify_group *group, |
50 | struct fsnotify_event *event) | |
74766bbf | 51 | { |
94e00d28 | 52 | struct list_head *list = &group->notification_list; |
74766bbf | 53 | struct fsnotify_event *last_event; |
74766bbf | 54 | |
7053aee2 | 55 | last_event = list_entry(list->prev, struct fsnotify_event, list); |
83c0e1b4 | 56 | return event_compare(last_event, event); |
74766bbf EP |
57 | } |
58 | ||
1a2620a9 AG |
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) | |
63c882a0 | 62 | { |
000285de | 63 | struct inotify_inode_mark *i_mark; |
7053aee2 | 64 | struct inotify_event_info *event; |
7053aee2 | 65 | struct fsnotify_event *fsn_event; |
1a2620a9 | 66 | struct fsnotify_group *group = inode_mark->group; |
83c0e1b4 | 67 | int ret; |
c915d8f5 | 68 | int len = 0, wd; |
7053aee2 | 69 | int alloc_len = sizeof(struct inotify_event_info); |
b87d8cef | 70 | struct mem_cgroup *old_memcg; |
63c882a0 | 71 | |
1a2620a9 AG |
72 | if (name) { |
73 | len = name->len; | |
7053aee2 JK |
74 | alloc_len += len + 1; |
75 | } | |
5ba08e2e | 76 | |
b54cecf5 | 77 | pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark, |
7053aee2 | 78 | mask); |
63c882a0 | 79 | |
ce8f76fb | 80 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
000285de | 81 | fsn_mark); |
63c882a0 | 82 | |
c915d8f5 JK |
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; | |
ec165450 SB |
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 | */ | |
b87d8cef | 95 | old_memcg = set_active_memcg(group->memcg); |
ec165450 | 96 | event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); |
b87d8cef | 97 | set_active_memcg(old_memcg); |
d46eb14b | 98 | |
7b1f6417 JK |
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); | |
63c882a0 | 105 | return -ENOMEM; |
7b1f6417 | 106 | } |
63c882a0 | 107 | |
0a20df7e AG |
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 | ||
7053aee2 | 117 | fsn_event = &event->fse; |
8988f11a | 118 | fsnotify_init_event(fsn_event); |
a0a92d26 | 119 | event->mask = mask; |
c915d8f5 | 120 | event->wd = wd; |
45a22f4c | 121 | event->sync_cookie = cookie; |
7053aee2 JK |
122 | event->name_len = len; |
123 | if (len) | |
1a2620a9 | 124 | strcpy(event->name, name->name); |
63c882a0 | 125 | |
1ad03c3a | 126 | ret = fsnotify_add_event(group, fsn_event, inotify_merge); |
83c0e1b4 | 127 | if (ret) { |
7053aee2 JK |
128 | /* Our event wasn't used in the end. Free it. */ |
129 | fsnotify_destroy_event(group, fsn_event); | |
eef3a116 | 130 | } |
63c882a0 | 131 | |
38035c04 | 132 | if (inode_mark->flags & FSNOTIFY_MARK_FLAG_IN_ONESHOT) |
e2a29943 | 133 | fsnotify_destroy_mark(inode_mark, group); |
63c882a0 | 134 | |
83c0e1b4 | 135 | return 0; |
63c882a0 EP |
136 | } |
137 | ||
000285de | 138 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
63c882a0 | 139 | { |
000285de | 140 | inotify_ignored_and_remove_idr(fsn_mark, group); |
63c882a0 EP |
141 | } |
142 | ||
cf437426 EP |
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 | */ | |
63c882a0 EP |
150 | static int idr_callback(int id, void *p, void *data) |
151 | { | |
000285de EP |
152 | struct fsnotify_mark *fsn_mark; |
153 | struct inotify_inode_mark *i_mark; | |
cf437426 EP |
154 | static bool warned = false; |
155 | ||
156 | if (warned) | |
157 | return 0; | |
158 | ||
976ae32b | 159 | warned = true; |
000285de EP |
160 | fsn_mark = p; |
161 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | |
cf437426 | 162 | |
000285de | 163 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " |
cf437426 EP |
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 | */ | |
000285de | 172 | if (fsn_mark) |
25c829af JK |
173 | printk(KERN_WARNING "fsn_mark->group=%p wd=%d\n", |
174 | fsn_mark->group, i_mark->wd); | |
63c882a0 EP |
175 | return 0; |
176 | } | |
177 | ||
178 | static void inotify_free_group_priv(struct fsnotify_group *group) | |
179 | { | |
25985edc | 180 | /* ideally the idr is empty and we won't hit the BUG in the callback */ |
cf437426 | 181 | idr_for_each(&group->inotify_data.idr, idr_callback, group); |
63c882a0 | 182 | idr_destroy(&group->inotify_data.idr); |
1cce1eea NB |
183 | if (group->inotify_data.ucounts) |
184 | dec_inotify_instances(group->inotify_data.ucounts); | |
63c882a0 EP |
185 | } |
186 | ||
330ae77d GKB |
187 | static void inotify_free_event(struct fsnotify_group *group, |
188 | struct fsnotify_event *fsn_event) | |
63c882a0 | 189 | { |
7053aee2 | 190 | kfree(INOTIFY_E(fsn_event)); |
63c882a0 EP |
191 | } |
192 | ||
054c636e JK |
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(inotify_inode_mark_cachep, i_mark); | |
201 | } | |
202 | ||
63c882a0 | 203 | const struct fsnotify_ops inotify_fsnotify_ops = { |
1a2620a9 | 204 | .handle_inode_event = inotify_handle_inode_event, |
63c882a0 | 205 | .free_group_priv = inotify_free_group_priv, |
7053aee2 | 206 | .free_event = inotify_free_event, |
63c882a0 | 207 | .freeing_mark = inotify_freeing_mark, |
054c636e | 208 | .free_mark = inotify_free_mark, |
63c882a0 | 209 | }; |