CyberSDF

Reprenez la main sur votre numérique

Espace disque occupé par un fichier supprimé : retrouver le processus avec lsof +L1

un poste de travail sobre vu de trois quarts, ecran eteint, clavier mecanique, carnet ouvert

df -h /var annonce 94 % d’occupation, du -sh /var n’explique que 12 Go sur 38, et le gros journal a bien été supprimé avec rm. Sur ext4, rm ne retire que le nom du fichier dans le répertoire. Le noyau ne libère l’inode et les blocs que lorsque plus aucun processus ne détient le fichier ouvert. Tant qu’un descripteur reste ouvert, le fichier n’a plus de nom mais pèse encore sur le disque, et du ne peut plus le voir puisqu’il parcourt les répertoires.

Sommaire
  1. lsof +L1 sélectionne les fichiers dont le nombre de liens est inférieur à 1
  2. Procédure : retrouver et libérer les blocs pris par le fichier supprimé
  3. Deux échecs qui font croire que la méthode ne fonctionne pas
  4. Sans sudo, lsof +L1 ne liste que vos propres processus
  5. Le fichier supprimé peut continuer de grossir, et le lien NFS ne compte pas
  6. Questions concrètes sur les fichiers supprimés encore ouverts
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        38G   36G  1.4G  97% /var

lsof +L1 sélectionne les fichiers dont le nombre de liens est inférieur à 1

La commande qui isole ce cas est lsof +L1. Le +L ajoute une colonne NLINK, le nombre de liens du fichier. La page de manuel lsof(8) l’écrit ainsi : When +L is followed by a number, only files having a link count less than that number will be listed, puis A specification of the form “+L1” will select open files that have been unlinked. Un fichier normal a au moins un lien (son entrée dans le répertoire). Un fichier supprimé mais encore ouvert affiche donc NLINK à 0, et son NAME se termine par (deleted).

La manipulation a été relevée sur Debian 12 (bookworm), dont le paquet lsof est la version 4.95.0-1, et sur Ubuntu 24.04, où le paquet est lsof 4.95.0-1build3. Les deux paquets installent le même binaire lsof, qui lit /proc.

Procédure : retrouver et libérer les blocs pris par le fichier supprimé

Les sept étapes se lancent dans l’ordre. Chaque étape indique ce que l’écran doit afficher avant de passer à la suivante.

  1. Vérifiez que lsof est installé.

    lsof -v 2>&1 | head -3
    

    Ce que vous devez voir : la version du programme, par exemple revision: 4.95.0. Si le shell répond lsof: command not found, installez le paquet.

    sudo apt install lsof
    
  2. Listez les fichiers supprimés encore ouverts par un processus. sudo est nécessaire, un utilisateur ordinaire ne voit que ses propres processus.

    sudo lsof +L1
    

    Ce que vous devez voir : un en-tête, puis une ou plusieurs lignes dont la colonne NLINK vaut 0 et dont le nom finit par (deleted).

    COMMAND    PID     USER   FD   TYPE DEVICE    SIZE/OFF NLINK   NODE NAME
    nginx    42113 www-data    3w   REG  259,2  9175040000     0 524301 /var/log/nginx/access.log (deleted)
    

    La colonne FD donne le descripteur et son mode ; 3w signifie descripteur 3 ouvert en écriture. La colonne SIZE/OFF donne le poids en octets, ici environ 9,1 Go.

  3. Classez les lignes par taille pour désigner le plus gros consommateur. -s force l’affichage de la taille et renomme la colonne en SIZE.

    sudo lsof +L1 -s | sort -k7 -rn | head
    

    Ce que vous devez voir : la même liste, la plus grosse taille en première ligne. Le numéro de colonne 7 est celle de SIZE dans la sortie de lsof sur Linux.

  4. Restreignez la recherche au système de fichiers qui refuse l’écriture, par exemple /var. Le +a combine les critères avec un ET logique.

    sudo lsof +aL1 /var
    

    Ce que vous devez voir : uniquement des fichiers situés sous /var. Si l’écran est vide, le fichier supprimé n’est pas encore ouvert sur ce système de fichiers et l’espace manquant vient d’ailleurs, notamment d’un volume NFS.

  5. Notez l’espace disponible avant toute action.

    df -h /var
    

    Ce que vous devez voir : la colonne Avail, par exemple 1.4G. C’est la valeur qui doit remonter à la fin.

  6. Libérez les blocs. Deux méthodes, selon que le processus peut être interrompu. La première vide le fichier sans arrêter le service, en écrivant dans le descripteur exposé par /proc.

    sudo truncate -s 0 /proc/42113/fd/3
    

    Le nombre après fd/ est le descripteur, chiffres seuls : 3, sans la lettre de mode de la colonne FD. La seconde méthode arrête et relance le service détenteur du fichier.

    sudo systemctl restart nginx
    

    Ce que vous devez voir : aucune sortie pour truncate lorsque la commande réussit, et les lignes d’état du service pour systemctl.

  7. Vérifiez que l’espace est bien revenu.

    df -h /var
    sudo lsof +L1 -s | grep -c '(deleted)'
    

    Ce que vous devez voir : une valeur Avail plus grande que celle notée à l’étape 5, et un compteur à 0. Tant que le compteur reste supérieur à 0, un descripteur est encore ouvert et les blocs ne sont pas rendus.

Deux échecs qui font croire que la méthode ne fonctionne pas

Rediriger la sortie vers /proc échoue quand le processus appartient à root, parce que la redirection est ouverte par le shell courant, pas par sudo. sudo élève la commande echo, mais le shell qui exécute le > reste votre compte.

sudo echo "" > /proc/42113/fd/3
bash: /proc/42113/fd/3: Permission denied

Le remède est de faire porter la redirection par un shell lancé en root, ou d’utiliser truncate, qui ouvre lui-même le fichier avec les privilèges élevés.

sudo sh -c ': > /proc/42113/fd/3'
sudo truncate -s 0 /proc/42113/fd/3

Le second échec concerne un exécutable ou une bibliothèque partagée supprimés pendant que le programme tourne. Le noyau refuse d’ouvrir en écriture l’inode d’un programme en cours d’exécution et renvoie le message suivant.

Text file busy

C’est le code d’erreur ETXTBSY : le fichier porte du code en cours d’exécution, et le noyau bloque toute écriture pour éviter de corrompre l’image du programme. La troncature est alors impossible, seul l’arrêt du processus libère les blocs. Sur Linux, lsof n’affiche pas les descripteurs de texte, mais un fichier mappé supprimé apparaît avec le type DEL dans la colonne TYPE. La même prudence vaut pour un fichier de base de données ou un index ouvert : le vider avec truncate corrompt les données, il faut redémarrer le service.

Sans sudo, lsof +L1 ne liste que vos propres processus

lsof lit les descripteurs de chaque processus dans /proc/<PID>/fd. L’accès à ce répertoire est réservé au propriétaire du processus et à root. Lancé sans sudo, lsof +L1 peut donc n’afficher qu’une liste partielle, voire rien du tout, alors que le service fautif appartient à www-data, à postgres ou à systemd-journald. Le disque reste plein et le diagnostic semble conclure à tort qu’aucun fichier n’est en cause.

Autre confirmation trompeuse : lsof +L1 ne prend en argument aucun fichier à rechercher, donc il se termine avec le code de retour 0 même quand sa sortie est vide. Le test $? ne dit rien sur la présence d’un fichier supprimé ; seule la lecture de la sortie le dit. Pour limiter la durée d’exécution sur une machine chargée, -n et -P suppriment la résolution des noms réseau.

Le fichier supprimé peut continuer de grossir, et le lien NFS ne compte pas

Un processus qui écrit dans un fichier supprimé continue d’y écrire : les blocs libres partent dans le même inode sans nom et df peut régresser encore après le rm. C’est le cas typique d’un journal applicatif. Un service bien élevé rouvre son fichier au signal SIGHUP, ce qui ferme l’ancien descripteur ; la rotation avec l’option copytruncate de logrotate vide le fichier sans le renommer et évite de recréer la situation. La rubrique services et journaux systemd détaille la lecture des journaux qui remplissent /var.

Sur un montage NFS, la colonne NLINK est fournie par le serveur et ne se comporte pas comme sur un disque local. La documentation de lsof précise que l’affirmation « un fichier non lié a un nombre de liens de zéro » n’est pas vraie pour les fichiers NFS accédés depuis un serveur distant. Un fichier supprimé sur le serveur reste donc invisible à +L1 côté client ; l’écran vide de l’étape 4 n’innocente pas un volume réseau.

Geste Effet sur le processus Espace libéré Échoue quand
sudo truncate -s 0 /proc/PID/fd/N aucun, le processus tourne oui, immédiatement exécutable ou logiciel mappé en cours d’exécution (Text file busy) ; fichier de base de données
sudo systemctl restart service arrêt puis relance oui, à la fermeture du descripteur processus non géré par systemd
logrotate avec copytruncate aucun, mais le fichier garde son nom oui, au prochain passage fichier déjà supprimé, absent de la configuration

Questions concrètes sur les fichiers supprimés encore ouverts

lsof +L1 est vide alors que df affiche un disque plein, que vérifier ?

Trois causes reviennent. La commande a été lancée sans sudo et masque les processus des autres utilisateurs ; le système de fichiers concerné est un montage NFS, où NLINK ne vaut pas 0 ; ou l’espace est consommé par autre chose que des fichiers supprimés, comme une réserve de blocs réservée à root ou des inodes épuisés. Dans ce dernier cas, df -h et df -i sur le point de montage exact orientent le diagnostic.

Peut-on récupérer le contenu d’un fichier supprimé encore ouvert ?

Oui, tant que le descripteur reste ouvert. Le noyau expose le contenu par /proc, et une copie du descripteur suffit à en extraire les données.

sudo cp /proc/42113/fd/3 /tmp/access.log.recupere

La copie est possible même si le fichier n’a plus de nom dans l’arborescence. Elle ne le rétablit pas à son emplacement d’origine.

Faut-il tronquer le descripteur ou redémarrer le service ?

Pour un journal, sudo truncate -s 0 /proc/PID/fd/N rend l’espace sans interrompre le service. Pour tout autre type de fichier, et notamment un exécutable mappé ou un fichier de base de données, le redémarrage du service est la seule voie sûre : truncate y échoue avec Text file busy ou corrompt les données.

Pourquoi NLINK vaut 0 et pas une valeur vide ?

La colonne NLINK n’existe que parce que +L a été demandé, conformément à la page de manuel lsof(8). La valeur 0 signifie qu’il ne reste aucune entrée de répertoire pointant vers l’inode ; le fichier est détaché de l’arborescence mais toujours alloué au processus.

Le fichier supprimé grossit encore, comment l’arrêter ?

Le processus détenteur écrit toujours dans l’inode sans nom. Vider le descripteur avec truncate remet le compteur à zéro, mais le fichier regrossit tant que le service n’est pas relancé ou que sa rotation n’est pas corrigée. La correction durable est le redémarrage du service ou une règle logrotate en copytruncate sur ce fichier. Pour situer la consommation par répertoire, fichiers et répertoires couvre les commandes de mesure, et Linux regroupe le reste du diagnostic disque.