/*
* linux/fs/proc/link.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* /proc link-file handling code
*/
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
static int proc_readlink(struct inode *, char *, int);
static int proc_follow_link(struct inode *, struct inode *, int, int,
struct inode **);
/*
* PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke
* when the files[] array was updated only after the open code
*/
#undef PLAN9_SEMANTICS
/*
* links can't do much...
*/
static struct file_operations proc_fd_link_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
NULL, /* readdir - bad */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
NULL, /* very special open code */
NULL, /* no special release code */
NULL /* can't fsync */
};
struct inode_operations proc_link_inode_operations = {
&proc_fd_link_operations,/* file-operations */
NULL, /* create */
NULL, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
proc_readlink, /* readlink */
proc_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
static int proc_follow_link(struct inode * dir, struct inode * inode,
int flag, int mode, struct inode ** res_inode)
{
unsigned int pid, ino;
struct task_struct * p;
struct inode * new_inode;
int i, error;
*res_inode = NULL;
if (dir)
iput(dir);
if (!inode)
return -ENOENT;
if ((error = permission(inode, MAY_EXEC)) != 0){
iput(inode);
return error;
}
ino = inode->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
for (i = 0 ; i < NR_TASKS ; i++)
if ((p = task[i]) && p->pid == pid)
break;
if (i >= NR_TASKS) {
iput(inode);
return -ENOENT;
}
new_inode = NULL;
switch (ino) {
case PROC_PID_CWD:
if (!p->fs)
break;
new_inode = p->fs->pwd;
break;
case PROC_PID_ROOT:
if (!p->fs)
break;
new_inode = p->fs->root;
break;
case PROC_PID_EXE: {
struct vm_area_struct * vma;
if (!p->mm)
break;
vma = p->mm->mmap;
while (vma) {
if (vma->vm_flags & VM_EXECUTABLE) {
new_inode = vma->vm_inode;
break;
}
vma = vma->vm_next;
}
break;
}
default:
switch (ino >> 8) {
case PROC_PID_FD_DIR:
if (!p->files)
break;
ino &= 0xff;
if (ino < NR_OPEN && p->files->fd[ino]) {
new_inode = p->files->fd[ino]->f_inode;
}
break;
}
}
iput(inode);
if (!new_inode)
return -ENOENT;
*res_inode = new_inode;
new_inode->i_count++;
return 0;
}
static int proc_readlink(struct inode * inode, char * buffer, int buflen)
{
int i;
unsigned int dev,ino;
char buf[64];
if (!S_ISLNK(inode->i_mode)) {
iput(inode);
return -EINVAL;
}
i = proc_follow_link(NULL, inode, 0, 0, &inode);
if (i)
return i;
if (!inode)
return -EIO;
dev = kdev_t_to_nr(inode->i_dev);
ino = inode->i_ino;
iput(inode);
i = sprintf(buf,"[%04x]:%u", dev, ino);
if (buflen > i)
buflen = i;
i = 0;
while (i < buflen)
put_user(buf[i++],buffer++);
return i;
}