diff options
Diffstat (limited to 'fs/proc/fd.c')
-rw-r--r-- | fs/proc/fd.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/fs/proc/fd.c b/fs/proc/fd.c new file mode 100644 index 000000000..954540871 --- /dev/null +++ b/fs/proc/fd.c @@ -0,0 +1,180 @@ +/* + * 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 || !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 (fd >= NR_OPEN || !p->files->fd[fd] || !p->files->fd[fd]->f_inode) + return -ENOENT; + + ino = (pid << 16) + 0x100 + 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) + 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 (fd >= NR_OPEN) + break; + + if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode) + continue; + + j = 10; + i = 1; + while (fd >= j) { + j *= 10; + i++; + } + j = i; + ino = (pid << 16) + 0x100 + 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; +} |