]> err.no Git - linux-2.6/blobdiff - fs/proc/base.c
Isolate some explicit usage of task->tgid
[linux-2.6] / fs / proc / base.c
index ae3627337a924acd9d2a0281fd11614900036353..991482811f1e7ecd8b9bc91abc4eace6fc2491d6 100644 (file)
 #include <linux/mount.h>
 #include <linux/security.h>
 #include <linux/ptrace.h>
+#include <linux/cgroup.h>
 #include <linux/cpuset.h>
 #include <linux/audit.h>
 #include <linux/poll.h>
 #include <linux/nsproxy.h>
 #include <linux/oom.h>
+#include <linux/elf.h>
+#include <linux/pid_namespace.h>
 #include "internal.h"
 
 /* NOTE:
@@ -198,27 +201,6 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf
         (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \
         security_ptrace(current,task) == 0))
 
-static int proc_pid_environ(struct task_struct *task, char * buffer)
-{
-       int res = 0;
-       struct mm_struct *mm = get_task_mm(task);
-       if (mm) {
-               unsigned int len;
-
-               res = -ESRCH;
-               if (!ptrace_may_attach(task))
-                       goto out;
-
-               len  = mm->env_end - mm->env_start;
-               if (len > PAGE_SIZE)
-                       len = PAGE_SIZE;
-               res = access_process_vm(task, mm->env_start, buffer, len, 0);
-out:
-               mmput(mm);
-       }
-       return res;
-}
-
 static int proc_pid_cmdline(struct task_struct *task, char * buffer)
 {
        int res = 0;
@@ -283,7 +265,7 @@ static int proc_pid_auxv(struct task_struct *task, char *buffer)
 static int proc_pid_wchan(struct task_struct *task, char *buffer)
 {
        unsigned long wchan;
-       char symname[KSYM_NAME_LEN+1];
+       char symname[KSYM_NAME_LEN];
 
        wchan = get_wchan(task);
 
@@ -303,7 +285,7 @@ static int proc_pid_schedstat(struct task_struct *task, char *buffer)
        return sprintf(buffer, "%llu %llu %lu\n",
                        task->sched_info.cpu_time,
                        task->sched_info.run_delay,
-                       task->sched_info.pcnt);
+                       task->sched_info.pcount);
 }
 #endif
 
@@ -369,18 +351,21 @@ struct proc_mounts {
 static int mounts_open(struct inode *inode, struct file *file)
 {
        struct task_struct *task = get_proc_task(inode);
+       struct nsproxy *nsp;
        struct mnt_namespace *ns = NULL;
        struct proc_mounts *p;
        int ret = -EINVAL;
 
        if (task) {
-               task_lock(task);
-               if (task->nsproxy) {
-                       ns = task->nsproxy->mnt_ns;
+               rcu_read_lock();
+               nsp = task_nsproxy(task);
+               if (nsp) {
+                       ns = nsp->mnt_ns;
                        if (ns)
                                get_mnt_ns(ns);
                }
-               task_unlock(task);
+               rcu_read_unlock();
+
                put_task_struct(task);
        }
 
@@ -443,16 +428,20 @@ static int mountstats_open(struct inode *inode, struct file *file)
 
        if (!ret) {
                struct seq_file *m = file->private_data;
+               struct nsproxy *nsp;
                struct mnt_namespace *mnt_ns = NULL;
                struct task_struct *task = get_proc_task(inode);
 
                if (task) {
-                       task_lock(task);
-                       if (task->nsproxy)
-                               mnt_ns = task->nsproxy->mnt_ns;
-                       if (mnt_ns)
-                               get_mnt_ns(mnt_ns);
-                       task_unlock(task);
+                       rcu_read_lock();
+                       nsp = task_nsproxy(task);
+                       if (nsp) {
+                               mnt_ns = nsp->mnt_ns;
+                               if (mnt_ns)
+                                       get_mnt_ns(mnt_ns);
+                       }
+                       rcu_read_unlock();
+
                        put_task_struct(task);
                }
 
@@ -491,7 +480,7 @@ static ssize_t proc_info_read(struct file * file, char __user * buf,
                count = PROC_BLOCK_SIZE;
 
        length = -ENOMEM;
-       if (!(page = __get_free_page(GFP_KERNEL)))
+       if (!(page = __get_free_page(GFP_TEMPORARY)))
                goto out;
 
        length = PROC_I(inode)->op.proc_read(task, (char*)page);
@@ -531,7 +520,7 @@ static ssize_t mem_read(struct file * file, char __user * buf,
                goto out;
 
        ret = -ENOMEM;
-       page = (char *)__get_free_page(GFP_USER);
+       page = (char *)__get_free_page(GFP_TEMPORARY);
        if (!page)
                goto out;
 
@@ -601,7 +590,7 @@ static ssize_t mem_write(struct file * file, const char __user *buf,
                goto out;
 
        copied = -ENOMEM;
-       page = (char *)__get_free_page(GFP_USER);
+       page = (char *)__get_free_page(GFP_TEMPORARY);
        if (!page)
                goto out;
 
@@ -657,6 +646,76 @@ static const struct file_operations proc_mem_operations = {
        .open           = mem_open,
 };
 
+static ssize_t environ_read(struct file *file, char __user *buf,
+                       size_t count, loff_t *ppos)
+{
+       struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
+       char *page;
+       unsigned long src = *ppos;
+       int ret = -ESRCH;
+       struct mm_struct *mm;
+
+       if (!task)
+               goto out_no_task;
+
+       if (!ptrace_may_attach(task))
+               goto out;
+
+       ret = -ENOMEM;
+       page = (char *)__get_free_page(GFP_TEMPORARY);
+       if (!page)
+               goto out;
+
+       ret = 0;
+
+       mm = get_task_mm(task);
+       if (!mm)
+               goto out_free;
+
+       while (count > 0) {
+               int this_len, retval, max_len;
+
+               this_len = mm->env_end - (mm->env_start + src);
+
+               if (this_len <= 0)
+                       break;
+
+               max_len = (count > PAGE_SIZE) ? PAGE_SIZE : count;
+               this_len = (this_len > max_len) ? max_len : this_len;
+
+               retval = access_process_vm(task, (mm->env_start + src),
+                       page, this_len, 0);
+
+               if (retval <= 0) {
+                       ret = retval;
+                       break;
+               }
+
+               if (copy_to_user(buf, page, retval)) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               ret += retval;
+               src += retval;
+               buf += retval;
+               count -= retval;
+       }
+       *ppos = src;
+
+       mmput(mm);
+out_free:
+       free_page((unsigned long) page);
+out:
+       put_task_struct(task);
+out_no_task:
+       return ret;
+}
+
+static const struct file_operations proc_environ_operations = {
+       .read           = environ_read,
+};
+
 static ssize_t oom_adjust_read(struct file *file, char __user *buf,
                                size_t count, loff_t *ppos)
 {
@@ -787,7 +846,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
                /* No partial writes. */
                return -EINVAL;
        }
-       page = (char*)__get_free_page(GFP_USER);
+       page = (char*)__get_free_page(GFP_TEMPORARY);
        if (!page)
                return -ENOMEM;
        length = -EFAULT;
@@ -926,7 +985,7 @@ static const struct file_operations proc_pid_sched_operations = {
        .read           = seq_read,
        .write          = sched_write,
        .llseek         = seq_lseek,
-       .release        = seq_release,
+       .release        = single_release,
 };
 
 #endif
@@ -953,7 +1012,8 @@ static int do_proc_readlink(struct dentry *dentry, struct vfsmount *mnt,
                            char __user *buffer, int buflen)
 {
        struct inode * inode;
-       char *tmp = (char*)__get_free_page(GFP_KERNEL), *path;
+       char *tmp = (char*)__get_free_page(GFP_TEMPORARY);
+       char *path;
        int len;
 
        if (!tmp)
@@ -1014,7 +1074,7 @@ static int task_dumpable(struct task_struct *task)
        task_lock(task);
        mm = task->mm;
        if (mm)
-               dumpable = mm->dumpable;
+               dumpable = get_dumpable(mm);
        task_unlock(task);
        if(dumpable == 1)
                return 1;
@@ -1725,7 +1785,7 @@ static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
                goto out;
 
        length = -ENOMEM;
-       page = (char*)__get_free_page(GFP_USER);
+       page = (char*)__get_free_page(GFP_TEMPORARY);
        if (!page)
                goto out;
 
@@ -1785,6 +1845,91 @@ static const struct inode_operations proc_attr_dir_inode_operations = {
 
 #endif
 
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+static ssize_t proc_coredump_filter_read(struct file *file, char __user *buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
+       struct mm_struct *mm;
+       char buffer[PROC_NUMBUF];
+       size_t len;
+       int ret;
+
+       if (!task)
+               return -ESRCH;
+
+       ret = 0;
+       mm = get_task_mm(task);
+       if (mm) {
+               len = snprintf(buffer, sizeof(buffer), "%08lx\n",
+                              ((mm->flags & MMF_DUMP_FILTER_MASK) >>
+                               MMF_DUMP_FILTER_SHIFT));
+               mmput(mm);
+               ret = simple_read_from_buffer(buf, count, ppos, buffer, len);
+       }
+
+       put_task_struct(task);
+
+       return ret;
+}
+
+static ssize_t proc_coredump_filter_write(struct file *file,
+                                         const char __user *buf,
+                                         size_t count,
+                                         loff_t *ppos)
+{
+       struct task_struct *task;
+       struct mm_struct *mm;
+       char buffer[PROC_NUMBUF], *end;
+       unsigned int val;
+       int ret;
+       int i;
+       unsigned long mask;
+
+       ret = -EFAULT;
+       memset(buffer, 0, sizeof(buffer));
+       if (count > sizeof(buffer) - 1)
+               count = sizeof(buffer) - 1;
+       if (copy_from_user(buffer, buf, count))
+               goto out_no_task;
+
+       ret = -EINVAL;
+       val = (unsigned int)simple_strtoul(buffer, &end, 0);
+       if (*end == '\n')
+               end++;
+       if (end - buffer == 0)
+               goto out_no_task;
+
+       ret = -ESRCH;
+       task = get_proc_task(file->f_dentry->d_inode);
+       if (!task)
+               goto out_no_task;
+
+       ret = end - buffer;
+       mm = get_task_mm(task);
+       if (!mm)
+               goto out_no_mm;
+
+       for (i = 0, mask = 1; i < MMF_DUMP_FILTER_BITS; i++, mask <<= 1) {
+               if (val & mask)
+                       set_bit(i + MMF_DUMP_FILTER_SHIFT, &mm->flags);
+               else
+                       clear_bit(i + MMF_DUMP_FILTER_SHIFT, &mm->flags);
+       }
+
+       mmput(mm);
+ out_no_mm:
+       put_task_struct(task);
+ out_no_task:
+       return ret;
+}
+
+static const struct file_operations proc_coredump_filter_operations = {
+       .read           = proc_coredump_filter_read,
+       .write          = proc_coredump_filter_write,
+};
+#endif
+
 /*
  * /proc/self:
  */
@@ -1792,14 +1937,14 @@ static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
                              int buflen)
 {
        char tmp[PROC_NUMBUF];
-       sprintf(tmp, "%d", current->tgid);
+       sprintf(tmp, "%d", task_tgid_vnr(current));
        return vfs_readlink(dentry,buffer,buflen,tmp);
 }
 
 static void *proc_self_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
        char tmp[PROC_NUMBUF];
-       sprintf(tmp, "%d", current->tgid);
+       sprintf(tmp, "%d", task_tgid_vnr(current));
        return ERR_PTR(vfs_follow_link(nd,tmp));
 }
 
@@ -1962,7 +2107,7 @@ static const struct pid_entry tgid_base_stuff[] = {
        DIR("task",       S_IRUGO|S_IXUGO, task),
        DIR("fd",         S_IRUSR|S_IXUSR, fd),
        DIR("fdinfo",     S_IRUSR|S_IXUSR, fdinfo),
-       INF("environ",    S_IRUSR, pid_environ),
+       REG("environ",    S_IRUSR, environ),
        INF("auxv",       S_IRUSR, pid_auxv),
        INF("status",     S_IRUGO, pid_status),
 #ifdef CONFIG_SCHED_DEBUG
@@ -1994,8 +2139,11 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_SCHEDSTATS
        INF("schedstat",  S_IRUGO, pid_schedstat),
 #endif
-#ifdef CONFIG_CPUSETS
+#ifdef CONFIG_PROC_PID_CPUSET
        REG("cpuset",     S_IRUGO, cpuset),
+#endif
+#ifdef CONFIG_CGROUPS
+       REG("cgroup",  S_IRUGO, cgroup),
 #endif
        INF("oom_score",  S_IRUGO, oom_score),
        REG("oom_adj",    S_IRUGO|S_IWUSR, oom_adjust),
@@ -2005,6 +2153,9 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_FAULT_INJECTION
        REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject),
 #endif
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+       REG("coredump_filter", S_IRUGO|S_IWUSR, coredump_filter),
+#endif
 #ifdef CONFIG_TASK_IO_ACCOUNTING
        INF("io",       S_IRUGO, pid_io_accounting),
 #endif
@@ -2054,27 +2205,27 @@ static const struct inode_operations proc_tgid_base_inode_operations = {
  *       that no dcache entries will exist at process exit time it
  *       just makes it very unlikely that any will persist.
  */
-void proc_flush_task(struct task_struct *task)
+static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid)
 {
        struct dentry *dentry, *leader, *dir;
        char buf[PROC_NUMBUF];
        struct qstr name;
 
        name.name = buf;
-       name.len = snprintf(buf, sizeof(buf), "%d", task->pid);
-       dentry = d_hash_and_lookup(proc_mnt->mnt_root, &name);
+       name.len = snprintf(buf, sizeof(buf), "%d", pid);
+       dentry = d_hash_and_lookup(mnt->mnt_root, &name);
        if (dentry) {
                shrink_dcache_parent(dentry);
                d_drop(dentry);
                dput(dentry);
        }
 
-       if (thread_group_leader(task))
+       if (tgid == 0)
                goto out;
 
        name.name = buf;
-       name.len = snprintf(buf, sizeof(buf), "%d", task->tgid);
-       leader = d_hash_and_lookup(proc_mnt->mnt_root, &name);
+       name.len = snprintf(buf, sizeof(buf), "%d", tgid);
+       leader = d_hash_and_lookup(mnt->mnt_root, &name);
        if (!leader)
                goto out;
 
@@ -2085,7 +2236,7 @@ void proc_flush_task(struct task_struct *task)
                goto out_put_leader;
 
        name.name = buf;
-       name.len = snprintf(buf, sizeof(buf), "%d", task->pid);
+       name.len = snprintf(buf, sizeof(buf), "%d", pid);
        dentry = d_hash_and_lookup(dir, &name);
        if (dentry) {
                shrink_dcache_parent(dentry);
@@ -2100,6 +2251,36 @@ out:
        return;
 }
 
+/*
+ * when flushing dentries from proc one need to flush them from global
+ * proc (proc_mnt) and from all the namespaces' procs this task was seen
+ * in. this call is supposed to make all this job.
+ */
+
+void proc_flush_task(struct task_struct *task)
+{
+       int i, leader;
+       struct pid *pid, *tgid;
+       struct upid *upid;
+
+       leader = thread_group_leader(task);
+       proc_flush_task_mnt(proc_mnt, task->pid, leader ? task->tgid : 0);
+       pid = task_pid(task);
+       if (pid->level == 0)
+               return;
+
+       tgid = task_tgid(task);
+       for (i = 1; i <= pid->level; i++) {
+               upid = &pid->numbers[i];
+               proc_flush_task_mnt(upid->ns->proc_mnt, upid->nr,
+                               leader ? 0 : tgid->numbers[i].nr);
+       }
+
+       upid = &pid->numbers[pid->level];
+       if (upid->nr == 1)
+               pid_ns_release_proc(upid->ns);
+}
+
 static struct dentry *proc_pid_instantiate(struct inode *dir,
                                           struct dentry * dentry,
                                           struct task_struct *task, const void *ptr)
@@ -2135,6 +2316,7 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
        struct dentry *result = ERR_PTR(-ENOENT);
        struct task_struct *task;
        unsigned tgid;
+       struct pid_namespace *ns;
 
        result = proc_base_lookup(dir, dentry);
        if (!IS_ERR(result) || PTR_ERR(result) != -ENOENT)
@@ -2144,8 +2326,9 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
        if (tgid == ~0U)
                goto out;
 
+       ns = dentry->d_sb->s_fs_info;
        rcu_read_lock();
-       task = find_task_by_pid(tgid);
+       task = find_task_by_pid_ns(tgid, ns);
        if (task)
                get_task_struct(task);
        rcu_read_unlock();
@@ -2162,7 +2345,8 @@ out:
  * Find the first task with tgid >= tgid
  *
  */
-static struct task_struct *next_tgid(unsigned int tgid)
+static struct task_struct *next_tgid(unsigned int tgid,
+               struct pid_namespace *ns)
 {
        struct task_struct *task;
        struct pid *pid;
@@ -2170,9 +2354,9 @@ static struct task_struct *next_tgid(unsigned int tgid)
        rcu_read_lock();
 retry:
        task = NULL;
-       pid = find_ge_pid(tgid);
+       pid = find_ge_pid(tgid, ns);
        if (pid) {
-               tgid = pid->nr + 1;
+               tgid = pid_nr_ns(pid, ns) + 1;
                task = pid_task(pid, PIDTYPE_PID);
                /* What we to know is if the pid we have find is the
                 * pid of a thread_group_leader.  Testing for task
@@ -2212,6 +2396,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
        struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode);
        struct task_struct *task;
        int tgid;
+       struct pid_namespace *ns;
 
        if (!reaper)
                goto out_no_task;
@@ -2222,11 +2407,12 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
                        goto out;
        }
 
+       ns = filp->f_dentry->d_sb->s_fs_info;
        tgid = filp->f_pos - TGID_OFFSET;
-       for (task = next_tgid(tgid);
+       for (task = next_tgid(tgid, ns);
             task;
-            put_task_struct(task), task = next_tgid(tgid + 1)) {
-               tgid = task->pid;
+            put_task_struct(task), task = next_tgid(tgid + 1, ns)) {
+               tgid = task_pid_nr_ns(task, ns);
                filp->f_pos = tgid + TGID_OFFSET;
                if (proc_pid_fill_cache(filp, dirent, filldir, task, tgid) < 0) {
                        put_task_struct(task);
@@ -2246,7 +2432,7 @@ out_no_task:
 static const struct pid_entry tid_base_stuff[] = {
        DIR("fd",        S_IRUSR|S_IXUSR, fd),
        DIR("fdinfo",    S_IRUSR|S_IXUSR, fdinfo),
-       INF("environ",   S_IRUSR, pid_environ),
+       REG("environ",   S_IRUSR, environ),
        INF("auxv",      S_IRUSR, pid_auxv),
        INF("status",    S_IRUGO, pid_status),
 #ifdef CONFIG_SCHED_DEBUG
@@ -2277,8 +2463,11 @@ static const struct pid_entry tid_base_stuff[] = {
 #ifdef CONFIG_SCHEDSTATS
        INF("schedstat", S_IRUGO, pid_schedstat),
 #endif
-#ifdef CONFIG_CPUSETS
+#ifdef CONFIG_PROC_PID_CPUSET
        REG("cpuset",    S_IRUGO, cpuset),
+#endif
+#ifdef CONFIG_CGROUPS
+       REG("cgroup",  S_IRUGO, cgroup),
 #endif
        INF("oom_score", S_IRUGO, oom_score),
        REG("oom_adj",   S_IRUGO|S_IWUSR, oom_adjust),
@@ -2347,6 +2536,7 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
        struct task_struct *task;
        struct task_struct *leader = get_proc_task(dir);
        unsigned tid;
+       struct pid_namespace *ns;
 
        if (!leader)
                goto out_no_task;
@@ -2355,14 +2545,15 @@ static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry
        if (tid == ~0U)
                goto out;
 
+       ns = dentry->d_sb->s_fs_info;
        rcu_read_lock();
-       task = find_task_by_pid(tid);
+       task = find_task_by_pid_ns(tid, ns);
        if (task)
                get_task_struct(task);
        rcu_read_unlock();
        if (!task)
                goto out;
-       if (leader->tgid != task->tgid)
+       if (!same_thread_group(leader, task))
                goto out_drop_task;
 
        result = proc_task_instantiate(dir, dentry, task, NULL);
@@ -2387,14 +2578,14 @@ out_no_task:
  * threads past it.
  */
 static struct task_struct *first_tid(struct task_struct *leader,
-                                       int tid, int nr)
+               int tid, int nr, struct pid_namespace *ns)
 {
        struct task_struct *pos;
 
        rcu_read_lock();
        /* Attempt to start with the pid of a thread */
        if (tid && (nr > 0)) {
-               pos = find_task_by_pid(tid);
+               pos = find_task_by_pid_ns(tid, ns);
                if (pos && (pos->group_leader == leader))
                        goto found;
        }
@@ -2463,6 +2654,7 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
        ino_t ino;
        int tid;
        unsigned long pos = filp->f_pos;  /* avoiding "long long" filp->f_pos */
+       struct pid_namespace *ns;
 
        task = get_proc_task(inode);
        if (!task)
@@ -2496,16 +2688,17 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi
        /* f_version caches the tgid value that the last readdir call couldn't
         * return. lseek aka telldir automagically resets f_version to 0.
         */
-       tid = filp->f_version;
+       ns = filp->f_dentry->d_sb->s_fs_info;
+       tid = (int)filp->f_version;
        filp->f_version = 0;
-       for (task = first_tid(leader, tid, pos - 2);
+       for (task = first_tid(leader, tid, pos - 2, ns);
             task;
             task = next_tid(task), pos++) {
-               tid = task->pid;
+               tid = task_pid_nr_ns(task, ns);
                if (proc_task_fill_cache(filp, dirent, filldir, task, tid) < 0) {
                        /* returning this tgid failed, save it as the first
                         * pid for the next readir call */
-                       filp->f_version = tid;
+                       filp->f_version = (u64)tid;
                        put_task_struct(task);
                        break;
                }