2411 lines · 56.2 KB · C
RawDownload
/* *  linux/fs/proc/base.c * *  Copyright (C) 1991, 1992 Linus Torvalds * *  proc base directory handling functions * *  1999, Al Viro. Rewritten. Now it covers the whole per-process part. *  Instead of using magical inumbers to determine the kind of object *  we allocate and fill in-core inodes upon lookup. They don't even *  go into icache. We cache the reference to task_struct upon lookup too. *  Eventually it should become a filesystem in its own. We don't use the *  rest of procfs anymore. * * *  Changelog: *  17-Jan-2005 *  Allan Bezerra *  Bruna Moreira <bruna.moreira@indt.org.br> *  Edjard Mota <edjard.mota@indt.org.br> *  Ilias Biris <ilias.biris@indt.org.br> *  Mauricio Lin <mauricio.lin@indt.org.br> * *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT * *  A new process specific entry (smaps) included in /proc. It shows the *  size of rss for each memory area. The maps entry lacks information *  about physical memory size (rss) for each mapped file, i.e., *  rss information for executables and library files. *  This additional information is useful for any tools that need to know *  about physical memory consumption for a process specific library. * *  Changelog: *  21-Feb-2005 *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT *  Pud inclusion in the page table walking. * *  ChangeLog: *  10-Mar-2005 *  10LE Instituto Nokia de Tecnologia - INdT: *  A better way to walks through the page table as suggested by Hugh Dickins. * *  Simo Piiroinen <simo.piiroinen@nokia.com>: *  Smaps information related to shared, private, clean and dirty pages. * *  Paul Mundt <paul.mundt@nokia.com>: *  Overall revision about smaps. */ #include <asm/uaccess.h> #include <linux/errno.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/capability.h>#include <linux/file.h>#include <linux/string.h>#include <linux/seq_file.h>#include <linux/namei.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/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 "internal.h" /* NOTE: *	Implementing inode permission operations in /proc is almost *	certainly an error.  Permission checks need to happen during *	each system call not at open time.  The reason is that most of *	what we wish to check for permissions in /proc varies at runtime. * *	The classic example of a problem is opening file descriptors *	in /proc for a task before it execs a suid executable. */  /* Worst case buffer size needed for holding an integer. */#define PROC_NUMBUF 13 struct pid_entry {	int len;	char *name;	mode_t mode;	const struct inode_operations *iop;	const struct file_operations *fop;	union proc_op op;}; #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;	task_lock(task);	fs = task->fs;	if(fs)		atomic_inc(&fs->count);	task_unlock(task);	return fs;} static int get_nr_threads(struct task_struct *tsk){	/* Must be called with the rcu_read_lock held */	unsigned long flags;	int count = 0; 	if (lock_task_sighand(tsk, &flags)) {		count = atomic_read(&tsk->signal->count);		unlock_task_sighand(tsk, &flags);	}	return count;} static int proc_cwd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt){	struct task_struct *task = get_proc_task(inode);	struct fs_struct *fs = NULL;	int result = -ENOENT; 	if (task) {		fs = get_fs_struct(task);		put_task_struct(task);	}	if (fs) {		read_lock(&fs->lock);		*mnt = mntget(fs->pwdmnt);		*dentry = dget(fs->pwd);		read_unlock(&fs->lock);		result = 0;		put_fs_struct(fs);	}	return result;} static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt){	struct task_struct *task = get_proc_task(inode);	struct fs_struct *fs = NULL;	int result = -ENOENT; 	if (task) {		fs = get_fs_struct(task);		put_task_struct(task);	}	if (fs) {		read_lock(&fs->lock);		*mnt = mntget(fs->rootmnt);		*dentry = dget(fs->root);		read_unlock(&fs->lock);		result = 0;		put_fs_struct(fs);	}	return result;} #define MAY_PTRACE(task) \	(task == current || \	(task->parent == current && \	(task->ptrace & PT_PTRACED) && \	 (task->state == TASK_STOPPED || task->state == TASK_TRACED) && \	 security_ptrace(current,task) == 0)) static int proc_pid_environ(struct task_struct *task, char * buffer){	int res = 0;	struct mm_struct *mm = get_task_mm(task);	if (mm) {		unsigned int len = 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;		mmput(mm);	}	return res;} static int proc_pid_cmdline(struct task_struct *task, char * buffer){	int res = 0;	unsigned int len;	struct mm_struct *mm = get_task_mm(task);	if (!mm)		goto out;	if (!mm->arg_end)		goto out_mm;	/* Shh! No looking before we're done */  	len = mm->arg_end - mm->arg_start; 	if (len > PAGE_SIZE)		len = PAGE_SIZE; 	res = access_process_vm(task, mm->arg_start, buffer, len, 0); 	// If the nul at the end of args has been overwritten, then	// assume application is using setproctitle(3).	if (res > 0 && buffer[res-1] != '\0' && len < PAGE_SIZE) {		len = strnlen(buffer, res);		if (len < res) {		    res = len;		} else {			len = mm->env_end - mm->env_start;			if (len > PAGE_SIZE - res)				len = PAGE_SIZE - res;			res += access_process_vm(task, mm->env_start, buffer+res, len, 0);			res = strnlen(buffer, res);		}	}out_mm:	mmput(mm);out:	return res;} static int proc_pid_auxv(struct task_struct *task, char *buffer){	int res = 0;	struct mm_struct *mm = get_task_mm(task);	if (mm) {		unsigned int nwords = 0;		do			nwords += 2;		while (mm->saved_auxv[nwords - 2] != 0); /* AT_NULL */		res = nwords * sizeof(mm->saved_auxv[0]);		if (res > PAGE_SIZE)			res = PAGE_SIZE;		memcpy(buffer, mm->saved_auxv, res);		mmput(mm);	}	return res;}  #ifdef CONFIG_KALLSYMS/* * Provides a wchan file via kallsyms in a proper one-value-per-file format. * Returns the resolved symbol.  If that fails, simply return the address. */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]; 	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);}#endif /* CONFIG_KALLSYMS */ #ifdef CONFIG_SCHEDSTATS/* * Provides /proc/PID/schedstat */static int proc_pid_schedstat(struct task_struct *task, char *buffer){	return sprintf(buffer, "%lu %lu %lu\n",			task->sched_info.cpu_time,			task->sched_info.run_delay,			task->sched_info.pcnt);}#endif /* The badness from the OOM killer */unsigned long badness(struct task_struct *p, unsigned long uptime);static int proc_oom_score(struct task_struct *task, char *buffer){	unsigned long points;	struct timespec uptime; 	do_posix_clock_monotonic_gettime(&uptime);	points = badness(task, uptime.tv_sec);	return sprintf(buffer, "%lu\n", points);} /************************************************************************//*                       Here the fs part begins                        *//************************************************************************/ /* permission checks */static int proc_fd_access_allowed(struct inode *inode){	struct task_struct *task;	int allowed = 0;	/* Allow access to a task's file descriptors if it is us or we	 * may use ptrace attach to the process and find out that	 * information.	 */	task = get_proc_task(inode);	if (task) {		allowed = ptrace_may_attach(task);		put_task_struct(task);	}	return allowed;} static int proc_setattr(struct dentry *dentry, struct iattr *attr){	int error;	struct inode *inode = dentry->d_inode; 	if (attr->ia_valid & ATTR_MODE)		return -EPERM; 	error = inode_change_ok(inode, attr);	if (!error) {		error = security_inode_setattr(dentry, attr);		if (!error)			error = inode_setattr(inode, attr);	}	return error;} static const struct inode_operations proc_def_inode_operations = {	.setattr	= proc_setattr,}; extern struct seq_operations mounts_op;struct proc_mounts {	struct seq_file m;	int event;}; static int mounts_open(struct inode *inode, struct file *file){	struct task_struct *task = get_proc_task(inode);	struct mnt_namespace *ns = NULL;	struct proc_mounts *p;	int ret = -EINVAL; 	if (task) {		task_lock(task);		if (task->nsproxy) {			ns = task->nsproxy->mnt_ns;			if (ns)				get_mnt_ns(ns);		}		task_unlock(task);		put_task_struct(task);	} 	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 = ns;				p->event = ns->event;				return 0;			}			kfree(p);		}		put_mnt_ns(ns);	}	return ret;} static int mounts_release(struct inode *inode, struct file *file){	struct seq_file *m = file->private_data;	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 mnt_namespace *ns = p->m.private;	unsigned res = 0; 	poll_wait(file, &ns->poll, wait); 	spin_lock(&vfsmount_lock);	if (p->event != ns->event) {		p->event = ns->event;		res = POLLERR;	}	spin_unlock(&vfsmount_lock); 	return res;} static const struct file_operations proc_mounts_operations = {	.open		= mounts_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= mounts_release,	.poll		= mounts_poll,}; extern struct seq_operations mountstats_op;static int mountstats_open(struct inode *inode, struct file *file){	int ret = seq_open(file, &mountstats_op); 	if (!ret) {		struct seq_file *m = file->private_data;		struct mnt_namespace *mnt_ns = NULL;		struct task_struct *task = get_proc_task(inode); 		if (task) {			task_lock(task);			if (task->nsproxy)				mnt_ns = task->nsproxy->mnt_ns;			if (mnt_ns)				get_mnt_ns(mnt_ns);			task_unlock(task);			put_task_struct(task);		} 		if (mnt_ns)			m->private = mnt_ns;		else {			seq_release(inode, file);			ret = -EINVAL;		}	}	return ret;} static const struct file_operations proc_mountstats_operations = {	.open		= mountstats_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= mounts_release,}; #define PROC_BLOCK_SIZE	(3*1024)		/* 4K page size but our output routines use some slack for overruns */ static ssize_t proc_info_read(struct file * file, char __user * buf,			  size_t count, loff_t *ppos){	struct inode * inode = file->f_path.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 > PROC_BLOCK_SIZE)		count = PROC_BLOCK_SIZE; 	length = -ENOMEM;	if (!(page = __get_free_page(GFP_KERNEL)))		goto out; 	length = PROC_I(inode)->op.proc_read(task, (char*)page); 	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:

Lines 1–500 of 2411.