View of xos/fs/path.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 "node.h"
 
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <errno.h>
#include <enums.h>
#include <config.h>
#include <util.h>
 
struct traverse_fo_struct;
 
struct traverse_fo_vtbl_struct {
  void (*process)(struct traverse_fo_struct *traverse_fo, char c);
  int (*get_retval)(struct traverse_fo_struct *traverse_fo);
};
 
struct traverse_fo_struct {
  const struct traverse_fo_vtbl_struct *vptr;
};
 
/* get_path_length */
 
struct get_path_length_fo_struct {
  struct traverse_fo_struct traverse_fo;
  unsigned long *length;
};
 
static void get_path_length_process(struct traverse_fo_struct *traverse_fo, char c)
{
  struct get_path_length_fo_struct *get_path_length_fo;
 
  get_path_length_fo = downcast(struct get_path_length_fo_struct, traverse_fo, traverse_fo);
  (*get_path_length_fo->length)++;
}
 
static int get_path_length_get_retval(struct traverse_fo_struct *traverse_fo)
{
  return 0;
}
 
static const struct traverse_fo_vtbl_struct get_path_length_traverse_fo_vtbl = {
  .process = get_path_length_process,
  .get_retval = get_path_length_get_retval
};
 
static void get_path_length_fo_init(struct get_path_length_fo_struct *get_path_length_fo, unsigned long *length)
{
  get_path_length_fo->traverse_fo.vptr = &get_path_length_traverse_fo_vtbl;
  get_path_length_fo->length = length;
  (*get_path_length_fo->length) = 0;
}
 
/* write_path */
 
struct write_path_fo_struct {
  struct traverse_fo_struct traverse_fo;
  unsigned long pos;
  char *fs_buf;
  unsigned int count;
  unsigned long skip_count;
  unsigned int n;
};
 
static void write_path_process(struct traverse_fo_struct *traverse_fo, char c)
{
  struct write_path_fo_struct *write_path_fo;
 
  write_path_fo = downcast(struct write_path_fo_struct, traverse_fo, traverse_fo);
  if (write_path_fo->skip_count)
    write_path_fo->skip_count--;
  else if (write_path_fo->n < write_path_fo->count) {
    put_fs_byte(c, write_path_fo->fs_buf++);
    write_path_fo->n++;
    write_path_fo->pos++;
  }
}
 
static int write_path_get_retval(struct traverse_fo_struct *traverse_fo)
{
  struct write_path_fo_struct *write_path_fo;
 
  write_path_fo = downcast(struct write_path_fo_struct, traverse_fo, traverse_fo);
  return write_path_fo->n;
}
 
static const struct traverse_fo_vtbl_struct write_path_traverse_fo_vtbl = {
  .process = write_path_process,
  .get_retval = write_path_get_retval
};
 
static void write_path_fo_init(struct write_path_fo_struct *write_path_fo, unsigned long pos, char *fs_buf, unsigned int count)
{
  write_path_fo->traverse_fo.vptr = &write_path_traverse_fo_vtbl;
  write_path_fo->pos = pos;
  write_path_fo->fs_buf = fs_buf;
  write_path_fo->count = count;
  write_path_fo->skip_count = pos;
  write_path_fo->n = 0;
}
 
/* write_path_kernel */
 
struct write_path_kernel_fo_struct {
  struct traverse_fo_struct traverse_fo;
  char *buf;
};
 
static void write_path_kernel_process(struct traverse_fo_struct *traverse_fo, char c)
{
  struct write_path_kernel_fo_struct *write_path_kernel_fo;
 
  write_path_kernel_fo = downcast(struct write_path_kernel_fo_struct, traverse_fo, traverse_fo);
  *write_path_kernel_fo->buf++ = c;
}
 
static int write_path_kernel_get_retval(struct traverse_fo_struct *traverse_fo)
{
  return 0;
}
 
static const struct traverse_fo_vtbl_struct write_path_kernel_traverse_fo_vtbl = {
  .process = write_path_kernel_process,
  .get_retval = write_path_kernel_get_retval
};
 
static void write_path_kernel_fo_init(struct write_path_kernel_fo_struct *write_path_kernel_fo, char *buf)
{
  write_path_kernel_fo->traverse_fo.vptr = &write_path_kernel_traverse_fo_vtbl;
  write_path_kernel_fo->buf = buf;
}
 
/* traverse */
 
static int traverse_internal(const struct file_struct *node, int next, struct traverse_fo_struct *traverse_fo)
{
  char name[NAME_MAX + 1];
  const char *s;
  int rv;
 
  if (node == root)
    traverse_fo->vptr->process(traverse_fo, '/');
  else {
    if ((rv = traverse_internal(node_get_parent(node), 1, traverse_fo)) < 0)
      return rv;
    if ((rv = node_get_name(node, name)) < 0)
      return rv;
    for (s = name; *s; s++)
      traverse_fo->vptr->process(traverse_fo, *s);
    if (next)
      traverse_fo->vptr->process(traverse_fo, '/');
  }
  return 0;
}
 
static int traverse(const struct file_struct *node, struct traverse_fo_struct *traverse_fo)
{
  int rv;
 
  if ((rv = traverse_internal(node, 0, traverse_fo)) < 0)
    return rv;
  return traverse_fo->vptr->get_retval(traverse_fo);
}
 
/* Fonctions exportees */
 
int get_path_length(const struct file_struct *node, unsigned long *length)
{
  struct get_path_length_fo_struct get_path_length_fo;
 
  get_path_length_fo_init(&get_path_length_fo, length);
  return traverse(node, &get_path_length_fo.traverse_fo);
}
 
int write_path(const struct file_struct *node, unsigned long pos, char *fs_buf, unsigned int count)
{
  struct write_path_fo_struct write_path_fo;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
  write_path_fo_init(&write_path_fo, pos, fs_buf, count);
  return traverse(node, &write_path_fo.traverse_fo);
}
 
int get_path(const struct file_struct *node, char **path)
{
  unsigned long len;
  struct get_path_length_fo_struct get_path_length_fo;
  struct write_path_kernel_fo_struct write_path_kernel_fo;
  int rv;
 
  get_path_length_fo_init(&get_path_length_fo, &len);
  if ((rv = traverse(node, &get_path_length_fo.traverse_fo)) < 0)
    return rv;
  if (!(*path = kmalloc(len + 1)))
    return -ENOMEM;
  write_path_kernel_fo_init(&write_path_kernel_fo, *path);
  if ((rv = traverse(node, &write_path_kernel_fo.traverse_fo)) < 0)
    kfree(*path, len + 1);
  else
    (*path)[len] = '\0';
  return rv;
}