Artisan Numérique

/système/stockage/ Les liens dans les systèmes *nix

Le concept de lien est présent sur beaucoup d'OS mais sous *nix (et donc linux) c'est une religion. Les liens sont partout et il suffit de taper un ls -la /etc pour s'en convaincre. Ce petit tutorial a donc pour objectif d'expliquer un peu les différents liens et leur utilisation possible.

Le lien symbolique

Clairement le plus utilisé, il est possible, de loin, de comparer le lien symbolique au .lnk sous Windows, mais de très loin alors...

Le lien symbolique est un "faux fichier" ou un "faux dossier" qui contient le chemin d'un "vrai fichier" ou d'un "vrai dossier". Une fois un lien symbolique établi, pour la majorité des applications, le fichier ou dossier cible est bien réel. Et lorsqu'on supprime un lien symbolique, le fichier ou dossier d'origine n'en est en rien modifié. Dans l'autre sens, si l'on détruit le fichier/dossier source d'un lien symbolique, celui-ci ne disparaît pas pour autant, et lors d'un ls, il va apparaître clignotant et en erreur, ce sera un lien "cassé".

Une utilisation naturelle du lien symbolique est de "bouturer" un dossier/fichier venant d'un bout de l'arborescence du système de fichier vers un autre dossier, mais en le laissant "physiquement" là où il est.

La syntaxe du lien symbolique est ln -s source [cible]. Si la "cible" est omise, c'est le dernier élément du chemin de "source" qui est utilisé, dans le dossier courant. Par exemple :

gaston# Lien d'un dossier vers un autre
gastonln -s /home/mon_dossier /home/worspace/mon_dossier

gaston# Sans différence, lien d'un fichier vers un autre fichier
gastonln -s /home/fichier.txt /home/worspace/fichier.txt

gaston# Les mêmes syntaxe en omettant le chemin cible
gastoncd /home/workspace
gastonln -s /home/mon_dossier
gastonln -s /home/fichier.txt

gaston# Voir le résultat des liens effectués
gastonls -la
fichier.txt -> /home/fichier.txt
mon_dossier -> /home/mon_dossier

gaston# la même chose avec la commande stat
gastonstat /etc/alternatives/gcc
File: `/etc/alternatives/gcc' -> `/usr/bin/gcc-4.2.2'
Size: 18              Blocks: 0          IO Block: 4096   lien symbolique
...

Dans un script, il peut être important d'avoir le chemin réel d'un lien symbolique, c'est le rôle de la commande readlink. Le script suivant permet donc de tester si le paramétre est un lien symbolique et de le résoudre si c'est le cas pour renvoyer le véritable chemin :

#! /bin/sh
file=$1
while [ -L $file ] ; do
  file=$(readlink $file)
done
echo $file

L'autre utilisation classique des liens consiste à s'en servir pour choisir une version de logiciel. Par exemple si l'on a plusieurs version de mon_utilitaire (1.0, 2.0, etc...), si chacune, dans le dossier /usr/bin est nommée avec sa version comme suffixe (mon_utilitaire-1.0, mon_utilitaire-2.0, etc.), il suffit de faire un lien sur la bonne version au grès des besoins :

gaston# lien sur la version 1.0
gastonln -s /usr/bin/mon_utilitaire_1.0 /usr/bin/mon_utilitaire

gaston# Utilisation de mon utilitaire, en version 1.0
gastonmon_utilitaire

gaston# changement de version
gastonrm -f /usr/bin/mon_utilitaire
gastonln -s /usr/bin/mon_utilitaire_2.0 /usr/bin/mon_utilitaire

gaston# Utilisation de mon utilitaire, en version 2.0
gastonmon_utilitaire

Cette aspect est déjà implémenté dans beaucoup de distribution pour choisir ce que l'on appelle les "alternatives", c'est à dire la bonne version de gcc, de vi, de smb, etc... Sur la Mandriva, la liste des alternatives est disponible dans le dossier /etc/alternatives et la commande update-alternatives permet de gérer les liens symboliques pour vous :

gastonls -la /etc/alternatives
...
gcc -> gcc -> /usr/bin/gcc-4.2.2
...

gastonupdate-alternatives --list gcc
/usr/bin/gcc-4.2.2
/usr/bin/gcc-3.3.6

gastonupdate-alternatives --config gcc
There are 2 programs which provide `gcc'.
Selection    Command
-----------------------------------------------
*+    1        /usr/bin/gcc-4.2.2
2        /usr/bin/gcc-3.3.6
Enter to keep the default[*], or type selection number:

Dernier aspect du lien symbolique, il peut relier des systèmes de fichiers différents. C'est à dire que l'on peut faire un lien symbolique entre n'importe lesquelles des dossiers/fichiers sans se préoccuper du type de montage du dossier parent (clef usb, cd-rom, nfs, etc..).

Maintenant, l'inconvénient des liens symboliques reste leur relative lenteur. En effet, à chaque entrée dans un dossier lié, le fichier du lien symbolique va devoir être lu par le système, interprété, puis celui ci va devoir le fermer pour aller ouvrir le "vrai" fichier ou dossier. Il n'est donc pas très conseillé sur un serveur web par exemple où un dossier va être accédé des centaines de fois par minutes. Un aspect réglé par un autre type de lien, le hard-link.

Le lien "hard"

Si le lien symbolique est géré par le système d'exploitation et ne dépend donc pas du système de fichier utilisé, Le lien "hard" quant à lui est directement géré par un système de fichier. Cela veut déjà dire qu'il ne fonctionnera donc pas pour tous les systèmes de fichier. Un volume monté en FAT par exemple ne passera pas, mais tous les FS sérieux disposent du hard-link (ext3, reiser, xfs, etc..).

Pour bien saisir ce que sont les hard-links, il faut comprendre que, pour les systèmes de fichiers qui le supportent, tous les fichiers/dossiers sont des liens hard. En effet, il faut ici distinguer deux notions : le nom du fichier (avec son nom, son chemin, etc.) et le bloc de données (un paquet de 0 et de 1 localisé à une position N du disque dur). Lorsque l'on crée un fichier, le système va d'abord allouer un bloc de donnée sur le disque dur, puis faire un hard-link entre ce bloc de donnée et le nom de fichier.

Ainsi, si vous avez un fichier qui s'appelle toto.txt dans le dossier /mes_dossiers, le nom de fichier /mes_donnees/toto.txt est en réalité un hard-link entre d'un côté le bloc de données du fichier toto.txt (les 0 et les 1) et le nom de fichier que vous lui avez donné (comprenant son chemin). Il y a donc toujours au moins 1 hard-link entre toto.txt et les données qu'il représente. Ainsi, faire un hard-link sur toto.txt, consiste en réalité à en créer un deuxième...

Cette caractéristique octroie une propriété très utile au hard-link : le bloc de données d'un fichier continue à exister (n'est pas détruit) tant qu'au moins un hard-link pointe dessus. Ainsi si j'ai, pour reprendre l'exemple précédent, mon bloc de donnée pointé par /mes_donnees/toto.txt et /autre_chemin/tutu.txt. Si je détruit toto.txt, le bloc de donnée n'est pas détruit et pour le système, c'est comme s'il s'était toujours appelé tutu.txt et qu'il a toujours été dans le dossier /autre_chemin. En revanche, si je détruit tutu.txt, le bloc de donnée est perdu, le fichier est physiquement détruit...

En conséquence, contrairement aux liens symboliques, les hard-links ne peuvent pas être réalisés entre deux fichiers systèmes de fichiers différents, et à fortiori, entre deux volumes d'origine différente (usb, cd-rom, etc.). Autre conséquence, nous ne pouvons créer un hard-link qu'en deux systèmes de fichiers.

La commande pour créer un hard-link est ln, la même donc que pour un lien symbolique mais sans l'argument -s. Le code suivant reprends le brillant exemple de toto et tutu sur mon disque :

gaston# création du fichier toto
gastonmkdir ~/mes_données
gastonecho "Ces données sont celle de toto.. à l'origine ;-)" > ~/mes_données/toto.txt

gaston# Regardez la deuxième colonne du résultat, le 1 signifie que l'on a 1 hard-link sur
gaston# le bloc de données toto.txt
gastonls -l ~/mes_données

gaston# Création du hard-link
gastonmkdir ~/autre_chemin
gastonln ~/mes_données/toto.txt ~/autre_chemin/tutu.txt

gaston# En exécutant cette commande, la deuxième colonne du résultat du ls donne 2, on a donc
gaston# bien maintenant 2 hard-links sur le bloc de données (toto.txt et tutu.txt) !!
gastonls -l ~/mes_données

gaston# En exécutant cette commande, la deuxième colonne du résultat du ls donne 2, on a donc
gaston# bien maintenant 2 hard-links sur le bloc de données (toto.txt et tutu.txt) !!
gastonls -l ~/mes_données

gaston# Destruction du premier fichier, et donc du premier hard-link
gastonrm -f ~/mes_donnes/toto.txt

gaston# On vérifie que l'on a bien le contenu de toto.txt "transféré" sur tutu.txt
gastoncat ~/autre_chemin/tutu.txt

Une application du hard-link est donc l'historisation. Imaginons que l'on veuille faire la copie de sauvegarde d'un grand de fichiers. Et imaginons aussi que l'on veuille historiser cette sauvegarde sur 10 jours, c'est à dire disposer en permanence 10 dossiers contenant la même copie de ces fichiers, mais représentant chacune l'état du dossier à sauvegarder pour chacun des 10 derniers jours passés. Alors Comment faire ?

Tout d'abord, nous allons créer 10 dossiers nommés jour.J,jour.J-1,jour.J-2, ..., jour.J-9, représentant le backup du jour pour jour.J à celui d'il y a 9 jours avec jour.J-9

gastonmkdir ~/historisation
gastoncd ~/historisation
gastonmkdir jour.J
gastonmkdir jour.J-1
...
gastonmkdir jour.J-9

Ensuite, il va falloir copier les fichiers à sauvegarder dans le dossier jour.J :

rsync -a --delete /chemin/vers/mes_donnees_importantes jour.J

Le -a voulant dire tout-récursivement et le --delete pour supprimer les fichiers cibles qui ne sont plus dans source). C'est tout pour aujourd'hui, la suite s'effectue le lendemain matin.

Le lendemain donc, nous allons faire descendre tous nos dossiers d'un cran. Tout d'abord nous allons commencer par détruire le dossier jour-J-9 (notre historisation ne portant que sur 10 jours). Puis, nous allons renommer le dossier jour.J en jour.J-1, jour.J-1 en jour.J-2, etc jusqu'à jour-J-8 en jour-J-9. Ensuite nous allons recréer un nouveau dossier jour.J.

gaston# On détruit le 9° jour
gastonrm -rf jour-J-9

gaston# renommage des dossiers précédent
gastonfor jour in $(seq 8 -1 1) ; do
gastonlet jour_avant=${jour}+1;
gastonecho mv jour-J-$jour jour-J-$jour_avant
gastondone

gaston# création de l'espace pour la nouvelle sauvegarde
gastonmkdir jour-J

Ensuite, et c'est là que la magie commence, nous allons, pour chaque fichier du dossier jour.J-1, faire un hard-link dans le dossier jour.J. Pour cela nous utilisons la simple commande cp

gastoncp -lr jour.J-1/* jour.J/

Le paramètre -r indique à cp que la copie recursive. -l indique quant à lui que cp ne doit pas copier mais fait des hard-link à la place.

Ceci fait, il suffit de refaire l'opération du jour précédente, à savoir notre rsync

gastonrsync -a --delete /chemin/vers/mes_donnees_importantes jour.J

Alors que se passe t-il exactement lors du rsync ? Au moment de faire cette 2nd sauvegarde, nous avons en fait 4 cas de figure :

  1. Les fichiers qui n'ont pas changé entre J et J-1 : Je vais donc avoir au moins 2 hard-link sur le même bloc de donnée physique.
  2. Des fichiers modifiés par rapport à hier : Je vais donc avoir un hard-link sur le nouveau fichier dans le dossier Jour.J et un autre sur l'ancien dans Jour.J-1
  3. Des fichiers détruits (option --delete de rsync) : Le bloc de donnée existe encore grâce à leur hard-link conservé dans jour.J-1 mais n'existe plus dans Jour.J.
  4. Des nouveaux fichiers : ils ne sont en hard-link QUE dans Jour.J.

Ainsi nous avons déjà l'historique sur 2 jours sans prendre plus d'espace disque que la différence de volume entre ces deux jours.

Le 3ième jour nous recommençons à l'étape du renommage des jour.J-1 à jour.J-9, à nouveau le rsync, et ainsi de suite... jusqu'au 9ieme jour.

Arrivé au 9ieme jour, tous nos dossiers sont plein, nous avons une historisation sur 10 jours et lors de la quotidienne séance de renommage, le dossier jour-9 est supprimé. Du coup tous les fichiers qu'il contient vont voir leur compteur de hard-link décrémenté de 1. Pour ceux qui atteignent 0, c'est la fin, ils disparaissent, mais pour tous les autres, ils continuent de vivre, soit dans le dossier d'origine, soit dans une des 9 sauvegarde précédente.

Le binding

Comme nous l'avons vu, le hard-link souffre de deux contraintes. On ne peut hard-linker QUE des fichiers et toujours de la même partition. C'est à cela que sert le binding. Cette technique sort un peu du cadre du lien (elle n'est pas gérée par la commande ln mais permet de lier n'importe quel objet sur n'importe quel système de fichier. Cette fonction est assurée par la commande mount :

rootmount --bind dossier_source dossier_cible

L'avantage par rapport aux liens symboliques (qui fonctionellement répondent au même besoin), c'est qu'il trompe totalement les applications. Autant une application peut vérifier que le dossier est un lien symbolique et préférer aller sur le vrai dossier (ce que l'on ne veut pas), autant avec un binding elle n'y voit que du feu. Une application pratique de cette technique est de créer rapidement un sous-linux en vue d'un chrootage par exemple. En effet, si l'on cré un dossier mon_linux dans lequel on bind chaque dossier du linux hôte (/bin, /dev, /lib, /usr, etc...) et que l'on tapes chroot mon_linux, on est dans un sous linux complétement autonome. Le binding, lien de bas niveau par excellence, est en général à utiliser lorsque le lien symbolique est "trop léger" pour être utilisé et le hard-link par trop "définitif".

Enfin, comme tous les montages, le bind peut être effectué de manière automatique en l'inscrivant dans le fichier /etc/fstab /mon/dossier/source /mon/dossier/cible none bind

Conclusion

Les liens sont sans aucun doute très important sous tous les systèmes nix, ils permettent rapidement d'adapter, assouplir et étendre en fonctionnalité le concept de base de ces systèmes d'exploitation, à savoir Tout n'est que fichier sous nix, sauf lorsque ce n'est pas le cas ;-)