]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
fanotify: send events using read
authorEric Paris <eparis@redhat.com>
Fri, 18 Dec 2009 02:24:26 +0000 (21:24 -0500)
committerEric Paris <eparis@redhat.com>
Wed, 28 Jul 2010 13:58:56 +0000 (09:58 -0400)
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>
fs/notify/fanotify/fanotify.h
fs/notify/fanotify/fanotify_user.c
include/linux/fanotify.h

index 59c3331a0e81ad346643e5ef9c0beacfa97c53e7..5608783c6bca6b7177e2537013270b50c0febfd4 100644 (file)
@@ -30,3 +30,8 @@ static inline bool fanotify_mask_valid(__u32 mask)
                return false;
        return true;
 }
+
+static inline __u32 fanotify_outgoing_mask(__u32 mask)
+{
+       return mask & FAN_ALL_OUTGOING_EVENTS;
+}
index bc4fa48157f15bd16d43757b201d1b3c54c334f3..a99550f83f8aff4888c757a5440452e30724d95d 100644 (file)
 #include <linux/anon_inodes.h>
 #include <linux/fsnotify_backend.h>
 #include <linux/init.h>
+#include <linux/mount.h>
 #include <linux/namei.h>
+#include <linux/poll.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/types.h>
+#include <linux/uaccess.h>
+
+#include <asm/ioctls.h>
 
 #include "fanotify.h"
 
 static struct kmem_cache *fanotify_mark_cache __read_mostly;
 
+/*
+ * Get an fsnotify notification event if one exists and is small
+ * enough to fit in "count". Return an error pointer if the count
+ * is not large enough.
+ *
+ * Called with the group->notification_mutex held.
+ */
+static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
+                                           size_t count)
+{
+       BUG_ON(!mutex_is_locked(&group->notification_mutex));
+
+       pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
+
+       if (fsnotify_notify_queue_is_empty(group))
+               return NULL;
+
+       if (FAN_EVENT_METADATA_LEN > count)
+               return ERR_PTR(-EINVAL);
+
+       /* held the notification_mutex the whole time, so this is the
+        * same event we peeked above */
+       return fsnotify_remove_notify_event(group);
+}
+
+static int create_and_fill_fd(struct fsnotify_group *group,
+                             struct fanotify_event_metadata *metadata,
+                             struct fsnotify_event *event)
+{
+       int client_fd;
+       struct dentry *dentry;
+       struct vfsmount *mnt;
+       struct file *new_file;
+
+       pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group,
+                metadata, event);
+
+       client_fd = get_unused_fd();
+       if (client_fd < 0)
+               return client_fd;
+
+       if (event->data_type != FSNOTIFY_EVENT_PATH) {
+               WARN_ON(1);
+               put_unused_fd(client_fd);
+               return -EINVAL;
+       }
+
+       /*
+        * we need a new file handle for the userspace program so it can read even if it was
+        * originally opened O_WRONLY.
+        */
+       dentry = dget(event->path.dentry);
+       mnt = mntget(event->path.mnt);
+       /* it's possible this event was an overflow event.  in that case dentry and mnt
+        * are NULL;  That's fine, just don't call dentry open */
+       if (dentry && mnt)
+               new_file = dentry_open(dentry, mnt,
+                                      O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY,
+                                      current_cred());
+       else
+               new_file = ERR_PTR(-EOVERFLOW);
+       if (IS_ERR(new_file)) {
+               /*
+                * we still send an event even if we can't open the file.  this
+                * can happen when say tasks are gone and we try to open their
+                * /proc files or we try to open a WRONLY file like in sysfs
+                * we just send the errno to userspace since there isn't much
+                * else we can do.
+                */
+               put_unused_fd(client_fd);
+               client_fd = PTR_ERR(new_file);
+       } else {
+               fd_install(client_fd, new_file);
+       }
+
+       metadata->fd = client_fd;
+
+       return 0;
+}
+
+static ssize_t fill_event_metadata(struct fsnotify_group *group,
+                                  struct fanotify_event_metadata *metadata,
+                                  struct fsnotify_event *event)
+{
+       pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
+                group, metadata, event);
+
+       metadata->event_len = FAN_EVENT_METADATA_LEN;
+       metadata->vers = FANOTIFY_METADATA_VERSION;
+       metadata->mask = fanotify_outgoing_mask(event->mask);
+
+       return create_and_fill_fd(group, metadata, event);
+
+}
+
+static ssize_t copy_event_to_user(struct fsnotify_group *group,
+                                 struct fsnotify_event *event,
+                                 char __user *buf)
+{
+       struct fanotify_event_metadata fanotify_event_metadata;
+       int ret;
+
+       pr_debug("%s: group=%p event=%p\n", __func__, group, event);
+
+       ret = fill_event_metadata(group, &fanotify_event_metadata, event);
+       if (ret)
+               return ret;
+
+       if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN))
+               return -EFAULT;
+
+       return FAN_EVENT_METADATA_LEN;
+}
+
+/* intofiy userspace file descriptor functions */
+static unsigned int fanotify_poll(struct file *file, poll_table *wait)
+{
+       struct fsnotify_group *group = file->private_data;
+       int ret = 0;
+
+       poll_wait(file, &group->notification_waitq, wait);
+       mutex_lock(&group->notification_mutex);
+       if (!fsnotify_notify_queue_is_empty(group))
+               ret = POLLIN | POLLRDNORM;
+       mutex_unlock(&group->notification_mutex);
+
+       return ret;
+}
+
+static ssize_t fanotify_read(struct file *file, char __user *buf,
+                            size_t count, loff_t *pos)
+{
+       struct fsnotify_group *group;
+       struct fsnotify_event *kevent;
+       char __user *start;
+       int ret;
+       DEFINE_WAIT(wait);
+
+       start = buf;
+       group = file->private_data;
+
+       pr_debug("%s: group=%p\n", __func__, group);
+
+       while (1) {
+               prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE);
+
+               mutex_lock(&group->notification_mutex);
+               kevent = get_one_event(group, count);
+               mutex_unlock(&group->notification_mutex);
+
+               if (kevent) {
+                       ret = PTR_ERR(kevent);
+                       if (IS_ERR(kevent))
+                               break;
+                       ret = copy_event_to_user(group, kevent, buf);
+                       fsnotify_put_event(kevent);
+                       if (ret < 0)
+                               break;
+                       buf += ret;
+                       count -= ret;
+                       continue;
+               }
+
+               ret = -EAGAIN;
+               if (file->f_flags & O_NONBLOCK)
+                       break;
+               ret = -EINTR;
+               if (signal_pending(current))
+                       break;
+
+               if (start != buf)
+                       break;
+
+               schedule();
+       }
+
+       finish_wait(&group->notification_waitq, &wait);
+       if (start != buf && ret != -EFAULT)
+               ret = buf - start;
+       return ret;
+}
+
 static int fanotify_release(struct inode *ignored, struct file *file)
 {
        struct fsnotify_group *group = file->private_data;
@@ -25,13 +212,38 @@ static int fanotify_release(struct inode *ignored, struct file *file)
        return 0;
 }
 
+static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+       struct fsnotify_group *group;
+       struct fsnotify_event_holder *holder;
+       void __user *p;
+       int ret = -ENOTTY;
+       size_t send_len = 0;
+
+       group = file->private_data;
+
+       p = (void __user *) arg;
+
+       switch (cmd) {
+       case FIONREAD:
+               mutex_lock(&group->notification_mutex);
+               list_for_each_entry(holder, &group->notification_list, event_list)
+                       send_len += FAN_EVENT_METADATA_LEN;
+               mutex_unlock(&group->notification_mutex);
+               ret = put_user(send_len, (int __user *) p);
+               break;
+       }
+
+       return ret;
+}
+
 static const struct file_operations fanotify_fops = {
-       .poll           = NULL,
-       .read           = NULL,
+       .poll           = fanotify_poll,
+       .read           = fanotify_read,
        .fasync         = NULL,
        .release        = fanotify_release,
-       .unlocked_ioctl = NULL,
-       .compat_ioctl   = NULL,
+       .unlocked_ioctl = fanotify_ioctl,
+       .compat_ioctl   = fanotify_ioctl,
 };
 
 static void fanotify_free_mark(struct fsnotify_mark *fsn_mark)
index 95aeea2a3ca62724ade48157f8c519a3d0f5c06c..c1c66162a46cec6539ebc0d1e56a78accc306bf6 100644 (file)
  */
 #define FAN_ALL_INCOMING_EVENTS        (FAN_ALL_EVENTS |\
                                 FAN_EVENT_ON_CHILD)
+
+#define FAN_ALL_OUTGOING_EVENTS        (FAN_ALL_EVENTS |\
+                                FAN_Q_OVERFLOW)
+
+#define FANOTIFY_METADATA_VERSION      1
+
+struct fanotify_event_metadata {
+       __u32 event_len;
+       __u32 vers;
+       __s32 fd;
+       __u64 mask;
+} __attribute__ ((packed));
+
+/* Helper functions to deal with fanotify_event_metadata buffers */
+#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata))
+
+#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \
+                                  (struct fanotify_event_metadata*)(((char *)(meta)) + \
+                                  (meta)->event_len))
+
+#define FAN_EVENT_OK(meta, len)        ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \
+                               (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \
+                               (long)(meta)->event_len <= (long)(len))
+
 #ifdef __KERNEL__
 
 #endif /* __KERNEL__ */