| /* |
| * 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; |
| } |