Commit 28a6d671 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by Linus Torvalds

[PATCH] proc: reorder the functions in base.c

There were enough changes in my last round of cleaning up proc I had to break
up the patch series into smaller chunks, and my last chunk never got resent.

This patchset gives proc dynamic inode numbers (the static inode numbers were
a pain to maintain and prevent all kinds of things), and removes the horrible
switch statements that had to be kept in sync with everything else.  Being
fully table driver takes us 90% of the way of being able to register new
process specific attributes in proc.

This patch:

Group the functions by what they implement instead of by type of operation.
As it existed base.c was quickly approaching the point where it could not be
followed.

No functionality or code changes asside from adding/removing forward
declartions are implemented in this patch.
Signed-off-by: default avatarEric W. Biederman <ebiederm@xmission.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0804ef4b
...@@ -198,147 +198,6 @@ struct pid_entry { ...@@ -198,147 +198,6 @@ struct pid_entry {
#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)} #define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)}
static struct pid_entry tgid_base_stuff[] = {
E(PROC_TGID_TASK, "task", S_IFDIR|S_IRUGO|S_IXUGO),
E(PROC_TGID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR),
E(PROC_TGID_ENVIRON, "environ", S_IFREG|S_IRUSR),
E(PROC_TGID_AUXV, "auxv", S_IFREG|S_IRUSR),
E(PROC_TGID_STATUS, "status", S_IFREG|S_IRUGO),
E(PROC_TGID_CMDLINE, "cmdline", S_IFREG|S_IRUGO),
E(PROC_TGID_STAT, "stat", S_IFREG|S_IRUGO),
E(PROC_TGID_STATM, "statm", S_IFREG|S_IRUGO),
E(PROC_TGID_MAPS, "maps", S_IFREG|S_IRUGO),
#ifdef CONFIG_NUMA
E(PROC_TGID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO),
#endif
E(PROC_TGID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR),
#ifdef CONFIG_SECCOMP
E(PROC_TGID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR),
#endif
E(PROC_TGID_CWD, "cwd", S_IFLNK|S_IRWXUGO),
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),
E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR),
#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
#ifdef CONFIG_KALLSYMS
E(PROC_TGID_WCHAN, "wchan", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_SCHEDSTATS
E(PROC_TGID_SCHEDSTAT, "schedstat", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_CPUSETS
E(PROC_TGID_CPUSET, "cpuset", S_IFREG|S_IRUGO),
#endif
E(PROC_TGID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TGID_OOM_ADJUST,"oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TGID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
static struct pid_entry tid_base_stuff[] = {
E(PROC_TID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR),
E(PROC_TID_ENVIRON, "environ", S_IFREG|S_IRUSR),
E(PROC_TID_AUXV, "auxv", S_IFREG|S_IRUSR),
E(PROC_TID_STATUS, "status", S_IFREG|S_IRUGO),
E(PROC_TID_CMDLINE, "cmdline", S_IFREG|S_IRUGO),
E(PROC_TID_STAT, "stat", S_IFREG|S_IRUGO),
E(PROC_TID_STATM, "statm", S_IFREG|S_IRUGO),
E(PROC_TID_MAPS, "maps", S_IFREG|S_IRUGO),
#ifdef CONFIG_NUMA
E(PROC_TID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO),
#endif
E(PROC_TID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR),
#ifdef CONFIG_SECCOMP
E(PROC_TID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR),
#endif
E(PROC_TID_CWD, "cwd", S_IFLNK|S_IRWXUGO),
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
#ifdef CONFIG_KALLSYMS
E(PROC_TID_WCHAN, "wchan", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_SCHEDSTATS
E(PROC_TID_SCHEDSTAT, "schedstat",S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_CPUSETS
E(PROC_TID_CPUSET, "cpuset", S_IFREG|S_IRUGO),
#endif
E(PROC_TID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TID_OOM_ADJUST, "oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
#ifdef CONFIG_SECURITY
static struct pid_entry tgid_attr_stuff[] = {
E(PROC_TGID_ATTR_CURRENT, "current", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_PREV, "prev", S_IFREG|S_IRUGO),
E(PROC_TGID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO),
{0,0,NULL,0}
};
static struct pid_entry tid_attr_stuff[] = {
E(PROC_TID_ATTR_CURRENT, "current", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_PREV, "prev", S_IFREG|S_IRUGO),
E(PROC_TID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO),
{0,0,NULL,0}
};
#endif
#undef E
static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
{
struct task_struct *task = get_proc_task(inode);
struct files_struct *files = NULL;
struct file *file;
int fd = proc_fd(inode);
if (task) {
files = get_files_struct(task);
put_task_struct(task);
}
if (files) {
/*
* We are not taking a ref to the file structure, so we must
* hold ->file_lock.
*/
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
*mnt = mntget(file->f_vfsmnt);
*dentry = dget(file->f_dentry);
spin_unlock(&files->file_lock);
put_files_struct(files);
return 0;
}
spin_unlock(&files->file_lock);
put_files_struct(files);
}
return -ENOENT;
}
static struct fs_struct *get_fs_struct(struct task_struct *task) static struct fs_struct *get_fs_struct(struct task_struct *task)
{ {
struct fs_struct *fs; struct fs_struct *fs;
...@@ -1137,202 +996,86 @@ static struct inode_operations proc_pid_link_inode_operations = { ...@@ -1137,202 +996,86 @@ static struct inode_operations proc_pid_link_inode_operations = {
.setattr = proc_setattr, .setattr = proc_setattr,
}; };
static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
/* building an inode */
static int task_dumpable(struct task_struct *task)
{ {
struct dentry *dentry = filp->f_dentry; int dumpable = 0;
struct inode *inode = dentry->d_inode; struct mm_struct *mm;
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;
retval = -ENOENT; task_lock(task);
if (!p) mm = task->mm;
goto out_no_task; if (mm)
retval = 0; dumpable = mm->dumpable;
tid = p->pid; task_unlock(task);
if(dumpable == 1)
return 1;
return 0;
}
fd = filp->f_pos;
switch (fd) { static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino)
case 0: {
if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) struct inode * inode;
goto out; struct proc_inode *ei;
filp->f_pos++;
case 1: /* We need a new inode */
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) inode = new_inode(sb);
goto out; if (!inode)
filp->f_pos++;
default:
files = get_files_struct(p);
if (!files)
goto out; goto out;
rcu_read_lock();
fdt = files_fdtable(files);
for (fd = filp->f_pos-2;
fd < fdt->max_fds;
fd++, filp->f_pos++) {
unsigned int i,j;
if (!fcheck_files(files, fd)) /* Common stuff */
continue; ei = PROC_I(inode);
rcu_read_unlock(); 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;
j = PROC_NUMBUF; /*
i = fd; * grab the reference to task.
do { */
j--; ei->pid = get_pid(task->pids[PIDTYPE_PID].pid);
buf[j] = '0' + (i % 10); if (!ei->pid)
i /= 10; goto out_unlock;
} while (i);
ino = fake_ino(tid, PROC_TID_FD_DIR + fd); inode->i_uid = 0;
if (filldir(dirent, buf+j, PROC_NUMBUF-j, fd+2, ino, DT_LNK) < 0) { inode->i_gid = 0;
rcu_read_lock(); if (task_dumpable(task)) {
break; inode->i_uid = task->euid;
} inode->i_gid = task->egid;
rcu_read_lock();
}
rcu_read_unlock();
put_files_struct(files);
} }
security_task_to_inode(task, inode);
out: out:
put_task_struct(p); return inode;
out_no_task:
return retval; out_unlock:
iput(inode);
return NULL;
} }
static int proc_pident_readdir(struct file *filp, static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
void *dirent, filldir_t filldir,
struct pid_entry *ents, unsigned int nents)
{ {
int i;
int pid;
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode); struct task_struct *task;
struct pid_entry *p; generic_fillattr(inode, stat);
ino_t ino;
int ret;
ret = -ENOENT;
if (!task)
goto out;
ret = 0; rcu_read_lock();
pid = task->pid; stat->uid = 0;
put_task_struct(task); stat->gid = 0;
i = filp->f_pos; task = pid_task(proc_pid(inode), PIDTYPE_PID);
switch (i) { if (task) {
case 0: if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
ino = inode->i_ino; task_dumpable(task)) {
if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0) stat->uid = task->euid;
goto out; stat->gid = task->egid;
i++;
filp->f_pos++;
/* fall through */
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
goto out;
i++;
filp->f_pos++;
/* fall through */
default:
i -= 2;
if (i >= nents) {
ret = 1;
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)
goto out;
filp->f_pos++;
p++;
} }
} }
rcu_read_unlock();
ret = 1;
out:
return ret;
}
static int proc_tgid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
tgid_base_stuff,ARRAY_SIZE(tgid_base_stuff));
}
static int proc_tid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
tid_base_stuff,ARRAY_SIZE(tid_base_stuff));
}
/* building an inode */
static int task_dumpable(struct task_struct *task)
{
int dumpable = 0;
struct mm_struct *mm;
task_lock(task);
mm = task->mm;
if (mm)
dumpable = mm->dumpable;
task_unlock(task);
if(dumpable == 1)
return 1;
return 0; return 0;
} }
static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino)
{
struct inode * inode;
struct proc_inode *ei;
/* We need a new inode */
inode = new_inode(sb);
if (!inode)
goto out;
/* 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);
if (!ei->pid)
goto out_unlock;
inode->i_uid = 0;
inode->i_gid = 0;
if (task_dumpable(task)) {
inode->i_uid = task->euid;
inode->i_gid = task->egid;
}
security_task_to_inode(task, inode);
out:
return inode;
out_unlock:
iput(inode);
return NULL;
}
/* dentry stuff */ /* dentry stuff */
/* /*
...@@ -1372,25 +1115,74 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1372,25 +1115,74 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd)
return 0; return 0;
} }
static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) static int pid_delete_dentry(struct dentry * dentry)
{ {
struct inode *inode = dentry->d_inode; /* Is the task we represent dead?
struct task_struct *task; * If so, then don't put the dentry on the lru list,
generic_fillattr(inode, stat); * kill it immediately.
*/
return !proc_pid(dentry->d_inode)->tasks[PIDTYPE_PID].first;
}
rcu_read_lock(); static struct dentry_operations pid_dentry_operations =
stat->uid = 0; {
stat->gid = 0; .d_revalidate = pid_revalidate,
task = pid_task(proc_pid(inode), PIDTYPE_PID); .d_delete = pid_delete_dentry,
if (task) { };
if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) ||
task_dumpable(task)) { /* Lookups */
stat->uid = task->euid;
stat->gid = task->egid; static unsigned name_to_int(struct dentry *dentry)
{
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
unsigned n = 0;
if (len > 1 && *name == '0')
goto out;
while (len-- > 0) {
unsigned c = *name++ - '0';
if (c > 9)
goto out;
if (n >= (~0U-9)/10)
goto out;
n *= 10;
n += c;
} }
return n;
out:
return ~0U;
}
static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
{
struct task_struct *task = get_proc_task(inode);
struct files_struct *files = NULL;
struct file *file;
int fd = proc_fd(inode);
if (task) {
files = get_files_struct(task);
put_task_struct(task);
} }
rcu_read_unlock(); if (files) {
/*
* We are not taking a ref to the file structure, so we must
* hold ->file_lock.
*/
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
*mnt = mntget(file->f_vfsmnt);
*dentry = dget(file->f_dentry);
spin_unlock(&files->file_lock);
put_files_struct(files);
return 0; return 0;
}
spin_unlock(&files->file_lock);
put_files_struct(files);
}
return -ENOENT;
} }
static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
...@@ -1428,51 +1220,12 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -1428,51 +1220,12 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
return 0; return 0;
} }
static int pid_delete_dentry(struct dentry * dentry)
{
/* Is the task we represent dead?
* If so, then don't put the dentry on the lru list,
* kill it immediately.
*/
return !proc_pid(dentry->d_inode)->tasks[PIDTYPE_PID].first;
}
static struct dentry_operations tid_fd_dentry_operations = static struct dentry_operations tid_fd_dentry_operations =
{ {
.d_revalidate = tid_fd_revalidate, .d_revalidate = tid_fd_revalidate,
.d_delete = pid_delete_dentry, .d_delete = pid_delete_dentry,
}; };
static struct dentry_operations pid_dentry_operations =
{
.d_revalidate = pid_revalidate,
.d_delete = pid_delete_dentry,
};
/* Lookups */
static unsigned name_to_int(struct dentry *dentry)
{
const char *name = dentry->d_name.name;
int len = dentry->d_name.len;
unsigned n = 0;
if (len > 1 && *name == '0')
goto out;
while (len-- > 0) {
unsigned c = *name++ - '0';
if (c > 9)
goto out;
if (n >= (~0U-9)/10)
goto out;
n *= 10;
n += c;
}
return n;
out:
return ~0U;
}
/* SMP-safe */ /* SMP-safe */
static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd) static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
{ {
...@@ -1534,109 +1287,91 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, ...@@ -1534,109 +1287,91 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
goto out; goto out;
} }
static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir); static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd); {
static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
static struct file_operations proc_fd_operations = { struct task_struct *p = get_proc_task(inode);
.read = generic_read_dir, unsigned int fd, tid, ino;
.readdir = proc_readfd, int retval;
}; char buf[PROC_NUMBUF];
struct files_struct * files;
struct fdtable *fdt;
static struct file_operations proc_task_operations = { retval = -ENOENT;
.read = generic_read_dir, if (!p)
.readdir = proc_task_readdir,
};
/*
* proc directories can do almost nothing..
*/
static struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
.setattr = proc_setattr,
};
static struct inode_operations proc_task_inode_operations = {
.lookup = proc_task_lookup,
.getattr = proc_task_getattr,
.setattr = proc_setattr,
};
#ifdef CONFIG_SECURITY
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;
ssize_t length;
struct task_struct *task = get_proc_task(inode);
length = -ESRCH;
if (!task)
goto out_no_task; goto out_no_task;
retval = 0;
tid = p->pid;
if (count > PAGE_SIZE) fd = filp->f_pos;
count = PAGE_SIZE; switch (fd) {
length = -ENOMEM; case 0:
if (!(page = __get_free_page(GFP_KERNEL))) if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0)
goto out; goto out;
filp->f_pos++;
length = security_getprocattr(task, case 1:
(char*)file->f_dentry->d_name.name, ino = parent_ino(dentry);
(void*)page, count); if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0)
if (length >= 0)
length = simple_read_from_buffer(buf, count, ppos, (char *)page, length);
free_page(page);
out:
put_task_struct(task);
out_no_task:
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;
char *page;
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;
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out; goto out;
filp->f_pos++;
length = -ENOMEM; default:
page = (char*)__get_free_page(GFP_USER); files = get_files_struct(p);
if (!page) if (!files)
goto out; goto out;
rcu_read_lock();
fdt = files_fdtable(files);
for (fd = filp->f_pos-2;
fd < fdt->max_fds;
fd++, filp->f_pos++) {
unsigned int i,j;
length = -EFAULT; if (!fcheck_files(files, fd))
if (copy_from_user(page, buf, count)) continue;
goto out_free; rcu_read_unlock();
length = security_setprocattr(task, j = PROC_NUMBUF;
(char*)file->f_dentry->d_name.name, i = fd;
(void*)page, count); do {
out_free: j--;
free_page((unsigned long) page); 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) {
rcu_read_lock();
break;
}
rcu_read_lock();
}
rcu_read_unlock();
put_files_struct(files);
}
out: out:
put_task_struct(task); put_task_struct(p);
out_no_task: out_no_task:
return length; return retval;
} }
static struct file_operations proc_pid_attr_operations = { static struct file_operations proc_fd_operations = {
.read = proc_pid_attr_read, .read = generic_read_dir,
.write = proc_pid_attr_write, .readdir = proc_readfd,
}; };
/*
* proc directories can do almost nothing..
*/
static struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
.setattr = proc_setattr,
};
static struct file_operations proc_task_operations;
static struct inode_operations proc_task_inode_operations;
#ifdef CONFIG_SECURITY
static struct file_operations proc_pid_attr_operations;
static struct file_operations proc_tid_attr_operations; static struct file_operations proc_tid_attr_operations;
static struct inode_operations proc_tid_attr_inode_operations; static struct inode_operations proc_tid_attr_inode_operations;
static struct file_operations proc_tgid_attr_operations; static struct file_operations proc_tgid_attr_operations;
...@@ -1852,108 +1587,298 @@ static struct dentry *proc_pident_lookup(struct inode *dir, ...@@ -1852,108 +1587,298 @@ static struct dentry *proc_pident_lookup(struct inode *dir,
return error; return error;
} }
static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ static int proc_pident_readdir(struct file *filp,
return proc_pident_lookup(dir, dentry, tgid_base_stuff); void *dirent, filldir_t filldir,
struct pid_entry *ents, unsigned int nents)
{
int i;
int pid;
struct dentry *dentry = filp->f_dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
struct pid_entry *p;
ino_t ino;
int ret;
ret = -ENOENT;
if (!task)
goto out;
ret = 0;
pid = task->pid;
put_task_struct(task);
i = filp->f_pos;
switch (i) {
case 0:
ino = inode->i_ino;
if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
goto out;
i++;
filp->f_pos++;
/* fall through */
case 1:
ino = parent_ino(dentry);
if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
goto out;
i++;
filp->f_pos++;
/* fall through */
default:
i -= 2;
if (i >= nents) {
ret = 1;
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)
goto out;
filp->f_pos++;
p++;
}
}
ret = 1;
out:
return ret;
} }
static struct dentry *proc_tid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ #ifdef CONFIG_SECURITY
return proc_pident_lookup(dir, dentry, tid_base_stuff); 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;
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;
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:
put_task_struct(task);
out_no_task:
return length;
} }
static struct file_operations proc_tgid_base_operations = { 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;
char *page;
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;
/* No partial writes. */
length = -EINVAL;
if (*ppos != 0)
goto out;
length = -ENOMEM;
page = (char*)__get_free_page(GFP_USER);
if (!page)
goto out;
length = -EFAULT;
if (copy_from_user(page, buf, count))
goto out_free;
length = security_setprocattr(task,
(char*)file->f_dentry->d_name.name,
(void*)page, count);
out_free:
free_page((unsigned long) page);
out:
put_task_struct(task);
out_no_task:
return length;
}
static struct file_operations proc_pid_attr_operations = {
.read = proc_pid_attr_read,
.write = proc_pid_attr_write,
};
static struct pid_entry tgid_attr_stuff[] = {
E(PROC_TGID_ATTR_CURRENT, "current", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_PREV, "prev", S_IFREG|S_IRUGO),
E(PROC_TGID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TGID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO),
{0,0,NULL,0}
};
static struct pid_entry tid_attr_stuff[] = {
E(PROC_TID_ATTR_CURRENT, "current", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_PREV, "prev", S_IFREG|S_IRUGO),
E(PROC_TID_ATTR_EXEC, "exec", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_FSCREATE, "fscreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_KEYCREATE, "keycreate", S_IFREG|S_IRUGO|S_IWUGO),
E(PROC_TID_ATTR_SOCKCREATE, "sockcreate", S_IFREG|S_IRUGO|S_IWUGO),
{0,0,NULL,0}
};
static int proc_tgid_attr_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
tgid_attr_stuff,ARRAY_SIZE(tgid_attr_stuff));
}
static int proc_tid_attr_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
tid_attr_stuff,ARRAY_SIZE(tid_attr_stuff));
}
static struct file_operations proc_tgid_attr_operations = {
.read = generic_read_dir, .read = generic_read_dir,
.readdir = proc_tgid_base_readdir, .readdir = proc_tgid_attr_readdir,
}; };
static struct file_operations proc_tid_base_operations = { static struct file_operations proc_tid_attr_operations = {
.read = generic_read_dir, .read = generic_read_dir,
.readdir = proc_tid_base_readdir, .readdir = proc_tid_attr_readdir,
}; };
static struct inode_operations proc_tgid_base_inode_operations = { static struct dentry *proc_tgid_attr_lookup(struct inode *dir,
.lookup = proc_tgid_base_lookup, struct dentry *dentry, struct nameidata *nd)
{
return proc_pident_lookup(dir, dentry, tgid_attr_stuff);
}
static struct dentry *proc_tid_attr_lookup(struct inode *dir,
struct dentry *dentry, struct nameidata *nd)
{
return proc_pident_lookup(dir, dentry, tid_attr_stuff);
}
static struct inode_operations proc_tgid_attr_inode_operations = {
.lookup = proc_tgid_attr_lookup,
.getattr = pid_getattr, .getattr = pid_getattr,
.setattr = proc_setattr, .setattr = proc_setattr,
}; };
static struct inode_operations proc_tid_base_inode_operations = { static struct inode_operations proc_tid_attr_inode_operations = {
.lookup = proc_tid_base_lookup, .lookup = proc_tid_attr_lookup,
.getattr = pid_getattr, .getattr = pid_getattr,
.setattr = proc_setattr, .setattr = proc_setattr,
}; };
#endif
#ifdef CONFIG_SECURITY /*
static int proc_tgid_attr_readdir(struct file * filp, * /proc/self:
void * dirent, filldir_t filldir) */
static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
int buflen)
{ {
return proc_pident_readdir(filp,dirent,filldir, char tmp[PROC_NUMBUF];
tgid_attr_stuff,ARRAY_SIZE(tgid_attr_stuff)); sprintf(tmp, "%d", current->tgid);
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);
return ERR_PTR(vfs_follow_link(nd,tmp));
} }
static int proc_tid_attr_readdir(struct file * filp, static struct inode_operations proc_self_inode_operations = {
.readlink = proc_self_readlink,
.follow_link = proc_self_follow_link,
};
/*
* Thread groups
*/
static struct pid_entry tgid_base_stuff[] = {
E(PROC_TGID_TASK, "task", S_IFDIR|S_IRUGO|S_IXUGO),
E(PROC_TGID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR),
E(PROC_TGID_ENVIRON, "environ", S_IFREG|S_IRUSR),
E(PROC_TGID_AUXV, "auxv", S_IFREG|S_IRUSR),
E(PROC_TGID_STATUS, "status", S_IFREG|S_IRUGO),
E(PROC_TGID_CMDLINE, "cmdline", S_IFREG|S_IRUGO),
E(PROC_TGID_STAT, "stat", S_IFREG|S_IRUGO),
E(PROC_TGID_STATM, "statm", S_IFREG|S_IRUGO),
E(PROC_TGID_MAPS, "maps", S_IFREG|S_IRUGO),
#ifdef CONFIG_NUMA
E(PROC_TGID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO),
#endif
E(PROC_TGID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR),
#ifdef CONFIG_SECCOMP
E(PROC_TGID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR),
#endif
E(PROC_TGID_CWD, "cwd", S_IFLNK|S_IRWXUGO),
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),
E(PROC_TGID_MOUNTSTATS, "mountstats", S_IFREG|S_IRUSR),
#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
#ifdef CONFIG_KALLSYMS
E(PROC_TGID_WCHAN, "wchan", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_SCHEDSTATS
E(PROC_TGID_SCHEDSTAT, "schedstat", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_CPUSETS
E(PROC_TGID_CPUSET, "cpuset", S_IFREG|S_IRUGO),
#endif
E(PROC_TGID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TGID_OOM_ADJUST,"oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TGID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
static int proc_tgid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir) void * dirent, filldir_t filldir)
{ {
return proc_pident_readdir(filp,dirent,filldir, return proc_pident_readdir(filp,dirent,filldir,
tid_attr_stuff,ARRAY_SIZE(tid_attr_stuff)); tgid_base_stuff,ARRAY_SIZE(tgid_base_stuff));
} }
static struct file_operations proc_tgid_attr_operations = { static struct file_operations proc_tgid_base_operations = {
.read = generic_read_dir,
.readdir = proc_tgid_attr_readdir,
};
static struct file_operations proc_tid_attr_operations = {
.read = generic_read_dir, .read = generic_read_dir,
.readdir = proc_tid_attr_readdir, .readdir = proc_tgid_base_readdir,
}; };
static struct dentry *proc_tgid_attr_lookup(struct inode *dir, static struct dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){
struct dentry *dentry, struct nameidata *nd) return proc_pident_lookup(dir, dentry, tgid_base_stuff);
{
return proc_pident_lookup(dir, dentry, tgid_attr_stuff);
}
static struct dentry *proc_tid_attr_lookup(struct inode *dir,
struct dentry *dentry, struct nameidata *nd)
{
return proc_pident_lookup(dir, dentry, tid_attr_stuff);
} }
static struct inode_operations proc_tgid_attr_inode_operations = { static struct inode_operations proc_tgid_base_inode_operations = {
.lookup = proc_tgid_attr_lookup, .lookup = proc_tgid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
};
static struct inode_operations proc_tid_attr_inode_operations = {
.lookup = proc_tid_attr_lookup,
.getattr = pid_getattr, .getattr = pid_getattr,
.setattr = proc_setattr, .setattr = proc_setattr,
}; };
#endif
/*
* /proc/self:
*/
static int proc_self_readlink(struct dentry *dentry, char __user *buffer,
int buflen)
{
char tmp[PROC_NUMBUF];
sprintf(tmp, "%d", current->tgid);
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);
return ERR_PTR(vfs_follow_link(nd,tmp));
}
static struct inode_operations proc_self_inode_operations = {
.readlink = proc_self_readlink,
.follow_link = proc_self_follow_link,
};
/** /**
* proc_flush_task - Remove dcache entries for @task from the /proc dcache. * proc_flush_task - Remove dcache entries for @task from the /proc dcache.
...@@ -2085,62 +2010,6 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct ...@@ -2085,62 +2010,6 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct
return result; return result;
} }
/* SMP-safe */
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task;
struct task_struct *leader = get_proc_task(dir);
struct inode *inode;
unsigned tid;
if (!leader)
goto out_no_task;
tid = name_to_int(dentry);
if (tid == ~0U)
goto out;
rcu_read_lock();
task = find_task_by_pid(tid);
if (task)
get_task_struct(task);
rcu_read_unlock();
if (!task)
goto out;
if (leader->tgid != task->tgid)
goto out_drop_task;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_INO);
if (!inode)
goto out_drop_task;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations;
inode->i_flags|=S_IMMUTABLE;
#ifdef CONFIG_SECURITY
inode->i_nlink = 4;
#else
inode->i_nlink = 3;
#endif
dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode);
/* Close the race of the process dying before we return the dentry */
if (pid_revalidate(dentry, NULL))
result = NULL;
out_drop_task:
put_task_struct(task);
out:
put_task_struct(leader);
out_no_task:
return result;
}
/* /*
* Find the first task with tgid >= tgid * Find the first task with tgid >= tgid
* *
...@@ -2215,6 +2084,130 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) ...@@ -2215,6 +2084,130 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
return 0; return 0;
} }
/*
* Tasks
*/
static struct pid_entry tid_base_stuff[] = {
E(PROC_TID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR),
E(PROC_TID_ENVIRON, "environ", S_IFREG|S_IRUSR),
E(PROC_TID_AUXV, "auxv", S_IFREG|S_IRUSR),
E(PROC_TID_STATUS, "status", S_IFREG|S_IRUGO),
E(PROC_TID_CMDLINE, "cmdline", S_IFREG|S_IRUGO),
E(PROC_TID_STAT, "stat", S_IFREG|S_IRUGO),
E(PROC_TID_STATM, "statm", S_IFREG|S_IRUGO),
E(PROC_TID_MAPS, "maps", S_IFREG|S_IRUGO),
#ifdef CONFIG_NUMA
E(PROC_TID_NUMA_MAPS, "numa_maps", S_IFREG|S_IRUGO),
#endif
E(PROC_TID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR),
#ifdef CONFIG_SECCOMP
E(PROC_TID_SECCOMP, "seccomp", S_IFREG|S_IRUSR|S_IWUSR),
#endif
E(PROC_TID_CWD, "cwd", S_IFLNK|S_IRWXUGO),
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
#ifdef CONFIG_KALLSYMS
E(PROC_TID_WCHAN, "wchan", S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_SCHEDSTATS
E(PROC_TID_SCHEDSTAT, "schedstat",S_IFREG|S_IRUGO),
#endif
#ifdef CONFIG_CPUSETS
E(PROC_TID_CPUSET, "cpuset", S_IFREG|S_IRUGO),
#endif
E(PROC_TID_OOM_SCORE, "oom_score",S_IFREG|S_IRUGO),
E(PROC_TID_OOM_ADJUST, "oom_adj", S_IFREG|S_IRUGO|S_IWUSR),
#ifdef CONFIG_AUDITSYSCALL
E(PROC_TID_LOGINUID, "loginuid", S_IFREG|S_IWUSR|S_IRUGO),
#endif
{0,0,NULL,0}
};
static int proc_tid_base_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
return proc_pident_readdir(filp,dirent,filldir,
tid_base_stuff,ARRAY_SIZE(tid_base_stuff));
}
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);
}
static 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 = {
.lookup = proc_tid_base_lookup,
.getattr = pid_getattr,
.setattr = proc_setattr,
};
/* SMP-safe */
static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
{
struct dentry *result = ERR_PTR(-ENOENT);
struct task_struct *task;
struct task_struct *leader = get_proc_task(dir);
struct inode *inode;
unsigned tid;
if (!leader)
goto out_no_task;
tid = name_to_int(dentry);
if (tid == ~0U)
goto out;
rcu_read_lock();
task = find_task_by_pid(tid);
if (task)
get_task_struct(task);
rcu_read_unlock();
if (!task)
goto out;
if (leader->tgid != task->tgid)
goto out_drop_task;
inode = proc_pid_make_inode(dir->i_sb, task, PROC_TID_INO);
if (!inode)
goto out_drop_task;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations;
inode->i_flags|=S_IMMUTABLE;
#ifdef CONFIG_SECURITY
inode->i_nlink = 4;
#else
inode->i_nlink = 3;
#endif
dentry->d_op = &pid_dentry_operations;
d_add(dentry, inode);
/* Close the race of the process dying before we return the dentry */
if (pid_revalidate(dentry, NULL))
result = NULL;
out_drop_task:
put_task_struct(task);
out:
put_task_struct(leader);
out_no_task:
return result;
}
/* /*
* Find the first tid of a thread group to return to user space. * Find the first tid of a thread group to return to user space.
* *
...@@ -2358,3 +2351,14 @@ static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct ...@@ -2358,3 +2351,14 @@ static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct
return 0; return 0;
} }
static 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 = {
.read = generic_read_dir,
.readdir = proc_task_readdir,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment