SUNRPC: Fix rpcauth_prune_expired
[safe/jmp/linux-2.6] / fs / inotify_user.c
index 8b83c71..d85c7d9 100644 (file)
 #include <linux/list.h>
 #include <linux/inotify.h>
 #include <linux/syscalls.h>
+#include <linux/magic.h>
 
 #include <asm/ioctls.h>
 
-static kmem_cache_t *watch_cachep __read_mostly;
-static kmem_cache_t *event_cachep __read_mostly;
+static struct kmem_cache *watch_cachep __read_mostly;
+static struct kmem_cache *event_cachep __read_mostly;
 
 static struct vfsmount *inotify_mnt __read_mostly;
 
 /* these are configurable via /proc/sys/fs/inotify/ */
-int inotify_max_user_instances __read_mostly;
-int inotify_max_user_watches __read_mostly;
-int inotify_max_queued_events __read_mostly;
+static int inotify_max_user_instances __read_mostly;
+static int inotify_max_user_watches __read_mostly;
+static int inotify_max_queued_events __read_mostly;
 
 /*
  * Lock ordering:
@@ -78,6 +79,7 @@ struct inotify_device {
        atomic_t                count;          /* reference count */
        struct user_struct      *user;          /* user who opened this dev */
        struct inotify_handle   *ih;            /* inotify handle */
+       struct fasync_struct    *fa;            /* async notification */
        unsigned int            queue_size;     /* size of the queue (bytes) */
        unsigned int            event_count;    /* number of pending events */
        unsigned int            max_events;     /* maximum number of events */
@@ -187,7 +189,7 @@ static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
 {
        struct inotify_kernel_event *kevent;
 
-       kevent = kmem_cache_alloc(event_cachep, GFP_KERNEL);
+       kevent = kmem_cache_alloc(event_cachep, GFP_NOFS);
        if (unlikely(!kevent))
                return NULL;
 
@@ -247,6 +249,19 @@ inotify_dev_get_event(struct inotify_device *dev)
 }
 
 /*
+ * inotify_dev_get_last_event - return the last event in the given dev's queue
+ *
+ * Caller must hold dev->ev_mutex.
+ */
+static inline struct inotify_kernel_event *
+inotify_dev_get_last_event(struct inotify_device *dev)
+{
+       if (list_empty(&dev->events))
+               return NULL;
+       return list_entry(dev->events.prev, struct inotify_kernel_event, list);
+}
+
+/*
  * inotify_dev_queue_event - event handler registered with core inotify, adds
  * a new event to the given device
  *
@@ -268,11 +283,11 @@ static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
        /* we can safely put the watch as we don't reference it while
         * generating the event
         */
-       if (mask & IN_IGNORED || mask & IN_ONESHOT)
+       if (mask & IN_IGNORED || w->mask & IN_ONESHOT)
                put_inotify_watch(w); /* final put */
 
        /* coalescing: drop this event if it is a dupe of the previous */
-       last = inotify_dev_get_event(dev);
+       last = inotify_dev_get_last_event(dev);
        if (last && last->event.mask == mask && last->event.wd == wd &&
                        last->event.cookie == cookie) {
                const char *lastname = last->name;
@@ -301,13 +316,14 @@ static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd, u32 mask,
        dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
        list_add_tail(&kevent->list, &dev->events);
        wake_up_interruptible(&dev->wq);
+       kill_fasync(&dev->fa, SIGIO, POLL_IN);
 
 out:
        mutex_unlock(&dev->ev_mutex);
 }
 
 /*
- * remove_kevent - cleans up and ultimately frees the given kevent
+ * remove_kevent - cleans up the given kevent
  *
  * Caller must hold dev->ev_mutex.
  */
@@ -318,7 +334,13 @@ static void remove_kevent(struct inotify_device *dev,
 
        dev->event_count--;
        dev->queue_size -= sizeof(struct inotify_event) + kevent->event.len;
+}
 
+/*
+ * free_kevent - frees the given kevent.
+ */
+static void free_kevent(struct inotify_kernel_event *kevent)
+{
        kfree(kevent->name);
        kmem_cache_free(event_cachep, kevent);
 }
@@ -334,24 +356,25 @@ static void inotify_dev_event_dequeue(struct inotify_device *dev)
                struct inotify_kernel_event *kevent;
                kevent = inotify_dev_get_event(dev);
                remove_kevent(dev, kevent);
+               free_kevent(kevent);
        }
 }
 
 /*
- * find_inode - resolve a user-given path to a specific inode and return a nd
+ * find_inode - resolve a user-given path to a specific inode
  */
-static int find_inode(const char __user *dirname, struct nameidata *nd,
+static int find_inode(const char __user *dirname, struct path *path,
                      unsigned flags)
 {
        int error;
 
-       error = __user_walk(dirname, flags, nd);
+       error = user_path_at(AT_FDCWD, dirname, flags, path);
        if (error)
                return error;
        /* you can only watch an inode if you have read permissions on it */
-       error = vfs_permission(nd, MAY_READ);
+       error = inode_permission(path->dentry->d_inode, MAY_READ);
        if (error)
-               path_release(nd);
+               path_put(path);
        return error;
 }
 
@@ -380,6 +403,7 @@ static int create_watch(struct inotify_device *dev, struct inode *inode,
 
        atomic_inc(&dev->user->inotify_watches);
 
+       inotify_init_watch(&watch->wdata);
        ret = inotify_add_watch(dev->ih, &watch->wdata, inode, mask);
        if (ret < 0)
                free_inotify_user_watch(&watch->wdata);
@@ -416,17 +440,15 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
        dev = file->private_data;
 
        while (1) {
-               int events;
 
                prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
 
                mutex_lock(&dev->ev_mutex);
-               events = !list_empty(&dev->events);
-               mutex_unlock(&dev->ev_mutex);
-               if (events) {
+               if (!list_empty(&dev->events)) {
                        ret = 0;
                        break;
                }
+               mutex_unlock(&dev->ev_mutex);
 
                if (file->f_flags & O_NONBLOCK) {
                        ret = -EAGAIN;
@@ -445,7 +467,6 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
 
-       mutex_lock(&dev->ev_mutex);
        while (1) {
                struct inotify_kernel_event *kevent;
 
@@ -454,8 +475,23 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
                        break;
 
                kevent = inotify_dev_get_event(dev);
-               if (event_size + kevent->event.len > count)
+               if (event_size + kevent->event.len > count) {
+                       if (ret == 0 && count > 0) {
+                               /*
+                                * could not get a single event because we
+                                * didn't have enough buffer space.
+                                */
+                               ret = -EINVAL;
+                       }
                        break;
+               }
+               remove_kevent(dev, kevent);
+
+               /*
+                * Must perform the copy_to_user outside the mutex in order
+                * to avoid a lock order reversal with mmap_sem.
+                */
+               mutex_unlock(&dev->ev_mutex);
 
                if (copy_to_user(buf, &kevent->event, event_size)) {
                        ret = -EFAULT;
@@ -473,13 +509,22 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
                        count -= kevent->event.len;
                }
 
-               remove_kevent(dev, kevent);
+               free_kevent(kevent);
+
+               mutex_lock(&dev->ev_mutex);
        }
        mutex_unlock(&dev->ev_mutex);
 
        return ret;
 }
 
+static int inotify_fasync(int fd, struct file *file, int on)
+{
+       struct inotify_device *dev = file->private_data;
+
+       return fasync_helper(fd, file, on, &dev->fa) >= 0 ? 0 : -EIO;
+}
+
 static int inotify_release(struct inode *ignored, struct file *file)
 {
        struct inotify_device *dev = file->private_data;
@@ -492,6 +537,9 @@ static int inotify_release(struct inode *ignored, struct file *file)
                inotify_dev_event_dequeue(dev);
        mutex_unlock(&dev->ev_mutex);
 
+       if (file->f_flags & FASYNC)
+               inotify_fasync(-1, file, 0);
+
        /* free this device: the put matching the get in inotify_init() */
        put_inotify_dev(dev);
 
@@ -520,6 +568,7 @@ static long inotify_ioctl(struct file *file, unsigned int cmd,
 static const struct file_operations inotify_fops = {
        .poll           = inotify_poll,
        .read           = inotify_read,
+       .fasync         = inotify_fasync,
        .release        = inotify_release,
        .unlocked_ioctl = inotify_ioctl,
        .compat_ioctl   = inotify_ioctl,
@@ -530,7 +579,7 @@ static const struct inotify_operations inotify_user_ops = {
        .destroy_watch  = free_inotify_user_watch,
 };
 
-asmlinkage long sys_inotify_init(void)
+asmlinkage long sys_inotify_init1(int flags)
 {
        struct inotify_device *dev;
        struct inotify_handle *ih;
@@ -538,7 +587,14 @@ asmlinkage long sys_inotify_init(void)
        struct file *filp;
        int fd, ret;
 
-       fd = get_unused_fd();
+       /* Check the IN_* constants for consistency.  */
+       BUILD_BUG_ON(IN_CLOEXEC != O_CLOEXEC);
+       BUILD_BUG_ON(IN_NONBLOCK != O_NONBLOCK);
+
+       if (flags & ~(IN_CLOEXEC | IN_NONBLOCK))
+               return -EINVAL;
+
+       fd = get_unused_fd_flags(flags & O_CLOEXEC);
        if (fd < 0)
                return fd;
 
@@ -562,18 +618,19 @@ asmlinkage long sys_inotify_init(void)
        }
 
        ih = inotify_init(&inotify_user_ops);
-       if (unlikely(IS_ERR(ih))) {
+       if (IS_ERR(ih)) {
                ret = PTR_ERR(ih);
                goto out_free_dev;
        }
        dev->ih = ih;
+       dev->fa = NULL;
 
        filp->f_op = &inotify_fops;
-       filp->f_vfsmnt = mntget(inotify_mnt);
-       filp->f_dentry = dget(inotify_mnt->mnt_root);
-       filp->f_mapping = filp->f_dentry->d_inode->i_mapping;
+       filp->f_path.mnt = mntget(inotify_mnt);
+       filp->f_path.dentry = dget(inotify_mnt->mnt_root);
+       filp->f_mapping = filp->f_path.dentry->d_inode->i_mapping;
        filp->f_mode = FMODE_READ;
-       filp->f_flags = O_RDONLY;
+       filp->f_flags = O_RDONLY | (flags & O_NONBLOCK);
        filp->private_data = dev;
 
        INIT_LIST_HEAD(&dev->events);
@@ -601,11 +658,16 @@ out_put_fd:
        return ret;
 }
 
-asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
+asmlinkage long sys_inotify_init(void)
+{
+       return sys_inotify_init1(0);
+}
+
+asmlinkage long sys_inotify_add_watch(int fd, const char __user *pathname, u32 mask)
 {
        struct inode *inode;
        struct inotify_device *dev;
-       struct nameidata nd;
+       struct path path;
        struct file *filp;
        int ret, fput_needed;
        unsigned flags = 0;
@@ -625,12 +687,12 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
        if (mask & IN_ONLYDIR)
                flags |= LOOKUP_DIRECTORY;
 
-       ret = find_inode(path, &nd, flags);
+       ret = find_inode(pathname, &path, flags);
        if (unlikely(ret))
                goto fput_and_out;
 
-       /* inode held in place by reference to nd; dev by fget on fd */
-       inode = nd.dentry->d_inode;
+       /* inode held in place by reference to path; dev by fget on fd */
+       inode = path.dentry->d_inode;
        dev = filp->private_data;
 
        mutex_lock(&dev->up_mutex);
@@ -639,7 +701,7 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
                ret = create_watch(dev, inode, mask);
        mutex_unlock(&dev->up_mutex);
 
-       path_release(&nd);
+       path_put(&path);
 fput_and_out:
        fput_light(filp, fput_needed);
        return ret;
@@ -671,11 +733,12 @@ out:
        return ret;
 }
 
-static struct super_block *
+static int
 inotify_get_sb(struct file_system_type *fs_type, int flags,
-              const char *dev_name, void *data)
+              const char *dev_name, void *data, struct vfsmount *mnt)
 {
-    return get_sb_pseudo(fs_type, "inotify", NULL, 0xBAD1DEA);
+       return get_sb_pseudo(fs_type, "inotify", NULL,
+                       INOTIFYFS_SUPER_MAGIC, mnt);
 }
 
 static struct file_system_type inotify_fs_type = {
@@ -707,10 +770,10 @@ static int __init inotify_user_setup(void)
 
        watch_cachep = kmem_cache_create("inotify_watch_cache",
                                         sizeof(struct inotify_user_watch),
-                                        0, SLAB_PANIC, NULL, NULL);
+                                        0, SLAB_PANIC, NULL);
        event_cachep = kmem_cache_create("inotify_event_cache",
                                         sizeof(struct inotify_kernel_event),
-                                        0, SLAB_PANIC, NULL, NULL);
+                                        0, SLAB_PANIC, NULL);
 
        return 0;
 }