diff options
Diffstat (limited to 'fs/notify/inotify/inotify_fsnotify.c')
-rw-r--r-- | fs/notify/inotify/inotify_fsnotify.c | 151 |
1 files changed, 101 insertions, 50 deletions
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e27960cd76ab..5e73eeb2c697 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c | |||
@@ -22,6 +22,7 @@ | |||
22 | * General Public License for more details. | 22 | * General Public License for more details. |
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/dcache.h> /* d_unlinked */ | ||
25 | #include <linux/fs.h> /* struct inode */ | 26 | #include <linux/fs.h> /* struct inode */ |
26 | #include <linux/fsnotify_backend.h> | 27 | #include <linux/fsnotify_backend.h> |
27 | #include <linux/inotify.h> | 28 | #include <linux/inotify.h> |
@@ -32,26 +33,84 @@ | |||
32 | 33 | ||
33 | #include "inotify.h" | 34 | #include "inotify.h" |
34 | 35 | ||
35 | static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) | 36 | /* |
37 | * Check if 2 events contain the same information. We do not compare private data | ||
38 | * but at this moment that isn't a problem for any know fsnotify listeners. | ||
39 | */ | ||
40 | static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) | ||
41 | { | ||
42 | if ((old->mask == new->mask) && | ||
43 | (old->to_tell == new->to_tell) && | ||
44 | (old->data_type == new->data_type) && | ||
45 | (old->name_len == new->name_len)) { | ||
46 | switch (old->data_type) { | ||
47 | case (FSNOTIFY_EVENT_INODE): | ||
48 | /* remember, after old was put on the wait_q we aren't | ||
49 | * allowed to look at the inode any more, only thing | ||
50 | * left to check was if the file_name is the same */ | ||
51 | if (!old->name_len || | ||
52 | !strcmp(old->file_name, new->file_name)) | ||
53 | return true; | ||
54 | break; | ||
55 | case (FSNOTIFY_EVENT_FILE): | ||
56 | if ((old->file->f_path.mnt == new->file->f_path.mnt) && | ||
57 | (old->file->f_path.dentry == new->file->f_path.dentry)) | ||
58 | return true; | ||
59 | break; | ||
60 | case (FSNOTIFY_EVENT_NONE): | ||
61 | if (old->mask & FS_Q_OVERFLOW) | ||
62 | return true; | ||
63 | else if (old->mask & FS_IN_IGNORED) | ||
64 | return false; | ||
65 | return true; | ||
66 | }; | ||
67 | } | ||
68 | return false; | ||
69 | } | ||
70 | |||
71 | static struct fsnotify_event *inotify_merge(struct list_head *list, | ||
72 | struct fsnotify_event *event) | ||
36 | { | 73 | { |
37 | struct fsnotify_mark_entry *entry; | 74 | struct fsnotify_event_holder *last_holder; |
38 | struct inotify_inode_mark_entry *ientry; | 75 | struct fsnotify_event *last_event; |
76 | |||
77 | /* and the list better be locked by something too */ | ||
78 | spin_lock(&event->lock); | ||
79 | |||
80 | last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); | ||
81 | last_event = last_holder->event; | ||
82 | if (event_compare(last_event, event)) | ||
83 | fsnotify_get_event(last_event); | ||
84 | else | ||
85 | last_event = NULL; | ||
86 | |||
87 | spin_unlock(&event->lock); | ||
88 | |||
89 | return last_event; | ||
90 | } | ||
91 | |||
92 | static int inotify_handle_event(struct fsnotify_group *group, | ||
93 | struct fsnotify_mark *inode_mark, | ||
94 | struct fsnotify_mark *vfsmount_mark, | ||
95 | struct fsnotify_event *event) | ||
96 | { | ||
97 | struct inotify_inode_mark *i_mark; | ||
39 | struct inode *to_tell; | 98 | struct inode *to_tell; |
40 | struct inotify_event_private_data *event_priv; | 99 | struct inotify_event_private_data *event_priv; |
41 | struct fsnotify_event_private_data *fsn_event_priv; | 100 | struct fsnotify_event_private_data *fsn_event_priv; |
42 | int wd, ret; | 101 | struct fsnotify_event *added_event; |
102 | int wd, ret = 0; | ||
103 | |||
104 | BUG_ON(vfsmount_mark); | ||
105 | |||
106 | pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, | ||
107 | event, event->to_tell, event->mask); | ||
43 | 108 | ||
44 | to_tell = event->to_tell; | 109 | to_tell = event->to_tell; |
45 | 110 | ||
46 | spin_lock(&to_tell->i_lock); | 111 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
47 | entry = fsnotify_find_mark_entry(group, to_tell); | 112 | fsn_mark); |
48 | spin_unlock(&to_tell->i_lock); | 113 | wd = i_mark->wd; |
49 | /* race with watch removal? We already passes should_send */ | ||
50 | if (unlikely(!entry)) | ||
51 | return 0; | ||
52 | ientry = container_of(entry, struct inotify_inode_mark_entry, | ||
53 | fsn_entry); | ||
54 | wd = ientry->wd; | ||
55 | 114 | ||
56 | event_priv = kmem_cache_alloc(event_priv_cachep, GFP_KERNEL); | 115 | event_priv = kmem_cache_alloc(event_priv_cachep, GFP_KERNEL); |
57 | if (unlikely(!event_priv)) | 116 | if (unlikely(!event_priv)) |
@@ -62,48 +121,40 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev | |||
62 | fsn_event_priv->group = group; | 121 | fsn_event_priv->group = group; |
63 | event_priv->wd = wd; | 122 | event_priv->wd = wd; |
64 | 123 | ||
65 | ret = fsnotify_add_notify_event(group, event, fsn_event_priv); | 124 | added_event = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); |
66 | if (ret) { | 125 | if (added_event) { |
67 | inotify_free_event_priv(fsn_event_priv); | 126 | inotify_free_event_priv(fsn_event_priv); |
68 | /* EEXIST says we tail matched, EOVERFLOW isn't something | 127 | if (!IS_ERR(added_event)) |
69 | * to report up the stack. */ | 128 | fsnotify_put_event(added_event); |
70 | if ((ret == -EEXIST) || | 129 | else |
71 | (ret == -EOVERFLOW)) | 130 | ret = PTR_ERR(added_event); |
72 | ret = 0; | ||
73 | } | 131 | } |
74 | 132 | ||
75 | /* | 133 | if (inode_mark->mask & IN_ONESHOT) |
76 | * If we hold the entry until after the event is on the queue | 134 | fsnotify_destroy_mark(inode_mark); |
77 | * IN_IGNORED won't be able to pass this event in the queue | ||
78 | */ | ||
79 | fsnotify_put_mark(entry); | ||
80 | 135 | ||
81 | return ret; | 136 | return ret; |
82 | } | 137 | } |
83 | 138 | ||
84 | static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group) | 139 | static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) |
85 | { | 140 | { |
86 | inotify_ignored_and_remove_idr(entry, group); | 141 | inotify_ignored_and_remove_idr(fsn_mark, group); |
87 | } | 142 | } |
88 | 143 | ||
89 | static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) | 144 | static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, |
145 | struct fsnotify_mark *inode_mark, | ||
146 | struct fsnotify_mark *vfsmount_mark, | ||
147 | __u32 mask, void *data, int data_type) | ||
90 | { | 148 | { |
91 | struct fsnotify_mark_entry *entry; | 149 | if ((inode_mark->mask & FS_EXCL_UNLINK) && |
92 | bool send; | 150 | (data_type == FSNOTIFY_EVENT_FILE)) { |
93 | 151 | struct file *file = data; | |
94 | spin_lock(&inode->i_lock); | ||
95 | entry = fsnotify_find_mark_entry(group, inode); | ||
96 | spin_unlock(&inode->i_lock); | ||
97 | if (!entry) | ||
98 | return false; | ||
99 | 152 | ||
100 | mask = (mask & ~FS_EVENT_ON_CHILD); | 153 | if (d_unlinked(file->f_path.dentry)) |
101 | send = (entry->mask & mask); | 154 | return false; |
102 | 155 | } | |
103 | /* find took a reference */ | ||
104 | fsnotify_put_mark(entry); | ||
105 | 156 | ||
106 | return send; | 157 | return true; |
107 | } | 158 | } |
108 | 159 | ||
109 | /* | 160 | /* |
@@ -115,18 +166,18 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode | |||
115 | */ | 166 | */ |
116 | static int idr_callback(int id, void *p, void *data) | 167 | static int idr_callback(int id, void *p, void *data) |
117 | { | 168 | { |
118 | struct fsnotify_mark_entry *entry; | 169 | struct fsnotify_mark *fsn_mark; |
119 | struct inotify_inode_mark_entry *ientry; | 170 | struct inotify_inode_mark *i_mark; |
120 | static bool warned = false; | 171 | static bool warned = false; |
121 | 172 | ||
122 | if (warned) | 173 | if (warned) |
123 | return 0; | 174 | return 0; |
124 | 175 | ||
125 | warned = true; | 176 | warned = true; |
126 | entry = p; | 177 | fsn_mark = p; |
127 | ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); | 178 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); |
128 | 179 | ||
129 | WARN(1, "inotify closing but id=%d for entry=%p in group=%p still in " | 180 | WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " |
130 | "idr. Probably leaking memory\n", id, p, data); | 181 | "idr. Probably leaking memory\n", id, p, data); |
131 | 182 | ||
132 | /* | 183 | /* |
@@ -135,9 +186,9 @@ static int idr_callback(int id, void *p, void *data) | |||
135 | * out why we got here and the panic is no worse than the original | 186 | * out why we got here and the panic is no worse than the original |
136 | * BUG() that was here. | 187 | * BUG() that was here. |
137 | */ | 188 | */ |
138 | if (entry) | 189 | if (fsn_mark) |
139 | printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n", | 190 | printk(KERN_WARNING "fsn_mark->group=%p inode=%p wd=%d\n", |
140 | entry->group, entry->inode, ientry->wd); | 191 | fsn_mark->group, fsn_mark->i.inode, i_mark->wd); |
141 | return 0; | 192 | return 0; |
142 | } | 193 | } |
143 | 194 | ||