View of xos/fs/resolve.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 <current.h>
#include <verify_area.h>
#include <segment.h>
#include <errno.h>
#include <config.h>
 
/* Resolution de chemin */
 
static unsigned int get_len(const char *fs_basename)
{
  unsigned int len;
  char c;
 
  len = 0;
  while ((c = get_fs_byte(fs_basename)) && c != '/')
    len++, fs_basename++;
  return len;
}
 
/* Un point delicat dans resolve_internal() est de veiller a maintenir les
 * nombres de references de *file et *dir.
 * En cas de succes, 0 est retourne, une reference est comptee sur *dir, et si
 * *file n'est pas NULL, une reference est comptee sur *file.
 * En cas d'erreur, *file et *dir sont indefinis, et aucune reference n'est
 * comptee sur aucun fichier. */
static int resolve_internal(const char *fs_path, struct file_struct **file, struct file_struct **dir, const char **fs_basename)
{
  int retval;
  char c;
  unsigned int len;
 
  if (!verify_str(fs_path, PATH_MAX, &len))
    return -EFAULT;
  if (len == PATH_MAX)
    return -ENAMETOOLONG;
 
  /* un nom de chemin vide est une erreur */
  if (!get_fs_byte(fs_path))
    return -ENOENT;
 
  *dir = *file = get_fs_byte(fs_path) == '/' ? root : get_wd(); /* ! *dir demarre avec le premier repertoire du chemin, et non avec (*file)->parent */
  node_hold(*dir);
  node_hold(*file);
  *fs_basename = fs_path;
  while (1) {
    while ((c = get_fs_byte(fs_path)) == '/') /* passe les '/' consecutifs */
      fs_path++;
    if (!c) /* le chemin a ete entierement parcouru */
      return 0;
    node_release(*dir);
    if (!(*dir = *file)) { /* un composant non final n'existe pas */
      retval = -ENOENT;
      goto error; /* inutile de faire node_release() dans ce cas */
    }
    *fs_basename = fs_path;
    if ((len = get_len(fs_path)) > NAME_MAX) {
      retval = -ENAMETOOLONG;
      goto error_release_dir;
    }
    if ((retval = find_node(*dir, fs_path, file)) < 0) /* fichier designe par le composant */
      goto error_release_dir;
    if (*file)
      node_hold(*file);
    fs_path += len; /* passe le composant */
    if (*file && get_fs_byte(fs_path) == '/' && !node_is_dir(*file)) { /* s'il y a une suite, le composant doit etre un repertoire */
      retval = -ENOTDIR;
      goto error_release_file;
    }
  }
 
 error_release_file:
  node_release(*file);
 error_release_dir:
  node_release(*dir);
 error:
  return retval;
}
 
/* Le fichier retourne par resolve() ou resolve_dir() doit etre libere avec
 * node_release() des qu'il n'est plus utilise. */
 
int resolve(const char *fs_path, struct file_struct **file)
{
  struct file_struct *dir;
  const char *fs_basename;
  int rv;
 
  if ((rv = resolve_internal(fs_path, file, &dir, &fs_basename)) < 0)
    return rv;
  if (dir)
    node_release(dir);
  if (!*file)
    return -ENOENT;
  return 0;
}
 
int resolve_dir(const char *fs_path, struct file_struct **dir, const char **fs_basename)
{
  struct file_struct *file;
  int rv;
 
  if ((rv = resolve_internal(fs_path, &file, dir, fs_basename)) < 0)
    return rv;
  if (file)
    node_release(file);
  if (!*dir)
    return -ENOENT;
  return 0;
}
 
/* Retourne :
 * - une valeur negative en cas d'erreur ;
 * - 0 si le fichier n'existe pas ;
 * - une valeur positive si le fichier existe. */
int file_exists(const char *fs_path)
{
  struct file_struct *file;
  struct file_struct *dir;
  const char *fs_basename;
  int rv;
 
  if ((rv = resolve_internal(fs_path, &file, &dir, &fs_basename)) < 0)
    return rv;
  if (dir)
    node_release(dir);
  if (file)
    node_release(file);
  return file ? 1 : 0;
}