link.c 4.44 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 *  linux/fs/proc/link.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *
 *  /proc link-file handling code
 */

Linus Torvalds's avatar
Linus Torvalds committed
9
#include <asm/uaccess.h>
10 11 12

#include <linux/errno.h>
#include <linux/sched.h>
Linus Torvalds's avatar
Linus Torvalds committed
13
#include <linux/mm.h>
Linus Torvalds's avatar
Linus Torvalds committed
14 15
#include <linux/fs.h>
#include <linux/file.h>
Linus Torvalds's avatar
Linus Torvalds committed
16
#include <linux/proc_fs.h>
17 18
#include <linux/stat.h>

Linus Torvalds's avatar
Linus Torvalds committed
19
static int proc_readlink(struct dentry *, char *, int);
Linus Torvalds's avatar
Linus Torvalds committed
20
static struct dentry * proc_follow_link(struct dentry *, struct dentry *, unsigned int);
Linus Torvalds's avatar
Linus Torvalds committed
21

22 23 24
/*
 * links can't do much...
 */
Linus Torvalds's avatar
Linus Torvalds committed
25 26 27 28 29
static struct file_operations proc_fd_link_operations = {
	NULL,			/* lseek - default */
	NULL,			/* read - bad */
	NULL,			/* write - bad */
	NULL,			/* readdir - bad */
Linus Torvalds's avatar
Linus Torvalds committed
30
	NULL,			/* poll - default */
Linus Torvalds's avatar
Linus Torvalds committed
31 32
	NULL,			/* ioctl - default */
	NULL,			/* mmap */
Linus Torvalds's avatar
Linus Torvalds committed
33
	NULL,			/* very special open code */
Linus Torvalds's avatar
Linus Torvalds committed
34
	NULL,			/* flush */
Linus Torvalds's avatar
Linus Torvalds committed
35 36 37 38
	NULL,			/* no special release code */
	NULL			/* can't fsync */
};

39
struct inode_operations proc_link_inode_operations = {
Linus Torvalds's avatar
Linus Torvalds committed
40
	&proc_fd_link_operations,/* file-operations */
41 42 43 44 45 46 47 48 49 50
	NULL,			/* create */
	NULL,			/* lookup */
	NULL,			/* link */
	NULL,			/* unlink */
	NULL,			/* symlink */
	NULL,			/* mkdir */
	NULL,			/* rmdir */
	NULL,			/* mknod */
	NULL,			/* rename */
	proc_readlink,		/* readlink */
Linus Torvalds's avatar
Linus Torvalds committed
51
	proc_follow_link,	/* follow_link */
Linus Torvalds's avatar
Linus Torvalds committed
52
	NULL,			/* get_block */
Linus Torvalds's avatar
Linus Torvalds committed
53 54
	NULL,			/* readpage */
	NULL,			/* writepage */
55
	NULL,			/* flushpage */
56
	NULL,			/* truncate */
57 58 59
	proc_permission,	/* permission */
	NULL,			/* smap */
	NULL			/* revalidate */
60 61
};

Linus Torvalds's avatar
Linus Torvalds committed
62
static struct dentry * proc_follow_link(struct dentry *dentry,
Linus Torvalds's avatar
Linus Torvalds committed
63 64
					struct dentry *base,
					unsigned int follow)
65
{
Linus Torvalds's avatar
Linus Torvalds committed
66
	struct inode *inode = dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
67 68 69
	struct task_struct *p;
	struct dentry * result;
	int ino, pid;
Linus Torvalds's avatar
Linus Torvalds committed
70
	int error;
71

Linus Torvalds's avatar
Linus Torvalds committed
72 73 74 75 76 77
	/* We don't need a base pointer in the /proc filesystem */
	dput(base);

	error = permission(inode, MAY_EXEC);
	result = ERR_PTR(error);
	if (error)
Linus Torvalds's avatar
Linus Torvalds committed
78
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
79

80 81 82
	ino = inode->i_ino;
	pid = ino >> 16;
	ino &= 0x0000ffff;
Linus Torvalds's avatar
Linus Torvalds committed
83

Linus Torvalds's avatar
Linus Torvalds committed
84 85
	result = ERR_PTR(-ENOENT);

86
	switch (ino) {
Linus Torvalds's avatar
Linus Torvalds committed
87
		case PROC_PID_CWD:
Linus Torvalds's avatar
Linus Torvalds committed
88 89 90 91 92 93
			read_lock(&tasklist_lock);
			p = find_task_by_pid(pid);
			if (p && p->fs && p->fs->pwd)
				result = dget(p->fs->pwd);
			read_unlock(&tasklist_lock);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
94

Linus Torvalds's avatar
Linus Torvalds committed
95
		case PROC_PID_ROOT:
Linus Torvalds's avatar
Linus Torvalds committed
96 97 98 99 100 101
			read_lock(&tasklist_lock);
			p = find_task_by_pid(pid);
			if (p && p->fs && p->fs->root)
				result = dget(p->fs->root);
			read_unlock(&tasklist_lock);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
102

Linus Torvalds's avatar
Linus Torvalds committed
103
		case PROC_PID_EXE: {
Linus Torvalds's avatar
Linus Torvalds committed
104
			struct mm_struct *mm = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
105
			struct vm_area_struct * vma;
Linus Torvalds's avatar
Linus Torvalds committed
106 107 108 109 110 111 112 113 114 115 116
			read_lock(&tasklist_lock);
			p = find_task_by_pid(pid);
			if (p)
				mm = p->mm;
			if (mm)
				atomic_inc(&mm->mm_users);
			read_unlock(&tasklist_lock);
			if (!mm)
				break;
			down(&mm->mmap_sem);
			vma = mm->mmap;
Linus Torvalds's avatar
Linus Torvalds committed
117
			while (vma) {
Linus Torvalds's avatar
Linus Torvalds committed
118 119
				if ((vma->vm_flags & VM_EXECUTABLE) && 
				    vma->vm_file) {
Linus Torvalds's avatar
Linus Torvalds committed
120 121
					result = dget(vma->vm_file->f_dentry);
					break;
Linus Torvalds's avatar
Linus Torvalds committed
122
				}
Linus Torvalds's avatar
Linus Torvalds committed
123 124
				vma = vma->vm_next;
			}
Linus Torvalds's avatar
Linus Torvalds committed
125 126 127
			up(&mm->mmap_sem);
			mmput(mm);
			break;
Linus Torvalds's avatar
Linus Torvalds committed
128
		}
129
		default:
Linus Torvalds's avatar
Linus Torvalds committed
130
			if (ino & PROC_PID_FD_DIR) {
Linus Torvalds's avatar
Linus Torvalds committed
131
				struct file * file;
Linus Torvalds's avatar
Linus Torvalds committed
132 133 134 135 136 137 138 139
				struct files_struct *files = NULL;
				read_lock(&tasklist_lock);
				p = find_task_by_pid(pid);
				if (p)
					files = p->files;
				read_unlock(&tasklist_lock);
				if (!files)
					break;
Linus Torvalds's avatar
Linus Torvalds committed
140
				ino &= 0x7fff;
Linus Torvalds's avatar
Linus Torvalds committed
141 142 143 144 145
				read_lock(&files->file_lock);
				/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
				if (ino < files->max_fds &&
				    (file = files->fd[ino]) && file->f_dentry)
					result = dget(file->f_dentry);
Linus Torvalds's avatar
Linus Torvalds committed
146
				read_unlock(&p->files->file_lock);
147 148
			}
	}
Linus Torvalds's avatar
Linus Torvalds committed
149
out:
Linus Torvalds's avatar
Linus Torvalds committed
150
	return result;
151 152
}

Linus Torvalds's avatar
Linus Torvalds committed
153 154 155 156 157 158 159 160 161 162
/*
 * This pretty-prints the pathname of a dentry,
 * clarifying sockets etc.
 */
static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen)
{
	struct inode * inode;
	char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern;
	int len;

Linus Torvalds's avatar
Linus Torvalds committed
163 164 165
	if(tmp==NULL)
		return -ENOMEM;
		
Linus Torvalds's avatar
Linus Torvalds committed
166 167 168
	/* Check for special dentries.. */
	pattern = NULL;
	inode = dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
169
	if (inode && IS_ROOT(dentry)) {
Linus Torvalds's avatar
Linus Torvalds committed
170
		if (S_ISSOCK(inode->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
171
			pattern = "socket:[%lu]";
Linus Torvalds's avatar
Linus Torvalds committed
172
		if (S_ISFIFO(inode->i_mode))
Linus Torvalds's avatar
Linus Torvalds committed
173 174 175 176 177 178 179 180
			pattern = "pipe:[%lu]";
	}
	
	if (pattern) {
		len = sprintf(tmp, pattern, inode->i_ino);
		path = tmp;
	} else {
		path = d_path(dentry, tmp, PAGE_SIZE);
Linus Torvalds's avatar
Linus Torvalds committed
181
		len = tmp + PAGE_SIZE - 1 - path;
Linus Torvalds's avatar
Linus Torvalds committed
182 183 184 185 186 187 188 189 190 191
	}

	if (len < buflen)
		buflen = len;
	dput(dentry);
	copy_to_user(buffer, path, buflen);
	free_page((unsigned long)tmp);
	return buflen;
}

Linus Torvalds's avatar
Linus Torvalds committed
192
static int proc_readlink(struct dentry * dentry, char * buffer, int buflen)
193
{
Linus Torvalds's avatar
Linus Torvalds committed
194
	int error;
195

Linus Torvalds's avatar
Linus Torvalds committed
196
	dentry = proc_follow_link(dentry, NULL, 1);
Linus Torvalds's avatar
Linus Torvalds committed
197 198 199 200
	error = PTR_ERR(dentry);
	if (!IS_ERR(dentry)) {
		error = -ENOENT;
		if (dentry) {
Linus Torvalds's avatar
Linus Torvalds committed
201
			error = do_proc_readlink(dentry, buffer, buflen);
Linus Torvalds's avatar
Linus Torvalds committed
202
		}
Linus Torvalds's avatar
Linus Torvalds committed
203 204
	}
	return error;
205
}