URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/or1k/trunk/rc203soc/sw/uClinux/fs/proc
- from Rev 1628 to Rev 1765
- ↔ Reverse comparison
Rev 1628 → Rev 1765
/array.c
0,0 → 1,1269
/* |
* linux/fs/proc/array.c |
* |
* Copyright (C) 1992 by Linus Torvalds |
* based on ideas by Darren Senn |
* |
* Fixes: |
* Michael. K. Johnson: stat,statm extensions. |
* <johnsonm@stolaf.edu> |
* |
* Pauline Middelink : Made cmdline,envline only break at '\0's, to |
* make sure SET_PROCTITLE works. Also removed |
* bad '!' which forced address recalculation for |
* EVERY character on the current page. |
* <middelin@polyware.iaf.nl> |
* |
* Danny ter Haar : added cpuinfo |
* <dth@cistron.nl> |
* |
* Alessandro Rubini : profile extension. |
* <rubini@ipvvis.unipv.it> |
* |
* Jeff Tranter : added BogoMips field to cpuinfo |
* <Jeff_Tranter@Mitel.COM> |
* |
* Bruno Haible : remove 4K limit for the maps file |
* <haible@ma2s2.mathematik.uni-karlsruhe.de> |
* |
* Yves Arrouye : remove removal of trailing spaces in get_array. |
* <Yves.Arrouye@marin.fdn.fr> |
* |
* Alan Cox : security fixes. <Alan.Cox@linux.org> |
*/ |
|
/* |
* uClinux revisions for NO_MM |
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, |
* The Silver Hammer Group, Ltd. |
*/ |
|
#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/user.h> |
#include <linux/a.out.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 <asm/segment.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 |
|
extern unsigned long get_wchan(struct task_struct *); |
|
static int read_core(struct inode * inode, struct file * file,char * buf, int count) |
{ |
unsigned long p = file->f_pos, memsize; |
int read; |
int count1; |
char * pnt; |
struct user dump; |
#ifdef __i386__ |
# 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 = MAP_NR(high_memory); |
#ifdef __alpha__ |
dump.start_data = PAGE_OFFSET; |
#endif |
|
if (count < 0) |
return -EINVAL; |
memsize = MAP_NR(high_memory + PAGE_SIZE) << 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; |
memcpy_tofs(buf,(void *) pnt, count1); |
buf += count1; |
p += count1; |
count -= count1; |
read += count1; |
} |
|
while (count > 0 && p < PAGE_SIZE + FIRST_MAPPED) { |
put_user(0,buf); |
buf++; |
p++; |
count--; |
read++; |
} |
memcpy_tofs(buf, (void *) (PAGE_OFFSET + p - PAGE_SIZE), count); |
read += count; |
file->f_pos += read; |
return read; |
} |
|
static struct file_operations proc_kcore_operations = { |
NULL, /* lseek */ |
read_core, |
}; |
|
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 int read_profile(struct inode *inode, struct file *file, char *buf, int count) |
{ |
unsigned long p = file->f_pos; |
int read; |
char * pnt; |
unsigned int sample_step = 1 << prof_shift; |
|
if (count < 0) |
return -EINVAL; |
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); |
memcpy_tofs(buf,(void *)pnt,count); |
read += count; |
file->f_pos += read; |
return read; |
} |
|
/* Writing to /proc/profile resets the counters */ |
static int write_profile(struct inode * inode, struct file * file, const char * buf, int count) |
{ |
int i=prof_len; |
|
while (i--) |
prof_buffer[i]=0UL; |
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_tasks, last_pid); |
} |
|
static int get_kstat(char * buffer) |
{ |
int i, len; |
unsigned sum = 0; |
extern unsigned long total_forks; |
|
for (i = 0 ; i < NR_IRQS ; i++) |
sum += kstat.interrupts[i]; |
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", |
kstat.cpu_user, |
kstat.cpu_nice, |
kstat.cpu_system, |
jiffies - (kstat.cpu_user + kstat.cpu_nice + kstat.cpu_system), |
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.interrupts[i]); |
len += sprintf(buffer + len, |
"\nctxt %u\n" |
"btime %lu\n" |
"processes %lu\n", |
kstat.context_swtch, |
xtime.tv_sec - jiffies / HZ, |
total_forks); |
return len; |
} |
|
|
static int get_uptime(char * buffer) |
{ |
unsigned long uptime; |
unsigned long idle; |
|
uptime = jiffies; |
idle = task[0]->utime + task[0]->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; |
|
si_meminfo(&i); |
si_swapinfo(&i); |
len = sprintf(buffer, " total: used: free: shared: buffers: cached:\n" |
"Mem: %8lu %8lu %8lu %8lu %8lu %8lu\n" |
"Swap: %8lu %8lu %8lu\n", |
i.totalram, i.totalram-i.freeram, i.freeram, i.sharedram, i.bufferram, page_cache_size*PAGE_SIZE, |
i.totalswap, i.totalswap-i.freeswap, 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: %8lu kB\n" |
"SwapTotal: %8lu kB\n" |
"SwapFree: %8lu kB\n", |
i.totalram >> 10, |
i.freeram >> 10, |
i.sharedram >> 10, |
i.bufferram >> 10, |
page_cache_size << (PAGE_SHIFT - 10), |
i.totalswap >> 10, |
i.freeswap >> 10); |
} |
|
static int get_version(char * buffer) |
{ |
extern const 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 task_struct ** get_task(pid_t pid) |
{ |
struct task_struct ** p; |
|
p = task; |
while (++p < task+NR_TASKS) { |
if (*p && (*p)->pid == pid) |
return p; |
} |
return NULL; |
} |
|
static unsigned long get_phys_addr(struct task_struct * p, unsigned long ptr) |
{ |
#ifndef NO_MM |
pgd_t *page_dir; |
pmd_t *page_middle; |
pte_t pte; |
|
if (!p || !p->mm || ptr >= TASK_SIZE) |
return 0; |
page_dir = pgd_offset(p->mm,ptr); |
if (pgd_none(*page_dir)) |
return 0; |
if (pgd_bad(*page_dir)) { |
printk("bad page directory entry %08lx\n", pgd_val(*page_dir)); |
pgd_clear(page_dir); |
return 0; |
} |
page_middle = pmd_offset(page_dir,ptr); |
if (pmd_none(*page_middle)) |
return 0; |
if (pmd_bad(*page_middle)) { |
printk("bad page middle entry %08lx\n", pmd_val(*page_middle)); |
pmd_clear(page_middle); |
return 0; |
} |
pte = *pte_offset(page_middle,ptr); |
if (!pte_present(pte)) |
return 0; |
return pte_page(pte) + (ptr & ~PAGE_MASK); |
#else /* NO_MM */ |
return ptr; |
#endif /* NO_MM */ |
} |
|
static int get_array(struct task_struct ** p, unsigned long start, unsigned long end, char * buffer) |
{ |
unsigned long addr; |
int size = 0, result = 0; |
char c; |
|
if (start >= end) |
return result; |
for (;;) { |
addr = get_phys_addr(*p, start); |
if (!addr) |
return result; |
do { |
c = *(char *) addr; |
if (!c) |
result = size; |
if (size < PAGE_SIZE) |
buffer[size++] = c; |
else |
return result; |
addr++; |
start++; |
if (!c && start >= end) |
return result; |
} while (addr & ~PAGE_MASK); |
} |
return result; |
} |
|
static int get_env(int pid, char * buffer) |
{ |
struct task_struct ** p = get_task(pid); |
|
if (!p || !*p || !(*p)->mm) |
return 0; |
return get_array(p, (*p)->mm->env_start, (*p)->mm->env_end, buffer); |
} |
|
static int get_arg(int pid, char * buffer) |
{ |
struct task_struct ** p = get_task(pid); |
|
if (!p || !*p || !(*p)->mm) |
return 0; |
return get_array(p, (*p)->mm->arg_start, (*p)->mm->arg_end, buffer); |
} |
|
#if defined(__i386__) |
# define KSTK_EIP(tsk) (((unsigned long *)tsk->kernel_stack_page)[1019]) |
# define KSTK_ESP(tsk) (((unsigned long *)tsk->kernel_stack_page)[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 *)(tsk->kernel_stack_page + PT_REG(pc))) |
# define KSTK_ESP(tsk) ((tsk) == current ? rdusp() : (tsk)->tss.usp) |
#elif defined(__sparc__) |
# define PT_REG(reg) (PAGE_SIZE - sizeof(struct pt_regs) \ |
+ (long)&((struct pt_regs *)0)->reg) |
# define KSTK_EIP(tsk) (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(pc))) |
# define KSTK_ESP(tsk) (*(unsigned long *)(tsk->kernel_stack_page + PT_REG(u_regs[UREG_FP]))) |
#endif |
|
/* Gcc optimizes away "strlen(x)" for constant x */ |
#define ADDBUF(buffer, string) \ |
do { memcpy(buffer, string, strlen(string)); \ |
buffer += strlen(string); } while (0) |
|
static inline char * task_name(struct task_struct *p, char * buf) |
{ |
int i; |
char * name; |
|
ADDBUF(buf, "Name:\t"); |
name = p->comm; |
i = sizeof(p->comm); |
do { |
unsigned char c = *name; |
name++; |
i--; |
*buf = c; |
if (!c) |
break; |
if (c == '\\') { |
buf[1] = c; |
buf += 2; |
continue; |
} |
if (c == '\n') { |
buf[0] = '\\'; |
buf[1] = 'n'; |
buf += 2; |
continue; |
} |
buf++; |
} while (i); |
*buf = '\n'; |
return buf+1; |
} |
|
static inline char * task_state(struct task_struct *p, char *buffer) |
{ |
#define NR_STATES (sizeof(states)/sizeof(const char *)) |
unsigned int n = p->state; |
static const char * states[] = { |
"R (running)", |
"S (sleeping)", |
"D (disk sleep)", |
"Z (zombie)", |
"T (stopped)", |
"W (paging)", |
". Huh?" |
}; |
|
if (n >= NR_STATES) |
n = NR_STATES-1; |
|
buffer += sprintf(buffer, |
"State:\t%s\n" |
"Pid:\t%d\n" |
"PPid:\t%d\n" |
"Uid:\t%d\t%d\t%d\t%d\n" |
"Gid:\t%d\t%d\t%d\t%d\n", |
states[n], |
p->pid, p->p_pptr->pid, |
p->uid, p->euid, p->suid, p->fsuid, |
p->gid, p->egid, p->sgid, p->fsgid); |
return buffer; |
} |
|
static inline char * task_mem(struct task_struct *p, char *buffer) |
{ |
#ifndef NO_MM |
struct mm_struct * mm = p->mm; |
|
if (mm && mm != &init_mm) { |
struct vm_area_struct * vma = mm->mmap; |
unsigned long data = 0, stack = 0; |
unsigned long exec = 0, lib = 0; |
|
for (vma = mm->mmap; vma; vma = vma->vm_next) { |
unsigned long len = (vma->vm_end - vma->vm_start) >> 10; |
if (!vma->vm_inode) { |
data += len; |
if (vma->vm_flags & VM_GROWSDOWN) |
stack += len; |
continue; |
} |
if (vma->vm_flags & VM_WRITE) |
continue; |
if (vma->vm_flags & VM_EXEC) { |
exec += len; |
if (vma->vm_flags & VM_EXECUTABLE) |
continue; |
lib += len; |
} |
} |
buffer += sprintf(buffer, |
"VmSize:\t%8lu kB\n" |
"VmLck:\t%8lu kB\n" |
"VmRSS:\t%8lu kB\n" |
"VmData:\t%8lu kB\n" |
"VmStk:\t%8lu kB\n" |
"VmExe:\t%8lu kB\n" |
"VmLib:\t%8lu kB\n", |
mm->total_vm << (PAGE_SHIFT-10), |
mm->locked_vm << (PAGE_SHIFT-10), |
mm->rss << (PAGE_SHIFT-10), |
data - stack, stack, |
exec - lib, lib); |
} |
#else /* NO_MM */ |
unsigned long bytes = 0, sbytes = 0; |
struct mm_tblock_struct * tblock; |
|
/* Logic: we've got two memory sums for each process, "shared", and |
* "non-shared". Shared memory may get counted more then once, for |
* each process that owns it. Non-shared memory is counted |
* accurately. |
* |
* -- Kenneth Albanowski |
*/ |
|
for(tblock = &p->mm->tblock;tblock;tblock=tblock->next) { |
|
if (tblock->rblock) { |
|
bytes += ksize(tblock); |
|
if ((p->mm->count > 1) || (tblock->rblock->refcount > 1)) { |
sbytes += ksize(tblock->rblock->kblock); |
sbytes += ksize(tblock->rblock) ; |
} else { |
bytes += ksize(tblock->rblock->kblock); |
bytes += ksize(tblock->rblock) ; |
} |
|
} |
|
|
} |
|
((p->mm->count > 1) ? sbytes : bytes) += ksize(p->mm); |
((p->fs->count > 1) ? sbytes : bytes) += ksize(p->fs); |
((p->files->count > 1) ? sbytes : bytes) += ksize(p->files); |
((p->sig->count > 1) ? sbytes : bytes) += ksize(p->sig); |
bytes += ksize(p); |
|
bytes += PAGE_SIZE; /* Kernel stack */ |
|
buffer += sprintf(buffer, |
"Mem:\t%8lu bytes\n" |
"Shared:\t%8lu bytes\n", |
bytes, |
sbytes); |
#endif /* NO_MM */ |
return buffer; |
} |
|
static inline char * task_sig(struct task_struct *p, char *buffer) |
{ |
buffer += sprintf(buffer, |
"SigPnd:\t%08lx\n" |
"SigBlk:\t%08lx\n", |
p->signal, p->blocked); |
if (p->sig) { |
struct sigaction * action = p->sig->action; |
unsigned long sig_ign = 0, sig_caught = 0; |
unsigned long bit = 1; |
int i; |
|
for (i = 0; i < 32; i++) { |
switch((unsigned long) action->sa_handler) { |
case 0: |
break; |
case 1: |
sig_ign |= bit; |
break; |
default: |
sig_caught |= bit; |
} |
bit <<= 1; |
action++; |
} |
|
buffer += sprintf(buffer, |
"SigIgn:\t%08lx\n" |
"SigCgt:\t%08lx\n", |
sig_ign, sig_caught); |
} |
return buffer; |
} |
|
static int get_status(int pid, char * buffer) |
{ |
char * orig = buffer; |
struct task_struct ** p = get_task(pid), *tsk; |
|
if (!p || (tsk = *p) == NULL) |
return 0; |
buffer = task_name(tsk, buffer); |
buffer = task_state(tsk, buffer); |
buffer = task_mem(tsk, buffer); |
buffer = task_sig(tsk, buffer); |
return buffer - orig; |
} |
|
static int get_stat(int pid, char * buffer) |
{ |
struct task_struct ** p = get_task(pid), *tsk; |
unsigned long sigignore=0, sigcatch=0, wchan; |
unsigned long vsize, eip, esp; |
long priority, nice; |
int i,tty_pgrp; |
char state; |
|
if (!p || (tsk = *p) == NULL) |
return 0; |
if (tsk->state < 0 || tsk->state > 5) |
state = '.'; |
else |
state = "RSDZTW"[tsk->state]; |
vsize = eip = esp = 0; |
if (tsk->mm && tsk->mm != &init_mm) { |
#ifndef NO_MM |
struct vm_area_struct *vma = tsk->mm->mmap; |
while (vma) { |
vsize += vma->vm_end - vma->vm_start; |
vma = vma->vm_next; |
} |
if (tsk->kernel_stack_page) { |
eip = KSTK_EIP(tsk); |
esp = KSTK_ESP(tsk); |
} |
#endif /* !NO_MM */ |
} |
wchan = get_wchan(tsk); |
if (tsk->sig) { |
unsigned long bit = 1; |
for(i=0; i<32; ++i) { |
switch((unsigned long) tsk->sig->action[i].sa_handler) { |
case 0: |
break; |
case 1: |
sigignore |= bit; |
break; |
default: |
sigcatch |= bit; |
} |
bit <<= 1; |
} |
} |
if (tsk->tty) |
tty_pgrp = tsk->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 = 20 - (priority * 10 + DEF_PRIORITY / 2) / DEF_PRIORITY; |
nice = tsk->priority; |
nice = 20 - (nice * 20 + DEF_PRIORITY / 2) / DEF_PRIORITY; |
|
return 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\n", |
pid, |
tsk->comm, |
state, |
tsk->p_pptr->pid, |
tsk->pgrp, |
tsk->session, |
tsk->tty ? kdev_t_to_nr(tsk->tty->device) : 0, |
tty_pgrp, |
tsk->flags, |
tsk->min_flt, |
tsk->cmin_flt, |
tsk->maj_flt, |
tsk->cmaj_flt, |
tsk->utime, |
tsk->stime, |
tsk->cutime, |
tsk->cstime, |
priority, |
nice, |
tsk->timeout, |
tsk->it_real_value, |
tsk->start_time, |
vsize, |
tsk->mm ? tsk->mm->rss : 0, /* you might want to shift this left 3 */ |
tsk->rlim ? tsk->rlim[RLIMIT_RSS].rlim_cur : 0, |
tsk->mm ? tsk->mm->start_code : 0, |
tsk->mm ? tsk->mm->end_code : 0, |
tsk->mm ? tsk->mm->start_stack : 0, |
esp, |
eip, |
tsk->signal, |
tsk->blocked, |
sigignore, |
sigcatch, |
wchan, |
tsk->nswap, |
tsk->cnswap); |
} |
|
#ifndef NO_MM |
static inline void statm_pte_range(pmd_t * pmd, unsigned long address, unsigned long size, |
int * pages, int * shared, int * dirty, int * total) |
{ |
pte_t * pte; |
unsigned long end; |
|
if (pmd_none(*pmd)) |
return; |
if (pmd_bad(*pmd)) { |
printk("statm_pte_range: bad pmd (%08lx)\n", pmd_val(*pmd)); |
pmd_clear(pmd); |
return; |
} |
pte = pte_offset(pmd, address); |
address &= ~PMD_MASK; |
end = address + size; |
if (end > PMD_SIZE) |
end = PMD_SIZE; |
do { |
pte_t page = *pte; |
|
address += PAGE_SIZE; |
pte++; |
if (pte_none(page)) |
continue; |
++*total; |
if (!pte_present(page)) |
continue; |
++*pages; |
if (pte_dirty(page)) |
++*dirty; |
if (pte_page(page) >= high_memory) |
continue; |
if (mem_map[MAP_NR(pte_page(page))].count > 1) |
++*shared; |
} while (address < end); |
} |
|
static inline void statm_pmd_range(pgd_t * pgd, unsigned long address, unsigned long size, |
int * pages, int * shared, int * dirty, int * total) |
{ |
pmd_t * pmd; |
unsigned long end; |
|
if (pgd_none(*pgd)) |
return; |
if (pgd_bad(*pgd)) { |
printk("statm_pmd_range: bad pgd (%08lx)\n", pgd_val(*pgd)); |
pgd_clear(pgd); |
return; |
} |
pmd = pmd_offset(pgd, address); |
address &= ~PGDIR_MASK; |
end = address + size; |
if (end > PGDIR_SIZE) |
end = PGDIR_SIZE; |
do { |
statm_pte_range(pmd, address, end - address, pages, shared, dirty, total); |
address = (address + PMD_SIZE) & PMD_MASK; |
pmd++; |
} while (address < end); |
} |
|
static void statm_pgd_range(pgd_t * pgd, unsigned long address, unsigned long end, |
int * pages, int * shared, int * dirty, int * total) |
{ |
while (address < end) { |
statm_pmd_range(pgd, address, end - address, pages, shared, dirty, total); |
address = (address + PGDIR_SIZE) & PGDIR_MASK; |
pgd++; |
} |
} |
|
static int get_statm(int pid, char * buffer) |
{ |
struct task_struct ** p = get_task(pid), *tsk; |
int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0; |
|
if (!p || (tsk = *p) == NULL) |
return 0; |
if (tsk->mm && tsk->mm != &init_mm) { |
struct vm_area_struct * vma = tsk->mm->mmap; |
|
while (vma) { |
pgd_t *pgd = pgd_offset(tsk->mm, vma->vm_start); |
int pages = 0, shared = 0, dirty = 0, total = 0; |
|
statm_pgd_range(pgd, vma->vm_start, vma->vm_end, &pages, &shared, &dirty, &total); |
resident += pages; |
share += shared; |
dt += dirty; |
size += total; |
if (vma->vm_flags & VM_EXECUTABLE) |
trs += pages; /* text */ |
else if (vma->vm_flags & VM_GROWSDOWN) |
drs += pages; /* stack */ |
else if (vma->vm_end > 0x60000000) |
lrs += pages; /* library */ |
else |
drs += pages; |
vma = vma->vm_next; |
} |
} |
return sprintf(buffer,"%d %d %d %d %d %d %d\n", |
size, resident, share, trs, lrs, drs, dt); |
} |
|
/* |
* The way we support synthetic files > 4K |
* - without storing their contents in some buffer and |
* - without walking through the entire synthetic file until we reach the |
* position of the requested data |
* is to cleverly encode the current position in the file's f_pos field. |
* There is no requirement that a read() call which returns `count' bytes |
* of data increases f_pos by exactly `count'. |
* |
* This idea is Linus' one. Bruno implemented it. |
*/ |
|
/* |
* For the /proc/<pid>/maps file, we use fixed length records, each containing |
* a single line. |
*/ |
#define MAPS_LINE_LENGTH 1024 |
#define MAPS_LINE_SHIFT 10 |
/* |
* f_pos = (number of the vma in the task->mm->mmap list) * MAPS_LINE_LENGTH |
* + (index into the line) |
*/ |
/* for systems with sizeof(void*) == 4: */ |
#define MAPS_LINE_FORMAT4 "%08lx-%08lx %s %08lx %s %lu\n" |
#define MAPS_LINE_MAX4 49 /* sum of 8 1 8 1 4 1 8 1 5 1 10 1 */ |
|
/* for systems with sizeof(void*) == 8: */ |
#define MAPS_LINE_FORMAT8 "%016lx-%016lx %s %016lx %s %lu\n" |
#define MAPS_LINE_MAX8 73 /* sum of 16 1 16 1 4 1 16 1 5 1 10 1 */ |
|
#define MAPS_LINE_MAX MAPS_LINE_MAX8 |
|
|
static int read_maps (int pid, struct file * file, char * buf, int count) |
{ |
struct task_struct ** p = get_task(pid); |
char * destptr; |
loff_t lineno; |
int column; |
struct vm_area_struct * map; |
int i; |
|
if (!p || !*p) |
return -EINVAL; |
|
if (!(*p)->mm || (*p)->mm == &init_mm || count == 0) |
return 0; |
|
/* decode f_pos */ |
lineno = file->f_pos >> MAPS_LINE_SHIFT; |
column = file->f_pos & (MAPS_LINE_LENGTH-1); |
|
/* quickly go to line lineno */ |
for (map = (*p)->mm->mmap, i = 0; map && (i < lineno); map = map->vm_next, i++) |
continue; |
|
destptr = buf; |
|
for ( ; map ; ) { |
/* produce the next line */ |
char line[MAPS_LINE_MAX+1]; |
char str[5], *cp = str; |
int flags; |
kdev_t dev; |
unsigned long ino; |
int len; |
|
flags = map->vm_flags; |
|
*cp++ = flags & VM_READ ? 'r' : '-'; |
*cp++ = flags & VM_WRITE ? 'w' : '-'; |
*cp++ = flags & VM_EXEC ? 'x' : '-'; |
*cp++ = flags & VM_MAYSHARE ? 's' : 'p'; |
*cp++ = 0; |
|
if (map->vm_inode != NULL) { |
dev = map->vm_inode->i_dev; |
ino = map->vm_inode->i_ino; |
} else { |
dev = 0; |
ino = 0; |
} |
|
len = sprintf(line, |
sizeof(void*) == 4 ? MAPS_LINE_FORMAT4 : MAPS_LINE_FORMAT8, |
map->vm_start, map->vm_end, str, map->vm_offset, |
kdevname(dev), ino); |
|
if (column >= len) { |
column = 0; /* continue with next line at column 0 */ |
lineno++; |
map = map->vm_next; |
continue; |
} |
|
i = len-column; |
if (i > count) |
i = count; |
memcpy_tofs(destptr, line+column, i); |
destptr += i; count -= i; |
column += i; |
if (column >= len) { |
column = 0; /* next time: next line at column 0 */ |
lineno++; |
map = map->vm_next; |
} |
|
/* done? */ |
if (count == 0) |
break; |
|
/* By writing to user space, we might have slept. |
* Stop the loop, to avoid a race condition. |
*/ |
if (*p != current) |
break; |
} |
|
/* encode f_pos */ |
file->f_pos = (lineno << MAPS_LINE_SHIFT) + column; |
|
return destptr-buf; |
} |
#endif /* !NO_MM */ |
|
#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_filesystem_list(char *); |
extern int get_filesystem_info( char * ); |
extern int get_irq_list(char *); |
extern int get_serialinfo(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); |
#ifdef __SMP_PROF__ |
extern int get_smp_prof_list(char *); |
#endif |
|
static int get_root_array(char * page, int type, char **start, off_t offset, int 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 |
|
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_DEVICES: |
return get_device_list(page); |
|
case PROC_INTERRUPTS: |
return get_irq_list(page); |
|
case PROC_SERIAL: |
return get_serialinfo(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); |
#ifdef CONFIG_BLK_DEV_MD |
case PROC_MD: |
return get_md_status(page); |
#endif |
#ifdef __SMP_PROF__ |
case PROC_SMP_PROF: |
return get_smp_prof_list(page); |
#endif |
case PROC_CMDLINE: |
return get_cmdline(page); |
|
case PROC_MTAB: |
return get_filesystem_info( page ); |
#ifdef CONFIG_RTC |
case PROC_RTC: |
return get_rtc_status(page); |
#endif |
case PROC_LOCKS: |
return get_locks_status(page, start, offset, length); |
} |
return -EBADF; |
} |
|
static int process_unauthorized(int type, int pid) |
{ |
struct task_struct ** p = get_task(pid); |
|
if (!p || !*p || !(*p)->mm) |
return 1; |
|
switch(type) |
{ |
case PROC_PID_STATUS: |
case PROC_PID_STAT: |
case PROC_PID_CMDLINE: |
#ifndef NO_MM |
case PROC_PID_STATM: |
case PROC_PID_MAPS: |
#endif |
return 0; |
} |
if(suser() || current->fsuid == (*p)->euid) |
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); |
#ifndef NO_MM |
case PROC_PID_STATM: |
return get_statm(pid, page); |
#endif /* !NO_MM */ |
} |
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 int array_read(struct inode * inode, struct file * file,char * buf, int count) |
{ |
unsigned long page; |
char *start; |
int length; |
int end; |
unsigned int type, pid; |
struct proc_dir_entry *dp; |
|
if (count < 0) |
return -EINVAL; |
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, file->f_pos, |
count, 0); |
else |
length = fill_array((char *) page, pid, type, |
&start, file->f_pos, count); |
if (length < 0) { |
free_page(page); |
return length; |
} |
if (start != NULL) { |
/* We have had block-adjusting processing! */ |
memcpy_tofs(buf, start, length); |
file->f_pos += length; |
count = length; |
} else { |
/* Static 4kB (or whatever) block capacity */ |
if (file->f_pos >= length) { |
free_page(page); |
return 0; |
} |
if (count + file->f_pos > length) |
count = length - file->f_pos; |
end = count + file->f_pos; |
memcpy_tofs(buf, (char *) page + file->f_pos, count); |
file->f_pos = 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_select */ |
NULL, /* array_ioctl */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static int arraylong_read (struct inode * inode, struct file * file, char * buf, int count) |
{ |
#ifndef NO_MM |
unsigned int pid = inode->i_ino >> 16; |
unsigned int type = inode->i_ino & 0x0000ffff; |
|
if (count < 0) |
return -EINVAL; |
|
switch (type) { |
case PROC_PID_MAPS: |
return read_maps(pid, file, buf, count); |
} |
#endif /* !NO_MM */ |
return -EINVAL; |
} |
|
static struct file_operations proc_arraylong_operations = { |
NULL, /* array_lseek */ |
arraylong_read, |
NULL, /* array_write */ |
NULL, /* array_readdir */ |
NULL, /* array_select */ |
NULL, /* array_ioctl */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
/inode.c
0,0 → 1,266
/* |
* linux/fs/proc/inode.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
/* |
* uClinux revisions for NO_MM |
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, |
* The Silver Hammer Group, Ltd. |
*/ |
|
#include <linux/sched.h> |
#include <linux/proc_fs.h> |
#include <linux/kernel.h> |
#include <linux/mm.h> |
#include <linux/string.h> |
#include <linux/stat.h> |
#include <linux/locks.h> |
#include <linux/limits.h> |
|
#include <asm/system.h> |
#include <asm/segment.h> |
|
static void proc_put_inode(struct inode *inode) |
{ |
if (inode->i_nlink) |
return; |
inode->i_size = 0; |
} |
|
static void proc_put_super(struct super_block *sb) |
{ |
lock_super(sb); |
sb->s_dev = 0; |
unlock_super(sb); |
} |
|
static struct super_operations proc_sops = { |
proc_read_inode, |
NULL, |
proc_write_inode, |
proc_put_inode, |
proc_put_super, |
NULL, |
proc_statfs, |
NULL |
}; |
|
|
static int parse_options(char *options,uid_t *uid,gid_t *gid) |
{ |
char *this_char,*value; |
|
*uid = current->uid; |
*gid = current->gid; |
if (!options) return 1; |
for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) { |
if ((value = strchr(this_char,'=')) != NULL) |
*value++ = 0; |
if (!strcmp(this_char,"uid")) { |
if (!value || !*value) |
return 0; |
*uid = simple_strtoul(value,&value,0); |
if (*value) |
return 0; |
} |
else if (!strcmp(this_char,"gid")) { |
if (!value || !*value) |
return 0; |
*gid = simple_strtoul(value,&value,0); |
if (*value) |
return 0; |
} |
else return 1; |
} |
return 1; |
} |
|
struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de) |
{ |
struct inode * inode = iget(s, ino); |
if (inode && inode->i_sb == s) { |
inode->u.generic_ip = (void *) de; |
if (de) { |
if (de->mode) { |
inode->i_mode = de->mode; |
inode->i_uid = de->uid; |
inode->i_gid = de->gid; |
} |
if (de->size) |
inode->i_size = de->size; |
if (de->ops) |
inode->i_op = de->ops; |
if (de->nlink) |
inode->i_nlink = de->nlink; |
if (de->fill_inode) |
de->fill_inode(inode); |
} |
} |
return inode; |
} |
|
struct super_block *proc_read_super(struct super_block *s,void *data, |
int silent) |
{ |
proc_root_init(); |
lock_super(s); |
s->s_blocksize = 1024; |
s->s_blocksize_bits = 10; |
s->s_magic = PROC_SUPER_MAGIC; |
s->s_op = &proc_sops; |
unlock_super(s); |
if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) { |
s->s_dev = 0; |
printk("get root inode failed\n"); |
return NULL; |
} |
parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid); |
return s; |
} |
|
void proc_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) |
{ |
struct statfs tmp; |
|
tmp.f_type = PROC_SUPER_MAGIC; |
tmp.f_bsize = PAGE_SIZE/sizeof(long); |
tmp.f_blocks = 0; |
tmp.f_bfree = 0; |
tmp.f_bavail = 0; |
tmp.f_files = 0; |
tmp.f_ffree = 0; |
tmp.f_namelen = NAME_MAX; |
memcpy_tofs(buf, &tmp, bufsiz); |
} |
|
void proc_read_inode(struct inode * inode) |
{ |
unsigned long ino, pid; |
struct task_struct * p; |
int i; |
|
inode->i_op = NULL; |
inode->i_mode = 0; |
inode->i_uid = 0; |
inode->i_gid = 0; |
inode->i_nlink = 1; |
inode->i_size = 0; |
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; |
inode->i_blocks = 0; |
inode->i_blksize = 1024; |
ino = inode->i_ino; |
pid = ino >> 16; |
p = task[0]; |
for (i = 0; i < NR_TASKS ; i++) |
if ((p = task[i]) && (p->pid == pid)) |
break; |
if (!p || i >= NR_TASKS) |
return; |
if (ino == PROC_ROOT_INO) { |
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; |
inode->i_nlink = 2; |
for (i = 1 ; i < NR_TASKS ; i++) |
if (task[i]) |
inode->i_nlink++; |
return; |
} |
|
if (!pid) { |
switch (ino) { |
case PROC_KMSG: |
inode->i_mode = S_IFREG | S_IRUSR; |
inode->i_op = &proc_kmsg_inode_operations; |
break; |
case PROC_NET: |
inode->i_nlink = 2; |
break; |
case PROC_SCSI: |
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; |
inode->i_nlink = 2; |
inode->i_op = &proc_scsi_inode_operations; |
break; |
case PROC_KCORE: |
inode->i_mode = S_IFREG | S_IRUSR; |
inode->i_op = &proc_kcore_inode_operations; |
inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE; |
break; |
case PROC_PROFILE: |
inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR; |
inode->i_op = &proc_profile_inode_operations; |
inode->i_size = (1+prof_len) * sizeof(unsigned long); |
break; |
default: |
inode->i_mode = S_IFREG | S_IRUGO; |
inode->i_op = &proc_array_inode_operations; |
break; |
} |
return; |
} |
ino &= 0x0000ffff; |
if (ino == PROC_PID_INO || p->dumpable) { |
inode->i_uid = p->euid; |
inode->i_gid = p->egid; |
} |
switch (ino) { |
case PROC_PID_INO: |
inode->i_nlink = 4; |
return; |
case PROC_PID_MEM: |
inode->i_op = &proc_mem_inode_operations; |
inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR; |
return; |
case PROC_PID_CWD: |
case PROC_PID_ROOT: |
case PROC_PID_EXE: |
inode->i_op = &proc_link_inode_operations; |
inode->i_size = 64; |
inode->i_mode = S_IFLNK | S_IRWXU; |
return; |
case PROC_PID_FD: |
inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR; |
inode->i_op = &proc_fd_inode_operations; |
inode->i_nlink = 2; |
return; |
case PROC_PID_ENVIRON: |
inode->i_mode = S_IFREG | S_IRUSR; |
inode->i_op = &proc_array_inode_operations; |
return; |
case PROC_PID_CMDLINE: |
case PROC_PID_STATUS: |
case PROC_PID_STAT: |
#ifndef NO_MM |
case PROC_PID_STATM: |
#endif /* !NO_MM */ |
inode->i_mode = S_IFREG | S_IRUGO; |
inode->i_op = &proc_array_inode_operations; |
return; |
#ifndef NO_MM |
case PROC_PID_MAPS: |
inode->i_mode = S_IFIFO | S_IRUGO; |
inode->i_op = &proc_arraylong_inode_operations; |
return; |
#endif /* !NO_MM */ |
} |
switch (ino >> 8) { |
case PROC_PID_FD_DIR: |
ino &= 0xff; |
if (ino >= NR_OPEN || !p->files || !p->files->fd[ino]) |
return; |
inode->i_op = &proc_link_inode_operations; |
inode->i_size = 64; |
inode->i_mode = S_IFLNK; |
if (p->files->fd[ino]->f_mode & 1) |
inode->i_mode |= S_IRUSR | S_IXUSR; |
if (p->files->fd[ino]->f_mode & 2) |
inode->i_mode |= S_IWUSR | S_IXUSR; |
return; |
} |
return; |
} |
|
void proc_write_inode(struct inode * inode) |
{ |
inode->i_dirt=0; |
} |
/mem.c
0,0 → 1,508
/* |
* linux/fs/proc/mem.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
/* |
* uClinux revisions for NO_MM |
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, |
* The Silver Hammer Group, Ltd. |
*/ |
|
#include <linux/types.h> |
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/kernel.h> |
#include <linux/mm.h> |
|
#include <asm/page.h> |
#include <asm/segment.h> |
#include <asm/io.h> |
#include <asm/pgtable.h> |
|
#ifndef NO_MM |
|
/* |
* 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) { |
int i; |
tsk = NULL; |
for (i = 1 ; i < NR_TASKS ; i++) |
if (task[i] && task[i]->pid == pid) { |
tsk = task[i]; |
break; |
} |
/* |
* 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 int mem_read(struct inode * inode, struct file * file,char * buf, int count) |
{ |
pgd_t *page_dir; |
pmd_t *page_middle; |
pte_t pte; |
char * page; |
struct task_struct * tsk; |
unsigned long addr; |
char *tmp; |
int i; |
|
if (count < 0) |
return -EINVAL; |
tsk = get_task(inode->i_ino >> 16); |
if (!tsk) |
return -ESRCH; |
addr = file->f_pos; |
count = check_range(tsk->mm, addr, count); |
if (count < 0) |
return count; |
tmp = buf; |
while (count > 0) { |
if (current->signal & ~current->blocked) |
break; |
page_dir = pgd_offset(tsk->mm,addr); |
if (pgd_none(*page_dir)) |
break; |
if (pgd_bad(*page_dir)) { |
printk("Bad page dir entry %08lx\n", pgd_val(*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)) { |
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); |
pmd_clear(page_middle); |
break; |
} |
pte = *pte_offset(page_middle,addr); |
if (!pte_present(pte)) |
break; |
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); |
i = PAGE_SIZE-(addr & ~PAGE_MASK); |
if (i > count) |
i = count; |
memcpy_tofs(tmp, page, i); |
addr += i; |
tmp += i; |
count -= i; |
} |
file->f_pos = addr; |
return tmp-buf; |
} |
|
#ifndef mem_write |
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count) |
{ |
pgd_t *page_dir; |
pmd_t *page_middle; |
pte_t pte; |
char * page; |
struct task_struct * tsk; |
unsigned long addr; |
char *tmp; |
int i; |
|
if (count < 0) |
return -EINVAL; |
addr = file->f_pos; |
tsk = get_task(inode->i_ino >> 16); |
if (!tsk) |
return -ESRCH; |
tmp = buf; |
while (count > 0) { |
if (current->signal & ~current->blocked) |
break; |
page_dir = pgd_offset(tsk,addr); |
if (pgd_none(*page_dir)) |
break; |
if (pgd_bad(*page_dir)) { |
printk("Bad page dir entry %08lx\n", pgd_val(*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)) { |
printk("Bad page middle entry %08lx\n", pmd_val(*page_middle)); |
pmd_clear(page_middle); |
break; |
} |
pte = *pte_offset(page_middle,addr); |
if (!pte_present(pte)) |
break; |
if (!pte_write(pte)) |
break; |
page = (char *) pte_page(pte) + (addr & ~PAGE_MASK); |
i = PAGE_SIZE-(addr & ~PAGE_MASK); |
if (i > count) |
i = count; |
memcpy_fromfs(page, tmp, i); |
addr += i; |
tmp += i; |
count -= i; |
} |
file->f_pos = addr; |
if (tmp != buf) |
return tmp-buf; |
if (current->signal & ~current->blocked) |
return -ERESTARTSYS; |
return 0; |
} |
|
#endif |
|
static int mem_lseek(struct inode * inode, struct file * file, off_t 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 inode * inode, 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; |
|
/* 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)) { |
printk("Bad source page dir entry %08lx\n", pgd_val(*src_dir)); |
return -EINVAL; |
} |
src_middle = pmd_offset(src_dir, stmp); |
if (pmd_none(*src_middle)) |
return -EINVAL; |
if (pmd_bad(*src_middle)) { |
printk("Bad source page middle entry %08lx\n", pmd_val(*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)) |
do_no_page(tsk, src_vma, stmp, 1); |
|
if ((vma->vm_flags & VM_WRITE) && !pte_write(*src_table)) |
do_wp_page(tsk, src_vma, stmp, 1); |
|
set_pte(src_table, pte_mkdirty(*src_table)); |
set_pte(dest_table, *src_table); |
mapnr = MAP_NR(pte_page(*src_table)); |
if (mapnr < MAP_NR(high_memory)) |
mem_map[mapnr].count++; |
|
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_select */ |
NULL, /* mem_ioctl */ |
mem_mmap, /* mmap */ |
NULL, /* no special open code */ |
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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
#else /* NO_MM */ |
|
/* |
* 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 struct task_struct * get_task(int pid) |
{ |
struct task_struct * tsk = current; |
|
if (pid != tsk->pid) { |
int i; |
tsk = NULL; |
for (i = 1 ; i < NR_TASKS ; i++) |
if (task[i] && task[i]->pid == pid) { |
tsk = task[i]; |
break; |
} |
/* |
* 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 int mem_read(struct inode * inode, struct file * file,char * buf, int count) |
{ |
struct task_struct * tsk; |
unsigned long addr; |
char *tmp; |
int i; |
|
if (count < 0) |
return -EINVAL; |
tsk = get_task(inode->i_ino >> 16); |
if (!tsk) |
return -ESRCH; |
addr = file->f_pos; |
tmp = buf; |
while (count > 0) { |
if (current->signal & ~current->blocked) |
break; |
i = count; |
memcpy_tofs(tmp, (void*)addr, i); |
addr += i; |
tmp += i; |
count -= i; |
} |
file->f_pos = addr; |
return tmp-buf; |
} |
|
#ifndef mem_write |
|
static int mem_write(struct inode * inode, struct file * file,char * buf, int count) |
{ |
struct task_struct * tsk; |
unsigned long addr; |
char *tmp; |
int i; |
|
if (count < 0) |
return -EINVAL; |
addr = file->f_pos; |
tsk = get_task(inode->i_ino >> 16); |
if (!tsk) |
return -ESRCH; |
tmp = buf; |
while (count > 0) { |
if (current->signal & ~current->blocked) |
break; |
i = count; |
memcpy_fromfs((void*)addr, tmp, i); |
addr += i; |
tmp += i; |
count -= i; |
} |
file->f_pos = addr; |
if (tmp != buf) |
return tmp-buf; |
if (current->signal & ~current->blocked) |
return -ERESTARTSYS; |
return 0; |
} |
|
#endif |
|
int mem_mmap(struct inode * inode, struct file * file, |
struct vm_area_struct * vma) |
{ |
vma->vm_start = vma->vm_offset; |
return 0; |
} |
|
static int mem_lseek(struct inode * inode, struct file * file, off_t 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; |
} |
} |
|
static struct file_operations proc_mem_operations = { |
mem_lseek, |
mem_read, |
mem_write, |
NULL, /* mem_readdir */ |
NULL, /* mem_select */ |
NULL, /* mem_ioctl */ |
mem_mmap, /* mmap */ |
NULL, /* no special open code */ |
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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
#endif /* NO_MM */ |
/serial.c
0,0 → 1,45
/* |
* linux/fs/proc/serial.c |
* |
* Copyright (C) 1999, Greg Ungerer (gerg@moreton.com.au) |
* |
* Copied and hacked from array.c, which was: |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
*/ |
|
#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/user.h> |
#include <linux/a.out.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 <asm/segment.h> |
#include <asm/pgtable.h> |
#include <asm/io.h> |
|
int get_serialinfo(char * buffer) |
{ |
int len = 0; |
|
#ifdef CONFIG_COLDFIRE_SERIAL |
extern int mcfrs_readproc(char *buffer); |
len = mcfrs_readproc(buffer); |
#else |
len += sprintf(buffer, "No Serial Info\n"); |
#endif |
|
return(len); |
} |
|
/kmsg.c
0,0 → 1,78
/* |
* linux/fs/proc/kmsg.c |
* |
* Copyright (C) 1992 by Linus Torvalds |
* |
*/ |
|
#include <linux/types.h> |
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/kernel.h> |
|
#include <asm/segment.h> |
#include <asm/io.h> |
|
extern unsigned long log_size; |
extern struct wait_queue * log_wait; |
|
asmlinkage int sys_syslog(int type, char * bug, int count); |
|
static int kmsg_open(struct inode * inode, struct file * file) |
{ |
return sys_syslog(1,NULL,0); |
} |
|
static void kmsg_release(struct inode * inode, struct file * file) |
{ |
(void) sys_syslog(0,NULL,0); |
} |
|
static int kmsg_read(struct inode * inode, struct file * file,char * buf, int count) |
{ |
return sys_syslog(2,buf,count); |
} |
|
static int kmsg_select(struct inode *inode, struct file *file, int sel_type, select_table * wait) |
{ |
if (sel_type != SEL_IN) |
return 0; |
if (log_size) |
return 1; |
select_wait(&log_wait, wait); |
return 0; |
} |
|
|
static struct file_operations proc_kmsg_operations = { |
NULL, /* kmsg_lseek */ |
kmsg_read, |
NULL, /* kmsg_write */ |
NULL, /* kmsg_readdir */ |
kmsg_select, /* kmsg_select */ |
NULL, /* kmsg_ioctl */ |
NULL, /* mmap */ |
kmsg_open, |
kmsg_release, |
NULL /* can't fsync */ |
}; |
|
struct inode_operations proc_kmsg_inode_operations = { |
&proc_kmsg_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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
/scsi.c
0,0 → 1,223
/* |
* 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/segment.h> |
|
/* forward references */ |
static int proc_readscsi(struct inode * inode, struct file * file, |
char * buf, int count); |
static int proc_writescsi(struct inode * inode, struct file * file, |
const char * buf, int count); |
static int proc_scsilseek(struct inode *, struct file *, off_t, 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, /* select */ |
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_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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
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 int proc_readscsi(struct inode * inode, struct file * file, |
char * buf, int count) |
{ |
int length; |
int bytes = count; |
int copied = 0; |
int thistime; |
char * page; |
char * start; |
|
if (count < -1) /* Normally I wouldn't do this, */ |
return(-EINVAL); /* but it saves some redundant code. |
* Now it is possible to seek to the |
* end of the file */ |
if (!(page = (char *) __get_free_page(GFP_KERNEL))) |
return(-ENOMEM); |
|
while(bytes > 0 || count == -1) { |
thistime = bytes; |
if(bytes > PROC_BLOCK_SIZE || count == -1) |
thistime = PROC_BLOCK_SIZE; |
|
if(dispatch_scsi_info_ptr) |
length = dispatch_scsi_info_ptr(inode->i_ino, page, &start, |
file->f_pos, thistime, 0); |
else |
length = get_not_present_info(page, &start, file->f_pos, 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, if we're not doing a seek to |
* the end of the file |
*/ |
if (count != -1) |
memcpy_tofs(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((ulong) page); |
return(copied); |
} |
|
|
static int proc_writescsi(struct inode * inode, struct file * file, |
const char * buf, int count) |
{ |
int 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); |
memcpy_fromfs(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 int proc_scsilseek(struct inode * inode, struct file * file, |
off_t 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: /* This ugly hack allows us to */ |
if (offset) /* to determine the length of the */ |
return(-EINVAL); /* file and then later safely to */ |
proc_readscsi(inode, file, 0, -1); /* seek in it */ |
return(file->f_pos); |
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: |
*/ |
/root.c
0,0 → 1,629
/* |
* linux/fs/proc/root.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* proc root directory handling functions |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/proc_fs.h> |
#include <linux/stat.h> |
#include <linux/config.h> |
#include <asm/bitops.h> |
|
/* |
* Offset of the first process in the /proc root directory.. |
*/ |
#define FIRST_PROCESS_ENTRY 256 |
|
static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t); |
static int proc_root_lookup(struct inode *,const char *,int,struct inode **); |
|
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0}; |
|
/* |
* These are the generic /proc directory operations. They |
* use the in-memory "struct proc_dir_entry" tree to parse |
* the /proc directory. |
* |
* NOTE! The /proc/scsi directory currently does not correctly |
* build up the proc_dir_entry tree, and will show up empty. |
*/ |
static struct file_operations proc_dir_operations = { |
NULL, /* lseek - default */ |
NULL, /* read - bad */ |
NULL, /* write - bad */ |
proc_readdir, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* can't fsync */ |
}; |
|
/* |
* proc directories can do almost nothing.. |
*/ |
struct inode_operations proc_dir_inode_operations = { |
&proc_dir_operations, /* default net directory file-ops */ |
NULL, /* create */ |
proc_lookup, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
/* |
* The root /proc directory is special, as it has the |
* <pid> directories. Thus we don't use the generic |
* directory handling functions for that.. |
*/ |
static struct file_operations proc_root_operations = { |
NULL, /* lseek - default */ |
NULL, /* read - bad */ |
NULL, /* write - bad */ |
proc_root_readdir, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* no fsync */ |
}; |
|
/* |
* proc root can do almost nothing.. |
*/ |
static struct inode_operations proc_root_inode_operations = { |
&proc_root_operations, /* default base directory file-ops */ |
NULL, /* create */ |
proc_root_lookup, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
/* |
* This is the root "inode" in the /proc tree.. |
*/ |
struct proc_dir_entry proc_root = { |
PROC_ROOT_INO, 5, "/proc", |
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, |
0, &proc_root_inode_operations, |
NULL, NULL, |
NULL, |
&proc_root, NULL |
}; |
|
struct proc_dir_entry proc_net = { |
PROC_NET, 3, "net", |
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, |
0, &proc_dir_inode_operations, |
NULL, NULL, |
NULL, |
NULL, NULL |
}; |
|
struct proc_dir_entry proc_scsi = { |
PROC_SCSI, 4, "scsi", |
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, |
0, &proc_dir_inode_operations, |
NULL, NULL, |
NULL, &proc_root, NULL |
}; |
|
struct proc_dir_entry proc_sys_root = { |
PROC_SYS, 3, "sys", /* inode, name */ |
S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0, /* mode, nlink, uid, gid */ |
0, &proc_dir_inode_operations, /* size, ops */ |
NULL, NULL, /* get_info, fill_inode */ |
NULL, /* next */ |
NULL, NULL /* parent, subdir */ |
}; |
|
int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp) |
{ |
dp->next = dir->subdir; |
dp->parent = dir; |
dir->subdir = dp; |
if (S_ISDIR(dp->mode)) |
dir->nlink++; |
return 0; |
} |
|
int proc_unregister(struct proc_dir_entry * dir, int ino) |
{ |
struct proc_dir_entry **p = &dir->subdir, *dp; |
|
while ((dp = *p) != NULL) { |
if (dp->low_ino == ino) { |
*p = dp->next; |
dp->next = NULL; |
if (S_ISDIR(dp->mode)) |
dir->nlink--; |
if (ino >= PROC_DYNAMIC_FIRST && |
ino < PROC_DYNAMIC_FIRST+PROC_NDYNAMIC) |
clear_bit(ino-PROC_DYNAMIC_FIRST, |
(void *) proc_alloc_map); |
return 0; |
} |
p = &dp->next; |
} |
return -EINVAL; |
} |
|
static int make_inode_number(void) |
{ |
int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC); |
if (i<0 || i>=PROC_NDYNAMIC) |
return -1; |
set_bit(i, (void *) proc_alloc_map); |
return PROC_DYNAMIC_FIRST + i; |
} |
|
int proc_register_dynamic(struct proc_dir_entry * dir, |
struct proc_dir_entry * dp) |
{ |
int i = make_inode_number(); |
if (i < 0) |
return -EAGAIN; |
dp->low_ino = i; |
dp->next = dir->subdir; |
dp->parent = dir; |
dir->subdir = dp; |
if (S_ISDIR(dp->mode)) |
dir->nlink++; |
return 0; |
} |
|
/* |
* /proc/self: |
*/ |
static int proc_self_followlink(struct inode * dir, struct inode * inode, |
int flag, int mode, struct inode ** res_inode) |
{ |
iput(dir); |
*res_inode = proc_get_inode(inode->i_sb, (current->pid << 16) + PROC_PID_INO, &proc_pid); |
iput(inode); |
if (!*res_inode) |
return -ENOENT; |
return 0; |
} |
|
static int proc_self_readlink(struct inode * inode, char * buffer, int buflen) |
{ |
int len; |
char tmp[30]; |
|
iput(inode); |
len = 1 + sprintf(tmp, "%d", current->pid); |
if (buflen < len) |
len = buflen; |
memcpy_tofs(buffer, tmp, len); |
return len; |
} |
|
static struct inode_operations proc_self_inode_operations = { |
NULL, /* no file-ops */ |
NULL, /* create */ |
NULL, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
proc_self_readlink, /* readlink */ |
proc_self_followlink, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
struct proc_dir_entry pde_loadavg = { |
PROC_LOADAVG, 7, "loadavg", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_uptime = { |
PROC_UPTIME, 6, "uptime", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_meminfo = { |
PROC_MEMINFO, 7, "meminfo", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_serial = { |
PROC_SERIAL, 6, "serial", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_kmsg = { |
PROC_KMSG, 4, "kmsg", |
S_IFREG | S_IRUSR, 1, 0, 0, |
}; |
struct proc_dir_entry pde_version = { |
PROC_VERSION, 7, "version", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#ifdef CONFIG_PCI |
struct proc_dir_entry pde_pci = { |
PROC_PCI, 3, "pci", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#endif |
struct proc_dir_entry pde_cpuinfo = { |
PROC_CPUINFO, 7, "cpuinfo", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_self = { |
PROC_SELF, 4, "self", |
S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, 1, 0, 0, |
64, &proc_self_inode_operations, |
}; |
|
#ifdef CONFIG_DEBUG_MALLOC |
struct proc_dir_entry pde_malloc = { |
PROC_MALLOC, 6, "malloc", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#endif |
struct proc_dir_entry pde_kcore = { |
PROC_KCORE, 5, "kcore", |
S_IFREG | S_IRUSR, 1, 0, 0, |
}; |
|
#ifdef CONFIG_MODULES |
struct proc_dir_entry pde_modules = { |
PROC_MODULES, 7, "modules", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_ksyms = { |
PROC_KSYMS, 5, "ksyms", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#endif |
struct proc_dir_entry pde_stat = { |
PROC_STAT, 4, "stat", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_devices = { |
PROC_DEVICES, 7, "devices", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_interrupts = { |
PROC_INTERRUPTS, 10,"interrupts", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#ifdef __SMP_PROF__ |
struct proc_dir_entry pde_smp = { |
PROC_SMP_PROF, 3,"smp", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#endif |
struct proc_dir_entry pde_filesystems = { |
PROC_FILESYSTEMS, 11,"filesystems", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_dma = { |
PROC_DMA, 3, "dma", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_ioports = { |
PROC_IOPORTS, 7, "ioports", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
struct proc_dir_entry pde_cmdline = { |
PROC_CMDLINE, 7, "cmdline", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#ifdef CONFIG_RTC |
struct proc_dir_entry pde_rtc = { |
PROC_RTC, 3, "rtc", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
#endif |
struct proc_dir_entry pde_locks = { |
PROC_LOCKS, 5, "locks", |
S_IFREG | S_IRUGO, 1, 0, 0, |
}; |
|
struct proc_dir_entry pde_mounts = |
{ PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, }; |
|
struct proc_dir_entry pde_profile = { |
PROC_PROFILE, 7, "profile", |
S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, |
}; |
|
void proc_root_init(void) |
{ |
static int done = 0; |
|
if (done) |
return; |
done = 1; |
proc_base_init(); |
|
proc_register(&proc_root, &pde_loadavg); |
proc_register(&proc_root, &pde_uptime); |
proc_register(&proc_root, &pde_meminfo); |
proc_register(&proc_root, &pde_kmsg); |
proc_register(&proc_root, &pde_version); |
#ifdef CONFIG_PCI |
proc_register(&proc_root, &pde_pci); |
#endif |
proc_register(&proc_root, &pde_cpuinfo); |
proc_register(&proc_root, &pde_self); |
proc_register(&proc_root, &proc_net); |
proc_register(&proc_root, &proc_scsi); |
proc_register(&proc_root, &proc_sys_root); |
proc_register(&proc_root, &pde_serial); |
|
#ifdef CONFIG_DEBUG_MALLOC |
proc_register(&proc_root, &pde_malloc); |
#endif |
proc_register(&proc_root, &pde_kcore); |
|
#ifdef CONFIG_MODULES |
proc_register(&proc_root, &pde_modules); |
proc_register(&proc_root, &pde_ksyms); |
#endif |
proc_register(&proc_root, &pde_stat); |
proc_register(&proc_root, &pde_devices); |
proc_register(&proc_root, &pde_interrupts); |
#ifdef __SMP_PROF__ |
proc_register(&proc_root, &pde_smp); |
#endif |
proc_register(&proc_root, &pde_filesystems); |
proc_register(&proc_root, &pde_dma); |
proc_register(&proc_root, &pde_ioports); |
proc_register(&proc_root, &pde_cmdline); |
#ifdef CONFIG_RTC |
proc_register(&proc_root, &pde_rtc); |
#endif |
proc_register(&proc_root, &pde_locks); |
|
proc_register( &proc_root, &pde_mounts); |
|
if (prof_shift) { |
proc_register(&proc_root, &pde_profile); |
} |
} |
|
|
int proc_match(int len,const char * name,struct proc_dir_entry * de) |
{ |
if (!de || !de->low_ino) |
return 0; |
/* "" means "." ---> so paths like "/usr/lib//libc.a" work */ |
if (!len && (de->name[0]=='.') && (de->name[1]=='\0')) |
return 1; |
if (de->namelen != len) |
return 0; |
return !memcmp(name, de->name, len); |
} |
|
int proc_lookup(struct inode * dir,const char * name, int len, |
struct inode ** result) |
{ |
struct proc_dir_entry * de; |
int ino; |
|
*result = NULL; |
if (!dir || !S_ISDIR(dir->i_mode)) { |
iput(dir); |
return -ENOTDIR; |
} |
|
de = (struct proc_dir_entry *) dir->u.generic_ip; |
if (!de) { |
iput(dir); |
return -EINVAL; |
} |
|
/* Special case "." and "..": they aren't on the directory list */ |
*result = dir; |
if (!len) |
return 0; |
if (name[0] == '.') { |
if (len == 1) |
return 0; |
if (name[1] == '.' && len == 2) { |
struct inode * inode; |
inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent); |
iput(dir); |
if (!inode) |
return -EINVAL; |
*result = inode; |
return 0; |
} |
} |
|
*result = NULL; |
for (de = de->subdir; de ; de = de->next) { |
if (proc_match(len, name, de)) |
break; |
} |
if (!de) { |
iput(dir); |
return -ENOENT; |
} |
|
ino = de->low_ino | (dir->i_ino & ~(0xffff)); |
|
if (!(*result = proc_get_inode(dir->i_sb, ino, de))) { |
iput(dir); |
return -EINVAL; |
} |
iput(dir); |
return 0; |
} |
|
static int proc_root_lookup(struct inode * dir,const char * name, int len, |
struct inode ** result) |
{ |
unsigned int pid, c; |
int i, ino, retval; |
|
dir->i_count++; |
retval = proc_lookup(dir, name, len, result); |
if (retval != -ENOENT) { |
iput(dir); |
return retval; |
} |
|
pid = 0; |
while (len-- > 0) { |
c = *name - '0'; |
name++; |
if (c > 9) { |
pid = 0; |
break; |
} |
pid *= 10; |
pid += c; |
if (pid & 0xffff0000) { |
pid = 0; |
break; |
} |
} |
for (i = 0 ; i < NR_TASKS ; i++) |
if (task[i] && task[i]->pid == pid) |
break; |
if (!pid || i >= NR_TASKS) { |
iput(dir); |
return -ENOENT; |
} |
ino = (pid << 16) + PROC_PID_INO; |
if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) { |
iput(dir); |
return -EINVAL; |
} |
iput(dir); |
return 0; |
} |
|
/* |
* This returns non-zero if at EOF, so that the /proc |
* root directory can use this and check if it should |
* continue with the <pid> entries.. |
* |
* Note that the VFS-layer doesn't care about the return |
* value of the readdir() call, as long as it's non-negative |
* for success.. |
*/ |
int proc_readdir(struct inode * inode, struct file * filp, |
void * dirent, filldir_t filldir) |
{ |
struct proc_dir_entry * de; |
unsigned int ino; |
int i; |
|
if (!inode || !S_ISDIR(inode->i_mode)) |
return -ENOTDIR; |
ino = inode->i_ino; |
de = (struct proc_dir_entry *) inode->u.generic_ip; |
if (!de) |
return -EINVAL; |
i = filp->f_pos; |
switch (i) { |
case 0: |
if (filldir(dirent, ".", 1, i, ino) < 0) |
return 0; |
i++; |
filp->f_pos++; |
/* fall through */ |
case 1: |
if (filldir(dirent, "..", 2, i, de->parent->low_ino) < 0) |
return 0; |
i++; |
filp->f_pos++; |
/* fall through */ |
default: |
ino &= ~0xffff; |
de = de->subdir; |
i -= 2; |
for (;;) { |
if (!de) |
return 1; |
if (!i) |
break; |
de = de->next; |
i--; |
} |
|
do { |
if (filldir(dirent, de->name, de->namelen, filp->f_pos, ino | de->low_ino) < 0) |
return 0; |
filp->f_pos++; |
de = de->next; |
} while (de); |
} |
return 1; |
} |
|
#define NUMBUF 10 |
|
static int proc_root_readdir(struct inode * inode, struct file * filp, |
void * dirent, filldir_t filldir) |
{ |
char buf[NUMBUF]; |
unsigned int nr,pid; |
unsigned long i,j; |
|
nr = filp->f_pos; |
if (nr < FIRST_PROCESS_ENTRY) { |
int error = proc_readdir(inode, filp, dirent, filldir); |
if (error <= 0) |
return error; |
filp->f_pos = nr = FIRST_PROCESS_ENTRY; |
} |
|
for (nr -= FIRST_PROCESS_ENTRY; nr < NR_TASKS; nr++, filp->f_pos++) { |
struct task_struct * p = task[nr]; |
|
if (!p || !(pid = p->pid)) |
continue; |
|
j = NUMBUF; |
i = pid; |
do { |
j--; |
buf[j] = '0' + (i % 10); |
i /= 10; |
} while (i); |
|
if (filldir(dirent, buf+j, NUMBUF-j, filp->f_pos, (pid << 16) + PROC_PID_INO) < 0) |
break; |
} |
return 0; |
} |
/net.c
0,0 → 1,120
/* |
* 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/segment.h> |
|
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ |
|
static int proc_readnet(struct inode * inode, struct file * file, |
char * buf, int 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 |
*/ |
memcpy_tofs(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, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* can't fsync */ |
}; |
|
/* |
* proc directories can do almost nothing.. |
*/ |
struct inode_operations proc_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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
/procfs_syms.c
0,0 → 1,47
#include <linux/module.h> |
#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; |
|
static struct symbol_table procfs_syms = { |
/* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */ |
#include <linux/symtab_begin.h> |
X(proc_register), |
X(proc_register_dynamic), |
X(proc_unregister), |
X(proc_root), |
X(in_group_p), |
X(proc_net_inode_operations), |
X(proc_net), |
|
/* |
* This is required so that if we load scsi later, that the |
* scsi code can attach to /proc/scsi in the correct manner. |
*/ |
X(proc_scsi), |
X(proc_scsi_inode_operations), |
X(dispatch_scsi_info_ptr), |
#include <linux/symtab_end.h> |
}; |
|
static struct file_system_type proc_fs_type = { |
proc_read_super, "proc", 0, NULL |
}; |
|
int init_proc_fs(void) |
{ |
int status; |
|
if ((status = register_filesystem(&proc_fs_type)) == 0) |
status = register_symtab(&procfs_syms); |
return status; |
} |
|
/fd.c
0,0 → 1,178
/* |
* linux/fs/proc/fd.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* proc fd directory handling functions |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/proc_fs.h> |
#include <linux/stat.h> |
|
static int proc_readfd(struct inode *, struct file *, void *, filldir_t); |
static int proc_lookupfd(struct inode *,const char *,int,struct inode **); |
|
static struct file_operations proc_fd_operations = { |
NULL, /* lseek - default */ |
NULL, /* read - bad */ |
NULL, /* write - bad */ |
proc_readfd, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* can't fsync */ |
}; |
|
/* |
* proc directories can do almost nothing.. |
*/ |
struct inode_operations proc_fd_inode_operations = { |
&proc_fd_operations, /* default base directory file-ops */ |
NULL, /* create */ |
proc_lookupfd, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static int proc_lookupfd(struct inode * dir, const char * name, int len, |
struct inode ** result) |
{ |
unsigned int ino, pid, fd, c; |
struct task_struct * p; |
struct super_block * sb; |
int i; |
|
*result = NULL; |
ino = dir->i_ino; |
pid = ino >> 16; |
ino &= 0x0000ffff; |
if (!dir) |
return -ENOENT; |
sb = dir->i_sb; |
if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) { |
iput(dir); |
return -ENOENT; |
} |
if (!len || (name[0] == '.' && (len == 1 || |
(name[1] == '.' && len == 2)))) { |
if (len < 2) { |
*result = dir; |
return 0; |
} |
if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) { |
iput(dir); |
return -ENOENT; |
} |
iput(dir); |
return 0; |
} |
iput(dir); |
fd = 0; |
while (len-- > 0) { |
c = *name - '0'; |
name++; |
if (c > 9) { |
fd = 0xfffff; |
break; |
} |
fd *= 10; |
fd += c; |
if (fd & 0xffff0000) { |
fd = 0xfffff; |
break; |
} |
} |
for (i = 0 ; i < NR_TASKS ; i++) |
if ((p = task[i]) && p->pid == pid) |
break; |
if (!pid || i >= NR_TASKS) |
return -ENOENT; |
|
if (fd >= NR_OPEN || !p->files || !p->files->fd[fd] |
|| !p->files->fd[fd]->f_inode) |
return -ENOENT; |
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; |
|
if (!(*result = proc_get_inode(sb, ino, NULL))) |
return -ENOENT; |
return 0; |
} |
|
#define NUMBUF 10 |
|
static int proc_readfd(struct inode * inode, struct file * filp, |
void * dirent, filldir_t filldir) |
{ |
char buf[NUMBUF]; |
int task_nr; |
struct task_struct * p; |
unsigned int fd, pid, ino; |
unsigned long i,j; |
|
if (!inode || !S_ISDIR(inode->i_mode)) |
return -EBADF; |
ino = inode->i_ino; |
pid = ino >> 16; |
ino &= 0x0000ffff; |
if (ino != PROC_PID_FD) |
return 0; |
|
for (fd = filp->f_pos; fd < 2; fd++, filp->f_pos++) { |
unsigned long ino = inode->i_ino; |
if (fd) |
ino = (ino & 0xffff0000) | PROC_PID_INO; |
if (filldir(dirent, "..", fd+1, fd, ino) < 0) |
return 0; |
} |
|
task_nr = 1; |
for (;;) { |
if ((p = task[task_nr]) && p->pid == pid) |
break; |
if (++task_nr >= NR_TASKS) |
return 0; |
} |
|
for (fd -= 2 ; fd < NR_OPEN; fd++, filp->f_pos++) { |
if (!p->files) |
break; |
if (!p->files->fd[fd] || !p->files->fd[fd]->f_inode) |
continue; |
|
j = NUMBUF; |
i = fd; |
do { |
j--; |
buf[j] = '0' + (i % 10); |
i /= 10; |
} while (i); |
|
ino = (pid << 16) + (PROC_PID_FD_DIR << 8) + fd; |
if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino) < 0) |
break; |
|
/* filldir() might have slept, so we must re-validate "p" */ |
if (p != task[task_nr] || p->pid != pid) |
break; |
} |
return 0; |
} |
/base.c
0,0 → 1,174
/* |
* linux/fs/proc/base.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* proc base directory handling functions |
*/ |
|
/* |
* uClinux revisions for NO_MM |
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, |
* The Silver Hammer Group, Ltd. |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/proc_fs.h> |
#include <linux/stat.h> |
|
static struct file_operations proc_base_operations = { |
NULL, /* lseek - default */ |
NULL, /* read - bad */ |
NULL, /* write - bad */ |
proc_readdir, /* readdir */ |
NULL, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* no special open code */ |
NULL, /* no special release code */ |
NULL /* can't fsync */ |
}; |
|
/* |
* proc directories can do almost nothing.. |
*/ |
static struct inode_operations proc_base_inode_operations = { |
&proc_base_operations, /* default base directory file-ops */ |
NULL, /* create */ |
proc_lookup, /* lookup */ |
NULL, /* link */ |
NULL, /* unlink */ |
NULL, /* symlink */ |
NULL, /* mkdir */ |
NULL, /* rmdir */ |
NULL, /* mknod */ |
NULL, /* rename */ |
NULL, /* readlink */ |
NULL, /* follow_link */ |
NULL, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
static void proc_pid_fill_inode(struct inode * inode) |
{ |
struct task_struct * p; |
int pid = inode->i_ino >> 16; |
int ino = inode->i_ino & 0xffff; |
|
for_each_task(p) { |
if (p->pid == pid) { |
if (p->dumpable || ino == PROC_PID_INO) { |
inode->i_uid = p->euid; |
inode->i_gid = p->gid; |
} |
return; |
} |
} |
} |
|
/* |
* 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 |
}; |
|
struct proc_dir_entry pde_status = { |
PROC_PID_STATUS, 6, "status", |
S_IFREG | S_IRUGO, 1, 0, 0, |
0, &proc_array_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_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, |
}; |
struct proc_dir_entry pde_cwd = { |
PROC_PID_CWD, 3, "cwd", |
S_IFLNK | S_IRWXU, 1, 0, 0, |
0, &proc_link_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_root = { |
PROC_PID_ROOT, 4, "root", |
S_IFLNK | S_IRWXU, 1, 0, 0, |
0, &proc_link_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_exe = { |
PROC_PID_EXE, 3, "exe", |
S_IFLNK | S_IRWXU, 1, 0, 0, |
0, &proc_link_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_fd = { |
PROC_PID_FD, 2, "fd", |
S_IFDIR | S_IRUSR | S_IXUSR, 1, 0, 0, |
0, &proc_fd_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_environ = { |
PROC_PID_ENVIRON, 7, "environ", |
S_IFREG | S_IRUSR, 1, 0, 0, |
0, &proc_array_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_pidcmdline = { |
PROC_PID_CMDLINE, 7, "cmdline", |
S_IFREG | S_IRUGO, 1, 0, 0, |
0, &proc_array_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_pidstat = { |
PROC_PID_STAT, 4, "stat", |
S_IFREG | S_IRUGO, 1, 0, 0, |
0, &proc_array_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
#ifndef NO_MM |
struct proc_dir_entry pde_statm = { |
PROC_PID_STATM, 5, "statm", |
S_IFREG | S_IRUGO, 1, 0, 0, |
0, &proc_array_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
struct proc_dir_entry pde_maps = { |
PROC_PID_MAPS, 4, "maps", |
S_IFIFO | S_IRUGO, 1, 0, 0, |
0, &proc_arraylong_inode_operations, |
NULL, proc_pid_fill_inode, |
}; |
#endif /* !NO_MM */ |
|
|
void proc_base_init(void) |
{ |
proc_register(&proc_pid, &pde_status); |
proc_register(&proc_pid, &pde_mem); |
proc_register(&proc_pid, &pde_cwd); |
proc_register(&proc_pid, &pde_root); |
proc_register(&proc_pid, &pde_exe); |
proc_register(&proc_pid, &pde_fd); |
proc_register(&proc_pid, &pde_environ); |
proc_register(&proc_pid, &pde_pidcmdline); |
proc_register(&proc_pid, &pde_pidstat); |
#ifndef NO_MM |
proc_register(&proc_pid, &pde_statm); |
proc_register(&proc_pid, &pde_maps); |
#endif /* !NO_MM */ |
}; |
/.depend
0,0 → 1,117
array.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel_stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/tty.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/user.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/a.out.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mman.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ioport.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/config.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/swap.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/pgtable.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/io.h |
base.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h |
fd.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h |
inode.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/locks.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/limits.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/system.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h |
kmsg.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/io.h |
link.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h |
mem.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/page.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/io.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/pgtable.h |
net.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fcntl.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h |
procfs_syms.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/module.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/symtab_begin.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/symtab_end.h |
root.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/config.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/bitops.h |
scsi.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h |
serial.o: \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/types.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/errno.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/sched.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/kernel_stat.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/tty.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/user.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/a.out.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/string.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mman.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/proc_fs.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/ioport.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/config.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/mm.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/pagemap.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/linux/swap.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/segment.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/pgtable.h \ |
/home/javier/opencores/or1k/rc203soc/sw/uClinux/include/asm/io.h |
/Makefile
0,0 → 1,15
# |
# Makefile for the linux proc-filesystem routines. |
# |
# Note! Dependencies are done automagically by 'make dep', which also |
# removes any old dependencies. DON'T put your own dependencies here |
# unless it's something special (ie not a .c file). |
# |
# Note 2! The CFLAGS definitions are now in the main makefile... |
|
O_TARGET := proc.o |
O_OBJS := inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o scsi.o serial.o |
OX_OBJS := procfs_syms.o |
M_OBJS := $(O_TARGET) |
|
include $(TOPDIR)/Rules.make |
/link.c
0,0 → 1,176
/* |
* linux/fs/proc/link.c |
* |
* Copyright (C) 1991, 1992 Linus Torvalds |
* |
* /proc link-file handling code |
*/ |
|
/* |
* uClinux revisions for NO_MM |
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>, |
* The Silver Hammer Group, Ltd. |
*/ |
|
#include <asm/segment.h> |
|
#include <linux/errno.h> |
#include <linux/sched.h> |
#include <linux/fs.h> |
#include <linux/mm.h> |
#include <linux/proc_fs.h> |
#include <linux/stat.h> |
|
static int proc_readlink(struct inode *, char *, int); |
static int proc_follow_link(struct inode *, struct inode *, int, int, |
struct inode **); |
|
/* |
* PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke |
* when the files[] array was updated only after the open code |
*/ |
#undef PLAN9_SEMANTICS |
|
/* |
* 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, /* select - default */ |
NULL, /* ioctl - default */ |
NULL, /* mmap */ |
NULL, /* very special open code */ |
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, /* readpage */ |
NULL, /* writepage */ |
NULL, /* bmap */ |
NULL, /* truncate */ |
NULL /* permission */ |
}; |
|
|
static int proc_follow_link(struct inode * dir, struct inode * inode, |
int flag, int mode, struct inode ** res_inode) |
{ |
unsigned int pid, ino; |
struct task_struct * p; |
struct inode * new_inode; |
int i, error; |
|
*res_inode = NULL; |
if (dir) |
iput(dir); |
if (!inode) |
return -ENOENT; |
if ((error = permission(inode, MAY_EXEC)) != 0){ |
iput(inode); |
return error; |
} |
ino = inode->i_ino; |
pid = ino >> 16; |
ino &= 0x0000ffff; |
for (i = 0 ; i < NR_TASKS ; i++) |
if ((p = task[i]) && p->pid == pid) |
break; |
if (i >= NR_TASKS) { |
iput(inode); |
return -ENOENT; |
} |
new_inode = NULL; |
switch (ino) { |
case PROC_PID_CWD: |
if (!p->fs) |
break; |
new_inode = p->fs->pwd; |
break; |
case PROC_PID_ROOT: |
if (!p->fs) |
break; |
new_inode = p->fs->root; |
break; |
#ifndef NO_MM |
case PROC_PID_EXE: { |
struct vm_area_struct * vma; |
if (!p->mm) |
break; |
vma = p->mm->mmap; |
while (vma) { |
if (vma->vm_flags & VM_EXECUTABLE) { |
new_inode = vma->vm_inode; |
break; |
} |
vma = vma->vm_next; |
} |
break; |
} |
#else /* NO_MM */ |
case PROC_PID_EXE: { |
new_inode = p->mm->executable; |
break; |
} |
#endif /* NO_MM */ |
default: |
switch (ino >> 8) { |
case PROC_PID_FD_DIR: |
if (!p->files) |
break; |
ino &= 0xff; |
if (ino < NR_OPEN && p->files && p->files->fd[ino]) { |
new_inode = p->files->fd[ino]->f_inode; |
} |
break; |
} |
} |
iput(inode); |
if (!new_inode) |
return -ENOENT; |
*res_inode = new_inode; |
new_inode->i_count++; |
return 0; |
} |
|
static int proc_readlink(struct inode * inode, char * buffer, int buflen) |
{ |
int i; |
unsigned int dev,ino; |
char buf[64]; |
|
if (!S_ISLNK(inode->i_mode)) { |
iput(inode); |
return -EINVAL; |
} |
i = proc_follow_link(NULL, inode, 0, 0, &inode); |
if (i) |
return i; |
if (!inode) |
return -EIO; |
dev = kdev_t_to_nr(inode->i_dev); |
ino = inode->i_ino; |
iput(inode); |
i = sprintf(buf,"[%04x]:%u", dev, ino); |
if (buflen > i) |
buflen = i; |
i = 0; |
while (i < buflen) |
put_user(buf[i++],buffer++); |
return i; |
} |