1 | /* SPDX-License-Identifier: GPL-2.0 */ |
2 | #ifndef _LINUX_FS_NOTIFY_H |
3 | #define _LINUX_FS_NOTIFY_H |
4 | |
5 | /* |
6 | * include/linux/fsnotify.h - generic hooks for filesystem notification, to |
7 | * reduce in-source duplication from both dnotify and inotify. |
8 | * |
9 | * We don't compile any of this away in some complicated menagerie of ifdefs. |
10 | * Instead, we rely on the code inside to optimize away as needed. |
11 | * |
12 | * (C) Copyright 2005 Robert Love |
13 | */ |
14 | |
15 | #include <linux/fsnotify_backend.h> |
16 | #include <linux/audit.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/bug.h> |
19 | |
20 | /* Are there any inode/mount/sb objects that are being watched at all? */ |
21 | static inline bool fsnotify_sb_has_watchers(struct super_block *sb) |
22 | { |
23 | return atomic_long_read(v: &sb->s_fsnotify_connectors); |
24 | } |
25 | |
26 | /* |
27 | * Notify this @dir inode about a change in a child directory entry. |
28 | * The directory entry may have turned positive or negative or its inode may |
29 | * have changed (i.e. renamed over). |
30 | * |
31 | * Unlike fsnotify_parent(), the event will be reported regardless of the |
32 | * FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only |
33 | * the child is interested and not the parent. |
34 | */ |
35 | static inline int fsnotify_name(__u32 mask, const void *data, int data_type, |
36 | struct inode *dir, const struct qstr *name, |
37 | u32 cookie) |
38 | { |
39 | if (!fsnotify_sb_has_watchers(sb: dir->i_sb)) |
40 | return 0; |
41 | |
42 | return fsnotify(mask, data, data_type, dir, name, NULL, cookie); |
43 | } |
44 | |
45 | static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, |
46 | __u32 mask) |
47 | { |
48 | fsnotify_name(mask, data: dentry, data_type: FSNOTIFY_EVENT_DENTRY, dir, name: &dentry->d_name, cookie: 0); |
49 | } |
50 | |
51 | static inline void fsnotify_inode(struct inode *inode, __u32 mask) |
52 | { |
53 | if (!fsnotify_sb_has_watchers(sb: inode->i_sb)) |
54 | return; |
55 | |
56 | if (S_ISDIR(inode->i_mode)) |
57 | mask |= FS_ISDIR; |
58 | |
59 | fsnotify(mask, data: inode, data_type: FSNOTIFY_EVENT_INODE, NULL, NULL, inode, cookie: 0); |
60 | } |
61 | |
62 | /* Notify this dentry's parent about a child's events. */ |
63 | static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, |
64 | const void *data, int data_type) |
65 | { |
66 | struct inode *inode = d_inode(dentry); |
67 | |
68 | if (!fsnotify_sb_has_watchers(sb: inode->i_sb)) |
69 | return 0; |
70 | |
71 | if (S_ISDIR(inode->i_mode)) { |
72 | mask |= FS_ISDIR; |
73 | |
74 | /* sb/mount marks are not interested in name of directory */ |
75 | if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) |
76 | goto notify_child; |
77 | } |
78 | |
79 | /* disconnected dentry cannot notify parent */ |
80 | if (IS_ROOT(dentry)) |
81 | goto notify_child; |
82 | |
83 | return __fsnotify_parent(dentry, mask, data, data_type); |
84 | |
85 | notify_child: |
86 | return fsnotify(mask, data, data_type, NULL, NULL, inode, cookie: 0); |
87 | } |
88 | |
89 | /* |
90 | * Simple wrappers to consolidate calls to fsnotify_parent() when an event |
91 | * is on a file/dentry. |
92 | */ |
93 | static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask) |
94 | { |
95 | fsnotify_parent(dentry, mask, data: dentry, data_type: FSNOTIFY_EVENT_DENTRY); |
96 | } |
97 | |
98 | static inline int fsnotify_file(struct file *file, __u32 mask) |
99 | { |
100 | const struct path *path; |
101 | |
102 | if (file->f_mode & FMODE_NONOTIFY) |
103 | return 0; |
104 | |
105 | path = &file->f_path; |
106 | return fsnotify_parent(dentry: path->dentry, mask, data: path, data_type: FSNOTIFY_EVENT_PATH); |
107 | } |
108 | |
109 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
110 | /* |
111 | * fsnotify_file_area_perm - permission hook before access to file range |
112 | */ |
113 | static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, |
114 | const loff_t *ppos, size_t count) |
115 | { |
116 | __u32 fsnotify_mask = FS_ACCESS_PERM; |
117 | |
118 | /* |
119 | * filesystem may be modified in the context of permission events |
120 | * (e.g. by HSM filling a file on access), so sb freeze protection |
121 | * must not be held. |
122 | */ |
123 | lockdep_assert_once(file_write_not_started(file)); |
124 | |
125 | if (!(perm_mask & MAY_READ)) |
126 | return 0; |
127 | |
128 | return fsnotify_file(file, mask: fsnotify_mask); |
129 | } |
130 | |
131 | /* |
132 | * fsnotify_file_perm - permission hook before file access |
133 | */ |
134 | static inline int fsnotify_file_perm(struct file *file, int perm_mask) |
135 | { |
136 | return fsnotify_file_area_perm(file, perm_mask, NULL, count: 0); |
137 | } |
138 | |
139 | /* |
140 | * fsnotify_open_perm - permission hook before file open |
141 | */ |
142 | static inline int fsnotify_open_perm(struct file *file) |
143 | { |
144 | int ret; |
145 | |
146 | if (file->f_flags & __FMODE_EXEC) { |
147 | ret = fsnotify_file(file, FS_OPEN_EXEC_PERM); |
148 | if (ret) |
149 | return ret; |
150 | } |
151 | |
152 | return fsnotify_file(file, FS_OPEN_PERM); |
153 | } |
154 | |
155 | #else |
156 | static inline int fsnotify_file_area_perm(struct file *file, int perm_mask, |
157 | const loff_t *ppos, size_t count) |
158 | { |
159 | return 0; |
160 | } |
161 | |
162 | static inline int fsnotify_file_perm(struct file *file, int perm_mask) |
163 | { |
164 | return 0; |
165 | } |
166 | |
167 | static inline int fsnotify_open_perm(struct file *file) |
168 | { |
169 | return 0; |
170 | } |
171 | #endif |
172 | |
173 | /* |
174 | * fsnotify_link_count - inode's link count changed |
175 | */ |
176 | static inline void fsnotify_link_count(struct inode *inode) |
177 | { |
178 | fsnotify_inode(inode, FS_ATTRIB); |
179 | } |
180 | |
181 | /* |
182 | * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir |
183 | */ |
184 | static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, |
185 | const struct qstr *old_name, |
186 | int isdir, struct inode *target, |
187 | struct dentry *moved) |
188 | { |
189 | struct inode *source = moved->d_inode; |
190 | u32 fs_cookie = fsnotify_get_cookie(); |
191 | __u32 old_dir_mask = FS_MOVED_FROM; |
192 | __u32 new_dir_mask = FS_MOVED_TO; |
193 | __u32 rename_mask = FS_RENAME; |
194 | const struct qstr *new_name = &moved->d_name; |
195 | |
196 | if (isdir) { |
197 | old_dir_mask |= FS_ISDIR; |
198 | new_dir_mask |= FS_ISDIR; |
199 | rename_mask |= FS_ISDIR; |
200 | } |
201 | |
202 | /* Event with information about both old and new parent+name */ |
203 | fsnotify_name(mask: rename_mask, data: moved, data_type: FSNOTIFY_EVENT_DENTRY, |
204 | dir: old_dir, name: old_name, cookie: 0); |
205 | |
206 | fsnotify_name(mask: old_dir_mask, data: source, data_type: FSNOTIFY_EVENT_INODE, |
207 | dir: old_dir, name: old_name, cookie: fs_cookie); |
208 | fsnotify_name(mask: new_dir_mask, data: source, data_type: FSNOTIFY_EVENT_INODE, |
209 | dir: new_dir, name: new_name, cookie: fs_cookie); |
210 | |
211 | if (target) |
212 | fsnotify_link_count(inode: target); |
213 | fsnotify_inode(inode: source, FS_MOVE_SELF); |
214 | audit_inode_child(parent: new_dir, dentry: moved, AUDIT_TYPE_CHILD_CREATE); |
215 | } |
216 | |
217 | /* |
218 | * fsnotify_inode_delete - and inode is being evicted from cache, clean up is needed |
219 | */ |
220 | static inline void fsnotify_inode_delete(struct inode *inode) |
221 | { |
222 | __fsnotify_inode_delete(inode); |
223 | } |
224 | |
225 | /* |
226 | * fsnotify_vfsmount_delete - a vfsmount is being destroyed, clean up is needed |
227 | */ |
228 | static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt) |
229 | { |
230 | __fsnotify_vfsmount_delete(mnt); |
231 | } |
232 | |
233 | /* |
234 | * fsnotify_inoderemove - an inode is going away |
235 | */ |
236 | static inline void fsnotify_inoderemove(struct inode *inode) |
237 | { |
238 | fsnotify_inode(inode, FS_DELETE_SELF); |
239 | __fsnotify_inode_delete(inode); |
240 | } |
241 | |
242 | /* |
243 | * fsnotify_create - 'name' was linked in |
244 | * |
245 | * Caller must make sure that dentry->d_name is stable. |
246 | * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate |
247 | * ->d_inode later |
248 | */ |
249 | static inline void fsnotify_create(struct inode *dir, struct dentry *dentry) |
250 | { |
251 | audit_inode_child(parent: dir, dentry, AUDIT_TYPE_CHILD_CREATE); |
252 | |
253 | fsnotify_dirent(dir, dentry, FS_CREATE); |
254 | } |
255 | |
256 | /* |
257 | * fsnotify_link - new hardlink in 'inode' directory |
258 | * |
259 | * Caller must make sure that new_dentry->d_name is stable. |
260 | * Note: We have to pass also the linked inode ptr as some filesystems leave |
261 | * new_dentry->d_inode NULL and instantiate inode pointer later |
262 | */ |
263 | static inline void fsnotify_link(struct inode *dir, struct inode *inode, |
264 | struct dentry *new_dentry) |
265 | { |
266 | fsnotify_link_count(inode); |
267 | audit_inode_child(parent: dir, dentry: new_dentry, AUDIT_TYPE_CHILD_CREATE); |
268 | |
269 | fsnotify_name(FS_CREATE, data: inode, data_type: FSNOTIFY_EVENT_INODE, |
270 | dir, name: &new_dentry->d_name, cookie: 0); |
271 | } |
272 | |
273 | /* |
274 | * fsnotify_delete - @dentry was unlinked and unhashed |
275 | * |
276 | * Caller must make sure that dentry->d_name is stable. |
277 | * |
278 | * Note: unlike fsnotify_unlink(), we have to pass also the unlinked inode |
279 | * as this may be called after d_delete() and old_dentry may be negative. |
280 | */ |
281 | static inline void fsnotify_delete(struct inode *dir, struct inode *inode, |
282 | struct dentry *dentry) |
283 | { |
284 | __u32 mask = FS_DELETE; |
285 | |
286 | if (S_ISDIR(inode->i_mode)) |
287 | mask |= FS_ISDIR; |
288 | |
289 | fsnotify_name(mask, data: inode, data_type: FSNOTIFY_EVENT_INODE, dir, name: &dentry->d_name, |
290 | cookie: 0); |
291 | } |
292 | |
293 | /** |
294 | * d_delete_notify - delete a dentry and call fsnotify_delete() |
295 | * @dentry: The dentry to delete |
296 | * |
297 | * This helper is used to guaranty that the unlinked inode cannot be found |
298 | * by lookup of this name after fsnotify_delete() event has been delivered. |
299 | */ |
300 | static inline void d_delete_notify(struct inode *dir, struct dentry *dentry) |
301 | { |
302 | struct inode *inode = d_inode(dentry); |
303 | |
304 | ihold(inode); |
305 | d_delete(dentry); |
306 | fsnotify_delete(dir, inode, dentry); |
307 | iput(inode); |
308 | } |
309 | |
310 | /* |
311 | * fsnotify_unlink - 'name' was unlinked |
312 | * |
313 | * Caller must make sure that dentry->d_name is stable. |
314 | */ |
315 | static inline void fsnotify_unlink(struct inode *dir, struct dentry *dentry) |
316 | { |
317 | if (WARN_ON_ONCE(d_is_negative(dentry))) |
318 | return; |
319 | |
320 | fsnotify_delete(dir, inode: d_inode(dentry), dentry); |
321 | } |
322 | |
323 | /* |
324 | * fsnotify_mkdir - directory 'name' was created |
325 | * |
326 | * Caller must make sure that dentry->d_name is stable. |
327 | * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate |
328 | * ->d_inode later |
329 | */ |
330 | static inline void fsnotify_mkdir(struct inode *dir, struct dentry *dentry) |
331 | { |
332 | audit_inode_child(parent: dir, dentry, AUDIT_TYPE_CHILD_CREATE); |
333 | |
334 | fsnotify_dirent(dir, dentry, FS_CREATE | FS_ISDIR); |
335 | } |
336 | |
337 | /* |
338 | * fsnotify_rmdir - directory 'name' was removed |
339 | * |
340 | * Caller must make sure that dentry->d_name is stable. |
341 | */ |
342 | static inline void fsnotify_rmdir(struct inode *dir, struct dentry *dentry) |
343 | { |
344 | if (WARN_ON_ONCE(d_is_negative(dentry))) |
345 | return; |
346 | |
347 | fsnotify_delete(dir, inode: d_inode(dentry), dentry); |
348 | } |
349 | |
350 | /* |
351 | * fsnotify_access - file was read |
352 | */ |
353 | static inline void fsnotify_access(struct file *file) |
354 | { |
355 | fsnotify_file(file, FS_ACCESS); |
356 | } |
357 | |
358 | /* |
359 | * fsnotify_modify - file was modified |
360 | */ |
361 | static inline void fsnotify_modify(struct file *file) |
362 | { |
363 | fsnotify_file(file, FS_MODIFY); |
364 | } |
365 | |
366 | /* |
367 | * fsnotify_open - file was opened |
368 | */ |
369 | static inline void fsnotify_open(struct file *file) |
370 | { |
371 | __u32 mask = FS_OPEN; |
372 | |
373 | if (file->f_flags & __FMODE_EXEC) |
374 | mask |= FS_OPEN_EXEC; |
375 | |
376 | fsnotify_file(file, mask); |
377 | } |
378 | |
379 | /* |
380 | * fsnotify_close - file was closed |
381 | */ |
382 | static inline void fsnotify_close(struct file *file) |
383 | { |
384 | __u32 mask = (file->f_mode & FMODE_WRITE) ? FS_CLOSE_WRITE : |
385 | FS_CLOSE_NOWRITE; |
386 | |
387 | fsnotify_file(file, mask); |
388 | } |
389 | |
390 | /* |
391 | * fsnotify_xattr - extended attributes were changed |
392 | */ |
393 | static inline void fsnotify_xattr(struct dentry *dentry) |
394 | { |
395 | fsnotify_dentry(dentry, FS_ATTRIB); |
396 | } |
397 | |
398 | /* |
399 | * fsnotify_change - notify_change event. file was modified and/or metadata |
400 | * was changed. |
401 | */ |
402 | static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) |
403 | { |
404 | __u32 mask = 0; |
405 | |
406 | if (ia_valid & ATTR_UID) |
407 | mask |= FS_ATTRIB; |
408 | if (ia_valid & ATTR_GID) |
409 | mask |= FS_ATTRIB; |
410 | if (ia_valid & ATTR_SIZE) |
411 | mask |= FS_MODIFY; |
412 | |
413 | /* both times implies a utime(s) call */ |
414 | if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) |
415 | mask |= FS_ATTRIB; |
416 | else if (ia_valid & ATTR_ATIME) |
417 | mask |= FS_ACCESS; |
418 | else if (ia_valid & ATTR_MTIME) |
419 | mask |= FS_MODIFY; |
420 | |
421 | if (ia_valid & ATTR_MODE) |
422 | mask |= FS_ATTRIB; |
423 | |
424 | if (mask) |
425 | fsnotify_dentry(dentry, mask); |
426 | } |
427 | |
428 | static inline int fsnotify_sb_error(struct super_block *sb, struct inode *inode, |
429 | int error) |
430 | { |
431 | struct fs_error_report report = { |
432 | .error = error, |
433 | .inode = inode, |
434 | .sb = sb, |
435 | }; |
436 | |
437 | return fsnotify(FS_ERROR, data: &report, data_type: FSNOTIFY_EVENT_ERROR, |
438 | NULL, NULL, NULL, cookie: 0); |
439 | } |
440 | |
441 | #endif /* _LINUX_FS_NOTIFY_H */ |
442 | |