Système de fichiers

L'auteur de cette page est : Jean-Baptiste Yunès

Introduction

Un fichier est un objet sur lequel il est possible de réaliser certaines opérations comme la lecture/écriture des données qui lui sont associées ou la modification/consultation de certains de ses attributs. POSIX ne définit que les types de fichiers suivants : fichiers normaux, fichiers en mode caractère, fichiers en mode bloc, tubes et répertoires.

D'autres types de fichiers sont utilisés par les diverses normes Unix. Par exemple pour Unix98 il existe un type de fichier appelé lien symbolique (XSH - System Interface and Headers) ou un autre appelé socket (XNS - Networking Services).

Un nom de fichier est une suite de caractères ne contenant ni / ni le caractère <NUL>. On utilise parfois le terme composant d'une référence. Attention, le nom d'un fichier ne fait pas partie de ses attributs.

Un numéro de série est un identifiant unique associé à un fichier dans un système de fichiers donné.

Pour Unix cet identifiant est appelé numéro d'inoeud.

Un système de fichiers est une collection de fichiers. Il fournit un espace de nommage pour les numéros de série.

Le nombre de liens d'un fichier correspond au nombre d'entrées de répertoire faisant référence à un fichier particulier.

Les répertoires contiennent toujours au moins deux noms de fichiers : . et .. permettant d'accéder respectivement au répertoire lui-même et à son père.

Le répertoire père d'un fichier (par abus de language) est le répertoire contenant un entrée de répertoire faisant référence au fichier dont on parle. Le répertoire père d'un répertoire est celui qui contient une entrée de répertoire faisant référence au répertoire considéré et qui est référencé par .. dans le répertoire considéré. Il faut noter que le père du répertoire racine est lui-même.

Un préfixe de référence est une référence de répertoire pouvant se terminer par un caractère /.

Une référence est une chaîne de caractères utilisée pour identifier un fichier. La longueur maximale d'une telle chaîne est PATH_MAX y compris le caractère <NUL> de fin. Dans une référence toute suite de caractères / est interprétée comme un seul; ce caractère est utilisé comme séparateur de nom de fichier. Une référence de répertoire peut se terminer par une suite de /.

Une référence relative est une référence ne commençant pas par un /. La recherche du fichier commence à partir du répertoire courant.

Une référence absolue est une référence commençant par un /. La recherche du fichier commence à partir du répertoire racine.

Un nom de fichier portable (ISO/IEC 9945) n'utilise que les caractères alphanumériques et/ou ., _, -. Le dernier ne devant jamais apparaître comme premier caractère du nom.

Une référence portable ne doît utiliser que les caractères définis pour les noms de fichiers portables plus le caractère /.

Un fichier normal est un fichier dans lequel il est possible d'accéder à volonté à n'importe quel octet, de plus aucune structure n'est imposée par le système.

Ce n'est pas le cas pour de nombreux autres systèmes d'exploitation. Par exemple MS/DOS differencie les fichiers texte des fichiers binaires. Pour VMS il existe une très grande variété de fichiers structurés, indexés, etc.

Un répertoire est un fichier de type particulier contenant des entrées de répertoire.

Une entrée de répertoire ou un lien est un objet réalisant l'association entre un nom de fichier et un fichier.

Le répertoire racine est utilisé par l'algorithme de résolution des références afin de localiser un fichier dont la référence commence par /.

La hiérarchie des fichiers désigne un arbre dont la racine est le répertoire racine, les noeuds des répertoires et les feuilles des fichiers. En réalité il s'agit d'un graphe dirigé.

Un fichier en mode caractère est un fichier sur lequel les opérations sont naturellement des entrées/sorties de flots de caractères. Le seul fichier de type caractère définit par POSIX est le terminal.

Pour de nombreux systèmes de la famille Unix il existe d'autres périphériques dont l'accès est par nature effectué en mode caractère : les souris, les sorties sons ou mème la mémoire du noyau.

Un fichier en mode bloc est un fichier permettant d'accéder directement à un périhérique dont la nature est de communiquer naturellement par bloc d'octets, mais en réalité l'accès au périphérique est rendu entièrement transparent.

Ces périphériques sont généralement des disques.

Les tubes sont des fichiers dont les écritures et les lectures s'effectuent en mode "premier arrivé, premier servi".

Les attributs d'un fichier sont :

Le type d'un fichier est normal, répertoire, tube, mode bloc ou mode caractère.

Le contrôle d'accès à un fichier est constitué des droits d'accès et des délégations de pouvoir.

La délégation de pouvoir d'un fichier ne fonctionne que pour les fichiers exécutables. Elle permet d'attribuer les droits du propriétaire du fichier au processus exécutant le fichier ou les droits du groupe propriétaire du fichier.

Les droits d'accès d'un fichier permettent de sélectionner les processus qui tentent de réaliser des opérations sur le fichier. Ces droits sont divisés en trois classes :

L'autorisation d'accès utilise le mécanisme suivant :

Les opérations sur le système de fichiers

Caractéristiques des objets du système de fichiers

Le fichier d'entête <sys/stat.h> contient la définition du type struct stat définissant les caractéristiques associées suivantes associées à chaque inoeud :

Les champs du type struct stat
Type Nom Description
mode_t st_mode droits d'accès et type de l'inoeud
ino_t st_ino numéro de série ou d'inoeud
dev_t st_dev numéro du périphérique contenant l'objet
nlink_t st_nlink nombre de liens (références sur l'objet)
uid_t st_uid identité du propriétaire
gid_t st_gid identité du groupe propriétaire
off_t st_size longueur exprimée en octets pour les fichiers réguliers
time_t st_atime date de dernier accès
time_t st_mtime date de dernière modification
time_t st_ctime date de dernière modification du statut

On trouvera les macros-définitions suivantes permettant de tester si le champ st_mode correspond à un certain type d'inoeud :

S_ISDIR(mode) vrai si l'inoeud est celui d'un répertoire.

S_ISCHR(mode) vrai si l'inoeud est celui d'un fichier spécial à accès par caractère.

S_ISBLK(mode) vrai si l'inoeud est celui d'un fichier spécial à accès par bloc.

S_ISREG(mode) vrai si l'inoeud est celui d'un fichier normal (régulier).

S_ISFIFO(mode) vrai si l'inoeud est celui d'un tube nommé ou anonyme.

Les droits d'accès sont accessibles par l'intermédiaire des constantes (champs de bits) suivantes :

S_IRWXU
droits de lecture, écriture et exécution ou parcours pour le propriétaire. Cette constante est obtenue par combinaison des constantes suivantes :
S_IRUSR
droit de lecture pour le propriétaire
S_IWUSR
droit d'écriture pour le propriétaire
S_IXUSR
droit d'exécution ou de parcours pour le propriétaire
S_IRWXG
droits de lecture, écriture et exécution ou parcours pour le groupe propriétaire. Cette constante est obtenue par combinaison des constantes suivantes :
S_IRGRP
droit de lecture pour le groupe propriétaire
S_IWGRP
droit d'écriture pour le groupe propriétaire
S_IXGRP
droit d'exécution ou de parcours pour le groupe propriétaire
S_IRWXO
droits de lecture, écriture et exécution ou parcours pour les autres. Cette constante est obtenue par combinaison des constantes suivantes :
S_IROTH
droit de lecture pour les autres.
S_IWOTH
droit d'écriture pour les autres
S_IXOTH
droit d'exécution pour les autres
S_ISUID
surcharge du propriétaire effectif à l'exécution.
S_ISGID
surcharge du groupe propriétaire effectif à l'exécution.

La manipulation du répertoire courant

Le répertoire courant est utilisé comme point de départ de la résolution des références relatives. Deux fonctions de manipulation sont disponibles :

int getcwd(chaîne,taille) permet de retrouver la référence absolue du répertoire courant. Voici une possible commande pwd :


int chdir(référence) permet de changer de répertoire courant.

La création de fichiers et de liens.

int open(référence,mode_ouverture,mode_protection) peut être utilisée pour créer un fichier et lui associer un nom. Pour cela il faut utiliser le mode O_CREAT, mais attention car si la référence existe déjà aucune erreur n'est renvoyée. On peut alors utiliser O_EXCL qui permet de réaliser une véritable création.

int creat(référence,mode) est strictement équivalent à open(référence,O_WRONLY|O_CREAT|O_TRUNC,mode)

mode_t umask(mode) permet de modifier le masque de création (en renvoyant l'ancienne valeur). Les modes positionnés dans ce masque seront masqués lors d'une création d'inoeud (open(), creat(), mkdir() et mkfifo()).

int link(référence_source,référence) permet de créér un nouveau lien pour un fichier déjà existant, autrement dit le fichier devient alors accessible sous deux noms différents.

int mkdir(référence,mode) permet de créér un nouveau répertoire. Cette opération créé trois nouveaux liens : son nouveau nom dans le répertoire qui le contient, son propre nom dans lui-même (.) et un lien vers son père dans lui-même (..). Voici un exemple de commande mkdir :

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc,char *argv[]) {
  int retour;

  if (argc<2) {
    fprintf(stderr,"Usage: %s <dir> [<dir> ...]\n",argv[0]);
    exit(EXIT_FAILURE);
  }
  retour = EXIT_SUCCESS;
  while ( *++argv ) {
    if (mkdir(*argv,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) == -1) {
      fprintf(stderr,"Impossible de créer %s\n",*argv);
      retour = EXIT_FAILURE;
    }
  }
  exit(retour);
}

int mkfifo(référence,mode) permet de créér un fichier de type S_IFIFO.

La suppression de liens.

int unlink(référence) supprime une entrée de répertoire et décrémente le nombre de liens associé à l'inoeud correspondant. Le fichier n'est effacé que lorsque l'inoeud n'est plus accessible (plus de références ni de descripteurs).

int rmdir(référence) supprime un répertoire à condition qu'il soit vide (ne contient que les deux références . et ..).

La modification des liens

int rename(ancienne_référence,nouvelle_référence) permet de renommer un objet. Le renommage consiste en la création d'une nouvelle référence et suppression de l'ancienne de façon atomique. Attention : le terme de renommage est ambigu, en effet il s'agit bien de manipulation de références donc il est possible de déplacer (le nom pas l'inoeud).

Consultation des répertoires

Pour des raisons de portabilité l'accès aux données d'un fichier de type répertoire n'est rendue possible que par l'utilisation de diverses fonctions de manipulations.

Pour la création d'une entrée de répertoire il faut utiliser la fonction mkdir() car les données présentes dans les répertoires sont extrèmement sensibles (la consistance du système de fichiers lui-même est en jeu).

La consultation des répertoires est transparente vis-à-vis de l'implantation du système de fichiers à travers les fonctions suivantes :

DIR *opendir(const char *référence) renvoie un descripteur de répertoire lequel sera utilisé pour parcourir la liste des entrées une entrée à la fois.

struct dirent *readdir(DIR *descripteur) renvoie l'entrée suivante du répertoire désigné par le descripteur, NULL si l'on est en fin de parcours. Pour POSIX, une entrée de répertoire est au format suivant:

struct dirent {
    char d_name[];
};

void rewinddir(DIR *descripteur) peut être utilisée afin de reprendre le parcours du répertoire désigné par descripteur au début en resynchronisant la lecture sur l'état courant du répertoire.

void closedir(DIR *descripteur) referme le flot de lecture du répertoire, le descripteur n'est plus utilisable sauf à le réouvrir.

Un exemple : un mini-ls

#include "stdlib.h"
#include "stdio.h"
#include "sys/types.h"
#include "dirent.h"

static int ls(const char* repertoire) {
  DIR           *descripteur;
  struct dirent *entree;

  descripteur = opendir(repertoire);
  if (descripteur == NULL) {
    fprintf(stderr,"Ouverture du répertoire %s impossible.\n",repertoire);
    return EXIT_FAILURE;
  }
  while ((entree = readdir(descripteur)) != NULL) {
    puts(entree->d_name);
  }
  closedir(descripteur);
  return EXIT_SUCCESS;
}

int main(int argc,char *argv[]) {
  int i, retour;

  if (argc==1)
    retour = ls(".");
  else {
    retour = EXIT_SUCCESS;
    for (i=1; i<argc; i++)
      if (ls(argv[i]) != EXIT_SUCCESS)
	retour = EXIT_FAILURE;
  }
  exit(retour);
}

La manipulation du statut des inoeuds

int stat(référence,statut) permet de récupérer les caractéristiques associées à l'inoeud pointé par la référence.

int fstat(descripteur,statut) permet de récupérer les caractéristiques associées à l'inoeud associé au descripteur.

On pourra construire la commande suivante :


dont l'exécution pourrait fournir les résultats suivants :

<shell> statut statut . /dev/tty-rwxr-xr-x  1    user1    staff     18388 Thu  5 Apr 2001 15:18:18 statut
drwxr-xr-x  2    user1    staff       512 Thu  5 Apr 2001 15:18:15 .
crw-rw-rw-  1     root      tty         0 Fri 16 Mar 2001 19:57:01 /dev/tty
<shell> statutcrw--w----  1    user1      tty         0 Thu  5 Apr 2001 15:18:38 <STDIN>
<verif-37-[15:18]-[fs]>

int access(référence,mode_accès) permet de déterminer si l'appelant possèdent les droits suffisant pour tenter les différents accès demandés. Les mode d'accès possibles sont une combinaison quelconque des accès de base suivants :

R_OK
droit de lecture
W_OK
droit d'écriture
X_OK
droit d'exécution
F_OK
teste l'existence de l'objet

int chmod(référence,droits) permet de modifier les droits d'accès relatifs à l'inoeud correspondant à la référence.

int fchmod(descripteur,droits) permet de modifier les droits d'accès relatifs à l'inoeud correspondant au descripteur.

int chown(référence,propriétaire,groupe) permet de changer le propriétaire et groupe propriétaire de l'inoeud correspondant.

int utime(référence,dates) permet de modifier les dates de dernier accès et dernière modification de l'inoeud associé à la référence.

int ftruncate(descripteur,longueur) permet de tronquer un fichier à une longueur spécifiée.

Variables du système de fichiers

long pathconf(référence,symbole) permet de déterminer la valeur d'un certain nombre de constantes définies dans l'instance courante du système de fichier.

long fpathconf(descripteur,symbole) permet de déterminer la valeur d'un certain nombre de constantes associés au système de fichier associé.

Les symboles définis sont les suivants :

Symboles variables
symbole variable valeur minimale
(symbole POSIX)
valeur minimale description
_PC_LINK_MAX LINK_MAX _POSIX_LINK_MAX 32 nombre maximal de liens autorisés sur l'inoeud
_PC_MAX_CANON MAX_CANON _POSIX_MAX_CANON 255 nombre maximal de caractères gérés par un terminal en mode canonique
_PC_MAX_INPUT MAX_INPUT _POSIX_MAX_INPUT 255 nombre minimum de caractères pouvant être gérés par la file d'entrée d'un terminal (ie: nombre maximum qu'une application portable sera en droit d'obliger à saisir avant de les lire)
_PC_NAME_MAX NAME_MAX _POSIX_NAME_MAX 14 nombre maximal de caractères pouvant apparaitre dans un composant de référence
_PC_PATH_MAX PATH_MAX _POSIX_PATH_MAX 255 nombre maximal de caractères pouvant apparaitre dans une référence
_PC_PIPE_BUF PIPE_BUF _POSIX_PIPE_BUF 512 nombre maximal de caractères pour lequel l'atomicité d'une opération d'écriture dans un tube est garantie