/* 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 . */
#include "node.h"
#include
#include
#include
#include
#include
/* 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;
}