1 /* 2 * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2, or (at your option) 7 * any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; see the file COPYING. If not, write to 16 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 #include <linux/dcache.h> 20 #include <linux/fs.h> 21 #include <linux/gfp.h> 22 #include <linux/init.h> 23 #include <linux/module.h> 24 #include <linux/srcu.h> 25 26 #include <linux/fsnotify_backend.h> 27 #include "fsnotify.h" 28 29 /* 30 * Clear all of the marks on an inode when it is being evicted from core 31 */ 32 void __fsnotify_inode_delete(struct inode *inode) 33 { 34 fsnotify_clear_marks_by_inode(inode); 35 } 36 EXPORT_SYMBOL_GPL(__fsnotify_inode_delete); 37 38 /* 39 * Given an inode, first check if we care what happens to our children. Inotify 40 * and dnotify both tell their parents about events. If we care about any event 41 * on a child we run all of our children and set a dentry flag saying that the 42 * parent cares. Thus when an event happens on a child it can quickly tell if 43 * if there is a need to find a parent and send the event to the parent. 44 */ 45 void __fsnotify_update_child_dentry_flags(struct inode *inode) 46 { 47 struct dentry *alias; 48 int watched; 49 50 if (!S_ISDIR(inode->i_mode)) 51 return; 52 53 /* determine if the children should tell inode about their events */ 54 watched = fsnotify_inode_watches_children(inode); 55 56 spin_lock(&dcache_lock); 57 /* run all of the dentries associated with this inode. Since this is a 58 * directory, there damn well better only be one item on this list */ 59 list_for_each_entry(alias, &inode->i_dentry, d_alias) { 60 struct dentry *child; 61 62 /* run all of the children of the original inode and fix their 63 * d_flags to indicate parental interest (their parent is the 64 * original inode) */ 65 list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { 66 if (!child->d_inode) 67 continue; 68 69 spin_lock(&child->d_lock); 70 if (watched) 71 child->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED; 72 else 73 child->d_flags &= ~DCACHE_FSNOTIFY_PARENT_WATCHED; 74 spin_unlock(&child->d_lock); 75 } 76 } 77 spin_unlock(&dcache_lock); 78 } 79 80 /* Notify this dentry's parent about a child's events. */ 81 void __fsnotify_parent(struct dentry *dentry, __u32 mask) 82 { 83 struct dentry *parent; 84 struct inode *p_inode; 85 bool send = false; 86 bool should_update_children = false; 87 88 if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) 89 return; 90 91 spin_lock(&dentry->d_lock); 92 parent = dentry->d_parent; 93 p_inode = parent->d_inode; 94 95 if (fsnotify_inode_watches_children(p_inode)) { 96 if (p_inode->i_fsnotify_mask & mask) { 97 dget(parent); 98 send = true; 99 } 100 } else { 101 /* 102 * The parent doesn't care about events on it's children but 103 * at least one child thought it did. We need to run all the 104 * children and update their d_flags to let them know p_inode 105 * doesn't care about them any more. 106 */ 107 dget(parent); 108 should_update_children = true; 109 } 110 111 spin_unlock(&dentry->d_lock); 112 113 if (send) { 114 /* we are notifying a parent so come up with the new mask which 115 * specifies these are events which came from a child. */ 116 mask |= FS_EVENT_ON_CHILD; 117 118 fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, 119 dentry->d_name.name, 0); 120 dput(parent); 121 } 122 123 if (unlikely(should_update_children)) { 124 __fsnotify_update_child_dentry_flags(p_inode); 125 dput(parent); 126 } 127 } 128 EXPORT_SYMBOL_GPL(__fsnotify_parent); 129 130 /* 131 * This is the main call to fsnotify. The VFS calls into hook specific functions 132 * in linux/fsnotify.h. Those functions then in turn call here. Here will call 133 * out to all of the registered fsnotify_group. Those groups can then use the 134 * notification event in whatever means they feel necessary. 135 */ 136 void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *file_name, u32 cookie) 137 { 138 struct fsnotify_group *group; 139 struct fsnotify_event *event = NULL; 140 int idx; 141 /* global tests shouldn't care about events on child only the specific event */ 142 __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); 143 144 if (list_empty(&fsnotify_groups)) 145 return; 146 147 if (!(test_mask & fsnotify_mask)) 148 return; 149 150 if (!(test_mask & to_tell->i_fsnotify_mask)) 151 return; 152 /* 153 * SRCU!! the groups list is very very much read only and the path is 154 * very hot. The VAST majority of events are not going to need to do 155 * anything other than walk the list so it's crazy to pre-allocate. 156 */ 157 idx = srcu_read_lock(&fsnotify_grp_srcu); 158 list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { 159 if (test_mask & group->mask) { 160 if (!group->ops->should_send_event(group, to_tell, mask)) 161 continue; 162 if (!event) { 163 event = fsnotify_create_event(to_tell, mask, data, 164 data_is, file_name, cookie, 165 GFP_KERNEL); 166 /* shit, we OOM'd and now we can't tell, maybe 167 * someday someone else will want to do something 168 * here */ 169 if (!event) 170 break; 171 } 172 group->ops->handle_event(group, event); 173 } 174 } 175 srcu_read_unlock(&fsnotify_grp_srcu, idx); 176 /* 177 * fsnotify_create_event() took a reference so the event can't be cleaned 178 * up while we are still trying to add it to lists, drop that one. 179 */ 180 if (event) 181 fsnotify_put_event(event); 182 } 183 EXPORT_SYMBOL_GPL(fsnotify); 184 185 static __init int fsnotify_init(void) 186 { 187 return init_srcu_struct(&fsnotify_grp_srcu); 188 } 189 subsys_initcall(fsnotify_init); 190