diff options
author | Eric Paris <eparis@redhat.com> | 2009-12-17 21:24:34 -0500 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2010-07-28 09:59:02 -0400 |
commit | b2d879096ac799722e6017ee82c0586f0d101c9c (patch) | |
tree | 3628e99772d2bf51ce736a775a056bffaae44e8c /fs/notify/fanotify | |
parent | 9e66e4233db9c7e31e9ee706be2c9ddd54cf99b3 (diff) |
fanotify: userspace interface for permission responses
fanotify groups need to respond to events which include permissions types.
To do so groups will send a response using write() on the fanotify_fd they
have open.
Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs/notify/fanotify')
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 3 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 182 |
2 files changed, 179 insertions, 6 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 52d0a55a249e..bbcfccd4a8ea 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
@@ -114,6 +114,9 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, | |||
114 | event->response = 0; | 114 | event->response = 0; |
115 | spin_unlock(&event->lock); | 115 | spin_unlock(&event->lock); |
116 | 116 | ||
117 | pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, | ||
118 | group, event, ret); | ||
119 | |||
117 | return ret; | 120 | return ret; |
118 | } | 121 | } |
119 | #endif | 122 | #endif |
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 09d9bdb62af3..87f0be852f71 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
@@ -18,6 +18,13 @@ | |||
18 | extern const struct fsnotify_ops fanotify_fsnotify_ops; | 18 | extern const struct fsnotify_ops fanotify_fsnotify_ops; |
19 | 19 | ||
20 | static struct kmem_cache *fanotify_mark_cache __read_mostly; | 20 | static struct kmem_cache *fanotify_mark_cache __read_mostly; |
21 | static struct kmem_cache *fanotify_response_event_cache __read_mostly; | ||
22 | |||
23 | struct fanotify_response_event { | ||
24 | struct list_head list; | ||
25 | __s32 fd; | ||
26 | struct fsnotify_event *event; | ||
27 | }; | ||
21 | 28 | ||
22 | /* | 29 | /* |
23 | * Get an fsnotify notification event if one exists and is small | 30 | * Get an fsnotify notification event if one exists and is small |
@@ -110,23 +117,152 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, | |||
110 | return metadata->fd; | 117 | return metadata->fd; |
111 | } | 118 | } |
112 | 119 | ||
120 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | ||
121 | static struct fanotify_response_event *dequeue_re(struct fsnotify_group *group, | ||
122 | __s32 fd) | ||
123 | { | ||
124 | struct fanotify_response_event *re, *return_re = NULL; | ||
125 | |||
126 | mutex_lock(&group->fanotify_data.access_mutex); | ||
127 | list_for_each_entry(re, &group->fanotify_data.access_list, list) { | ||
128 | if (re->fd != fd) | ||
129 | continue; | ||
130 | |||
131 | list_del_init(&re->list); | ||
132 | return_re = re; | ||
133 | break; | ||
134 | } | ||
135 | mutex_unlock(&group->fanotify_data.access_mutex); | ||
136 | |||
137 | pr_debug("%s: found return_re=%p\n", __func__, return_re); | ||
138 | |||
139 | return return_re; | ||
140 | } | ||
141 | |||
142 | static int process_access_response(struct fsnotify_group *group, | ||
143 | struct fanotify_response *response_struct) | ||
144 | { | ||
145 | struct fanotify_response_event *re; | ||
146 | __s32 fd = response_struct->fd; | ||
147 | __u32 response = response_struct->response; | ||
148 | |||
149 | pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group, | ||
150 | fd, response); | ||
151 | /* | ||
152 | * make sure the response is valid, if invalid we do nothing and either | ||
153 | * userspace can send a valid responce or we will clean it up after the | ||
154 | * timeout | ||
155 | */ | ||
156 | switch (response) { | ||
157 | case FAN_ALLOW: | ||
158 | case FAN_DENY: | ||
159 | break; | ||
160 | default: | ||
161 | return -EINVAL; | ||
162 | } | ||
163 | |||
164 | if (fd < 0) | ||
165 | return -EINVAL; | ||
166 | |||
167 | re = dequeue_re(group, fd); | ||
168 | if (!re) | ||
169 | return -ENOENT; | ||
170 | |||
171 | re->event->response = response; | ||
172 | |||
173 | wake_up(&group->fanotify_data.access_waitq); | ||
174 | |||
175 | kmem_cache_free(fanotify_response_event_cache, re); | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | static int prepare_for_access_response(struct fsnotify_group *group, | ||
181 | struct fsnotify_event *event, | ||
182 | __s32 fd) | ||
183 | { | ||
184 | struct fanotify_response_event *re; | ||
185 | |||
186 | if (!(event->mask & FAN_ALL_PERM_EVENTS)) | ||
187 | return 0; | ||
188 | |||
189 | re = kmem_cache_alloc(fanotify_response_event_cache, GFP_KERNEL); | ||
190 | if (!re) | ||
191 | return -ENOMEM; | ||
192 | |||
193 | re->event = event; | ||
194 | re->fd = fd; | ||
195 | |||
196 | mutex_lock(&group->fanotify_data.access_mutex); | ||
197 | list_add_tail(&re->list, &group->fanotify_data.access_list); | ||
198 | mutex_unlock(&group->fanotify_data.access_mutex); | ||
199 | |||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | static void remove_access_response(struct fsnotify_group *group, | ||
204 | struct fsnotify_event *event, | ||
205 | __s32 fd) | ||
206 | { | ||
207 | struct fanotify_response_event *re; | ||
208 | |||
209 | if (!(event->mask & FAN_ALL_PERM_EVENTS)) | ||
210 | return; | ||
211 | |||
212 | re = dequeue_re(group, fd); | ||
213 | if (!re) | ||
214 | return; | ||
215 | |||
216 | BUG_ON(re->event != event); | ||
217 | |||
218 | kmem_cache_free(fanotify_response_event_cache, re); | ||
219 | |||
220 | return; | ||
221 | } | ||
222 | #else | ||
223 | static int prepare_for_access_response(struct fsnotify_group *group, | ||
224 | struct fsnotify_event *event, | ||
225 | __s32 fd) | ||
226 | { | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | static void remove_access_response(struct fsnotify_group *group, | ||
231 | struct fsnotify_event *event, | ||
232 | __s32 fd) | ||
233 | { | ||
234 | return 0; | ||
235 | } | ||
236 | #endif | ||
237 | |||
113 | static ssize_t copy_event_to_user(struct fsnotify_group *group, | 238 | static ssize_t copy_event_to_user(struct fsnotify_group *group, |
114 | struct fsnotify_event *event, | 239 | struct fsnotify_event *event, |
115 | char __user *buf) | 240 | char __user *buf) |
116 | { | 241 | { |
117 | struct fanotify_event_metadata fanotify_event_metadata; | 242 | struct fanotify_event_metadata fanotify_event_metadata; |
118 | int ret; | 243 | int fd, ret; |
119 | 244 | ||
120 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 245 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
121 | 246 | ||
122 | ret = fill_event_metadata(group, &fanotify_event_metadata, event); | 247 | fd = fill_event_metadata(group, &fanotify_event_metadata, event); |
123 | if (ret < 0) | 248 | if (fd < 0) |
124 | return ret; | 249 | return fd; |
250 | |||
251 | ret = prepare_for_access_response(group, event, fd); | ||
252 | if (ret) | ||
253 | goto out_close_fd; | ||
125 | 254 | ||
255 | ret = -EFAULT; | ||
126 | if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) | 256 | if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) |
127 | return -EFAULT; | 257 | goto out_kill_access_response; |
128 | 258 | ||
129 | return FAN_EVENT_METADATA_LEN; | 259 | return FAN_EVENT_METADATA_LEN; |
260 | |||
261 | out_kill_access_response: | ||
262 | remove_access_response(group, event, fd); | ||
263 | out_close_fd: | ||
264 | sys_close(fd); | ||
265 | return ret; | ||
130 | } | 266 | } |
131 | 267 | ||
132 | /* intofiy userspace file descriptor functions */ | 268 | /* intofiy userspace file descriptor functions */ |
@@ -197,6 +333,33 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, | |||
197 | return ret; | 333 | return ret; |
198 | } | 334 | } |
199 | 335 | ||
336 | static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) | ||
337 | { | ||
338 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | ||
339 | struct fanotify_response response = { .fd = -1, .response = -1 }; | ||
340 | struct fsnotify_group *group; | ||
341 | int ret; | ||
342 | |||
343 | group = file->private_data; | ||
344 | |||
345 | if (count > sizeof(response)) | ||
346 | count = sizeof(response); | ||
347 | |||
348 | pr_debug("%s: group=%p count=%zu\n", __func__, group, count); | ||
349 | |||
350 | if (copy_from_user(&response, buf, count)) | ||
351 | return -EFAULT; | ||
352 | |||
353 | ret = process_access_response(group, &response); | ||
354 | if (ret < 0) | ||
355 | count = ret; | ||
356 | |||
357 | return count; | ||
358 | #else | ||
359 | return -EINVAL; | ||
360 | #endif | ||
361 | } | ||
362 | |||
200 | static int fanotify_release(struct inode *ignored, struct file *file) | 363 | static int fanotify_release(struct inode *ignored, struct file *file) |
201 | { | 364 | { |
202 | struct fsnotify_group *group = file->private_data; | 365 | struct fsnotify_group *group = file->private_data; |
@@ -237,6 +400,7 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar | |||
237 | static const struct file_operations fanotify_fops = { | 400 | static const struct file_operations fanotify_fops = { |
238 | .poll = fanotify_poll, | 401 | .poll = fanotify_poll, |
239 | .read = fanotify_read, | 402 | .read = fanotify_read, |
403 | .write = fanotify_write, | ||
240 | .fasync = NULL, | 404 | .fasync = NULL, |
241 | .release = fanotify_release, | 405 | .release = fanotify_release, |
242 | .unlocked_ioctl = fanotify_ioctl, | 406 | .unlocked_ioctl = fanotify_ioctl, |
@@ -470,7 +634,7 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, | |||
470 | if (flags & ~FAN_ALL_INIT_FLAGS) | 634 | if (flags & ~FAN_ALL_INIT_FLAGS) |
471 | return -EINVAL; | 635 | return -EINVAL; |
472 | 636 | ||
473 | f_flags = (O_RDONLY | FMODE_NONOTIFY); | 637 | f_flags = O_RDWR | FMODE_NONOTIFY; |
474 | if (flags & FAN_CLOEXEC) | 638 | if (flags & FAN_CLOEXEC) |
475 | f_flags |= O_CLOEXEC; | 639 | f_flags |= O_CLOEXEC; |
476 | if (flags & FAN_NONBLOCK) | 640 | if (flags & FAN_NONBLOCK) |
@@ -527,7 +691,11 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, | |||
527 | default: | 691 | default: |
528 | return -EINVAL; | 692 | return -EINVAL; |
529 | } | 693 | } |
694 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | ||
695 | if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD)) | ||
696 | #else | ||
530 | if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) | 697 | if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) |
698 | #endif | ||
531 | return -EINVAL; | 699 | return -EINVAL; |
532 | 700 | ||
533 | filp = fget_light(fanotify_fd, &fput_needed); | 701 | filp = fget_light(fanotify_fd, &fput_needed); |
@@ -600,6 +768,8 @@ SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); | |||
600 | static int __init fanotify_user_setup(void) | 768 | static int __init fanotify_user_setup(void) |
601 | { | 769 | { |
602 | fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); | 770 | fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); |
771 | fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, | ||
772 | SLAB_PANIC); | ||
603 | 773 | ||
604 | return 0; | 774 | return 0; |
605 | } | 775 | } |