]>
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) && | |
7053aee2 JK |
42 | (old_fsn->inode == new_fsn->inode) && |
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 | ||
83c0e1b4 JK |
49 | static int inotify_merge(struct list_head *list, |
50 | struct fsnotify_event *event) | |
74766bbf | 51 | { |
74766bbf | 52 | struct fsnotify_event *last_event; |
74766bbf | 53 | |
7053aee2 | 54 | last_event = list_entry(list->prev, struct fsnotify_event, list); |
83c0e1b4 | 55 | return event_compare(last_event, event); |
74766bbf EP |
56 | } |
57 | ||
7053aee2 JK |
58 | int inotify_handle_event(struct fsnotify_group *group, |
59 | struct inode *inode, | |
3cd5eca8 | 60 | u32 mask, const void *data, int data_type, |
e43e9c33 | 61 | const struct qstr *file_name, u32 cookie, |
9385a84d | 62 | struct fsnotify_iter_info *iter_info) |
63c882a0 | 63 | { |
5b0457ad | 64 | struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info); |
000285de | 65 | struct inotify_inode_mark *i_mark; |
7053aee2 | 66 | struct inotify_event_info *event; |
7053aee2 | 67 | struct fsnotify_event *fsn_event; |
83c0e1b4 | 68 | int ret; |
7053aee2 JK |
69 | int len = 0; |
70 | int alloc_len = sizeof(struct inotify_event_info); | |
63c882a0 | 71 | |
5b0457ad AG |
72 | if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info))) |
73 | return 0; | |
ce8f76fb | 74 | |
83c4c4b0 JK |
75 | if ((inode_mark->mask & FS_EXCL_UNLINK) && |
76 | (data_type == FSNOTIFY_EVENT_PATH)) { | |
3cd5eca8 | 77 | const struct path *path = data; |
83c4c4b0 JK |
78 | |
79 | if (d_unlinked(path->dentry)) | |
80 | return 0; | |
81 | } | |
7053aee2 | 82 | if (file_name) { |
ce163918 | 83 | len = file_name->len; |
7053aee2 JK |
84 | alloc_len += len + 1; |
85 | } | |
5ba08e2e | 86 | |
7053aee2 JK |
87 | pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, |
88 | mask); | |
63c882a0 | 89 | |
ce8f76fb | 90 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
000285de | 91 | fsn_mark); |
63c882a0 | 92 | |
d46eb14b SB |
93 | /* Whoever is interested in the event, pays for the allocation. */ |
94 | memalloc_use_memcg(group->memcg); | |
95 | event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT); | |
96 | memalloc_unuse_memcg(); | |
97 | ||
7b1f6417 JK |
98 | if (unlikely(!event)) { |
99 | /* | |
100 | * Treat lost event due to ENOMEM the same way as queue | |
101 | * overflow to let userspace know event was lost. | |
102 | */ | |
103 | fsnotify_queue_overflow(group); | |
63c882a0 | 104 | return -ENOMEM; |
7b1f6417 | 105 | } |
63c882a0 | 106 | |
0a20df7e AG |
107 | /* |
108 | * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events | |
109 | * for fanotify. inotify never reported IN_ISDIR with those events. | |
110 | * It looks like an oversight, but to avoid the risk of breaking | |
111 | * existing inotify programs, mask the flag out from those events. | |
112 | */ | |
113 | if (mask & (IN_MOVE_SELF | IN_DELETE_SELF)) | |
114 | mask &= ~IN_ISDIR; | |
115 | ||
7053aee2 | 116 | fsn_event = &event->fse; |
a0a92d26 AG |
117 | fsnotify_init_event(fsn_event, inode); |
118 | event->mask = mask; | |
7053aee2 | 119 | event->wd = i_mark->wd; |
45a22f4c | 120 | event->sync_cookie = cookie; |
7053aee2 JK |
121 | event->name_len = len; |
122 | if (len) | |
e43e9c33 | 123 | strcpy(event->name, file_name->name); |
63c882a0 | 124 | |
8ba8fa91 | 125 | ret = fsnotify_add_event(group, fsn_event, inotify_merge); |
83c0e1b4 | 126 | if (ret) { |
7053aee2 JK |
127 | /* Our event wasn't used in the end. Free it. */ |
128 | fsnotify_destroy_event(group, fsn_event); | |
eef3a116 | 129 | } |
63c882a0 | 130 | |
ce8f76fb | 131 | if (inode_mark->mask & IN_ONESHOT) |
e2a29943 | 132 | fsnotify_destroy_mark(inode_mark, group); |
63c882a0 | 133 | |
83c0e1b4 | 134 | return 0; |
63c882a0 EP |
135 | } |
136 | ||
000285de | 137 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
63c882a0 | 138 | { |
000285de | 139 | inotify_ignored_and_remove_idr(fsn_mark, group); |
63c882a0 EP |
140 | } |
141 | ||
cf437426 EP |
142 | /* |
143 | * This is NEVER supposed to be called. Inotify marks should either have been | |
144 | * removed from the idr when the watch was removed or in the | |
145 | * fsnotify_destroy_mark_by_group() call when the inotify instance was being | |
146 | * torn down. This is only called if the idr is about to be freed but there | |
147 | * are still marks in it. | |
148 | */ | |
63c882a0 EP |
149 | static int idr_callback(int id, void *p, void *data) |
150 | { | |
000285de EP |
151 | struct fsnotify_mark *fsn_mark; |
152 | struct inotify_inode_mark *i_mark; | |
cf437426 EP |
153 | static bool warned = false; |
154 | ||
155 | if (warned) | |
156 | return 0; | |
157 | ||
976ae32b | 158 | warned = true; |
000285de EP |
159 | fsn_mark = p; |
160 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | |
cf437426 | 161 | |
000285de | 162 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " |
cf437426 EP |
163 | "idr. Probably leaking memory\n", id, p, data); |
164 | ||
165 | /* | |
166 | * I'm taking the liberty of assuming that the mark in question is a | |
167 | * valid address and I'm dereferencing it. This might help to figure | |
168 | * out why we got here and the panic is no worse than the original | |
169 | * BUG() that was here. | |
170 | */ | |
000285de | 171 | if (fsn_mark) |
25c829af JK |
172 | printk(KERN_WARNING "fsn_mark->group=%p wd=%d\n", |
173 | fsn_mark->group, i_mark->wd); | |
63c882a0 EP |
174 | return 0; |
175 | } | |
176 | ||
177 | static void inotify_free_group_priv(struct fsnotify_group *group) | |
178 | { | |
25985edc | 179 | /* ideally the idr is empty and we won't hit the BUG in the callback */ |
cf437426 | 180 | idr_for_each(&group->inotify_data.idr, idr_callback, group); |
63c882a0 | 181 | idr_destroy(&group->inotify_data.idr); |
1cce1eea NB |
182 | if (group->inotify_data.ucounts) |
183 | dec_inotify_instances(group->inotify_data.ucounts); | |
63c882a0 EP |
184 | } |
185 | ||
7053aee2 | 186 | static void inotify_free_event(struct fsnotify_event *fsn_event) |
63c882a0 | 187 | { |
7053aee2 | 188 | kfree(INOTIFY_E(fsn_event)); |
63c882a0 EP |
189 | } |
190 | ||
054c636e JK |
191 | /* ding dong the mark is dead */ |
192 | static void inotify_free_mark(struct fsnotify_mark *fsn_mark) | |
193 | { | |
194 | struct inotify_inode_mark *i_mark; | |
195 | ||
196 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | |
197 | ||
198 | kmem_cache_free(inotify_inode_mark_cachep, i_mark); | |
199 | } | |
200 | ||
63c882a0 EP |
201 | const struct fsnotify_ops inotify_fsnotify_ops = { |
202 | .handle_event = inotify_handle_event, | |
63c882a0 | 203 | .free_group_priv = inotify_free_group_priv, |
7053aee2 | 204 | .free_event = inotify_free_event, |
63c882a0 | 205 | .freeing_mark = inotify_freeing_mark, |
054c636e | 206 | .free_mark = inotify_free_mark, |
63c882a0 | 207 | }; |