Cette partie est soit très facile, soit nettement moins, selon que les fichiers que vous essayez de récupérer occupent moins ou plus de 12 blocs.
Si le fichier n'occupait pas plus de 12 blocs, alors les numéros de
blocs où sont situées toutes ses données sont écrits
dans l'inode : vous pouvez les lire directement sur la sortie de
stat
correspondant à l'inode. De surcroît, debugfs
a
une commande qui automatise cette tâche. Pour reprendre l'exemple précédent :
debugfs: stat <148003>
Inode: 148003 Type: regular Mode: 0644 Flags: 0x0 Version: 1
User: 503 Group: 100 Size: 6065
File ACL: 0 Directory ACL: 0
Links: 0 Blockcount: 12
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
mtime: 0x313bf4d7 -- Tue Mar 5 08:01:27 1996
dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
BLOCKS:
594810 594811 594814 594815 594816 594817
TOTAL: 6
Ce fichier a six blocs. Puisqu'il est en-dessous de la limite des 12,
nous demandons à debugfs
d'écrire le fichier dans un nouvel
endroit, comme par exemple /mnt/recovered.000
:
debugfs: dump <148003> /mnt/recovered.000
Bien sûr, on peut faire ça aussi avec fsgrab
; je le montre ici
en guise d'exemple d'utilisation :
# fsgrab -c 2 -s 594810 /dev/hda5> /mnt/recovered.000
# fsgrab -c 4 -s 594814 /dev/hda5>> /mnt/recovered.000
Que ce soit avec debugfs
ou avec fsgrab
, il y aura un peu
de déchet à la fin de /mnt/recovered.000
, mais ce n'est pas
très important. Si vous voulez vous en débarrasser, la méthode la
plus simple est de prendre le champ Size
de l'inode, et le
brancher sur l'option bs
d'une ligne de commande dd
.
# dd count=1 if=/mnt/recovered.000 of=/mnt/resized.000 bs=6065
Bien sûr, il est possible qu'un ou plusieurs blocs où était écrit votre fichier aient été écrasés. Si c'est le cas, pas de chance : le bloc est mort et enterré (rendez-vous compte, si seulement vous aviez démonté plus tôt !).
Les problèmes apparaissent lorsque le fichier tient sur plus de 12 blocs de données. Ici, il vaut mieux en savoir un peu sur la manière dont sont structurés les systèmes de fichiers Unix. Les données du fichier sont stockées dans des unités appelées « blocs ». Ces blocs peuvent être numérotés séquentiellement. Un fichier a également un « inode », où sont placées des informations telles que propriétaire, permissions ou type. Comme les blocs, les inodes sont numérotés séquentiellement, bien que la séquence soit différente. Une entrée de répertoire consiste en un nom de fichier associé à un numéro d'inode.
Mais, si on en restait là, le noyau ne saurait toujours pas trouver les données correspondant à une entrée de répertoire. Ainsi l'inode indique également l'endroit où se trouvent les blocs de données du fichier, comme suit :
Relisez bien tout ça : je sais que c'est compliqué, mais c'est important, aussi.
Maintenant, l'implantation du noyau pour toutes les versions actuelles (2.0.36 inclue) efface malheureusement tous les blocs indirects (et doublement indirects, etc.) lors de la suppression d'un fichier. Alors, si votre fichier occupait plus de 12 blocs, vous n'êtes pas garanti de pouvoir retrouver les numéros de tous les blocs dont vous avez besoin (sans parler de leur contenu).
La seule méthode que j'aie pu trouver jusqu'ici consiste à supposer que le fichier n'est pas fragmenté : s'il l'est, vous aurez des ennuis. En supposant que le fichier n'est pas fragmenté, il y a plusieurs dispositions de blocs de données, selon le nombre de blocs de données utilisés par le fichier :
les numéros de bloc sont indiqués dans l'inode, comme décrit précédemment ;
après les blocs directs, comptez un pour le bloc indirect, puis vous avez 256 blocs de données ;
comme avant, il y a 12 blocs directs, un bloc indirect (inutile), et 256 blocs. Ils sont suivis d'un bloc doublement indirect (inutile), et 256 répétitions de : un bloc indirect (inutile) et 256 blocs de données ;
la disposition des 65804 premiers blocs est identique à ce qui est décrit di-dessus. Suivent un bloc triplement indirect (inutile) et 256 répétitions d'une séquence « doublement indirect ». Chaque séquence doublement indirecte consiste en un bloc doublement indirect (inutile), suivi de 256 répétitions de : un bloc indirect (inutile) et 256 blocs de données.
Bien entendu, même si ces blocs sont supposés corrects, rien ne garantit que les données qu'ils contiennent sont intactes. De plus, plus le fichier est long, moins vous avez de chances qu'il ait pu être écrit dans le système de fichiers sans fragmentation raisonnable (sauf dans certaines circonstances particulières).
Notez que j'ai supposé depuis le début que vos blocs occupaient la taille de 1024 octets, c'est-à-dire la valeur standard. Si vos blocs sont plus grands, une partie des nombres écrits plus haut doivent être changés. Typiquement, puisque chaque numéro de bloc occupe 4 octets, le nombre de numéros de bloc pouvant être placés dans chaque bloc indirect est taille_du_bloc/4. Donc, chaque fois que le nombre 256 apparaît dans la dicussion qui précède, remplacez-le par taille_du_bloc/4. Les limitations « nombre de blocs requis » devront également être modifiées.
Examinons un exemple de récupération de fichier plus long.
debugfs: stat <1387>
Inode: 148004 Type: regular Mode: 0644 Flags: 0x0 Version: 1
User: 503 Group: 100 Size: 1851347
File ACL: 0 Directory ACL: 0
Links: 0 Blockcount: 3616
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x31a9a574 -- Mon May 27 13:52:04 1996
atime: 0x31a21dd1 -- Tue May 21 20:47:29 1996
mtime: 0x313bf4d7 -- Tue Mar 5 08:01:27 1996
dtime: 0x31a9a574 -- Mon May 27 13:52:04 1996
BLOCKS:
8314 8315 8316 8317 8318 8319 8320 8321 8322 8323 8324 8325 8326 8583
TOTAL: 14
Il semble y avoir de bonnes chances pour que ce fichier ne soit pas fragmenté : de façon évidente, les 12 premiers blocs listés dans l'inode (qui sont tous des blocs de données) sont contigus. Nous pouvons donc commencer par récupérer ces blocs :
# fsgrab -c 12 -s 8314 /dev/hda5> /mnt/recovered.001
Maintenant, le bloc suivant listé dans l'inode, 8326, est un bloc indirect, que nous pouvons ignorer. Mais nous nous fions à notre intuition qu'il sera suivi de 256 blocs de données (du numéro 8327 au numéro 8582).
# fsgrab -c 256 -s 8327 /dev/hda5>> /mnt/recovered.001
Le dernier bloc listé dans l'inode est le 8583. Notez que ça ressemble toujours bien à un fichier contigu : le numéro du dernier bloc que nous ayons écrit était le 8582, donc 8327 + 255. Ce bloc 8583 est un bloc doublement indirect, que nous pouvons ignorer. Il est suivi par jusqu'à 256 répétitions d'un bloc indirect (ignoré) suivi de 256 blocs de données. Après un petit calcul mental, on en déduit les commandes suivantes. Remarquez qu'on saute le bloc doublement indirect 8583 et le bloc indirect 8584, qui suivent immédiatement (espérons-le) et qu'on commence directement à lire les données depuis le bloc 8585.
# fsgrab -c 256 -s 8585 /dev/hda5>> /mnt/recovered.001
# fsgrab -c 256 -s 8842 /dev/hda5>> /mnt/recovered.001
# fsgrab -c 256 -s 9099 /dev/hda5>> /mnt/recovered.001
# fsgrab -c 256 -s 9356 /dev/hda5>> /mnt/recovered.001
# fsgrab -c 256 -s 9613 /dev/hda5>> /mnt/recovered.001
# fsgrab -c 256 -s 9870 /dev/hda5>> /mnt/recovered.001
En rassemblant tout, on voit qu'on a écrit depuis le début 12 + (7 * 256) blocs, c'est-à-dire 1804. La commande « stat » nous a indiqué pour l'inode un « blockcount » de 3616 ; mais ces blocs occupaient malheureusement 512 octets (un reliquat d'Unix), ce que nous voulons réellement est alors 3616/2 = 1808 blocs de 1024 octets. Cela signifie que nous avons seulement besoin de quatre blocs de plus. Le dernier bloc de données écrit portait le numéro 10125. De la même façon que depuis le début, on saute un bloc indirect (numéro 10126) ; on peut alors écrire ces quatre derniers blocs.
# fsgrab -c 4 -s 10127 /dev/hda5>> /mnt/recovered.001
Et maintenant, avec un peu de chance, le fichier complet a été récupéré avec succès.