[PATCH] capable/capability.h (fs/)
[cascardo/linux.git] / fs / proc / base.c
index 23db452..20feb75 100644 (file)
@@ -55,6 +55,7 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 #include <linux/init.h>
+#include <linux/capability.h>
 #include <linux/file.h>
 #include <linux/string.h>
 #include <linux/seq_file.h>
@@ -70,6 +71,7 @@
 #include <linux/seccomp.h>
 #include <linux/cpuset.h>
 #include <linux/audit.h>
+#include <linux/poll.h>
 #include "internal.h"
 
 /*
@@ -103,7 +105,9 @@ enum pid_directory_inos {
        PROC_TGID_NUMA_MAPS,
        PROC_TGID_MOUNTS,
        PROC_TGID_WCHAN,
+#ifdef CONFIG_MMU
        PROC_TGID_SMAPS,
+#endif
 #ifdef CONFIG_SCHEDSTATS
        PROC_TGID_SCHEDSTAT,
 #endif
@@ -141,7 +145,9 @@ enum pid_directory_inos {
        PROC_TID_NUMA_MAPS,
        PROC_TID_MOUNTS,
        PROC_TID_WCHAN,
+#ifdef CONFIG_MMU
        PROC_TID_SMAPS,
+#endif
 #ifdef CONFIG_SCHEDSTATS
        PROC_TID_SCHEDSTAT,
 #endif
@@ -195,7 +201,9 @@ static struct pid_entry tgid_base_stuff[] = {
        E(PROC_TGID_ROOT,      "root",    S_IFLNK|S_IRWXUGO),
        E(PROC_TGID_EXE,       "exe",     S_IFLNK|S_IRWXUGO),
        E(PROC_TGID_MOUNTS,    "mounts",  S_IFREG|S_IRUGO),
+#ifdef CONFIG_MMU
        E(PROC_TGID_SMAPS,     "smaps",   S_IFREG|S_IRUGO),
+#endif
 #ifdef CONFIG_SECURITY
        E(PROC_TGID_ATTR,      "attr",    S_IFDIR|S_IRUGO|S_IXUGO),
 #endif
@@ -235,7 +243,9 @@ static struct pid_entry tid_base_stuff[] = {
        E(PROC_TID_ROOT,       "root",    S_IFLNK|S_IRWXUGO),
        E(PROC_TID_EXE,        "exe",     S_IFLNK|S_IRWXUGO),
        E(PROC_TID_MOUNTS,     "mounts",  S_IFREG|S_IRUGO),
+#ifdef CONFIG_MMU
        E(PROC_TID_SMAPS,      "smaps",   S_IFREG|S_IRUGO),
+#endif
 #ifdef CONFIG_SECURITY
        E(PROC_TID_ATTR,       "attr",    S_IFDIR|S_IRUGO|S_IXUGO),
 #endif
@@ -340,6 +350,54 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf
        return result;
 }
 
+
+/* Same as proc_root_link, but this addionally tries to get fs from other
+ * threads in the group */
+static int proc_task_root_link(struct inode *inode, struct dentry **dentry,
+                               struct vfsmount **mnt)
+{
+       struct fs_struct *fs;
+       int result = -ENOENT;
+       struct task_struct *leader = proc_task(inode);
+
+       task_lock(leader);
+       fs = leader->fs;
+       if (fs) {
+               atomic_inc(&fs->count);
+               task_unlock(leader);
+       } else {
+               /* Try to get fs from other threads */
+               task_unlock(leader);
+               read_lock(&tasklist_lock);
+               if (pid_alive(leader)) {
+                       struct task_struct *task = leader;
+
+                       while ((task = next_thread(task)) != leader) {
+                               task_lock(task);
+                               fs = task->fs;
+                               if (fs) {
+                                       atomic_inc(&fs->count);
+                                       task_unlock(task);
+                                       break;
+                               }
+                               task_unlock(task);
+                       }
+               }
+               read_unlock(&tasklist_lock);
+       }
+
+       if (fs) {
+               read_lock(&fs->lock);
+               *mnt = mntget(fs->rootmnt);
+               *dentry = dget(fs->root);
+               read_unlock(&fs->lock);
+               result = 0;
+               put_fs_struct(fs);
+       }
+       return result;
+}
+
+
 #define MAY_PTRACE(task) \
        (task == current || \
        (task->parent == current && \
@@ -471,14 +529,14 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
 
 /* permission checks */
 
-static int proc_check_root(struct inode *inode)
+/* If the process being read is separated by chroot from the reading process,
+ * don't let the reader access the threads.
+ */
+static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt)
 {
-       struct dentry *de, *base, *root;
-       struct vfsmount *our_vfsmnt, *vfsmnt, *mnt;
+       struct dentry *de, *base;
+       struct vfsmount *our_vfsmnt, *mnt;
        int res = 0;
-
-       if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
-               return -ENOENT;
        read_lock(&current->fs->lock);
        our_vfsmnt = mntget(current->fs->rootmnt);
        base = dget(current->fs->root);
@@ -511,6 +569,16 @@ out:
        goto exit;
 }
 
+static int proc_check_root(struct inode *inode)
+{
+       struct dentry *root;
+       struct vfsmount *vfsmnt;
+
+       if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */
+               return -ENOENT;
+       return proc_check_chroot(root, vfsmnt);
+}
+
 static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
 {
        if (generic_permission(inode, mask, NULL) != 0)
@@ -518,6 +586,20 @@ static int proc_permission(struct inode *inode, int mask, struct nameidata *nd)
        return proc_check_root(inode);
 }
 
+static int proc_task_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       struct dentry *root;
+       struct vfsmount *vfsmnt;
+
+       if (generic_permission(inode, mask, NULL) != 0)
+               return -EACCES;
+
+       if (proc_task_root_link(inode, &root, &vfsmnt))
+               return -ENOENT;
+
+       return proc_check_chroot(root, vfsmnt);
+}
+
 extern struct seq_operations proc_pid_maps_op;
 static int maps_open(struct inode *inode, struct file *file)
 {
@@ -558,6 +640,7 @@ static struct file_operations proc_numa_maps_operations = {
 };
 #endif
 
+#ifdef CONFIG_MMU
 extern struct seq_operations proc_pid_smaps_op;
 static int smaps_open(struct inode *inode, struct file *file)
 {
@@ -576,28 +659,41 @@ static struct file_operations proc_smaps_operations = {
        .llseek         = seq_lseek,
        .release        = seq_release,
 };
+#endif
 
 extern struct seq_operations mounts_op;
+struct proc_mounts {
+       struct seq_file m;
+       int event;
+};
+
 static int mounts_open(struct inode *inode, struct file *file)
 {
        struct task_struct *task = proc_task(inode);
-       int ret = seq_open(file, &mounts_op);
+       struct namespace *namespace;
+       struct proc_mounts *p;
+       int ret = -EINVAL;
 
-       if (!ret) {
-               struct seq_file *m = file->private_data;
-               struct namespace *namespace;
-               task_lock(task);
-               namespace = task->namespace;
-               if (namespace)
-                       get_namespace(namespace);
-               task_unlock(task);
-
-               if (namespace)
-                       m->private = namespace;
-               else {
-                       seq_release(inode, file);
-                       ret = -EINVAL;
+       task_lock(task);
+       namespace = task->namespace;
+       if (namespace)
+               get_namespace(namespace);
+       task_unlock(task);
+
+       if (namespace) {
+               ret = -ENOMEM;
+               p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL);
+               if (p) {
+                       file->private_data = &p->m;
+                       ret = seq_open(file, &mounts_op);
+                       if (!ret) {
+                               p->m.private = namespace;
+                               p->event = namespace->event;
+                               return 0;
+                       }
+                       kfree(p);
                }
+               put_namespace(namespace);
        }
        return ret;
 }
@@ -610,11 +706,30 @@ static int mounts_release(struct inode *inode, struct file *file)
        return seq_release(inode, file);
 }
 
+static unsigned mounts_poll(struct file *file, poll_table *wait)
+{
+       struct proc_mounts *p = file->private_data;
+       struct namespace *ns = p->m.private;
+       unsigned res = 0;
+
+       poll_wait(file, &ns->poll, wait);
+
+       spin_lock(&vfsmount_lock);
+       if (p->event != ns->event) {
+               p->event = ns->event;
+               res = POLLERR;
+       }
+       spin_unlock(&vfsmount_lock);
+
+       return res;
+}
+
 static struct file_operations proc_mounts_operations = {
        .open           = mounts_open,
        .read           = seq_read,
        .llseek         = seq_lseek,
        .release        = mounts_release,
+       .poll           = mounts_poll,
 };
 
 #define PROC_BLOCK_SIZE        (3*1024)                /* 4K page size but our output routines use some slack for overruns */
@@ -1419,7 +1534,7 @@ static struct inode_operations proc_fd_inode_operations = {
 
 static struct inode_operations proc_task_inode_operations = {
        .lookup         = proc_task_lookup,
-       .permission     = proc_permission,
+       .permission     = proc_task_permission,
 };
 
 #ifdef CONFIG_SECURITY
@@ -1609,10 +1724,12 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
                case PROC_TGID_MOUNTS:
                        inode->i_fop = &proc_mounts_operations;
                        break;
+#ifdef CONFIG_MMU
                case PROC_TID_SMAPS:
                case PROC_TGID_SMAPS:
                        inode->i_fop = &proc_smaps_operations;
                        break;
+#endif
 #ifdef CONFIG_SECURITY
                case PROC_TID_ATTR:
                        inode->i_nlink = 2;