diff options
-rw-r--r-- | fs/notify/fanotify/fanotify.h | 18 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 239 | ||||
-rw-r--r-- | include/linux/fanotify.h | 13 |
3 files changed, 269 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 | ||
7 | extern const struct fsnotify_ops fanotify_fsnotify_ops; | 7 | extern const struct fsnotify_ops fanotify_fsnotify_ops; |
8 | 8 | ||
9 | static 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 | |||
9 | static inline bool fanotify_mask_valid(__u32 mask) | 27 | static 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 | ||
14 | static struct kmem_cache *fanotify_mark_cache __read_mostly; | ||
15 | |||
10 | static int fanotify_release(struct inode *ignored, struct file *file) | 16 | static 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 | ||
37 | static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) | ||
38 | { | ||
39 | kmem_cache_free(fanotify_mark_cache, fsn_mark); | ||
40 | } | ||
41 | |||
42 | static 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); | ||
86 | out: | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static 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 | |||
122 | static 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); | ||
181 | out: | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static 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 | |||
202 | static 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 */ |
32 | SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, | 217 | SYSCALL_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: | |||
74 | SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, | 259 | SYSCALL_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); | ||
299 | fput_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 | */ | ||
309 | static int __init fanotify_user_setup(void) | ||
310 | { | ||
311 | fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); | ||
312 | |||
313 | return 0; | ||
78 | } | 314 | } |
315 | device_initcall(fanotify_user_setup); | ||
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 00bc6d4fbb58..95aeea2a3ca6 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h | |||
@@ -18,10 +18,23 @@ | |||
18 | /* helper events */ | 18 | /* helper events */ |
19 | #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ | 19 | #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ |
20 | 20 | ||
21 | /* flags used for fanotify_init() */ | ||
21 | #define FAN_CLOEXEC 0x00000001 | 22 | #define FAN_CLOEXEC 0x00000001 |
22 | #define FAN_NONBLOCK 0x00000002 | 23 | #define FAN_NONBLOCK 0x00000002 |
23 | 24 | ||
24 | #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK) | 25 | #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK) |
26 | |||
27 | /* flags used for fanotify_modify_mark() */ | ||
28 | #define FAN_MARK_ADD 0x00000001 | ||
29 | #define FAN_MARK_REMOVE 0x00000002 | ||
30 | #define FAN_MARK_DONT_FOLLOW 0x00000004 | ||
31 | #define FAN_MARK_ONLYDIR 0x00000008 | ||
32 | |||
33 | #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ | ||
34 | FAN_MARK_REMOVE |\ | ||
35 | FAN_MARK_DONT_FOLLOW |\ | ||
36 | FAN_MARK_ONLYDIR) | ||
37 | |||
25 | /* | 38 | /* |
26 | * All of the events - we build the list by hand so that we can add flags in | 39 | * All of the events - we build the list by hand so that we can add flags in |
27 | * the future and not break backward compatibility. Apps will get only the | 40 | * the future and not break backward compatibility. Apps will get only the |