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