Partie 2 - Fichiers

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 :

$ 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
Résumé des différents dossiers sous la racine de Linux. Plus d'informations sur le site source.

Source de l’image.

Pour parcourir les dossiers, on distingue deux types de chemins :

  • absolucommence à la racine / :
    • /home/user/file.txt

    • /bin/bash

  • relatifdé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 :

$ 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 :

$ 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 :

$ 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 :

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, ``<stdio.h>``) : fonctions de haut niveau, bufferisées en mémoire utilisateur, pratiques mais moins prévisibles.

  2. Avec les appels système POSIX (``<unistd.h>``, ``<fcntl.h>``) : 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.

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3
 4 int main(void) {
 5     FILE *f = fopen("data.txt", "w");
 6     if (!f) {
 7         perror("fopen");
 8         exit(EXIT_FAILURE);
 9     }
10     fprintf(f, "Hello bufferisé !\n");
11     fclose(f);
12     return 0;
13 }

fopen() et fclose() permettent d’ouvrir et fermer un flux FILE.

#include <stdio.h>
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 :

 1#include <fcntl.h>
 2#include <unistd.h>
 3#include <stdio.h>
 4#include <stdlib.h>
 5#include <string.h>
 6#include <errno.h>
 7
 8int main(void) {
 9    int fd = open("data.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
10    if (fd == -1) {
11        perror("open");
12        exit(EXIT_FAILURE);
13    }
14
15    const char *msg = "Hello bas niveau !\n";
16    ssize_t n = write(fd, msg, strlen(msg));
17    if (n == -1) {
18        perror("write");
19        close(fd);
20        exit(EXIT_FAILURE);
21    }
22
23    close(fd);
24    return 0;
25}

open() Ouvre un fichier et renvoie un descripteur de fichier, close() ferme le descripteur de fichier.

#include <fcntl.h>
#include <sys/stat.h>
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.

#include <sys/stat.h>
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 :

#include <sys/file.h>

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 :

$ 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.

$ 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