#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/namei.h>
-#include <linux/namespace.h>
+#include <linux/mnt_namespace.h>
#include <linux/mm.h>
-#include <linux/smp_lock.h>
#include <linux/rcupdate.h>
#include <linux/kallsyms.h>
+#include <linux/module.h>
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/ptrace.h>
-#include <linux/seccomp.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 "internal.h"
/* NOTE:
* in /proc for a task before it execs a suid executable.
*/
-/*
- * For hysterical raisins we keep the same inumbers as in the old procfs.
- * Feel free to change the macro below - just keep the range distinct from
- * inumbers of the rest of procfs (currently those are in 0x0000--0xffff).
- * As soon as we'll get a separate superblock we will be able to forget
- * about magical ranges too.
- */
-
-#define fake_ino(pid,ino) (((pid)<<16)|(ino))
-
-enum pid_directory_inos {
- PROC_TGID_INO = 2,
- PROC_TGID_TASK,
- PROC_TGID_STATUS,
- PROC_TGID_MEM,
-#ifdef CONFIG_SECCOMP
- PROC_TGID_SECCOMP,
-#endif
- PROC_TGID_CWD,
- PROC_TGID_ROOT,
- PROC_TGID_EXE,
- PROC_TGID_FD,
- PROC_TGID_ENVIRON,
- PROC_TGID_AUXV,
- PROC_TGID_CMDLINE,
- PROC_TGID_STAT,
- PROC_TGID_STATM,
- PROC_TGID_MAPS,
- PROC_TGID_NUMA_MAPS,
- PROC_TGID_MOUNTS,
- PROC_TGID_MOUNTSTATS,
- PROC_TGID_WCHAN,
-#ifdef CONFIG_MMU
- PROC_TGID_SMAPS,
-#endif
-#ifdef CONFIG_SCHEDSTATS
- PROC_TGID_SCHEDSTAT,
-#endif
-#ifdef CONFIG_CPUSETS
- PROC_TGID_CPUSET,
-#endif
-#ifdef CONFIG_SECURITY
- PROC_TGID_ATTR,
- PROC_TGID_ATTR_CURRENT,
- PROC_TGID_ATTR_PREV,
- PROC_TGID_ATTR_EXEC,
- PROC_TGID_ATTR_FSCREATE,
- PROC_TGID_ATTR_KEYCREATE,
- PROC_TGID_ATTR_SOCKCREATE,
-#endif
-#ifdef CONFIG_AUDITSYSCALL
- PROC_TGID_LOGINUID,
-#endif
- PROC_TGID_OOM_SCORE,
- PROC_TGID_OOM_ADJUST,
- PROC_TID_INO,
- PROC_TID_STATUS,
- PROC_TID_MEM,
-#ifdef CONFIG_SECCOMP
- PROC_TID_SECCOMP,
-#endif
- PROC_TID_CWD,
- PROC_TID_ROOT,
- PROC_TID_EXE,
- PROC_TID_FD,
- PROC_TID_ENVIRON,
- PROC_TID_AUXV,
- PROC_TID_CMDLINE,
- PROC_TID_STAT,
- PROC_TID_STATM,
- PROC_TID_MAPS,
- PROC_TID_NUMA_MAPS,
- PROC_TID_MOUNTS,
- PROC_TID_MOUNTSTATS,
- PROC_TID_WCHAN,
-#ifdef CONFIG_MMU
- PROC_TID_SMAPS,
-#endif
-#ifdef CONFIG_SCHEDSTATS
- PROC_TID_SCHEDSTAT,
-#endif
-#ifdef CONFIG_CPUSETS
- PROC_TID_CPUSET,
-#endif
-#ifdef CONFIG_SECURITY
- PROC_TID_ATTR,
- PROC_TID_ATTR_CURRENT,
- PROC_TID_ATTR_PREV,
- PROC_TID_ATTR_EXEC,
- PROC_TID_ATTR_FSCREATE,
- PROC_TID_ATTR_KEYCREATE,
- PROC_TID_ATTR_SOCKCREATE,
-#endif
-#ifdef CONFIG_AUDITSYSCALL
- PROC_TID_LOGINUID,
-#endif
- PROC_TID_OOM_SCORE,
- PROC_TID_OOM_ADJUST,
-
- /* Add new entries before this */
- PROC_TID_FD_DIR = 0x8000, /* 0x8000-0xffff */
-};
/* Worst case buffer size needed for holding an integer. */
-#define PROC_NUMBUF 10
+#define PROC_NUMBUF 13
struct pid_entry {
- int type;
- int len;
char *name;
+ int len;
mode_t mode;
- struct inode_operations *iop;
- struct file_operations *fop;
+ const struct inode_operations *iop;
+ const struct file_operations *fop;
union proc_op op;
};
-#define NOD(TYPE, NAME, MODE, IOP, FOP, OP) { \
- .type = (TYPE), \
- .len = sizeof(NAME) - 1, \
+#define NOD(NAME, MODE, IOP, FOP, OP) { \
.name = (NAME), \
+ .len = sizeof(NAME) - 1, \
.mode = MODE, \
.iop = IOP, \
.fop = FOP, \
.op = OP, \
}
-#define DIR(TYPE, NAME, MODE, OTYPE) \
- NOD(TYPE, NAME, (S_IFDIR|(MODE)), \
+#define DIR(NAME, MODE, OTYPE) \
+ NOD(NAME, (S_IFDIR|(MODE)), \
&proc_##OTYPE##_inode_operations, &proc_##OTYPE##_operations, \
{} )
-#define LNK(TYPE, NAME, OTYPE) \
- NOD(TYPE, NAME, (S_IFLNK|S_IRWXUGO), \
+#define LNK(NAME, OTYPE) \
+ NOD(NAME, (S_IFLNK|S_IRWXUGO), \
&proc_pid_link_inode_operations, NULL, \
{ .proc_get_link = &proc_##OTYPE##_link } )
-#define REG(TYPE, NAME, MODE, OTYPE) \
- NOD(TYPE, NAME, (S_IFREG|(MODE)), NULL, \
+#define REG(NAME, MODE, OTYPE) \
+ NOD(NAME, (S_IFREG|(MODE)), NULL, \
&proc_##OTYPE##_operations, {})
-#define INF(TYPE, NAME, MODE, OTYPE) \
- NOD(TYPE, NAME, (S_IFREG|(MODE)), \
+#define INF(NAME, MODE, OTYPE) \
+ NOD(NAME, (S_IFREG|(MODE)), \
NULL, &proc_info_file_operations, \
{ .proc_read = &proc_##OTYPE } )
+int maps_protect;
+EXPORT_SYMBOL(maps_protect);
+
static struct fs_struct *get_fs_struct(struct task_struct *task)
{
struct fs_struct *fs;
int res = 0;
struct mm_struct *mm = get_task_mm(task);
if (mm) {
- unsigned int len = mm->env_end - mm->env_start;
+ 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);
- if (!ptrace_may_attach(task))
- res = -ESRCH;
+out:
mmput(mm);
}
return res;
*/
static int proc_pid_wchan(struct task_struct *task, char *buffer)
{
- char *modname;
- const char *sym_name;
- unsigned long wchan, size, offset;
- char namebuf[KSYM_NAME_LEN+1];
+ unsigned long wchan;
+ char symname[KSYM_NAME_LEN];
wchan = get_wchan(task);
- sym_name = kallsyms_lookup(wchan, &size, &offset, &modname, namebuf);
- if (sym_name)
- return sprintf(buffer, "%s", sym_name);
- return sprintf(buffer, "%lu", wchan);
+ if (lookup_symbol_name(wchan, symname) < 0)
+ return sprintf(buffer, "%lu", wchan);
+ else
+ return sprintf(buffer, "%s", symname);
}
#endif /* CONFIG_KALLSYMS */
*/
static int proc_pid_schedstat(struct task_struct *task, char *buffer)
{
- return sprintf(buffer, "%lu %lu %lu\n",
+ return sprintf(buffer, "%llu %llu %lu\n",
task->sched_info.cpu_time,
task->sched_info.run_delay,
task->sched_info.pcnt);
struct timespec uptime;
do_posix_clock_monotonic_gettime(&uptime);
+ read_lock(&tasklist_lock);
points = badness(task, uptime.tv_sec);
+ read_unlock(&tasklist_lock);
return sprintf(buffer, "%lu\n", points);
}
return -EPERM;
error = inode_change_ok(inode, attr);
- if (!error) {
- error = security_inode_setattr(dentry, attr);
- if (!error)
- error = inode_setattr(inode, attr);
- }
+ if (!error)
+ error = inode_setattr(inode, attr);
return error;
}
-static struct inode_operations proc_def_inode_operations = {
+static const struct inode_operations proc_def_inode_operations = {
.setattr = proc_setattr,
};
static int mounts_open(struct inode *inode, struct file *file)
{
struct task_struct *task = get_proc_task(inode);
- struct namespace *namespace = NULL;
+ struct mnt_namespace *ns = NULL;
struct proc_mounts *p;
int ret = -EINVAL;
if (task) {
task_lock(task);
- namespace = task->nsproxy->namespace;
- if (namespace)
- get_namespace(namespace);
+ if (task->nsproxy) {
+ ns = task->nsproxy->mnt_ns;
+ if (ns)
+ get_mnt_ns(ns);
+ }
task_unlock(task);
put_task_struct(task);
}
- if (namespace) {
+ if (ns) {
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;
+ p->m.private = ns;
+ p->event = ns->event;
return 0;
}
kfree(p);
}
- put_namespace(namespace);
+ put_mnt_ns(ns);
}
return ret;
}
static int mounts_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
- struct namespace *namespace = m->private;
- put_namespace(namespace);
+ struct mnt_namespace *ns = m->private;
+ put_mnt_ns(ns);
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;
+ struct mnt_namespace *ns = p->m.private;
unsigned res = 0;
poll_wait(file, &ns->poll, wait);
return res;
}
-static struct file_operations proc_mounts_operations = {
+static const struct file_operations proc_mounts_operations = {
.open = mounts_open,
.read = seq_read,
.llseek = seq_lseek,
if (!ret) {
struct seq_file *m = file->private_data;
- struct namespace *namespace = NULL;
+ struct mnt_namespace *mnt_ns = NULL;
struct task_struct *task = get_proc_task(inode);
if (task) {
task_lock(task);
- namespace = task->nsproxy->namespace;
- if (namespace)
- get_namespace(namespace);
+ if (task->nsproxy)
+ mnt_ns = task->nsproxy->mnt_ns;
+ if (mnt_ns)
+ get_mnt_ns(mnt_ns);
task_unlock(task);
put_task_struct(task);
}
- if (namespace)
- m->private = namespace;
+ if (mnt_ns)
+ m->private = mnt_ns;
else {
seq_release(inode, file);
ret = -EINVAL;
return ret;
}
-static struct file_operations proc_mountstats_operations = {
+static const struct file_operations proc_mountstats_operations = {
.open = mountstats_open,
.read = seq_read,
.llseek = seq_lseek,
static ssize_t proc_info_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct inode * inode = file->f_path.dentry->d_inode;
unsigned long page;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
return length;
}
-static struct file_operations proc_info_file_operations = {
+static const struct file_operations proc_info_file_operations = {
.read = proc_info_read,
};
static ssize_t mem_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 task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
char *page;
unsigned long src = *ppos;
int ret = -ESRCH;
#ifndef mem_write
/* This is a security hazard */
-static ssize_t mem_write(struct file * file, const char * buf,
+static ssize_t mem_write(struct file * file, const char __user *buf,
size_t count, loff_t *ppos)
{
int copied;
char *page;
- struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
+ struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
unsigned long dst = *ppos;
copied = -ESRCH;
return file->f_pos;
}
-static struct file_operations proc_mem_operations = {
+static const struct file_operations proc_mem_operations = {
.llseek = mem_lseek,
.read = mem_read,
.write = mem_write,
static ssize_t oom_adjust_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 task_struct *task = get_proc_task(file->f_path.dentry->d_inode);
char buffer[PROC_NUMBUF];
size_t len;
int oom_adjust;
- loff_t __ppos = *ppos;
if (!task)
return -ESRCH;
put_task_struct(task);
len = snprintf(buffer, sizeof(buffer), "%i\n", oom_adjust);
- if (__ppos >= len)
- return 0;
- if (count > len-__ppos)
- count = len-__ppos;
- if (copy_to_user(buf, buffer + __ppos, count))
- return -EFAULT;
- *ppos = __ppos + count;
- return count;
+
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
}
static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
char buffer[PROC_NUMBUF], *end;
int oom_adjust;
- if (!capable(CAP_SYS_RESOURCE))
- return -EPERM;
memset(buffer, 0, sizeof(buffer));
if (count > sizeof(buffer) - 1)
count = sizeof(buffer) - 1;
if (copy_from_user(buffer, buf, count))
return -EFAULT;
oom_adjust = simple_strtol(buffer, &end, 0);
- if ((oom_adjust < -16 || oom_adjust > 15) && oom_adjust != OOM_DISABLE)
+ if ((oom_adjust < OOM_ADJUST_MIN || oom_adjust > OOM_ADJUST_MAX) &&
+ oom_adjust != OOM_DISABLE)
return -EINVAL;
if (*end == '\n')
end++;
- task = get_proc_task(file->f_dentry->d_inode);
+ task = get_proc_task(file->f_path.dentry->d_inode);
if (!task)
return -ESRCH;
+ if (oom_adjust < task->oomkilladj && !capable(CAP_SYS_RESOURCE)) {
+ put_task_struct(task);
+ return -EACCES;
+ }
task->oomkilladj = oom_adjust;
put_task_struct(task);
if (end - buffer == 0)
return end - buffer;
}
-static struct file_operations proc_oom_adjust_operations = {
+static const struct file_operations proc_oom_adjust_operations = {
.read = oom_adjust_read,
.write = oom_adjust_write,
};
+#ifdef CONFIG_MMU
+static ssize_t clear_refs_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF], *end;
+ struct mm_struct *mm;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count))
+ return -EFAULT;
+ if (!simple_strtol(buffer, &end, 0))
+ return -EINVAL;
+ if (*end == '\n')
+ end++;
+ task = get_proc_task(file->f_path.dentry->d_inode);
+ if (!task)
+ return -ESRCH;
+ mm = get_task_mm(task);
+ if (mm) {
+ clear_refs_smap(mm);
+ mmput(mm);
+ }
+ put_task_struct(task);
+ if (end - buffer == 0)
+ return -EIO;
+ return end - buffer;
+}
+
+static struct file_operations proc_clear_refs_operations = {
+ .write = clear_refs_write,
+};
+#endif
+
#ifdef CONFIG_AUDITSYSCALL
#define TMPBUFLEN 21
static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct inode * inode = file->f_path.dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
ssize_t length;
char tmpbuf[TMPBUFLEN];
static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct inode * inode = file->f_path.dentry->d_inode;
char *page, *tmp;
ssize_t length;
uid_t loginuid;
return length;
}
-static struct file_operations proc_loginuid_operations = {
+static const struct file_operations proc_loginuid_operations = {
.read = proc_loginuid_read,
.write = proc_loginuid_write,
};
#endif
-#ifdef CONFIG_SECCOMP
-static ssize_t seccomp_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+#ifdef CONFIG_FAULT_INJECTION
+static ssize_t proc_fault_inject_read(struct file * file, char __user * buf,
+ size_t count, loff_t *ppos)
{
- struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode);
- char __buf[20];
- loff_t __ppos = *ppos;
+ struct task_struct *task = get_proc_task(file->f_dentry->d_inode);
+ char buffer[PROC_NUMBUF];
size_t len;
+ int make_it_fail;
- if (!tsk)
+ if (!task)
return -ESRCH;
- /* no need to print the trailing zero, so use only len */
- len = sprintf(__buf, "%u\n", tsk->seccomp.mode);
- put_task_struct(tsk);
- if (__ppos >= len)
- return 0;
- if (count > len - __ppos)
- count = len - __ppos;
- if (copy_to_user(buf, __buf + __ppos, count))
+ make_it_fail = task->make_it_fail;
+ put_task_struct(task);
+
+ len = snprintf(buffer, sizeof(buffer), "%i\n", make_it_fail);
+
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
+}
+
+static ssize_t proc_fault_inject_write(struct file * file,
+ const char __user * buf, size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF], *end;
+ int make_it_fail;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+ memset(buffer, 0, sizeof(buffer));
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count))
return -EFAULT;
- *ppos = __ppos + count;
- return count;
+ make_it_fail = simple_strtol(buffer, &end, 0);
+ if (*end == '\n')
+ end++;
+ task = get_proc_task(file->f_dentry->d_inode);
+ if (!task)
+ return -ESRCH;
+ task->make_it_fail = make_it_fail;
+ put_task_struct(task);
+ if (end - buffer == 0)
+ return -EIO;
+ return end - buffer;
}
-static ssize_t seccomp_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
+static const struct file_operations proc_fault_inject_operations = {
+ .read = proc_fault_inject_read,
+ .write = proc_fault_inject_write,
+};
+#endif
+
+#ifdef CONFIG_SCHED_DEBUG
+/*
+ * Print out various scheduling related per-task fields:
+ */
+static int sched_show(struct seq_file *m, void *v)
{
- struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode);
- char __buf[20], *end;
- unsigned int seccomp_mode;
- ssize_t result;
+ struct inode *inode = m->private;
+ struct task_struct *p;
- result = -ESRCH;
- if (!tsk)
- goto out_no_task;
+ WARN_ON(!inode);
- /* can set it only once to be even more secure */
- result = -EPERM;
- if (unlikely(tsk->seccomp.mode))
- goto out;
+ p = get_proc_task(inode);
+ if (!p)
+ return -ESRCH;
+ proc_sched_show_task(p, m);
- result = -EFAULT;
- memset(__buf, 0, sizeof(__buf));
- count = min(count, sizeof(__buf) - 1);
- if (copy_from_user(__buf, buf, count))
- goto out;
+ put_task_struct(p);
- seccomp_mode = simple_strtoul(__buf, &end, 0);
- if (*end == '\n')
- end++;
- result = -EINVAL;
- if (seccomp_mode && seccomp_mode <= NR_SECCOMP_MODES) {
- tsk->seccomp.mode = seccomp_mode;
- set_tsk_thread_flag(tsk, TIF_SECCOMP);
- } else
- goto out;
- result = -EIO;
- if (unlikely(!(end - __buf)))
- goto out;
- result = end - __buf;
-out:
- put_task_struct(tsk);
-out_no_task:
- return result;
+ return 0;
+}
+
+static ssize_t
+sched_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *offset)
+{
+ struct inode *inode = file->f_path.dentry->d_inode;
+ struct task_struct *p;
+
+ WARN_ON(!inode);
+
+ p = get_proc_task(inode);
+ if (!p)
+ return -ESRCH;
+ proc_sched_set_task(p);
+
+ put_task_struct(p);
+
+ return count;
}
-static struct file_operations proc_seccomp_operations = {
- .read = seccomp_read,
- .write = seccomp_write,
+static int sched_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+
+ ret = single_open(filp, sched_show, NULL);
+ if (!ret) {
+ struct seq_file *m = filp->private_data;
+
+ m->private = inode;
+ }
+ return ret;
+}
+
+static const struct file_operations proc_pid_sched_operations = {
+ .open = sched_open,
+ .read = seq_read,
+ .write = sched_write,
+ .llseek = seq_lseek,
+ .release = single_release,
};
-#endif /* CONFIG_SECCOMP */
+
+#endif
static void *proc_pid_follow_link(struct dentry *dentry, struct nameidata *nd)
{
if (!tmp)
return -ENOMEM;
-
+
inode = dentry->d_inode;
path = d_path(dentry, mnt, tmp, PAGE_SIZE);
len = PTR_ERR(path);
return error;
}
-static struct inode_operations proc_pid_link_inode_operations = {
+static const struct inode_operations proc_pid_link_inode_operations = {
.readlink = proc_pid_readlink,
.follow_link = proc_pid_follow_link,
.setattr = proc_setattr,
task_lock(task);
mm = task->mm;
if (mm)
- dumpable = mm->dumpable;
+ dumpable = get_dumpable(mm);
task_unlock(task);
if(dumpable == 1)
return 1;
}
-static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino)
+static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task)
{
struct inode * inode;
struct proc_inode *ei;
/* Common stuff */
ei = PROC_I(inode);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->i_ino = fake_ino(task->pid, ino);
inode->i_op = &proc_def_inode_operations;
/*
* grab the reference to task.
*/
- ei->pid = get_pid(task->pids[PIDTYPE_PID].pid);
+ ei->pid = get_task_pid(task, PIDTYPE_PID);
if (!ei->pid)
goto out_unlock;
/* Lookups */
+typedef struct dentry *instantiate_t(struct inode *, struct dentry *,
+ struct task_struct *, const void *);
+
+/*
+ * Fill a directory entry.
+ *
+ * If possible create the dcache entry and derive our inode number and
+ * file type from dcache entry.
+ *
+ * Since all of the proc inode numbers are dynamically generated, the inode
+ * numbers do not exist until the inode is cache. This means creating the
+ * the dcache entry in readdir is necessary to keep the inode numbers
+ * reported by readdir in sync with the inode numbers reported
+ * by stat.
+ */
+static int proc_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ char *name, int len,
+ instantiate_t instantiate, struct task_struct *task, const void *ptr)
+{
+ struct dentry *child, *dir = filp->f_path.dentry;
+ struct inode *inode;
+ struct qstr qname;
+ ino_t ino = 0;
+ unsigned type = DT_UNKNOWN;
+
+ qname.name = name;
+ qname.len = len;
+ qname.hash = full_name_hash(name, len);
+
+ child = d_lookup(dir, &qname);
+ if (!child) {
+ struct dentry *new;
+ new = d_alloc(dir, &qname);
+ if (new) {
+ child = instantiate(dir->d_inode, new, task, ptr);
+ if (child)
+ dput(new);
+ else
+ child = new;
+ }
+ }
+ if (!child || IS_ERR(child) || !child->d_inode)
+ goto end_instantiate;
+ inode = child->d_inode;
+ if (inode) {
+ ino = inode->i_ino;
+ type = inode->i_mode >> 12;
+ }
+ dput(child);
+end_instantiate:
+ if (!ino)
+ ino = find_inode_number(dir, &qname);
+ if (!ino)
+ ino = 1;
+ return filldir(dirent, name, len, filp->f_pos, ino, type);
+}
+
static unsigned name_to_int(struct dentry *dentry)
{
const char *name = dentry->d_name.name;
return ~0U;
}
-static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+#define PROC_FDINFO_MAX 64
+
+static int proc_fd_info(struct inode *inode, struct dentry **dentry,
+ struct vfsmount **mnt, char *info)
{
struct task_struct *task = get_proc_task(inode);
struct files_struct *files = NULL;
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
- *mnt = mntget(file->f_vfsmnt);
- *dentry = dget(file->f_dentry);
+ if (mnt)
+ *mnt = mntget(file->f_path.mnt);
+ if (dentry)
+ *dentry = dget(file->f_path.dentry);
+ if (info)
+ snprintf(info, PROC_FDINFO_MAX,
+ "pos:\t%lli\n"
+ "flags:\t0%o\n",
+ (long long) file->f_pos,
+ file->f_flags);
spin_unlock(&files->file_lock);
put_files_struct(files);
return 0;
return -ENOENT;
}
+static int proc_fd_link(struct inode *inode, struct dentry **dentry,
+ struct vfsmount **mnt)
+{
+ return proc_fd_info(inode, dentry, mnt, NULL);
+}
+
static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
};
static struct dentry *proc_fd_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- unsigned fd = *(unsigned *)ptr;
+ unsigned fd = *(const unsigned *)ptr;
struct file *file;
struct files_struct *files;
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-ENOENT);
- inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_FD_DIR+fd);
+ inode = proc_pid_make_inode(dir->i_sb, task);
if (!inode)
goto out;
ei = PROC_I(inode);
goto out;
}
-/* SMP-safe */
-static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
+static struct dentry *proc_lookupfd_common(struct inode *dir,
+ struct dentry *dentry,
+ instantiate_t instantiate)
{
struct task_struct *task = get_proc_task(dir);
unsigned fd = name_to_int(dentry);
if (fd == ~0U)
goto out;
- result = proc_fd_instantiate(dir, dentry, task, &fd);
+ result = instantiate(dir, dentry, task, &fd);
out:
put_task_struct(task);
out_no_task:
return result;
}
-static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
+static int proc_readfd_common(struct file * filp, void * dirent,
+ filldir_t filldir, instantiate_t instantiate)
{
- struct dentry *dentry = filp->f_dentry;
+ struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *p = get_proc_task(inode);
unsigned int fd, tid, ino;
int retval;
- char buf[PROC_NUMBUF];
struct files_struct * files;
struct fdtable *fdt;
for (fd = filp->f_pos-2;
fd < fdt->max_fds;
fd++, filp->f_pos++) {
- unsigned int i,j;
+ char name[PROC_NUMBUF];
+ int len;
if (!fcheck_files(files, fd))
continue;
rcu_read_unlock();
- j = PROC_NUMBUF;
- i = fd;
- do {
- j--;
- buf[j] = '0' + (i % 10);
- i /= 10;
- } while (i);
-
- ino = fake_ino(tid, PROC_TID_FD_DIR + fd);
- if (filldir(dirent, buf+j, PROC_NUMBUF-j, fd+2, ino, DT_LNK) < 0) {
+ len = snprintf(name, sizeof(name), "%d", fd);
+ if (proc_fill_cache(filp, dirent, filldir,
+ name, len, instantiate,
+ p, &fd) < 0) {
rcu_read_lock();
break;
}
return retval;
}
-static struct file_operations proc_fd_operations = {
+static struct dentry *proc_lookupfd(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ return proc_lookupfd_common(dir, dentry, proc_fd_instantiate);
+}
+
+static int proc_readfd(struct file *filp, void *dirent, filldir_t filldir)
+{
+ return proc_readfd_common(filp, dirent, filldir, proc_fd_instantiate);
+}
+
+static ssize_t proc_fdinfo_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ char tmp[PROC_FDINFO_MAX];
+ int err = proc_fd_info(file->f_path.dentry->d_inode, NULL, NULL, tmp);
+ if (!err)
+ err = simple_read_from_buffer(buf, len, ppos, tmp, strlen(tmp));
+ return err;
+}
+
+static const struct file_operations proc_fdinfo_file_operations = {
+ .open = nonseekable_open,
+ .read = proc_fdinfo_read,
+};
+
+static const struct file_operations proc_fd_operations = {
.read = generic_read_dir,
.readdir = proc_readfd,
};
+/*
+ * /proc/pid/fd needs a special permission handler so that a process can still
+ * access /proc/self/fd after it has executed a setuid().
+ */
+static int proc_fd_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int rv;
+
+ rv = generic_permission(inode, mask, NULL);
+ if (rv == 0)
+ return 0;
+ if (task_pid(current) == proc_pid(inode))
+ rv = 0;
+ return rv;
+}
+
/*
* proc directories can do almost nothing..
*/
-static struct inode_operations proc_fd_inode_operations = {
+static const struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
+ .permission = proc_fd_permission,
+ .setattr = proc_setattr,
+};
+
+static struct dentry *proc_fdinfo_instantiate(struct inode *dir,
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
+{
+ unsigned fd = *(unsigned *)ptr;
+ struct inode *inode;
+ struct proc_inode *ei;
+ struct dentry *error = ERR_PTR(-ENOENT);
+
+ inode = proc_pid_make_inode(dir->i_sb, task);
+ if (!inode)
+ goto out;
+ ei = PROC_I(inode);
+ ei->fd = fd;
+ inode->i_mode = S_IFREG | S_IRUSR;
+ inode->i_fop = &proc_fdinfo_file_operations;
+ dentry->d_op = &tid_fd_dentry_operations;
+ d_add(dentry, inode);
+ /* Close the race of the process dying before we return the dentry */
+ if (tid_fd_revalidate(dentry, NULL))
+ error = NULL;
+
+ out:
+ return error;
+}
+
+static struct dentry *proc_lookupfdinfo(struct inode *dir,
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ return proc_lookupfd_common(dir, dentry, proc_fdinfo_instantiate);
+}
+
+static int proc_readfdinfo(struct file *filp, void *dirent, filldir_t filldir)
+{
+ return proc_readfd_common(filp, dirent, filldir,
+ proc_fdinfo_instantiate);
+}
+
+static const struct file_operations proc_fdinfo_operations = {
+ .read = generic_read_dir,
+ .readdir = proc_readfdinfo,
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+static const struct inode_operations proc_fdinfo_inode_operations = {
+ .lookup = proc_lookupfdinfo,
.setattr = proc_setattr,
};
+
static struct dentry *proc_pident_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- struct pid_entry *p = ptr;
+ const struct pid_entry *p = ptr;
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-EINVAL);
- inode = proc_pid_make_inode(dir->i_sb, task, p->type);
+ inode = proc_pid_make_inode(dir->i_sb, task);
if (!inode)
goto out;
return error;
}
-/* SMP-safe */
static struct dentry *proc_pident_lookup(struct inode *dir,
struct dentry *dentry,
- struct pid_entry *ents)
+ const struct pid_entry *ents,
+ unsigned int nents)
{
struct inode *inode;
struct dentry *error;
struct task_struct *task = get_proc_task(dir);
- struct pid_entry *p;
+ const struct pid_entry *p, *last;
error = ERR_PTR(-ENOENT);
inode = NULL;
* Yes, it does not scale. And it should not. Don't add
* new entries into /proc/<tgid>/ without very good reasons.
*/
- for (p = ents; p->name; p++) {
+ last = &ents[nents - 1];
+ for (p = ents; p <= last; p++) {
if (p->len != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, p->name, p->len))
break;
}
- if (!p->name)
+ if (p > last)
goto out;
error = proc_pident_instantiate(dir, dentry, task, p);
return error;
}
+static int proc_pident_fill_cache(struct file *filp, void *dirent,
+ filldir_t filldir, struct task_struct *task, const struct pid_entry *p)
+{
+ return proc_fill_cache(filp, dirent, filldir, p->name, p->len,
+ proc_pident_instantiate, task, p);
+}
+
static int proc_pident_readdir(struct file *filp,
void *dirent, filldir_t filldir,
- struct pid_entry *ents, unsigned int nents)
+ const struct pid_entry *ents, unsigned int nents)
{
int i;
int pid;
- struct dentry *dentry = filp->f_dentry;
+ struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
- struct pid_entry *p;
+ const struct pid_entry *p, *last;
ino_t ino;
int ret;
ret = -ENOENT;
if (!task)
- goto out;
+ goto out_no_task;
ret = 0;
pid = task->pid;
- put_task_struct(task);
i = filp->f_pos;
switch (i) {
case 0:
goto out;
}
p = ents + i;
- while (p->name) {
- if (filldir(dirent, p->name, p->len, filp->f_pos,
- fake_ino(pid, p->type), p->mode >> 12) < 0)
+ last = &ents[nents - 1];
+ while (p <= last) {
+ if (proc_pident_fill_cache(filp, dirent, filldir, task, p) < 0)
goto out;
filp->f_pos++;
p++;
ret = 1;
out:
+ put_task_struct(task);
+out_no_task:
return ret;
}
static ssize_t proc_pid_attr_read(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
- unsigned long page;
+ struct inode * inode = file->f_path.dentry->d_inode;
+ char *p = NULL;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
- length = -ESRCH;
if (!task)
- goto out_no_task;
-
- if (count > PAGE_SIZE)
- count = PAGE_SIZE;
- length = -ENOMEM;
- if (!(page = __get_free_page(GFP_KERNEL)))
- goto out;
+ return -ESRCH;
length = security_getprocattr(task,
- (char*)file->f_dentry->d_name.name,
- (void*)page, count);
- if (length >= 0)
- length = simple_read_from_buffer(buf, count, ppos, (char *)page, length);
- free_page(page);
-out:
+ (char*)file->f_path.dentry->d_name.name,
+ &p);
put_task_struct(task);
-out_no_task:
+ if (length > 0)
+ length = simple_read_from_buffer(buf, count, ppos, p, length);
+ kfree(p);
return length;
}
static ssize_t proc_pid_attr_write(struct file * file, const char __user * buf,
size_t count, loff_t *ppos)
{
- struct inode * inode = file->f_dentry->d_inode;
+ struct inode * inode = file->f_path.dentry->d_inode;
char *page;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
goto out_free;
length = security_setprocattr(task,
- (char*)file->f_dentry->d_name.name,
+ (char*)file->f_path.dentry->d_name.name,
(void*)page, count);
out_free:
free_page((unsigned long) page);
return length;
}
-static struct file_operations proc_pid_attr_operations = {
+static const struct file_operations proc_pid_attr_operations = {
.read = proc_pid_attr_read,
.write = proc_pid_attr_write,
};
-static struct pid_entry tgid_attr_stuff[] = {
- REG(PROC_TGID_ATTR_CURRENT, "current", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TGID_ATTR_PREV, "prev", S_IRUGO, pid_attr),
- REG(PROC_TGID_ATTR_EXEC, "exec", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TGID_ATTR_FSCREATE, "fscreate", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TGID_ATTR_KEYCREATE, "keycreate", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TGID_ATTR_SOCKCREATE, "sockcreate", S_IRUGO|S_IWUGO, pid_attr),
- {}
-};
-static struct pid_entry tid_attr_stuff[] = {
- REG(PROC_TID_ATTR_CURRENT, "current", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TID_ATTR_PREV, "prev", S_IRUGO, pid_attr),
- REG(PROC_TID_ATTR_EXEC, "exec", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TID_ATTR_FSCREATE, "fscreate", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TID_ATTR_KEYCREATE, "keycreate", S_IRUGO|S_IWUGO, pid_attr),
- REG(PROC_TID_ATTR_SOCKCREATE, "sockcreate", S_IRUGO|S_IWUGO, pid_attr),
- {}
+static const struct pid_entry attr_dir_stuff[] = {
+ REG("current", S_IRUGO|S_IWUGO, pid_attr),
+ REG("prev", S_IRUGO, pid_attr),
+ REG("exec", S_IRUGO|S_IWUGO, pid_attr),
+ REG("fscreate", S_IRUGO|S_IWUGO, pid_attr),
+ REG("keycreate", S_IRUGO|S_IWUGO, pid_attr),
+ REG("sockcreate", S_IRUGO|S_IWUGO, pid_attr),
};
-static int proc_tgid_attr_readdir(struct file * filp,
+static int proc_attr_dir_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
- tgid_attr_stuff,ARRAY_SIZE(tgid_attr_stuff));
+ attr_dir_stuff,ARRAY_SIZE(attr_dir_stuff));
}
-static int proc_tid_attr_readdir(struct file * filp,
- void * dirent, filldir_t filldir)
+static const struct file_operations proc_attr_dir_operations = {
+ .read = generic_read_dir,
+ .readdir = proc_attr_dir_readdir,
+};
+
+static struct dentry *proc_attr_dir_lookup(struct inode *dir,
+ struct dentry *dentry, struct nameidata *nd)
{
- return proc_pident_readdir(filp,dirent,filldir,
- tid_attr_stuff,ARRAY_SIZE(tid_attr_stuff));
+ return proc_pident_lookup(dir, dentry,
+ attr_dir_stuff, ARRAY_SIZE(attr_dir_stuff));
}
-static struct file_operations proc_tgid_attr_operations = {
- .read = generic_read_dir,
- .readdir = proc_tgid_attr_readdir,
+static const struct inode_operations proc_attr_dir_inode_operations = {
+ .lookup = proc_attr_dir_lookup,
+ .getattr = pid_getattr,
+ .setattr = proc_setattr,
};
-static struct file_operations proc_tid_attr_operations = {
- .read = generic_read_dir,
- .readdir = proc_tid_attr_readdir,
-};
+#endif
-static struct dentry *proc_tgid_attr_lookup(struct inode *dir,
- struct dentry *dentry, struct nameidata *nd)
+#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)
{
- return proc_pident_lookup(dir, dentry, tgid_attr_stuff);
+ 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 struct dentry *proc_tid_attr_lookup(struct inode *dir,
- struct dentry *dentry, struct nameidata *nd)
+static ssize_t proc_coredump_filter_write(struct file *file,
+ const char __user *buf,
+ size_t count,
+ loff_t *ppos)
{
- return proc_pident_lookup(dir, dentry, tid_attr_stuff);
-}
+ struct task_struct *task;
+ struct mm_struct *mm;
+ char buffer[PROC_NUMBUF], *end;
+ unsigned int val;
+ int ret;
+ int i;
+ unsigned long mask;
-static struct inode_operations proc_tgid_attr_inode_operations = {
- .lookup = proc_tgid_attr_lookup,
- .getattr = pid_getattr,
- .setattr = proc_setattr,
-};
+ 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;
-static struct inode_operations proc_tid_attr_inode_operations = {
- .lookup = proc_tid_attr_lookup,
- .getattr = pid_getattr,
- .setattr = proc_setattr,
+ 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
return ERR_PTR(vfs_follow_link(nd,tmp));
}
-static struct inode_operations proc_self_inode_operations = {
+static const struct inode_operations proc_self_inode_operations = {
.readlink = proc_self_readlink,
.follow_link = proc_self_follow_link,
};
* that properly belong to the /proc filesystem, as they describe
* describe something that is process related.
*/
-static struct pid_entry proc_base_stuff[] = {
- NOD(PROC_TGID_INO, "self", S_IFLNK|S_IRWXUGO,
+static const struct pid_entry proc_base_stuff[] = {
+ NOD("self", S_IFLNK|S_IRWXUGO,
&proc_self_inode_operations, NULL, {}),
- {}
};
/*
};
static struct dentry *proc_base_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- struct pid_entry *p = ptr;
+ const struct pid_entry *p = ptr;
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-EINVAL);
/* Initialize the inode */
ei = PROC_I(inode);
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
- inode->i_ino = fake_ino(0, p->type);
/*
* grab the reference to the task.
*/
- ei->pid = get_pid(task_pid(task));
+ ei->pid = get_task_pid(task, PIDTYPE_PID);
if (!ei->pid)
goto out_iput;
{
struct dentry *error;
struct task_struct *task = get_proc_task(dir);
- struct pid_entry *p;
+ const struct pid_entry *p, *last;
error = ERR_PTR(-ENOENT);
goto out_no_task;
/* Lookup the directory entry */
- for (p = proc_base_stuff; p->name; p++) {
+ last = &proc_base_stuff[ARRAY_SIZE(proc_base_stuff) - 1];
+ for (p = proc_base_stuff; p <= last; p++) {
if (p->len != dentry->d_name.len)
continue;
if (!memcmp(dentry->d_name.name, p->name, p->len))
break;
}
- if (!p->name)
+ if (p > last)
goto out;
error = proc_base_instantiate(dir, dentry, task, p);
return error;
}
+static int proc_base_fill_cache(struct file *filp, void *dirent,
+ filldir_t filldir, struct task_struct *task, const struct pid_entry *p)
+{
+ return proc_fill_cache(filp, dirent, filldir, p->name, p->len,
+ proc_base_instantiate, task, p);
+}
+
+#ifdef CONFIG_TASK_IO_ACCOUNTING
+static int proc_pid_io_accounting(struct task_struct *task, char *buffer)
+{
+ return sprintf(buffer,
+#ifdef CONFIG_TASK_XACCT
+ "rchar: %llu\n"
+ "wchar: %llu\n"
+ "syscr: %llu\n"
+ "syscw: %llu\n"
+#endif
+ "read_bytes: %llu\n"
+ "write_bytes: %llu\n"
+ "cancelled_write_bytes: %llu\n",
+#ifdef CONFIG_TASK_XACCT
+ (unsigned long long)task->rchar,
+ (unsigned long long)task->wchar,
+ (unsigned long long)task->syscr,
+ (unsigned long long)task->syscw,
+#endif
+ (unsigned long long)task->ioac.read_bytes,
+ (unsigned long long)task->ioac.write_bytes,
+ (unsigned long long)task->ioac.cancelled_write_bytes);
+}
+#endif
+
/*
* Thread groups
*/
-static struct file_operations proc_task_operations;
-static struct inode_operations proc_task_inode_operations;
-
-static struct pid_entry tgid_base_stuff[] = {
- DIR(PROC_TGID_TASK, "task", S_IRUGO|S_IXUGO, task),
- DIR(PROC_TGID_FD, "fd", S_IRUSR|S_IXUSR, fd),
- INF(PROC_TGID_ENVIRON, "environ", S_IRUSR, pid_environ),
- INF(PROC_TGID_AUXV, "auxv", S_IRUSR, pid_auxv),
- INF(PROC_TGID_STATUS, "status", S_IRUGO, pid_status),
- INF(PROC_TGID_CMDLINE, "cmdline", S_IRUGO, pid_cmdline),
- INF(PROC_TGID_STAT, "stat", S_IRUGO, tgid_stat),
- INF(PROC_TGID_STATM, "statm", S_IRUGO, pid_statm),
- REG(PROC_TGID_MAPS, "maps", S_IRUGO, maps),
-#ifdef CONFIG_NUMA
- REG(PROC_TGID_NUMA_MAPS, "numa_maps", S_IRUGO, numa_maps),
+static const struct file_operations proc_task_operations;
+static const struct inode_operations proc_task_inode_operations;
+
+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),
+ INF("auxv", S_IRUSR, pid_auxv),
+ INF("status", S_IRUGO, pid_status),
+#ifdef CONFIG_SCHED_DEBUG
+ REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
- REG(PROC_TGID_MEM, "mem", S_IRUSR|S_IWUSR, mem),
-#ifdef CONFIG_SECCOMP
- REG(PROC_TGID_SECCOMP, "seccomp", S_IRUSR|S_IWUSR, seccomp),
+ INF("cmdline", S_IRUGO, pid_cmdline),
+ INF("stat", S_IRUGO, tgid_stat),
+ INF("statm", S_IRUGO, pid_statm),
+ REG("maps", S_IRUGO, maps),
+#ifdef CONFIG_NUMA
+ REG("numa_maps", S_IRUGO, numa_maps),
#endif
- LNK(PROC_TGID_CWD, "cwd", cwd),
- LNK(PROC_TGID_ROOT, "root", root),
- LNK(PROC_TGID_EXE, "exe", exe),
- REG(PROC_TGID_MOUNTS, "mounts", S_IRUGO, mounts),
- REG(PROC_TGID_MOUNTSTATS, "mountstats", S_IRUSR, mountstats),
+ REG("mem", S_IRUSR|S_IWUSR, mem),
+ LNK("cwd", cwd),
+ LNK("root", root),
+ LNK("exe", exe),
+ REG("mounts", S_IRUGO, mounts),
+ REG("mountstats", S_IRUSR, mountstats),
#ifdef CONFIG_MMU
- REG(PROC_TGID_SMAPS, "smaps", S_IRUGO, smaps),
+ REG("clear_refs", S_IWUSR, clear_refs),
+ REG("smaps", S_IRUGO, smaps),
#endif
#ifdef CONFIG_SECURITY
- DIR(PROC_TGID_ATTR, "attr", S_IRUGO|S_IXUGO, tgid_attr),
+ DIR("attr", S_IRUGO|S_IXUGO, attr_dir),
#endif
#ifdef CONFIG_KALLSYMS
- INF(PROC_TGID_WCHAN, "wchan", S_IRUGO, pid_wchan),
+ INF("wchan", S_IRUGO, pid_wchan),
#endif
#ifdef CONFIG_SCHEDSTATS
- INF(PROC_TGID_SCHEDSTAT, "schedstat", S_IRUGO, pid_schedstat),
+ INF("schedstat", S_IRUGO, pid_schedstat),
#endif
#ifdef CONFIG_CPUSETS
- REG(PROC_TGID_CPUSET, "cpuset", S_IRUGO, cpuset),
+ REG("cpuset", S_IRUGO, cpuset),
#endif
- INF(PROC_TGID_OOM_SCORE, "oom_score", S_IRUGO, oom_score),
- REG(PROC_TGID_OOM_ADJUST, "oom_adj", S_IRUGO|S_IWUSR, oom_adjust),
+ INF("oom_score", S_IRUGO, oom_score),
+ REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust),
#ifdef CONFIG_AUDITSYSCALL
- REG(PROC_TGID_LOGINUID, "loginuid", S_IWUSR|S_IRUGO, loginuid),
+ REG("loginuid", S_IWUSR|S_IRUGO, loginuid),
+#endif
+#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
- {}
};
static int proc_tgid_base_readdir(struct file * filp,
tgid_base_stuff,ARRAY_SIZE(tgid_base_stuff));
}
-static struct file_operations proc_tgid_base_operations = {
+static const struct file_operations proc_tgid_base_operations = {
.read = generic_read_dir,
.readdir = proc_tgid_base_readdir,
};
static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){
- return proc_pident_lookup(dir, dentry, tgid_base_stuff);
+ return proc_pident_lookup(dir, dentry,
+ tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff));
}
-static struct inode_operations proc_tgid_base_inode_operations = {
+static const struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
return;
}
-struct dentry *proc_pid_instantiate(struct inode *dir,
- struct dentry * dentry, struct task_struct *task, void *ptr)
+static struct dentry *proc_pid_instantiate(struct inode *dir,
+ struct dentry * dentry,
+ struct task_struct *task, const void *ptr)
{
struct dentry *error = ERR_PTR(-ENOENT);
struct inode *inode;
- inode = proc_pid_make_inode(dir->i_sb, task, PROC_TGID_INO);
+ inode = proc_pid_make_inode(dir->i_sb, task);
if (!inode)
goto out;
inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations;
inode->i_flags|=S_IMMUTABLE;
- inode->i_nlink = 4;
+ inode->i_nlink = 5;
#ifdef CONFIG_SECURITY
inode->i_nlink += 1;
#endif
return error;
}
-/* SMP-safe */
struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
return task;
}
-#define TGID_OFFSET (FIRST_PROCESS_ENTRY + (1 /* /proc/self */))
+#define TGID_OFFSET (FIRST_PROCESS_ENTRY + ARRAY_SIZE(proc_base_stuff))
+
+static int proc_pid_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ struct task_struct *task, int tgid)
+{
+ char name[PROC_NUMBUF];
+ int len = snprintf(name, sizeof(name), "%d", tgid);
+ return proc_fill_cache(filp, dirent, filldir, name, len,
+ proc_pid_instantiate, task, NULL);
+}
/* for the /proc/ directory itself, after non-process stuff has been done */
int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
- char buf[PROC_NUMBUF];
unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
+ struct task_struct *reaper = get_proc_task(filp->f_path.dentry->d_inode);
struct task_struct *task;
int tgid;
- for (; nr < (ARRAY_SIZE(proc_base_stuff) - 1); filp->f_pos++, nr++) {
- struct pid_entry *p = &proc_base_stuff[nr];
- if (filldir(dirent, p->name, p->len, filp->f_pos,
- fake_ino(0, p->type), p->mode >> 12) < 0)
+ if (!reaper)
+ goto out_no_task;
+
+ for (; nr < ARRAY_SIZE(proc_base_stuff); filp->f_pos++, nr++) {
+ const struct pid_entry *p = &proc_base_stuff[nr];
+ if (proc_base_fill_cache(filp, dirent, filldir, reaper, p) < 0)
goto out;
}
for (task = next_tgid(tgid);
task;
put_task_struct(task), task = next_tgid(tgid + 1)) {
- int len;
- ino_t ino;
tgid = task->pid;
filp->f_pos = tgid + TGID_OFFSET;
- len = snprintf(buf, sizeof(buf), "%d", tgid);
- ino = fake_ino(tgid, PROC_TGID_INO);
- if (filldir(dirent, buf, len, filp->f_pos, ino, DT_DIR) < 0) {
+ if (proc_pid_fill_cache(filp, dirent, filldir, task, tgid) < 0) {
put_task_struct(task);
goto out;
}
}
filp->f_pos = PID_MAX_LIMIT + TGID_OFFSET;
out:
+ put_task_struct(reaper);
+out_no_task:
return 0;
}
/*
* Tasks
*/
-static struct pid_entry tid_base_stuff[] = {
- DIR(PROC_TID_FD, "fd", S_IRUSR|S_IXUSR, fd),
- INF(PROC_TID_ENVIRON, "environ", S_IRUSR, pid_environ),
- INF(PROC_TID_AUXV, "auxv", S_IRUSR, pid_auxv),
- INF(PROC_TID_STATUS, "status", S_IRUGO, pid_status),
- INF(PROC_TID_CMDLINE, "cmdline", S_IRUGO, pid_cmdline),
- INF(PROC_TID_STAT, "stat", S_IRUGO, tid_stat),
- INF(PROC_TID_STATM, "statm", S_IRUGO, pid_statm),
- REG(PROC_TID_MAPS, "maps", S_IRUGO, maps),
-#ifdef CONFIG_NUMA
- REG(PROC_TID_NUMA_MAPS, "numa_maps", S_IRUGO, numa_maps),
+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),
+ INF("auxv", S_IRUSR, pid_auxv),
+ INF("status", S_IRUGO, pid_status),
+#ifdef CONFIG_SCHED_DEBUG
+ REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
- REG(PROC_TID_MEM, "mem", S_IRUSR|S_IWUSR, mem),
-#ifdef CONFIG_SECCOMP
- REG(PROC_TID_SECCOMP, "seccomp", S_IRUSR|S_IWUSR, seccomp),
+ INF("cmdline", S_IRUGO, pid_cmdline),
+ INF("stat", S_IRUGO, tid_stat),
+ INF("statm", S_IRUGO, pid_statm),
+ REG("maps", S_IRUGO, maps),
+#ifdef CONFIG_NUMA
+ REG("numa_maps", S_IRUGO, numa_maps),
#endif
- LNK(PROC_TID_CWD, "cwd", cwd),
- LNK(PROC_TID_ROOT, "root", root),
- LNK(PROC_TID_EXE, "exe", exe),
- REG(PROC_TID_MOUNTS, "mounts", S_IRUGO, mounts),
+ REG("mem", S_IRUSR|S_IWUSR, mem),
+ LNK("cwd", cwd),
+ LNK("root", root),
+ LNK("exe", exe),
+ REG("mounts", S_IRUGO, mounts),
#ifdef CONFIG_MMU
- REG(PROC_TID_SMAPS, "smaps", S_IRUGO, smaps),
+ REG("clear_refs", S_IWUSR, clear_refs),
+ REG("smaps", S_IRUGO, smaps),
#endif
#ifdef CONFIG_SECURITY
- DIR(PROC_TID_ATTR, "attr", S_IRUGO|S_IXUGO, tid_attr),
+ DIR("attr", S_IRUGO|S_IXUGO, attr_dir),
#endif
#ifdef CONFIG_KALLSYMS
- INF(PROC_TID_WCHAN, "wchan", S_IRUGO, pid_wchan),
+ INF("wchan", S_IRUGO, pid_wchan),
#endif
#ifdef CONFIG_SCHEDSTATS
- INF(PROC_TID_SCHEDSTAT, "schedstat", S_IRUGO, pid_schedstat),
+ INF("schedstat", S_IRUGO, pid_schedstat),
#endif
#ifdef CONFIG_CPUSETS
- REG(PROC_TID_CPUSET, "cpuset", S_IRUGO, cpuset),
+ REG("cpuset", S_IRUGO, cpuset),
#endif
- INF(PROC_TID_OOM_SCORE, "oom_score", S_IRUGO, oom_score),
- REG(PROC_TID_OOM_ADJUST, "oom_adj", S_IRUGO|S_IWUSR, oom_adjust),
+ INF("oom_score", S_IRUGO, oom_score),
+ REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust),
#ifdef CONFIG_AUDITSYSCALL
- REG(PROC_TID_LOGINUID, "loginuid", S_IWUSR|S_IRUGO, loginuid),
+ REG("loginuid", S_IWUSR|S_IRUGO, loginuid),
+#endif
+#ifdef CONFIG_FAULT_INJECTION
+ REG("make-it-fail", S_IRUGO|S_IWUSR, fault_inject),
#endif
- {}
};
static int proc_tid_base_readdir(struct file * filp,
}
static struct dentry *proc_tid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){
- return proc_pident_lookup(dir, dentry, tid_base_stuff);
+ return proc_pident_lookup(dir, dentry,
+ tid_base_stuff, ARRAY_SIZE(tid_base_stuff));
}
-static struct file_operations proc_tid_base_operations = {
+static const struct file_operations proc_tid_base_operations = {
.read = generic_read_dir,
.readdir = proc_tid_base_readdir,
};
-static struct inode_operations proc_tid_base_inode_operations = {
+static const struct inode_operations proc_tid_base_inode_operations = {
.lookup = proc_tid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
};
static struct dentry *proc_task_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
struct dentry *error = ERR_PTR(-ENOENT);
struct inode *inode;
- inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_INO);
+ inode = proc_pid_make_inode(dir->i_sb, task);
if (!inode)
goto out;
inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations;
inode->i_flags|=S_IMMUTABLE;
- inode->i_nlink = 3;
+ inode->i_nlink = 4;
#ifdef CONFIG_SECURITY
inode->i_nlink += 1;
#endif
return error;
}
-/* SMP-safe */
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
return pos;
}
+static int proc_task_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
+ struct task_struct *task, int tid)
+{
+ char name[PROC_NUMBUF];
+ int len = snprintf(name, sizeof(name), "%d", tid);
+ return proc_fill_cache(filp, dirent, filldir, name, len,
+ proc_task_instantiate, task, NULL);
+}
+
/* for the /proc/TGID/task/ directories */
static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
- char buf[PROC_NUMBUF];
- struct dentry *dentry = filp->f_dentry;
+ struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
- struct task_struct *leader = get_proc_task(inode);
+ struct task_struct *leader = NULL;
struct task_struct *task;
int retval = -ENOENT;
ino_t ino;
int tid;
unsigned long pos = filp->f_pos; /* avoiding "long long" filp->f_pos */
+ task = get_proc_task(inode);
+ if (!task)
+ goto out_no_task;
+ rcu_read_lock();
+ if (pid_alive(task)) {
+ leader = task->group_leader;
+ get_task_struct(leader);
+ }
+ rcu_read_unlock();
+ put_task_struct(task);
if (!leader)
goto out_no_task;
retval = 0;
for (task = first_tid(leader, tid, pos - 2);
task;
task = next_tid(task), pos++) {
- int len;
tid = task->pid;
- len = snprintf(buf, sizeof(buf), "%d", tid);
- ino = fake_ino(tid, PROC_TID_INO);
- if (filldir(dirent, buf, len, pos, ino, DT_DIR < 0)) {
+ 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;
return 0;
}
-static struct inode_operations proc_task_inode_operations = {
+static const struct inode_operations proc_task_inode_operations = {
.lookup = proc_task_lookup,
.getattr = proc_task_getattr,
.setattr = proc_setattr,
};
-static struct file_operations proc_task_operations = {
+static const struct file_operations proc_task_operations = {
.read = generic_read_dir,
.readdir = proc_task_readdir,
};