/*
|
/*
|
* linux/fs/proc/fd.c
|
* linux/fs/proc/fd.c
|
*
|
*
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*
|
*
|
* proc fd directory handling functions
|
* proc fd directory handling functions
|
*/
|
*/
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
|
|
static int proc_readfd(struct inode *, struct file *, void *, filldir_t);
|
static int proc_readfd(struct inode *, struct file *, void *, filldir_t);
|
static int proc_lookupfd(struct inode *,const char *,int,struct inode **);
|
static int proc_lookupfd(struct inode *,const char *,int,struct inode **);
|
|
|
static struct file_operations proc_fd_operations = {
|
static struct file_operations proc_fd_operations = {
|
NULL, /* lseek - default */
|
NULL, /* lseek - default */
|
NULL, /* read - bad */
|
NULL, /* read - bad */
|
NULL, /* write - bad */
|
NULL, /* write - bad */
|
proc_readfd, /* readdir */
|
proc_readfd, /* readdir */
|
NULL, /* select - default */
|
NULL, /* select - default */
|
NULL, /* ioctl - default */
|
NULL, /* ioctl - default */
|
NULL, /* mmap */
|
NULL, /* mmap */
|
NULL, /* no special open code */
|
NULL, /* no special open code */
|
NULL, /* no special release code */
|
NULL, /* no special release code */
|
NULL /* can't fsync */
|
NULL /* can't fsync */
|
};
|
};
|
|
|
/*
|
/*
|
* proc directories can do almost nothing..
|
* proc directories can do almost nothing..
|
*/
|
*/
|
struct inode_operations proc_fd_inode_operations = {
|
struct inode_operations proc_fd_inode_operations = {
|
&proc_fd_operations, /* default base directory file-ops */
|
&proc_fd_operations, /* default base directory file-ops */
|
NULL, /* create */
|
NULL, /* create */
|
proc_lookupfd, /* lookup */
|
proc_lookupfd, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL /* permission */
|
NULL /* permission */
|
};
|
};
|
|
|
static int proc_lookupfd(struct inode * dir, const char * name, int len,
|
static int proc_lookupfd(struct inode * dir, const char * name, int len,
|
struct inode ** result)
|
struct inode ** result)
|
{
|
{
|
unsigned int ino, pid, fd, c;
|
unsigned int ino, pid, fd, c;
|
struct task_struct * p;
|
struct task_struct * p;
|
struct super_block * sb;
|
struct super_block * sb;
|
int i;
|
int i;
|
|
|
*result = NULL;
|
*result = NULL;
|
ino = dir->i_ino;
|
ino = dir->i_ino;
|
pid = ino >> 16;
|
pid = ino >> 16;
|
ino &= 0x0000ffff;
|
ino &= 0x0000ffff;
|
if (!dir)
|
if (!dir)
|
return -ENOENT;
|
return -ENOENT;
|
sb = dir->i_sb;
|
sb = dir->i_sb;
|
if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) {
|
if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) {
|
iput(dir);
|
iput(dir);
|
return -ENOENT;
|
return -ENOENT;
|
}
|
}
|
if (!len || (name[0] == '.' && (len == 1 ||
|
if (!len || (name[0] == '.' && (len == 1 ||
|
(name[1] == '.' && len == 2)))) {
|
(name[1] == '.' && len == 2)))) {
|
if (len < 2) {
|
if (len < 2) {
|
*result = dir;
|
*result = dir;
|
return 0;
|
return 0;
|
}
|
}
|
if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) {
|
if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) {
|
iput(dir);
|
iput(dir);
|
return -ENOENT;
|
return -ENOENT;
|
}
|
}
|
iput(dir);
|
iput(dir);
|
return 0;
|
return 0;
|
}
|
}
|
iput(dir);
|
iput(dir);
|
fd = 0;
|
fd = 0;
|
while (len-- > 0) {
|
while (len-- > 0) {
|
c = *name - '0';
|
c = *name - '0';
|
name++;
|
name++;
|
if (c > 9) {
|
if (c > 9) {
|
fd = 0xfffff;
|
fd = 0xfffff;
|
break;
|
break;
|
}
|
}
|
fd *= 10;
|
fd *= 10;
|
fd += c;
|
fd += c;
|
if (fd & 0xffff0000) {
|
if (fd & 0xffff0000) {
|
fd = 0xfffff;
|
fd = 0xfffff;
|
break;
|
break;
|
}
|
}
|
}
|
}
|
for (i = 0 ; i < NR_TASKS ; i++)
|
for (i = 0 ; i < NR_TASKS ; i++)
|
if ((p = task[i]) && p->pid == pid)
|
if ((p = task[i]) && p->pid == pid)
|
break;
|
break;
|
if (!pid || i >= NR_TASKS)
|
if (!pid || i >= NR_TASKS)
|
return -ENOENT;
|
return -ENOENT;
|
|
|
if (fd >= NR_OPEN || !p->files || !p->files->fd[fd]
|
if (fd >= NR_OPEN || !p->files || !p->files->fd[fd]
|
|| !p->files->fd[fd]->f_inode)
|
|| !p->files->fd[fd]->f_inode)
|
return -ENOENT;
|
return -ENOENT;
|
|
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
|
|
|
if (!(*result = proc_get_inode(sb, ino, NULL)))
|
if (!(*result = proc_get_inode(sb, ino, NULL)))
|
return -ENOENT;
|
return -ENOENT;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
#define NUMBUF 10
|
#define NUMBUF 10
|
|
|
static int proc_readfd(struct inode * inode, struct file * filp,
|
static int proc_readfd(struct inode * inode, struct file * filp,
|
void * dirent, filldir_t filldir)
|
void * dirent, filldir_t filldir)
|
{
|
{
|
char buf[NUMBUF];
|
char buf[NUMBUF];
|
int task_nr;
|
int task_nr;
|
struct task_struct * p;
|
struct task_struct * p;
|
unsigned int fd, pid, ino;
|
unsigned int fd, pid, ino;
|
unsigned long i,j;
|
unsigned long i,j;
|
|
|
if (!inode || !S_ISDIR(inode->i_mode))
|
if (!inode || !S_ISDIR(inode->i_mode))
|
return -EBADF;
|
return -EBADF;
|
ino = inode->i_ino;
|
ino = inode->i_ino;
|
pid = ino >> 16;
|
pid = ino >> 16;
|
ino &= 0x0000ffff;
|
ino &= 0x0000ffff;
|
if (ino != PROC_PID_FD)
|
if (ino != PROC_PID_FD)
|
return 0;
|
return 0;
|
|
|
for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) {
|
for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) {
|
unsigned long ino = inode->i_ino;
|
unsigned long ino = inode->i_ino;
|
if (fd)
|
if (fd)
|
ino = (ino & 0xffff0000) | PROC_PID_INO;
|
ino = (ino & 0xffff0000) | PROC_PID_INO;
|
if (filldir(dirent, "..", fd+1, fd, ino) < 0)
|
if (filldir(dirent, "..", fd+1, fd, ino) < 0)
|
return 0;
|
return 0;
|
}
|
}
|
|
|
task_nr = 1;
|
task_nr = 1;
|
for (;;) {
|
for (;;) {
|
if ((p = task[task_nr]) && p->pid == pid)
|
if ((p = task[task_nr]) && p->pid == pid)
|
break;
|
break;
|
if (++task_nr >= NR_TASKS)
|
if (++task_nr >= NR_TASKS)
|
return 0;
|
return 0;
|
}
|
}
|
|
|
for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) {
|
for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) {
|
if (!p->files)
|
if (!p->files)
|
break;
|
break;
|
if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode)
|
if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode)
|
continue;
|
continue;
|
|
|
j = NUMBUF;
|
j = NUMBUF;
|
i = fd;
|
i = fd;
|
do {
|
do {
|
j--;
|
j--;
|
buf[j] = '0' + (i % 10);
|
buf[j] = '0' + (i % 10);
|
i /= 10;
|
i /= 10;
|
} while (i);
|
} while (i);
|
|
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd;
|
if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0)
|
if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0)
|
break;
|
break;
|
|
|
/* filldir() might have slept, so we must re-validate "p" */
|
/* filldir() might have slept, so we must re-validate "p" */
|
if (p != task[task_nr] || p->pid != pid)
|
if (p != task[task_nr] || p->pid != pid)
|
break;
|
break;
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|