diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-01-29 01:41:54 +0000 |
commit | f969d69ba9f952e5bdd38278e25e26a3e4a61a70 (patch) | |
tree | b3530d803df59d726afaabebc6626987dee1ca05 /fs/proc | |
parent | a10ce7ef2066b455d69187643ddf2073bfc4db24 (diff) |
Merge with 2.3.27.
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/Makefile | 4 | ||||
-rw-r--r-- | fs/proc/array.c | 1166 | ||||
-rw-r--r-- | fs/proc/base.c | 1097 | ||||
-rw-r--r-- | fs/proc/fd.c | 199 | ||||
-rw-r--r-- | fs/proc/generic.c | 27 | ||||
-rw-r--r-- | fs/proc/inode-alloc.txt | 46 | ||||
-rw-r--r-- | fs/proc/inode.c | 199 | ||||
-rw-r--r-- | fs/proc/kcore.c | 388 | ||||
-rw-r--r-- | fs/proc/link.c | 205 | ||||
-rw-r--r-- | fs/proc/mem.c | 354 | ||||
-rw-r--r-- | fs/proc/net.c | 124 | ||||
-rw-r--r-- | fs/proc/proc_misc.c | 692 | ||||
-rw-r--r-- | fs/proc/procfs_syms.c | 19 | ||||
-rw-r--r-- | fs/proc/root.c | 585 | ||||
-rw-r--r-- | fs/proc/scsi.c | 219 | ||||
-rw-r--r-- | fs/proc/sysvipc.c | 141 |
16 files changed, 2207 insertions, 3258 deletions
diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 8a5286fa5..8ff6cd522 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -8,8 +8,8 @@ # Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := proc.o -O_OBJS := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \ - kmsg.o scsi.o proc_tty.o sysvipc.o +O_OBJS := inode.o root.o base.o generic.o array.o \ + kmsg.o proc_tty.o proc_misc.o kcore.o ifdef CONFIG_OMIRR O_OBJS := $(O_OBJS) omirr.o endif diff --git a/fs/proc/array.c b/fs/proc/array.c index 249abd8cd..181cc4e62 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -46,6 +46,10 @@ * * Gerhard Wichert : added BIGMEM support * Siemens AG <Gerhard.Wichert@pdb.siemens.de> + * + * Al Viro & Jeff Garzik : moved most of the thing into base.c and + * : proc_misc.c. The rest may eventually go into + * : base.c too. */ #include <linux/types.h> @@ -54,8 +58,6 @@ #include <linux/kernel.h> #include <linux/kernel_stat.h> #include <linux/tty.h> -#include <linux/user.h> -#include <linux/a.out.h> #include <linux/string.h> #include <linux/mman.h> #include <linux/proc_fs.h> @@ -67,663 +69,12 @@ #include <linux/slab.h> #include <linux/smp.h> #include <linux/signal.h> +#include <linux/highmem.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/io.h> - -#define LOAD_INT(x) ((x) >> FSHIFT) -#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) - -#ifdef CONFIG_DEBUG_MALLOC -int get_malloc(char * buffer); -#endif - - -static int open_kcore(struct inode * inode, struct file * filp) -{ - return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; -} - -static ssize_t read_core(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - unsigned long p = *ppos, memsize; - ssize_t read; - ssize_t count1; - char * pnt; - struct user dump; -#if defined (__i386__) || defined (__mc68000__) -# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */ -#else -# define FIRST_MAPPED 0 -#endif - - memset(&dump, 0, sizeof(struct user)); - dump.magic = CMAGIC; - dump.u_dsize = max_mapnr; -#if defined (__i386__) - dump.start_code = PAGE_OFFSET; -#endif -#ifdef __alpha__ - dump.start_data = PAGE_OFFSET; -#endif - - memsize = (max_mapnr + 1) << PAGE_SHIFT; - if (p >= memsize) - return 0; - if (count > memsize - p) - count = memsize - p; - read = 0; - - if (p < sizeof(struct user) && count > 0) { - count1 = count; - if (p + count1 > sizeof(struct user)) - count1 = sizeof(struct user)-p; - pnt = (char *) &dump + p; - copy_to_user(buf,(void *) pnt, count1); - buf += count1; - p += count1; - count -= count1; - read += count1; - } - - if (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) { - count1 = PAGE_SIZE + FIRST_MAPPED - p; - if (count1 > count) - count1 = count; - clear_user(buf, count1); - buf += count1; - p += count1; - count -= count1; - read += count1; - } - if (count > 0) { - copy_to_user(buf, (void *) (PAGE_OFFSET+p-PAGE_SIZE), count); - read += count; - } - *ppos += read; - return read; -} - -static struct file_operations proc_kcore_operations = { - NULL, /* lseek */ - read_core, - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - open_kcore -}; - -struct inode_operations proc_kcore_inode_operations = { - &proc_kcore_operations, -}; - -/* - * This function accesses profiling information. The returned data is - * binary: the sampling step and the actual contents of the profile - * buffer. Use of the program readprofile is recommended in order to - * get meaningful info out of these data. - */ -static ssize_t read_profile(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - unsigned long p = *ppos; - ssize_t read; - char * pnt; - unsigned int sample_step = 1 << prof_shift; - - if (p >= (prof_len+1)*sizeof(unsigned int)) - return 0; - if (count > (prof_len+1)*sizeof(unsigned int) - p) - count = (prof_len+1)*sizeof(unsigned int) - p; - read = 0; - - while (p < sizeof(unsigned int) && count > 0) { - put_user(*((char *)(&sample_step)+p),buf); - buf++; p++; count--; read++; - } - pnt = (char *)prof_buffer + p - sizeof(unsigned int); - copy_to_user(buf,(void *)pnt,count); - read += count; - *ppos += read; - return read; -} - -/* - * Writing to /proc/profile resets the counters - * - * Writing a 'profiling multiplier' value into it also re-sets the profiling - * interrupt frequency, on architectures that support this. - */ -static ssize_t write_profile(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ -#ifdef __SMP__ - extern int setup_profiling_timer (unsigned int multiplier); - - if (count==sizeof(int)) { - unsigned int multiplier; - - if (copy_from_user(&multiplier, buf, sizeof(int))) - return -EFAULT; - - if (setup_profiling_timer(multiplier)) - return -EINVAL; - } -#endif - - memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer)); - return count; -} - -static struct file_operations proc_profile_operations = { - NULL, /* lseek */ - read_profile, - write_profile, -}; - -struct inode_operations proc_profile_inode_operations = { - &proc_profile_operations, -}; - - -static int get_loadavg(char * buffer) -{ - int a, b, c; - - a = avenrun[0] + (FIXED_1/200); - b = avenrun[1] + (FIXED_1/200); - c = avenrun[2] + (FIXED_1/200); - return sprintf(buffer,"%d.%02d %d.%02d %d.%02d %d/%d %d\n", - LOAD_INT(a), LOAD_FRAC(a), - LOAD_INT(b), LOAD_FRAC(b), - LOAD_INT(c), LOAD_FRAC(c), - nr_running, nr_threads, last_pid); -} - -static int get_kstat(char * buffer) -{ - int i, len; - unsigned sum = 0; - extern unsigned long total_forks; - unsigned long jif = HZ_TO_STD(jiffies); - - for (i = 0 ; i < NR_IRQS ; i++) - sum += kstat_irqs(i); - -#ifdef __SMP__ - len = sprintf(buffer, - "cpu %u %u %u %lu\n", - kstat.cpu_user, - kstat.cpu_nice, - kstat.cpu_system, - jif*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), - for (i = 0 ; i < smp_num_cpus; i++) - len += sprintf(buffer + len, "cpu%d %u %u %u %lu\n", - i, - kstat.per_cpu_user[cpu_logical_map(i)], - kstat.per_cpu_nice[cpu_logical_map(i)], - kstat.per_cpu_system[cpu_logical_map(i)], - jif - ( kstat.per_cpu_user[cpu_logical_map(i)] \ - + kstat.per_cpu_nice[cpu_logical_map(i)] \ - + kstat.per_cpu_system[cpu_logical_map(i)])); - len += sprintf(buffer + len, - "disk %u %u %u %u\n" - "disk_rio %u %u %u %u\n" - "disk_wio %u %u %u %u\n" - "disk_rblk %u %u %u %u\n" - "disk_wblk %u %u %u %u\n" - "page %u %u\n" - "swap %u %u\n" - "intr %u", -#else - len = sprintf(buffer, - "cpu %u %u %u %lu\n" - "disk %u %u %u %u\n" - "disk_rio %u %u %u %u\n" - "disk_wio %u %u %u %u\n" - "disk_rblk %u %u %u %u\n" - "disk_wblk %u %u %u %u\n" - "page %u %u\n" - "swap %u %u\n" - "intr %u", - HZ_TO_STD(kstat.cpu_user), - HZ_TO_STD(kstat.cpu_nice), - HZ_TO_STD(kstat.cpu_system), - jif*smp_num_cpus - HZ_TO_STD(kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), -#endif - kstat.dk_drive[0], kstat.dk_drive[1], - kstat.dk_drive[2], kstat.dk_drive[3], - kstat.dk_drive_rio[0], kstat.dk_drive_rio[1], - kstat.dk_drive_rio[2], kstat.dk_drive_rio[3], - kstat.dk_drive_wio[0], kstat.dk_drive_wio[1], - kstat.dk_drive_wio[2], kstat.dk_drive_wio[3], - kstat.dk_drive_rblk[0], kstat.dk_drive_rblk[1], - kstat.dk_drive_rblk[2], kstat.dk_drive_rblk[3], - kstat.dk_drive_wblk[0], kstat.dk_drive_wblk[1], - kstat.dk_drive_wblk[2], kstat.dk_drive_wblk[3], - kstat.pgpgin, - kstat.pgpgout, - kstat.pswpin, - kstat.pswpout, - sum); - for (i = 0 ; i < NR_IRQS ; i++) - len += sprintf(buffer + len, " %u", kstat_irqs(i)); - len += sprintf(buffer + len, - "\nctxt %u\n" - "btime %lu\n" - "processes %lu\n", - kstat.context_swtch, - xtime.tv_sec - jif / HZ, - total_forks); - return len; -} - - -static int get_uptime(char * buffer) -{ - unsigned long uptime; - unsigned long idle; - - uptime = jiffies; - idle = init_tasks[0]->times.tms_utime + init_tasks[0]->times.tms_stime; - - /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but - that would overflow about every five days at HZ == 100. - Therefore the identity a = (a / b) * b + a % b is used so that it is - calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100. - The part in front of the '+' always evaluates as 0 (mod 100). All divisions - in the above formulas are truncating. For HZ being a power of 10, the - calculations simplify to the version in the #else part (if the printf - format is adapted to the same number of digits as zeroes in HZ. - */ -#if HZ!=100 - return sprintf(buffer,"%lu.%02lu %lu.%02lu\n", - uptime / HZ, - (((uptime % HZ) * 100) / HZ) % 100, - idle / HZ, - (((idle % HZ) * 100) / HZ) % 100); -#else - return sprintf(buffer,"%lu.%02lu %lu.%02lu\n", - uptime / HZ, - uptime % HZ, - idle / HZ, - idle % HZ); -#endif -} - -static int get_meminfo(char * buffer) -{ - struct sysinfo i; - int len; - -/* - * display in kilobytes. - */ -#define K(x) ((x) << (PAGE_SHIFT - 10)) - - si_meminfo(&i); - si_swapinfo(&i); - len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" - "Mem: %8lu %8lu %8lu %8lu %8lu %8u\n" - "Swap: %8lu %8lu %8lu\n", - K(i.totalram), K(i.totalram-i.freeram), K(i.freeram), - K(i.sharedram), K(i.bufferram), - K(atomic_read(&page_cache_size)), K(i.totalswap), - K(i.totalswap-i.freeswap), K(i.freeswap)); - /* - * Tagged format, for easy grepping and expansion. - * The above will go away eventually, once the tools - * have been updated. - */ - return len + sprintf(buffer+len, - "MemTotal: %8lu kB\n" - "MemFree: %8lu kB\n" - "MemShared: %8lu kB\n" - "Buffers: %8lu kB\n" - "Cached: %8u kB\n" - "HighTotal: %8lu kB\n" - "HighFree: %8lu kB\n" - "SwapTotal: %8lu kB\n" - "SwapFree: %8lu kB\n", - K(i.totalram), - K(i.freeram), - K(i.sharedram), - K(i.bufferram), - K(atomic_read(&page_cache_size)), - K(i.totalhigh), - K(i.freehigh), - K(i.totalswap), - K(i.freeswap)); -#undef K -} - -static int get_version(char * buffer) -{ - extern char *linux_banner; - - strcpy(buffer, linux_banner); - return strlen(buffer); -} - -static int get_cmdline(char * buffer) -{ - extern char saved_command_line[]; - - return sprintf(buffer, "%s\n", saved_command_line); -} - -static struct page * get_phys_addr(struct mm_struct * mm, unsigned long ptr) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t pte; - - if (ptr >= TASK_SIZE) - return 0; - pgd = pgd_offset(mm,ptr); - if (pgd_none(*pgd)) - return 0; - if (pgd_bad(*pgd)) { - pgd_ERROR(*pgd); - pgd_clear(pgd); - return 0; - } - pmd = pmd_offset(pgd,ptr); - if (pmd_none(*pmd)) - return 0; - if (pmd_bad(*pmd)) { - pmd_ERROR(*pmd); - pmd_clear(pmd); - return 0; - } - pte = *pte_offset(pmd,ptr); - if (!pte_present(pte)) - return 0; - return pte_page(pte); -} - -static int get_array(struct mm_struct *mm, unsigned long start, unsigned long end, char * buffer) -{ - struct page *page; - unsigned long kaddr; - int size = 0, result = 0; - char c; - - if (start >= end) - return result; - for (;;) { - page = get_phys_addr(mm, start); - if (!page) - return result; - kaddr = kmap(page, KM_READ) + (start & ~PAGE_MASK); - do { - c = *(char *) kaddr; - if (!c) - result = size; - if (size < PAGE_SIZE) - buffer[size++] = c; - else { - kunmap(kaddr, KM_READ); - return result; - } - kaddr++; - start++; - if (!c && start >= end) { - kunmap(kaddr, KM_READ); - return result; - } - } while (kaddr & ~PAGE_MASK); - kunmap(kaddr, KM_READ); - } - return result; -} - -static struct mm_struct *get_mm(int pid) -{ - struct task_struct *p; - struct mm_struct *mm = NULL; - - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (p) - mm = p->mm; - if (mm) - atomic_inc(&mm->mm_users); - read_unlock(&tasklist_lock); - return mm; -} - - -static int get_env(int pid, char * buffer) -{ - struct mm_struct *mm = get_mm(pid); - int res = 0; - if (mm) { - res = get_array(mm, mm->env_start, mm->env_end, buffer); - mmput(mm); - } - return res; -} - -static int get_arg(int pid, char * buffer) -{ - struct mm_struct *mm = get_mm(pid); - int res = 0; - if (mm) { - res = get_array(mm, mm->arg_start, mm->arg_end, buffer); - mmput(mm); - } - return res; -} - -/* - * These bracket the sleeping functions.. - */ -extern void scheduling_functions_start_here(void); -extern void scheduling_functions_end_here(void); -#define first_sched ((unsigned long) scheduling_functions_start_here) -#define last_sched ((unsigned long) scheduling_functions_end_here) - -static unsigned long get_wchan(struct task_struct *p) -{ - if (!p || p == current || p->state == TASK_RUNNING) - return 0; -#if defined(__i386__) - { - unsigned long ebp, esp, eip; - unsigned long stack_page; - int count = 0; - - stack_page = (unsigned long)p; - esp = p->thread.esp; - if (!stack_page || esp < stack_page || esp > 8188+stack_page) - return 0; - /* include/asm-i386/system.h:switch_to() pushes ebp last. */ - ebp = *(unsigned long *) esp; - do { - if (ebp < stack_page || ebp > 8184+stack_page) - return 0; - eip = *(unsigned long *) (ebp+4); - if (eip < first_sched || eip >= last_sched) - return eip; - ebp = *(unsigned long *) ebp; - } while (count++ < 16); - } -#elif defined(__alpha__) - /* - * This one depends on the frame size of schedule(). Do a - * "disass schedule" in gdb to find the frame size. Also, the - * code assumes that sleep_on() follows immediately after - * interruptible_sleep_on() and that add_timer() follows - * immediately after interruptible_sleep(). Ugly, isn't it? - * Maybe adding a wchan field to task_struct would be better, - * after all... - */ - { - unsigned long schedule_frame; - unsigned long pc; - - pc = thread_saved_pc(&p->thread); - if (pc >= first_sched && pc < last_sched) { - schedule_frame = ((unsigned long *)p->thread.ksp)[6]; - return ((unsigned long *)schedule_frame)[12]; - } - return pc; - } -#elif defined(__mips__) - /* - * The same comment as on the Alpha applies here, too ... - */ - { - unsigned long schedule_frame; - unsigned long pc; - - pc = thread_saved_pc(&p->thread); - if (pc == (unsigned long) interruptible_sleep_on - || pc == (unsigned long) sleep_on) { - schedule_frame = ((unsigned long *)p->thread.reg30)[9]; - return ((unsigned long *)schedule_frame)[15]; - } - if (pc == (unsigned long) interruptible_sleep_on_timeout - || pc == (unsigned long) sleep_on_timeout) { - schedule_frame = ((unsigned long *)p->thread.reg30)[9]; - return ((unsigned long *)schedule_frame)[16]; - } - if (pc >= first_sched && pc < last_sched) { - printk(KERN_DEBUG "Bug in %s\n", __FUNCTION__); - } - - return pc; - } -#elif defined(__mc68000__) - { - unsigned long fp, pc; - unsigned long stack_page; - int count = 0; - - stack_page = (unsigned long)p; - fp = ((struct switch_stack *)p->thread.ksp)->a6; - do { - if (fp < stack_page+sizeof(struct task_struct) || - fp >= 8184+stack_page) - return 0; - pc = ((unsigned long *)fp)[1]; - /* FIXME: This depends on the order of these functions. */ - if (pc < first_sched || pc >= last_sched) - return pc; - fp = *(unsigned long *) fp; - } while (count++ < 16); - } -#elif defined(__powerpc__) - { - unsigned long ip, sp; - unsigned long stack_page = (unsigned long) p; - int count = 0; - - sp = p->thread.ksp; - do { - sp = *(unsigned long *)sp; - if (sp < stack_page || sp >= stack_page + 8188) - return 0; - if (count > 0) { - ip = *(unsigned long *)(sp + 4); - if (ip < first_sched || ip >= last_sched) - return ip; - } - } while (count++ < 16); - } -#elif defined(__arm__) - { - unsigned long fp, lr; - unsigned long stack_page; - int count = 0; - - stack_page = 4096 + (unsigned long)p; - fp = get_css_fp (&p->tss); - do { - if (fp < stack_page || fp > 4092+stack_page) - return 0; - lr = pc_pointer (((unsigned long *)fp)[-1]); - if (lr < first_sched || lr > last_sched) - return lr; - fp = *(unsigned long *) (fp - 12); - } while (count ++ < 16); - } -#elif defined (__sparc__) - { - unsigned long pc, fp, bias = 0; - unsigned long task_base = (unsigned long) p; - struct reg_window *rw; - int count = 0; - -#ifdef __sparc_v9__ - bias = STACK_BIAS; -#endif - fp = p->thread.ksp + bias; - do { - /* Bogus frame pointer? */ - if (fp < (task_base + sizeof(struct task_struct)) || - fp >= (task_base + (2 * PAGE_SIZE))) - break; - rw = (struct reg_window *) fp; - pc = rw->ins[7]; - if (pc < first_sched || pc >= last_sched) - return pc; - fp = rw->ins[6] + bias; - } while (++count < 16); - } -#endif - - return 0; -} - -#if defined(__i386__) -# define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1019]) -# define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) -#elif defined(__alpha__) - /* - * See arch/alpha/kernel/ptrace.c for details. - */ -# define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \ - + (long)&((struct pt_regs *)0)->reg) -# define KSTK_EIP(tsk) \ - (*(unsigned long *)(PT_REG(pc) + PAGE_SIZE + (unsigned long)(tsk))) -# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) -#elif defined(__arm__) -# define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1022]) -# define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)))[1020]) -#elif defined(__mc68000__) -#define KSTK_EIP(tsk) \ - ({ \ - unsigned long eip = 0; \ - if ((tsk)->thread.esp0 > PAGE_SIZE && \ - MAP_NR((tsk)->thread.esp0) < max_mapnr) \ - eip = ((struct pt_regs *) (tsk)->thread.esp0)->pc; \ - eip; }) -#define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->thread.usp) -#elif defined(__powerpc__) -#define KSTK_EIP(tsk) ((tsk)->thread.regs->nip) -#define KSTK_ESP(tsk) ((tsk)->thread.regs->gpr[1]) -#elif defined (__sparc_v9__) -# define KSTK_EIP(tsk) ((tsk)->thread.kregs->tpc) -# define KSTK_ESP(tsk) ((tsk)->thread.kregs->u_regs[UREG_FP]) -#elif defined(__sparc__) -# define KSTK_EIP(tsk) ((tsk)->thread.kregs->pc) -# define KSTK_ESP(tsk) ((tsk)->thread.kregs->u_regs[UREG_FP]) -#elif defined(__mips__) -# define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg \ - - sizeof(struct pt_regs)) -#define KSTK_TOS(tsk) ((unsigned long)(tsk) + KERNEL_STACK_SIZE - 32) -# define KSTK_EIP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(cp0_epc))) -# define KSTK_ESP(tsk) (*(unsigned long *)(KSTK_TOS(tsk) + PT_REG(regs[29]))) -#elif defined(__sh__) -# define KSTK_EIP(tsk) ((tsk)->thread.pc) -# define KSTK_ESP(tsk) ((tsk)->thread.sp) -#endif +#include <asm/processor.h> /* Gcc optimizes away "strlen(x)" for constant x */ #define ADDBUF(buffer, string) \ @@ -913,36 +264,27 @@ extern inline char *task_cap(struct task_struct *p, char *buffer) } -static int get_status(int pid, char * buffer) +/* task is locked, so we are safe here */ + +int proc_pid_status(struct task_struct *task, char * buffer) { char * orig = buffer; - struct task_struct *tsk; - struct mm_struct *mm = NULL; + struct mm_struct *mm = task->mm; - read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - if (tsk) - mm = tsk->mm; - if (mm) - atomic_inc(&mm->mm_users); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - if (!tsk) - return 0; - buffer = task_name(tsk, buffer); - buffer = task_state(tsk, buffer); + buffer = task_name(task, buffer); + buffer = task_state(task, buffer); if (mm) buffer = task_mem(mm, buffer); - buffer = task_sig(tsk, buffer); - buffer = task_cap(tsk, buffer); - if (mm) - mmput(mm); + buffer = task_sig(task, buffer); + buffer = task_cap(task, buffer); return buffer - orig; } -static int get_stat(int pid, char * buffer) +/* task is locked, so we are safe here */ + +int proc_pid_stat(struct task_struct *task, char * buffer) { - struct task_struct *tsk; - struct mm_struct *mm = NULL; + struct mm_struct *mm = task->mm; unsigned long vsize, eip, esp, wchan; long priority, nice; int tty_pgrp; @@ -950,16 +292,7 @@ static int get_stat(int pid, char * buffer) char state; int res; - read_lock(&tasklist_lock); - tsk = find_task_by_pid(pid); - if (tsk) - mm = tsk->mm; - if (mm) - atomic_inc(&mm->mm_users); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - if (!tsk) - return 0; - state = *get_task_state(tsk); + state = *get_task_state(task); vsize = eip = esp = 0; if (mm) { struct vm_area_struct *vma; @@ -969,55 +302,55 @@ static int get_stat(int pid, char * buffer) vsize += vma->vm_end - vma->vm_start; vma = vma->vm_next; } - eip = KSTK_EIP(tsk); - esp = KSTK_ESP(tsk); + eip = KSTK_EIP(task); + esp = KSTK_ESP(task); up(&mm->mmap_sem); } - wchan = get_wchan(tsk); + wchan = get_wchan(task); - collect_sigign_sigcatch(tsk, &sigign, &sigcatch); + collect_sigign_sigcatch(task, &sigign, &sigcatch); - if (tsk->tty) - tty_pgrp = tsk->tty->pgrp; + if (task->tty) + tty_pgrp = task->tty->pgrp; else tty_pgrp = -1; /* scale priority and nice values from timeslices to -20..20 */ /* to make it look like a "normal" Unix priority/nice value */ - priority = tsk->counter; + priority = task->counter; priority = 20 - (priority * 10 + DEF_PRIORITY / 2) / DEF_PRIORITY; - nice = tsk->priority; + nice = task->priority; nice = 20 - (nice * 20 + DEF_PRIORITY / 2) / DEF_PRIORITY; res = sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %lu %lu %ld %lu %lu %lu %lu %lu \ %lu %lu %lu %lu %lu %lu %lu %lu %d %d\n", - pid, - tsk->comm, + task->pid, + task->comm, state, - tsk->p_pptr->pid, - tsk->pgrp, - tsk->session, - tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0, + task->p_pptr->pid, + task->pgrp, + task->session, + task->tty ? kdev_t_to_nr(task->tty->device) : 0, tty_pgrp, - tsk->flags, - tsk->min_flt, - tsk->cmin_flt, - tsk->maj_flt, - tsk->cmaj_flt, - HZ_TO_STD(tsk->times.tms_utime), - HZ_TO_STD(tsk->times.tms_stime), - HZ_TO_STD(tsk->times.tms_cutime), - HZ_TO_STD(tsk->times.tms_cstime), + task->flags, + task->min_flt, + task->cmin_flt, + task->maj_flt, + task->cmaj_flt, + task->times.tms_utime, + task->times.tms_stime, + task->times.tms_cutime, + task->times.tms_cstime, priority, nice, 0UL /* removed */, - tsk->it_real_value, - tsk->start_time, + task->it_real_value, + task->start_time, vsize, mm ? mm->rss : 0, /* you might want to shift this left 3 */ - tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0, + task->rlim ? task->rlim[RLIMIT_RSS].rlim_cur : 0, mm ? mm->start_code : 0, mm ? mm->end_code : 0, mm ? mm->start_stack : 0, @@ -1027,17 +360,15 @@ static int get_stat(int pid, char * buffer) * It must be decimal for Linux 2.0 compatibility. * Use /proc/#/status for real-time signals. */ - tsk->signal .sig[0] & 0x7fffffffUL, - tsk->blocked.sig[0] & 0x7fffffffUL, + task->signal .sig[0] & 0x7fffffffUL, + task->blocked.sig[0] & 0x7fffffffUL, sigign .sig[0] & 0x7fffffffUL, sigcatch .sig[0] & 0x7fffffffUL, wchan, - tsk->nswap, - tsk->cnswap, - tsk->exit_signal, - tsk->processor); - if (mm) - mmput(mm); + task->nswap, + task->cnswap, + task->exit_signal, + task->processor); return res; } @@ -1072,9 +403,9 @@ static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned ++*pages; if (pte_dirty(page)) ++*dirty; - if (MAP_NR(pte_page(page)) >= max_mapnr) + if (pte_pagenr(page) >= max_mapnr) continue; - if (page_count(mem_map + MAP_NR(pte_page(page))) > 1) + if (page_count(pte_page(page)) > 1) ++*shared; } while (address < end); } @@ -1114,9 +445,9 @@ static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long en } } -static int get_statm(int pid, char * buffer) +int proc_pid_statm(struct task_struct *task, char * buffer) { - struct mm_struct *mm = get_mm(pid); + struct mm_struct *mm = task->mm; int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0; if (mm) { @@ -1143,7 +474,6 @@ static int get_statm(int pid, char * buffer) vma = vma->vm_next; } up(&mm->mmap_sem); - mmput(mm); } return sprintf(buffer,"%d %d %d %d %d %d %d\n", size, resident, share, trs, lrs, drs, dt); @@ -1182,10 +512,10 @@ static int get_statm(int pid, char * buffer) #define MAPS_LINE_MAX MAPS_LINE_MAX8 -static ssize_t read_maps (int pid, struct file * file, char * buf, +ssize_t proc_pid_read_maps (struct task_struct *task, struct file * file, char * buf, size_t count, loff_t *ppos) { - struct task_struct *p; + struct mm_struct *mm = task->mm; struct vm_area_struct * map, * next; char * destptr = buf, * buffer; loff_t lineno; @@ -1201,25 +531,19 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, if (!buffer) goto out; - retval = -EINVAL; - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - if (!p) - goto freepage_out; - - if (!p->mm || count == 0) + if (!mm || count == 0) goto getlen_out; /* Check whether the mmaps could change if we sleep */ - volatile_task = (p != current || atomic_read(&p->mm->mm_users) > 1); + volatile_task = (task != current || atomic_read(&mm->mm_users) > 1); /* decode f_pos */ lineno = *ppos >> MAPS_LINE_SHIFT; column = *ppos & (MAPS_LINE_LENGTH-1); /* quickly go to line lineno */ - for (map = p->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) + down(&mm->mmap_sem); + for (map = mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) continue; for ( ; map ; map = next ) { @@ -1260,7 +584,7 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, len = sprintf(line, sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8, - map->vm_start, map->vm_end, str, map->vm_offset, + map->vm_start, map->vm_end, str, map->vm_pgoff << PAGE_SHIFT, kdevname(dev), ino); if(map->vm_file) { @@ -1278,7 +602,9 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, i = len-column; if (i > count) i = count; + up(&mm->mmap_sem); copy_to_user(destptr, line+column, i); /* may have slept */ + down(&mm->mmap_sem); destptr += i; count -= i; column += i; @@ -1297,382 +623,34 @@ static ssize_t read_maps (int pid, struct file * file, char * buf, if (volatile_task) break; } + up(&mm->mmap_sem); /* encode f_pos */ *ppos = (lineno << MAPS_LINE_SHIFT) + column; getlen_out: retval = destptr - buf; - -freepage_out: free_page((unsigned long)buffer); out: return retval; } #ifdef __SMP__ -static int get_pidcpu(int pid, char * buffer) +int proc_pid_cpu(struct task_struct *task, char * buffer) { - struct task_struct * tsk = current ; - int i, len = 0; - - read_lock(&tasklist_lock); - if (pid != tsk->pid) - tsk = find_task_by_pid(pid); - read_unlock(&tasklist_lock); /* FIXME!! This should be done after the last use */ - - if (tsk == NULL) - return 0; + int i, len; len = sprintf(buffer, "cpu %lu %lu\n", - HZ_TO_STD(tsk->times.tms_utime), - HZ_TO_STD(tsk->times.tms_stime)); - + task->times.tms_utime, + task->times.tms_stime); + for (i = 0 ; i < smp_num_cpus; i++) len += sprintf(buffer + len, "cpu%d %lu %lu\n", i, - HZ_TO_STD(tsk->per_cpu_utime[cpu_logical_map(i)]), - HZ_TO_STD(tsk->per_cpu_stime[cpu_logical_map(i)])); + task->per_cpu_utime[cpu_logical_map(i)], + task->per_cpu_stime[cpu_logical_map(i)]); return len; } #endif - -#ifdef CONFIG_MODULES -extern int get_module_list(char *); -extern int get_ksyms_list(char *, char **, off_t, int); -#endif -extern int get_device_list(char *); -extern int get_partition_list(char *); -extern int get_filesystem_list(char *); -extern int get_filesystem_info( char * ); -extern int get_irq_list(char *); -extern int get_dma_list(char *); -extern int get_cpuinfo(char *); -extern int get_pci_list(char *); -extern int get_md_status (char *); -extern int get_rtc_status (char *); -extern int get_locks_status (char *, char **, off_t, int); -extern int get_swaparea_info (char *); -extern int get_hardware_list(char *); -extern int get_stram_list(char *); - -static long get_root_array(char * page, int type, char **start, - off_t offset, unsigned long length) -{ - switch (type) { - case PROC_LOADAVG: - return get_loadavg(page); - - case PROC_UPTIME: - return get_uptime(page); - - case PROC_MEMINFO: - return get_meminfo(page); - -#ifdef CONFIG_PCI - case PROC_PCI: - return get_pci_list(page); -#endif - -#ifdef CONFIG_NUBUS - case PROC_NUBUS: - return get_nubus_list(page); -#endif - - case PROC_CPUINFO: - return get_cpuinfo(page); - - case PROC_VERSION: - return get_version(page); - -#ifdef CONFIG_DEBUG_MALLOC - case PROC_MALLOC: - return get_malloc(page); -#endif - -#ifdef CONFIG_MODULES - case PROC_MODULES: - return get_module_list(page); - - case PROC_KSYMS: - return get_ksyms_list(page, start, offset, length); -#endif - - case PROC_STAT: - return get_kstat(page); - - case PROC_SLABINFO: - return get_slabinfo(page); - - case PROC_DEVICES: - return get_device_list(page); - - case PROC_PARTITIONS: - return get_partition_list(page); - - case PROC_INTERRUPTS: - return get_irq_list(page); - - case PROC_FILESYSTEMS: - return get_filesystem_list(page); - - case PROC_DMA: - return get_dma_list(page); - - case PROC_IOPORTS: - return get_ioport_list(page); - - case PROC_MEMORY: - return get_mem_list(page); -#ifdef CONFIG_BLK_DEV_MD - case PROC_MD: - return get_md_status(page); -#endif - case PROC_CMDLINE: - return get_cmdline(page); - - case PROC_MTAB: - return get_filesystem_info( page ); - - case PROC_SWAP: - return get_swaparea_info(page); -#ifdef CONFIG_RTC - case PROC_RTC: - return get_rtc_status(page); -#endif -#ifdef CONFIG_SGI_DS1286 - case PROC_RTC: - return get_ds1286_status(page); -#endif - case PROC_LOCKS: - return get_locks_status(page, start, offset, length); -#ifdef CONFIG_PROC_HARDWARE - case PROC_HARDWARE: - return get_hardware_list(page); -#endif -#ifdef CONFIG_STRAM_PROC - case PROC_STRAM: - return get_stram_list(page); -#endif - } - return -EBADF; -} - -static int process_unauthorized(int type, int pid) -{ - struct task_struct *p; - uid_t euid=0; /* Save the euid keep the lock short */ - int ok = 0; - - read_lock(&tasklist_lock); - - /* - * Grab the lock, find the task, save the uid and - * check it has an mm still (ie its not dead) - */ - - p = find_task_by_pid(pid); - if (p) { - euid=p->euid; - ok = p->dumpable; - if(!cap_issubset(p->cap_permitted, current->cap_permitted)) - ok=0; - } - - read_unlock(&tasklist_lock); - - if (!p) - return 1; - - switch(type) { - case PROC_PID_STATUS: - case PROC_PID_STATM: - case PROC_PID_STAT: - case PROC_PID_MAPS: - case PROC_PID_CMDLINE: - case PROC_PID_CPU: - return 0; - } - if(capable(CAP_DAC_OVERRIDE) || (current->fsuid == euid && ok)) - return 0; - return 1; -} - - -static int get_process_array(char * page, int pid, int type) -{ - switch (type) { - case PROC_PID_STATUS: - return get_status(pid, page); - case PROC_PID_ENVIRON: - return get_env(pid, page); - case PROC_PID_CMDLINE: - return get_arg(pid, page); - case PROC_PID_STAT: - return get_stat(pid, page); - case PROC_PID_STATM: - return get_statm(pid, page); -#ifdef __SMP__ - case PROC_PID_CPU: - return get_pidcpu(pid, page); -#endif - } - return -EBADF; -} - - -static inline int fill_array(char * page, int pid, int type, char **start, off_t offset, int length) -{ - if (pid) - return get_process_array(page, pid, type); - return get_root_array(page, type, start, offset, length); -} - -#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ - -static ssize_t array_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - unsigned long page; - char *start; - ssize_t length; - ssize_t end; - unsigned int type, pid; - struct proc_dir_entry *dp; - - if (count > PROC_BLOCK_SIZE) - count = PROC_BLOCK_SIZE; - if (!(page = __get_free_page(GFP_KERNEL))) - return -ENOMEM; - type = inode->i_ino; - pid = type >> 16; - type &= 0x0000ffff; - start = NULL; - dp = (struct proc_dir_entry *) inode->u.generic_ip; - - if (pid && process_unauthorized(type, pid)) { - free_page(page); - return -EIO; - } - - if (dp->get_info) - length = dp->get_info((char *)page, &start, *ppos, - count, 0); - else - length = fill_array((char *) page, pid, type, - &start, *ppos, count); - if (length < 0) { - free_page(page); - return length; - } - if (start != NULL) { - /* We have had block-adjusting processing! */ - copy_to_user(buf, start, length); - *ppos += length; - count = length; - } else { - /* Static 4kB (or whatever) block capacity */ - if (*ppos >= length) { - free_page(page); - return 0; - } - if (count + *ppos > length) - count = length - *ppos; - end = count + *ppos; - copy_to_user(buf, (char *) page + *ppos, count); - *ppos = end; - } - free_page(page); - return count; -} - -static struct file_operations proc_array_operations = { - NULL, /* array_lseek */ - array_read, - NULL, /* array_write */ - NULL, /* array_readdir */ - NULL, /* array_poll */ - NULL, /* array_ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -struct inode_operations proc_array_inode_operations = { - &proc_array_operations, /* default base directory file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -static ssize_t arraylong_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - unsigned int pid = inode->i_ino >> 16; - unsigned int type = inode->i_ino & 0x0000ffff; - - switch (type) { - case PROC_PID_MAPS: - return read_maps(pid, file, buf, count, ppos); - } - return -EINVAL; -} - -static struct file_operations proc_arraylong_operations = { - NULL, /* array_lseek */ - arraylong_read, - NULL, /* array_write */ - NULL, /* array_readdir */ - NULL, /* array_poll */ - NULL, /* array_ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -struct inode_operations proc_arraylong_inode_operations = { - &proc_arraylong_operations, /* default base directory file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; diff --git a/fs/proc/base.c b/fs/proc/base.c index f0a0febf8..866bb50d4 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -4,6 +4,13 @@ * Copyright (C) 1991, 1992 Linus Torvalds * * proc base directory handling functions + * + * 1999, Al Viro. Rewritten. Now it covers the whole per-process part. + * Instead of using magical inumbers to determine the kind of object + * we allocate and fill in-core inodes upon lookup. They don't even + * go into icache. We cache the reference to task_struct upon lookup too. + * Eventually it should become a filesystem in its own. We don't use the + * rest of procfs anymore. */ #include <asm/uaccess.h> @@ -14,28 +21,336 @@ #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/init.h> - -static struct file_operations proc_base_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - proc_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; +#include <linux/file.h> /* - * proc directories can do almost nothing.. + * For hysterical raisins we keep the same inumbers as in the old procfs. + * Feel free to change the macro below - just keep the range distinct from + * inumbers of the rest of procfs (currently those are in 0x0000--0xffff). + * As soon as we'll get a separate superblock we will be able to forget + * about magical ranges too. */ -static struct inode_operations proc_base_inode_operations = { - &proc_base_operations, /* default base directory file-ops */ + +#define fake_ino(pid,ino) (((pid)<<16)|(ino)) + +ssize_t proc_pid_read_maps(struct task_struct*,struct file*,char*,size_t,loff_t*); +int proc_pid_stat(struct task_struct*,char*); +int proc_pid_status(struct task_struct*,char*); +int proc_pid_statm(struct task_struct*,char*); +int proc_pid_cpu(struct task_struct*,char*); + +static struct dentry *proc_fd_link(struct inode *inode) +{ + if (inode->u.proc_i.file) + return dget(inode->u.proc_i.file->f_dentry); + return NULL; +} + +static struct dentry *proc_exe_link(struct inode *inode) +{ + struct mm_struct * mm; + struct vm_area_struct * vma; + struct dentry *result = NULL; + struct task_struct *task = inode->u.proc_i.task; + + if (!task_lock(task)) + return NULL; + mm = task->mm; + if (!mm) + goto out; + down(&mm->mmap_sem); + vma = mm->mmap; + while (vma) { + if ((vma->vm_flags & VM_EXECUTABLE) && + vma->vm_file) { + result = dget(vma->vm_file->f_dentry); + break; + } + vma = vma->vm_next; + } + up(&mm->mmap_sem); +out: + task_unlock(task); + return result; +} + +static struct dentry *proc_cwd_link(struct inode *inode) +{ + struct dentry *result = NULL; + if (task_lock(inode->u.proc_i.task)) { + struct fs_struct *fs = inode->u.proc_i.task->fs; + if (fs) + result = dget(fs->pwd); + task_unlock(inode->u.proc_i.task); + } + return result; +} + +static struct dentry *proc_root_link(struct inode *inode) +{ + struct dentry *result = NULL; + if (task_lock(inode->u.proc_i.task)) { + struct fs_struct *fs = inode->u.proc_i.task->fs; + if (fs) + result = dget(fs->root); + task_unlock(inode->u.proc_i.task); + } + return result; +} + +/* task is locked and can't drop mm, so we are safe */ + +static int proc_pid_environ(struct task_struct *task, char * buffer) +{ + struct mm_struct *mm = task->mm; + int res = 0; + if (mm) + res = access_process_vm(task, mm->env_start, buffer, + mm->env_end - mm->env_start, 0); + return res; +} + +/* task is locked and can't drop mm, so we are safe */ + +static int proc_pid_cmdline(struct task_struct *task, char * buffer) +{ + struct mm_struct *mm = task->mm; + int res = 0; + if (mm) + res = access_process_vm(task, mm->arg_start, buffer, + mm->arg_end - mm->arg_start, 0); + return res; +} + +/************************************************************************/ +/* Here the fs part begins */ +/************************************************************************/ + +/* permission checks */ + +static int standard_permission(struct inode *inode, int mask) +{ + int mode = inode->i_mode; + + if ((mask & S_IWOTH) && IS_RDONLY(inode) && + (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) + return -EROFS; /* Nobody gets write access to a read-only fs */ + else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) + return -EACCES; /* Nobody gets write access to an immutable file */ + else if (current->fsuid == inode->i_uid) + mode >>= 6; + else if (in_group_p(inode->i_gid)) + mode >>= 3; + if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE)) + return 0; + /* read and search access */ + if ((mask == S_IROTH) || + (S_ISDIR(mode) && !(mask & ~(S_IROTH | S_IXOTH)))) + if (capable(CAP_DAC_READ_SEARCH)) + return 0; + return -EACCES; +} + +static int proc_permission(struct inode *inode, int mask) +{ + struct dentry *de, *base, *root; + struct super_block *our_sb, *sb, *below; + + if (standard_permission(inode, mask) != 0) + return -EACCES; + + base = current->fs->root; + de = root = proc_root_link(inode); /* Ewww... */ + + if (!de) + return -ENOENT; + + our_sb = base->d_inode->i_sb; + sb = de->d_inode->i_sb; + while (sb != our_sb) { + de = sb->s_root->d_covers; + below = de->d_inode->i_sb; + if (sb == below) + goto out; + sb = below; + } + + if (!is_subdir(de, base)) + goto out; + + dput(root); + return 0; +out: + dput(root); + return -EACCES; +} + +static ssize_t pid_maps_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; + struct task_struct *task = inode->u.proc_i.task; + ssize_t res; + + if (!task_lock(task)) + return -EIO; + res = proc_pid_read_maps(task, file, buf, count, ppos); + task_unlock(task); + return res; +} + +static struct file_operations proc_maps_operations = { + NULL, /* array_lseek */ + pid_maps_read, +}; + +struct inode_operations proc_maps_inode_operations = { + &proc_maps_operations, /* default base directory file-ops */ +}; + +#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ + +static ssize_t proc_info_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; + unsigned long page; + ssize_t length; + ssize_t end; + struct task_struct *task = inode->u.proc_i.task; + + if (count > PROC_BLOCK_SIZE) + count = PROC_BLOCK_SIZE; + if (!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (!task_lock(task)) { + free_page(page); + return -EIO; + } + + length = inode->u.proc_i.op.proc_read(task, (char*)page); + + task_unlock(task); + + if (length < 0) { + free_page(page); + return length; + } + /* Static 4kB (or whatever) block capacity */ + if (*ppos >= length) { + free_page(page); + return 0; + } + if (count + *ppos > length) + count = length - *ppos; + end = count + *ppos; + copy_to_user(buf, (char *) page + *ppos, count); + *ppos = end; + free_page(page); + return count; +} + +static struct file_operations proc_info_file_operations = { + NULL, /* lseek */ + proc_info_read, /* read */ +}; + +static struct inode_operations proc_info_inode_operations = { + &proc_info_file_operations, /* default proc file-ops */ +}; + +#define MAY_PTRACE(p) \ +(p==current||(p->p_pptr==current&&(p->flags&PF_PTRACED)&&p->state==TASK_STOPPED)) + +static ssize_t mem_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task = file->f_dentry->d_inode->u.proc_i.task; + char *page; + unsigned long src = *ppos; + int copied = 0; + + if (!MAY_PTRACE(task)) + return -ESRCH; + + page = (char *)__get_free_page(GFP_USER); + if (!page) + return -ENOMEM; + + while (count > 0) { + int this_len, retval; + + this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; + retval = access_process_vm(task, src, page, this_len, 0); + if (!retval) { + if (!copied) + copied = -EIO; + break; + } + if (copy_to_user(buf, page, retval)) { + copied = -EFAULT; + break; + } + copied += retval; + src += retval; + buf += retval; + count -= retval; + } + *ppos = src; + free_page((unsigned long) page); + return copied; +} + +static ssize_t mem_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + int copied = 0; + char *page; + struct task_struct *task = file->f_dentry->d_inode->u.proc_i.task; + unsigned long dst = *ppos; + + if (!MAY_PTRACE(task)) + return -ESRCH; + + page = (char *)__get_free_page(GFP_USER); + if (!page) + return -ENOMEM; + + while (count > 0) { + int this_len, retval; + + this_len = (count > PAGE_SIZE) ? PAGE_SIZE : count; + if (copy_from_user(page, buf, this_len)) { + copied = -EFAULT; + break; + } + retval = access_process_vm(task, dst, page, this_len, 1); + if (!retval) { + if (!copied) + copied = -EIO; + break; + } + copied += retval; + buf += retval; + dst += retval; + count -= retval; + } + *ppos = dst; + free_page((unsigned long) page); + return copied; +} + +static struct file_operations proc_mem_operations = { + NULL, /* lseek - default */ + mem_read, + mem_write, +}; + +static struct inode_operations proc_mem_inode_operations = { + &proc_mem_operations, /* default base directory file-ops */ NULL, /* create */ - proc_lookup, /* lookup */ + NULL, /* lookup */ NULL, /* link */ NULL, /* unlink */ NULL, /* symlink */ @@ -50,161 +365,653 @@ static struct inode_operations proc_base_inode_operations = { NULL, /* writepage */ NULL, /* flushpage */ NULL, /* truncate */ - NULL, /* permission */ + proc_permission, /* permission */ NULL, /* smap */ NULL /* revalidate */ }; -/* - * The fill argument is non-zero when the inode is being filled ... - * we don't need to do anything when it's being deleted. - */ -static void proc_pid_fill_inode(struct inode * inode, int fill) +static struct dentry * proc_pid_follow_link(struct dentry *dentry, + struct dentry *base, + unsigned int follow) { - struct task_struct *p; - int pid = inode->i_ino >> 16; - int ino = inode->i_ino & 0xffff; + struct inode *inode = dentry->d_inode; + struct dentry * result; + int error; - read_lock(&tasklist_lock); - if (fill && (p = find_task_by_pid(pid)) != NULL) { - uid_t uid = 0; - gid_t gid = 0; - if (p->dumpable || ino == PROC_PID_INO) { - uid = p->euid; - gid = p->egid; - } - inode->i_uid = uid; - inode->i_gid = gid; + /* We don't need a base pointer in the /proc filesystem */ + dput(base); + + error = proc_permission(inode, MAY_EXEC); + result = ERR_PTR(error); + if (error) + goto out; + + result = inode->u.proc_i.op.proc_get_link(inode); +out: + if (!result) + result = ERR_PTR(-ENOENT); + return result; +} + +static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen) +{ + struct inode * inode; + char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern; + int len; + + if (!tmp) + return -ENOMEM; + + /* Check for special dentries.. */ + pattern = NULL; + inode = dentry->d_inode; + if (inode && IS_ROOT(dentry)) { + if (S_ISSOCK(inode->i_mode)) + pattern = "socket:[%lu]"; + if (S_ISFIFO(inode->i_mode)) + pattern = "pipe:[%lu]"; } - read_unlock(&tasklist_lock); + + if (pattern) { + len = sprintf(tmp, pattern, inode->i_ino); + path = tmp; + } else { + path = d_path(dentry, tmp, PAGE_SIZE); + len = tmp + PAGE_SIZE - 1 - path; + } + + if (len < buflen) + buflen = len; + copy_to_user(buffer, path, buflen); + free_page((unsigned long)tmp); + return buflen; } -/* - * This is really a pseudo-entry, and only links - * backwards to the parent with no link from the - * root directory to this. This way we can have just - * one entry for every /proc/<pid>/ directory. - */ -struct proc_dir_entry proc_pid = { - PROC_PID_INO, 5, "<pid>", - S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, - 0, &proc_base_inode_operations, - NULL, proc_pid_fill_inode, - NULL, &proc_root, NULL -}; - -static struct proc_dir_entry proc_pid_status = { - PROC_PID_STATUS, 6, "status", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_mem = { - PROC_PID_MEM, 3, "mem", - S_IFREG | S_IRUSR | S_IWUSR, 1, 0, 0, - 0, &proc_mem_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_cwd = { - PROC_PID_CWD, 3, "cwd", - S_IFLNK | S_IRWXU, 1, 0, 0, - 0, &proc_link_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_root = { - PROC_PID_ROOT, 4, "root", - S_IFLNK | S_IRWXU, 1, 0, 0, - 0, &proc_link_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_exe = { - PROC_PID_EXE, 3, "exe", - S_IFLNK | S_IRWXU, 1, 0, 0, - 0, &proc_link_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_fd = { - PROC_PID_FD, 2, "fd", - S_IFDIR | S_IRUSR | S_IXUSR, 2, 0, 0, - 0, &proc_fd_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_environ = { - PROC_PID_ENVIRON, 7, "environ", - S_IFREG | S_IRUSR, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_cmdline = { - PROC_PID_CMDLINE, 7, "cmdline", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_stat = { - PROC_PID_STAT, 4, "stat", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_statm = { - PROC_PID_STATM, 5, "statm", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, -}; -static struct proc_dir_entry proc_pid_maps = { - PROC_PID_MAPS, 4, "maps", - S_IFIFO | S_IRUGO, 1, 0, 0, - 0, &proc_arraylong_inode_operations, - NULL, proc_pid_fill_inode, +static int proc_pid_readlink(struct dentry * dentry, char * buffer, int buflen) +{ + int error; + struct inode *inode = dentry->d_inode; + + error = proc_permission(inode, MAY_EXEC); + if (error) + goto out; + + dentry = inode->u.proc_i.op.proc_get_link(inode); + error = -ENOENT; + if (!dentry) + goto out; + + error = PTR_ERR(dentry); + if (IS_ERR(dentry)) + goto out; + + error = do_proc_readlink(dentry, buffer, buflen); + dput(dentry); +out: + return error; +} + +static struct inode_operations proc_pid_link_inode_operations = { + NULL, /* file-operations */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + proc_pid_readlink, /* readlink */ + proc_pid_follow_link, /* follow_link */ }; +/* reading from directory - bad */ + +static ssize_t proc_dir_read (struct file * filp, char * buf, + size_t count, loff_t *ppos) +{ + return -EISDIR; +} + +struct pid_entry { + int type; + int len; + char *name; + mode_t mode; +}; + +enum pid_directory_inos { + PROC_PID_INO = 2, + PROC_PID_STATUS, + PROC_PID_MEM, + PROC_PID_CWD, + PROC_PID_ROOT, + PROC_PID_EXE, + PROC_PID_FD, + PROC_PID_ENVIRON, + PROC_PID_CMDLINE, + PROC_PID_STAT, + PROC_PID_STATM, + PROC_PID_MAPS, #if CONFIG_AP1000 -static struct proc_dir_entry proc_pid_ringbuf = { - PROC_PID_RINGBUF, 7, "ringbuf", - S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, - 0, &proc_ringbuf_inode_operations, - NULL, proc_pid_fill_inode, + PROC_PID_RINGBUF, +#endif + PROC_PID_CPU, + PROC_PID_FD_DIR = 0x8000, /* 0x8000-0xffff */ }; + +#define E(type,name,mode) {(type),sizeof(name)-1,(name),(mode)} +static struct pid_entry base_stuff[] = { + E(PROC_PID_FD, "fd", S_IFDIR|S_IRUSR|S_IXUSR), + E(PROC_PID_ENVIRON, "environ", S_IFREG|S_IRUSR), + E(PROC_PID_STATUS, "status", S_IFREG|S_IRUGO), + E(PROC_PID_CMDLINE, "cmdline", S_IFREG|S_IRUGO), + E(PROC_PID_STAT, "stat", S_IFREG|S_IRUGO), + E(PROC_PID_STATM, "statm", S_IFREG|S_IRUGO), +#ifdef SMP + E(PROC_PID_CPU, "cpu", S_IFREG|S_IRUGO), +#endif +#if CONFIG_AP1000 + E(PROC_PID_RINGBUF, "ringbuf", S_IFREG|S_IRUGO|S_IWUSR), #endif + E(PROC_PID_MAPS, "maps", S_IFREG|S_IRUGO), + E(PROC_PID_MEM, "mem", S_IFREG|S_IRUSR|S_IWUSR), + E(PROC_PID_CWD, "cwd", S_IFLNK|S_IRWXUGO), + E(PROC_PID_ROOT, "root", S_IFLNK|S_IRWXUGO), + E(PROC_PID_EXE, "exe", S_IFLNK|S_IRWXUGO), + {0,0,NULL,0} +}; +#undef E + +#define NUMBUF 10 + +static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + struct task_struct *p = inode->u.proc_i.task; + unsigned int fd, pid, ino; + int retval; + char buf[NUMBUF]; + + retval = 0; + pid = p->pid; + + fd = filp->f_pos; + switch (fd) { + case 0: + if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0) + goto out; + filp->f_pos++; + case 1: + ino = fake_ino(pid, PROC_PID_INO); + if (filldir(dirent, "..", 2, 1, ino) < 0) + goto out; + filp->f_pos++; + default: + for (fd = filp->f_pos-2; + p->p_pptr && p->files && fd < p->files->max_fds; + fd++, filp->f_pos++) { + unsigned int i,j; + + if (!fcheck_task(p, fd)) + continue; + + j = NUMBUF; + i = fd; + do { + j--; + buf[j] = '0' + (i % 10); + i /= 10; + } while (i); + + ino = fake_ino(pid, PROC_PID_FD_DIR + fd); + if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) + break; + + } + } +out: + return retval; +} + +static int proc_base_readdir(struct file * filp, + void * dirent, filldir_t filldir) +{ + int i; + int pid; + struct inode *inode = filp->f_dentry->d_inode; + struct pid_entry *p; + + pid = inode->u.proc_i.task->pid; + if (!inode->u.proc_i.task->p_pptr) + return -ENOENT; + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, inode->i_ino) < 0) + return 0; + i++; + filp->f_pos++; + /* fall through */ + case 1: + if (filldir(dirent, "..", 2, i, PROC_ROOT_INO) < 0) + return 0; + i++; + filp->f_pos++; + /* fall through */ + default: + i -= 2; + if (i>=sizeof(base_stuff)/sizeof(base_stuff[0])) + return 1; + p = base_stuff + i; + while (p->name) { + if (filldir(dirent, p->name, p->len, filp->f_pos, fake_ino(pid, p->type)) < 0) + return 0; + filp->f_pos++; + p++; + } + } + return 1; +} + +/* building an inode */ + +static struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, int ino) +{ + struct inode * inode; + + /* We need a new inode */ + + inode = get_empty_inode(); + if (!inode) + goto out; + + /* Common stuff */ + + inode->i_sb = sb; + inode->i_dev = sb->s_dev; + inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; + inode->i_ino = fake_ino(task->pid, ino); + + inode->u.proc_i.file = NULL; + /* + * grab the reference to task. + */ + inode->u.proc_i.task = task; + atomic_inc(&mem_map[MAP_NR(task)].count); + if (!task->p_pptr) + goto out_unlock; -#ifdef __SMP__ -static struct proc_dir_entry proc_pid_cpu = { - PROC_PID_CPU, 3, "cpu", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations, - NULL, proc_pid_fill_inode, + inode->i_uid = 0; + inode->i_gid = 0; + if (ino == PROC_PID_INO || task->dumpable) { + inode->i_uid = task->euid; + inode->i_gid = task->egid; + } + +out: + return inode; + +out_unlock: + iput(inode); + return NULL; +} + +/* dentry stuff */ + +static int pid_fd_revalidate(struct dentry * dentry, int flags) +{ + return 0; +} + +static int pid_base_revalidate(struct dentry * dentry, int flags) +{ + if (dentry->d_inode->u.proc_i.task->p_pptr) + return 1; + d_drop(dentry); + return 0; +} + +static void pid_delete_dentry(struct dentry * dentry) +{ + d_drop(dentry); +} + +static struct dentry_operations pid_fd_dentry_operations = +{ + pid_fd_revalidate, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + pid_delete_dentry /* d_delete(struct dentry *) */ +}; + +static struct dentry_operations pid_dentry_operations = +{ + NULL, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + pid_delete_dentry /* d_delete(struct dentry *) */ +}; + +static struct dentry_operations pid_base_dentry_operations = +{ + pid_base_revalidate, /* revalidate */ + NULL, /* d_hash */ + NULL, /* d_compare */ + pid_delete_dentry /* d_delete(struct dentry *) */ +}; + +/* Lookups */ + +static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) +{ + unsigned int fd, c; + struct task_struct *task = dir->u.proc_i.task; + struct file * file; + struct files_struct * files; + struct inode *inode; + const char *name; + int len; + + fd = 0; + len = dentry->d_name.len; + name = dentry->d_name.name; + if (len > 1 && *name == '0') goto out; + while (len-- > 0) { + c = *name - '0'; + name++; + if (c > 9) + goto out; + fd *= 10; + fd += c; + if (fd & 0xffff8000) + goto out; + } + + inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_FD_DIR+fd); + if (!inode) + goto out; + /* FIXME */ + files = task->files; + if (!files) /* can we ever get here if that's the case? */ + goto out_unlock; + read_lock(&files->file_lock); + file = inode->u.proc_i.file = fcheck_task(task, fd); + if (!file) + goto out_unlock2; + get_file(file); + read_unlock(&files->file_lock); + inode->i_op = &proc_pid_link_inode_operations; + inode->i_size = 64; + inode->i_mode = S_IFLNK; + inode->u.proc_i.op.proc_get_link = proc_fd_link; + if (file->f_mode & 1) + inode->i_mode |= S_IRUSR | S_IXUSR; + if (file->f_mode & 2) + inode->i_mode |= S_IWUSR | S_IXUSR; + dentry->d_op = &pid_fd_dentry_operations; + d_add(dentry, inode); + return NULL; + +out_unlock2: + read_unlock(&files->file_lock); +out_unlock: + iput(inode); +out: + return ERR_PTR(-ENOENT); +} + +static struct file_operations proc_fd_operations = { + NULL, /* lseek - default */ + proc_dir_read, /* read - bad */ + NULL, /* write - bad */ + proc_readfd, /* readdir */ }; -#endif -void __init proc_base_init(void) +/* + * proc directories can do almost nothing.. + */ +static 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, /* get_block */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + proc_permission, /* permission */ +}; + +static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry) { + struct inode *inode; + int error; + struct task_struct *task = dir->u.proc_i.task; + struct pid_entry *p; + + error = -ENOENT; + inode = NULL; + + for (p = base_stuff; p->name; p++) { + if (p->len != dentry->d_name.len) + continue; + if (!memcmp(dentry->d_name.name, p->name, p->len)) + break; + } + if (!p->name) + goto out; + + error = -EINVAL; + inode = proc_pid_make_inode(dir->i_sb, task, p->type); + if (!inode) + goto out; + + inode->i_mode = p->mode; + /* + * Yes, it does not scale. And it should not. Don't add + * new entries into /proc/<pid>/ without very good reasons. + */ + switch(p->type) { + case PROC_PID_FD: + inode->i_nlink = 2; + inode->i_op = &proc_fd_inode_operations; + break; + case PROC_PID_EXE: + inode->i_op = &proc_pid_link_inode_operations; + inode->u.proc_i.op.proc_get_link = proc_exe_link; + break; + case PROC_PID_CWD: + inode->i_op = &proc_pid_link_inode_operations; + inode->u.proc_i.op.proc_get_link = proc_cwd_link; + break; + case PROC_PID_ROOT: + inode->i_op = &proc_pid_link_inode_operations; + inode->u.proc_i.op.proc_get_link = proc_root_link; + break; + case PROC_PID_ENVIRON: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_environ; + break; + case PROC_PID_STATUS: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_status; + break; + case PROC_PID_STAT: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_stat; + break; + case PROC_PID_CMDLINE: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_cmdline; + break; + case PROC_PID_STATM: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_statm; + break; + case PROC_PID_MAPS: + inode->i_op = &proc_maps_inode_operations; + break; +#ifdef SMP + case PROC_PID_CPU: + inode->i_op = &proc_info_inode_operations; + inode->u.proc_i.op.proc_read = proc_pid_cpu; + break; +#endif #if CONFIG_AP1000 - proc_register(&proc_pid, &proc_pid_ringbuf); + case PROC_PID_RINGBUF: + inode->i_op = &proc_ringbuf_inode_operations; + break; #endif - proc_register(&proc_pid, &proc_pid_status); - proc_register(&proc_pid, &proc_pid_mem); - proc_register(&proc_pid, &proc_pid_cwd); - proc_register(&proc_pid, &proc_pid_root); - proc_register(&proc_pid, &proc_pid_exe); - proc_register(&proc_pid, &proc_pid_fd); - proc_register(&proc_pid, &proc_pid_environ); - proc_register(&proc_pid, &proc_pid_cmdline); - proc_register(&proc_pid, &proc_pid_stat); - proc_register(&proc_pid, &proc_pid_statm); - proc_register(&proc_pid, &proc_pid_maps); -#ifdef __SMP__ - proc_register(&proc_pid, &proc_pid_cpu); -#endif + case PROC_PID_MEM: + inode->i_op = &proc_mem_inode_operations; + break; + default: + printk("procfs: impossible type (%d)",p->type); + iput(inode); + return ERR_PTR(-EINVAL); + } + dentry->d_op = &pid_dentry_operations; + d_add(dentry, inode); + return NULL; + +out: + return ERR_PTR(error); +} + +static struct file_operations proc_base_operations = { + NULL, /* lseek - default */ + proc_dir_read, /* read - bad */ + NULL, /* write - bad */ + proc_base_readdir, /* readdir */ +}; + +static struct inode_operations proc_base_inode_operations = { + &proc_base_operations, /* default base directory file-ops */ + NULL, /* create */ + proc_base_lookup, /* lookup */ }; +struct dentry *proc_pid_lookup(struct inode *dir, struct dentry * dentry) +{ + unsigned int pid, c; + struct task_struct *task; + const char *name; + struct inode *inode; + int len; + + pid = 0; + name = dentry->d_name.name; + len = dentry->d_name.len; + while (len-- > 0) { + c = *name - '0'; + name++; + if (c > 9) + goto out; + pid *= 10; + pid += c; + if (!pid) + goto out; + if (pid & 0xffff0000) + goto out; + } + + read_lock(&tasklist_lock); + task = find_task_by_pid(pid); + if (task) + atomic_inc(&mem_map[MAP_NR(task)].count); + read_unlock(&tasklist_lock); + if (!task) + goto out; + + inode = proc_pid_make_inode(dir->i_sb, task, PROC_PID_INO); + + free_task_struct(task); + if (!inode) + goto out; + inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO; + inode->i_op = &proc_base_inode_operations; + inode->i_nlink = 3; + inode->i_flags|=S_IMMUTABLE; + dentry->d_op = &pid_base_dentry_operations; + d_add(dentry, inode); + return NULL; +out: + return ERR_PTR(-ENOENT); +} + +void proc_pid_delete_inode(struct inode *inode) +{ + if (inode->u.proc_i.file) + fput(inode->u.proc_i.file); + free_task_struct(inode->u.proc_i.task); +} +#define PROC_NUMBUF 10 +#define PROC_MAXPIDS 20 + +/* + * Get a few pid's to return for filldir - we need to hold the + * tasklist lock while doing this, and we must release it before + * we actually do the filldir itself, so we use a temp buffer.. + */ +static int get_pid_list(int index, unsigned int *pids) +{ + struct task_struct *p; + int nr_pids = 0; + index -= FIRST_PROCESS_ENTRY; + read_lock(&tasklist_lock); + for_each_task(p) { + int pid = p->pid; + if (!pid) + continue; + if (--index >= 0) + continue; + pids[nr_pids] = pid; + nr_pids++; + if (nr_pids >= PROC_MAXPIDS) + break; + } + read_unlock(&tasklist_lock); + return nr_pids; +} +int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) +{ + unsigned int pid_array[PROC_MAXPIDS]; + char buf[PROC_NUMBUF]; + unsigned int nr = filp->f_pos; + unsigned int nr_pids, i; + nr_pids = get_pid_list(nr, pid_array); + for (i = 0; i < nr_pids; i++) { + int pid = pid_array[i]; + ino_t ino = fake_ino(pid,PROC_PID_INO); + unsigned long j = PROC_NUMBUF; + do { + j--; + buf[j] = '0' + (pid % 10); + pid /= 10; + } while (pid); + if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) + break; + filp->f_pos++; + } + return 0; +} diff --git a/fs/proc/fd.c b/fs/proc/fd.c deleted file mode 100644 index 1862f6f5f..000000000 --- a/fs/proc/fd.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * linux/fs/proc/fd.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * proc fd directory handling functions - * - * 01-May-98 Edgar Toernig <froese@gmx.de> - * Added support for more than 256 fds. - * Limit raised to 32768. - */ - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/file.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> - -#include <asm/uaccess.h> - -static int proc_readfd(struct file *, void *, filldir_t); -static struct dentry *proc_lookupfd(struct inode *, struct dentry *); - -static struct file_operations proc_fd_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - proc_readfd, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - 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, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - proc_permission, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -/* - * NOTE! Normally we'd indicate that a file does not - * exist by creating a negative dentry and returning - * a successful return code. However, for this case - * we do not want to create negative dentries, because - * the state of the world can change behind our backs. - * - * Thus just return -ENOENT instead. - */ -static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry) -{ - unsigned int ino, pid, fd, c; - struct task_struct * p; - struct file * file; - struct inode *inode; - const char *name; - int len, err; - - err = -ENOENT; - ino = dir->i_ino; - pid = ino >> 16; - ino &= 0x0000ffff; - - if (!pid || ino != PROC_PID_FD) - goto out; - - fd = 0; - len = dentry->d_name.len; - name = dentry->d_name.name; - if (len > 1 && *name == '0') goto out; - while (len-- > 0) { - c = *name - '0'; - name++; - if (c > 9) - goto out; - fd *= 10; - fd += c; - if (fd & 0xffff8000) - goto out; - } - - read_lock(&tasklist_lock); - file = NULL; - p = find_task_by_pid(pid); - if (p && p->files) - file = fcheck_task(p, fd); - read_unlock(&tasklist_lock); - - /* - * File handle is invalid if it is out of range, if the process - * has no files (Zombie) if the file is closed, or if its inode - * is NULL - */ - - if (!file) - goto out; - - ino = (pid << 16) + PROC_PID_FD_DIR + fd; - inode = proc_get_inode(dir->i_sb, ino, NULL); - if (inode) { - dentry->d_op = &proc_dentry_operations; - d_add(dentry, inode); - return NULL; - } -out: - return ERR_PTR(err); -} - -#define NUMBUF 10 - -static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct task_struct *p, *tmp; - unsigned int fd, pid, ino; - int retval; - char buf[NUMBUF]; - - retval = 0; - ino = inode->i_ino; - pid = ino >> 16; - ino &= 0x0000ffff; - if (ino != PROC_PID_FD) - goto out; - - for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) { - ino = inode->i_ino; - if (fd) - ino = (ino & 0xffff0000) | PROC_PID_INO; - if (filldir(dirent, "..", fd+1, fd, ino) < 0) - goto out; - } - - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (!p) - goto out_unlock; - - for (fd -= 2 ; p->files && fd < p->files->max_fds; fd++, filp->f_pos++) - { - unsigned int i,j; - - if (!fcheck_task(p, fd)) - continue; - - j = NUMBUF; - i = fd; - do { - j--; - buf[j] = '0' + (i % 10); - i /= 10; - } while (i); - - /* Drop the task lock, as the filldir function may block */ - read_unlock(&tasklist_lock); - - ino = (pid << 16) + PROC_PID_FD_DIR + fd; - if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) - goto out; - - read_lock(&tasklist_lock); - /* - * filldir() might have slept, so we must - * re-validate "p". This is fast enough due - * to the pidhash - */ - tmp = find_task_by_pid(pid); - if (p != tmp) - break; - } -out_unlock: - read_unlock(&tasklist_lock); - -out: - return retval; -} diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 1a2fe0f6e..5ef59004c 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -70,33 +70,6 @@ struct inode_operations proc_file_inode_operations = { NULL /* revalidate */ }; -/* - * compatibility to replace fs/proc/net.c - */ -struct inode_operations proc_net_inode_operations = { - &proc_file_operations, /* default net file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - - #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif diff --git a/fs/proc/inode-alloc.txt b/fs/proc/inode-alloc.txt index 440d3baf8..4af79966c 100644 --- a/fs/proc/inode-alloc.txt +++ b/fs/proc/inode-alloc.txt @@ -1,50 +1,14 @@ -Inode allocations in the proc-fs (hex-numbers): +Current inode allocations in the proc-fs (hex-numbers): 00000000 reserved - 00000001-00000fff static entries + 00000001-00000fff static entries (goners) 001 root-ino - 002 load-avg - 003 uptime - ... - 080 net/* - ... - 100 scsi/* - ... - xxx mca/* - ... - yyy bus/* - ... - fff end 00001000-00001fff dynamic entries - 00002000-00002fff openprom entries - 0001xxxx-7fffxxxx pid-dir entries for pid 1-7fff - 0000 unused - 0001 unused - 0002 pid - 0003 pid/status - ... - 0008 pid/fd - ... - 00xx-00ff unused - 01xx pid/fd/* for fd 0-ff - ... - 01ff end - 0200-ffff unused - 80000000-ffffffff unused - - -New allocation: - - 00000000-0000ffff unchanged - - 0001xxxx-7fffxxxx pid-dir entries for pid 1-7fff - 0000-00ff unchanged - 0100-7fff unused - 8000-ffff pid/fd/* for fd 0-7fff - - 80000000-ffffffff unchanged +Goal: + a) once we'll split the thing into several virtual filesystems we + will get rid of magical ranges (and this file, BTW). diff --git a/fs/proc/inode.c b/fs/proc/inode.c index a5596e4ee..8842cc253 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -14,6 +14,7 @@ #include <linux/locks.h> #include <linux/limits.h> #include <linux/config.h> +#include <linux/module.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -51,9 +52,7 @@ void de_put(struct proc_dir_entry *de) static void proc_put_inode(struct inode *inode) { #ifdef CONFIG_SUN_OPENPROMFS_MODULE - if ((inode->i_ino >= PROC_OPENPROM_FIRST) && - (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) && - proc_openprom_use) + if (PROC_INODE_OPENPROM(inode) && proc_openprom_use) (*proc_openprom_use)(inode, 0); #endif /* @@ -71,18 +70,16 @@ static void proc_delete_inode(struct inode *inode) { struct proc_dir_entry *de = inode->u.generic_ip; -#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) - if ((inode->i_ino >= PROC_OPENPROM_FIRST) && - (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM)) + if (PROC_INODE_PROPER(inode)) { + proc_pid_delete_inode(inode); + return; + } + if (PROC_INODE_OPENPROM(inode)) return; -#endif if (de) { - /* - * Call the fill_inode hook to release module counts. - */ - if (de->fill_inode) - de->fill_inode(inode, 0); + if (de->owner) + __MOD_DEC_USE_COUNT(de->owner); de_put(de); } } @@ -142,113 +139,6 @@ static int parse_options(char *options,uid_t *uid,gid_t *gid) return 1; } -/* - * The standard rules, copied from fs/namei.c:permission(). - */ -static int standard_permission(struct inode *inode, int mask) -{ - int mode = inode->i_mode; - - if ((mask & S_IWOTH) && IS_RDONLY(inode) && - (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) - return -EROFS; /* Nobody gets write access to a read-only fs */ - else if ((mask & S_IWOTH) && IS_IMMUTABLE(inode)) - return -EACCES; /* Nobody gets write access to an immutable file */ - else if (current->fsuid == inode->i_uid) - mode >>= 6; - else if (in_group_p(inode->i_gid)) - mode >>= 3; - if (((mode & mask & S_IRWXO) == mask) || capable(CAP_DAC_OVERRIDE)) - return 0; - /* read and search access */ - if ((mask == S_IROTH) || - (S_ISDIR(mode) && !(mask & ~(S_IROTH | S_IXOTH)))) - if (capable(CAP_DAC_READ_SEARCH)) - return 0; - return -EACCES; -} - -/* - * Set up permission rules for processes looking at other processes. - * You're not allowed to see a process unless it has the same or more - * restricted root than your own. This prevents a chrooted processes - * from escaping through the /proc entries of less restricted - * processes, and thus allows /proc to be safely mounted in a chrooted - * area. - * - * Note that root (uid 0) doesn't get permission for this either, - * since chroot is stronger than root. - * - * XXX TODO: use the dentry mechanism to make off-limits procs simply - * invisible rather than denied? Does each namespace root get its own - * dentry tree? - * - * This also applies the default permissions checks, as it only adds - * restrictions. - * - * Jeremy Fitzhardinge <jeremy@zip.com.au> - */ -int proc_permission(struct inode *inode, int mask) -{ - struct task_struct *p; - unsigned long ino = inode->i_ino; - unsigned long pid; - struct dentry *de, *base; - - if (standard_permission(inode, mask) != 0) - return -EACCES; - - /* - * Find the root of the processes being examined (if any). - * XXX Surely there's a better way of doing this? - */ - if (ino >= PROC_OPENPROM_FIRST && - ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) - return 0; /* already allowed */ - - pid = ino >> 16; - if (pid == 0) - return 0; /* already allowed */ - - de = NULL; - base = current->fs->root; - - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - - if (p && p->fs) - de = p->fs->root; - read_unlock(&tasklist_lock); /* FIXME! */ - - if (p == NULL) - return -EACCES; /* ENOENT? */ - - if (de == NULL) - { - /* kswapd and bdflush don't have proper root or cwd... */ - return -EACCES; - } - - /* XXX locking? */ - for(;;) - { - struct dentry *parent; - - if (de == base) - return 0; /* already allowed */ - - de = de->d_covers; - parent = de->d_parent; - - if (de == parent) - break; - - de = parent; - } - - return -EACCES; /* incompatible roots */ -} - struct inode * proc_get_inode(struct super_block * sb, int ino, struct proc_dir_entry * de) { @@ -269,14 +159,9 @@ printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count); goto out_fail; #ifdef CONFIG_SUN_OPENPROMFS_MODULE - if ((inode->i_ino >= PROC_OPENPROM_FIRST) - && (inode->i_ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) - && proc_openprom_use) + if (PROC_INODE_OPENPROM(inode) && proc_openprom_use) (*proc_openprom_use)(inode, 1); #endif - /* N.B. How can this test ever fail?? */ - if (inode->i_sb != sb) - printk("proc_get_inode: inode fubar\n"); inode->u.generic_ip = (void *) de; if (de) { @@ -291,12 +176,8 @@ printk("proc_iget: using deleted entry %s, count=%d\n", de->name, de->count); inode->i_op = de->ops; if (de->nlink) inode->i_nlink = de->nlink; - /* - * The fill_inode routine should use this call - * to increment module counts, if necessary. - */ - if (de->fill_inode) - de->fill_inode(inode, 1); + if (de->owner) + __MOD_INC_USE_COUNT(de->owner); } /* * Fixup the root inode's nlink value @@ -366,63 +247,7 @@ int proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) void proc_read_inode(struct inode * inode) { - unsigned long ino, pid; - struct task_struct * p; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_blocks = 0; - inode->i_blksize = 1024; - ino = inode->i_ino; - if (ino >= PROC_OPENPROM_FIRST && - ino < PROC_OPENPROM_FIRST + PROC_NOPENPROM) - goto out; - inode->i_op = NULL; - inode->i_mode = 0; - inode->i_uid = 0; - inode->i_gid = 0; - inode->i_nlink = 1; - inode->i_size = 0; - - pid = ino >> 16; - if (!pid) - goto out; - - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (!p) - goto out_unlock; - - ino &= 0x0000ffff; - if (ino == PROC_PID_INO || p->dumpable) { - inode->i_uid = p->euid; - inode->i_gid = p->egid; - } - if (ino & PROC_PID_FD_DIR) { - struct file * file; - ino &= 0x7fff; - if (!p->files) /* can we ever get here if that's the case? */ - goto out_unlock; - read_lock(&p->files->file_lock); - file = fcheck_task(p, ino); - if (!file) - goto out_unlock2; - - inode->i_op = &proc_link_inode_operations; - inode->i_size = 64; - inode->i_mode = S_IFLNK; - if (file->f_mode & 1) - inode->i_mode |= S_IRUSR | S_IXUSR; - if (file->f_mode & 2) - inode->i_mode |= S_IWUSR | S_IXUSR; -out_unlock2: - read_unlock(&p->files->file_lock); - } -out_unlock: - /* Defer unlocking until we're done with the task */ - read_unlock(&tasklist_lock); - -out: - return; } void proc_write_inode(struct inode * inode) diff --git a/fs/proc/kcore.c b/fs/proc/kcore.c new file mode 100644 index 000000000..ec2a92db3 --- /dev/null +++ b/fs/proc/kcore.c @@ -0,0 +1,388 @@ +/* + * fs/proc/kcore.c kernel ELF/AOUT core dumper + * + * Modelled on fs/exec.c:aout_core_dump() + * Jeremy Fitzhardinge <jeremy@sw.oz.au> + * Implemented by David Howells <David.Howells@nexor.co.uk> + * Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@sco.com> + * Support to dump vmalloc'd data structures (ELF only), Tigran Aivazian <tigran@sco.com> + */ + +#include <linux/config.h> +#include <linux/mm.h> +#include <linux/proc_fs.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/elf.h> +#include <linux/elfcore.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + + +static int open_kcore(struct inode * inode, struct file * filp) +{ + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static ssize_t read_kcore(struct file *, char *, size_t, loff_t *); + +static struct file_operations proc_kcore_operations = { + NULL, /* lseek */ + read_kcore, + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + open_kcore +}; + +struct inode_operations proc_kcore_inode_operations = { + &proc_kcore_operations, +}; + +#ifdef CONFIG_KCORE_AOUT +static ssize_t read_kcore(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned long long p = *ppos, memsize; + ssize_t read; + ssize_t count1; + char * pnt; + struct user dump; +#if defined (__i386__) || defined (__mc68000__) +# define FIRST_MAPPED PAGE_SIZE /* we don't have page 0 mapped on x86.. */ +#else +# define FIRST_MAPPED 0 +#endif + + memset(&dump, 0, sizeof(struct user)); + dump.magic = CMAGIC; + dump.u_dsize = max_mapnr; +#if defined (__i386__) + dump.start_code = PAGE_OFFSET; +#endif +#ifdef __alpha__ + dump.start_data = PAGE_OFFSET; +#endif + + memsize = (max_mapnr + 1) << PAGE_SHIFT; + if (p >= memsize) + return 0; + if (count > memsize - p) + count = memsize - p; + read = 0; + + if (p < sizeof(struct user) && count > 0) { + count1 = count; + if (p + count1 > sizeof(struct user)) + count1 = sizeof(struct user)-p; + pnt = (char *) &dump + p; + copy_to_user(buf,(void *) pnt, count1); + buf += count1; + p += count1; + count -= count1; + read += count1; + } + + if (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) { + count1 = PAGE_SIZE + FIRST_MAPPED - p; + if (count1 > count) + count1 = count; + clear_user(buf, count1); + buf += count1; + p += count1; + count -= count1; + read += count1; + } + if (count > 0) { + copy_to_user(buf, (void *) (PAGE_OFFSET+p-PAGE_SIZE), count); + read += count; + } + *ppos += read; + return read; +} +#else /* CONFIG_KCORE_AOUT */ + +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) + +/* An ELF note in memory */ +struct memelfnote +{ + const char *name; + int type; + unsigned int datasz; + void *data; +}; + +extern char saved_command_line[]; + +static size_t get_kcore_size(int *num_vma, int *elf_buflen) +{ + size_t try, size = 0; + struct vm_struct *m; + + *num_vma = 0; + if (!vmlist) { + *elf_buflen = PAGE_SIZE; + return ((size_t)high_memory - PAGE_OFFSET + PAGE_SIZE); + } + + for (m=vmlist; m; m=m->next) { + try = (size_t)m->addr + m->size; + if (try > size) + size = try; + *num_vma = *num_vma + 1; + } + *elf_buflen = sizeof(struct elfhdr) + + (*num_vma + 2)*sizeof(struct elf_phdr) + + 3 * sizeof(struct memelfnote); + *elf_buflen = PAGE_ALIGN(*elf_buflen); + return (size - PAGE_OFFSET + *elf_buflen); +} + + +/*****************************************************************************/ +/* + * determine size of ELF note + */ +static int notesize(struct memelfnote *en) +{ + int sz; + + sz = sizeof(struct elf_note); + sz += roundup(strlen(en->name), 4); + sz += roundup(en->datasz, 4); + + return sz; +} /* end notesize() */ + +/*****************************************************************************/ +/* + * store a note in the header buffer + */ +static char *storenote(struct memelfnote *men, char *bufp) +{ + struct elf_note en; + +#define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0) + + en.n_namesz = strlen(men->name); + en.n_descsz = men->datasz; + en.n_type = men->type; + + DUMP_WRITE(&en, sizeof(en)); + DUMP_WRITE(men->name, en.n_namesz); + + /* XXX - cast from long long to long to avoid need for libgcc.a */ + bufp = (char*) roundup((unsigned long)bufp,4); + DUMP_WRITE(men->data, men->datasz); + bufp = (char*) roundup((unsigned long)bufp,4); + +#undef DUMP_WRITE + + return bufp; +} /* end storenote() */ + +/* + * store an ELF coredump header in the supplied buffer + * num_vma is the number of elements in vmlist + */ +static void elf_kcore_store_hdr(char *bufp, int num_vma, int elf_buflen) +{ + struct elf_prstatus prstatus; /* NT_PRSTATUS */ + struct elf_prpsinfo prpsinfo; /* NT_PRPSINFO */ + struct elf_phdr *nhdr, *phdr; + struct elfhdr *elf; + struct memelfnote notes[3]; + off_t offset = 0; + struct vm_struct *m; + + /* setup ELF header */ + elf = (struct elfhdr *) bufp; + bufp += sizeof(struct elfhdr); + offset += sizeof(struct elfhdr); + memcpy(elf->e_ident, ELFMAG, SELFMAG); + elf->e_ident[EI_CLASS] = ELF_CLASS; + elf->e_ident[EI_DATA] = ELF_DATA; + elf->e_ident[EI_VERSION]= EV_CURRENT; + memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); + elf->e_type = ET_CORE; + elf->e_machine = ELF_ARCH; + elf->e_version = EV_CURRENT; + elf->e_entry = 0; + elf->e_phoff = sizeof(struct elfhdr); + elf->e_shoff = 0; + elf->e_flags = 0; + elf->e_ehsize = sizeof(struct elfhdr); + elf->e_phentsize= sizeof(struct elf_phdr); + elf->e_phnum = 2 + num_vma; + elf->e_shentsize= 0; + elf->e_shnum = 0; + elf->e_shstrndx = 0; + + /* setup ELF PT_NOTE program header */ + nhdr = (struct elf_phdr *) bufp; + bufp += sizeof(struct elf_phdr); + offset += sizeof(struct elf_phdr); + nhdr->p_type = PT_NOTE; + nhdr->p_offset = 0; + nhdr->p_vaddr = 0; + nhdr->p_paddr = 0; + nhdr->p_filesz = 0; + nhdr->p_memsz = 0; + nhdr->p_flags = 0; + nhdr->p_align = 0; + + /* setup ELF PT_LOAD program header */ + phdr = (struct elf_phdr *) bufp; + bufp += sizeof(struct elf_phdr); + offset += sizeof(struct elf_phdr); + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = elf_buflen; + phdr->p_vaddr = PAGE_OFFSET; + phdr->p_paddr = __pa(PAGE_OFFSET); + phdr->p_filesz = phdr->p_memsz = ((unsigned long)high_memory - PAGE_OFFSET); + phdr->p_align = PAGE_SIZE; + + for (m=vmlist; m; m=m->next) { + phdr = (struct elf_phdr *) bufp; + bufp += sizeof(struct elf_phdr); + offset += sizeof(struct elf_phdr); + + phdr->p_type = PT_LOAD; + phdr->p_flags = PF_R|PF_W|PF_X; + phdr->p_offset = (size_t)m->addr - PAGE_OFFSET + elf_buflen; + phdr->p_vaddr = (size_t)m->addr; + phdr->p_paddr = __pa(m); + phdr->p_filesz = phdr->p_memsz = m->size; + phdr->p_align = PAGE_SIZE; + } + + /* + * Set up the notes in similar form to SVR4 core dumps made + * with info from their /proc. + */ + nhdr->p_offset = offset; + + /* set up the process status */ + notes[0].name = "CORE"; + notes[0].type = NT_PRSTATUS; + notes[0].datasz = sizeof(struct elf_prstatus); + notes[0].data = &prstatus; + + memset(&prstatus, 0, sizeof(struct elf_prstatus)); + + nhdr->p_filesz = notesize(¬es[0]); + bufp = storenote(¬es[0], bufp); + + /* set up the process info */ + notes[1].name = "CORE"; + notes[1].type = NT_PRPSINFO; + notes[1].datasz = sizeof(struct elf_prpsinfo); + notes[1].data = &prpsinfo; + + memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo)); + prpsinfo.pr_state = 0; + prpsinfo.pr_sname = 'R'; + prpsinfo.pr_zomb = 0; + + strcpy(prpsinfo.pr_fname, "vmlinux"); + strncpy(prpsinfo.pr_psargs, saved_command_line, ELF_PRARGSZ); + + nhdr->p_filesz = notesize(¬es[1]); + bufp = storenote(¬es[1], bufp); + + /* set up the task structure */ + notes[2].name = "CORE"; + notes[2].type = NT_TASKSTRUCT; + notes[2].datasz = sizeof(struct task_struct); + notes[2].data = current; + + nhdr->p_filesz = notesize(¬es[2]); + bufp = storenote(¬es[2], bufp); + +} /* end elf_kcore_store_hdr() */ + +/*****************************************************************************/ +/* + * read from the ELF header and then kernel memory + */ +static ssize_t read_kcore(struct file *file, char *buffer, size_t buflen, + loff_t *fpos) +{ + ssize_t acc = 0; + size_t size, tsz; + char * elf_buffer; + int elf_buflen = 0, num_vma = 0; + + /* XXX we need to somehow lock vmlist between here + * and after elf_kcore_store_hdr() returns. + * For now assume that num_vma does not change (TA) + */ + proc_root_kcore.size = size = get_kcore_size(&num_vma, &elf_buflen); + if (buflen == 0 || *fpos >= size) + return 0; + + /* trim buflen to not go beyond EOF */ + if (buflen > size - *fpos) + buflen = size - *fpos; + + /* construct an ELF core header if we'll need some of it */ + if (*fpos < elf_buflen) { + tsz = elf_buflen - *fpos; + if (buflen < tsz) + tsz = buflen; + elf_buffer = kmalloc(elf_buflen, GFP_KERNEL); + if (!elf_buffer) + return -ENOMEM; + memset(elf_buffer, 0, elf_buflen); + elf_kcore_store_hdr(elf_buffer, num_vma, elf_buflen); + copy_to_user(buffer, elf_buffer, tsz); + kfree(elf_buffer); + buflen -= tsz; + *fpos += tsz; + buffer += tsz; + acc += tsz; + + /* leave now if filled buffer already */ + if (buflen == 0) + return tsz; + } + + /* where page 0 not mapped, write zeros into buffer */ +#if defined (__i386__) || defined (__mc68000__) + if (*fpos < PAGE_SIZE + elf_buflen) { + /* work out how much to clear */ + tsz = PAGE_SIZE + elf_buflen - *fpos; + if (buflen < tsz) + tsz = buflen; + + /* write zeros to buffer */ + clear_user(buffer, tsz); + buflen -= tsz; + *fpos += tsz; + buffer += tsz; + acc += tsz; + + /* leave now if filled buffer already */ + if (buflen == 0) + return tsz; + } +#endif + + /* fill the remainder of the buffer from kernel VM space */ +#if defined (__i386__) || defined (__mc68000__) + copy_to_user(buffer, __va(*fpos - PAGE_SIZE), buflen); +#else + copy_to_user(buffer, __va(*fpos), buflen); +#endif + acc += buflen; + *fpos += buflen; + + return acc; + +} +#endif /* CONFIG_KCORE_AOUT */ diff --git a/fs/proc/link.c b/fs/proc/link.c deleted file mode 100644 index 69d435600..000000000 --- a/fs/proc/link.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * linux/fs/proc/link.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * /proc link-file handling code - */ - -#include <asm/uaccess.h> - -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/file.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> - -static int proc_readlink(struct dentry *, char *, int); -static struct dentry * proc_follow_link(struct dentry *, struct dentry *, unsigned int); - -/* - * links can't do much... - */ -static struct file_operations proc_fd_link_operations = { - NULL, /* lseek - default */ - NULL, /* read - bad */ - NULL, /* write - bad */ - NULL, /* readdir - bad */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* very special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -struct inode_operations proc_link_inode_operations = { - &proc_fd_link_operations,/* file-operations */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - proc_readlink, /* readlink */ - proc_follow_link, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - proc_permission, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -static struct dentry * proc_follow_link(struct dentry *dentry, - struct dentry *base, - unsigned int follow) -{ - struct inode *inode = dentry->d_inode; - struct task_struct *p; - struct dentry * result; - int ino, pid; - int error; - - /* We don't need a base pointer in the /proc filesystem */ - dput(base); - - error = permission(inode, MAY_EXEC); - result = ERR_PTR(error); - if (error) - goto out; - - ino = inode->i_ino; - pid = ino >> 16; - ino &= 0x0000ffff; - - result = ERR_PTR(-ENOENT); - - switch (ino) { - case PROC_PID_CWD: - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (p && p->fs && p->fs->pwd) - result = dget(p->fs->pwd); - read_unlock(&tasklist_lock); - break; - - case PROC_PID_ROOT: - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (p && p->fs && p->fs->root) - result = dget(p->fs->root); - read_unlock(&tasklist_lock); - break; - - case PROC_PID_EXE: { - struct mm_struct *mm = NULL; - struct vm_area_struct * vma; - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (p) - mm = p->mm; - if (mm) - atomic_inc(&mm->mm_users); - read_unlock(&tasklist_lock); - if (!mm) - break; - down(&mm->mmap_sem); - vma = mm->mmap; - while (vma) { - if ((vma->vm_flags & VM_EXECUTABLE) && - vma->vm_file) { - result = dget(vma->vm_file->f_dentry); - break; - } - vma = vma->vm_next; - } - up(&mm->mmap_sem); - mmput(mm); - break; - } - default: - if (ino & PROC_PID_FD_DIR) { - struct file * file; - struct files_struct *files = NULL; - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - if (p) - files = p->files; - read_unlock(&tasklist_lock); - if (!files) - break; - ino &= 0x7fff; - read_lock(&files->file_lock); - /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ - if (ino < files->max_fds && - (file = files->fd[ino]) && file->f_dentry) - result = dget(file->f_dentry); - read_unlock(&p->files->file_lock); - } - } -out: - return result; -} - -/* - * This pretty-prints the pathname of a dentry, - * clarifying sockets etc. - */ -static int do_proc_readlink(struct dentry *dentry, char * buffer, int buflen) -{ - struct inode * inode; - char * tmp = (char*)__get_free_page(GFP_KERNEL), *path, *pattern; - int len; - - if(tmp==NULL) - return -ENOMEM; - - /* Check for special dentries.. */ - pattern = NULL; - inode = dentry->d_inode; - if (inode && IS_ROOT(dentry)) { - if (S_ISSOCK(inode->i_mode)) - pattern = "socket:[%lu]"; - if (S_ISFIFO(inode->i_mode)) - pattern = "pipe:[%lu]"; - } - - if (pattern) { - len = sprintf(tmp, pattern, inode->i_ino); - path = tmp; - } else { - path = d_path(dentry, tmp, PAGE_SIZE); - len = tmp + PAGE_SIZE - 1 - path; - } - - if (len < buflen) - buflen = len; - dput(dentry); - copy_to_user(buffer, path, buflen); - free_page((unsigned long)tmp); - return buflen; -} - -static int proc_readlink(struct dentry * dentry, char * buffer, int buflen) -{ - int error; - - dentry = proc_follow_link(dentry, NULL, 1); - error = PTR_ERR(dentry); - if (!IS_ERR(dentry)) { - error = -ENOENT; - if (dentry) { - error = do_proc_readlink(dentry, buffer, buflen); - } - } - return error; -} diff --git a/fs/proc/mem.c b/fs/proc/mem.c deleted file mode 100644 index 90cd79722..000000000 --- a/fs/proc/mem.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * linux/fs/proc/mem.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/proc_fs.h> -#include <linux/highmem.h> - -#include <asm/page.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/pgtable.h> - -/* - * mem_write isn't really a good idea right now. It needs - * to check a lot more: if the process we try to write to - * dies in the middle right now, mem_write will overwrite - * kernel memory.. This disables it altogether. - */ -#define mem_write NULL - -static int check_range(struct mm_struct * mm, unsigned long addr, int count) -{ - struct vm_area_struct *vma; - int retval; - - vma = find_vma(mm, addr); - if (!vma) - return -EACCES; - if (vma->vm_start > addr) - return -EACCES; - if (!(vma->vm_flags & VM_READ)) - return -EACCES; - while ((retval = vma->vm_end - addr) < count) { - struct vm_area_struct *next = vma->vm_next; - if (!next) - break; - if (vma->vm_end != next->vm_start) - break; - if (!(next->vm_flags & VM_READ)) - break; - vma = next; - } - if (retval > count) - retval = count; - return retval; -} - -static struct task_struct * get_task(int pid) -{ - struct task_struct * tsk = current; - - if (pid != tsk->pid) { - tsk = find_task_by_pid(pid); - - /* Allow accesses only under the same circumstances - * that we would allow ptrace to work. - */ - if (tsk) { - if (!(tsk->flags & PF_PTRACED) - || tsk->state != TASK_STOPPED - || tsk->p_pptr != current) - tsk = NULL; - } - } - return tsk; -} - -static ssize_t mem_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - pgd_t *page_dir; - pmd_t *page_middle; - pte_t pte; - struct page * page; - struct task_struct * tsk; - unsigned long addr; - unsigned long maddr; /* temporary mapped address */ - char *tmp; - ssize_t scount, i; - - read_lock(&tasklist_lock); - tsk = get_task(inode->i_ino >> 16); - read_unlock(&tasklist_lock); /* FIXME: This should really be done only afetr not using tsk any more!!! */ - if (!tsk) - return -ESRCH; - addr = *ppos; - scount = check_range(tsk->mm, addr, count); - if (scount < 0) - return scount; - tmp = buf; - while (scount > 0) { - if (signal_pending(current)) - break; - page_dir = pgd_offset(tsk->mm,addr); - if (pgd_none(*page_dir)) - break; - if (pgd_bad(*page_dir)) { - pgd_ERROR(*page_dir); - pgd_clear(page_dir); - break; - } - page_middle = pmd_offset(page_dir,addr); - if (pmd_none(*page_middle)) - break; - if (pmd_bad(*page_middle)) { - pmd_ERROR(*page_middle); - pmd_clear(page_middle); - break; - } - pte = *pte_offset(page_middle,addr); - if (!pte_present(pte)) - break; - page = pte_page(pte); - i = PAGE_SIZE-(addr & ~PAGE_MASK); - if (i > scount) - i = scount; - maddr = kmap(page, KM_READ); - copy_to_user(tmp, (char *)maddr + (addr & ~PAGE_MASK), i); - kunmap(maddr, KM_READ); - addr += i; - tmp += i; - scount -= i; - } - *ppos = addr; - return tmp-buf; -} - -#ifndef mem_write - -static ssize_t mem_write(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - pgd_t *page_dir; - pmd_t *page_middle; - pte_t pte; - struct page * page; - struct task_struct * tsk; - unsigned long addr; - unsigned long maddr; /* temporary mapped address */ - char *tmp; - long i; - - addr = *ppos; - tsk = get_task(inode->i_ino >> 16); - if (!tsk) - return -ESRCH; - tmp = buf; - while (count > 0) { - if (signal_pending(current)) - break; - page_dir = pgd_offset(tsk,addr); - if (pgd_none(*page_dir)) - break; - if (pgd_bad(*page_dir)) { - pgd_ERROR(*page_dir); - pgd_clear(page_dir); - break; - } - page_middle = pmd_offset(page_dir,addr); - if (pmd_none(*page_middle)) - break; - if (pmd_bad(*page_middle)) { - pmd_ERROR(*page_middle); - pmd_clear(page_middle); - break; - } - pte = *pte_offset(page_middle,addr); - if (!pte_present(pte)) - break; - if (!pte_write(pte)) - break; - page = pte_page(pte); - i = PAGE_SIZE-(addr & ~PAGE_MASK); - if (i > count) - i = count; - maddr = kmap(page, KM_WRITE); - copy_from_user((char *)maddr + (addr & ~PAGE_MASK), tmp, i); - kunmap(maddr, KM_WRITE); - addr += i; - tmp += i; - count -= i; - } - *ppos = addr; - if (tmp != buf) - return tmp-buf; - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -#endif - -static long long mem_lseek(struct file * file, long long offset, int orig) -{ - switch (orig) { - case 0: - file->f_pos = offset; - return file->f_pos; - case 1: - file->f_pos += offset; - return file->f_pos; - default: - return -EINVAL; - } -} - -/* - * This isn't really reliable by any means.. - */ -int mem_mmap(struct file * file, struct vm_area_struct * vma) -{ - struct task_struct *tsk; - pgd_t *src_dir, *dest_dir; - pmd_t *src_middle, *dest_middle; - pte_t *src_table, *dest_table; - unsigned long stmp, dtmp, mapnr; - struct vm_area_struct *src_vma = NULL; - struct inode *inode = file->f_dentry->d_inode; - - /* Get the source's task information */ - - tsk = get_task(inode->i_ino >> 16); - - if (!tsk) - return -ESRCH; - - /* Ensure that we have a valid source area. (Has to be mmap'ed and - have valid page information.) We can't map shared memory at the - moment because working out the vm_area_struct & nattach stuff isn't - worth it. */ - - src_vma = tsk->mm->mmap; - stmp = vma->vm_offset; - while (stmp < vma->vm_offset + (vma->vm_end - vma->vm_start)) { - while (src_vma && stmp > src_vma->vm_end) - src_vma = src_vma->vm_next; - if (!src_vma || (src_vma->vm_flags & VM_SHM)) - return -EINVAL; - - src_dir = pgd_offset(tsk->mm, stmp); - if (pgd_none(*src_dir)) - return -EINVAL; - if (pgd_bad(*src_dir)) { - pgd_ERROR(*src_dir); - return -EINVAL; - } - src_middle = pmd_offset(src_dir, stmp); - if (pmd_none(*src_middle)) - return -EINVAL; - if (pmd_bad(*src_middle)) { - pmd_ERROR(*src_middle); - return -EINVAL; - } - src_table = pte_offset(src_middle, stmp); - if (pte_none(*src_table)) - return -EINVAL; - - if (stmp < src_vma->vm_start) { - if (!(src_vma->vm_flags & VM_GROWSDOWN)) - return -EINVAL; - if (src_vma->vm_end - stmp > current->rlim[RLIMIT_STACK].rlim_cur) - return -EINVAL; - } - stmp += PAGE_SIZE; - } - - src_vma = tsk->mm->mmap; - stmp = vma->vm_offset; - dtmp = vma->vm_start; - - flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end); - flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end); - while (dtmp < vma->vm_end) { - while (src_vma && stmp > src_vma->vm_end) - src_vma = src_vma->vm_next; - - src_dir = pgd_offset(tsk->mm, stmp); - src_middle = pmd_offset(src_dir, stmp); - src_table = pte_offset(src_middle, stmp); - - dest_dir = pgd_offset(current->mm, dtmp); - dest_middle = pmd_alloc(dest_dir, dtmp); - if (!dest_middle) - return -ENOMEM; - dest_table = pte_alloc(dest_middle, dtmp); - if (!dest_table) - return -ENOMEM; - - if (!pte_present(*src_table)) - handle_mm_fault(tsk, src_vma, stmp, 1); - - if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table)) - handle_mm_fault(tsk, src_vma, stmp, 1); - - set_pte(src_table, pte_mkdirty(*src_table)); - set_pte(dest_table, *src_table); - mapnr = pte_pagenr(*src_table); - if (mapnr < max_mapnr) - get_page(mem_map + pte_pagenr(*src_table)); - - stmp += PAGE_SIZE; - dtmp += PAGE_SIZE; - } - - flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end); - flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end); - return 0; -} - -static struct file_operations proc_mem_operations = { - mem_lseek, - mem_read, - mem_write, - NULL, /* mem_readdir */ - NULL, /* mem_poll */ - NULL, /* mem_ioctl */ - mem_mmap, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -struct inode_operations proc_mem_inode_operations = { - &proc_mem_operations, /* default base directory file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - proc_permission, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; diff --git a/fs/proc/net.c b/fs/proc/net.c deleted file mode 100644 index b4e18bc49..000000000 --- a/fs/proc/net.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * linux/fs/proc/net.c - * - * Copyright (C) 1991, 1992 Linus Torvalds - * - * gjh 3/'93 heim@peanuts.informatik.uni-tuebingen.de (Gerald J. Heim) - * most of this file is stolen from base.c - * it works, but you shouldn't use it as a guideline - * for new proc-fs entries. once i'll make it better. - * fvk 3/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen) - * cleaned up the whole thing, moved "net" specific code to - * the NET kernel layer (where it belonged in the first place). - * Michael K. Johnson (johnsonm@stolaf.edu) 3/93 - * Added support from my previous inet.c. Cleaned things up - * quite a bit, modularized the code. - * fvk 4/'93 waltje@uwalt.nl.mugnet.org (Fred N. van Kempen) - * Renamed "route_get_info()" to "rt_get_info()" for consistency. - * Alan Cox (gw4pts@gw4pts.ampr.org) 4/94 - * Dusted off the code and added IPX. Fixed the 4K limit. - * Erik Schoenfelder (schoenfr@ibr.cs.tu-bs.de) - * /proc/net/snmp. - * Alan Cox (gw4pts@gw4pts.ampr.org) 1/95 - * Added AppleTalk slots - * - * proc net directory handling functions - */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/fcntl.h> -#include <linux/mm.h> - -#include <asm/uaccess.h> - -#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ - -static long proc_readnet(struct inode * inode, struct file * file, - char * buf, unsigned long count) -{ - char * page; - int bytes=count; - int copied=0; - char *start; - struct proc_dir_entry * dp; - - if (count < 0) - return -EINVAL; - dp = (struct proc_dir_entry *) inode->u.generic_ip; - if (!(page = (char*) __get_free_page(GFP_KERNEL))) - return -ENOMEM; - - while (bytes>0) - { - int length, thistime=bytes; - if (bytes > PROC_BLOCK_SIZE) - thistime=PROC_BLOCK_SIZE; - - length = dp->get_info(page, &start, - file->f_pos, - thistime, - (file->f_flags & O_ACCMODE) == O_RDWR); - - /* - * 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 - */ - copy_to_user(buf+copied, start, length); - file->f_pos += length; /* Move down the file */ - bytes -= length; - copied += length; - if (length<thistime) - break; /* End of file */ - } - free_page((unsigned long) page); - return copied; -} - -static struct file_operations proc_net_operations = { - NULL, /* lseek - default */ - proc_readnet, /* read - bad */ - NULL, /* write - bad */ - NULL, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -/* - * proc directories can do almost nothing.. - */ -struct inode_operations proc_net_inode_operations = { - &proc_net_operations, /* default net file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c new file mode 100644 index 000000000..8ea9813a6 --- /dev/null +++ b/fs/proc/proc_misc.c @@ -0,0 +1,692 @@ +/* + * linux/fs/proc/proc_misc.c + * + * linux/fs/proc/array.c + * Copyright (C) 1992 by Linus Torvalds + * based on ideas by Darren Senn + * + * This used to be the part of array.c. See the rest of history and credits + * there. I took this into a separate file and switched the thing to generic + * proc_file_inode_operations, leaving in array.c only per-process stuff. + * Inumbers allocation made dynamic (via create_proc_entry()). AV, May 1999. + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/tty.h> +#include <linux/string.h> +#include <linux/mman.h> +#include <linux/proc_fs.h> +#include <linux/ioport.h> +#include <linux/config.h> +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <linux/swap.h> +#include <linux/slab.h> +#include <linux/smp.h> +#include <linux/signal.h> +#include <linux/module.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/io.h> + + +#define LOAD_INT(x) ((x) >> FSHIFT) +#define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100) +/* + * Warning: stuff below (imported functions) assumes that its output will fit + * into one page. For some of those functions it may be wrong. Moreover, we + * have a way to deal with that gracefully. Right now I used straightforward + * wrappers, but this needs further analysis wrt potential overflows. + */ +extern int get_cpuinfo(char *); +extern int get_hardware_list(char *); +extern int get_stram_list(char *); +#ifdef CONFIG_DEBUG_MALLOC +extern int get_malloc(char * buffer); +#endif +#ifdef CONFIG_MODULES +extern int get_module_list(char *); +extern int get_ksyms_list(char *, char **, off_t, int); +#endif +extern int get_device_list(char *); +extern int get_partition_list(char *); +extern int get_filesystem_list(char *); +extern int get_filesystem_info(char *); +extern int get_exec_domain_list(char *); +extern int get_irq_list(char *); +extern int get_dma_list(char *); +extern int get_rtc_status (char *); +extern int get_locks_status (char *, char **, off_t, int); +extern int get_swaparea_info (char *); +#ifdef CONFIG_SGI_DS1286 +extern int get_ds1286_status(char *); +#endif + +static int loadavg_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int a, b, c; + int len; + + a = avenrun[0] + (FIXED_1/200); + b = avenrun[1] + (FIXED_1/200); + c = avenrun[2] + (FIXED_1/200); + len = sprintf(page,"%d.%02d %d.%02d %d.%02d %d/%d %d\n", + LOAD_INT(a), LOAD_FRAC(a), + LOAD_INT(b), LOAD_FRAC(b), + LOAD_INT(c), LOAD_FRAC(c), + nr_running, nr_threads, last_pid); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int uptime_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long uptime; + unsigned long idle; + int len; + + uptime = jiffies; + idle = init_tasks[0]->times.tms_utime + init_tasks[0]->times.tms_stime; + + /* The formula for the fraction parts really is ((t * 100) / HZ) % 100, but + that would overflow about every five days at HZ == 100. + Therefore the identity a = (a / b) * b + a % b is used so that it is + calculated as (((t / HZ) * 100) + ((t % HZ) * 100) / HZ) % 100. + The part in front of the '+' always evaluates as 0 (mod 100). All divisions + in the above formulas are truncating. For HZ being a power of 10, the + calculations simplify to the version in the #else part (if the printf + format is adapted to the same number of digits as zeroes in HZ. + */ +#if HZ!=100 + len = sprintf(page,"%lu.%02lu %lu.%02lu\n", + uptime / HZ, + (((uptime % HZ) * 100) / HZ) % 100, + idle / HZ, + (((idle % HZ) * 100) / HZ) % 100); +#else + len = sprintf(page,"%lu.%02lu %lu.%02lu\n", + uptime / HZ, + uptime % HZ, + idle / HZ, + idle % HZ); +#endif + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int meminfo_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct sysinfo i; + int len; + +/* + * display in kilobytes. + */ +#define K(x) ((x) << (PAGE_SHIFT - 10)) +#define B(x) ((x) << PAGE_SHIFT) + si_meminfo(&i); + si_swapinfo(&i); + len = sprintf(page, " total: used: free: shared: buffers: cached:\n" + "Mem: %8lu %8lu %8lu %8lu %8lu %8u\n" + "Swap: %8lu %8lu %8lu\n", + B(i.totalram), B(i.totalram-i.freeram), B(i.freeram), + B(i.sharedram), B(i.bufferram), + B(atomic_read(&page_cache_size)), B(i.totalswap), + B(i.totalswap-i.freeswap), B(i.freeswap)); + /* + * Tagged format, for easy grepping and expansion. + * The above will go away eventually, once the tools + * have been updated. + */ + len += sprintf(page+len, + "MemTotal: %8lu kB\n" + "MemFree: %8lu kB\n" + "MemShared: %8lu kB\n" + "Buffers: %8lu kB\n" + "Cached: %8u kB\n" + "HighTotal: %8lu kB\n" + "HighFree: %8lu kB\n" + "LowTotal: %8lu kB\n" + "LowFree: %8lu kB\n" + "SwapTotal: %8lu kB\n" + "SwapFree: %8lu kB\n", + K(i.totalram), + K(i.freeram), + K(i.sharedram), + K(i.bufferram), + K(atomic_read(&page_cache_size)), + K(i.totalhigh), + K(i.freehigh), + K(i.totalram-i.totalhigh), + K(i.freeram-i.freehigh), + K(i.totalswap), + K(i.freeswap)); + + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +#undef B +#undef K +} + +static int version_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + extern char *linux_banner; + int len; + + strcpy(page, linux_banner); + len = strlen(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int cpuinfo_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_cpuinfo(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +#ifdef CONFIG_PROC_HARDWARE +static int hardware_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_hardware_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#endif + +#ifdef CONFIG_STRAM_PROC +static int stram_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_stram_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#endif + +#ifdef CONFIG_DEBUG_MALLOC +static int malloc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_malloc(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#endif + +#ifdef CONFIG_MODULES +static int modules_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_module_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int ksyms_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_ksyms_list(page, start, off, count); + if (len < count) *eof = 1; + return len; +} +#endif + +static int kstat_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int i, len; + unsigned sum = 0; + extern unsigned long total_forks; + unsigned long jif = jiffies; + + for (i = 0 ; i < NR_IRQS ; i++) + sum += kstat_irqs(i); + +#ifdef __SMP__ + len = sprintf(page, + "cpu %u %u %u %lu\n", + kstat.cpu_user, + kstat.cpu_nice, + kstat.cpu_system, + jif*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system)); + for (i = 0 ; i < smp_num_cpus; i++) + len += sprintf(page + len, "cpu%d %u %u %u %lu\n", + i, + kstat.per_cpu_user[cpu_logical_map(i)], + kstat.per_cpu_nice[cpu_logical_map(i)], + kstat.per_cpu_system[cpu_logical_map(i)], + jif - ( kstat.per_cpu_user[cpu_logical_map(i)] \ + + kstat.per_cpu_nice[cpu_logical_map(i)] \ + + kstat.per_cpu_system[cpu_logical_map(i)])); + len += sprintf(page + len, + "disk %u %u %u %u\n" + "disk_rio %u %u %u %u\n" + "disk_wio %u %u %u %u\n" + "disk_rblk %u %u %u %u\n" + "disk_wblk %u %u %u %u\n" + "page %u %u\n" + "swap %u %u\n" + "intr %u", +#else + len = sprintf(page, + "cpu %u %u %u %lu\n" + "disk %u %u %u %u\n" + "disk_rio %u %u %u %u\n" + "disk_wio %u %u %u %u\n" + "disk_rblk %u %u %u %u\n" + "disk_wblk %u %u %u %u\n" + "page %u %u\n" + "swap %u %u\n" + "intr %u", + kstat.cpu_user, + kstat.cpu_nice, + kstat.cpu_system, + jif*smp_num_cpus - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), +#endif + kstat.dk_drive[0], kstat.dk_drive[1], + kstat.dk_drive[2], kstat.dk_drive[3], + kstat.dk_drive_rio[0], kstat.dk_drive_rio[1], + kstat.dk_drive_rio[2], kstat.dk_drive_rio[3], + kstat.dk_drive_wio[0], kstat.dk_drive_wio[1], + kstat.dk_drive_wio[2], kstat.dk_drive_wio[3], + kstat.dk_drive_rblk[0], kstat.dk_drive_rblk[1], + kstat.dk_drive_rblk[2], kstat.dk_drive_rblk[3], + kstat.dk_drive_wblk[0], kstat.dk_drive_wblk[1], + kstat.dk_drive_wblk[2], kstat.dk_drive_wblk[3], + kstat.pgpgin, + kstat.pgpgout, + kstat.pswpin, + kstat.pswpout, + sum); + for (i = 0 ; i < NR_IRQS ; i++) + len += sprintf(page + len, " %u", kstat_irqs(i)); + len += sprintf(page + len, + "\nctxt %u\n" + "btime %lu\n" + "processes %lu\n", + kstat.context_swtch, + xtime.tv_sec - jif / HZ, + total_forks); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int devices_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_device_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int partitions_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_partition_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int interrupts_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_irq_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int filesystems_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_filesystem_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int dma_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_dma_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int ioports_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_ioport_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int cmdline_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + extern char saved_command_line[]; + int len; + + len = sprintf(page, "%s\n", saved_command_line); + len = strlen(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +#ifdef CONFIG_RTC +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_rtc_status(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#endif + +#ifdef CONFIG_SGI_DS1286 +static int ds1286_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_ds1286_status(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#endif + +static int locks_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_locks_status(page, start, off, count); + if (len < count) *eof = 1; + return len; +} + +static int mounts_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_filesystem_info(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int execdomains_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_exec_domain_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int swaps_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_swaparea_info(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int slabinfo_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_slabinfo(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int memory_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = get_mem_list(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +/* + * This function accesses profiling information. The returned data is + * binary: the sampling step and the actual contents of the profile + * buffer. Use of the program readprofile is recommended in order to + * get meaningful info out of these data. + */ +static ssize_t read_profile(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + unsigned long p = *ppos; + ssize_t read; + char * pnt; + unsigned int sample_step = 1 << prof_shift; + + if (p >= (prof_len+1)*sizeof(unsigned int)) + return 0; + if (count > (prof_len+1)*sizeof(unsigned int) - p) + count = (prof_len+1)*sizeof(unsigned int) - p; + read = 0; + + while (p < sizeof(unsigned int) && count > 0) { + put_user(*((char *)(&sample_step)+p),buf); + buf++; p++; count--; read++; + } + pnt = (char *)prof_buffer + p - sizeof(unsigned int); + copy_to_user(buf,(void *)pnt,count); + read += count; + *ppos += read; + return read; +} + +/* + * Writing to /proc/profile resets the counters + * + * Writing a 'profiling multiplier' value into it also re-sets the profiling + * interrupt frequency, on architectures that support this. + */ +static ssize_t write_profile(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ +#ifdef __SMP__ + extern int setup_profiling_timer (unsigned int multiplier); + + if (count==sizeof(int)) { + unsigned int multiplier; + + if (copy_from_user(&multiplier, buf, sizeof(int))) + return -EFAULT; + + if (setup_profiling_timer(multiplier)) + return -EINVAL; + } +#endif + + memset(prof_buffer, 0, prof_len * sizeof(*prof_buffer)); + return count; +} + +static struct file_operations proc_profile_operations = { + NULL, /* lseek */ + read_profile, + write_profile, +}; + +static struct inode_operations proc_profile_inode_operations = { + &proc_profile_operations, +}; + +static struct proc_dir_entry proc_root_kmsg = { + 0, 4, "kmsg", + S_IFREG | S_IRUSR, 1, 0, 0, + 0, &proc_kmsg_inode_operations +}; +struct proc_dir_entry proc_root_kcore = { + 0, 5, "kcore", + S_IFREG | S_IRUSR, 1, 0, 0, + 0, &proc_kcore_inode_operations +}; +static struct proc_dir_entry proc_root_profile = { + 0, 7, "profile", + S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, + 0, &proc_profile_inode_operations +}; + +void proc_misc_init(void) +{ + static struct { + char *name; + int (*read_proc)(char*,char**,off_t,int,int*,void*); + } *p, simple_ones[] = { + {"loadavg", loadavg_read_proc}, + {"uptime", uptime_read_proc}, + {"meminfo", meminfo_read_proc}, + {"version", version_read_proc}, + {"cpuinfo", cpuinfo_read_proc}, +#ifdef CONFIG_PROC_HARDWARE + {"hardware", hardware_read_proc}, +#endif +#ifdef CONFIG_STRAM_PROC + {"stram", stram_read_proc}, +#endif +#ifdef CONFIG_DEBUG_MALLOC + {"malloc", malloc_read_proc}, +#endif +#ifdef CONFIG_MODULES + {"modules", modules_read_proc}, + {"ksyms", ksyms_read_proc}, +#endif + {"stat", kstat_read_proc}, + {"devices", devices_read_proc}, + {"partitions", partitions_read_proc}, + {"interrupts", interrupts_read_proc}, + {"filesystems", filesystems_read_proc}, + {"dma", dma_read_proc}, + {"ioports", ioports_read_proc}, + {"cmdline", cmdline_read_proc}, +#ifdef CONFIG_RTC + {"rtc", rtc_read_proc}, +#endif +#ifdef CONFIG_SGI_DS1286 + {"rtc", ds1286_read_proc}, +#endif + {"locks", locks_read_proc}, + {"mounts", mounts_read_proc}, + {"swaps", swaps_read_proc}, + {"slabinfo", slabinfo_read_proc}, + {"iomem", memory_read_proc}, + {"execdomains", execdomains_read_proc}, + {NULL,NULL} + }; + for(p=simple_ones;p->name;p++) + create_proc_read_entry(p->name, 0, NULL, p->read_proc, NULL); + + /* And now for trickier ones */ + proc_register(&proc_root, &proc_root_kmsg); + proc_register(&proc_root, &proc_root_kcore); + proc_root_kcore.size = (size_t)high_memory - PAGE_OFFSET + PAGE_SIZE; + if (prof_shift) { + proc_register(&proc_root, &proc_root_profile); + proc_root_profile.size = (1+prof_len) * sizeof(unsigned int); + } +} diff --git a/fs/proc/procfs_syms.c b/fs/proc/procfs_syms.c index c6d0032c5..fa2ee362e 100644 --- a/fs/proc/procfs_syms.c +++ b/fs/proc/procfs_syms.c @@ -3,15 +3,7 @@ #include <linux/fs.h> #include <linux/proc_fs.h> -/* - * This is all required so that if we load all of scsi as a module, - * that the scsi code will be able to talk to the /proc/scsi handling - * in the procfs. - */ -extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, - off_t offset, int length, int inout); -extern struct inode_operations proc_scsi_inode_operations; -extern struct proc_dir_entry proc_sys_root; +extern struct proc_dir_entry *proc_sys_root; #ifdef CONFIG_SYSCTL EXPORT_SYMBOL(proc_sys_root); @@ -24,18 +16,9 @@ EXPORT_SYMBOL(proc_root); EXPORT_SYMBOL(proc_root_fs); EXPORT_SYMBOL(proc_get_inode); EXPORT_SYMBOL(proc_dir_inode_operations); -EXPORT_SYMBOL(proc_net_inode_operations); EXPORT_SYMBOL(proc_net); EXPORT_SYMBOL(proc_bus); -/* - * This is required so that if we load scsi later, that the - * scsi code can attach to /proc/scsi in the correct manner. - */ -EXPORT_SYMBOL(proc_scsi); -EXPORT_SYMBOL(proc_scsi_inode_operations); -EXPORT_SYMBOL(dispatch_scsi_info_ptr); - #if defined(CONFIG_SUN_OPENPROMFS_MODULE) EXPORT_SYMBOL(proc_openprom_register); EXPORT_SYMBOL(proc_openprom_deregister); diff --git a/fs/proc/root.c b/fs/proc/root.c index 84478312c..b87b0fb4f 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -22,11 +22,6 @@ #include <linux/zorro.h> #endif -/* - * Offset of the first process in the /proc root directory.. - */ -#define FIRST_PROCESS_ENTRY 256 - static int proc_root_readdir(struct file *, void *, filldir_t); static struct dentry *proc_root_lookup(struct inode *,struct dentry *); static int proc_unlink(struct inode *, struct dentry *); @@ -37,22 +32,12 @@ 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, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ }; /* @@ -62,23 +47,6 @@ 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, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ }; /* @@ -90,21 +58,6 @@ struct inode_operations proc_dyna_dir_inode_operations = { proc_lookup, /* lookup */ NULL, /* link */ proc_unlink, /* unlink(struct inode *, struct dentry *) */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ }; /* @@ -117,13 +70,6 @@ static struct file_operations proc_root_operations = { NULL, /* read - bad */ NULL, /* write - bad */ proc_root_readdir, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* no fsync */ }; /* @@ -133,23 +79,6 @@ 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, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ }; /* @@ -164,27 +93,14 @@ struct proc_dir_entry proc_root = { &proc_root, NULL }; -struct proc_dir_entry *proc_net, *proc_scsi, *proc_bus, *proc_sysvipc; +struct proc_dir_entry *proc_net, *proc_bus, *proc_root_fs, *proc_root_driver; #ifdef CONFIG_MCA -struct proc_dir_entry proc_mca = { - PROC_MCA, 3, "mca", - 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_mca; #endif #ifdef CONFIG_SYSCTL -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 */ -}; +struct proc_dir_entry *proc_sys_root; #endif #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) @@ -280,36 +196,12 @@ static struct file_operations proc_openprom_operations = { NULL, /* read - bad */ NULL, /* write - bad */ OPENPROM_DEFREADDIR, /* readdir */ - NULL, /* poll - default */ - NULL, /* ioctl - default */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ }; struct inode_operations proc_openprom_inode_operations = { &proc_openprom_operations,/* default net directory file-ops */ NULL, /* create */ OPENPROM_DEFLOOKUP, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ }; struct proc_dir_entry proc_openprom = { @@ -333,6 +225,73 @@ static int make_inode_number(void) return PROC_DYNAMIC_FIRST + i; } +int proc_readlink(struct dentry * dentry, char * buffer, int buflen) +{ + struct inode *inode = dentry->d_inode; + struct proc_dir_entry * de; + char *page; + int len = 0; + + de = (struct proc_dir_entry *) inode->u.generic_ip; + if (!de) + return -ENOENT; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + + if (de->readlink_proc) + len = de->readlink_proc(de, page); + + if (len > buflen) + len = buflen; + + copy_to_user(buffer, page, len); + free_page((unsigned long) page); + return len; +} + +struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) +{ + struct inode *inode = dentry->d_inode; + struct proc_dir_entry * de; + char *page; + struct dentry *d; + int len = 0; + + de = (struct proc_dir_entry *) inode->u.generic_ip; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return NULL; + + if (de->readlink_proc) + len = de->readlink_proc(de, page); + + d = lookup_dentry(page, base, follow); + free_page((unsigned long) page); + return d; +} + +static struct inode_operations proc_link_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_readlink, /* readlink */ + proc_follow_link, /* follow_link */ + NULL, /* get_block */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* flushpage */ + NULL, /* truncate */ + NULL, /* permission */ + NULL, /* smap */ + NULL /* revalidate */ +}; + int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) { int i; @@ -445,50 +404,6 @@ static struct dentry * proc_self_follow_link(struct dentry *dentry, return lookup_dentry(tmp, base, follow); } -int proc_readlink(struct dentry * dentry, char * buffer, int buflen) -{ - struct inode *inode = dentry->d_inode; - struct proc_dir_entry * de; - char *page; - int len = 0; - - de = (struct proc_dir_entry *) inode->u.generic_ip; - if (!de) - return -ENOENT; - if (!(page = (char*) __get_free_page(GFP_KERNEL))) - return -ENOMEM; - - if (de->readlink_proc) - len = de->readlink_proc(de, page); - - if (len > buflen) - len = buflen; - - copy_to_user(buffer, page, len); - free_page((unsigned long) page); - return len; -} - -struct dentry * proc_follow_link(struct dentry * dentry, struct dentry *base, unsigned int follow) -{ - struct inode *inode = dentry->d_inode; - struct proc_dir_entry * de; - char *page; - struct dentry *d; - int len = 0; - - de = (struct proc_dir_entry *) inode->u.generic_ip; - if (!(page = (char*) __get_free_page(GFP_KERNEL))) - return NULL; - - if (de->readlink_proc) - len = de->readlink_proc(de, page); - - d = lookup_dentry(page, base, follow); - free_page((unsigned long) page); - return d; -} - static struct inode_operations proc_self_inode_operations = { NULL, /* no file-ops */ NULL, /* create */ @@ -512,286 +427,41 @@ static struct inode_operations proc_self_inode_operations = { NULL /* revalidate */ }; -static struct inode_operations proc_link_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_readlink, /* readlink */ - proc_follow_link, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -static struct proc_dir_entry proc_root_loadavg = { - PROC_LOADAVG, 7, "loadavg", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_uptime = { - PROC_UPTIME, 6, "uptime", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_meminfo = { - PROC_MEMINFO, 7, "meminfo", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_kmsg = { - PROC_KMSG, 4, "kmsg", - S_IFREG | S_IRUSR, 1, 0, 0, - 0, &proc_kmsg_inode_operations -}; -static struct proc_dir_entry proc_root_version = { - PROC_VERSION, 7, "version", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_cpuinfo = { - PROC_CPUINFO, 7, "cpuinfo", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#if defined (CONFIG_PROC_HARDWARE) -static struct proc_dir_entry proc_root_hardware = { - PROC_HARDWARE, 8, "hardware", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif -#ifdef CONFIG_STRAM_PROC -static struct proc_dir_entry proc_root_stram = { - PROC_STRAM, 5, "stram", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif static struct proc_dir_entry proc_root_self = { - PROC_SELF, 4, "self", + 0, 4, "self", S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0, 64, &proc_self_inode_operations, }; -#ifdef CONFIG_DEBUG_MALLOC -static struct proc_dir_entry proc_root_malloc = { - PROC_MALLOC, 6, "malloc", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif -static struct proc_dir_entry proc_root_kcore = { - PROC_KCORE, 5, "kcore", - S_IFREG | S_IRUSR, 1, 0, 0, - 0, &proc_kcore_inode_operations -}; -#ifdef CONFIG_MODULES -static struct proc_dir_entry proc_root_modules = { - PROC_MODULES, 7, "modules", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_ksyms = { - PROC_KSYMS, 5, "ksyms", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif -static struct proc_dir_entry proc_root_stat = { - PROC_STAT, 4, "stat", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_devices = { - PROC_DEVICES, 7, "devices", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_partitions = { - PROC_PARTITIONS, 10, "partitions", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_interrupts = { - PROC_INTERRUPTS, 10,"interrupts", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_filesystems = { - PROC_FILESYSTEMS, 11,"filesystems", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -struct proc_dir_entry proc_root_fs = { - PROC_FS, 2, "fs", - S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, - 0, &proc_dir_inode_operations, - NULL, NULL, - NULL, - NULL, NULL -}; -struct proc_dir_entry proc_root_driver = { - PROC_DRIVER, 6, "driver", - S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, - 0, &proc_dir_inode_operations, - NULL, NULL, - NULL, - NULL, NULL -}; -static struct proc_dir_entry proc_root_dma = { - PROC_DMA, 3, "dma", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_ioports = { - PROC_IOPORTS, 7, "ioports", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_iomem = { - PROC_MEMORY, 5, "iomem", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_cmdline = { - PROC_CMDLINE, 7, "cmdline", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#ifdef CONFIG_RTC -static struct proc_dir_entry proc_root_rtc = { - PROC_RTC, 3, "rtc", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif -#ifdef CONFIG_SGI_DS1286 -static struct proc_dir_entry proc_root_ds1286 = { - PROC_RTC, 3, "rtc", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -#endif -static struct proc_dir_entry proc_root_locks = { - PROC_LOCKS, 5, "locks", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_mounts = { - PROC_MTAB, 6, "mounts", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_swaps = { - PROC_SWAP, 5, "swaps", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; -static struct proc_dir_entry proc_root_profile = { - PROC_PROFILE, 7, "profile", - S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, - 0, &proc_profile_inode_operations -}; -static struct proc_dir_entry proc_root_slab = { - PROC_SLABINFO, 8, "slabinfo", - S_IFREG | S_IRUGO, 1, 0, 0, - 0, &proc_array_inode_operations -}; #ifdef __powerpc__ static struct proc_dir_entry proc_root_ppc_htab = { - PROC_PPC_HTAB, 8, "ppc_htab", + 0, 8, "ppc_htab", S_IFREG | S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 1, 0, 0, 0, &proc_ppc_htab_inode_operations, - NULL, NULL, /* get_info, fill_inode */ - NULL, /* next */ - NULL, NULL /* parent, subdir */ }; #endif void __init proc_root_init(void) { - proc_base_init(); - proc_register(&proc_root, &proc_root_loadavg); - proc_register(&proc_root, &proc_root_uptime); - proc_register(&proc_root, &proc_root_meminfo); - proc_register(&proc_root, &proc_root_kmsg); - proc_register(&proc_root, &proc_root_version); - proc_register(&proc_root, &proc_root_cpuinfo); + proc_misc_init(); proc_register(&proc_root, &proc_root_self); proc_net = create_proc_entry("net", S_IFDIR, 0); - proc_scsi = create_proc_entry("scsi", S_IFDIR, 0); #ifdef CONFIG_SYSVIPC - proc_sysvipc = create_proc_entry("sysvipc", S_IFDIR, 0); + create_proc_entry("sysvipc", S_IFDIR, 0); #endif #ifdef CONFIG_SYSCTL - proc_register(&proc_root, &proc_sys_root); + proc_sys_root = create_proc_entry("sys", S_IFDIR, 0); #endif #ifdef CONFIG_MCA - proc_register(&proc_root, &proc_mca); -#endif - -#ifdef CONFIG_DEBUG_MALLOC - proc_register(&proc_root, &proc_root_malloc); -#endif - proc_register(&proc_root, &proc_root_kcore); - proc_root_kcore.size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE; - -#ifdef CONFIG_MODULES - proc_register(&proc_root, &proc_root_modules); - proc_register(&proc_root, &proc_root_ksyms); -#endif - proc_register(&proc_root, &proc_root_stat); - proc_register(&proc_root, &proc_root_devices); - proc_register(&proc_root, &proc_root_driver); - proc_register(&proc_root, &proc_root_partitions); - proc_register(&proc_root, &proc_root_interrupts); - proc_register(&proc_root, &proc_root_filesystems); - proc_register(&proc_root, &proc_root_fs); - proc_register(&proc_root, &proc_root_dma); - proc_register(&proc_root, &proc_root_ioports); - proc_register(&proc_root, &proc_root_iomem); - proc_register(&proc_root, &proc_root_cmdline); -#ifdef CONFIG_RTC - proc_register(&proc_root, &proc_root_rtc); -#endif -#ifdef CONFIG_SGI_DS1286 - proc_register(&proc_root, &proc_root_ds1286); + proc_mca = create_proc_entry("mca", S_IFDIR, 0); #endif - proc_register(&proc_root, &proc_root_locks); - - proc_register(&proc_root, &proc_root_mounts); - proc_register(&proc_root, &proc_root_swaps); - + proc_root_fs = create_proc_entry("fs", S_IFDIR, 0); + proc_root_driver = create_proc_entry("driver", S_IFDIR, 0); #if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE) #ifdef CONFIG_SUN_OPENPROMFS openpromfs_init (); #endif proc_register(&proc_root, &proc_openprom); #endif -#ifdef CONFIG_PROC_HARDWARE - proc_register(&proc_root, &proc_root_hardware); -#endif -#ifdef CONFIG_STRAM_PROC - proc_register(&proc_root, &proc_root_stram); -#endif - proc_register(&proc_root, &proc_root_slab); - - if (prof_shift) { - proc_register(&proc_root, &proc_root_profile); - proc_root_profile.size = (1+prof_len) * sizeof(unsigned int); - } - proc_tty_init(); #ifdef __powerpc__ proc_register(&proc_root, &proc_root_ppc_htab); @@ -799,7 +469,6 @@ void __init proc_root_init(void) #ifdef CONFIG_PROC_DEVICETREE proc_device_tree_init(); #endif - proc_bus = create_proc_entry("bus", S_IFDIR, 0); } @@ -843,7 +512,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry) if (de->namelen != dentry->d_name.len) continue; if (!memcmp(dentry->d_name.name, de->name, de->namelen)) { - int ino = de->low_ino | (dir->i_ino & ~(0xffff)); + int ino = de->low_ino; error = -EINVAL; inode = proc_get_inode(dir->i_sb, ino, de); break; @@ -861,11 +530,7 @@ struct dentry *proc_lookup(struct inode * dir, struct dentry *dentry) static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentry) { - unsigned int pid, c; struct task_struct *p; - const char *name; - struct inode *inode; - int len; if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */ extern unsigned long total_forks; @@ -896,40 +561,7 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr if (!proc_lookup(dir, dentry)) return NULL; - pid = 0; - name = dentry->d_name.name; - len = dentry->d_name.len; - while (len-- > 0) { - c = *name - '0'; - name++; - if (c > 9) { - pid = 0; - break; - } - pid *= 10; - pid += c; - if (!pid) - break; - if (pid & 0xffff0000) { - pid = 0; - break; - } - } - read_lock(&tasklist_lock); - p = find_task_by_pid(pid); - read_unlock(&tasklist_lock); - inode = NULL; - if (pid && p) { - unsigned long ino = (pid << 16) + PROC_PID_INO; - inode = proc_get_inode(dir->i_sb, ino, &proc_pid); - if (!inode) - return ERR_PTR(-EINVAL); - inode->i_flags|=S_IMMUTABLE; - } - - dentry->d_op = &proc_dentry_operations; - d_add(dentry, inode); - return NULL; + return proc_pid_lookup(dir, dentry); } /* @@ -968,7 +600,6 @@ int proc_readdir(struct file * filp, filp->f_pos++; /* fall through */ default: - ino &= ~0xffff; de = de->subdir; i -= 2; for (;;) { @@ -981,7 +612,7 @@ int proc_readdir(struct file * filp, } do { - if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino | de->low_ino) < 0) + if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino) < 0) return 0; filp->f_pos++; de = de->next; @@ -990,69 +621,19 @@ int proc_readdir(struct file * filp, return 1; } -#define PROC_NUMBUF 10 -#define PROC_MAXPIDS 20 - -/* - * Get a few pid's to return for filldir - we need to hold the - * tasklist lock while doing this, and we must release it before - * we actually do the filldir itself, so we use a temp buffer.. - */ -static int get_pid_list(int index, unsigned int *pids) -{ - struct task_struct *p; - int nr_pids = 0; - - index -= FIRST_PROCESS_ENTRY; - read_lock(&tasklist_lock); - for_each_task(p) { - int pid = p->pid; - if (!pid) - continue; - if (--index >= 0) - continue; - pids[nr_pids] = pid; - nr_pids++; - if (nr_pids >= PROC_MAXPIDS) - break; - } - read_unlock(&tasklist_lock); - return nr_pids; -} - static int proc_root_readdir(struct file * filp, void * dirent, filldir_t filldir) { - unsigned int pid_array[PROC_MAXPIDS]; - char buf[PROC_NUMBUF]; unsigned int nr = filp->f_pos; - unsigned int nr_pids, i; if (nr < FIRST_PROCESS_ENTRY) { int error = proc_readdir(filp, dirent, filldir); if (error <= 0) return error; - filp->f_pos = nr = FIRST_PROCESS_ENTRY; + filp->f_pos = FIRST_PROCESS_ENTRY; } - nr_pids = get_pid_list(nr, pid_array); - - for (i = 0; i < nr_pids; i++) { - int pid = pid_array[i]; - ino_t ino = (pid << 16) + PROC_PID_INO; - unsigned long j = PROC_NUMBUF; - - do { - j--; - buf[j] = '0' + (pid % 10); - pid /= 10; - } while (pid); - - if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino) < 0) - break; - filp->f_pos++; - } - return 0; + return proc_pid_readdir(filp, dirent, filldir); } static int proc_unlink(struct inode *dir, struct dentry *dentry) diff --git a/fs/proc/scsi.c b/fs/proc/scsi.c deleted file mode 100644 index 679fa383f..000000000 --- a/fs/proc/scsi.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * linux/fs/proc/scsi.c - * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de - * - * The original version was derived from linux/fs/proc/net.c, - * which is Copyright (C) 1991, 1992 Linus Torvalds. - * Much has been rewritten, but some of the code still remains. - * - * /proc/scsi directory handling functions - * - * last change: 95/07/04 - * - * Initial version: March '95 - * 95/05/15 Added subdirectories for each driver and show every - * registered HBA as a single file. - * 95/05/30 Added rudimentary write support for parameter passing - * 95/07/04 Fixed bugs in directory handling - * 95/09/13 Update to support the new proc-dir tree - * - * TODO: Improve support to write to the driver files - * Add some more comments - */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/mm.h> - -#include <asm/uaccess.h> - -/* forward references */ -static ssize_t proc_readscsi(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t proc_writescsi(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static long long proc_scsilseek(struct file *, long long, int); - -extern void build_proc_dir_hba_entries(uint); - -/* the *_get_info() functions are in the respective scsi driver code */ -int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start, - off_t offset, int length, int inout) = 0; - -static struct file_operations proc_scsi_operations = { - proc_scsilseek, /* lseek */ - proc_readscsi, /* read */ - proc_writescsi, /* write */ - proc_readdir, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - NULL, /* mmap */ - NULL, /* no special open code */ - NULL, /* flush */ - NULL, /* no special release code */ - NULL /* can't fsync */ -}; - -/* - * proc directories can do almost nothing.. - */ -struct inode_operations proc_scsi_inode_operations = { -&proc_scsi_operations, /* default scsi 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, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; - -int get_not_present_info(char *buffer, char **start, off_t offset, int length) -{ - int len, pos, begin; - - begin = 0; - pos = len = sprintf(buffer, - "No low-level scsi modules are currently present\n"); - if(pos < offset) { - len = 0; - begin = pos; - } - - *start = buffer + (offset - begin); /* Start of wanted data */ - len -= (offset - begin); - if(len > length) - len = length; - - return(len); -} - -#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines - * use some slack for overruns - */ - -static ssize_t proc_readscsi(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - ssize_t length; - ssize_t bytes = count; - ssize_t copied = 0; - ssize_t thistime; - char * page; - char * start; - - if (!(page = (char *) __get_free_page(GFP_KERNEL))) - return(-ENOMEM); - - while (bytes > 0) { - thistime = bytes; - if(bytes > PROC_BLOCK_SIZE) - thistime = PROC_BLOCK_SIZE; - - if(dispatch_scsi_info_ptr) - length = dispatch_scsi_info_ptr(inode->i_ino, page, &start, - *ppos, thistime, 0); - else - length = get_not_present_info(page, &start, *ppos, thistime); - if(length < 0) { - free_page((ulong) page); - return(length); - } - - /* - * 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 - */ - copy_to_user(buf + copied, start, length); - *ppos += length; /* Move down the file */ - bytes -= length; - copied += length; - - if(length < thistime) - break; /* End of file */ - - } - - free_page((ulong) page); - return(copied); -} - - -static ssize_t proc_writescsi(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - ssize_t ret = 0; - char * page; - - if(count > PROC_BLOCK_SIZE) { - return(-EOVERFLOW); - } - - if(dispatch_scsi_info_ptr != NULL) { - if (!(page = (char *) __get_free_page(GFP_KERNEL))) - return(-ENOMEM); - copy_from_user(page, buf, count); - ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1); - } else - return(-ENOPKG); /* Nothing here */ - - free_page((ulong) page); - return(ret); -} - - -static long long proc_scsilseek(struct file * file, long long offset, int orig) -{ - switch (orig) { - case 0: - file->f_pos = offset; - return(file->f_pos); - case 1: - file->f_pos += offset; - return(file->f_pos); - case 2: - return(-EINVAL); - default: - return(-EINVAL); - } -} - -/* - * Overrides for Emacs so that we almost follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-indent-level: 4 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -4 - * c-argdecl-indent: 4 - * c-label-offset: -4 - * c-continued-statement-offset: 4 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 - * End: - */ diff --git a/fs/proc/sysvipc.c b/fs/proc/sysvipc.c deleted file mode 100644 index 7fff0ed03..000000000 --- a/fs/proc/sysvipc.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * linux/fs/proc/sysvipc.c - * - * Copyright (c) 1999 Dragos Acostachioaie - * - * This code is derived from linux/fs/proc/generic.c, - * which is Copyright (C) 1991, 1992 Linus Torvalds. - * - * /proc/sysvipc directory handling functions - */ -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/mm.h> - -#include <asm/uaccess.h> - -#ifndef MIN -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) -#endif - -/* 4K page size but our output routines use some slack for overruns */ -#define PROC_BLOCK_SIZE (3*1024) - -static ssize_t -proc_sysvipc_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos) -{ - struct inode * inode = file->f_dentry->d_inode; - char *page; - ssize_t retval=0; - int eof=0; - ssize_t n, count; - char *start; - struct proc_dir_entry * dp; - - dp = (struct proc_dir_entry *) inode->u.generic_ip; - if (!(page = (char*) __get_free_page(GFP_KERNEL))) - return -ENOMEM; - - while ((nbytes > 0) && !eof) - { - count = MIN(PROC_BLOCK_SIZE, nbytes); - - start = NULL; - if (dp->get_info) { - /* - * Handle backwards compatibility with the old net - * routines. - * - * XXX What gives with the file->f_flags & O_ACCMODE - * test? Seems stupid to me.... - */ - n = dp->get_info(page, &start, *ppos, count, - (file->f_flags & O_ACCMODE) == O_RDWR); - if (n < count) - eof = 1; - } else if (dp->read_proc) { - n = dp->read_proc(page, &start, *ppos, - count, &eof, dp->data); - } else - break; - - if (!start) { - /* - * For proc files that are less than 4k - */ - start = page + *ppos; - n -= *ppos; - if (n <= 0) - break; - if (n > count) - n = count; - } - if (n == 0) - break; /* End of file */ - if (n < 0) { - if (retval == 0) - retval = n; - break; - } - - /* This is a hack to allow mangling of file pos independent - * of actual bytes read. Simply place the data at page, - * return the bytes, and set `start' to the desired offset - * as an unsigned int. - Paul.Russell@rustcorp.com.au - */ - n -= copy_to_user(buf, start < page ? page : start, n); - if (n == 0) { - if (retval == 0) - retval = -EFAULT; - break; - } - - *ppos += start < page ? (long)start : n; /* Move down the file */ - nbytes -= n; - buf += n; - retval += n; - } - free_page((unsigned long) page); - return retval; -} - -static struct file_operations proc_sysvipc_operations = { - NULL, /* lseek */ - proc_sysvipc_read, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - NULL, /* ioctl */ - 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_sysvipc_inode_operations = { - &proc_sysvipc_operations, /* default net file-ops */ - NULL, /* create */ - NULL, /* lookup */ - NULL, /* link */ - NULL, /* unlink */ - NULL, /* symlink */ - NULL, /* mkdir */ - NULL, /* rmdir */ - NULL, /* mknod */ - NULL, /* rename */ - NULL, /* readlink */ - NULL, /* follow_link */ - NULL, /* get_block */ - NULL, /* readpage */ - NULL, /* writepage */ - NULL, /* flushpage */ - NULL, /* truncate */ - NULL, /* permission */ - NULL, /* smap */ - NULL /* revalidate */ -}; |