Aller au contenu principal

Unix & Shell · L1 · Section 2/7

Système de fichiers & permissions

Progression

Points d’expérience : XPSérie de jours consécutifs : · —Progression du module : — / —compris

#Système de fichiers et permissions

Sous Unix, tout est fichier: un dossier, un périphérique, un socket. L'arborescence part de la racine /. Ce chapitre vous apprend à la lire, à décoder ses droits et à les modifier sans casser l'existant.

#Prérequis et objectifs

Prérequis: navigation de base (cd, ls). Objectifs: décoder une ligne de ls -l en quelques secondes, calculer les permissions résultant d'un umask, choisir entre lien matériel et lien symbolique.

#L'arborescence et ses conventions

Les sous-répertoires ont des rôles usuels: /etc pour la configuration, /var pour les données variables (journaux, files d'attente), /home pour les dossiers utilisateurs, /tmp pour les fichiers temporaires, /usr pour les programmes installés par le système.

Un même contenu peut avoir plusieurs noms. Les liens symboliques (symlinks) se comportent comme des raccourcis: ils pointent vers un chemin, qui peut disparaître. Les liens matériels (hard links) ajoutent un second nom pointant vers le même inode, l'identité du fichier sur disque.

bashbash

1ln original copie-dure      # lien matériel: deux noms, un inode2ln -s original raccourci    # lien symbolique: un chemin cible3ls -li original copie-dure raccourci4# Les deux premières colonnes montrent le même numéro d'inode pour original5# et copie-dure, un numéro différent pour raccourci (type l).

#Trois façons d'écrire un chemin

Le cours distingue trois natures de chemin, et l'épreuve demande régulièrement de les reconnaître.

  • Le chemin absolu parcourt l'arborescence depuis la racine: /home/toto/TP05/Exercice1.py. Il fonctionne depuis n'importe quel répertoire courant.
  • Le chemin relatif relie le répertoire courant à la cible: ../TP05/Exercice1.py.
  • Le chemin local part du répertoire personnel de l'utilisateur ou du répertoire courant: ~/TP05/Exercice1.py, ou simplement TP05/Exercice1.py.

Trois notations en découlent, à connaître par cœur.

  • ~ désigne le répertoire personnel et peut être remplacé par la variable $HOME: cd ~/TP05 et cd $HOME/TP05 sont équivalents.
  • . désigne le répertoire courant. Il est souvent implicite, mais devient nécessaire pour exécuter un script: ./configuration.sh. Le cours le classe à la fois comme chemin relatif et comme chemin local, puisque le point de départ ne change pas.
  • .. désigne le répertoire parent, et les remontées se cumulent: si le répertoire courant est /home/toto/TP05, alors ../../ désigne /home.
bashbash

1cd /var/log            # absolu2cd ../etc              # relatif3cd ~/Syst1/TP02        # local4cd -                   # revient au répertoire précédent (OLDPWD)5cd                     # sans argument, va dans le répertoire personnel6pwd                    # affiche le nom ABSOLU du répertoire courant

#Nommer un fichier : les règles

Tous les systèmes de fichiers Unix imposent les mêmes contraintes de nommage, que le cours énumère.

  • Le nom ne doit pas dépasser 255 caractères.
  • Le nom est sensible à la casse: premiertexte.txt et PREMIERTEXTE.txt sont deux fichiers différents. C'est la cause d'erreur la plus fréquente aux épreuves.
  • Seuls les caractères alphanumériques et quelques caractères spéciaux (_, ., @, -, +) sont autorisés sans précaution. Tout autre caractère spécial doit être protégé par des apostrophes ou un antislash.
bashbash

1touch "[Fa#]"      # les crochets et le croisillon sont protégés par le quoting2touch -- -point!   # le double tiret termine la liste des options3ls                 # observation: les deux fichiers existent, avec leur nom exact

#Jokers et expressions rationnelles

Ces deux langages se ressemblent et ne servent pas à la même chose. Le tableau de correspondance est la clé de bien des questions d'examen.

Les jokers sont interprétés par le shell et portent sur des noms de fichiers:

  • * — une chaîne quelconque, éventuellement vide;
  • ? — exactement un caractère quelconque;
  • [aeiouy] — un caractère pris dans l'ensemble; [a-z] une plage; [^abcd] le complément.

Deux limites: le caractère / ne peut pas être décrit par un joker, et le caractère . en début de nom non plus — ce qui explique qu'un joker ne voie jamais les fichiers cachés. Pour les atteindre, il faut écrire le point: .*.

bashbash

1ls *[0-9]*             # le SHELL développe le motif avant d'appeler ls2ls p*                  # tout ce qui commence par p3ls po????              # po suivi de exactement 4 caractères4ls *[aeiouy]*          # contient au moins une voyelle5ls [^abcd]*            # ne commence pas par a, b, c ou d6ls -a                  # les fichiers cachés, que les jokers ignorent

Les expressions rationnelles sont interprétées par la commande (grep, sed, tr, Emacs) et portent sur des contenus. Les symboles changent de sens:

  • * — répétition de l'élément précédent, zéro fois ou plus (et non « n'importe quelle chaîne »);
  • + — répétition une fois ou plus; ? — zéro ou une fois; . — un caractère quelconque;
  • ^ en tête de motif — début de ligne; $ en fin de motif — fin de ligne;
  • ( ) — regroupement; | — alternative; {n}, {n,}, {n,m} — répétitions comptées.
bashbash

1grep -E "^ab.*!$" alphabet.txt    # ab en début de ligne, ! en fin2grep -E "a(b|r)*c" alphabet.txt   # b ou r, répété3grep -E "^[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}$" dates.txt   # format JJ-MM-AAAA

#Lire une ligne de ls -l

Dans -rwxr-x--- 1 alice dev 12K 3 sept 10:00 script.sh, le premier caractère décrit le type (- fichier ordinaire, d dossier, l lien symbolique). Viennent ensuite trois triplets rwx: propriétaire, groupe, autres. Ici, alice peut tout faire, les membres du groupe dev peuvent lire et traverser mais pas écrire, les autres n'ont aucun droit.

Sur un dossier, x signifie « traverser »: sans lui, on ne peut ni entrer ni atteindre un sous-chemin, même lisible.

#La sémantique des droits dépend du type d'élément

C'est la distinction que le cours traite le plus longuement, et elle explique tout le reste.

Sur un fichier ordinaire: r permet d'afficher le contenu; w permet d'écrire dans le fichier; x permet d'exécuter un script ou un programme binaire.

Sur un répertoire: r permet de consulter la liste des fichiers sur lesquels il pointe; w permet d'ajouter ou d'enlever des références de fichiers au répertoire — donc de créer ou de supprimer des entrées, pas de modifier un contenu; x permet d'accéder aux fichiers répertoriés, c'est-à-dire de traverser le répertoire pour atteindre un inode. Le cours résume: il faut à la fois r et x pour pouvoir explorer un répertoire.

Cette différence a une explication précise, et c'est l'inode qui la fournit.

#L'inode : l'identité du fichier

Un système de fichiers Linux utilise une structure de données appelée inode (index node). Chaque fichier et chaque répertoire se voit attribuer un numéro d'inode, qui localise sur le disque:

  • le bloc de disque où la donnée est stockée;
  • le type du fichier;
  • la taille;
  • l'identifiant du propriétaire;
  • les autorisations de lecture, d'écriture et d'exécution;
  • les dates de dernière modification et de dernier accès.

Et une chose qu'il ne contient pas: le nom du fichier. Le nom est stocké dans l'inode du répertoire, sous forme d'une association entre un nom et un numéro d'inode. Un répertoire est donc lui-même un fichier dont le contenu est une table de correspondances — ce qui explique pourquoi le droit x sur un répertoire signifie « accéder aux inodes référencés ».

Deux conséquences pratiques découlent directement de là.

Créer un fichier, c'est d'abord placer un lien dur dans le répertoire correspondant. Plusieurs liens durs peuvent donc pointer vers le même fichier, et l'on peut dire qu'« un fichier n'est pas dans un répertoire »: seul son nom y est.

La commande rm ne supprime pas un fichier, elle supprime le lien dur mentionné en argument. Le fichier n'est réellement détruit que lorsque plus aucun lien dur ne le désigne. Un lien symbolique, lui, est un fichier spécial dont le contenu est un chemin: supprimer le lien n'a aucune influence sur le fichier désigné.

bashbash

1echo donnees > a.txt2ln a.txt b.txt                            # lien dur: deux noms, un inode3stat -c '%n inode=%i liens=%h' a.txt b.txt   # même inode, liens=24rm a.txt                                  # supprime le NOM a.txt5cat b.txt                                 # observation: le contenu est intact6stat -c '%h' b.txt                        # observation: liens=1

#Modifier les droits et la propriété

Changer les permissions revient à allumer ou éteindre des bits. En notation octale, chaque triplet se calcule avec r=4, w=2, x=1:

bashbash

1chmod 750 script.sh          # rwx pour u, r-x pour g, rien pour o2chmod u+x deploy.sh          # notation symbolique: ajoute l'exécution au propriétaire3chmod g-w rapport.md         # retire l'écriture au groupe

Vérification: ls -l script.sh doit afficher -rwxr-x---. La propriété se gère avec chown et chgrp, qui nécessitent des droits administrateur:

bashbash

1sudo chown alice:dev script.sh

umask fixe les permissions retirées à la création: le noyau propose 666 pour un fichier et 777 pour un dossier, puis soustrait le masque.

bashbash

1umask                            # observation courante: 00222umask 0273touch nouveau.txt && ls -l nouveau.txt   # -rw-r-----   (666 - 027)4mkdir nouveau-d && ls -ld nouveau-d      # drwxr-x---   (777 - 027)

#Le umask dans les deux sens

L'épreuve demande aussi bien de calculer les droits à partir d'un masque que de retrouver le masque à partir des droits observés. Les deux sens reposent sur la même égalité: droits = base privée du masque, où la base vaut 777 pour un répertoire et 666 pour un fichier ordinaire. Le masque s'applique bit à bit, chiffre octal par chiffre octal, et non par une soustraction arithmétique.

Dans le sens direct, il suffit de poser le masque puis de créer:

bashbash

1umask 0722mkdir TP04_1          # observation: drwx---r-x  (777 privé de 072 = 705)3touch vide            # observation: -rw----r--  (666 privé de 072 = 604)

Les deux résultats diffèrent alors que le masque est le même: c'est la base qui change. Pour un fichier, le droit d'exécution n'est jamais accordé à la création — « pour des raisons de sécurité, Linux n'autorise pas le droit d'exécution lors de la création d'un fichier », précise le cours.

Dans le sens inverse, on résout chiffre par chiffre. Devant drw-r----x (soit 641), on cherche le masque tel que 777 privé du masque donne 641: le complément vaut 641, donc le masque vaut 136. Vérification: umask 136 puis mkdir produit bien drw-r----x.

Le cas du fichier est plus subtil, parce que les bits d'exécution du masque n'ont aucun effet sur un fichier. Devant -r---w---- (soit 420), on résout 666 privé du masque égal à 420:

  • propriétaire: 6 privé du masque donne 4, donc le bit 2 du masque est posé;
  • groupe: 6 privé du masque donne 2, donc le bit 4 du masque est posé;
  • autres: 6 privé du masque donne 0, donc les bits 4 et 2 du masque sont posés.

Les masques possibles sont donc 246, 247, 256, 257, 346, 347, 356, 357, le troisième chiffre hésitant entre 6 et 7 selon que le bit x du masque est posé ou non. Retenir ce raisonnement vaut mieux que retenir la réponse.

#Notation symbolique : +, -, =

chmod accepte deux notations, et le choix entre elles n'est pas indifférent.

  • + ajoute un droit: chmod g+x transforme un tiret en lettre sans toucher au reste.
  • - retire un droit: chmod o-rw transforme des lettres en tirets.
  • = établit exactement les droits indiqués, quels que soient les droits courants.

C'est la notation = qu'il faut employer dès qu'un énoncé dit « sans connaître les droits actuels du fichier ». Une question classique demande de passer un fichier aux droits -r--rw-rw- puis à -rw-r-xr-- sans connaître son état: seule la notation absolue garantit le résultat.

bashbash

1chmod u=r,g=rw,o=rw fic      # équivalent de chmod 466 fic2chmod u=rw,g=rx,o=r fic      # équivalent de chmod 654 fic3chmod u= fic                 # retire TOUS les droits du propriétaire4chmod go+rx SI/TP02          # équivalent de chmod u=rwx,go=rx SI/TP025chmod u=rwx,go=rx SI/TP02    # la même chose, en une seule indication

Les indications se séparent par des virgules sans blanc, et a signifie ugo. Enfin, chmod n'est possible que si l'on a la permission d'écriture sur le répertoire contenant le fichier: seul le propriétaire et le super-utilisateur peuvent définir les droits.

#Créer avec les droits voulus

mkdir accepte l'option -m, qui prend une notation octale et évite le passage par chmod:

bashbash

1mkdir -m 750 TP04_2          # observation: drwxr-x---2ls -ld TP04_2

Et pour lire ou reposer un masque sous forme symbolique, umask sans argument affiche la valeur courante, umask -S l'affiche en lettres. Rappel décisif: le umask n'a aucune influence sur les fichiers déjà créés.

#Espace disque et tailles

bashbash

1df -h                   # occupation par système de fichiers2du -sh * | sort -h      # poids des éléments du dossier courant

Investiguez récursivement, mais attention aux dossiers volumineux comme .git ou node_modules.

#Rechercher dans l'arborescence : find

find est récursif et permet une recherche beaucoup plus étendue que ls. Sa ligne d'arguments a une forme particulière: un chemin de départ, puis une liste de mots-clés suivis de valeurs pour sélectionner les fichiers, puis une action à effectuer.

bashbash

1find . -name "*s*"                       # tout ce dont le nom contient un s2find . -type f -name "*e*"               # seulement les fichiers ordinaires3find . -type f -name "*e*" -size +3k     # et de taille supérieure à 3 ko4find /etc -type d -name "*zsh*"          # seulement les répertoires5find . -user alice                       # les fichiers appartenant à alice6find /usr/bin -type f -name "*python*" -printf "%T+ %p\n"7find . -name "*.txt" -exec grep -n motif {} \; -print

Les mots-clés utiles: -name (motif en expression générique, pas en expression rationnelle), -type (f fichier, d répertoire, l lien), -user, -group, -size, -printf (formatage précis, avec %p pour le nom et %T pour la date de dernière modification), -exec (exécute une commande sur chaque fichier sélectionné, le couple {} et \; délimitant l'argument). Le chemin de départ est obligatoire, sauf s'il s'agit du répertoire courant, que l'on peut noter .. Contrairement aux jokers du shell, find -name voit les fichiers cachés.

Un usage à connaître, car il fait comprendre le mécanisme de -exec:

bashbash

1find . -name "*.txt" -exec grep -n technologie {} \; -print

La commande imprime, pour chaque fichier .txt, les lignes contenant technologie avec leur numéro de ligne, puis le nom du fichier.

#Exercices complémentaires

1. Déterminez la valeur du umask avant la création des répertoires drwxr-xr-x rep1 et drw-r----x rep2. Déterminez ensuite les valeurs possibles du umask avant la création du fichier -r---w---- fic.

2. Quels droits obtient un répertoire créé avec umask 345 ? Avec umask 136 ?

3. Le type affiché est -rw-rw-r-- répertoire1. S'agit-il d'un fichier ou d'un répertoire ? Quels umask sont possibles ?

4. Créez TP04_1 (répertoire) et vide (fichier) sous umask 072. Pourquoi leurs permissions diffèrent-elles ?

5. Un fichier fic a les droits -rwxr-xr-x. Passez-les à -r--rw-rw-, puis à -rw-r-xr-- sans connaître les droits courants.

Corrections détaillées

1. La base d'un répertoire est 777. drwxr-xr-x vaut 755, donc le masque vaut 022. drw-r----x vaut 641; le complément vaut 641, donc le masque vaut 136.

Pour le fichier -r---w----, la base est 666 et le résultat vaut 420. En résolvant chiffre par chiffre: le bit 2 du masque doit être posé (propriétaire), le bit 4 du masque doit être posé (groupe), les bits 4 et 2 du masque doivent être posés (autres); les bits x sont libres. Les masques possibles sont 246, 247, 256, 257, 346, 347, 356, 357.

2. On calcule 777 privé du masque. Pour 345, le complément vaut 432, donc dr---wx-w-. Pour 136, le complément vaut 641, donc drw-r----x. Ces résultats surprenants montrent qu'un umask est un masque de bits, pas une valeur « interdite ».

3. Le premier caractère est un tiret: c'est un fichier ordinaire, de droits 664, malgré son nom trompeur. La base étant 666, seul le bit « écriture pour les autres » (002) doit être posé dans le masque; les bits de lecture et d'écriture du propriétaire et du groupe doivent être à 0; les bits d'exécution sont libres. Masques possibles: 002, 003, 012, 013, 102, 103, 112, 113, la valeur usuelle étant 002.

4.

bashbash

1umask 0722mkdir TP04_13touch vide4ls -ld TP04_1 vide

TP04_1 vaut 705 (base 777), soit drwx---r-x; vide vaut 604 (base 666), soit -rw----r--. Les permissions diffèrent parce que les bases diffèrent: le noyau n'accorde jamais x à la création d'un fichier ordinaire.

5.

bashbash

1chmod 466 fic      # ou chmod u=r,g=rw,o=rw fic2chmod 654 fic      # ou chmod u=rw,g=rx,o=r fic

L'expression « sans connaître les droits » est le signal qu'il faut utiliser la notation = et non + ou -: seule l'affectation absolue rend le résultat indépendant de l'état initial.

#Bits spéciaux

rwx sur u/g/o
Proprio, groupe, autres
umask
Masque soustractif à la création
setuid/sgid
Exécuter avec uid/gid du fichier
sticky
Sur un dossier: seul le proprio peut supprimer

#Exercice (15 minutes)

  1. Avec umask 027 actif, créez secret.txt puis coffre/. Quels droits obtiennent-ils ?
  2. Créez un lien matériel vers secret.txt, supprimez l'original, puis vérifiez que le contenu reste accessible et observez le compteur de liens avec stat.

Correction:

  1. Fichier: 666 - 027 = 640, soit rw-r-----. Dossier: 777 - 027 = 750, soit rwxr-x---. Vérifiez avec ls -l secret.txt et ls -ld coffre/.
  2. ln secret.txt copie.txt puis stat secret.txt montre Links: 2. Après rm secret.txt, cat copie.txt affiche encore le contenu et stat copie.txt montre Links: 1: le lien matériel partage l'inode, supprimer un nom ne détruit les données que lorsque le compteur tombe à zéro.

#Bonnes pratiques

Séparez code et données; évitez sudo dans les scripts (privilégiez une élévation ciblée au besoin); comprenez ce que vous tapez; documentez un minimum votre structure de dossiers et planifiez des sauvegardes.