]>
Commit | Line | Data |
---|---|---|
63c882a0 EP |
1 | /* |
2 | * fs/inotify_user.c - inotify support for userspace | |
3 | * | |
4 | * Authors: | |
5 | * John McCutchan <ttb@tentacle.dhs.org> | |
6 | * Robert Love <rml@novell.com> | |
7 | * | |
8 | * Copyright (C) 2005 John McCutchan | |
9 | * Copyright 2006 Hewlett-Packard Development Company, L.P. | |
10 | * | |
11 | * Copyright (C) 2009 Eric Paris <Red Hat Inc> | |
12 | * inotify was largely rewriten to make use of the fsnotify infrastructure | |
13 | * | |
14 | * This program is free software; you can redistribute it and/or modify it | |
15 | * under the terms of the GNU General Public License as published by the | |
16 | * Free Software Foundation; either version 2, or (at your option) any | |
17 | * later version. | |
18 | * | |
19 | * This program is distributed in the hope that it will be useful, but | |
20 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
22 | * General Public License for more details. | |
23 | */ | |
24 | ||
8c1934c8 | 25 | #include <linux/dcache.h> /* d_unlinked */ |
63c882a0 EP |
26 | #include <linux/fs.h> /* struct inode */ |
27 | #include <linux/fsnotify_backend.h> | |
28 | #include <linux/inotify.h> | |
29 | #include <linux/path.h> /* struct path */ | |
30 | #include <linux/slab.h> /* kmem_* */ | |
31 | #include <linux/types.h> | |
b3b38d84 | 32 | #include <linux/sched.h> |
5b825c3a | 33 | #include <linux/sched/user.h> |
63c882a0 EP |
34 | |
35 | #include "inotify.h" | |
36 | ||
74766bbf | 37 | /* |
7053aee2 | 38 | * Check if 2 events contain the same information. |
74766bbf | 39 | */ |
7053aee2 JK |
40 | static bool event_compare(struct fsnotify_event *old_fsn, |
41 | struct fsnotify_event *new_fsn) | |
74766bbf | 42 | { |
7053aee2 JK |
43 | struct inotify_event_info *old, *new; |
44 | ||
45 | if (old_fsn->mask & FS_IN_IGNORED) | |
46 | return false; | |
47 | old = INOTIFY_E(old_fsn); | |
48 | new = INOTIFY_E(new_fsn); | |
49 | if ((old_fsn->mask == new_fsn->mask) && | |
50 | (old_fsn->inode == new_fsn->inode) && | |
51 | (old->name_len == new->name_len) && | |
52 | (!old->name_len || !strcmp(old->name, new->name))) | |
53 | return true; | |
74766bbf EP |
54 | return false; |
55 | } | |
56 | ||
83c0e1b4 JK |
57 | static int inotify_merge(struct list_head *list, |
58 | struct fsnotify_event *event) | |
74766bbf | 59 | { |
74766bbf | 60 | struct fsnotify_event *last_event; |
74766bbf | 61 | |
7053aee2 | 62 | last_event = list_entry(list->prev, struct fsnotify_event, list); |
83c0e1b4 | 63 | return event_compare(last_event, event); |
74766bbf EP |
64 | } |
65 | ||
7053aee2 JK |
66 | int inotify_handle_event(struct fsnotify_group *group, |
67 | struct inode *inode, | |
68 | struct fsnotify_mark *inode_mark, | |
69 | struct fsnotify_mark *vfsmount_mark, | |
3cd5eca8 | 70 | u32 mask, const void *data, int data_type, |
45a22f4c | 71 | const unsigned char *file_name, u32 cookie) |
63c882a0 | 72 | { |
000285de | 73 | struct inotify_inode_mark *i_mark; |
7053aee2 | 74 | struct inotify_event_info *event; |
7053aee2 | 75 | struct fsnotify_event *fsn_event; |
83c0e1b4 | 76 | int ret; |
7053aee2 JK |
77 | int len = 0; |
78 | int alloc_len = sizeof(struct inotify_event_info); | |
63c882a0 | 79 | |
ce8f76fb EP |
80 | BUG_ON(vfsmount_mark); |
81 | ||
83c4c4b0 JK |
82 | if ((inode_mark->mask & FS_EXCL_UNLINK) && |
83 | (data_type == FSNOTIFY_EVENT_PATH)) { | |
3cd5eca8 | 84 | const struct path *path = data; |
83c4c4b0 JK |
85 | |
86 | if (d_unlinked(path->dentry)) | |
87 | return 0; | |
88 | } | |
7053aee2 JK |
89 | if (file_name) { |
90 | len = strlen(file_name); | |
91 | alloc_len += len + 1; | |
92 | } | |
5ba08e2e | 93 | |
7053aee2 JK |
94 | pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, |
95 | mask); | |
63c882a0 | 96 | |
ce8f76fb | 97 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
000285de | 98 | fsn_mark); |
63c882a0 | 99 | |
7053aee2 JK |
100 | event = kmalloc(alloc_len, GFP_KERNEL); |
101 | if (unlikely(!event)) | |
63c882a0 EP |
102 | return -ENOMEM; |
103 | ||
7053aee2 JK |
104 | fsn_event = &event->fse; |
105 | fsnotify_init_event(fsn_event, inode, mask); | |
106 | event->wd = i_mark->wd; | |
45a22f4c | 107 | event->sync_cookie = cookie; |
7053aee2 JK |
108 | event->name_len = len; |
109 | if (len) | |
110 | strcpy(event->name, file_name); | |
63c882a0 | 111 | |
8ba8fa91 | 112 | ret = fsnotify_add_event(group, fsn_event, inotify_merge); |
83c0e1b4 | 113 | if (ret) { |
7053aee2 JK |
114 | /* Our event wasn't used in the end. Free it. */ |
115 | fsnotify_destroy_event(group, fsn_event); | |
eef3a116 | 116 | } |
63c882a0 | 117 | |
ce8f76fb | 118 | if (inode_mark->mask & IN_ONESHOT) |
e2a29943 | 119 | fsnotify_destroy_mark(inode_mark, group); |
63c882a0 | 120 | |
83c0e1b4 | 121 | return 0; |
63c882a0 EP |
122 | } |
123 | ||
000285de | 124 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
63c882a0 | 125 | { |
000285de | 126 | inotify_ignored_and_remove_idr(fsn_mark, group); |
63c882a0 EP |
127 | } |
128 | ||
cf437426 EP |
129 | /* |
130 | * This is NEVER supposed to be called. Inotify marks should either have been | |
131 | * removed from the idr when the watch was removed or in the | |
132 | * fsnotify_destroy_mark_by_group() call when the inotify instance was being | |
133 | * torn down. This is only called if the idr is about to be freed but there | |
134 | * are still marks in it. | |
135 | */ | |
63c882a0 EP |
136 | static int idr_callback(int id, void *p, void *data) |
137 | { | |
000285de EP |
138 | struct fsnotify_mark *fsn_mark; |
139 | struct inotify_inode_mark *i_mark; | |
cf437426 EP |
140 | static bool warned = false; |
141 | ||
142 | if (warned) | |
143 | return 0; | |
144 | ||
976ae32b | 145 | warned = true; |
000285de EP |
146 | fsn_mark = p; |
147 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | |
cf437426 | 148 | |
000285de | 149 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " |
cf437426 EP |
150 | "idr. Probably leaking memory\n", id, p, data); |
151 | ||
152 | /* | |
153 | * I'm taking the liberty of assuming that the mark in question is a | |
154 | * valid address and I'm dereferencing it. This might help to figure | |
155 | * out why we got here and the panic is no worse than the original | |
156 | * BUG() that was here. | |
157 | */ | |
000285de EP |
158 | if (fsn_mark) |
159 | printk(KERN_WARNING "fsn_mark->group=%p inode=%p wd=%d\n", | |
0809ab69 | 160 | fsn_mark->group, fsn_mark->inode, i_mark->wd); |
63c882a0 EP |
161 | return 0; |
162 | } | |
163 | ||
164 | static void inotify_free_group_priv(struct fsnotify_group *group) | |
165 | { | |
25985edc | 166 | /* ideally the idr is empty and we won't hit the BUG in the callback */ |
cf437426 | 167 | idr_for_each(&group->inotify_data.idr, idr_callback, group); |
63c882a0 | 168 | idr_destroy(&group->inotify_data.idr); |
1cce1eea NB |
169 | if (group->inotify_data.ucounts) |
170 | dec_inotify_instances(group->inotify_data.ucounts); | |
63c882a0 EP |
171 | } |
172 | ||
7053aee2 | 173 | static void inotify_free_event(struct fsnotify_event *fsn_event) |
63c882a0 | 174 | { |
7053aee2 | 175 | kfree(INOTIFY_E(fsn_event)); |
63c882a0 EP |
176 | } |
177 | ||
178 | const struct fsnotify_ops inotify_fsnotify_ops = { | |
179 | .handle_event = inotify_handle_event, | |
63c882a0 | 180 | .free_group_priv = inotify_free_group_priv, |
7053aee2 | 181 | .free_event = inotify_free_event, |
63c882a0 EP |
182 | .freeing_mark = inotify_freeing_mark, |
183 | }; |