aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-05-21 17:01:20 -0400
committerEric Paris <eparis@redhat.com>2009-06-11 14:57:52 -0400
commit90586523eb4b349806887c62ee70685a49415124 (patch)
tree2ba6da637f897bbb2309b141b81801e4151d87b0
parentc9059598ea8981d02356eead3188bf7fa4d717b8 (diff)
fsnotify: unified filesystem notification backend
fsnotify is a backend for filesystem notification. fsnotify does not provide any userspace interface but does provide the basis needed for other notification schemes such as dnotify. fsnotify can be extended to be the backend for inotify or the upcoming fanotify. fsnotify provides a mechanism for "groups" to register for some set of filesystem events and to then deliver those events to those groups for processing. fsnotify has a number of benefits, the first being actually shrinking the size of an inode. Before fsnotify to support both dnotify and inotify an inode had unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ struct list_head inotify_watches; /* watches on this inode */ struct mutex inotify_mutex; /* protects the watches list But with fsnotify this same functionallity (and more) is done with just __u32 i_fsnotify_mask; /* all events for this inode */ struct hlist_head i_fsnotify_mark_entries; /* marks on this inode */ That's right, inotify, dnotify, and fanotify all in 64 bits. We used that much space just in inotify_watches alone, before this patch set. fsnotify object lifetime and locking is MUCH better than what we have today. inotify locking is incredibly complex. See 8f7b0ba1c8539 as an example of what's been busted since inception. inotify needs to know internal semantics of superblock destruction and unmounting to function. The inode pinning and vfs contortions are horrible. no fsnotify implementers do allocation under locks. This means things like f04b30de3 which (due to an overabundance of caution) changes GFP_KERNEL to GFP_NOFS can be reverted. There are no longer any allocation rules when using or implementing your own fsnotify listener. fsnotify paves the way for fanotify. In brief fanotify is a notification mechanism that delivers the lisener both an 'event' and an open file descriptor to the object in question. This means that fanotify is pathname agnostic. Some on lkml may not care for the original companies or users that pushed for TALPA, but fanotify was designed with flexibility and input for other users in mind. The readahead group expressed interest in fanotify as it could be used to profile disk access on boot without breaking the audit system. The desktop search groups have also expressed interest in fanotify as it solves a number of the race conditions and problems present with managing inotify when more than a limited number of specific files are of interest. fanotify can provide for a userspace access control system which makes it a clean interface for AV vendors to hook without trying to do binary patching on the syscall table, LSM, and everywhere else they do their things today. With this patch series fanotify can be implemented in less than 1200 lines of easy to review code. Almost all of which is the socket based user interface. This patch series builds fsnotify to the point that it can implement dnotify and inotify_user. Patches exist and will be sent soon after acceptance to finish the in kernel inotify conversion (audit) and implement fanotify. Signed-off-by: Eric Paris <eparis@redhat.com> Acked-by: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de>
-rw-r--r--fs/notify/Kconfig13
-rw-r--r--fs/notify/Makefile2
-rw-r--r--fs/notify/fsnotify.c79
-rw-r--r--fs/notify/fsnotify.h15
-rw-r--r--fs/notify/group.c198
-rw-r--r--fs/notify/inotify/inotify.c20
-rw-r--r--fs/notify/notification.c121
-rw-r--r--include/linux/fsnotify.h115
-rw-r--r--include/linux/fsnotify_backend.h177
9 files changed, 705 insertions, 35 deletions
diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig
index 50914d7303c6..31dac7e3b0f1 100644
--- a/fs/notify/Kconfig
+++ b/fs/notify/Kconfig
@@ -1,2 +1,15 @@
1config FSNOTIFY
2 bool "Filesystem notification backend"
3 default y
4 ---help---
5 fsnotify is a backend for filesystem notification. fsnotify does
6 not provide any userspace interface but does provide the basis
7 needed for other notification schemes such as dnotify, inotify,
8 and fanotify.
9
10 Say Y here to enable fsnotify suport.
11
12 If unsure, say Y.
13
1source "fs/notify/dnotify/Kconfig" 14source "fs/notify/dnotify/Kconfig"
2source "fs/notify/inotify/Kconfig" 15source "fs/notify/inotify/Kconfig"
diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 5a95b6010ce7..db5467b5b58d 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -1,2 +1,4 @@
1obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o
2
1obj-y += dnotify/ 3obj-y += dnotify/
2obj-y += inotify/ 4obj-y += inotify/
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
new file mode 100644
index 000000000000..56bee0f10c38
--- /dev/null
+++ b/fs/notify/fsnotify.c
@@ -0,0 +1,79 @@
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/init.h>
22#include <linux/module.h>
23#include <linux/srcu.h>
24
25#include <linux/fsnotify_backend.h>
26#include "fsnotify.h"
27
28/*
29 * This is the main call to fsnotify. The VFS calls into hook specific functions
30 * in linux/fsnotify.h. Those functions then in turn call here. Here will call
31 * out to all of the registered fsnotify_group. Those groups can then use the
32 * notification event in whatever means they feel necessary.
33 */
34void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is)
35{
36 struct fsnotify_group *group;
37 struct fsnotify_event *event = NULL;
38 int idx;
39
40 if (list_empty(&fsnotify_groups))
41 return;
42
43 if (!(mask & fsnotify_mask))
44 return;
45
46 /*
47 * SRCU!! the groups list is very very much read only and the path is
48 * very hot. The VAST majority of events are not going to need to do
49 * anything other than walk the list so it's crazy to pre-allocate.
50 */
51 idx = srcu_read_lock(&fsnotify_grp_srcu);
52 list_for_each_entry_rcu(group, &fsnotify_groups, group_list) {
53 if (mask & group->mask) {
54 if (!event) {
55 event = fsnotify_create_event(to_tell, mask, data, data_is);
56 /* shit, we OOM'd and now we can't tell, maybe
57 * someday someone else will want to do something
58 * here */
59 if (!event)
60 break;
61 }
62 group->ops->handle_event(group, event);
63 }
64 }
65 srcu_read_unlock(&fsnotify_grp_srcu, idx);
66 /*
67 * fsnotify_create_event() took a reference so the event can't be cleaned
68 * up while we are still trying to add it to lists, drop that one.
69 */
70 if (event)
71 fsnotify_put_event(event);
72}
73EXPORT_SYMBOL_GPL(fsnotify);
74
75static __init int fsnotify_init(void)
76{
77 return init_srcu_struct(&fsnotify_grp_srcu);
78}
79subsys_initcall(fsnotify_init);
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
new file mode 100644
index 000000000000..c6a8bd476572
--- /dev/null
+++ b/fs/notify/fsnotify.h
@@ -0,0 +1,15 @@
1#ifndef __FS_NOTIFY_FSNOTIFY_H_
2#define __FS_NOTIFY_FSNOTIFY_H_
3
4#include <linux/list.h>
5#include <linux/fsnotify.h>
6#include <linux/srcu.h>
7#include <linux/types.h>
8
9/* protects reads of fsnotify_groups */
10extern struct srcu_struct fsnotify_grp_srcu;
11/* all groups which receive fsnotify events */
12extern struct list_head fsnotify_groups;
13/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */
14extern __u32 fsnotify_mask;
15#endif /* __FS_NOTIFY_FSNOTIFY_H_ */
diff --git a/fs/notify/group.c b/fs/notify/group.c
new file mode 100644
index 000000000000..c6812953b968
--- /dev/null
+++ b/fs/notify/group.c
@@ -0,0 +1,198 @@
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/list.h>
20#include <linux/mutex.h>
21#include <linux/slab.h>
22#include <linux/srcu.h>
23#include <linux/rculist.h>
24#include <linux/wait.h>
25
26#include <linux/fsnotify_backend.h>
27#include "fsnotify.h"
28
29#include <asm/atomic.h>
30
31/* protects writes to fsnotify_groups and fsnotify_mask */
32static DEFINE_MUTEX(fsnotify_grp_mutex);
33/* protects reads while running the fsnotify_groups list */
34struct srcu_struct fsnotify_grp_srcu;
35/* all groups registered to receive filesystem notifications */
36LIST_HEAD(fsnotify_groups);
37/* bitwise OR of all events (FS_*) interesting to some group on this system */
38__u32 fsnotify_mask;
39
40/*
41 * When a new group registers or changes it's set of interesting events
42 * this function updates the fsnotify_mask to contain all interesting events
43 */
44void fsnotify_recalc_global_mask(void)
45{
46 struct fsnotify_group *group;
47 __u32 mask = 0;
48 int idx;
49
50 idx = srcu_read_lock(&fsnotify_grp_srcu);
51 list_for_each_entry_rcu(group, &fsnotify_groups, group_list)
52 mask |= group->mask;
53 srcu_read_unlock(&fsnotify_grp_srcu, idx);
54 fsnotify_mask = mask;
55}
56
57/*
58 * Take a reference to a group so things found under the fsnotify_grp_mutex
59 * can't get freed under us
60 */
61static void fsnotify_get_group(struct fsnotify_group *group)
62{
63 atomic_inc(&group->refcnt);
64}
65
66/*
67 * Final freeing of a group
68 */
69static void fsnotify_destroy_group(struct fsnotify_group *group)
70{
71 if (group->ops->free_group_priv)
72 group->ops->free_group_priv(group);
73
74 kfree(group);
75}
76
77/*
78 * Remove this group from the global list of groups that will get events
79 * this can be done even if there are still references and things still using
80 * this group. This just stops the group from getting new events.
81 */
82static void __fsnotify_evict_group(struct fsnotify_group *group)
83{
84 BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex));
85
86 if (group->on_group_list)
87 list_del_rcu(&group->group_list);
88 group->on_group_list = 0;
89}
90
91/*
92 * Called when a group is no longer interested in getting events. This can be
93 * used if a group is misbehaving or if for some reason a group should no longer
94 * get any filesystem events.
95 */
96void fsnotify_evict_group(struct fsnotify_group *group)
97{
98 mutex_lock(&fsnotify_grp_mutex);
99 __fsnotify_evict_group(group);
100 mutex_unlock(&fsnotify_grp_mutex);
101}
102
103/*
104 * Drop a reference to a group. Free it if it's through.
105 */
106void fsnotify_put_group(struct fsnotify_group *group)
107{
108 if (!atomic_dec_and_mutex_lock(&group->refcnt, &fsnotify_grp_mutex))
109 return;
110
111 /*
112 * OK, now we know that there's no other users *and* we hold mutex,
113 * so no new references will appear
114 */
115 __fsnotify_evict_group(group);
116
117 /*
118 * now it's off the list, so the only thing we might care about is
119 * srcu access....
120 */
121 mutex_unlock(&fsnotify_grp_mutex);
122 synchronize_srcu(&fsnotify_grp_srcu);
123
124 /* and now it is really dead. _Nothing_ could be seeing it */
125 fsnotify_recalc_global_mask();
126 fsnotify_destroy_group(group);
127}
128
129/*
130 * Simply run the fsnotify_groups list and find a group which matches
131 * the given parameters. If a group is found we take a reference to that
132 * group.
133 */
134static struct fsnotify_group *fsnotify_find_group(unsigned int group_num, __u32 mask,
135 const struct fsnotify_ops *ops)
136{
137 struct fsnotify_group *group_iter;
138 struct fsnotify_group *group = NULL;
139
140 BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex));
141
142 list_for_each_entry_rcu(group_iter, &fsnotify_groups, group_list) {
143 if (group_iter->group_num == group_num) {
144 if ((group_iter->mask == mask) &&
145 (group_iter->ops == ops)) {
146 fsnotify_get_group(group_iter);
147 group = group_iter;
148 } else
149 group = ERR_PTR(-EEXIST);
150 }
151 }
152 return group;
153}
154
155/*
156 * Either finds an existing group which matches the group_num, mask, and ops or
157 * creates a new group and adds it to the global group list. In either case we
158 * take a reference for the group returned.
159 */
160struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask,
161 const struct fsnotify_ops *ops)
162{
163 struct fsnotify_group *group, *tgroup;
164
165 /* very low use, simpler locking if we just always alloc */
166 group = kmalloc(sizeof(struct fsnotify_group), GFP_KERNEL);
167 if (!group)
168 return ERR_PTR(-ENOMEM);
169
170 atomic_set(&group->refcnt, 1);
171
172 group->on_group_list = 0;
173 group->group_num = group_num;
174 group->mask = mask;
175
176 group->ops = ops;
177
178 mutex_lock(&fsnotify_grp_mutex);
179 tgroup = fsnotify_find_group(group_num, mask, ops);
180 if (tgroup) {
181 /* group already exists */
182 mutex_unlock(&fsnotify_grp_mutex);
183 /* destroy the new one we made */
184 fsnotify_put_group(group);
185 return tgroup;
186 }
187
188 /* group not found, add a new one */
189 list_add_rcu(&group->group_list, &fsnotify_groups);
190 group->on_group_list = 1;
191
192 mutex_unlock(&fsnotify_grp_mutex);
193
194 if (mask)
195 fsnotify_recalc_global_mask();
196
197 return group;
198}
diff --git a/fs/notify/inotify/inotify.c b/fs/notify/inotify/inotify.c
index 220c13f0d73d..40b1cf914ccb 100644
--- a/fs/notify/inotify/inotify.c
+++ b/fs/notify/inotify/inotify.c
@@ -32,6 +32,7 @@
32#include <linux/list.h> 32#include <linux/list.h>
33#include <linux/writeback.h> 33#include <linux/writeback.h>
34#include <linux/inotify.h> 34#include <linux/inotify.h>
35#include <linux/fsnotify_backend.h>
35 36
36static atomic_t inotify_cookie; 37static atomic_t inotify_cookie;
37 38
@@ -905,6 +906,25 @@ EXPORT_SYMBOL_GPL(inotify_rm_watch);
905 */ 906 */
906static int __init inotify_setup(void) 907static int __init inotify_setup(void)
907{ 908{
909 BUILD_BUG_ON(IN_ACCESS != FS_ACCESS);
910 BUILD_BUG_ON(IN_MODIFY != FS_MODIFY);
911 BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB);
912 BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE);
913 BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE);
914 BUILD_BUG_ON(IN_OPEN != FS_OPEN);
915 BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM);
916 BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO);
917 BUILD_BUG_ON(IN_CREATE != FS_CREATE);
918 BUILD_BUG_ON(IN_DELETE != FS_DELETE);
919 BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF);
920 BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF);
921 BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW);
922
923 BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT);
924 BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR);
925 BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED);
926 BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
927
908 atomic_set(&inotify_cookie, 0); 928 atomic_set(&inotify_cookie, 0);
909 929
910 return 0; 930 return 0;
diff --git a/fs/notify/notification.c b/fs/notify/notification.c
new file mode 100644
index 000000000000..b8e9a87f8f58
--- /dev/null
+++ b/fs/notify/notification.c
@@ -0,0 +1,121 @@
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/fs.h>
20#include <linux/init.h>
21#include <linux/kernel.h>
22#include <linux/list.h>
23#include <linux/mount.h>
24#include <linux/mutex.h>
25#include <linux/namei.h>
26#include <linux/path.h>
27#include <linux/slab.h>
28#include <linux/spinlock.h>
29
30#include <asm/atomic.h>
31
32#include <linux/fsnotify_backend.h>
33#include "fsnotify.h"
34
35static struct kmem_cache *fsnotify_event_cachep;
36
37void fsnotify_get_event(struct fsnotify_event *event)
38{
39 atomic_inc(&event->refcnt);
40}
41
42void fsnotify_put_event(struct fsnotify_event *event)
43{
44 if (!event)
45 return;
46
47 if (atomic_dec_and_test(&event->refcnt)) {
48 if (event->data_type == FSNOTIFY_EVENT_PATH)
49 path_put(&event->path);
50
51 kmem_cache_free(fsnotify_event_cachep, event);
52 }
53}
54
55/*
56 * Allocate a new event which will be sent to each group's handle_event function
57 * if the group was interested in this particular event.
58 */
59struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
60 void *data, int data_type)
61{
62 struct fsnotify_event *event;
63
64 event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL);
65 if (!event)
66 return NULL;
67
68 atomic_set(&event->refcnt, 1);
69
70 spin_lock_init(&event->lock);
71
72 event->path.dentry = NULL;
73 event->path.mnt = NULL;
74 event->inode = NULL;
75
76 event->to_tell = to_tell;
77
78 switch (data_type) {
79 case FSNOTIFY_EVENT_FILE: {
80 struct file *file = data;
81 struct path *path = &file->f_path;
82 event->path.dentry = path->dentry;
83 event->path.mnt = path->mnt;
84 path_get(&event->path);
85 event->data_type = FSNOTIFY_EVENT_PATH;
86 break;
87 }
88 case FSNOTIFY_EVENT_PATH: {
89 struct path *path = data;
90 event->path.dentry = path->dentry;
91 event->path.mnt = path->mnt;
92 path_get(&event->path);
93 event->data_type = FSNOTIFY_EVENT_PATH;
94 break;
95 }
96 case FSNOTIFY_EVENT_INODE:
97 event->inode = data;
98 event->data_type = FSNOTIFY_EVENT_INODE;
99 break;
100 case FSNOTIFY_EVENT_NONE:
101 event->inode = NULL;
102 event->path.dentry = NULL;
103 event->path.mnt = NULL;
104 break;
105 default:
106 BUG();
107 }
108
109 event->mask = mask;
110
111 return event;
112}
113
114__init int fsnotify_notification_init(void)
115{
116 fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC);
117
118 return 0;
119}
120subsys_initcall(fsnotify_notification_init);
121
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index 00fbd5b245c9..6c9ebefdac8e 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -13,6 +13,7 @@
13 13
14#include <linux/dnotify.h> 14#include <linux/dnotify.h>
15#include <linux/inotify.h> 15#include <linux/inotify.h>
16#include <linux/fsnotify_backend.h>
16#include <linux/audit.h> 17#include <linux/audit.h>
17 18
18/* 19/*
@@ -35,6 +36,16 @@ static inline void fsnotify_d_move(struct dentry *entry)
35} 36}
36 37
37/* 38/*
39 * fsnotify_link_count - inode's link count changed
40 */
41static inline void fsnotify_link_count(struct inode *inode)
42{
43 inotify_inode_queue_event(inode, IN_ATTRIB, 0, NULL, NULL);
44
45 fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE);
46}
47
48/*
38 * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir 49 * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir
39 */ 50 */
40static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, 51static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
@@ -43,28 +54,47 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
43{ 54{
44 struct inode *source = moved->d_inode; 55 struct inode *source = moved->d_inode;
45 u32 cookie = inotify_get_cookie(); 56 u32 cookie = inotify_get_cookie();
57 __u32 old_dir_mask = 0;
58 __u32 new_dir_mask = 0;
46 59
47 if (old_dir == new_dir) 60 if (old_dir == new_dir) {
48 inode_dir_notify(old_dir, DN_RENAME); 61 inode_dir_notify(old_dir, DN_RENAME);
49 else { 62 old_dir_mask = FS_DN_RENAME;
63 } else {
50 inode_dir_notify(old_dir, DN_DELETE); 64 inode_dir_notify(old_dir, DN_DELETE);
65 old_dir_mask = FS_DELETE;
51 inode_dir_notify(new_dir, DN_CREATE); 66 inode_dir_notify(new_dir, DN_CREATE);
67 new_dir_mask = FS_CREATE;
52 } 68 }
53 69
54 if (isdir) 70 if (isdir) {
55 isdir = IN_ISDIR; 71 isdir = IN_ISDIR;
72 old_dir_mask |= FS_IN_ISDIR;
73 new_dir_mask |= FS_IN_ISDIR;
74 }
75
76 old_dir_mask |= FS_MOVED_FROM;
77 new_dir_mask |= FS_MOVED_TO;
78
56 inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name, 79 inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir,cookie,old_name,
57 source); 80 source);
58 inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name, 81 inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, cookie, new_name,
59 source); 82 source);
60 83
84 fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE);
85 fsnotify(new_dir, new_dir_mask, new_dir, FSNOTIFY_EVENT_INODE);
86
61 if (target) { 87 if (target) {
62 inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL); 88 inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL);
63 inotify_inode_is_dead(target); 89 inotify_inode_is_dead(target);
90
91 /* this is really a link_count change not a removal */
92 fsnotify_link_count(target);
64 } 93 }
65 94
66 if (source) { 95 if (source) {
67 inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL); 96 inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL);
97 fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE);
68 } 98 }
69 audit_inode_child(new_name, moved, new_dir); 99 audit_inode_child(new_name, moved, new_dir);
70} 100}
@@ -74,10 +104,12 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
74 */ 104 */
75static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) 105static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
76{ 106{
107 __u32 mask = FS_DELETE;
108
77 if (isdir) 109 if (isdir)
78 isdir = IN_ISDIR; 110 mask |= FS_IN_ISDIR;
79 dnotify_parent(dentry, DN_DELETE); 111 dnotify_parent(dentry, DN_DELETE);
80 inotify_dentry_parent_queue_event(dentry, IN_DELETE|isdir, 0, dentry->d_name.name); 112 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
81} 113}
82 114
83/* 115/*
@@ -87,14 +119,8 @@ static inline void fsnotify_inoderemove(struct inode *inode)
87{ 119{
88 inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL); 120 inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL);
89 inotify_inode_is_dead(inode); 121 inotify_inode_is_dead(inode);
90}
91 122
92/* 123 fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE);
93 * fsnotify_link_count - inode's link count changed
94 */
95static inline void fsnotify_link_count(struct inode *inode)
96{
97 inotify_inode_queue_event(inode, IN_ATTRIB, 0, NULL, NULL);
98} 124}
99 125
100/* 126/*
@@ -106,6 +132,8 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry)
106 inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name, 132 inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name,
107 dentry->d_inode); 133 dentry->d_inode);
108 audit_inode_child(dentry->d_name.name, dentry, inode); 134 audit_inode_child(dentry->d_name.name, dentry, inode);
135
136 fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE);
109} 137}
110 138
111/* 139/*
@@ -120,6 +148,8 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct
120 inode); 148 inode);
121 fsnotify_link_count(inode); 149 fsnotify_link_count(inode);
122 audit_inode_child(new_dentry->d_name.name, new_dentry, dir); 150 audit_inode_child(new_dentry->d_name.name, new_dentry, dir);
151
152 fsnotify(dir, FS_CREATE, inode, FSNOTIFY_EVENT_INODE);
123} 153}
124 154
125/* 155/*
@@ -127,10 +157,14 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct
127 */ 157 */
128static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) 158static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
129{ 159{
160 __u32 mask = (FS_CREATE | FS_IN_ISDIR);
161 struct inode *d_inode = dentry->d_inode;
162
130 inode_dir_notify(inode, DN_CREATE); 163 inode_dir_notify(inode, DN_CREATE);
131 inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, 164 inotify_inode_queue_event(inode, mask, 0, dentry->d_name.name, d_inode);
132 dentry->d_name.name, dentry->d_inode);
133 audit_inode_child(dentry->d_name.name, dentry, inode); 165 audit_inode_child(dentry->d_name.name, dentry, inode);
166
167 fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE);
134} 168}
135 169
136/* 170/*
@@ -139,14 +173,16 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
139static inline void fsnotify_access(struct dentry *dentry) 173static inline void fsnotify_access(struct dentry *dentry)
140{ 174{
141 struct inode *inode = dentry->d_inode; 175 struct inode *inode = dentry->d_inode;
142 u32 mask = IN_ACCESS; 176 __u32 mask = FS_ACCESS;
143 177
144 if (S_ISDIR(inode->i_mode)) 178 if (S_ISDIR(inode->i_mode))
145 mask |= IN_ISDIR; 179 mask |= FS_IN_ISDIR;
146 180
147 dnotify_parent(dentry, DN_ACCESS); 181 dnotify_parent(dentry, DN_ACCESS);
148 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); 182 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
149 inotify_inode_queue_event(inode, mask, 0, NULL, NULL); 183 inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
184
185 fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE);
150} 186}
151 187
152/* 188/*
@@ -155,14 +191,16 @@ static inline void fsnotify_access(struct dentry *dentry)
155static inline void fsnotify_modify(struct dentry *dentry) 191static inline void fsnotify_modify(struct dentry *dentry)
156{ 192{
157 struct inode *inode = dentry->d_inode; 193 struct inode *inode = dentry->d_inode;
158 u32 mask = IN_MODIFY; 194 __u32 mask = FS_MODIFY;
159 195
160 if (S_ISDIR(inode->i_mode)) 196 if (S_ISDIR(inode->i_mode))
161 mask |= IN_ISDIR; 197 mask |= FS_IN_ISDIR;
162 198
163 dnotify_parent(dentry, DN_MODIFY); 199 dnotify_parent(dentry, DN_MODIFY);
164 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); 200 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
165 inotify_inode_queue_event(inode, mask, 0, NULL, NULL); 201 inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
202
203 fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE);
166} 204}
167 205
168/* 206/*
@@ -171,13 +209,15 @@ static inline void fsnotify_modify(struct dentry *dentry)
171static inline void fsnotify_open(struct dentry *dentry) 209static inline void fsnotify_open(struct dentry *dentry)
172{ 210{
173 struct inode *inode = dentry->d_inode; 211 struct inode *inode = dentry->d_inode;
174 u32 mask = IN_OPEN; 212 __u32 mask = FS_OPEN;
175 213
176 if (S_ISDIR(inode->i_mode)) 214 if (S_ISDIR(inode->i_mode))
177 mask |= IN_ISDIR; 215 mask |= FS_IN_ISDIR;
178 216
179 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); 217 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
180 inotify_inode_queue_event(inode, mask, 0, NULL, NULL); 218 inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
219
220 fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE);
181} 221}
182 222
183/* 223/*
@@ -189,13 +229,15 @@ static inline void fsnotify_close(struct file *file)
189 struct inode *inode = dentry->d_inode; 229 struct inode *inode = dentry->d_inode;
190 const char *name = dentry->d_name.name; 230 const char *name = dentry->d_name.name;
191 fmode_t mode = file->f_mode; 231 fmode_t mode = file->f_mode;
192 u32 mask = (mode & FMODE_WRITE) ? IN_CLOSE_WRITE : IN_CLOSE_NOWRITE; 232 __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
193 233
194 if (S_ISDIR(inode->i_mode)) 234 if (S_ISDIR(inode->i_mode))
195 mask |= IN_ISDIR; 235 mask |= FS_IN_ISDIR;
196 236
197 inotify_dentry_parent_queue_event(dentry, mask, 0, name); 237 inotify_dentry_parent_queue_event(dentry, mask, 0, name);
198 inotify_inode_queue_event(inode, mask, 0, NULL, NULL); 238 inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
239
240 fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE);
199} 241}
200 242
201/* 243/*
@@ -204,13 +246,15 @@ static inline void fsnotify_close(struct file *file)
204static inline void fsnotify_xattr(struct dentry *dentry) 246static inline void fsnotify_xattr(struct dentry *dentry)
205{ 247{
206 struct inode *inode = dentry->d_inode; 248 struct inode *inode = dentry->d_inode;
207 u32 mask = IN_ATTRIB; 249 __u32 mask = FS_ATTRIB;
208 250
209 if (S_ISDIR(inode->i_mode)) 251 if (S_ISDIR(inode->i_mode))
210 mask |= IN_ISDIR; 252 mask |= FS_IN_ISDIR;
211 253
212 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); 254 inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
213 inotify_inode_queue_event(inode, mask, 0, NULL, NULL); 255 inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
256
257 fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE);
214} 258}
215 259
216/* 260/*
@@ -221,34 +265,34 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
221{ 265{
222 struct inode *inode = dentry->d_inode; 266 struct inode *inode = dentry->d_inode;
223 int dn_mask = 0; 267 int dn_mask = 0;
224 u32 in_mask = 0; 268 __u32 in_mask = 0;
225 269
226 if (ia_valid & ATTR_UID) { 270 if (ia_valid & ATTR_UID) {
227 in_mask |= IN_ATTRIB; 271 in_mask |= FS_ATTRIB;
228 dn_mask |= DN_ATTRIB; 272 dn_mask |= DN_ATTRIB;
229 } 273 }
230 if (ia_valid & ATTR_GID) { 274 if (ia_valid & ATTR_GID) {
231 in_mask |= IN_ATTRIB; 275 in_mask |= FS_ATTRIB;
232 dn_mask |= DN_ATTRIB; 276 dn_mask |= DN_ATTRIB;
233 } 277 }
234 if (ia_valid & ATTR_SIZE) { 278 if (ia_valid & ATTR_SIZE) {
235 in_mask |= IN_MODIFY; 279 in_mask |= FS_MODIFY;
236 dn_mask |= DN_MODIFY; 280 dn_mask |= DN_MODIFY;
237 } 281 }
238 /* both times implies a utime(s) call */ 282 /* both times implies a utime(s) call */
239 if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) 283 if ((ia_valid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME))
240 { 284 {
241 in_mask |= IN_ATTRIB; 285 in_mask |= FS_ATTRIB;
242 dn_mask |= DN_ATTRIB; 286 dn_mask |= DN_ATTRIB;
243 } else if (ia_valid & ATTR_ATIME) { 287 } else if (ia_valid & ATTR_ATIME) {
244 in_mask |= IN_ACCESS; 288 in_mask |= FS_ACCESS;
245 dn_mask |= DN_ACCESS; 289 dn_mask |= DN_ACCESS;
246 } else if (ia_valid & ATTR_MTIME) { 290 } else if (ia_valid & ATTR_MTIME) {
247 in_mask |= IN_MODIFY; 291 in_mask |= FS_MODIFY;
248 dn_mask |= DN_MODIFY; 292 dn_mask |= DN_MODIFY;
249 } 293 }
250 if (ia_valid & ATTR_MODE) { 294 if (ia_valid & ATTR_MODE) {
251 in_mask |= IN_ATTRIB; 295 in_mask |= FS_ATTRIB;
252 dn_mask |= DN_ATTRIB; 296 dn_mask |= DN_ATTRIB;
253 } 297 }
254 298
@@ -256,14 +300,15 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
256 dnotify_parent(dentry, dn_mask); 300 dnotify_parent(dentry, dn_mask);
257 if (in_mask) { 301 if (in_mask) {
258 if (S_ISDIR(inode->i_mode)) 302 if (S_ISDIR(inode->i_mode))
259 in_mask |= IN_ISDIR; 303 in_mask |= FS_IN_ISDIR;
260 inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL); 304 inotify_inode_queue_event(inode, in_mask, 0, NULL, NULL);
261 inotify_dentry_parent_queue_event(dentry, in_mask, 0, 305 inotify_dentry_parent_queue_event(dentry, in_mask, 0,
262 dentry->d_name.name); 306 dentry->d_name.name);
307 fsnotify(inode, in_mask, inode, FSNOTIFY_EVENT_INODE);
263 } 308 }
264} 309}
265 310
266#ifdef CONFIG_INOTIFY /* inotify helpers */ 311#if defined(CONFIG_INOTIFY) || defined(CONFIG_FSNOTIFY) /* notify helpers */
267 312
268/* 313/*
269 * fsnotify_oldname_init - save off the old filename before we change it 314 * fsnotify_oldname_init - save off the old filename before we change it
@@ -281,7 +326,7 @@ static inline void fsnotify_oldname_free(const char *old_name)
281 kfree(old_name); 326 kfree(old_name);
282} 327}
283 328
284#else /* CONFIG_INOTIFY */ 329#else /* CONFIG_INOTIFY || CONFIG_FSNOTIFY */
285 330
286static inline const char *fsnotify_oldname_init(const char *name) 331static inline const char *fsnotify_oldname_init(const char *name)
287{ 332{
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
new file mode 100644
index 000000000000..1a55718b38aa
--- /dev/null
+++ b/include/linux/fsnotify_backend.h
@@ -0,0 +1,177 @@
1/*
2 * Filesystem access notification for Linux
3 *
4 * Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
5 */
6
7#ifndef __LINUX_FSNOTIFY_BACKEND_H
8#define __LINUX_FSNOTIFY_BACKEND_H
9
10#ifdef __KERNEL__
11
12#include <linux/fs.h> /* struct inode */
13#include <linux/list.h>
14#include <linux/path.h> /* struct path */
15#include <linux/spinlock.h>
16#include <linux/types.h>
17
18#include <asm/atomic.h>
19
20/*
21 * IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily
22 * convert between them. dnotify only needs conversion at watch creation
23 * so no perf loss there. fanotify isn't defined yet, so it can use the
24 * wholes if it needs more events.
25 */
26#define FS_ACCESS 0x00000001 /* File was accessed */
27#define FS_MODIFY 0x00000002 /* File was modified */
28#define FS_ATTRIB 0x00000004 /* Metadata changed */
29#define FS_CLOSE_WRITE 0x00000008 /* Writtable file was closed */
30#define FS_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
31#define FS_OPEN 0x00000020 /* File was opened */
32#define FS_MOVED_FROM 0x00000040 /* File was moved from X */
33#define FS_MOVED_TO 0x00000080 /* File was moved to Y */
34#define FS_CREATE 0x00000100 /* Subfile was created */
35#define FS_DELETE 0x00000200 /* Subfile was deleted */
36#define FS_DELETE_SELF 0x00000400 /* Self was deleted */
37#define FS_MOVE_SELF 0x00000800 /* Self was moved */
38
39#define FS_UNMOUNT 0x00002000 /* inode on umount fs */
40#define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
41#define FS_IN_IGNORED 0x00008000 /* last inotify event here */
42
43#define FS_IN_ISDIR 0x40000000 /* event occurred against dir */
44#define FS_IN_ONESHOT 0x80000000 /* only send event once */
45
46#define FS_DN_RENAME 0x10000000 /* file renamed */
47#define FS_DN_MULTISHOT 0x20000000 /* dnotify multishot */
48
49struct fsnotify_group;
50struct fsnotify_event;
51
52/*
53 * Each group much define these ops. The fsnotify infrastructure will call
54 * these operations for each relevant group.
55 *
56 * handle_event - main call for a group to handle an fs event
57 * free_group_priv - called when a group refcnt hits 0 to clean up the private union
58 */
59struct fsnotify_ops {
60 int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event);
61 void (*free_group_priv)(struct fsnotify_group *group);
62};
63
64/*
65 * A group is a "thing" that wants to receive notification about filesystem
66 * events. The mask holds the subset of event types this group cares about.
67 * refcnt on a group is up to the implementor and at any moment if it goes 0
68 * everything will be cleaned up.
69 */
70struct fsnotify_group {
71 /*
72 * global list of all groups receiving events from fsnotify.
73 * anchored by fsnotify_groups and protected by either fsnotify_grp_mutex
74 * or fsnotify_grp_srcu depending on write vs read.
75 */
76 struct list_head group_list;
77
78 /*
79 * Defines all of the event types in which this group is interested.
80 * This mask is a bitwise OR of the FS_* events from above. Each time
81 * this mask changes for a group (if it changes) the correct functions
82 * must be called to update the global structures which indicate global
83 * interest in event types.
84 */
85 __u32 mask;
86
87 /*
88 * How the refcnt is used is up to each group. When the refcnt hits 0
89 * fsnotify will clean up all of the resources associated with this group.
90 * As an example, the dnotify group will always have a refcnt=1 and that
91 * will never change. Inotify, on the other hand, has a group per
92 * inotify_init() and the refcnt will hit 0 only when that fd has been
93 * closed.
94 */
95 atomic_t refcnt; /* things with interest in this group */
96 unsigned int group_num; /* simply prevents accidental group collision */
97
98 const struct fsnotify_ops *ops; /* how this group handles things */
99
100 /* prevents double list_del of group_list. protected by global fsnotify_gr_mutex */
101 bool on_group_list;
102
103 /* groups can define private fields here or use the void *private */
104 union {
105 void *private;
106 };
107};
108
109/*
110 * all of the information about the original object we want to now send to
111 * a group. If you want to carry more info from the accessing task to the
112 * listener this structure is where you need to be adding fields.
113 */
114struct fsnotify_event {
115 spinlock_t lock; /* protection for the associated event_holder and private_list */
116 /* to_tell may ONLY be dereferenced during handle_event(). */
117 struct inode *to_tell; /* either the inode the event happened to or its parent */
118 /*
119 * depending on the event type we should have either a path or inode
120 * We hold a reference on path, but NOT on inode. Since we have the ref on
121 * the path, it may be dereferenced at any point during this object's
122 * lifetime. That reference is dropped when this object's refcnt hits
123 * 0. If this event contains an inode instead of a path, the inode may
124 * ONLY be used during handle_event().
125 */
126 union {
127 struct path path;
128 struct inode *inode;
129 };
130/* when calling fsnotify tell it if the data is a path or inode */
131#define FSNOTIFY_EVENT_NONE 0
132#define FSNOTIFY_EVENT_PATH 1
133#define FSNOTIFY_EVENT_INODE 2
134#define FSNOTIFY_EVENT_FILE 3
135 int data_type; /* which of the above union we have */
136 atomic_t refcnt; /* how many groups still are using/need to send this event */
137 __u32 mask; /* the type of access, bitwise OR for FS_* event types */
138};
139
140#ifdef CONFIG_FSNOTIFY
141
142/* called from the vfs helpers */
143
144/* main fsnotify call to send events */
145extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is);
146
147
148/* called from fsnotify listeners, such as fanotify or dnotify */
149
150/* must call when a group changes its ->mask */
151extern void fsnotify_recalc_global_mask(void);
152/* get a reference to an existing or create a new group */
153extern struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num,
154 __u32 mask,
155 const struct fsnotify_ops *ops);
156/* drop reference on a group from fsnotify_obtain_group */
157extern void fsnotify_put_group(struct fsnotify_group *group);
158
159/* take a reference to an event */
160extern void fsnotify_get_event(struct fsnotify_event *event);
161extern void fsnotify_put_event(struct fsnotify_event *event);
162/* find private data previously attached to an event */
163extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group,
164 struct fsnotify_event *event);
165
166/* put here because inotify does some weird stuff when destroying watches */
167extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
168 void *data, int data_is);
169#else
170
171static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is)
172{}
173#endif /* CONFIG_FSNOTIFY */
174
175#endif /* __KERNEL __ */
176
177#endif /* __LINUX_FSNOTIFY_BACKEND_H */