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