/* 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 };