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
commita1014f102322398e67524b68b3300acf384e6c1f (patch)
tree2d9d82e6a3d84bae796e4aedfd8be3409be6a4c3 /fs
parent2a3edf86040a7e15684525a2aadc29f532c51325 (diff)
fanotify: send events using read
Send events to userspace by reading the file descriptor from fanotify_init(). One will get blocks of data which look like: struct fanotify_event_metadata { __u32 event_len; __u32 vers; __s32 fd; __u64 mask; __s64 pid; __u64 cookie; } __attribute__ ((packed)); Simple code to retrieve and deal with events is below while ((len = read(fan_fd, buf, sizeof(buf))) > 0) { struct fanotify_event_metadata *metadata; metadata = (void *)buf; while(FAN_EVENT_OK(metadata, len)) { [PROCESS HERE!!] if (metadata->fd >= 0 && close(metadata->fd) != 0) goto fail; metadata = FAN_EVENT_NEXT(metadata, len); } } Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify.h5
-rw-r--r--fs/notify/fanotify/fanotify_user.c220
2 files changed, 221 insertions, 4 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 59c3331a0e81..5608783c6bca 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -30,3 +30,8 @@ static inline bool fanotify_mask_valid(__u32 mask)
30 return false; 30 return false;
31 return true; 31 return true;
32} 32}
33
34static inline __u32 fanotify_outgoing_mask(__u32 mask)
35{
36 return mask & FAN_ALL_OUTGOING_EVENTS;
37}
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index bc4fa48157f1..a99550f83f8a 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -4,15 +4,202 @@
4#include <linux/anon_inodes.h> 4#include <linux/anon_inodes.h>
5#include <linux/fsnotify_backend.h> 5#include <linux/fsnotify_backend.h>
6#include <linux/init.h> 6#include <linux/init.h>
7#include <linux/mount.h>
7#include <linux/namei.h> 8#include <linux/namei.h>
9#include <linux/poll.h>
8#include <linux/security.h> 10#include <linux/security.h>
9#include <linux/syscalls.h> 11#include <linux/syscalls.h>
10#include <linux/types.h> 12#include <linux/types.h>
13#include <linux/uaccess.h>
14
15#include <asm/ioctls.h>
11 16
12#include "fanotify.h" 17#include "fanotify.h"
13 18
14static struct kmem_cache *fanotify_mark_cache __read_mostly; 19static struct kmem_cache *fanotify_mark_cache __read_mostly;
15 20
21/*
22 * Get an fsnotify notification event if one exists and is small
23 * enough to fit in "count". Return an error pointer if the count
24 * is not large enough.
25 *
26 * Called with the group->notification_mutex held.
27 */
28static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
29 size_t count)
30{
31 BUG_ON(!mutex_is_locked(&group->notification_mutex));
32
33 pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
34
35 if (fsnotify_notify_queue_is_empty(group))
36 return NULL;
37
38 if (FAN_EVENT_METADATA_LEN > count)
39 return ERR_PTR(-EINVAL);
40
41 /* held the notification_mutex the whole time, so this is the
42 * same event we peeked above */
43 return fsnotify_remove_notify_event(group);
44}
45
46static int create_and_fill_fd(struct fsnotify_group *group,
47 struct fanotify_event_metadata *metadata,
48 struct fsnotify_event *event)
49{
50 int client_fd;
51 struct dentry *dentry;
52 struct vfsmount *mnt;
53 struct file *new_file;
54
55 pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group,
56 metadata, event);
57
58 client_fd = get_unused_fd();
59 if (client_fd < 0)
60 return client_fd;
61
62 if (event->data_type != FSNOTIFY_EVENT_PATH) {
63 WARN_ON(1);
64 put_unused_fd(client_fd);
65 return -EINVAL;
66 }
67
68 /*
69 * we need a new file handle for the userspace program so it can read even if it was
70 * originally opened O_WRONLY.
71 */
72 dentry = dget(event->path.dentry);
73 mnt = mntget(event->path.mnt);
74 /* it's possible this event was an overflow event. in that case dentry and mnt
75 * are NULL; That's fine, just don't call dentry open */
76 if (dentry && mnt)
77 new_file = dentry_open(dentry, mnt,
78 O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY,
79 current_cred());
80 else
81 new_file = ERR_PTR(-EOVERFLOW);
82 if (IS_ERR(new_file)) {
83 /*
84 * we still send an event even if we can't open the file. this
85 * can happen when say tasks are gone and we try to open their
86 * /proc files or we try to open a WRONLY file like in sysfs
87 * we just send the errno to userspace since there isn't much
88 * else we can do.
89 */
90 put_unused_fd(client_fd);
91 client_fd = PTR_ERR(new_file);
92 } else {
93 fd_install(client_fd, new_file);
94 }
95
96 metadata->fd = client_fd;
97
98 return 0;
99}
100
101static ssize_t fill_event_metadata(struct fsnotify_group *group,
102 struct fanotify_event_metadata *metadata,
103 struct fsnotify_event *event)
104{
105 pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
106 group, metadata, event);
107
108 metadata->event_len = FAN_EVENT_METADATA_LEN;
109 metadata->vers = FANOTIFY_METADATA_VERSION;
110 metadata->mask = fanotify_outgoing_mask(event->mask);
111
112 return create_and_fill_fd(group, metadata, event);
113
114}
115
116static ssize_t copy_event_to_user(struct fsnotify_group *group,
117 struct fsnotify_event *event,
118 char __user *buf)
119{
120 struct fanotify_event_metadata fanotify_event_metadata;
121 int ret;
122
123 pr_debug("%s: group=%p event=%p\n", __func__, group, event);
124
125 ret = fill_event_metadata(group, &fanotify_event_metadata, event);
126 if (ret)
127 return ret;
128
129 if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN))
130 return -EFAULT;
131
132 return FAN_EVENT_METADATA_LEN;
133}
134
135/* intofiy userspace file descriptor functions */
136static unsigned int fanotify_poll(struct file *file, poll_table *wait)
137{
138 struct fsnotify_group *group = file->private_data;
139 int ret = 0;
140
141 poll_wait(file, &group->notification_waitq, wait);
142 mutex_lock(&group->notification_mutex);
143 if (!fsnotify_notify_queue_is_empty(group))
144 ret = POLLIN | POLLRDNORM;
145 mutex_unlock(&group->notification_mutex);
146
147 return ret;
148}
149
150static ssize_t fanotify_read(struct file *file, char __user *buf,
151 size_t count, loff_t *pos)
152{
153 struct fsnotify_group *group;
154 struct fsnotify_event *kevent;
155 char __user *start;
156 int ret;
157 DEFINE_WAIT(wait);
158
159 start = buf;
160 group = file->private_data;
161
162 pr_debug("%s: group=%p\n", __func__, group);
163
164 while (1) {
165 prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE);
166
167 mutex_lock(&group->notification_mutex);
168 kevent = get_one_event(group, count);
169 mutex_unlock(&group->notification_mutex);
170
171 if (kevent) {
172 ret = PTR_ERR(kevent);
173 if (IS_ERR(kevent))
174 break;
175 ret = copy_event_to_user(group, kevent, buf);
176 fsnotify_put_event(kevent);
177 if (ret < 0)
178 break;
179 buf += ret;
180 count -= ret;
181 continue;
182 }
183
184 ret = -EAGAIN;
185 if (file->f_flags & O_NONBLOCK)
186 break;
187 ret = -EINTR;
188 if (signal_pending(current))
189 break;
190
191 if (start != buf)
192 break;
193
194 schedule();
195 }
196
197 finish_wait(&group->notification_waitq, &wait);
198 if (start != buf && ret != -EFAULT)
199 ret = buf - start;
200 return ret;
201}
202
16static int fanotify_release(struct inode *ignored, struct file *file) 203static int fanotify_release(struct inode *ignored, struct file *file)
17{ 204{
18 struct fsnotify_group *group = file->private_data; 205 struct fsnotify_group *group = file->private_data;
@@ -25,13 +212,38 @@ static int fanotify_release(struct inode *ignored, struct file *file)
25 return 0; 212 return 0;
26} 213}
27 214
215static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
216{
217 struct fsnotify_group *group;
218 struct fsnotify_event_holder *holder;
219 void __user *p;
220 int ret = -ENOTTY;
221 size_t send_len = 0;
222
223 group = file->private_data;
224
225 p = (void __user *) arg;
226
227 switch (cmd) {
228 case FIONREAD:
229 mutex_lock(&group->notification_mutex);
230 list_for_each_entry(holder, &group->notification_list, event_list)
231 send_len += FAN_EVENT_METADATA_LEN;
232 mutex_unlock(&group->notification_mutex);
233 ret = put_user(send_len, (int __user *) p);
234 break;
235 }
236
237 return ret;
238}
239
28static const struct file_operations fanotify_fops = { 240static const struct file_operations fanotify_fops = {
29 .poll = NULL, 241 .poll = fanotify_poll,
30 .read = NULL, 242 .read = fanotify_read,
31 .fasync = NULL, 243 .fasync = NULL,
32 .release = fanotify_release, 244 .release = fanotify_release,
33 .unlocked_ioctl = NULL, 245 .unlocked_ioctl = fanotify_ioctl,
34 .compat_ioctl = NULL, 246 .compat_ioctl = fanotify_ioctl,
35}; 247};
36 248
37static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) 249static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)