aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 21:24:26 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:56 -0400
commit2a3edf86040a7e15684525a2aadc29f532c51325 (patch)
treecf0a5436139125d054fe46b75d5388c914b22c21 /fs
parentbbaa4168b2d2d8cc674e6d35806e8426aef464b8 (diff)
fanotify: fanotify_mark syscall implementation
NAME fanotify_mark - add, remove, or modify an fanotify mark on a filesystem object SYNOPSIS int fanotify_mark(int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname) DESCRIPTION fanotify_mark() is used to add remove or modify a mark on a filesystem object. Marks are used to indicate that the fanotify group is interested in events which occur on that object. At this point in time marks may only be added to files and directories. fanotify_fd must be a file descriptor returned by fanotify_init() The flags field must contain exactly one of the following: FAN_MARK_ADD - or the bits in mask and ignored mask into the mark FAN_MARK_REMOVE - bitwise remove the bits in mask and ignored mark from the mark The following values can be OR'd into the flags field: FAN_MARK_DONT_FOLLOW - same meaning as O_NOFOLLOW as described in open(2) FAN_MARK_ONLYDIR - same meaning as O_DIRECTORY as described in open(2) dfd may be any of the following: AT_FDCWD: the object will be lookup up based on pathname similar to open(2) file descriptor of a directory: if pathname is not NULL the object to modify will be lookup up similar to openat(2) file descriptor of the final object: if pathname is NULL the object to modify will be the object referenced by dfd The mask is the bitwise OR of the set of events of interest such as: FAN_ACCESS - object was accessed (read) FAN_MODIFY - object was modified (write) FAN_CLOSE_WRITE - object was writable and was closed FAN_CLOSE_NOWRITE - object was read only and was closed FAN_OPEN - object was opened FAN_EVENT_ON_CHILD - interested in objected that happen to children. Only relavent when the object is a directory FAN_Q_OVERFLOW - event queue overflowed (not implemented) RETURN VALUE On success, this system call returns 0. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL An invalid value was specified in mask. EINVAL An invalid value was specified in ignored_mask. EINVAL fanotify_fd is not a file descriptor as returned by fanotify_init() EBADF fanotify_fd is not a valid file descriptor EBADF dfd is not a valid file descriptor and path is NULL. ENOTDIR dfd is not a directory and path is not NULL EACCESS no search permissions on some part of the path ENENT file not found ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify.h18
-rw-r--r--fs/notify/fanotify/fanotify_user.c239
2 files changed, 256 insertions, 1 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index dd656cfab1ba..59c3331a0e81 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -6,6 +6,24 @@
6 6
7extern const struct fsnotify_ops fanotify_fsnotify_ops; 7extern const struct fsnotify_ops fanotify_fsnotify_ops;
8 8
9static inline bool fanotify_mark_flags_valid(unsigned int flags)
10{
11 /* must be either and add or a remove */
12 if (!(flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)))
13 return false;
14
15 /* cannot be both add and remove */
16 if ((flags & FAN_MARK_ADD) &&
17 (flags & FAN_MARK_REMOVE))
18 return false;
19
20 /* cannot have more flags than we know about */
21 if (flags & ~FAN_ALL_MARK_FLAGS)
22 return false;
23
24 return true;
25}
26
9static inline bool fanotify_mask_valid(__u32 mask) 27static inline bool fanotify_mask_valid(__u32 mask)
10{ 28{
11 if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) 29 if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS))
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 55d6e379f2b6..bc4fa48157f1 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1,12 +1,18 @@
1#include <linux/fcntl.h> 1#include <linux/fcntl.h>
2#include <linux/file.h>
2#include <linux/fs.h> 3#include <linux/fs.h>
3#include <linux/anon_inodes.h> 4#include <linux/anon_inodes.h>
4#include <linux/fsnotify_backend.h> 5#include <linux/fsnotify_backend.h>
6#include <linux/init.h>
7#include <linux/namei.h>
5#include <linux/security.h> 8#include <linux/security.h>
6#include <linux/syscalls.h> 9#include <linux/syscalls.h>
10#include <linux/types.h>
7 11
8#include "fanotify.h" 12#include "fanotify.h"
9 13
14static struct kmem_cache *fanotify_mark_cache __read_mostly;
15
10static int fanotify_release(struct inode *ignored, struct file *file) 16static int fanotify_release(struct inode *ignored, struct file *file)
11{ 17{
12 struct fsnotify_group *group = file->private_data; 18 struct fsnotify_group *group = file->private_data;
@@ -28,6 +34,185 @@ static const struct file_operations fanotify_fops = {
28 .compat_ioctl = NULL, 34 .compat_ioctl = NULL,
29}; 35};
30 36
37static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
38{
39 kmem_cache_free(fanotify_mark_cache, fsn_mark);
40}
41
42static int fanotify_find_path(int dfd, const char __user *filename,
43 struct path *path, unsigned int flags)
44{
45 int ret;
46
47 pr_debug("%s: dfd=%d filename=%p flags=%x\n", __func__,
48 dfd, filename, flags);
49
50 if (filename == NULL) {
51 struct file *file;
52 int fput_needed;
53
54 ret = -EBADF;
55 file = fget_light(dfd, &fput_needed);
56 if (!file)
57 goto out;
58
59 ret = -ENOTDIR;
60 if ((flags & FAN_MARK_ONLYDIR) &&
61 !(S_ISDIR(file->f_path.dentry->d_inode->i_mode))) {
62 fput_light(file, fput_needed);
63 goto out;
64 }
65
66 *path = file->f_path;
67 path_get(path);
68 fput_light(file, fput_needed);
69 } else {
70 unsigned int lookup_flags = 0;
71
72 if (!(flags & FAN_MARK_DONT_FOLLOW))
73 lookup_flags |= LOOKUP_FOLLOW;
74 if (flags & FAN_MARK_ONLYDIR)
75 lookup_flags |= LOOKUP_DIRECTORY;
76
77 ret = user_path_at(dfd, filename, lookup_flags, path);
78 if (ret)
79 goto out;
80 }
81
82 /* you can only watch an inode if you have read permissions on it */
83 ret = inode_permission(path->dentry->d_inode, MAY_READ);
84 if (ret)
85 path_put(path);
86out:
87 return ret;
88}
89
90static int fanotify_remove_mark(struct fsnotify_group *group,
91 struct inode *inode,
92 __u32 mask)
93{
94 struct fsnotify_mark *fsn_mark;
95 __u32 new_mask;
96
97 pr_debug("%s: group=%p inode=%p mask=%x\n", __func__,
98 group, inode, mask);
99
100 fsn_mark = fsnotify_find_mark(group, inode);
101 if (!fsn_mark)
102 return -ENOENT;
103
104 spin_lock(&fsn_mark->lock);
105 fsn_mark->mask &= ~mask;
106 new_mask = fsn_mark->mask;
107 spin_unlock(&fsn_mark->lock);
108
109 if (!new_mask)
110 fsnotify_destroy_mark(fsn_mark);
111 else
112 fsnotify_recalc_inode_mask(inode);
113
114 fsnotify_recalc_group_mask(group);
115
116 /* matches the fsnotify_find_mark() */
117 fsnotify_put_mark(fsn_mark);
118
119 return 0;
120}
121
122static int fanotify_add_mark(struct fsnotify_group *group,
123 struct inode *inode,
124 __u32 mask)
125{
126 struct fsnotify_mark *fsn_mark;
127 __u32 old_mask, new_mask;
128 int ret;
129
130 pr_debug("%s: group=%p inode=%p mask=%x\n", __func__,
131 group, inode, mask);
132
133 fsn_mark = fsnotify_find_mark(group, inode);
134 if (!fsn_mark) {
135 struct fsnotify_mark *new_fsn_mark;
136
137 ret = -ENOMEM;
138 new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
139 if (!new_fsn_mark)
140 goto out;
141
142 fsnotify_init_mark(new_fsn_mark, fanotify_free_mark);
143 ret = fsnotify_add_mark(new_fsn_mark, group, inode, 0);
144 if (ret) {
145 fanotify_free_mark(new_fsn_mark);
146 goto out;
147 }
148
149 fsn_mark = new_fsn_mark;
150 }
151
152 ret = 0;
153
154 spin_lock(&fsn_mark->lock);
155 old_mask = fsn_mark->mask;
156 fsn_mark->mask |= mask;
157 new_mask = fsn_mark->mask;
158 spin_unlock(&fsn_mark->lock);
159
160 /* we made changes to a mask, update the group mask and the inode mask
161 * so things happen quickly. */
162 if (old_mask != new_mask) {
163 /* more bits in old than in new? */
164 int dropped = (old_mask & ~new_mask);
165 /* more bits in this mark than the inode's mask? */
166 int do_inode = (new_mask & ~inode->i_fsnotify_mask);
167 /* more bits in this mark than the group? */
168 int do_group = (new_mask & ~group->mask);
169
170 /* update the inode with this new mark */
171 if (dropped || do_inode)
172 fsnotify_recalc_inode_mask(inode);
173
174 /* update the group mask with the new mask */
175 if (dropped || do_group)
176 fsnotify_recalc_group_mask(group);
177 }
178
179 /* match the init or the find.... */
180 fsnotify_put_mark(fsn_mark);
181out:
182 return ret;
183}
184
185static int fanotify_update_mark(struct fsnotify_group *group,
186 struct inode *inode, int flags,
187 __u32 mask)
188{
189 pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__,
190 group, inode, flags, mask);
191
192 if (flags & FAN_MARK_ADD)
193 fanotify_add_mark(group, inode, mask);
194 else if (flags & FAN_MARK_REMOVE)
195 fanotify_remove_mark(group, inode, mask);
196 else
197 BUG();
198
199 return 0;
200}
201
202static bool fanotify_mark_validate_input(int flags,
203 __u32 mask)
204{
205 pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask);
206
207 /* are flags valid of this operation? */
208 if (!fanotify_mark_flags_valid(flags))
209 return false;
210 /* is the mask valid? */
211 if (!fanotify_mask_valid(mask))
212 return false;
213 return true;
214}
215
31/* fanotify syscalls */ 216/* fanotify syscalls */
32SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, 217SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags,
33 unsigned int, priority) 218 unsigned int, priority)
@@ -74,5 +259,57 @@ out_put_group:
74SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, 259SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags,
75 __u64, mask, int, dfd, const char __user *, pathname) 260 __u64, mask, int, dfd, const char __user *, pathname)
76{ 261{
77 return -ENOSYS; 262 struct inode *inode;
263 struct fsnotify_group *group;
264 struct file *filp;
265 struct path path;
266 int ret, fput_needed;
267
268 pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
269 __func__, fanotify_fd, flags, dfd, pathname, mask);
270
271 /* we only use the lower 32 bits as of right now. */
272 if (mask & ((__u64)0xffffffff << 32))
273 return -EINVAL;
274
275 if (!fanotify_mark_validate_input(flags, mask))
276 return -EINVAL;
277
278 filp = fget_light(fanotify_fd, &fput_needed);
279 if (unlikely(!filp))
280 return -EBADF;
281
282 /* verify that this is indeed an fanotify instance */
283 ret = -EINVAL;
284 if (unlikely(filp->f_op != &fanotify_fops))
285 goto fput_and_out;
286
287 ret = fanotify_find_path(dfd, pathname, &path, flags);
288 if (ret)
289 goto fput_and_out;
290
291 /* inode held in place by reference to path; group by fget on fd */
292 inode = path.dentry->d_inode;
293 group = filp->private_data;
294
295 /* create/update an inode mark */
296 ret = fanotify_update_mark(group, inode, flags, mask);
297
298 path_put(&path);
299fput_and_out:
300 fput_light(filp, fput_needed);
301 return ret;
302}
303
304/*
305 * fanotify_user_setup - Our initialization function. Note that we cannnot return
306 * error because we have compiled-in VFS hooks. So an (unlikely) failure here
307 * must result in panic().
308 */
309static int __init fanotify_user_setup(void)
310{
311 fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC);
312
313 return 0;
78} 314}
315device_initcall(fanotify_user_setup);