/* 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 "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 struct devfs_file_struct; struct devfs_file_operations_struct { int (*stat)(const struct devfs_file_struct *, struct stat_struct *); int (*read)(const struct devfs_file_struct *, unsigned long, char *, unsigned int); int (*write)(const struct devfs_file_struct *, unsigned long, const char *, unsigned int); int (*ioctl)(const struct devfs_file_struct *, int request, void *fs_arg); }; struct devfs_file_struct { struct pfs_dir_entry_struct dir_entry; /* entree de repertoire */ union { struct pfs_dir_struct dir; /* repertoire */ int device_id; /* numero de peripherique */ }; const struct devfs_file_operations_struct *operations; }; static int mount_count; /* nombre de montages */ static long mount_time; /* heure du premier montage */ /* Gestion des fichiers */ static int init_device_file(struct devfs_file_struct *file, const struct device_struct *device, const struct devfs_file_operations_struct *operations) { int rv; file->dir_entry.info.id = device->id; if ((rv = pfs_set_dir_entry_info_name_dup(&file->dir_entry.info, device->name)) < 0) return rv; file->dir_entry.info.type = device->type; file->device_id = device->id; file->operations = operations; return 0; } static void destroy_device_file(struct devfs_file_struct *file) { pfs_free_dir_entry_info_name(&file->dir_entry.info); } /* Operations sur les fichiers */ static int find(const struct devfs_file_struct *file, const char *fs_basename, unsigned long *id, int *type) { return pfs_find(&file->dir, fs_basename, id, type); } static int get_name(const struct devfs_file_struct *file, char name[]) { strcpy(name, file->dir_entry.info.name); return 0; } static int spec_get_device(const struct devfs_file_struct *file, struct device_struct **spec_device) { struct device_struct *device; if (!(device = get_device(file->device_id))) return -ENXIO; *spec_device = device; return 0; } static int stat_common(const struct devfs_file_struct *file, struct stat_struct *fs_buf, int device_id, long mtime) { struct stat_struct statbuf; 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 = device_id; statbuf.size = 0; statbuf.mtime = mtime; memcpy_tofs(fs_buf, &statbuf, sizeof (struct stat_struct)); return 0; } static int root_stat(const struct devfs_file_struct *file, struct stat_struct *fs_buf) { return stat_common(file, fs_buf, DEV_NONE, mount_time); } static int spec_stat(const struct devfs_file_struct *file, struct stat_struct *fs_buf) { return stat_common(file, fs_buf, file->device_id, 0); } static int read_dir(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count) { return pfs_read_dir(&file->dir, pos, fs_buf, count); } static int block_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count) { const struct device_struct *device; if (!(device = get_device(file->device_id))) return -ENXIO; return pfs_read_block(device, pos, fs_buf, count); } static int devfs_console_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count) { return console_read(fs_buf, count); } static int null_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count) { return 0; } static int zero_read(const struct devfs_file_struct *file, unsigned long pos, char *fs_buf, unsigned int count) { return pfs_read_zeros(fs_buf, count); } static int block_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count) { const struct device_struct *device; if (!(device = get_device(file->device_id))) return -ENXIO; return pfs_write_block(device, pos, fs_buf, count); } static int devfs_console_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count) { return console_write(fs_buf, count); } static int null_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count) { return count; } static int full_write(const struct devfs_file_struct *file, unsigned long pos, const char *fs_buf, unsigned int count) { return -ENOSPC; } static int inval_ioctl(const struct devfs_file_struct *file, int request, void *fs_arg) { return -EINVAL; } static int devfs_console_ioctl(const struct devfs_file_struct *file, int request, void *fs_arg) { return console_ioctl(request, fs_arg); } static const struct devfs_file_operations_struct root_operations = { .stat = root_stat }; static const struct devfs_file_operations_struct fd_operations = { .stat = spec_stat, .read = block_read, .write = block_write, .ioctl = inval_ioctl }; static const struct devfs_file_operations_struct console_operations = { .stat = spec_stat, .read = devfs_console_read, .write = devfs_console_write, .ioctl = devfs_console_ioctl }; static const struct devfs_file_operations_struct null_operations = { .stat = spec_stat, .read = null_read, .write = null_write, .ioctl = inval_ioctl }; static const struct devfs_file_operations_struct zero_operations = { .stat = spec_stat, .read = zero_read, .write = null_write, .ioctl = inval_ioctl }; static const struct devfs_file_operations_struct full_operations = { .stat = spec_stat, .read = zero_read, .write = full_write, .ioctl = inval_ioctl }; /* Fichiers */ /* racine */ static struct devfs_file_struct root_file = { .operations = &root_operations, .dir_entry = { .info = { .id = 0, .type = FT_DIR } } }; /* fichiers peripheriques */ static const struct devfs_file_operations_struct *const device_operations[NR_DEVICES] = { [DEV_FD] = &fd_operations, [DEV_CONSOLE] = &console_operations, [DEV_NULL] = &null_operations, [DEV_ZERO] = &zero_operations, [DEV_FULL] = &full_operations }; /* index des fichiers par identifiant */ static struct devfs_file_struct *file_index[NR_DEVICES]; /* creation des fichiers selon les peripheriques existants */ static int probe_devices() { int id; const struct device_struct *device; struct devfs_file_struct *file; int retval; for (id = 1; id < NR_DEVICES; id++) if ((device = get_device(id)) && device_operations[id]) { if (!(file = kmalloc(sizeof (struct devfs_file_struct)))) { retval = -ENOMEM; goto error; } if ((retval = init_device_file(file, device, device_operations[id])) < 0) goto error_free; pfs_insert_dir_entry(&file->dir_entry, &root_file.dir); file_index[id] = file; } else file_index[id] = NULL; return 0; error_free: kfree(file, sizeof (struct devfs_file_struct)); error: while (id > 1) { id--; if (file_index[id]) { destroy_device_file(file_index[id]); kfree(file_index[id], sizeof (struct devfs_file_struct)); } } return retval; } static int init() { int rv; pfs_init_dir(&root_file.dir); file_index[0] = &root_file; if ((rv = probe_devices()) < 0) return rv; return 0; } static void destroy() { int id; for (id = 1; id < NR_DEVICES; id++) if (file_index[id]) { destroy_device_file(file_index[id]); kfree(file_index[id], sizeof (struct devfs_file_struct)); } } /* resolution des fichiers */ static struct devfs_file_struct *get_file(unsigned long id) { return file_index[id]; } /* Methodes du systeme de fichiers */ static int devfs_mount(const struct device_struct *device, void **data) { int rv; if (!mount_count) { mount_time = get_time(); if ((rv = init()) < 0) return rv; } mount_count++; *data = NULL; return 0; } static void devfs_umount(void *data) { if (!--mount_count) destroy(); } static int devfs_find(void *data, unsigned long dir_id, const char *fs_basename, unsigned long *id, int *type) { return find(get_file(dir_id), fs_basename, id, type); } static int devfs_get_name(void *data, unsigned long id, char name[]) { return get_name(get_file(id), name); } static int devfs_get_device(void *data, unsigned long spec_id, struct device_struct **spec_device) { return spec_get_device(get_file(spec_id), spec_device); } static int devfs_stat(void *data, unsigned long id, struct stat_struct *fs_buf) { const struct devfs_file_struct *file; file = get_file(id); return file->operations->stat(file, fs_buf); } static int devfs_read_dir(void *data, unsigned long dir_id, unsigned long pos, char *fs_buf, unsigned int count) { return read_dir(get_file(dir_id), pos, fs_buf, count); } static int devfs_read(void *data, unsigned long leaf_id, unsigned long pos, char *fs_buf, unsigned int count) { const struct devfs_file_struct *file; file = get_file(leaf_id); return file->operations->read(file, pos, fs_buf, count); } static int devfs_write(void *data, unsigned long leaf_id, unsigned long pos, const char *fs_buf, unsigned int count) { const struct devfs_file_struct *file; file = get_file(leaf_id); return file->operations->write(file, pos, fs_buf, count); } static int devfs_ioctl(void *data, unsigned long spec_id, int request, void *fs_arg) { const struct devfs_file_struct *file; file = get_file(spec_id); return file->operations->ioctl(file, request, fs_arg); } const struct file_system_struct devfs = { .name = "devfs", .type = FT_NONE, .mount = devfs_mount, .umount = devfs_umount, .find = devfs_find, .get_name = devfs_get_name, .get_device = devfs_get_device, .get_size = NULL, .stat = devfs_stat, .read_dir = devfs_read_dir, .read = devfs_read, .write = devfs_write, .ioctl = devfs_ioctl, .create = no_create, .mkdir = no_mkdir, .can_remove = no_can_remove, .remove = no_remove, .rmdir = no_rmdir, .rename = no_rename };