/* * linux/fs/proc/dev.c * * Copyright (C) 1991, 1992 Linus Torvalds * * orc 9/1996 Extracted from net.c * * proc dev directory handling functions */ #include #include #include #include #include #include #include #include #include /* forward references */ static int proc_readproc(struct inode * inode, struct file * file, char * buf, int count); static int proc_readprocdir(struct inode *, struct file *, struct dirent *, int); static int proc_lookupproc(struct inode *,const char *,int,struct inode **); static struct file_operations proc_dev_operations = { NULL, /* lseek - default */ proc_readproc, /* read - bad */ NULL, /* write - bad */ proc_readprocdir, /* 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_dev_inode_operations = { &proc_dev_operations, /* default net directory file-ops */ NULL, /* create */ proc_lookupproc, /* 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_dev_entry { proc_dir_entry procinfo; /* normal procinfo stuff */ short int major, minor, mode; /* device-specific stuff */ } * dev_dir[] = { { { PROC_NET, 1, "." }, 0, 0, IF_DIR }, { { PROC_ROOT_INO, 2, ".." }, 0, 0, IF_DIR }, }; int proc_dev_last_ino = PROC_DEV_FIRST; static int nr_dev_dir_entries = 2; int proc_registerdev(char *name, int mode, short int major, short int minor) { } /* proc_registerdev */ static int proc_lookupproc(struct inode * dir,const char * name, int len, struct inode ** result) { struct proc_dir_entry *de; *result = NULL; if (!dir) return -ENOENT; if (!S_ISDIR(dir->i_mode)) { iput(dir); return -ENOENT; } for (de = net_dir ; de->name ; de++) { if (!proc_match(len, name, de->procinfo)) continue; *result = iget(dir->i_sb, de->procinfo.low_ino); iput(dir); if (!*result) return -ENOENT; return 0; } iput(dir); return -ENOENT; } static int proc_readprocdir(struct inode * inode, struct file * filp, struct dirent * dirent, int count) { struct proc_dir_entry * de; unsigned int ino; int i,j; if (!inode || !S_ISDIR(inode->i_mode)) return -EBADF; ino = inode->i_ino; if (((unsigned) filp->f_pos) < nr_dev_dir_entries) { de = net_dir + filp->f_pos; filp->f_pos++; i = de->namelen; ino = de->low_ino; 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; } #define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ static int proc_readproc(struct inode * inode, struct file * file, char * buf, int count) { char * page; int length; unsigned int ino; int bytes=count; int thistime; int copied=0; char *start; if (count < 0) return -EINVAL; if (!(page = (char*) __get_free_page(GFP_KERNEL))) return -ENOMEM; ino = inode->i_ino; while(bytes>0) { thistime=bytes; if(bytes>PROC_BLOCK_SIZE) thistime=PROC_BLOCK_SIZE; switch (ino) { case PROC_NET_UNIX: length = unix_get_info(page,&start,file->f_pos,thistime); break; #ifdef CONFIG_INET case PROC_NET_SOCKSTAT: length = afinet_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_ARP: length = arp_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_ROUTE: length = rt_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_DEV: length = dev_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_RAW: length = raw_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_TCP: length = tcp_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_UDP: length = udp_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_SNMP: length = snmp_get_info(page, &start, file->f_pos,thistime); break; #ifdef CONFIG_IP_MULTICAST case PROC_NET_IGMP: length = ip_mc_procinfo(page, &start, file->f_pos,thistime); break; #endif #ifdef CONFIG_IP_FIREWALL case PROC_NET_IPFWFWD: length = ip_fw_fwd_procinfo(page, &start, file->f_pos, thistime, (file->f_flags & O_ACCMODE) == O_RDWR); break; case PROC_NET_IPFWBLK: length = ip_fw_blk_procinfo(page, &start, file->f_pos, thistime, (file->f_flags & O_ACCMODE) == O_RDWR); break; #endif #ifdef CONFIG_IP_ACCT case PROC_NET_IPACCT: length = ip_acct_procinfo(page, &start, file->f_pos, thistime, (file->f_flags & O_ACCMODE) == O_RDWR); break; #endif #ifdef CONFIG_IP_MASQUERADE case PROC_NET_IPMSQHST: length = ip_msqhst_procinfo(page, &start, file->f_pos,thistime); break; #endif #ifdef CONFIG_INET_RARP case PROC_NET_RARP: length = rarp_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_INET_RARP */ #if defined(CONFIG_WAVELAN) case PROC_NET_WAVELAN: length = wavelan_get_info(page, &start, file->f_pos, thistime); break; #endif /* defined(CONFIG_WAVELAN) */ #endif /* CONFIG_INET */ #ifdef CONFIG_IPX case PROC_NET_IPX_INTERFACE: length = ipx_get_interface_info(page, &start, file->f_pos, thistime); break; case PROC_NET_IPX_ROUTE: length = ipx_rt_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_IPX: length = ipx_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_IPX */ #ifdef CONFIG_ATALK case PROC_NET_ATALK: length = atalk_get_info(page, &start, file->f_pos, thistime); break; case PROC_NET_AT_ROUTE: length = atalk_rt_get_info(page, &start, file->f_pos, thistime); break; case PROC_NET_ATIF: length = atalk_if_get_info(page, &start, file->f_pos, thistime); break; #endif /* CONFIG_ATALK */ #ifdef CONFIG_AX25 case PROC_NET_AX25_ROUTE: length = ax25_rt_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_AX25: length = ax25_get_info(page,&start,file->f_pos,thistime); break; #ifdef CONFIG_NETROM case PROC_NET_NR_NODES: length = nr_nodes_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_NR_NEIGH: length = nr_neigh_get_info(page,&start,file->f_pos,thistime); break; case PROC_NET_NR: length = nr_get_info(page,&start,file->f_pos,thistime); break; #endif /* CONFIG_NETROM */ #endif /* CONFIG_AX25 */ default: free_page((unsigned long) page); return -EBADF; } /* * We have been given a non page aligned block of * the data we asked for + a bit. We have been given * the start pointer and we know the length.. */ if (length <= 0) break; /* * Copy the bytes */ memcpy_tofs(buf+copied, start, length); file->f_pos+=length; /* Move down the file */ bytes-=length; copied+=length; if(length