mercredi 6 avril 2016

Réparer une partition partiellement agrandie

L’autre jour, au bureau, je devais redistribuer l’espace attribué aux partitions d’un disque dur externe connecté à mon portable. Je devais réduire une partition et allouer l’espace libéré à une partition située à droite de la première. J’ai utilisé pour cela l’excellent outil gparted; l’opération de réduction s’est bien déroulée, mais l’opération d’agrandissement m’a posé un petit problème.

Comme la partition devait être agrandie vers la gauche, l’opération nécessitait le déplacement de toutes les données de la partition, soit près de 600 GB. Le volume de données à déplacer étant plutôt grand, c’est une opération assez longue, plus longue que ce que j’avais estimé. Vers 17h, l’opération de déplacement des données n’était pas encore terminée et je devais quitter le bureau avec mon portable… ce qui signifiait que je devais interrompre l’opération. En googlant un peu, j’ai réalisé qu’on pouvait utiliser le mécanisme de contrôle des processus pour suspendre le déplacement (avec kill -STOP). C’est donc ce que j’ai fait, puis j’ai mis mon portable en veille et je suis parti avec le disque pour la maison en espérant que tout se passe bien pour la suite.

Ce ne fut pas le cas. Arrivé à la maison, j’ai rebranché le disque au portable pour poursuivre l’opération. Cependant, le disque s’attachait obstinément sur /dev/sdc plutôt que /dev/sdb, comme il le faisait au bureau. Gparted, qui s’attendait à travailler avec le disque sur /dev/sdb, échouait à poursuivre l’opération et je me suis retrouvé avec un disque contenant des centaines de gigaoctets de données inaccessibles.

Bien déterminé à ne pas perdre ces données qui, je le savais, étaient quasi intactes sur le disque, je me suis mis à googler à nouveau. Je ne devais certainement pas être la première personne à qui ceci était arrivé. À mon grand soulagement, je suis tombé sur cette discussion qui apportait une solution à mon problème, quoiqu’elle fut un peu avare de détails. Je vais donc vous décrire pas à pas la procédure que j’ai suivie pour rendre ma partition utilisable à nouveau, dans l’espoir que cela pourra vous être utile. Mais attention: vérifiez vos commandes et vos calculs car une erreur pourrait causer une perte irréversible de vos données. Si vous le pouvez, faites une copie de vos données avant de tenter cette procédure.

D’abord, il faut comprendre que lors de l’agrandissement d’une partition vers la gauche, le système de fichiers existant doit être déplacé au nouveau début de partition. Gparted accomplit cela en copiant le système de fichiers par morceaux de quelques mégaoctets, en ordre d’adresse croissant (de gauche à droite). Si le déplacement est interrompu, les données sont toujours sur le disque, mais le système de fichier, ext4 dans mon cas, est dans un état incorrect, séparé en deux morceaux. On doit compléter la copie des données pour remettre le système de fichiers en bon état.

ext4 divise l’espace de stockage en blocs, généralement de 4096 octets. Le premier bloc contient diverses informations essentielles à propos du système de fichiers; c’est le superbloc. Comme ces informations sont très importantes, le superbloc est reproduit à plusieurs autres endroits. La commande sudo mke2fs -n /dev/sdc2 nous renseigne sur l’emplacement des copies du superblocs (et sur le système de fichiers en général):

mke2fs 1.42.9 (4-Feb-2014)
Étiquette de système de fichiers=
Type de système d'exploitation : Linux
Taille de bloc=4096 (log=2)
Taille de fragment=4096 (log=2)
« Stride » = 0 blocs, « Stripe width » = 0 blocs
57212928 i-noeuds, 228830520 blocs
11441526 blocs (5.00%) réservés pour le super utilisateur
Premier bloc de données=0
Nombre maximum de blocs du système de fichiers=4294967296
6984 groupes de blocs
32768 blocs par groupe, 32768 fragments par groupe
8192 i-noeuds par groupe
Superblocs de secours stockés sur les blocs :
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
        4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
        102400000, 214990848

On voit, à la fin de la sortie, les numéros des superblocs. Comme le déplacement du système de fichiers a été interrompu, ce ne sont pas tous les blocs listés qui contiennent un superbloc. Nous allons déterminer à quelle adresse la copie a été interrompue en examinant le contenu de ces supposés superblocs.

Dans le système de fichiers ext4, les superblocs ont une signature unique: ils ont la valeur 0xEF53 (leur signature magique) à l’offset 0x38 (1). Comme le premier superbloc est par convention à l’adresse 0x400, nous devrions retrouver les octets 0xEF53 à l’adresse 0x438. On peut vérifier cela avec hexedit /dev/sdc2 (remplacez /dev/sdc2 par le chemin où votre disque est attaché): en allant à l’adresse 0x438, on devrait voir 53 EF. On vérifie ensuite chacune des copies du superbloc. Comme la taille d’un bloc est de 4096 octets (c’est indiqué dans la sortie de mke2fs), on multiplie les numéros de bloc des superblocs par 4096 puis on additionne 56 (0x38) pour trouver l’adresse de la signature magique de chaque superbloc. Une calculatrice avec conversion hexadécimale est d’un précieux secours pour ces calculs. Avec hexedit, on vérifie si l’on retrouve la signature du superbloc à chacune des adresses calculées (protip: la touche enter permet de se déplacer à une adresse). Normalement, on devrait atteindre un point où on ne retrouve plus la signature: la copie a donc été interrompue entre l’adresse de ce superbloc incorrect et l’adresse du superbloc précédent. Nommons ces adresses B et A, respectivement.

Ensuite, nous allons chercher les adresses supérieures à A où l’on retrouve des données correspondant à des superblocs. Pour cela, toujours dans hexedit, on se rend à l’adresse de A puis on note les premiers quelques octets du contenu du superbloc. On cherche ensuite ce contenu à partir de l’adresse A et on note toutes les adresses où on le retrouve (protip: Ctrl-S). Ces adresses correspondent soit à des superblocs déjà copiés, soit à des superblocs qui n’ont pas été copiés. Il faut trouver, parmi ces adresses, celle qui correspond au superbloc A. Pour ma part, je me suis servi de hexedit pour examiner les données autour des superblocs. Une fois que l’on croit avoir établi une correspondance, on vérifie l’hypothèse à l’aide d’un programme qui va comparer les données de façon exhaustive:

#!/usr/bin/python

device = '/dev/sdc2'
offset = OFFSET # hypothèse - A
chunk_size = 2 * 1024 * 1024  # 2MB

offset_a = 0
offset_b = offset
d = open(device, 'rb')

a = b = ''
while a == b:
  d.seek(offset_a)
  a = d.read(chunk_size)

  d.seek(offset_b)
  b = d.read(chunk_size)

  for i in xrange(0, chunk_size):
     if a[i] != b[i]:
       print("Data differ at", offset_a + i)
       exit()

  offset_a += chunk_size
  offset_b += chunk_size

Si la valeur affichée (appelons la SEEK) est plus grande que A mais plus petite que B, notre hypothèse est confirmée et la valeur OFFSET est la distance, en octets, de la copie.

Avec ces valeurs, on peut utiliser dd pour poursuivre la copie (attention de bien vérifier vos paramètres car vous pourriez perdre définitivement des données si cette commande est exécutée avec de mauvais paramètres; surtout, n’oubliez pas le paramètre conv=notrunc):

dd bs=CHUNK_SIZE count=COUNT if=DEVICE seek=SEEK skip=SKIP iflag=count_bytes,skip_bytes oflag=seek_bytes conv=notrunc

avec:

CHUNK_SIZE: la copie se fera par morceaux de cette taille (par exemple: 2M)
SKIP: SEEK + OFFSET
COUNT: FS_SIZE - SKIP
DEVICE: votre disque, par exemple: /dev/sdc2

Tous les nombres (excepté CHUNK_SIZE) doivent être écrits en base 10. Pour FS_SIZE, la taille de la partition en octets, hexedit nous l’indique en hexadécimal au bas de l’écran.

Une fois la copie terminée (ce qui peut prendre plusieurs heures), il faut redimensionner le système de fichiers. En effet, nous avons actuellement un système de fichiers qui n’occupe qu’une partie de la partition. Dans gparted, on peut facilement agrandir le système de fichiers afin qu’il occupe toute la partition. Si vous préférez la ligne de commande, lisez le manuel de resize2fs.

Et voilà; si tout s’est bien déroulé, vous devriez maintenant avoir un système de fichiers en état de marche.