netfilter: drop outermost socket lock in getsockopt()
[pandora-kernel.git] / fs / proc / proc_sysctl.c
index b441132..ec20595 100644 (file)
@@ -3,6 +3,7 @@
  */
 #include <linux/init.h>
 #include <linux/sysctl.h>
+#include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <linux/security.h>
 #include <linux/namei.h>
@@ -14,6 +15,15 @@ static const struct inode_operations proc_sys_inode_operations;
 static const struct file_operations proc_sys_dir_file_operations;
 static const struct inode_operations proc_sys_dir_operations;
 
+void proc_sys_poll_notify(struct ctl_table_poll *poll)
+{
+       if (!poll)
+               return;
+
+       atomic_inc(&poll->event);
+       wake_up_interruptible(&poll->wait);
+}
+
 static struct inode *proc_sys_make_inode(struct super_block *sb,
                struct ctl_table_header *head, struct ctl_table *table)
 {
@@ -113,9 +123,6 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
 
        err = ERR_PTR(-ENOMEM);
        inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
-       if (h)
-               sysctl_head_finish(h);
-
        if (!inode)
                goto out;
 
@@ -124,6 +131,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
        d_add(dentry, inode);
 
 out:
+       if (h)
+               sysctl_head_finish(h);
        sysctl_head_finish(head);
        return err;
 }
@@ -176,6 +185,54 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf,
        return proc_sys_call_handler(filp, (void __user *)buf, count, ppos, 1);
 }
 
+static int proc_sys_open(struct inode *inode, struct file *filp)
+{
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+
+       /* sysctl was unregistered */
+       if (IS_ERR(head))
+               return PTR_ERR(head);
+
+       if (table->poll)
+               filp->private_data = proc_sys_poll_event(table->poll);
+
+       sysctl_head_finish(head);
+
+       return 0;
+}
+
+static unsigned int proc_sys_poll(struct file *filp, poll_table *wait)
+{
+       struct inode *inode = filp->f_path.dentry->d_inode;
+       struct ctl_table_header *head = grab_header(inode);
+       struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+       unsigned int ret = DEFAULT_POLLMASK;
+       unsigned long event;
+
+       /* sysctl was unregistered */
+       if (IS_ERR(head))
+               return POLLERR | POLLHUP;
+
+       if (!table->proc_handler)
+               goto out;
+
+       if (!table->poll)
+               goto out;
+
+       event = (unsigned long)filp->private_data;
+       poll_wait(filp, &table->poll->wait, wait);
+
+       if (event != atomic_read(&table->poll->event)) {
+               filp->private_data = proc_sys_poll_event(table->poll);
+               ret = POLLIN | POLLRDNORM | POLLERR | POLLPRI;
+       }
+
+out:
+       sysctl_head_finish(head);
+
+       return ret;
+}
 
 static int proc_sys_fill_cache(struct file *filp, void *dirent,
                                filldir_t filldir,
@@ -330,7 +387,7 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr)
        if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
                return -EPERM;
 
-       error = inode_change_ok(inode, attr);
+       error = setattr_prepare(dentry, attr);
        if (error)
                return error;
 
@@ -364,12 +421,15 @@ static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct
 }
 
 static const struct file_operations proc_sys_file_operations = {
+       .open           = proc_sys_open,
+       .poll           = proc_sys_poll,
        .read           = proc_sys_read,
        .write          = proc_sys_write,
        .llseek         = default_llseek,
 };
 
 static const struct file_operations proc_sys_dir_file_operations = {
+       .read           = generic_read_dir,
        .readdir        = proc_sys_readdir,
        .llseek         = generic_file_llseek,
 };