.. | .. |
---|
17 | 17 | #include <linux/slab.h> |
---|
18 | 18 | #include <linux/bug.h> |
---|
19 | 19 | |
---|
20 | | -/* Notify this dentry's parent about a child's events. */ |
---|
21 | | -static inline int fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask) |
---|
| 20 | +/* |
---|
| 21 | + * Notify this @dir inode about a change in a child directory entry. |
---|
| 22 | + * The directory entry may have turned positive or negative or its inode may |
---|
| 23 | + * have changed (i.e. renamed over). |
---|
| 24 | + * |
---|
| 25 | + * Unlike fsnotify_parent(), the event will be reported regardless of the |
---|
| 26 | + * FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only |
---|
| 27 | + * the child is interested and not the parent. |
---|
| 28 | + */ |
---|
| 29 | +static inline void fsnotify_name(struct inode *dir, __u32 mask, |
---|
| 30 | + struct inode *child, |
---|
| 31 | + const struct qstr *name, u32 cookie) |
---|
22 | 32 | { |
---|
23 | | - if (!dentry) |
---|
24 | | - dentry = path->dentry; |
---|
25 | | - |
---|
26 | | - return __fsnotify_parent(path, dentry, mask); |
---|
| 33 | + fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie); |
---|
27 | 34 | } |
---|
28 | 35 | |
---|
29 | | -/* simple call site for access decisions */ |
---|
30 | | -static inline int fsnotify_perm(struct file *file, int mask) |
---|
| 36 | +static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, |
---|
| 37 | + __u32 mask) |
---|
| 38 | +{ |
---|
| 39 | + fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0); |
---|
| 40 | +} |
---|
| 41 | + |
---|
| 42 | +static inline void fsnotify_inode(struct inode *inode, __u32 mask) |
---|
| 43 | +{ |
---|
| 44 | + if (S_ISDIR(inode->i_mode)) |
---|
| 45 | + mask |= FS_ISDIR; |
---|
| 46 | + |
---|
| 47 | + fsnotify(mask, inode, FSNOTIFY_EVENT_INODE, NULL, NULL, inode, 0); |
---|
| 48 | +} |
---|
| 49 | + |
---|
| 50 | +/* Notify this dentry's parent about a child's events. */ |
---|
| 51 | +static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, |
---|
| 52 | + const void *data, int data_type) |
---|
| 53 | +{ |
---|
| 54 | + struct inode *inode = d_inode(dentry); |
---|
| 55 | + |
---|
| 56 | + if (S_ISDIR(inode->i_mode)) { |
---|
| 57 | + mask |= FS_ISDIR; |
---|
| 58 | + |
---|
| 59 | + /* sb/mount marks are not interested in name of directory */ |
---|
| 60 | + if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) |
---|
| 61 | + goto notify_child; |
---|
| 62 | + } |
---|
| 63 | + |
---|
| 64 | + /* disconnected dentry cannot notify parent */ |
---|
| 65 | + if (IS_ROOT(dentry)) |
---|
| 66 | + goto notify_child; |
---|
| 67 | + |
---|
| 68 | + return __fsnotify_parent(dentry, mask, data, data_type); |
---|
| 69 | + |
---|
| 70 | +notify_child: |
---|
| 71 | + return fsnotify(mask, data, data_type, NULL, NULL, inode, 0); |
---|
| 72 | +} |
---|
| 73 | + |
---|
| 74 | +/* |
---|
| 75 | + * Simple wrappers to consolidate calls to fsnotify_parent() when an event |
---|
| 76 | + * is on a file/dentry. |
---|
| 77 | + */ |
---|
| 78 | +static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask) |
---|
| 79 | +{ |
---|
| 80 | + fsnotify_parent(dentry, mask, d_inode(dentry), FSNOTIFY_EVENT_INODE); |
---|
| 81 | +} |
---|
| 82 | + |
---|
| 83 | +static inline int fsnotify_file(struct file *file, __u32 mask) |
---|
31 | 84 | { |
---|
32 | 85 | const struct path *path = &file->f_path; |
---|
33 | | - struct inode *inode = file_inode(file); |
---|
34 | | - __u32 fsnotify_mask = 0; |
---|
35 | | - int ret; |
---|
36 | 86 | |
---|
37 | 87 | if (file->f_mode & FMODE_NONOTIFY) |
---|
38 | 88 | return 0; |
---|
| 89 | + |
---|
| 90 | + return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH); |
---|
| 91 | +} |
---|
| 92 | + |
---|
| 93 | +/* Simple call site for access decisions */ |
---|
| 94 | +static inline int fsnotify_perm(struct file *file, int mask) |
---|
| 95 | +{ |
---|
| 96 | + int ret; |
---|
| 97 | + __u32 fsnotify_mask = 0; |
---|
| 98 | + |
---|
39 | 99 | if (!(mask & (MAY_READ | MAY_OPEN))) |
---|
40 | 100 | return 0; |
---|
41 | | - if (mask & MAY_OPEN) |
---|
| 101 | + |
---|
| 102 | + if (mask & MAY_OPEN) { |
---|
42 | 103 | fsnotify_mask = FS_OPEN_PERM; |
---|
43 | | - else if (mask & MAY_READ) |
---|
| 104 | + |
---|
| 105 | + if (file->f_flags & __FMODE_EXEC) { |
---|
| 106 | + ret = fsnotify_file(file, FS_OPEN_EXEC_PERM); |
---|
| 107 | + |
---|
| 108 | + if (ret) |
---|
| 109 | + return ret; |
---|
| 110 | + } |
---|
| 111 | + } else if (mask & MAY_READ) { |
---|
44 | 112 | fsnotify_mask = FS_ACCESS_PERM; |
---|
45 | | - else |
---|
46 | | - BUG(); |
---|
| 113 | + } |
---|
47 | 114 | |
---|
48 | | - ret = fsnotify_parent(path, NULL, fsnotify_mask); |
---|
49 | | - if (ret) |
---|
50 | | - return ret; |
---|
51 | | - |
---|
52 | | - return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
| 115 | + return fsnotify_file(file, fsnotify_mask); |
---|
53 | 116 | } |
---|
54 | 117 | |
---|
55 | 118 | /* |
---|
.. | .. |
---|
57 | 120 | */ |
---|
58 | 121 | static inline void fsnotify_link_count(struct inode *inode) |
---|
59 | 122 | { |
---|
60 | | - fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
---|
| 123 | + fsnotify_inode(inode, FS_ATTRIB); |
---|
61 | 124 | } |
---|
62 | 125 | |
---|
63 | 126 | /* |
---|
64 | 127 | * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir |
---|
65 | 128 | */ |
---|
66 | 129 | static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, |
---|
67 | | - const unsigned char *old_name, |
---|
68 | | - int isdir, struct inode *target, struct dentry *moved) |
---|
| 130 | + const struct qstr *old_name, |
---|
| 131 | + int isdir, struct inode *target, |
---|
| 132 | + struct dentry *moved) |
---|
69 | 133 | { |
---|
70 | 134 | struct inode *source = moved->d_inode; |
---|
71 | 135 | u32 fs_cookie = fsnotify_get_cookie(); |
---|
72 | | - __u32 old_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_FROM); |
---|
73 | | - __u32 new_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_TO); |
---|
74 | | - const unsigned char *new_name = moved->d_name.name; |
---|
| 136 | + __u32 old_dir_mask = FS_MOVED_FROM; |
---|
| 137 | + __u32 new_dir_mask = FS_MOVED_TO; |
---|
| 138 | + const struct qstr *new_name = &moved->d_name; |
---|
75 | 139 | |
---|
76 | 140 | if (old_dir == new_dir) |
---|
77 | 141 | old_dir_mask |= FS_DN_RENAME; |
---|
.. | .. |
---|
81 | 145 | new_dir_mask |= FS_ISDIR; |
---|
82 | 146 | } |
---|
83 | 147 | |
---|
84 | | - fsnotify(old_dir, old_dir_mask, source, FSNOTIFY_EVENT_INODE, old_name, |
---|
85 | | - fs_cookie); |
---|
86 | | - fsnotify(new_dir, new_dir_mask, source, FSNOTIFY_EVENT_INODE, new_name, |
---|
87 | | - fs_cookie); |
---|
| 148 | + fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie); |
---|
| 149 | + fsnotify_name(new_dir, new_dir_mask, source, new_name, fs_cookie); |
---|
88 | 150 | |
---|
89 | 151 | if (target) |
---|
90 | 152 | fsnotify_link_count(target); |
---|
91 | | - |
---|
92 | | - if (source) |
---|
93 | | - fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
---|
| 153 | + fsnotify_inode(source, FS_MOVE_SELF); |
---|
94 | 154 | audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE); |
---|
95 | 155 | } |
---|
96 | 156 | |
---|
.. | .. |
---|
111 | 171 | } |
---|
112 | 172 | |
---|
113 | 173 | /* |
---|
114 | | - * fsnotify_nameremove - a filename was removed from a directory |
---|
115 | | - */ |
---|
116 | | -static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) |
---|
117 | | -{ |
---|
118 | | - __u32 mask = FS_DELETE; |
---|
119 | | - |
---|
120 | | - if (isdir) |
---|
121 | | - mask |= FS_ISDIR; |
---|
122 | | - |
---|
123 | | - fsnotify_parent(NULL, dentry, mask); |
---|
124 | | -} |
---|
125 | | - |
---|
126 | | -/* |
---|
127 | 174 | * fsnotify_inoderemove - an inode is going away |
---|
128 | 175 | */ |
---|
129 | 176 | static inline void fsnotify_inoderemove(struct inode *inode) |
---|
130 | 177 | { |
---|
131 | | - fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
---|
| 178 | + fsnotify_inode(inode, FS_DELETE_SELF); |
---|
132 | 179 | __fsnotify_inode_delete(inode); |
---|
133 | 180 | } |
---|
134 | 181 | |
---|
.. | .. |
---|
139 | 186 | { |
---|
140 | 187 | audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); |
---|
141 | 188 | |
---|
142 | | - fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); |
---|
| 189 | + fsnotify_dirent(inode, dentry, FS_CREATE); |
---|
143 | 190 | } |
---|
144 | 191 | |
---|
145 | 192 | /* |
---|
.. | .. |
---|
147 | 194 | * Note: We have to pass also the linked inode ptr as some filesystems leave |
---|
148 | 195 | * new_dentry->d_inode NULL and instantiate inode pointer later |
---|
149 | 196 | */ |
---|
150 | | -static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry) |
---|
| 197 | +static inline void fsnotify_link(struct inode *dir, struct inode *inode, |
---|
| 198 | + struct dentry *new_dentry) |
---|
151 | 199 | { |
---|
152 | 200 | fsnotify_link_count(inode); |
---|
153 | 201 | audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE); |
---|
154 | 202 | |
---|
155 | | - fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE, new_dentry->d_name.name, 0); |
---|
| 203 | + fsnotify_name(dir, FS_CREATE, inode, &new_dentry->d_name, 0); |
---|
| 204 | +} |
---|
| 205 | + |
---|
| 206 | +/* |
---|
| 207 | + * fsnotify_delete - @dentry was unlinked and unhashed |
---|
| 208 | + * |
---|
| 209 | + * Caller must make sure that dentry->d_name is stable. |
---|
| 210 | + * |
---|
| 211 | + * Note: unlike fsnotify_unlink(), we have to pass also the unlinked inode |
---|
| 212 | + * as this may be called after d_delete() and old_dentry may be negative. |
---|
| 213 | + */ |
---|
| 214 | +static inline void fsnotify_delete(struct inode *dir, struct inode *inode, |
---|
| 215 | + struct dentry *dentry) |
---|
| 216 | +{ |
---|
| 217 | + __u32 mask = FS_DELETE; |
---|
| 218 | + |
---|
| 219 | + if (S_ISDIR(inode->i_mode)) |
---|
| 220 | + mask |= FS_ISDIR; |
---|
| 221 | + |
---|
| 222 | + fsnotify_name(dir, mask, inode, &dentry->d_name, 0); |
---|
| 223 | +} |
---|
| 224 | + |
---|
| 225 | +/** |
---|
| 226 | + * d_delete_notify - delete a dentry and call fsnotify_delete() |
---|
| 227 | + * @dentry: The dentry to delete |
---|
| 228 | + * |
---|
| 229 | + * This helper is used to guaranty that the unlinked inode cannot be found |
---|
| 230 | + * by lookup of this name after fsnotify_delete() event has been delivered. |
---|
| 231 | + */ |
---|
| 232 | +static inline void d_delete_notify(struct inode *dir, struct dentry *dentry) |
---|
| 233 | +{ |
---|
| 234 | + struct inode *inode = d_inode(dentry); |
---|
| 235 | + |
---|
| 236 | + ihold(inode); |
---|
| 237 | + d_delete(dentry); |
---|
| 238 | + fsnotify_delete(dir, inode, dentry); |
---|
| 239 | + iput(inode); |
---|
| 240 | +} |
---|
| 241 | + |
---|
| 242 | +/* |
---|
| 243 | + * fsnotify_unlink - 'name' was unlinked |
---|
| 244 | + * |
---|
| 245 | + * Caller must make sure that dentry->d_name is stable. |
---|
| 246 | + */ |
---|
| 247 | +static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry) |
---|
| 248 | +{ |
---|
| 249 | + if (WARN_ON_ONCE(d_is_negative(dentry))) |
---|
| 250 | + return; |
---|
| 251 | + |
---|
| 252 | + fsnotify_delete(dir, d_inode(dentry), dentry); |
---|
156 | 253 | } |
---|
157 | 254 | |
---|
158 | 255 | /* |
---|
.. | .. |
---|
160 | 257 | */ |
---|
161 | 258 | static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) |
---|
162 | 259 | { |
---|
163 | | - __u32 mask = (FS_CREATE | FS_ISDIR); |
---|
164 | | - struct inode *d_inode = dentry->d_inode; |
---|
165 | | - |
---|
166 | 260 | audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); |
---|
167 | 261 | |
---|
168 | | - fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); |
---|
| 262 | + fsnotify_dirent(inode, dentry, FS_CREATE | FS_ISDIR); |
---|
| 263 | +} |
---|
| 264 | + |
---|
| 265 | +/* |
---|
| 266 | + * fsnotify_rmdir - directory 'name' was removed |
---|
| 267 | + * |
---|
| 268 | + * Caller must make sure that dentry->d_name is stable. |
---|
| 269 | + */ |
---|
| 270 | +static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry) |
---|
| 271 | +{ |
---|
| 272 | + if (WARN_ON_ONCE(d_is_negative(dentry))) |
---|
| 273 | + return; |
---|
| 274 | + |
---|
| 275 | + fsnotify_delete(dir, d_inode(dentry), dentry); |
---|
169 | 276 | } |
---|
170 | 277 | |
---|
171 | 278 | /* |
---|
.. | .. |
---|
173 | 280 | */ |
---|
174 | 281 | static inline void fsnotify_access(struct file *file) |
---|
175 | 282 | { |
---|
176 | | - const struct path *path = &file->f_path; |
---|
177 | | - struct inode *inode = file_inode(file); |
---|
178 | | - __u32 mask = FS_ACCESS; |
---|
179 | | - |
---|
180 | | - if (S_ISDIR(inode->i_mode)) |
---|
181 | | - mask |= FS_ISDIR; |
---|
182 | | - |
---|
183 | | - if (!(file->f_mode & FMODE_NONOTIFY)) { |
---|
184 | | - fsnotify_parent(path, NULL, mask); |
---|
185 | | - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
186 | | - } |
---|
| 283 | + fsnotify_file(file, FS_ACCESS); |
---|
187 | 284 | } |
---|
188 | 285 | |
---|
189 | 286 | /* |
---|
.. | .. |
---|
191 | 288 | */ |
---|
192 | 289 | static inline void fsnotify_modify(struct file *file) |
---|
193 | 290 | { |
---|
194 | | - const struct path *path = &file->f_path; |
---|
195 | | - struct inode *inode = file_inode(file); |
---|
196 | | - __u32 mask = FS_MODIFY; |
---|
197 | | - |
---|
198 | | - if (S_ISDIR(inode->i_mode)) |
---|
199 | | - mask |= FS_ISDIR; |
---|
200 | | - |
---|
201 | | - if (!(file->f_mode & FMODE_NONOTIFY)) { |
---|
202 | | - fsnotify_parent(path, NULL, mask); |
---|
203 | | - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
204 | | - } |
---|
| 291 | + fsnotify_file(file, FS_MODIFY); |
---|
205 | 292 | } |
---|
206 | 293 | |
---|
207 | 294 | /* |
---|
.. | .. |
---|
209 | 296 | */ |
---|
210 | 297 | static inline void fsnotify_open(struct file *file) |
---|
211 | 298 | { |
---|
212 | | - const struct path *path = &file->f_path; |
---|
213 | | - struct inode *inode = file_inode(file); |
---|
214 | | - struct path lower_path; |
---|
215 | 299 | __u32 mask = FS_OPEN; |
---|
216 | 300 | |
---|
217 | | - if (S_ISDIR(inode->i_mode)) |
---|
218 | | - mask |= FS_ISDIR; |
---|
| 301 | + if (file->f_flags & __FMODE_EXEC) |
---|
| 302 | + mask |= FS_OPEN_EXEC; |
---|
219 | 303 | |
---|
220 | | - if (path->dentry->d_op && path->dentry->d_op->d_canonical_path) { |
---|
221 | | - path->dentry->d_op->d_canonical_path(path, &lower_path); |
---|
222 | | - fsnotify_parent(&lower_path, NULL, mask); |
---|
223 | | - fsnotify(lower_path.dentry->d_inode, mask, &lower_path, |
---|
224 | | - FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
225 | | - path_put(&lower_path); |
---|
226 | | - } |
---|
227 | | - |
---|
228 | | - fsnotify_parent(path, NULL, mask); |
---|
229 | | - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
| 304 | + fsnotify_file(file, mask); |
---|
230 | 305 | } |
---|
231 | 306 | |
---|
232 | 307 | /* |
---|
.. | .. |
---|
234 | 309 | */ |
---|
235 | 310 | static inline void fsnotify_close(struct file *file) |
---|
236 | 311 | { |
---|
237 | | - const struct path *path = &file->f_path; |
---|
238 | | - struct inode *inode = file_inode(file); |
---|
239 | | - fmode_t mode = file->f_mode; |
---|
240 | | - __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; |
---|
| 312 | + __u32 mask = (file->f_mode & FMODE_WRITE) ? FS_CLOSE_WRITE : |
---|
| 313 | + FS_CLOSE_NOWRITE; |
---|
241 | 314 | |
---|
242 | | - if (S_ISDIR(inode->i_mode)) |
---|
243 | | - mask |= FS_ISDIR; |
---|
244 | | - |
---|
245 | | - if (!(file->f_mode & FMODE_NONOTIFY)) { |
---|
246 | | - fsnotify_parent(path, NULL, mask); |
---|
247 | | - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
---|
248 | | - } |
---|
| 315 | + fsnotify_file(file, mask); |
---|
249 | 316 | } |
---|
250 | 317 | |
---|
251 | 318 | /* |
---|
.. | .. |
---|
253 | 320 | */ |
---|
254 | 321 | static inline void fsnotify_xattr(struct dentry *dentry) |
---|
255 | 322 | { |
---|
256 | | - struct inode *inode = dentry->d_inode; |
---|
257 | | - __u32 mask = FS_ATTRIB; |
---|
258 | | - |
---|
259 | | - if (S_ISDIR(inode->i_mode)) |
---|
260 | | - mask |= FS_ISDIR; |
---|
261 | | - |
---|
262 | | - fsnotify_parent(NULL, dentry, mask); |
---|
263 | | - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
---|
| 323 | + fsnotify_dentry(dentry, FS_ATTRIB); |
---|
264 | 324 | } |
---|
265 | 325 | |
---|
266 | 326 | /* |
---|
.. | .. |
---|
269 | 329 | */ |
---|
270 | 330 | static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) |
---|
271 | 331 | { |
---|
272 | | - struct inode *inode = dentry->d_inode; |
---|
273 | 332 | __u32 mask = 0; |
---|
274 | 333 | |
---|
275 | 334 | if (ia_valid & ATTR_UID) |
---|
.. | .. |
---|
290 | 349 | if (ia_valid & ATTR_MODE) |
---|
291 | 350 | mask |= FS_ATTRIB; |
---|
292 | 351 | |
---|
293 | | - if (mask) { |
---|
294 | | - if (S_ISDIR(inode->i_mode)) |
---|
295 | | - mask |= FS_ISDIR; |
---|
296 | | - |
---|
297 | | - fsnotify_parent(NULL, dentry, mask); |
---|
298 | | - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); |
---|
299 | | - } |
---|
| 352 | + if (mask) |
---|
| 353 | + fsnotify_dentry(dentry, mask); |
---|
300 | 354 | } |
---|
301 | 355 | |
---|
302 | 356 | #endif /* _LINUX_FS_NOTIFY_H */ |
---|