View of xos/fs/devfs.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "file_system_struct.h"
#include "ro_fs.h"
#include "pfs.h"
#include "pfs_structs.h"
#include "device_struct.h"
 
#include <console.h>
#include <time.h>
#include <stat_struct.h>
#include <device_manager.h>
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <consts.h>
#include <enums.h>
#include <errno.h>
#include <config.h>
#include <string.h>
#include <stddef.h>
 
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
};