View of xos/fs/procfs.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 "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 <current.h>
#include <proc.h>
#include <map_info.h>
#include <map_info_struct.h>
#include <string_table.h>
#include <string_table_struct.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 <sprintf.h>
#include <string.h>
#include <stddef.h>
 
#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
};