/* * linux/fs/proc/root.c * * Copyright (C) 1991, 1992 Linus Torvalds * * proc root directory handling functions */ #include <asm/segment.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/config.h> #include <asm/bitops.h> /* * Offset of the first process in the /proc root directory.. */ #define FIRST_PROCESS_ENTRY 256 static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t); static int proc_root_lookup(struct inode *,const char *,int,struct inode **); static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; /* * These are the generic /proc directory operations. They * use the in-memory "struct proc_dir_entry" tree to parse * the /proc directory. * * NOTE! The /proc/scsi directory currently does not correctly * build up the proc_dir_entry tree, and will show up empty. */ static struct file_operations proc_dir_operations = { NULL, /* lseek - default */ NULL, /* read - bad */ NULL, /* write - bad */ proc_readdir, /* 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_dir_inode_operations = { &proc_dir_operations, /* default net directory file-ops */ NULL, /* create */ proc_lookup, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; /* * The root /proc directory is special, as it has the * <pid> directories. Thus we don't use the generic * directory handling functions for that.. */ static struct file_operations proc_root_operations = { NULL, /* lseek - default */ NULL, /* read - bad */ NULL, /* write - bad */ proc_root_readdir, /* readdir */ NULL, /* select - default */ NULL, /* ioctl - default */ NULL, /* mmap */ NULL, /* no special open code */ NULL, /* no special release code */ NULL /* no fsync */ }; /* * proc root can do almost nothing.. */ static struct inode_operations proc_root_inode_operations = { &proc_root_operations, /* default base directory file-ops */ NULL, /* create */ proc_root_lookup, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ NULL, /* readlink */ NULL, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; /* * This is the root "inode" in the /proc tree.. */ struct proc_dir_entry proc_root = { PROC_ROOT_INO, 5, "/proc", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, &proc_root_inode_operations, NULL, NULL, NULL, &proc_root, NULL }; struct proc_dir_entry proc_net = { PROC_NET, 3, "net", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, &proc_dir_inode_operations, NULL, NULL, NULL, NULL, NULL }; struct proc_dir_entry proc_scsi = { PROC_SCSI, 4, "scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, 0, &proc_dir_inode_operations, NULL, NULL, NULL, &proc_root, NULL }; struct proc_dir_entry proc_sys_root = { PROC_SYS, 3, "sys", /* inode, name */ S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, /* mode, nlink, uid, gid */ 0, &proc_dir_inode_operations, /* size, ops */ NULL, NULL, /* get_info, fill_inode */ NULL, /* next */ NULL, NULL /* parent, subdir */ }; int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { dp->next = dir->subdir; dp->parent = dir; dir->subdir = dp; if (S_ISDIR(dp->mode)) dir->nlink++; return 0; } int proc_unregister(struct proc_dir_entry * dir, int ino) { struct proc_dir_entry **p = &dir->subdir, *dp; while ((dp = *p) != NULL) { if (dp->low_ino == ino) { *p = dp->next; dp->next = NULL; if (S_ISDIR(dp->mode)) dir->nlink--; if (ino >= PROC_DYNAMIC_FIRST && ino < PROC_DYNAMIC_FIRST+PROC_NDYNAMIC) clear_bit(ino-PROC_DYNAMIC_FIRST, (void *) proc_alloc_map); return 0; } p = &dp->next; } return -EINVAL; } static int make_inode_number(void) { int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC); if (i<0 || i>=PROC_NDYNAMIC) return -1; set_bit(i, (void *) proc_alloc_map); return PROC_DYNAMIC_FIRST + i; } int proc_register_dynamic(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { int i = make_inode_number(); if (i < 0) return -EAGAIN; dp->low_ino = i; dp->next = dir->subdir; dp->parent = dir; dir->subdir = dp; if (S_ISDIR(dp->mode)) dir->nlink++; return 0; } /* * /proc/self: */ static int proc_self_followlink(struct inode * dir, struct inode * inode, int flag, int mode, struct inode ** res_inode) { iput(dir); *res_inode = proc_get_inode(inode->i_sb, (current->pid << 16) + PROC_PID_INO, &proc_pid); iput(inode); if (!*res_inode) return -ENOENT; return 0; } static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) { int len; char tmp[30]; iput(inode); len = 1 + sprintf(tmp, "%d", current->pid); if (buflen < len) len = buflen; memcpy_tofs(buffer, tmp, len); return len; } static struct inode_operations proc_self_inode_operations = { NULL, /* no file-ops */ NULL, /* create */ NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ NULL, /* mkdir */ NULL, /* rmdir */ NULL, /* mknod */ NULL, /* rename */ proc_self_readlink, /* readlink */ proc_self_followlink, /* follow_link */ NULL, /* readpage */ NULL, /* writepage */ NULL, /* bmap */ NULL, /* truncate */ NULL /* permission */ }; void proc_root_init(void) { static int done = 0; if (done) return; done = 1; proc_base_init(); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_LOADAVG, 7, "loadavg", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_UPTIME, 6, "uptime", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_MEMINFO, 7, "meminfo", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_KMSG, 4, "kmsg", S_IFREG | S_IRUSR, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_VERSION, 7, "version", S_IFREG | S_IRUGO, 1, 0, 0, }); #ifdef CONFIG_PCI proc_register(&proc_root, &(struct proc_dir_entry) { PROC_PCI, 3, "pci", S_IFREG | S_IRUGO, 1, 0, 0, }); #endif proc_register(&proc_root, &(struct proc_dir_entry) { PROC_CPUINFO, 7, "cpuinfo", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_SELF, 4, "self", S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0, 64, &proc_self_inode_operations, }); proc_register(&proc_root, &proc_net); proc_register(&proc_root, &proc_scsi); proc_register(&proc_root, &proc_sys_root); #ifdef CONFIG_DEBUG_MALLOC proc_register(&proc_root, &(struct proc_dir_entry) { PROC_MALLOC, 6, "malloc", S_IFREG | S_IRUGO, 1, 0, 0, }); #endif proc_register(&proc_root, &(struct proc_dir_entry) { PROC_KCORE, 5, "kcore", S_IFREG | S_IRUSR, 1, 0, 0, }); #ifdef CONFIG_MODULES proc_register(&proc_root, &(struct proc_dir_entry) { PROC_MODULES, 7, "modules", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_KSYMS, 5, "ksyms", S_IFREG | S_IRUGO, 1, 0, 0, }); #endif proc_register(&proc_root, &(struct proc_dir_entry) { PROC_STAT, 4, "stat", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_DEVICES, 7, "devices", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_INTERRUPTS, 10,"interrupts", S_IFREG | S_IRUGO, 1, 0, 0, }); #ifdef __SMP_PROF__ proc_register(&proc_root, &(struct proc_dir_entry) { PROC_SMP_PROF, 3,"smp", S_IFREG | S_IRUGO, 1, 0, 0, }); #endif proc_register(&proc_root, &(struct proc_dir_entry) { PROC_FILESYSTEMS, 11,"filesystems", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_DMA, 3, "dma", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_IOPORTS, 7, "ioports", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register(&proc_root, &(struct proc_dir_entry) { PROC_CMDLINE, 7, "cmdline", S_IFREG | S_IRUGO, 1, 0, 0, }); #ifdef CONFIG_RTC proc_register(&proc_root, &(struct proc_dir_entry) { PROC_RTC, 3, "rtc", S_IFREG | S_IRUGO, 1, 0, 0, }); #endif proc_register(&proc_root, &(struct proc_dir_entry) { PROC_LOCKS, 5, "locks", S_IFREG | S_IRUGO, 1, 0, 0, }); proc_register( &proc_root, &(struct proc_dir_entry) { PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, } ); if (prof_shift) { proc_register(&proc_root, &(struct proc_dir_entry) { PROC_PROFILE, 7, "profile", S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, }); } } int proc_match(int len,const char * name,struct proc_dir_entry * de) { 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; return !memcmp(name, de->name, len); } int proc_lookup(struct inode * dir,const char * name, int len, struct inode ** result) { struct proc_dir_entry * de; int ino; *result = NULL; if (!dir || !S_ISDIR(dir->i_mode)) { iput(dir); return -ENOTDIR; } de = (struct proc_dir_entry *) dir->u.generic_ip; if (!de) { iput(dir); return -EINVAL; } /* Special case "." and "..": they aren't on the directory list */ *result = dir; if (!len) return 0; if (name[0] == '.') { if (len == 1) return 0; if (name[1] == '.' && len == 2) { struct inode * inode; inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent); iput(dir); if (!inode) return -EINVAL; *result = inode; return 0; } } *result = NULL; for (de = de->subdir; de ; de = de->next) { if (proc_match(len, name, de)) break; } if (!de) { iput(dir); return -ENOENT; } ino = de->low_ino | (dir->i_ino & ~(0xffff)); if (!(*result = proc_get_inode(dir->i_sb, ino, de))) { iput(dir); return -EINVAL; } iput(dir); return 0; } static int proc_root_lookup(struct inode * dir,const char * name, int len, struct inode ** result) { unsigned int pid, c; int i, ino, retval; dir->i_count++; retval = proc_lookup(dir, name, len, result); if (retval != -ENOENT) { iput(dir); return retval; } pid = 0; while (len-- > 0) { c = *name - '0'; name++; if (c > 9) { pid = 0; break; } pid *= 10; pid += c; if (pid & 0xffff0000) { pid = 0; break; } } for (i = 0 ; i < NR_TASKS ; i++) if (task[i] && task[i]->pid == pid) break; if (!pid || i >= NR_TASKS) { iput(dir); return -ENOENT; } ino = (pid << 16) + PROC_PID_INO; if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) { iput(dir); return -EINVAL; } iput(dir); return 0; } /* * This returns non-zero if at EOF, so that the /proc * root directory can use this and check if it should * continue with the <pid> entries.. * * Note that the VFS-layer doesn't care about the return * value of the readdir() call, as long as it's non-negative * for success.. */ int proc_readdir(struct inode * inode, struct file * filp, void * dirent, filldir_t filldir) { struct proc_dir_entry * de; unsigned int ino; int i; if (!inode || !S_ISDIR(inode->i_mode)) return -ENOTDIR; ino = inode->i_ino; de = (struct proc_dir_entry *) inode->u.generic_ip; if (!de) return -EINVAL; i = filp->f_pos; switch (i) { case 0: if (filldir(dirent, ".", 1, i, ino) < 0) return 0; i++; filp->f_pos++; /* fall through */ case 1: if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0) return 0; i++; filp->f_pos++; /* fall through */ default: ino &= ~0xffff; de = de->subdir; i -= 2; for (;;) { if (!de) return 1; if (!i) break; de = de->next; i--; } do { if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino | de->low_ino) < 0) return 0; filp->f_pos++; de = de->next; } while (de); } return 1; } #define NUMBUF 10 static int proc_root_readdir(struct inode * inode, struct file * filp, void * dirent, filldir_t filldir) { char buf[NUMBUF]; unsigned int nr,pid; unsigned long i,j; nr = filp->f_pos; if (nr < FIRST_PROCESS_ENTRY) { int error = proc_readdir(inode, filp, dirent, filldir); if (error <= 0) return error; filp->f_pos = nr = FIRST_PROCESS_ENTRY; } for (nr -= FIRST_PROCESS_ENTRY; nr < NR_TASKS; nr++, filp->f_pos++) { struct task_struct * p = task[nr]; if (!p || !(pid = p->pid)) continue; j = NUMBUF; i = pid; do { j--; buf[j] = '0' + (i % 10); i /= 10; } while (i); if (filldir(dirent, buf+j, NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0) break; } return 0; }