blob: a130e30ce1e8e6c04992cebccde3c50556fa952d [file] [log] [blame]
/*
* linux/fs/proc/fd.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* proc fd directory handling functions
*/
#include <asm/segment.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
static int proc_readfd(struct inode *, struct file *, struct dirent *, int);
static int proc_lookupfd(struct inode *,const char *,int,struct inode **);
static struct file_operations proc_fd_operations = {
NULL, /* lseek - default */
NULL, /* read - bad */
NULL, /* write - bad */
proc_readfd, /* readdir */
NULL, /* select - default */
NULL, /* ioctl - default */
NULL, /* mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* can't fsync */
};
/*
* proc directories can do almost nothing..
*/
struct inode_operations proc_fd_inode_operations = {
&proc_fd_operations, /* default base directory file-ops */
NULL, /* create */
proc_lookupfd, /* lookup */
NULL, /* link */
NULL, /* unlink */
NULL, /* symlink */
NULL, /* mkdir */
NULL, /* rmdir */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
NULL, /* follow_link */
NULL, /* bmap */
NULL, /* truncate */
NULL /* permission */
};
static int proc_lookupfd(struct inode * dir,const char * name, int len,
struct inode ** result)
{
unsigned int ino, pid, fd, c;
struct task_struct * p;
struct super_block * sb;
int i;
*result = NULL;
ino = dir->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
ino -= 7;
if (!dir)
return -ENOENT;
sb = dir->i_sb;
if (!pid || ino > 1 || !S_ISDIR(dir->i_mode)) {
iput(dir);
return -ENOENT;
}
if (!len || (name[0] == '.' && (len == 1 ||
(name[1] == '.' && len == 2)))) {
if (len < 2) {
*result = dir;
return 0;
}
if (!(*result = iget(sb,(pid << 16)+2))) {
iput(dir);
return -ENOENT;
}
iput(dir);
return 0;
}
iput(dir);
fd = 0;
while (len-- > 0) {
c = *name - '0';
name++;
if (c > 9) {
fd = 0xfffff;
break;
}
fd *= 10;
fd += c;
if (fd & 0xffff0000) {
fd = 0xfffff;
break;
}
}
for (i = 0 ; i < NR_TASKS ; i++)
if ((p = task[i]) && p->pid == pid)
break;
if (!pid || i >= NR_TASKS)
return -ENOENT;
if (!ino) {
if (fd >= NR_OPEN || !p->filp[fd] || !p->filp[fd]->f_inode)
return -ENOENT;
ino = (pid << 16) + 0x100 + fd;
} else {
int j = 0;
struct vm_area_struct * mpnt;
for (mpnt = p->mmap; mpnt; mpnt = mpnt->vm_next)
if (mpnt->vm_inode)
j++;
if (fd >= j)
return -ENOENT;
ino = (pid << 16) + 0x200 + fd;
}
if (!(*result = iget(sb,ino)))
return -ENOENT;
return 0;
}
static int proc_readfd(struct inode * inode, struct file * filp,
struct dirent * dirent, int count)
{
struct task_struct * p;
unsigned int fd, pid, ino;
int i,j;
if (!inode || !S_ISDIR(inode->i_mode))
return -EBADF;
ino = inode->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
ino -= 7;
if (ino > 1)
return 0;
while (1) {
fd = filp->f_pos;
filp->f_pos++;
if (fd < 2) {
i = j = fd+1;
if (!fd)
fd = inode->i_ino;
else
fd = (inode->i_ino & 0xffff0000) | 2;
put_fs_long(fd, &dirent->d_ino);
put_fs_word(i, &dirent->d_reclen);
put_fs_byte(0, i+dirent->d_name);
while (i--)
put_fs_byte('.', i+dirent->d_name);
return j;
}
fd -= 2;
for (i = 1 ; i < NR_TASKS ; i++)
if ((p = task[i]) && p->pid == pid)
break;
if (i >= NR_TASKS)
return 0;
if (!ino) {
if (fd >= NR_OPEN)
break;
if (!p->filp[fd] || !p->filp[fd]->f_inode)
continue;
} else {
int j = 0;
struct vm_area_struct * mpnt;
for (mpnt = p->mmap ; mpnt ; mpnt = mpnt->vm_next)
if (mpnt->vm_inode)
j++;
if (fd >= j)
break;
}
j = 10;
i = 1;
while (fd >= j) {
j *= 10;
i++;
}
j = i;
if (!ino)
ino = (pid << 16) + 0x100 + fd;
else
ino = (pid << 16) + 0x200 + fd;
put_fs_long(ino, &dirent->d_ino);
put_fs_word(i, &dirent->d_reclen);
put_fs_byte(0, i+dirent->d_name);
while (i--) {
put_fs_byte('0'+(fd % 10), i+dirent->d_name);
fd /= 10;
}
return j;
}
return 0;
}