| /* |
| * linux/fs/proc/base.c |
| * |
| * Copyright (C) 1991, 1992 Linus Torvalds |
| * |
| * proc base 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_readbase(struct inode *, struct file *, struct dirent *, int); |
| static int proc_lookupbase(struct inode *,const char *,int,struct inode **); |
| |
| static struct file_operations proc_base_operations = { |
| NULL, /* lseek - default */ |
| NULL, /* read - bad */ |
| NULL, /* write - bad */ |
| proc_readbase, /* 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_base_inode_operations = { |
| &proc_base_operations, /* default base directory file-ops */ |
| NULL, /* create */ |
| proc_lookupbase, /* 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 struct proc_dir_entry base_dir[] = { |
| { 1,2,".." }, |
| { 2,1,"." }, |
| { 3,3,"mem" }, |
| { 4,3,"cwd" }, |
| { 5,4,"root" }, |
| { 6,3,"exe" }, |
| { 7,2,"fd" }, |
| { 8,4,"mmap" }, |
| { 9,7,"environ" }, |
| { 10,7,"cmdline" }, |
| { 11,4,"stat" }, |
| { 12,5,"statm" } |
| }; |
| |
| #define NR_BASE_DIRENTRY ((sizeof (base_dir))/(sizeof (base_dir[0]))) |
| |
| int proc_match(int len,const char * name,struct proc_dir_entry * de) |
| { |
| register int same __asm__("ax"); |
| |
| if (!de || !de->low_ino) |
| return 0; |
| /* "" means "." ---> so paths like "/usr/lib//libc.a" work */ |
| if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) |
| return 1; |
| if (de->namelen != len) |
| return 0; |
| __asm__("cld\n\t" |
| "repe ; cmpsb\n\t" |
| "setz %%al" |
| :"=a" (same) |
| :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len) |
| :"cx","di","si"); |
| return same; |
| } |
| |
| static int proc_lookupbase(struct inode * dir,const char * name, int len, |
| struct inode ** result) |
| { |
| unsigned int pid, ino; |
| int i; |
| |
| *result = NULL; |
| if (!dir) |
| return -ENOENT; |
| if (!S_ISDIR(dir->i_mode)) { |
| iput(dir); |
| return -ENOENT; |
| } |
| ino = dir->i_ino; |
| pid = ino >> 16; |
| i = NR_BASE_DIRENTRY; |
| while (i-- > 0 && !proc_match(len,name,base_dir+i)) |
| /* nothing */; |
| if (i < 0) { |
| iput(dir); |
| return -ENOENT; |
| } |
| if (base_dir[i].low_ino == 1) |
| ino = 1; |
| else |
| ino = (pid << 16) + base_dir[i].low_ino; |
| for (i = 0 ; i < NR_TASKS ; i++) |
| if (task[i] && task[i]->pid == pid) |
| break; |
| if (!pid || i >= NR_TASKS) { |
| iput(dir); |
| return -ENOENT; |
| } |
| if (!(*result = iget(dir->i_sb,ino))) { |
| iput(dir); |
| return -ENOENT; |
| } |
| iput(dir); |
| return 0; |
| } |
| |
| static int proc_readbase(struct inode * inode, struct file * filp, |
| struct dirent * dirent, int count) |
| { |
| struct proc_dir_entry * de; |
| unsigned int pid, ino; |
| int i,j; |
| |
| if (!inode || !S_ISDIR(inode->i_mode)) |
| return -EBADF; |
| ino = inode->i_ino; |
| pid = ino >> 16; |
| for (i = 0 ; i < NR_TASKS ; i++) |
| if (task[i] && task[i]->pid == pid) |
| break; |
| if (!pid || i >= NR_TASKS) |
| return 0; |
| if (((unsigned) filp->f_pos) < NR_BASE_DIRENTRY) { |
| de = base_dir + filp->f_pos; |
| filp->f_pos++; |
| i = de->namelen; |
| ino = de->low_ino; |
| if (ino != 1) |
| ino |= (pid << 16); |
| put_fs_long(ino, &dirent->d_ino); |
| put_fs_word(i,&dirent->d_reclen); |
| put_fs_byte(0,i+dirent->d_name); |
| j = i; |
| while (i--) |
| put_fs_byte(de->name[i], i+dirent->d_name); |
| return j; |
| } |
| return 0; |
| } |