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> |
63c882a0 EP |
33 | |
34 | #include "inotify.h" | |
35 | ||
74766bbf | 36 | /* |
7053aee2 | 37 | * Check if 2 events contain the same information. |
74766bbf | 38 | */ |
7053aee2 JK |
39 | static bool event_compare(struct fsnotify_event *old_fsn, |
40 | struct fsnotify_event *new_fsn) | |
74766bbf | 41 | { |
7053aee2 JK |
42 | struct inotify_event_info *old, *new; |
43 | ||
44 | if (old_fsn->mask & FS_IN_IGNORED) | |
45 | return false; | |
46 | old = INOTIFY_E(old_fsn); | |
47 | new = INOTIFY_E(new_fsn); | |
48 | if ((old_fsn->mask == new_fsn->mask) && | |
49 | (old_fsn->inode == new_fsn->inode) && | |
50 | (old->name_len == new->name_len) && | |
51 | (!old->name_len || !strcmp(old->name, new->name))) | |
52 | return true; | |
74766bbf EP |
53 | return false; |
54 | } | |
55 | ||
83c0e1b4 JK |
56 | static int inotify_merge(struct list_head *list, |
57 | struct fsnotify_event *event) | |
74766bbf | 58 | { |
74766bbf | 59 | struct fsnotify_event *last_event; |
74766bbf | 60 | |
7053aee2 | 61 | last_event = list_entry(list->prev, struct fsnotify_event, list); |
83c0e1b4 | 62 | return event_compare(last_event, event); |
74766bbf EP |
63 | } |
64 | ||
7053aee2 JK |
65 | int inotify_handle_event(struct fsnotify_group *group, |
66 | struct inode *inode, | |
67 | struct fsnotify_mark *inode_mark, | |
68 | struct fsnotify_mark *vfsmount_mark, | |
69 | u32 mask, void *data, int data_type, | |
45a22f4c | 70 | const unsigned char *file_name, u32 cookie) |
63c882a0 | 71 | { |
000285de | 72 | struct inotify_inode_mark *i_mark; |
7053aee2 | 73 | struct inotify_event_info *event; |
7053aee2 | 74 | struct fsnotify_event *fsn_event; |
83c0e1b4 | 75 | int ret; |
7053aee2 JK |
76 | int len = 0; |
77 | int alloc_len = sizeof(struct inotify_event_info); | |
63c882a0 | 78 | |
ce8f76fb EP |
79 | BUG_ON(vfsmount_mark); |
80 | ||
83c4c4b0 JK |
81 | if ((inode_mark->mask & FS_EXCL_UNLINK) && |
82 | (data_type == FSNOTIFY_EVENT_PATH)) { | |
83 | struct path *path = data; | |
84 | ||
85 | if (d_unlinked(path->dentry)) | |
86 | return 0; | |
87 | } | |
7053aee2 JK |
88 | if (file_name) { |
89 | len = strlen(file_name); | |
90 | alloc_len += len + 1; | |
91 | } | |
5ba08e2e | 92 | |
7053aee2 JK |
93 | pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, |
94 | mask); | |
63c882a0 | 95 | |
ce8f76fb | 96 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
000285de | 97 | fsn_mark); |
63c882a0 | 98 | |
7053aee2 JK |
99 | event = kmalloc(alloc_len, GFP_KERNEL); |
100 | if (unlikely(!event)) | |
63c882a0 EP |
101 | return -ENOMEM; |
102 | ||
7053aee2 JK |
103 | fsn_event = &event->fse; |
104 | fsnotify_init_event(fsn_event, inode, mask); | |
105 | event->wd = i_mark->wd; | |
45a22f4c | 106 | event->sync_cookie = cookie; |
7053aee2 JK |
107 | event->name_len = len; |
108 | if (len) | |
109 | strcpy(event->name, file_name); | |
63c882a0 | 110 | |
83c0e1b4 JK |
111 | ret = fsnotify_add_notify_event(group, fsn_event, inotify_merge); |
112 | if (ret) { | |
7053aee2 JK |
113 | /* Our event wasn't used in the end. Free it. */ |
114 | fsnotify_destroy_event(group, fsn_event); | |
eef3a116 | 115 | } |
63c882a0 | 116 | |
ce8f76fb | 117 | if (inode_mark->mask & IN_ONESHOT) |
e2a29943 | 118 | fsnotify_destroy_mark(inode_mark, group); |
63c882a0 | 119 | |
83c0e1b4 | 120 | return 0; |
63c882a0 EP |
121 | } |
122 | ||
000285de | 123 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
63c882a0 | 124 | { |
000285de | 125 | inotify_ignored_and_remove_idr(fsn_mark, group); |
63c882a0 EP |
126 | } |
127 | ||
cf437426 EP |
128 | /* |
129 | * This is NEVER supposed to be called. Inotify marks should either have been | |
130 | * removed from the idr when the watch was removed or in the | |
131 | * fsnotify_destroy_mark_by_group() call when the inotify instance was being | |
132 | * torn down. This is only called if the idr is about to be freed but there | |
133 | * are still marks in it. | |
134 | */ | |
63c882a0 EP |
135 | static int idr_callback(int id, void *p, void *data) |
136 | { | |
000285de EP |
137 | struct fsnotify_mark *fsn_mark; |
138 | struct inotify_inode_mark *i_mark; | |
cf437426 EP |
139 | static bool warned = false; |
140 | ||
141 | if (warned) | |
142 | return 0; | |
143 | ||
976ae32b | 144 | warned = true; |
000285de EP |
145 | fsn_mark = p; |
146 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | |
cf437426 | 147 | |
000285de | 148 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " |
cf437426 EP |
149 | "idr. Probably leaking memory\n", id, p, data); |
150 | ||
151 | /* | |
152 | * I'm taking the liberty of assuming that the mark in question is a | |
153 | * valid address and I'm dereferencing it. This might help to figure | |
154 | * out why we got here and the panic is no worse than the original | |
155 | * BUG() that was here. | |
156 | */ | |
000285de EP |
157 | if (fsn_mark) |
158 | printk(KERN_WARNING "fsn_mark->group=%p inode=%p wd=%d\n", | |
159 | fsn_mark->group, fsn_mark->i.inode, i_mark->wd); | |
63c882a0 EP |
160 | return 0; |
161 | } | |
162 | ||
163 | static void inotify_free_group_priv(struct fsnotify_group *group) | |
164 | { | |
25985edc | 165 | /* ideally the idr is empty and we won't hit the BUG in the callback */ |
cf437426 | 166 | idr_for_each(&group->inotify_data.idr, idr_callback, group); |
63c882a0 | 167 | idr_destroy(&group->inotify_data.idr); |
d0de4dc5 | 168 | atomic_dec(&group->inotify_data.user->inotify_devs); |
b3b38d84 | 169 | free_uid(group->inotify_data.user); |
63c882a0 EP |
170 | } |
171 | ||
7053aee2 | 172 | static void inotify_free_event(struct fsnotify_event *fsn_event) |
63c882a0 | 173 | { |
7053aee2 | 174 | kfree(INOTIFY_E(fsn_event)); |
63c882a0 EP |
175 | } |
176 | ||
177 | const struct fsnotify_ops inotify_fsnotify_ops = { | |
178 | .handle_event = inotify_handle_event, | |
63c882a0 | 179 | .free_group_priv = inotify_free_group_priv, |
7053aee2 | 180 | .free_event = inotify_free_event, |
63c882a0 EP |
181 | .freeing_mark = inotify_freeing_mark, |
182 | }; |