/* 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; }