diff options
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r-- | fs/proc/base.c | 1759 |
1 files changed, 837 insertions, 922 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 89c20d9d50b..82da55b5cff 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -71,6 +71,7 @@ #include <linux/cpuset.h> #include <linux/audit.h> #include <linux/poll.h> +#include <linux/nsproxy.h> #include "internal.h" /* NOTE: @@ -83,262 +84,44 @@ * 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 struct pid_entry { - int type; int len; char *name; mode_t mode; + struct inode_operations *iop; + struct file_operations *fop; + union proc_op op; }; -#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; +#define NOD(NAME, MODE, IOP, FOP, OP) { \ + .len = sizeof(NAME) - 1, \ + .name = (NAME), \ + .mode = MODE, \ + .iop = IOP, \ + .fop = FOP, \ + .op = OP, \ } +#define DIR(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFDIR|(MODE)), \ + &proc_##OTYPE##_inode_operations, &proc_##OTYPE##_operations, \ + {} ) +#define LNK(NAME, OTYPE) \ + NOD(NAME, (S_IFLNK|S_IRWXUGO), \ + &proc_pid_link_inode_operations, NULL, \ + { .proc_get_link = &proc_##OTYPE##_link } ) +#define REG(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFREG|(MODE)), NULL, \ + &proc_##OTYPE##_operations, {}) +#define INF(NAME, MODE, OTYPE) \ + NOD(NAME, (S_IFREG|(MODE)), \ + NULL, &proc_info_file_operations, \ + { .proc_read = &proc_##OTYPE } ) + static struct fs_struct *get_fs_struct(struct task_struct *task) { struct fs_struct *fs; @@ -587,7 +370,7 @@ static int mounts_open(struct inode *inode, struct file *file) if (task) { task_lock(task); - namespace = task->namespace; + namespace = task->nsproxy->namespace; if (namespace) get_namespace(namespace); task_unlock(task); @@ -658,7 +441,7 @@ static int mountstats_open(struct inode *inode, struct file *file) if (task) { task_lock(task); - namespace = task->namespace; + namespace = task->nsproxy->namespace; if (namespace) get_namespace(namespace); task_unlock(task); @@ -1137,143 +920,6 @@ static struct inode_operations proc_pid_link_inode_operations = { .setattr = proc_setattr, }; -static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) -{ - struct dentry *dentry = filp->f_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; - - retval = -ENOENT; - if (!p) - goto out_no_task; - retval = 0; - tid = p->pid; - - fd = filp->f_pos; - switch (fd) { - case 0: - if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) - goto out; - filp->f_pos++; - case 1: - ino = parent_ino(dentry); - if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) - goto out; - filp->f_pos++; - default: - files = get_files_struct(p); - if (!files) - 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)) - 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) { - rcu_read_lock(); - break; - } - rcu_read_lock(); - } - rcu_read_unlock(); - put_files_struct(files); - } -out: - put_task_struct(p); -out_no_task: - return retval; -} - -static int proc_pident_readdir(struct file *filp, - 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 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 */ @@ -1293,13 +939,13 @@ static int task_dumpable(struct task_struct *task) } -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; /* We need a new inode */ - + inode = new_inode(sb); if (!inode) goto out; @@ -1307,13 +953,12 @@ static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_st /* 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; @@ -1333,6 +978,27 @@ out_unlock: return NULL; } +static int pid_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) +{ + struct inode *inode = dentry->d_inode; + struct task_struct *task; + generic_fillattr(inode, stat); + + rcu_read_lock(); + stat->uid = 0; + stat->gid = 0; + task = pid_task(proc_pid(inode), PIDTYPE_PID); + if (task) { + if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || + task_dumpable(task)) { + stat->uid = task->euid; + stat->gid = task->egid; + } + } + rcu_read_unlock(); + return 0; +} + /* dentry stuff */ /* @@ -1372,25 +1038,130 @@ static int pid_revalidate(struct dentry *dentry, struct nameidata *nd) 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; - struct task_struct *task; - generic_fillattr(inode, stat); + /* 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 pid_dentry_operations = +{ + .d_revalidate = pid_revalidate, + .d_delete = pid_delete_dentry, +}; + +/* Lookups */ + +typedef struct dentry *instantiate_t(struct inode *, struct dentry *, struct task_struct *, 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, void *ptr) +{ + struct dentry *child, *dir = filp->f_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; + 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); - rcu_read_lock(); - stat->uid = 0; - stat->gid = 0; - task = pid_task(proc_pid(inode), PIDTYPE_PID); if (task) { - if ((inode->i_mode == (S_IFDIR|S_IRUGO|S_IXUGO)) || - task_dumpable(task)) { - stat->uid = task->euid; - stat->gid = task->egid; + 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); } - rcu_read_unlock(); - return 0; + return -ENOENT; } static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) @@ -1428,75 +1199,30 @@ static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd) 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 = { .d_revalidate = tid_fd_revalidate, .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 */ -static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd) +static struct dentry *proc_fd_instantiate(struct inode *dir, + struct dentry *dentry, struct task_struct *task, void *ptr) { - struct task_struct *task = get_proc_task(dir); - unsigned fd = name_to_int(dentry); - struct dentry *result = ERR_PTR(-ENOENT); - struct file * file; - struct files_struct * files; - struct inode *inode; - struct proc_inode *ei; - - if (!task) - goto out_no_task; - if (fd == ~0U) - goto out; + unsigned fd = *(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); ei->fd = fd; files = get_files_struct(task); if (!files) - goto out_unlock; + goto out_iput; inode->i_mode = S_IFLNK; /* @@ -1506,13 +1232,14 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, spin_lock(&files->file_lock); file = fcheck_files(files, fd); if (!file) - goto out_unlock2; + goto out_unlock; if (file->f_mode & 1) inode->i_mode |= S_IRUSR | S_IXUSR; if (file->f_mode & 2) inode->i_mode |= S_IWUSR | S_IXUSR; spin_unlock(&files->file_lock); put_files_struct(files); + inode->i_op = &proc_pid_link_inode_operations; inode->i_size = 64; ei->op.proc_get_link = proc_fd_link; @@ -1520,34 +1247,106 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (tid_fd_revalidate(dentry, NULL)) - result = NULL; -out: - put_task_struct(task); -out_no_task: - return result; + error = NULL; -out_unlock2: + out: + return error; +out_unlock: spin_unlock(&files->file_lock); put_files_struct(files); -out_unlock: +out_iput: iput(inode); goto out; } -static int proc_task_readdir(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); +static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd) +{ + struct task_struct *task = get_proc_task(dir); + unsigned fd = name_to_int(dentry); + struct dentry *result = ERR_PTR(-ENOENT); + + if (!task) + goto out_no_task; + if (fd == ~0U) + goto out; + + result = proc_fd_instantiate(dir, dentry, task, &fd); +out: + put_task_struct(task); +out_no_task: + return result; +} + +static int proc_fd_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct task_struct *task, int fd) +{ + char name[PROC_NUMBUF]; + int len = snprintf(name, sizeof(name), "%d", fd); + return proc_fill_cache(filp, dirent, filldir, name, len, + proc_fd_instantiate, task, &fd); +} + +static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) +{ + struct dentry *dentry = filp->f_dentry; + struct inode *inode = dentry->d_inode; + struct task_struct *p = get_proc_task(inode); + unsigned int fd, tid, ino; + int retval; + struct files_struct * files; + struct fdtable *fdt; + + retval = -ENOENT; + if (!p) + goto out_no_task; + retval = 0; + tid = p->pid; + + fd = filp->f_pos; + switch (fd) { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) + goto out; + filp->f_pos++; + case 1: + ino = parent_ino(dentry); + if (filldir(dirent, "..", 2, 1, ino, DT_DIR) < 0) + goto out; + filp->f_pos++; + default: + files = get_files_struct(p); + if (!files) + goto out; + rcu_read_lock(); + fdt = files_fdtable(files); + for (fd = filp->f_pos-2; + fd < fdt->max_fds; + fd++, filp->f_pos++) { + + if (!fcheck_files(files, fd)) + continue; + rcu_read_unlock(); + + if (proc_fd_fill_cache(filp, dirent, filldir, p, fd) < 0) { + rcu_read_lock(); + break; + } + rcu_read_lock(); + } + rcu_read_unlock(); + put_files_struct(files); + } +out: + put_task_struct(p); +out_no_task: + return retval; +} static struct file_operations proc_fd_operations = { .read = generic_read_dir, .readdir = proc_readfd, }; -static struct file_operations proc_task_operations = { - .read = generic_read_dir, - .readdir = proc_task_readdir, -}; - /* * proc directories can do almost nothing.. */ @@ -1556,11 +1355,137 @@ static struct inode_operations proc_fd_inode_operations = { .setattr = proc_setattr, }; -static struct inode_operations proc_task_inode_operations = { - .lookup = proc_task_lookup, - .getattr = proc_task_getattr, - .setattr = proc_setattr, -}; +static struct dentry *proc_pident_instantiate(struct inode *dir, + struct dentry *dentry, struct task_struct *task, void *ptr) +{ + 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); + if (!inode) + goto out; + + ei = PROC_I(inode); + inode->i_mode = p->mode; + if (S_ISDIR(inode->i_mode)) + inode->i_nlink = 2; /* Use getattr to fix if necessary */ + if (p->iop) + inode->i_op = p->iop; + if (p->fop) + inode->i_fop = p->fop; + ei->op = p->op; + 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)) + error = NULL; +out: + return error; +} + +static struct dentry *proc_pident_lookup(struct inode *dir, + struct dentry *dentry, + 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, *last; + + error = ERR_PTR(-ENOENT); + inode = NULL; + + if (!task) + goto out_no_task; + + /* + * Yes, it does not scale. And it should not. Don't add + * new entries into /proc/<tgid>/ without very good reasons. + */ + 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 > last) + goto out; + + error = proc_pident_instantiate(dir, dentry, task, p); +out: + put_task_struct(task); +out_no_task: + return error; +} + +static int proc_pident_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct task_struct *task, 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) +{ + 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, *last; + ino_t ino; + int ret; + + ret = -ENOENT; + if (!task) + goto out_no_task; + + ret = 0; + pid = task->pid; + 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; + 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; +} #ifdef CONFIG_SECURITY static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, @@ -1581,8 +1506,8 @@ static ssize_t proc_pid_attr_read(struct file * file, char __user * buf, if (!(page = __get_free_page(GFP_KERNEL))) goto out; - length = security_getprocattr(task, - (char*)file->f_dentry->d_name.name, + 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); @@ -1595,17 +1520,17 @@ out_no_task: 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; + 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; + if (count > PAGE_SIZE) + count = PAGE_SIZE; /* No partial writes. */ length = -EINVAL; @@ -1613,16 +1538,16 @@ 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); - if (!page) + page = (char*)__get_free_page(GFP_USER); + if (!page) goto out; - length = -EFAULT; - if (copy_from_user(page, buf, count)) + length = -EFAULT; + if (copy_from_user(page, buf, count)) goto out_free; - length = security_setprocattr(task, - (char*)file->f_dentry->d_name.name, + length = security_setprocattr(task, + (char*)file->f_dentry->d_name.name, (void*)page, count); out_free: free_page((unsigned long) page); @@ -1630,330 +1555,263 @@ 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 file_operations proc_tid_attr_operations; -static struct inode_operations proc_tid_attr_inode_operations; -static struct file_operations proc_tgid_attr_operations; -static struct inode_operations proc_tgid_attr_inode_operations; +static 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_attr_dir_readdir(struct file * filp, + void * dirent, filldir_t filldir) +{ + return proc_pident_readdir(filp,dirent,filldir, + attr_dir_stuff,ARRAY_SIZE(attr_dir_stuff)); +} + +static 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_lookup(dir, dentry, + attr_dir_stuff, ARRAY_SIZE(attr_dir_stuff)); +} + +static struct inode_operations proc_attr_dir_inode_operations = { + .lookup = proc_attr_dir_lookup, + .getattr = pid_getattr, + .setattr = proc_setattr, +}; + #endif -/* SMP-safe */ -static struct dentry *proc_pident_lookup(struct inode *dir, - struct dentry *dentry, - struct pid_entry *ents) +/* + * /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 base + * + * These are the directory entries in the root directory of /proc + * that properly belong to the /proc filesystem, as they describe + * describe something that is process related. + */ +static struct pid_entry proc_base_stuff[] = { + NOD("self", S_IFLNK|S_IRWXUGO, + &proc_self_inode_operations, NULL, {}), +}; + +/* + * Exceptional case: normally we are not allowed to unhash a busy + * directory. In this case, however, we can do it - no aliasing problems + * due to the way we treat inodes. + */ +static int proc_base_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode = dentry->d_inode; + struct task_struct *task = get_proc_task(inode); + if (task) { + put_task_struct(task); + return 1; + } + d_drop(dentry); + return 0; +} + +static struct dentry_operations proc_base_dentry_operations = +{ + .d_revalidate = proc_base_revalidate, + .d_delete = pid_delete_dentry, +}; + +static struct dentry *proc_base_instantiate(struct inode *dir, + struct dentry *dentry, struct task_struct *task, void *ptr) +{ + struct pid_entry *p = ptr; struct inode *inode; + struct proc_inode *ei; + struct dentry *error = ERR_PTR(-EINVAL); + + /* Allocate the inode */ + error = ERR_PTR(-ENOMEM); + inode = new_inode(dir->i_sb); + if (!inode) + goto out; + + /* Initialize the inode */ + ei = PROC_I(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + + /* + * grab the reference to the task. + */ + ei->pid = get_task_pid(task, PIDTYPE_PID); + if (!ei->pid) + goto out_iput; + + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_mode = p->mode; + if (S_ISDIR(inode->i_mode)) + inode->i_nlink = 2; + if (S_ISLNK(inode->i_mode)) + inode->i_size = 64; + if (p->iop) + inode->i_op = p->iop; + if (p->fop) + inode->i_fop = p->fop; + ei->op = p->op; + dentry->d_op = &proc_base_dentry_operations; + d_add(dentry, inode); + error = NULL; +out: + return error; +out_iput: + iput(inode); + goto out; +} + +static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) +{ struct dentry *error; struct task_struct *task = get_proc_task(dir); - struct pid_entry *p; - struct proc_inode *ei; + struct pid_entry *p, *last; error = ERR_PTR(-ENOENT); - inode = NULL; if (!task) goto out_no_task; - for (p = ents; p->name; p++) { + /* Lookup the directory entry */ + 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 = ERR_PTR(-EINVAL); - inode = proc_pid_make_inode(dir->i_sb, task, p->type); - if (!inode) - goto out; + error = proc_base_instantiate(dir, dentry, task, p); - ei = PROC_I(inode); - inode->i_mode = p->mode; - /* - * Yes, it does not scale. And it should not. Don't add - * new entries into /proc/<tgid>/ without very good reasons. - */ - switch(p->type) { - case PROC_TGID_TASK: - inode->i_nlink = 2; - inode->i_op = &proc_task_inode_operations; - inode->i_fop = &proc_task_operations; - break; - case PROC_TID_FD: - case PROC_TGID_FD: - inode->i_nlink = 2; - inode->i_op = &proc_fd_inode_operations; - inode->i_fop = &proc_fd_operations; - break; - case PROC_TID_EXE: - case PROC_TGID_EXE: - inode->i_op = &proc_pid_link_inode_operations; - ei->op.proc_get_link = proc_exe_link; - break; - case PROC_TID_CWD: - case PROC_TGID_CWD: - inode->i_op = &proc_pid_link_inode_operations; - ei->op.proc_get_link = proc_cwd_link; - break; - case PROC_TID_ROOT: - case PROC_TGID_ROOT: - inode->i_op = &proc_pid_link_inode_operations; - ei->op.proc_get_link = proc_root_link; - break; - case PROC_TID_ENVIRON: - case PROC_TGID_ENVIRON: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_environ; - break; - case PROC_TID_AUXV: - case PROC_TGID_AUXV: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_auxv; - break; - case PROC_TID_STATUS: - case PROC_TGID_STATUS: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_status; - break; - case PROC_TID_STAT: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_tid_stat; - break; - case PROC_TGID_STAT: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_tgid_stat; - break; - case PROC_TID_CMDLINE: - case PROC_TGID_CMDLINE: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_cmdline; - break; - case PROC_TID_STATM: - case PROC_TGID_STATM: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_statm; - break; - case PROC_TID_MAPS: - case PROC_TGID_MAPS: - inode->i_fop = &proc_maps_operations; - break; +out: + put_task_struct(task); +out_no_task: + return error; +} + +static int proc_base_fill_cache(struct file *filp, void *dirent, filldir_t filldir, + struct task_struct *task, struct pid_entry *p) +{ + return proc_fill_cache(filp, dirent, filldir, p->name, p->len, + proc_base_instantiate, task, p); +} + +/* + * 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("task", S_IRUGO|S_IXUGO, task), + DIR("fd", S_IRUSR|S_IXUSR, fd), + INF("environ", S_IRUSR, pid_environ), + INF("auxv", S_IRUSR, pid_auxv), + INF("status", S_IRUGO, pid_status), + 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 - case PROC_TID_NUMA_MAPS: - case PROC_TGID_NUMA_MAPS: - inode->i_fop = &proc_numa_maps_operations; - break; + REG("numa_maps", S_IRUGO, numa_maps), #endif - case PROC_TID_MEM: - case PROC_TGID_MEM: - inode->i_fop = &proc_mem_operations; - break; + REG("mem", S_IRUSR|S_IWUSR, mem), #ifdef CONFIG_SECCOMP - case PROC_TID_SECCOMP: - case PROC_TGID_SECCOMP: - inode->i_fop = &proc_seccomp_operations; - break; -#endif /* CONFIG_SECCOMP */ - case PROC_TID_MOUNTS: - case PROC_TGID_MOUNTS: - inode->i_fop = &proc_mounts_operations; - break; + REG("seccomp", S_IRUSR|S_IWUSR, seccomp), +#endif + LNK("cwd", cwd), + LNK("root", root), + LNK("exe", exe), + REG("mounts", S_IRUGO, mounts), + REG("mountstats", S_IRUSR, mountstats), #ifdef CONFIG_MMU - case PROC_TID_SMAPS: - case PROC_TGID_SMAPS: - inode->i_fop = &proc_smaps_operations; - break; + REG("smaps", S_IRUGO, smaps), #endif - case PROC_TID_MOUNTSTATS: - case PROC_TGID_MOUNTSTATS: - inode->i_fop = &proc_mountstats_operations; - break; #ifdef CONFIG_SECURITY - case PROC_TID_ATTR: - inode->i_nlink = 2; - inode->i_op = &proc_tid_attr_inode_operations; - inode->i_fop = &proc_tid_attr_operations; - break; - case PROC_TGID_ATTR: - inode->i_nlink = 2; - inode->i_op = &proc_tgid_attr_inode_operations; - inode->i_fop = &proc_tgid_attr_operations; - break; - case PROC_TID_ATTR_CURRENT: - case PROC_TGID_ATTR_CURRENT: - case PROC_TID_ATTR_PREV: - case PROC_TGID_ATTR_PREV: - case PROC_TID_ATTR_EXEC: - case PROC_TGID_ATTR_EXEC: - case PROC_TID_ATTR_FSCREATE: - case PROC_TGID_ATTR_FSCREATE: - case PROC_TID_ATTR_KEYCREATE: - case PROC_TGID_ATTR_KEYCREATE: - case PROC_TID_ATTR_SOCKCREATE: - case PROC_TGID_ATTR_SOCKCREATE: - inode->i_fop = &proc_pid_attr_operations; - break; + DIR("attr", S_IRUGO|S_IXUGO, attr_dir), #endif #ifdef CONFIG_KALLSYMS - case PROC_TID_WCHAN: - case PROC_TGID_WCHAN: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_wchan; - break; + INF("wchan", S_IRUGO, pid_wchan), #endif #ifdef CONFIG_SCHEDSTATS - case PROC_TID_SCHEDSTAT: - case PROC_TGID_SCHEDSTAT: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_pid_schedstat; - break; + INF("schedstat", S_IRUGO, pid_schedstat), #endif #ifdef CONFIG_CPUSETS - case PROC_TID_CPUSET: - case PROC_TGID_CPUSET: - inode->i_fop = &proc_cpuset_operations; - break; + REG("cpuset", S_IRUGO, cpuset), #endif - case PROC_TID_OOM_SCORE: - case PROC_TGID_OOM_SCORE: - inode->i_fop = &proc_info_file_operations; - ei->op.proc_read = proc_oom_score; - break; - case PROC_TID_OOM_ADJUST: - case PROC_TGID_OOM_ADJUST: - inode->i_fop = &proc_oom_adjust_operations; - break; + INF("oom_score", S_IRUGO, oom_score), + REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), #ifdef CONFIG_AUDITSYSCALL - case PROC_TID_LOGINUID: - case PROC_TGID_LOGINUID: - inode->i_fop = &proc_loginuid_operations; - break; + REG("loginuid", S_IWUSR|S_IRUGO, loginuid), #endif - default: - printk("procfs: impossible type (%d)",p->type); - iput(inode); - error = ERR_PTR(-EINVAL); - goto out; - } - 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)) - error = NULL; -out: - put_task_struct(task); -out_no_task: - return error; -} - -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); -} - -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_tgid_base_operations = { - .read = generic_read_dir, - .readdir = proc_tgid_base_readdir, }; -static struct file_operations proc_tid_base_operations = { - .read = generic_read_dir, - .readdir = proc_tid_base_readdir, -}; - -static struct inode_operations proc_tgid_base_inode_operations = { - .lookup = proc_tgid_base_lookup, - .getattr = pid_getattr, - .setattr = proc_setattr, -}; - -static struct inode_operations proc_tid_base_inode_operations = { - .lookup = proc_tid_base_lookup, - .getattr = pid_getattr, - .setattr = proc_setattr, -}; - -#ifdef CONFIG_SECURITY -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, +static int proc_tgid_base_readdir(struct file * filp, void * dirent, filldir_t 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 = { - .read = generic_read_dir, - .readdir = proc_tgid_attr_readdir, -}; - -static struct file_operations proc_tid_attr_operations = { +static struct file_operations proc_tgid_base_operations = { .read = generic_read_dir, - .readdir = proc_tid_attr_readdir, + .readdir = proc_tgid_base_readdir, }; -static struct dentry *proc_tgid_attr_lookup(struct inode *dir, - 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 dentry *proc_tgid_base_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd){ + return proc_pident_lookup(dir, dentry, + tgid_base_stuff, ARRAY_SIZE(tgid_base_stuff)); } -static struct inode_operations proc_tgid_attr_inode_operations = { - .lookup = proc_tgid_attr_lookup, - .getattr = pid_getattr, - .setattr = proc_setattr, -}; - -static struct inode_operations proc_tid_attr_inode_operations = { - .lookup = proc_tid_attr_lookup, +static struct inode_operations proc_tgid_base_inode_operations = { + .lookup = proc_tgid_base_lookup, .getattr = pid_getattr, .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. @@ -2022,54 +1880,23 @@ out: return; } -/* SMP-safe */ -struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) +struct dentry *proc_pid_instantiate(struct inode *dir, + struct dentry * dentry, struct task_struct *task, void *ptr) { - struct dentry *result = ERR_PTR(-ENOENT); - struct task_struct *task; + struct dentry *error = ERR_PTR(-ENOENT); struct inode *inode; - struct proc_inode *ei; - unsigned tgid; - - if (dentry->d_name.len == 4 && !memcmp(dentry->d_name.name,"self",4)) { - inode = new_inode(dir->i_sb); - if (!inode) - return ERR_PTR(-ENOMEM); - ei = PROC_I(inode); - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_ino = fake_ino(0, PROC_TGID_INO); - ei->pde = NULL; - inode->i_mode = S_IFLNK|S_IRWXUGO; - inode->i_uid = inode->i_gid = 0; - inode->i_size = 64; - inode->i_op = &proc_self_inode_operations; - d_add(dentry, inode); - return NULL; - } - tgid = name_to_int(dentry); - if (tgid == ~0U) - goto out; - rcu_read_lock(); - task = find_task_by_pid(tgid); - if (task) - get_task_struct(task); - rcu_read_unlock(); - if (!task) - goto out; - - 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_put_task; + goto out; inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; inode->i_op = &proc_tgid_base_inode_operations; inode->i_fop = &proc_tgid_base_operations; inode->i_flags|=S_IMMUTABLE; -#ifdef CONFIG_SECURITY - inode->i_nlink = 5; -#else inode->i_nlink = 4; +#ifdef CONFIG_SECURITY + inode->i_nlink += 1; #endif dentry->d_op = &pid_dentry_operations; @@ -2077,179 +1904,251 @@ struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry, struct d_add(dentry, inode); /* Close the race of the process dying before we return the dentry */ if (pid_revalidate(dentry, NULL)) - result = NULL; - -out_put_task: - put_task_struct(task); + error = NULL; out: - return result; + return error; } -/* SMP-safe */ -static struct dentry *proc_task_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) +struct dentry *proc_pid_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; + unsigned tgid; - if (!leader) - goto out_no_task; + result = proc_base_lookup(dir, dentry); + if (!IS_ERR(result) || PTR_ERR(result) != -ENOENT) + goto out; - tid = name_to_int(dentry); - if (tid == ~0U) + tgid = name_to_int(dentry); + if (tgid == ~0U) goto out; rcu_read_lock(); - task = find_task_by_pid(tid); + task = find_task_by_pid(tgid); 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: + result = proc_pid_instantiate(dir, dentry, task, NULL); put_task_struct(task); out: - put_task_struct(leader); -out_no_task: return result; } /* - * Find the first tgid to return to user space. - * - * Usually this is just whatever follows &init_task, but if the users - * buffer was too small to hold the full list or there was a seek into - * the middle of the directory we have more work to do. - * - * In the case of a short read we start with find_task_by_pid. + * Find the first task with tgid >= tgid * - * In the case of a seek we start with &init_task and walk nr - * threads past it. */ -static struct task_struct *first_tgid(int tgid, unsigned int nr) +static struct task_struct *next_tgid(unsigned int tgid) { - struct task_struct *pos; - rcu_read_lock(); - if (tgid && nr) { - pos = find_task_by_pid(tgid); - if (pos && thread_group_leader(pos)) - goto found; - } - /* If nr exceeds the number of processes get out quickly */ - pos = NULL; - if (nr && nr >= nr_processes()) - goto done; + struct task_struct *task; + struct pid *pid; - /* If we haven't found our starting place yet start with - * the init_task and walk nr tasks forward. - */ - for (pos = next_task(&init_task); nr > 0; --nr) { - pos = next_task(pos); - if (pos == &init_task) { - pos = NULL; - goto done; - } + rcu_read_lock(); +retry: + task = NULL; + pid = find_ge_pid(tgid); + if (pid) { + tgid = pid->nr + 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 + * being a thread_group_leader is the obvious thing + * todo but there is a window when it fails, due to + * the pid transfer logic in de_thread. + * + * So we perform the straight forward test of seeing + * if the pid we have found is the pid of a thread + * group leader, and don't worry if the task we have + * found doesn't happen to be a thread group leader. + * As we don't care in the case of readdir. + */ + if (!task || !has_group_leader_pid(task)) + goto retry; + get_task_struct(task); } -found: - get_task_struct(pos); -done: rcu_read_unlock(); - return pos; + return task; } -/* - * Find the next task in the task list. - * Return NULL if we loop or there is any error. - * - * The reference to the input task_struct is released. - */ -static struct task_struct *next_tgid(struct task_struct *start) +#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) { - struct task_struct *pos; - rcu_read_lock(); - pos = start; - if (pid_alive(start)) - pos = next_task(start); - if (pid_alive(pos) && (pos != &init_task)) { - get_task_struct(pos); - goto done; - } - pos = NULL; -done: - rcu_read_unlock(); - put_task_struct(start); - return pos; + 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_dentry->d_inode); struct task_struct *task; int tgid; - if (!nr) { - ino_t ino = fake_ino(0,PROC_TGID_INO); - if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0) - return 0; - filp->f_pos++; - nr++; + if (!reaper) + goto out_no_task; + + for (; nr < ARRAY_SIZE(proc_base_stuff); filp->f_pos++, nr++) { + struct pid_entry *p = &proc_base_stuff[nr]; + if (proc_base_fill_cache(filp, dirent, filldir, reaper, p) < 0) + goto out; } - nr -= 1; - /* f_version caches the tgid value that the last readdir call couldn't - * return. lseek aka telldir automagically resets f_version to 0. - */ - tgid = filp->f_version; - filp->f_version = 0; - for (task = first_tgid(tgid, nr); + tgid = filp->f_pos - TGID_OFFSET; + for (task = next_tgid(tgid); task; - task = next_tgid(task), filp->f_pos++) { - int len; - ino_t ino; + put_task_struct(task), task = next_tgid(tgid + 1)) { tgid = task->pid; - 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) { - /* returning this tgid failed, save it as the first - * pid for the next readir call */ - filp->f_version = tgid; + filp->f_pos = tgid + TGID_OFFSET; + if (proc_pid_fill_cache(filp, dirent, filldir, task, tgid) < 0) { put_task_struct(task); - break; + 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("fd", S_IRUSR|S_IXUSR, fd), + INF("environ", S_IRUSR, pid_environ), + INF("auxv", S_IRUSR, pid_auxv), + INF("status", S_IRUGO, pid_status), + 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 + REG("mem", S_IRUSR|S_IWUSR, mem), +#ifdef CONFIG_SECCOMP + REG("seccomp", S_IRUSR|S_IWUSR, seccomp), +#endif + LNK("cwd", cwd), + LNK("root", root), + LNK("exe", exe), + REG("mounts", S_IRUGO, mounts), +#ifdef CONFIG_MMU + REG("smaps", S_IRUGO, smaps), +#endif +#ifdef CONFIG_SECURITY + DIR("attr", S_IRUGO|S_IXUGO, attr_dir), +#endif +#ifdef CONFIG_KALLSYMS + INF("wchan", S_IRUGO, pid_wchan), +#endif +#ifdef CONFIG_SCHEDSTATS + INF("schedstat", S_IRUGO, pid_schedstat), +#endif +#ifdef CONFIG_CPUSETS + REG("cpuset", S_IRUGO, cpuset), +#endif + INF("oom_score", S_IRUGO, oom_score), + REG("oom_adj", S_IRUGO|S_IWUSR, oom_adjust), +#ifdef CONFIG_AUDITSYSCALL + REG("loginuid", S_IWUSR|S_IRUGO, loginuid), +#endif +}; + +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, ARRAY_SIZE(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, +}; + +static struct dentry *proc_task_instantiate(struct inode *dir, + struct dentry *dentry, struct task_struct *task, void *ptr) +{ + struct dentry *error = ERR_PTR(-ENOENT); + struct inode *inode; + inode = proc_pid_make_inode(dir->i_sb, task); + + if (!inode) + goto out; + 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; + inode->i_nlink = 3; +#ifdef CONFIG_SECURITY + inode->i_nlink += 1; +#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)) + error = NULL; +out: + return error; +} + +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); + 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; + + result = proc_task_instantiate(dir, dentry, task, 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. * * Usually this is just the thread group leader, but if the users @@ -2318,10 +2217,18 @@ static struct task_struct *next_tid(struct task_struct *start) 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 inode *inode = dentry->d_inode; struct task_struct *leader = get_proc_task(inode); @@ -2358,11 +2265,8 @@ static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldi 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; @@ -2392,3 +2296,14 @@ static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct 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, +}; |