Partie 2 - Fichiers =================== .. raw:: html Support présentation Introduction ------------ Dans les systèmes Unix/Linux, un **fichier** est une simple **séquence d'octets** stockée sur un périphérique. Le système ne connaît pas sa signification (texte, image, vidéo, binaire, base de données…). C'est le programme qui interprète les octets. Il existe cependant des standards de format (``.png``, ``.mp3``, ``.wav``…) permettant à différents programmes de lire un fichier de la même manière, mais rien n'est imposé par le système. La commande ``file`` détermine le type de contenu. Le principe fondamental d'Unix : **"Tout est fichier"**. Cela inclut : - fichiers classiques (texte, binaire, vidéo, ...), - répertoires (``/home``, ``/tmp``, ...), - périphériques (clavier, disque, carte son…), - tubes (pipes), - sockets, - liens symboliques, - etc. Tous ces objets peuvent être manipulés avec les mêmes primitives systèmes (``open``, ``read``, ``write``, ``close``, ...). Sous Linux, les fichiers sont organisés sous forme de structure arborescente, commençant à la **racine** ``/``. La commande ``tree`` permet d'afficher l'arborescence d'un dossier : .. code-block:: console $ tree -L 1 / / ├── bin -> usr/bin ├── boot ├── dev ├── etc ├── home ├── lib -> usr/lib ├── lib32 -> usr/lib32 ├── lib64 -> usr/lib64 ├── libx32 -> usr/libx32 ├── lost+found ├── media ├── mnt ├── opt ├── proc ├── root ├── run ├── sbin -> usr/sbin ├── snap ├── srv ├── sys ├── tmp ├── usr └── var 23 directories, 0 files .. figure:: https://www.linuxfoundation.org/hubfs/Imported_Blog_Media/standard-unix-filesystem-hierarchy-1500x826.png :alt: Résumé des différents dossiers sous la racine de Linux. Plus d'informations sur le site source. :align: center `Source de l'image `__. Pour parcourir les dossiers, on distingue deux types de chemins : - **absolu** : commence à la racine ``/`` : - ``/home/user/file.txt`` - ``/bin/bash`` - **relatif** : dépend du répertoire courant : - ``./file.txt`` (où ``.`` représente le répertoire courant) - ``../autre.txt`` (où ``..`` représente le répertoire parent) Système de fichiers ------------------- Le système de fichiers (ext4, NTFS, FAT32…) est le logiciel responsable de l'organisation des fichiers sur le disque. Il gère : - les inodes, répertoires et blocs disque, - les permissions et métadonnées, - l'accès efficace et sécurisé aux fichiers, - la cohérence des données (journalisation, verrouillage, ...). Il fait l'interface entre le matériel (disque physique) et le noyau. Nous obtenons la chaîne suivante : Disque physique <- système de fichiers <- inodes & répertoires <- noyau Linux <- descripteurs de fichiers <- votre programme Lorsque votre programme veut lire un fichier, il appelle ``open()``, le noyau associe le fichier à un descripteur de fichier (un ``int``) puis le programme peut utiliser ce descripteur avec ``read()``, ``write()``, ``close()``, ... Il existe de nombreux systèmes de fichiers, chacun ayant ses spécificités en termes de performances, fiabilité, compatibilité ou fonctionnalités. Quelques exemples courants : - **ext4 (Linux)** - Le plus répandu sous Linux. - Rapide, fiable, avec journalisation. - Gère de très gros fichiers et volumes. - Limite de taille : fichier jusqu'à 16 To, partition jusqu'à 1 Eo. - **NTFS (Windows)** - Par défaut sur Windows modernes. - Supporte les droits d'accès avancés, la compression, le chiffrement. - Très bonne compatibilité Windows, mais écriture limitée sous Linux/macOS (mieux gérée avec des pilotes tiers). - **FAT32 (Windows, ancien, universel)** - Très compatible (clé USB, cartes SD, BIOS). - Mais limité : fichiers max 4 Go, partitions max 2 To. - Pas de journalisation → moins fiable en cas de coupure brutale. - **exFAT (Microsoft, optimisé pour flash)** - Successeur de FAT32, sans limite de 4 Go par fichier. - Très utilisé pour les clés USB, cartes SDXC, disques externes. - Supporté par Windows, macOS et Linux (via pilotes). - **XFS (Linux)** - Système journalisé hautes performances. - Optimisé pour les très gros fichiers et les serveurs de stockage. - Pas de réduction de taille de partition possible. - **Btrfs (Linux)** - Système moderne, orienté fonctionnalités avancées (snapshots, checksums, RAID intégré). - Encore considéré moins stable qu'ext4 dans certains contextes. - Bon candidat pour serveurs et systèmes exigeants. - **APFS (macOS)** - Système de fichiers par défaut sur macOS récent. - Optimisé SSD, snapshots intégrés, chiffrement natif. - Non compatible nativement avec Linux ou Windows. Inodes ------ Un fichier est décrit par une structure interne : l'**inode** (*index node*) qui contient ses métadonnées : - type (fichier, répertoire, lien…), - taille (en octets), - droits d'accès (lecture/écriture/exécution), - propriétaire (UID) et groupe (GID), - dates (création, dernier accès, modification), - nombre de liens, - pointeurs vers les blocs disque. La commande ``stat`` affiche les métadonnées : .. code-block:: console $ echo "coucou" > mon_fichier.txt $ stat mon_fichier.txt Fichier : mon_fichier.txt Taille : 7 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17309649 Liens : 1 Accès : (0644/-rw-r--r--) UID : (1000/user) GID : (1000/user) Accès : 2025-08-19 13:43:30.226795166 +0200 Modif. : 2025-08-19 13:43:30.227795178 +0200 Changt : 2025-08-19 13:43:30.227795178 +0200 Créé : 2025-08-19 13:43:30.226795166 +0200 $ file mon_fichier.txt mon_fichier.txt: ASCII text Taille logique vs physique ~~~~~~~~~~~~~~~~~~~~~~~~~~ Chaque fichier occupe au minimum un **bloc disque** (souvent 4 Ko). La **taille logique** (octets de données) peut être bien plus petite que la place réellement allouée. Dans l'exemple ci-dessus avec ``stat``, le fichier ne contient que 7 octets, mais occupe un bloc entier (8 blocs de 512 octets, soit 4 Ko minimum). Types de fichiers ~~~~~~~~~~~~~~~~~ On peut les voir avec ``ls -l``, le premier caractère indique le type : - ``-`` : fichier régulier - ``d`` : répertoire (directory) - ``l`` : lien symbolique (symlink) - ``c`` : périphérique caractère (terminal, clavier…) - ``b`` : périphérique bloc (disque dur…) - ``s`` : socket - ``p`` : pipe nommé (FIFO) Exemple : .. code-block:: console $ ls -l /dev/null /bin/ls / -rwxr-xr-x 1 root root 138216 févr. 8 2024 /bin/ls crw-rw-rw- 1 root root 1, 3 août 18 09:33 /dev/null ... drwxr-xr-x 169 root root 12288 août 18 10:08 etc ... Droits des fichiers ~~~~~~~~~~~~~~~~~~~ ``ls -l`` affiche aussi, après le type, les droits/permissions sur les fichiers avec les ``r`` (*read*, droit de lecture), ``w`` (*write*, droit de modification), ``x`` (*execute*, droit d'execution) pour le propriétaire, puis le groupe puis les autres. ainsi : - ``rwxr-xr-x`` indique que le propriétaire a les droits de lecture, écriture et execution et le groupe et les autres ont les droits de lecture et d'execution. La commande ``chmod`` permet de changer ces droits. La commande ``chown`` permet de changer le propriétaire et groupe. Plus de détail dans le cours de bash. Descripteurs de fichiers ~~~~~~~~~~~~~~~~~~~~~~~~ Chaque processus possède une **table des fichiers ouverts**. Le noyau associe un entier (le **descripteur**) à une structure interne décrivant le fichier. Trois descripteurs standards sont toujours ouverts au lancement : - ``0`` : entrée standard (**stdin**), - ``1`` : sortie standard (**stdout**), - ``2`` : sortie d'erreur (**stderr**). Ce mécanisme permet les **redirections** : .. code-block:: console $ ls > out.txt # redirige stdout vers un fichier $ ls 2> err.txt # redirige stderr $ sort < data.txt # lit sur stdin depuis un fichier $ ls | grep ".c" # pipe : stdout de ls vers stdin de grep Liens symboliques et physiques ------------------------------ Deux mécanismes Unix permettent de donner plusieurs noms à un fichier : - **Lien physique (hard link)** : un second nom pointant vers le même inode. Le fichier reste valide tant qu'au moins un lien existe. - **Lien symbolique (soft link / symlink)** : un raccourci vers un autre fichier. S'il est supprimé, le lien devient cassé. Commandes : .. code-block:: bash ln fichier_cible lien # lien physique ln -s fichier_cible lien # lien symbolique Accès aux fichiers en C ----------------------- En C sous Unix/Linux, il existe deux façons principales d'accéder aux fichiers : 1. **Avec la bibliothèque standard du C (libc, ````)** : fonctions de haut niveau, bufferisées en mémoire utilisateur, pratiques mais moins prévisibles. 2. **Avec les appels système POSIX (````, ````)** : fonctions de bas niveau, non bufferisées, proches du noyau, utilisées en interne par la libc. .. note:: La bufferisation stocke temporairement les données dans un tampon pour réduire les appels système et améliorer les performances. Un équivalent serait d'attendre que le réservoir d'essence d'une voiture soit vide (ou presque vide) pour aller le remplir plutôt que d'aller faire le plein à chaque trajet. Pour de nombreuses fonctions ci-dessous, la valeur de retour est ``0`` si tout c'est bien passé et ``-1`` s'il y a une erreur qu'il faut mieux afficher avec ``perror``. Libc (I/O bufferisées) ~~~~~~~~~~~~~~~~~~~~~~ La manipulation via la libc se fait avec la structure ``FILE`` (flux *stream*) qui encapsule un descripteur de fichier noyau, un tampon, les états EOF/erreur, la position logique, etc. .. code-block:: c :linenos: #include #include int main(void) { FILE *f = fopen("data.txt", "w"); if (!f) { perror("fopen"); exit(EXIT_FAILURE); } fprintf(f, "Hello bufferisé !\n"); fclose(f); return 0; } ``fopen()`` et ``fclose()`` permettent d'ouvrir et fermer un flux ``FILE``. .. code-block:: c #include FILE *fopen(const char *restrict pathname, const char *restrict mode); int fclose(FILE *stream); Paramètres - ``pathname`` : chemin du fichier. - ``mode`` : - ``"r"`` : lecture - ``"w"`` : écriture (efface si existant) - ``"a"`` : ajout en fin - ``"r+"`` : lecture + écriture - ``"b"`` : mode binaire (ex : ``"rb"``, ``"wb"``) Retour - ``fopen`` : pointeur ``FILE*`` ou ``NULL`` (``errno``). - ``fclose`` : ``0`` si succès, ``EOF`` sinon. Pour les autres fonctions de la libc, voir les consignes dans les exercices, si vous avez un doute le ``man`` peut vous aider. Appels système (I/O non bufferisées) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Les appels système donnent directement les descripteurs de fichiers et travaillent ensuite avec : .. code-block:: c :linenos: #include #include #include #include #include #include int main(void) { int fd = open("data.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("open"); exit(EXIT_FAILURE); } const char *msg = "Hello bas niveau !\n"; ssize_t n = write(fd, msg, strlen(msg)); if (n == -1) { perror("write"); close(fd); exit(EXIT_FAILURE); } close(fd); return 0; } ``open()`` Ouvre un fichier et renvoie un descripteur de fichier, ``close()`` ferme le descripteur de fichier. .. code-block:: c #include #include int open(const char *pathname, int flags, ... /* mode_t mode */); Paramètres - ``pathname`` : chemin du fichier. - ``flags`` : voir ci-dessous - ``mode`` : requis si ``O_CREAT`` (droits initiaux, affectés par ``umask``). Flags courants : - ``O_RDONLY`` (lecture seule), ``O_WRONLY`` (écriture seule), ``O_RDWR`` (lecture+écriture) - ``O_CREAT`` : créer si inexistant (**nécessite** ``mode``) - ``O_TRUNC`` : tronquer à 0 si existant (avec écriture) - ``O_APPEND`` : ajout en fin - ``O_EXCL`` : échec si le fichier existe déjà (à combiner avec ``O_CREAT``) - ``O_CLOEXEC`` : ferme automatiquement le fd lors d'un ``execve`` (sécurité) - ``O_NONBLOCK`` : E/S non bloquantes (si pertinent) - ``O_DIRECTORY`` : échouer si ``pathname`` n'est pas un répertoire Pour les autres fonctions POSIX, voir les consignes dans les exercices, si vous avez un doute le ``man`` peut vous aider. Permissions et masque (umask) ----------------------------- Chaque fichier a des **permissions Unix** : lecture (``r``), écriture (``w``), exécution (``x``), définies pour **utilisateur**, **groupe**, **autres**. Exemple : ``-rw-r--r--`` = propriétaire lecture/écriture, autres lecture seule. La fonction ``umask`` définit un **masque par défaut** qui supprime certains droits à la création. .. code-block:: c #include umask(0022); // interdit l'écriture pour groupe/autres int fd = open("fichier.txt", O_CREAT | O_WRONLY, 0666); // droits effectifs : 0666 & ~0022 = 0644 Verrouillage de fichiers ------------------------ Quand plusieurs processus écrivent dans le même fichier, il faut éviter les conflits. Deux mécanismes existent : - ``flock(fd, LOCK_EX)`` : verrouillage simple (exclusif ou partagé) - ``fcntl(fd, F_SETLK)`` : verrouillage plus fin (plages de bytes) Exemple simple avec ``flock`` : .. code-block:: c #include int fd = open("data.txt", O_WRONLY); flock(fd, LOCK_EX); // verrou exclusif write(fd, "Hello\n", 6); flock(fd, LOCK_UN); // libère close(fd); Fichiers spéciaux ----------------- Sous Unix, certains fichiers n'ont pas de données réelles mais sont des interfaces système : - ``/dev/null`` : tout ce qui est écrit est jeté, lecture = EOF, - ``/dev/urandom`` : générateur pseudo-aléatoire, - ``/proc`` : expose des infos sur les processus et le noyau, - ``/sys`` : interface avec les périphériques. Exemple : .. code-block:: console $ cat /dev/null $ head -c 16 /dev/urandom | hexdump -C $ cat /proc/cpuinfo Exemple long de l'utilisation de stat ------------------------------------- Ci-dessous, on crée un fichier vide avec ``touch``, puis on affiche les métadonnées avec ``stat``. Ensuite on ajoute des données dans le fichier puis constate que la taille et la date de modification change, on modifie ensuite les permissions avec ``chmod``. Enfin, on crée un lien symbolique avec ``ln -s`` de ``salut`` vers ``coucou``. C'est un raccourci, il pointe donc vers un autre fichier par son nom. Puis un lien physique de ``bonjour`` vers ``coucou``. Quand ``coucou`` est supprimé, ``salut`` devient un lien mort. Le lien physique ajoute un nouveau nom à un même inode qui reste fonctionnel tant qu'un nom le référence. .. code-block:: console $ touch coucou $ stat coucou Fichier : coucou Taille : 0 Blocs : 0 Blocs d'E/S : 4096 fichier vide Périphérique : fc01h/64513d Inœud : 17386826 Liens : 1 Accès : (0644/-rw-r--r--) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:10.187566033 +0200 Changt : 2025-07-21 17:36:10.187566033 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ echo "salut" >> coucou $ stat coucou Fichier : coucou Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 1 Accès : (0644/-rw-r--r--) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:36:26.285805213 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ chmod +x coucou $ stat coucou Fichier : coucou Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 1 Accès : (0755/-rwxr-xr-x) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:36:40.146011146 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ ln -s coucou salut $ stat coucou Fichier : coucou Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 1 Accès : (0755/-rwxr-xr-x) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:36:40.146011146 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ stat salut Fichier : salut -> coucou Taille : 6 Blocs : 0 Blocs d'E/S : 4096 lien symbolique Périphérique : fc01h/64513d Inœud : 17387173 Liens : 1 Accès : (0777/lrwxrwxrwx) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:52.110188907 +0200 Modif. : 2025-07-21 17:36:52.109188892 +0200 Changt : 2025-07-21 17:36:52.109188892 +0200 Créé : 2025-07-21 17:36:52.109188892 +0200 $ ln coucou bonjour $ stat coucou Fichier : coucou Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 2 Accès : (0755/-rwxr-xr-x) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:37:11.421475831 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ stat salut Fichier : salut -> coucou Taille : 6 Blocs : 0 Blocs d'E/S : 4096 lien symbolique Périphérique : fc01h/64513d Inœud : 17387173 Liens : 1 Accès : (0777/lrwxrwxrwx) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:52.110188907 +0200 Modif. : 2025-07-21 17:36:52.109188892 +0200 Changt : 2025-07-21 17:36:52.109188892 +0200 Créé : 2025-07-21 17:36:52.109188892 +0200 $ stat bonjour Fichier : bonjour Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 2 Accès : (0755/-rwxr-xr-x) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:37:11.421475831 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ rm coucou $ stat coucou stat: impossible d'exécuter statx 'coucou': Aucun fichier ou dossier de ce nom $ stat bonjour Fichier : bonjour Taille : 6 Blocs : 8 Blocs d'E/S : 4096 fichier Périphérique : fc01h/64513d Inœud : 17386826 Liens : 1 Accès : (0755/-rwxr-xr-x) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:10.187566033 +0200 Modif. : 2025-07-21 17:36:26.285805213 +0200 Changt : 2025-07-21 17:37:29.461743875 +0200 Créé : 2025-07-21 17:36:10.186566018 +0200 $ stat salut Fichier : salut -> coucou Taille : 6 Blocs : 0 Blocs d'E/S : 4096 lien symbolique Périphérique : fc01h/64513d Inœud : 17387173 Liens : 1 Accès : (0777/lrwxrwxrwx) UID : (675349/cgrelier) GID : (200367/optimist) Accès : 2025-07-21 17:36:52.110188907 +0200 Modif. : 2025-07-21 17:36:52.109188892 +0200 Changt : 2025-07-21 17:36:52.109188892 +0200 Créé : 2025-07-21 17:36:52.109188892 +0200 $ cat salut cat: salut: Aucun fichier ou dossier de ce nom