/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "path.h"
#include "file_system_instance.h"
#include "file_systems.h"
#include "file_system_struct.h"
#include "ro_fs.h"
#include "pfs.h"
#include "pfs_structs.h"
#include "device_struct.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define procfs_get_id(pid, num) ((unsigned long)((((pid) + 1) << 4) + (num))) /* l'id 0 est reserve a la racine */
#define procfs_get_dir_id(pid) procfs_get_id(pid, 0)
#define procfs_get_pid(id) ((int)(((id) >> 4) - 1))
#define procfs_get_num(id) ((unsigned int)((id) & 15))
#define print_buf(expr, s, size) ({ \
unsigned int _n = (expr); \
s += _n; \
size = size > _n ? size - _n : 0; \
})
struct procfs_file_struct;
struct procfs_file_operations_struct {
int (*get_size)(const struct procfs_file_struct *, unsigned long *);
long (*get_mtime)();
int (*read)(const struct procfs_file_struct *, unsigned long, char *, unsigned int);
};
struct procfs_dir_struct;
struct procfs_file_struct {
struct pfs_dir_entry_info_struct dir_entry_info; /* informations de repertoire */
int (*build_dir)(const struct procfs_file_struct *, struct procfs_dir_struct *);
void (*destroy_dir)(struct procfs_dir_struct *);
int pid; /* pid */
const struct procfs_file_operations_struct *operations;
};
/* patron pour les fichiers fournissant des informations sur les processus */
struct procfs_proc_file_struct {
unsigned int num; /* identifiant dans le repertoire */
struct procfs_file_struct file; /* patron */
void (*build_file)(const struct procfs_proc_file_struct *, struct procfs_file_struct *, int);
};
static int mount_count; /* nombre de montages */
static long mount_time; /* heure du premier montage */
/* Repertoires */
static void init_root_file_dir_entry_info(struct pfs_dir_entry_info_struct *dir_entry_info, const struct procfs_file_struct *file)
{
*dir_entry_info = file->dir_entry_info;
}
static int init_proc_root_dir_entry_info(struct pfs_dir_entry_info_struct *dir_entry_info, int pid)
{
char buf[32];
int rv;
dir_entry_info->id = procfs_get_dir_id(pid);
sprintf(buf, "%d", pid);
if ((rv = pfs_set_dir_entry_info_name_dup(dir_entry_info, buf)) < 0)
return rv;
dir_entry_info->type = FT_DIR;
return 0;
}
static void destroy_proc_root_dir_entry_info(struct pfs_dir_entry_info_struct *dir_entry_info)
{
pfs_free_dir_entry_info_name(dir_entry_info);
}
static void init_self_dir_entry_info(struct pfs_dir_entry_info_struct *dir_entry_info)
{
dir_entry_info->id = procfs_get_dir_id(get_pid());
dir_entry_info->name = "self";
dir_entry_info->type = FT_DIR;
}
static void init_proc_file_dir_entry_info(struct pfs_dir_entry_info_struct *dir_entry_info, const struct procfs_proc_file_struct *proc_file, int pid)
{
dir_entry_info->id = procfs_get_id(pid, proc_file->num);
dir_entry_info->name = proc_file->file.dir_entry_info.name;
dir_entry_info->type = proc_file->file.dir_entry_info.type;
}
/* Operations sur les fichiers */
static long get_mount_time()
{
return mount_time;
}
/* root */
static int root_build_dir(const struct procfs_file_struct *file, struct procfs_dir_struct *dir);
static void root_destroy_dir(struct procfs_dir_struct *dir);
static const struct procfs_file_operations_struct root_operations = {
.get_mtime = get_mount_time
};
/* devices */
static int print_device(const struct device_struct *device, char *buf, unsigned int size)
{
return snprintf(buf, size, "%3d %s\n", device->id, device->name);
}
static int print_devices(char *buf, unsigned int size)
{
char *s;
int id;
const struct device_struct *device;
s = buf;
print_buf(snprintf(s, size, "Character devices:\n"), s, size);
for (id = 0; id < NR_DEVICES; id++)
if ((device = get_device(id)) && device->type == FT_CHR)
print_buf(print_device(device, s, size), s, size);
print_buf(snprintf(s, size, "\n"), s, size);
print_buf(snprintf(s, size, "Block devices:\n"), s, size);
for (id = 0; id < NR_DEVICES; id++)
if ((device = get_device(id)) && device->type == FT_BLK)
print_buf(print_device(device, s, size), s, size);
return s - buf;
}
static int devices_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
*size = print_devices(NULL, 0);
return 0;
}
static int devices_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
char *buf;
unsigned int len;
int retval;
if (!(len = print_devices(NULL, 0)))
return 0;
if (!(buf = kmalloc(len + 1)))
return -ENOMEM;
print_devices(buf, len + 1);
retval = pfs_read_buf(buf, len, pos, fs_buf, count);
kfree(buf, len + 1);
return retval;
}
static const struct procfs_file_operations_struct devices_operations = {
.get_size = devices_get_size,
.get_mtime = get_time,
.read = devices_read
};
/* filesystems */
static int filesystems_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
*size = print_file_systems(NULL, 0);
return 0;
}
static int filesystems_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
char *buf;
unsigned int len;
int retval;
if (!(len = print_file_systems(NULL, 0)))
return 0;
if (!(buf = kmalloc(len + 1)))
return -ENOMEM;
print_file_systems(buf, len + 1);
retval = pfs_read_buf(buf, len, pos, fs_buf, count);
kfree(buf, len + 1);
return retval;
}
static const struct procfs_file_operations_struct filesystems_operations = {
.get_size = filesystems_get_size,
.get_mtime = get_time,
.read = filesystems_read
};
/* mounts */
static int mounts_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
*size = print_mounts(NULL, 0);
return 0;
}
static int mounts_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
char *buf;
unsigned int len;
int retval;
if ((len = print_mounts(NULL, 0)) <= 0)
return 0;
if (!(buf = kmalloc(len + 1)))
return -ENOMEM;
print_mounts(buf, len + 1);
retval = pfs_read_buf(buf, len, pos, fs_buf, count);
kfree(buf, len + 1);
return retval;
}
static const struct procfs_file_operations_struct mounts_operations = {
.get_size = mounts_get_size,
.get_mtime = get_time,
.read = mounts_read
};
/* proc */
static int proc_build_dir(const struct procfs_file_struct *file, struct procfs_dir_struct *dir);
static void proc_destroy_dir(struct procfs_dir_struct *dir);
static const struct procfs_file_operations_struct proc_operations = {
.get_mtime = get_time
};
static void build_proc_root(const struct procfs_proc_file_struct *proc_file, struct procfs_file_struct *file, int pid)
{
*file = proc_file->file;
init_proc_root_dir_entry_info(&file->dir_entry_info, pid);
file->pid = pid;
}
static void build_proc_file(const struct procfs_proc_file_struct *proc_file, struct procfs_file_struct *file, int pid)
{
*file = proc_file->file;
file->dir_entry_info.id = procfs_get_id(pid, proc_file->num);
file->pid = pid;
}
/* stat */
static int get_vsize(int pid, unsigned long *vsize)
{
struct map_info_struct *map_info_array;
unsigned int map_info_count;
struct map_info_struct *map_info;
int retval;
if ((retval = proc_get_maps(pid, &map_info_array, &map_info_count)) < 0)
return retval;
*vsize = 0;
for (map_info = &map_info_array[0]; map_info < &map_info_array[map_info_count]; map_info++)
*vsize += map_info->end_addr - map_info->start_addr;
retval = 0;
if (map_info_array) {
for (map_info = &map_info_array[0]; map_info < &map_info_array[map_info_count]; map_info++)
map_info_destroy(map_info);
kfree(map_info_array, map_info_count * sizeof (struct map_info_struct));
}
return retval;
}
static char get_state_char(int state)
{
switch (state) {
case PS_IDLE:
return 'I';
case PS_RUNNING:
return 'R';
case PS_WAITING_UNINTERRUPTIBLE:
return 'D';
case PS_WAITING_INTERRUPTIBLE:
return 'W';
case PS_SLEEPING:
return 'S';
case PS_STOPPED:
return 'T';
case PS_ZOMBIE:
return 'Z';
default:
return '?';
}
}
static int print_stat(int pid, char *buf, unsigned int buf_size)
{
int state;
int ppid;
int pgid;
unsigned long vsize;
int rv;
if ((rv = proc_get_state_pid(pid, &state)) < 0)
return rv;
if ((rv = proc_get_ppid_pid(pid, &ppid)) < 0)
return rv;
if ((rv = proc_get_pgid_pid(pid, &pgid)) < 0)
return rv;
if ((rv = get_vsize(pid, &vsize)) < 0)
return rv;
return snprintf(buf, buf_size, "%d %c %d %d %lu\n", pid, buf ? get_state_char(state) : ' ', ppid, pgid, vsize);
}
static int stat_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
int rv;
if ((rv = print_stat(file->pid, NULL, 0)) < 0)
return rv;
*size = rv;
return 0;
}
static int stat_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
char buf[128];
int rv;
if ((rv = print_stat(file->pid, buf, sizeof buf)) < 0)
return rv;
return pfs_read_buf(buf, rv, pos, fs_buf, count);
}
static const struct procfs_file_operations_struct stat_operations = {
.get_size = stat_get_size,
.get_mtime = get_time,
.read = stat_read
};
/* cmdline */
static int cmdline_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
struct string_table_struct cmdline;
int rv;
if ((rv = proc_get_cmdline_pid(file->pid, &cmdline)) < 0)
return rv;
*size = cmdline.size;
string_table_destroy(&cmdline);
return 0;
}
static int cmdline_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
struct string_table_struct cmdline;
int retval;
if ((retval = proc_get_cmdline_pid(file->pid, &cmdline)) < 0)
return retval;
retval = pfs_read_buf(cmdline.buf, cmdline.size, pos, fs_buf, count);
string_table_destroy(&cmdline);
return retval;
}
static const struct procfs_file_operations_struct cmdline_operations = {
.get_size = cmdline_get_size,
.get_mtime = get_time,
.read = cmdline_read
};
/* cwd */
static int cwd_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
struct file_struct *wd;
int rv;
if ((rv = proc_get_wd_pid(file->pid, &wd)) < 0)
return rv;
if (!wd) {
*size = 0;
return 0;
}
else
return get_path_length(wd, size);
}
static int cwd_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
struct file_struct *wd;
int rv;
if ((rv = proc_get_wd_pid(file->pid, &wd)) < 0)
return rv;
return wd ? write_path(wd, pos, fs_buf, count) : 0;
}
static const struct procfs_file_operations_struct cwd_operations = {
.get_size = cwd_get_size,
.get_mtime = get_time,
.read = cwd_read
};
/* maps */
static int print_maps(const struct map_info_struct *map_info_array, unsigned int map_info_count, char *buf, unsigned int size)
{
char *s;
const struct map_info_struct *map_info;
s = buf;
for (map_info = &map_info_array[0]; map_info < &map_info_array[map_info_count]; map_info++)
print_buf(snprintf(s, size, "%08lx-%08lx %c%c %08lx %-10u %-10lu %s\n", map_info->start_addr, map_info->end_addr, map_info->perms.read ? 'r' : '-', map_info->perms.write ? 'w' : '-', map_info->offset, map_info->dev, map_info->id, map_info->pathname ? : ""), s, size);
return s - buf;
}
static int maps_get_size(const struct procfs_file_struct *file, unsigned long *size)
{
struct map_info_struct *map_info_array;
unsigned int map_info_count;
int rv;
if ((rv = proc_get_maps(file->pid, &map_info_array, &map_info_count)) < 0)
return rv;
*size = print_maps(map_info_array, map_info_count, NULL, 0);
return 0;
}
static int maps_read(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
struct map_info_struct *map_info_array, *map_info;
unsigned int map_info_count;
char *buf;
unsigned int len;
int retval;
if ((retval = proc_get_maps(file->pid, &map_info_array, &map_info_count)) < 0)
return retval;
if (!(len = print_maps(map_info_array, map_info_count, NULL, 0))) {
retval = 0;
goto free_map_info_array;
}
if (!(buf = kmalloc(len + 1))) {
retval = -ENOMEM;
goto free_map_info_array;
}
print_maps(map_info_array, map_info_count, buf, len + 1);
retval = pfs_read_buf(buf, len, pos, fs_buf, count);
kfree(buf, len + 1);
free_map_info_array:
if (map_info_array) {
for (map_info = &map_info_array[0]; map_info < &map_info_array[map_info_count]; map_info++)
map_info_destroy(map_info);
kfree(map_info_array, map_info_count * sizeof (struct map_info_struct));
}
return retval;
}
static const struct procfs_file_operations_struct maps_operations = {
.get_size = maps_get_size,
.get_mtime = get_time,
.read = maps_read
};
/* Fichiers */
static struct procfs_file_struct root_file_table[] = {
/* repertoire racine */
{
.dir_entry_info = {
.type = FT_DIR
},
.build_dir = root_build_dir,
.destroy_dir = root_destroy_dir,
.operations = &root_operations
},
/* devices */
{
.dir_entry_info = {
.name = "devices",
.type = FT_REG
},
.operations = &devices_operations
},
/* filesystems */
{
.dir_entry_info = {
.name = "filesystems",
.type = FT_REG
},
.operations = &filesystems_operations
},
/* mounts */
{
.dir_entry_info = {
.name = "mounts",
.type = FT_REG
},
.operations = &mounts_operations
}
};
static const unsigned int root_file_table_size = sizeof root_file_table / sizeof root_file_table[0];
static struct procfs_proc_file_struct proc_file_table[] = {
/* repertoire racine */
{
.file = {
.build_dir = proc_build_dir,
.destroy_dir = proc_destroy_dir,
.operations = &proc_operations
},
.build_file = build_proc_root
},
/* stat */
{
.file = {
.dir_entry_info = {
.name = "stat",
.type = FT_REG
},
.operations = &stat_operations
},
.build_file = build_proc_file
},
/* cmdline */
{
.file = {
.dir_entry_info = {
.name = "cmdline",
.type = FT_REG
},
.operations = &cmdline_operations
},
.build_file = build_proc_file
},
/* cwd */
{
.file = {
.dir_entry_info = {
.name = "cwd",
.type = FT_REG
},
.operations = &cwd_operations
},
.build_file = build_proc_file
},
/* maps */
{
.file = {
.dir_entry_info = {
.name = "maps",
.type = FT_REG
},
.operations = &maps_operations
},
.build_file = build_proc_file
}
};
static const unsigned int proc_file_table_size = sizeof proc_file_table / sizeof proc_file_table[0];
/* donnees additionnelles specifiques aux repertoires */
struct procfs_dir_struct {
struct pfs_dir_struct dir; /* repertoire */
union {
struct { /* racine */
struct pfs_dir_entry_struct root_file_dir_entry_table[sizeof root_file_table / sizeof root_file_table[0]];
struct pfs_dir_entry_struct *root_proc_dir_entry_table;
unsigned int root_proc_dir_entry_table_size;
struct pfs_dir_entry_struct self_dir_entry;
};
struct { /* repertoire representant un processus */
struct pfs_dir_entry_struct proc_file_dir_entry_table[sizeof proc_file_table / sizeof proc_file_table[0]];
};
};
};
static int find(const struct procfs_file_struct *file, const char *fs_basename, unsigned long *id, int *type)
{
struct procfs_dir_struct dir;
int retval;
if ((retval = file->build_dir(file, &dir)) < 0)
return retval;
retval = pfs_find(&dir.dir, fs_basename, id, type);
file->destroy_dir(&dir);
return retval;
}
static int stat(const struct procfs_file_struct *file, struct stat_struct *fs_buf)
{
struct stat_struct statbuf;
int rv;
if (!verify_area(fs_buf, sizeof (struct stat_struct), PF_WRITE))
return -EFAULT;
statbuf.dev = DEV_NONE;
statbuf.id = file->dir_entry_info.id;
statbuf.type = file->dir_entry_info.type;
statbuf.rdev = DEV_NONE;
if (file->dir_entry_info.type == FT_REG) {
if ((rv = file->operations->get_size(file, &statbuf.size)) < 0)
return rv;
}
else
statbuf.size = 0;
statbuf.mtime = file->operations->get_mtime();
memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct));
return 0;
}
static int read_dir(const struct procfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count)
{
struct procfs_dir_struct dir;
int retval;
if ((retval = file->build_dir(file, &dir)) < 0)
return retval;
retval = pfs_read_dir(&dir.dir, pos, fs_buf, count);
file->destroy_dir(&dir);
return retval;
}
static int get_name(const struct procfs_file_struct *file, char name[])
{
strcpy(name, file->dir_entry_info.name);
return 0;
}
static int root_build_dir(const struct procfs_file_struct *file, struct procfs_dir_struct *dir)
{
int *pid_table;
unsigned int pid_table_size;
unsigned int i;
int retval;
pfs_init_dir(&dir->dir);
/* fichiers */
for (i = 1; i < root_file_table_size; i++) {
init_root_file_dir_entry_info(&dir->root_file_dir_entry_table[i].info, &root_file_table[i]);
pfs_insert_dir_entry(&dir->root_file_dir_entry_table[i], &dir->dir);
}
/* repertoires des processus */
if ((retval = proc_get_pid_table(&pid_table, &pid_table_size)) < 0)
goto error;
if (!(dir->root_proc_dir_entry_table = kmalloc(pid_table_size * sizeof (struct pfs_dir_entry_struct)))) {
retval = -ENOMEM;
goto error_free_pid_table;
}
dir->root_proc_dir_entry_table_size = pid_table_size;
for (i = 0; i < dir->root_proc_dir_entry_table_size; i++) {
if ((retval = init_proc_root_dir_entry_info(&dir->root_proc_dir_entry_table[i].info, pid_table[i])) < 0)
goto error_free_root_proc_dir_entry_table;
pfs_insert_dir_entry(&dir->root_proc_dir_entry_table[i], &dir->dir);
}
kfree(pid_table, pid_table_size * sizeof (int));
/* self */
init_self_dir_entry_info(&dir->self_dir_entry.info);
pfs_insert_dir_entry(&dir->self_dir_entry, &dir->dir);
return 0;
error_free_root_proc_dir_entry_table:
while (i > 0) {
i--;
destroy_proc_root_dir_entry_info(&dir->root_proc_dir_entry_table[i].info);
}
kfree(dir->root_proc_dir_entry_table, dir->root_proc_dir_entry_table_size * sizeof (struct pfs_dir_entry_struct));
error_free_pid_table:
kfree(pid_table, pid_table_size * sizeof (int));
error:
return retval;
}
static void root_destroy_dir(struct procfs_dir_struct *dir)
{
unsigned int i;
for (i = 0; i < dir->root_proc_dir_entry_table_size; i++)
destroy_proc_root_dir_entry_info(&dir->root_proc_dir_entry_table[i].info);
kfree(dir->root_proc_dir_entry_table, dir->root_proc_dir_entry_table_size * sizeof (struct pfs_dir_entry_struct));
}
static int proc_build_dir(const struct procfs_file_struct *file, struct procfs_dir_struct *dir)
{
unsigned int i;
pfs_init_dir(&dir->dir);
for (i = 1; i < proc_file_table_size; i++) {
init_proc_file_dir_entry_info(&dir->proc_file_dir_entry_table[i].info, &proc_file_table[i], file->pid);
pfs_insert_dir_entry(&dir->proc_file_dir_entry_table[i], &dir->dir);
}
return 0;
}
static void proc_destroy_dir(struct procfs_dir_struct *dir) {}
static void init()
{
unsigned int i;
for (i = 0; i < root_file_table_size; i++)
root_file_table[i].dir_entry_info.id = i;
for (i = 0; i < proc_file_table_size; i++)
proc_file_table[i].num = i;
}
/* resolution des fichiers */
static struct procfs_file_struct *get_file(unsigned long id, struct procfs_file_struct *filebuf)
{
const struct procfs_proc_file_struct *proc_file;
if (id < root_file_table_size)
return &root_file_table[id];
else {
proc_file = &proc_file_table[procfs_get_num(id)];
proc_file->build_file(proc_file, filebuf, procfs_get_pid(id));
return filebuf;
}
}
/* Methodes du systeme de fichiers */
static int procfs_mount(const struct device_struct *device, void **data)
{
if (!mount_count) {
mount_time = get_time();
init();
}
mount_count++;
*data = NULL;
return 0;
}
static void procfs_umount(void *data)
{
mount_count--;
}
static int procfs_find(void *data, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type)
{
struct procfs_file_struct filebuf;
return find(get_file(dir_id, &filebuf), fs_basename, id, type);
}
static int procfs_get_name(void *data, unsigned long id, char name[])
{
struct procfs_file_struct filebuf;
return get_name(get_file(id, &filebuf), name);
}
static int procfs_get_size(void *data, unsigned long reg_id, unsigned long *size)
{
struct procfs_file_struct filebuf;
const struct procfs_file_struct *file;
file = get_file(reg_id, &filebuf);
return file->operations->get_size(file, size);
}
static int procfs_stat(void *data, unsigned long id, struct stat_struct *fs_buf)
{
struct procfs_file_struct filebuf;
return stat(get_file(id, &filebuf), fs_buf);
}
static int procfs_read_dir(void *data, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count)
{
struct procfs_file_struct filebuf;
return read_dir(get_file(dir_id, &filebuf), pos, fs_buf, count);
}
static int procfs_read(void *data, unsigned long leaf_id, unsigned long pos, char *fs_buf, unsigned int count)
{
struct procfs_file_struct filebuf;
const struct procfs_file_struct *file;
file = get_file(leaf_id, &filebuf);
return file->operations->read(file, pos, fs_buf, count);
}
const struct file_system_struct procfs = {
.name = "procfs",
.type = FT_NONE,
.mount = procfs_mount,
.umount = procfs_umount,
.find = procfs_find,
.get_name = procfs_get_name,
.get_device = NULL,
.get_size = procfs_get_size,
.stat = procfs_stat,
.read_dir = procfs_read_dir,
.read = procfs_read,
.write = no_write,
.ioctl = NULL,
.create = no_create,
.mkdir = no_mkdir,
.can_remove = no_can_remove,
.remove = no_remove,
.rmdir = no_rmdir,
.rename = no_rename
};