View of xos/fs/pfs.c


XOS | Parent Directory | View | Download

/* Bibliotheque de structure de donnees et fonctions pour les pseudo-systemes de fichiers */
/* 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 "pfs_structs.h"
#include "buffer.h"
#include "device_struct.h"
 
#include <kmalloc.h>
#include <verify_area.h>
#include <segment.h>
#include <errno.h>
#include <enums.h>
#include <config.h>
#include <string.h>
#include <stddef.h>
 
/* Noms de fichiers */
 
static inline char get_basename_char(const char *fs_p)
{
  char c;
 
  return (c = get_fs_byte(fs_p)) && c != '/' ? c : '\0';
}
 
static inline unsigned int get_basename_len(const char *fs_basename)
{
  unsigned int len;
  char c;
 
  len = 0;
  while ((c = get_basename_char(fs_basename++)))
    len++;
  return len;
}
 
/* Compare 2 noms. */
int pfs_name_match(const char *fs_basename, const char *name)
{
  char c;
 
  while ((c = get_basename_char(fs_basename++)))
    if (c != *name++)
      return 0;
  return !*name;
}
 
/* Retourne un nom alloue avec kmalloc(). */
char *pfs_create_name(const char *fs_basename)
{
  unsigned int len;
  char *name;
 
  len = get_basename_len(fs_basename);
  if (!(name = kmalloc(len + 1)))
    return NULL;
  memcpy_fromfs(name, fs_basename, len);
  name[len] = '\0';
  return name;
}
 
/* Repertoires */
 
/* Assigne le nom de fichier dans les informations d'une entree de repertoire.
 * name doit etre le pointeur nul ou avoir ete alloue avec kmalloc(). */
void pfs_set_dir_entry_info_name(struct pfs_dir_entry_info_struct *dir_entry_info, char *name)
{
  dir_entry_info->name_ptr = name;
  dir_entry_info->name = dir_entry_info->name_ptr;
}
 
/* Copie le nom de fichier dans les informations d'une entree de repertoire. */
int pfs_set_dir_entry_info_name_dup(struct pfs_dir_entry_info_struct *dir_entry_info, const char *name)
{
  unsigned int size;
 
  size = strlen(name) + 1;
  if (size <= sizeof dir_entry_info->name_buf) {
    dir_entry_info->name_ptr = NULL;
    strcpy(dir_entry_info->name_buf, name);
    dir_entry_info->name = dir_entry_info->name_buf;
  }
  else {
    if (!(dir_entry_info->name_ptr = kmalloc(size)))
      return -ENOMEM;
    strcpy(dir_entry_info->name_ptr, name);
    dir_entry_info->name = dir_entry_info->name_ptr;
  }
  return 0;
}
 
/* Libere un nom de fichier cree avec pfs_dir_entry_info_set_name() ou
 * pfs_dir_entry_info_set_name_dup(). */
void pfs_free_dir_entry_info_name(struct pfs_dir_entry_info_struct *dir_entry_info)
{
  if (dir_entry_info->name_ptr)
    kfree(dir_entry_info->name_ptr, strlen(dir_entry_info->name_ptr) + 1);
}
 
/* Initialise un repertoire. */
void pfs_init_dir(struct pfs_dir_struct *dir)
{
  dir->first_entry = NULL;
  dir->last_entry = NULL;
}
 
/* Insere une entree de repertoire dans un repertoire. */
void pfs_insert_dir_entry(struct pfs_dir_entry_struct *dir_entry, struct pfs_dir_struct *dir)
{
  dir_entry->previous = dir->last_entry;
  dir_entry->next = NULL;
  if (dir->last_entry)
    dir->last_entry->next = dir_entry;
  else
    dir->first_entry = dir_entry;
  dir->last_entry = dir_entry;
}
 
/* Supprime une entree de repertoire d'un repertoire. */
void pfs_remove_dir_entry(struct pfs_dir_entry_struct *dir_entry, struct pfs_dir_struct *dir)
{
  if (dir_entry->previous)
    dir_entry->previous->next = dir_entry->next;
  else
    dir->first_entry = dir_entry->next;
  if (dir_entry->next)
    dir_entry->next->previous = dir_entry->previous;
  else
    dir->last_entry = dir_entry->previous;
}
 
/* Operations sur les repertoires */
 
/* Dit si un nom de fichier existe dans un repertoire. */
int pfs_file_exists(const struct pfs_dir_struct *dir, const char *fs_basename)
{
  const struct pfs_dir_entry_struct *dir_entry;
 
  for (dir_entry = dir->first_entry; dir_entry; dir_entry = dir_entry->next)
    if (pfs_name_match(fs_basename, dir_entry->info.name))
      return 1;
  return 0;
}
 
/* Recherche un fichier dans un repertoire. */
int pfs_find(const struct pfs_dir_struct *dir, const char *fs_basename, unsigned long *id, int *type)
{
  const struct pfs_dir_entry_struct *dir_entry;
 
  for (dir_entry = dir->first_entry; dir_entry; dir_entry = dir_entry->next)
    if (pfs_name_match(fs_basename, dir_entry->info.name)) {
      *id = dir_entry->info.id;
      *type = dir_entry->info.type;
      return 1;
    }
  return 0;
}
 
/* Lit un nom. */
int pfs_read_name(const char *name, unsigned long pos, char *fs_buf, unsigned int count)
{
  const char *s;
  int n;
 
  s = &name[pos]; /* position courante */
  n = 0; /* nombre de caracteres lus */
  while (1) {
    put_fs_byte(*s, fs_buf++);
    n++;
    count--;
    if (!count || !*s) /* on a lu le nombre de caracteres demandes ou on arrive au bout du nom de l'entree */
      break;
    s++;
  }
  return n;
}
 
/* Lit un repertoire. */
int pfs_read_dir(const struct pfs_dir_struct *dir, unsigned long pos, char *fs_buf, unsigned int count)
{
  const struct pfs_dir_entry_struct *dir_entry;
  unsigned int len;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  /* on passe les entrees inferieures a pos */
  dir_entry = dir->first_entry;
  while (1) {
    if (!dir_entry)
      return 0; /* plus d'entree a lire */
    len = strlen(dir_entry->info.name);
    if (pos < len + 1)
      break; /* une entree a lire */
    pos -= len + 1;
    dir_entry = dir_entry->next;
  }
 
  /* lecture de l'entree ou se trouve pos */
  return pfs_read_name(dir_entry->info.name, pos, fs_buf, count);
}
 
/* Utilitaires pour les fichiers */
 
/* Lit des zeros. */
int pfs_read_zeros(char *fs_buf, unsigned int count)
{
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  memset_tofs(fs_buf, '\0', count);
  return count;
}
 
/* Lit dans une zone memoire. */
int pfs_read_buf(const char *buf, unsigned int buf_size, unsigned long pos, char *fs_buf, unsigned int count)
{
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  if (pos >= buf_size)
    return 0;
  if (count > buf_size - pos)
    count = buf_size - pos;
  memcpy_tofs(fs_buf, buf + pos, count);
  return count;
}
 
/* Lit dans un peripherique de type bloc. */
int pfs_read_block(const struct device_struct *device, unsigned long pos, char *fs_buf, unsigned int count)
{
  unsigned int n;
  int block;
  char *buf;
  unsigned int size;
  int retval;
 
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_WRITE))
    return -EFAULT;
 
  n = 0;
  for (block = pos / BLOCK_SIZE; ; block++) {
    if (!(buf = buffer_get(device, block))) {
      retval = -EIO;
      goto end;
    }
    if (!buffer_update(buf)) {
      retval = -EIO;
      goto release;
    }
    size = BLOCK_SIZE - pos % BLOCK_SIZE;
    if (count - n <= size) {
      memcpy_tofs(fs_buf, buf + pos % BLOCK_SIZE, count - n);
      retval = count;
      goto release;
    }
    else {
      memcpy_tofs(fs_buf, buf + pos % BLOCK_SIZE, size);
      n += size;
      fs_buf += size;
      pos += size;
    }
    buffer_release(buf);
  }
 release:
  buffer_release(buf);
 end:
  return retval;
}
 
/* Ecrit dans un peripherique de type bloc. */
int pfs_write_block(const struct device_struct *device, unsigned long pos, const char *fs_buf, unsigned int count)
{
  unsigned int n, p;
  int block;
  char *buf;
  unsigned int size;
  int retval;
 
  if (!buffer_is_writable(device))
    return -EPERM;
  if (!count)
    return 0;
  if (!verify_area(fs_buf, count, PF_READ))
    return -EFAULT;
 
  n = 0;
  for (block = pos / BLOCK_SIZE; ; block++) {
    if (!(buf = buffer_get(device, block))) {
      retval = -EIO;
      goto end;
    }
    if (!buffer_update(buf)) {
      retval = -EIO;
      goto release;
    }
    p = n; /* p compte le nombre de caracteres qui seraient ecrits si le bloc etait ecrit */
    size = BLOCK_SIZE - pos % BLOCK_SIZE;
    if (size > count - p)
      size = count - p;
    memcpy_fromfs(buf + pos % BLOCK_SIZE, fs_buf, size);
    p += size;
    fs_buf += size;
    pos += size;
    buffer_dirty(buf);
    n = p; /* le bloc est (potentiellement) ecrit */
    if (n == count) {
      retval = n;
      goto release;
    }
    buffer_release(buf);
  }
 release:
  buffer_release(buf);
 end:
  buffer_sync(device);
  return retval;
}