HardStacked
Partager :

Clonage SD · intégrité des données

Un clone de carte SD qui boote n'est pas un clone qui marche

2026-07-26 · HardStacked · 7 min de lecture

Vous migrez votre Raspberry Pi vers une carte endurante avec l'outil fourni. La nouvelle carte boote — mais l'écran de mot de passe tarde, et une fois le mot de passe entré, un long écran noir passe avant que le bureau apparaisse. Le réflexe, « la carte est lente », pointe dans la mauvaise direction.

On a fait cette migration sur un Pi 5 sous Debian 13, d'une V30 générique vers une SanDisk High Endurance 128 Go, en mesurant chaque étape : le clone à chaud qui a échoué, l'autopsie octet par octet, le clone à froid qui a réussi. Voici les chiffres, nos erreurs incluses, parce qu'elles portent les leçons.

1. Le chronomètre disait plus rapide, l'écran disait le contraire

piclone (le « SD Card Copier » préinstallé sur Raspberry Pi OS) n'est pas un dd bit-à-bit. Dans le binaire : un dd if=/dev/zero sur le premier secteur de la cible, un mkfs.ext4, puis un cp -ax d'un système vivant — fichier par fichier pendant que l'OS écrit.

Le résultat boote, et systemd-analyze annonce 24,5 s et 29,8 s sur deux essais, contre 39,029 s pour la source. À l'écran, l'inverse exact : l'attente est nettement plus longue qu'avant, avant comme après le mot de passe.

Les deux sont vrais. systemd-analyze arrête son chronomètre quand graphical.target est atteint ; le greeter, puis la session, démarrent après — hors de son périmètre. C'est précisément là que la panne se trouve. La carte suspecte montée en lecture seule depuis un système sain, le journal la nomme :

Failed to start gvfs-daemon.service Failed to activate service 'org.gtk.vfs.Daemon': timed out (service_start_timeout=120000ms) ← 120 s d'attente, à chaque session

Le greeter réclame gvfs, puis la session utilisateur à son tour : D-Bus attend 120 secondes chaque fois. D'où les deux attentes — celle qui retarde le mot de passe, avec son erreur en haut de l'écran, et celle qui suit le login. La seconde est noire parce que c'est pcmanfm --desktop qui dessine le fond d'écran et les icônes : tant qu'il attend gvfs, il n'affiche rien. Pourquoi gvfs meurt :

$ stat -c %s usr/libexec/gvfsd 67760 ← exactement la bonne taille $ md5sum usr/libexec/gvfsd 593a1ef6a5d338baa4fecb47ce98e886 ← dpkg attendait 100c13dbd4613636479dc979b274b6d8

Relu trois fois avec drop_caches entre chaque : même somme. La mauvaise donnée est gravée sur la carte, ce n'est pas une lecture instable.

L'habitude

« Ça boote » ne prouve rien, et un chronomètre qui contredit ce que vous voyez à l'écran ne gagne pas l'arbitrage : commencez par regarder ce qu'il mesure, et où il s'arrête. Puis montez le clone en lecture seule depuis un système sain, lisez son journal, et comparez des binaires aux sommes md5 de dpkg avant de déclarer la copie bonne.

2. Les octets faux ont une forme, et cette forme disculpe la carte

Le balayage complet a trouvé 69 fichiers au contenu faux, dispersés sans lien logique : gvfsd, libcrypto.a, libnssckbi.so, un firmware Wi-Fi, des icônes, des en-têtes de noyau. Le désordre apparent cachait un motif parfait :

$ cmp -l gvfsd.src gvfsd.clone | awk '{print $3}' | sort -u 0 ← 100 % des octets divergents valent 0x00

Des plages contiguës, qui finissent soit à la fin du fichier, soit pile sur 2 Mio (2 097 152). Pour gvfsd, 67 760 octets : la divergence court de l'octet 63 673 à 67 745 — une queue de zéros.

Un média défaillant rend du bruit aléatoire. Une écriture perdue rend des zéros alignés. La carte et le lecteur USB sont donc disculpés : c'est la copie à chaud qui a perdu des écritures en vol.

L'habitude

Avant d'accuser le matériel, regardez la forme des octets faux avec cmp -l. Bruit aléatoire : le média. Zéros contigus alignés : l'outil de copie.

3. Notre premier balayage se trompait d'un facteur 300

On l'avoue : le premier md5sum -c est parti sans les droits root. Verdict : 21 513 « échecs ». Le vrai chiffre : 69. Les 21 444 autres étaient juste illisibles — fichiers d'un HIDS réservés à root, compilateurs en -rwx------. md5sum -c mélange « contenu faux » et « pas pu ouvrir » dans le même flux ; on a failli conclure à une carte massivement pourrie.

L'habitude

Balayez en root, et séparez toujours les échecs de lecture des échecs de contenu.

4. Notre clone à froid a failli reproduire la même panne, dans le script

La méthode qui marche : source démontée, e2fsck -fn, e2image -ra -p (ne copie que les blocs utilisés), dd conv=fsync en écriture intégrale, e2fsck -fp.

Deuxième aveu : le premier jet portait dd conv=sparse, attrapé à la relecture. sparse saute les blocs de zéros : l'ancien contenu corrompu de la cible ne serait jamais écrasé, et un fichier de vrais zéros relirait la vieille donnée. La même panne, par un autre chemin. blkdiscard, refusé par le lecteur USB — sans conséquence, justement parce que l'écriture est intégrale.

L'habitude

Sur une cible déjà écrite, écriture intégrale seulement — relisez votre ligne dd, jamais conv=sparse.

5. Un verdict « zéro divergence » aurait rejeté le clone parfait

Vérification du clone à froid : 193 497 sommes md5 de paquets, 849 divergences. Sur un clone parfait. Toutes légitimes : 845 fichiers .pyc d'un HIDS qui les recompile en runtime, 2 configs du même, un EXTERNALLY-MANAGED pour lequel dpkg garde deux sommes (la stdlib et le paquet Raspberry Pi qui l'écrase — faux positif structurel), un g_ether.conf. Plus 229 « erreurs d'ouverture » : autant de fichiers inexistants (scripts d'installation supprimés après déploiement).

Un script qui aurait exigé « 0 divergence » aurait affiché « COPIE NON SAINE, NE PAS booter dessus » sur un clone parfaitement bon. Le nombre de divergences ne mesure pas la corruption ; la signature de la section 2, oui : bloc contigu de zéros de 64 Kio ou plus, ou queue de zéros de 64 Kio ou plus. Sur le clone à froid : 0 fichier flagué.

Et le détecteur doit s'auto-tester avant de juger, parce qu'un détecteur cassé rend « 0 » lui aussi. Le test : un fichier rempli de zéros doit être flagué, un fichier sain avec une queue de 100 k zéros aussi, un fichier aléatoire non. Vérifié en sabotant volontairement le seuil : le refus de verdict se déclenche.

L'habitude

Jugez une copie sur la signature de la panne que vous cherchez, pas sur un compte de divergences. Et faites échouer votre détecteur exprès une fois — s'il ne sait pas dire « je suis cassé », son « 0 » ne vaut rien.

6. Le boot 13 secondes plus rapide, il fallait le prouver

Après la migration : 25,948 s (1,700 s noyau + 24,247 s espace utilisateur), contre 39,029 s avant. Le gain est réel — mais le clone corrompu de la section 1 affichait déjà 24-29 s au même chronomètre, sur une machine dont le bureau ne s'affichait pas. Un bon chiffre de boot peut cohabiter avec un système cassé : il ne mesure pas la santé.

Le contrôle qui tranche : compter les unités systemd activées et masquées, avant et après. Ici 47 et 21, identiques. Même compte de services, moins de temps : un vrai gain d'entrées/sorties. Confirmé poste à poste par systemd-analyze blame : le service du HIDS, gourmand en I/O au démarrage, passe de 21,8 s à 12,937 s — environ 9 des 13 secondes viennent de lui seul.

L'habitude

Un gain de boot ne se célèbre qu'après recomptage des unités enabled et masked. Sinon votre « carte plus vite » peut être un service qui ne se lève plus.

7. Les compteurs d'usure décrivent la source, pas la carte

$ dumpe2fs -h <partition> | grep 'Lifetime writes' Lifetime writes: 1188 GB ← sur une carte fabriquée deux mois plus tôt

Ce compteur de dumpe2fs vit dans le superbloc — copié avec le reste. Après un clone, tout compteur logé dans une structure copiée décrit la source. Le recoupement rassurant : 364 661 inodes et 13 564 397 blocs utilisés, comptés indépendamment sur deux machines, identiques.

L'habitude

L'usure se lit sur le contrôleur (/sys/block/mmcblk0/device/ : cid, name, date), jamais dans le système de fichiers.

Les cinq règles d'un clone honnête

  1. À froid seulement. Une copie d'un système vivant perd des écritures en vol, et le résultat boote quand même.
  2. Écriture intégrale sur la cible, jamais conv=sparse : les blocs de zéros sautés laissent l'ancien contenu corrompu en place.
  3. Le verdict se rend sur une signature, pas sur un compte. Un clone parfait affiche des centaines de divergences légitimes ; un clone pourri se reconnaît à ses plages de zéros.
  4. Séparez « contenu faux » et « pas pu lire ». Mélangés dans le même flux, ils gonflent le verdict d'un facteur 300.
  5. Auto-testez le détecteur. Cassé, il rend « 0 » lui aussi — et un « 0 » qu'on n'a jamais su faire échouer ne vaut rien.

De quoi a l'air un vrai clone

Mesuré sur un Pi 5 sous Debian 13, V30 générique vers SanDisk High Endurance 128 Go. Le clone à chaud bootait avec 69 fichiers faux, tous en zéros contigus ; le clone à froid, vérifié sur 193 497 sommes md5, sort avec 0 fichier flagué et un double comptage inodes/blocs identique sur deux machines.

Une dernière conséquence, contre-intuitive et facile à oublier : un clone à froid fige la source à l'instant T. Tout ce qui s'écrit après le début de la copie n'existe pas sur la carte gravée — y compris le rapport de la copie elle-même, resté sur la machine qui la pilotait. On a cherché ce fichier sur la mauvaise carte avant de s'en rendre compte.

La même leçon traverse toute la migration : « ça boote » n'est pas « ça marche », « plus vite » n'est pas « plus sain », « zéro erreur » n'est pas « propre ». Le chiffre est l'instrument, pas la santé — et chaque verdict doit sortir d'une mesure capable de distinguer, rendue par un instrument qu'on a fait échouer exprès au moins une fois.

La leçon de fond

Un clone qui boote, un chronomètre qui descend, un balayage qui rend zéro : trois signaux rassurants, et trois instruments qui ne mesuraient pas ce qu'on croyait. La discipline qui les démasque est la même partout — savoir ce que le chiffre mesure, chercher la signature de la panne plutôt qu'un compte, et faire échouer l'instrument avant de lui faire confiance.

On est une boutique de sécurité, alors nos incitatifs pointent dans la même direction que les vôtres : rien de ce qu'on vend ne dépend d'une configuration qui reste faible chez vous. Cet alignement, c'est exactement ce que les grandes plateformes ne peuvent pas offrir, et il est vérifiable, pas un slogan. Scannez hardstacked.net vous-même, tout de suite, sur le même scanner.

La même philosophie porte HardVps : il applique une base de durcissement révisée sur votre serveur, capture chaque fichier original d'abord, et vérifie toute restauration à l'octet près par SHA-256. Une rigueur qu'on peut annuler, c'est une rigueur qu'on ose déployer.

Voir ce que HardVps fait →

Sur un Raspberry Pi ? Commencez gratuitement avec audit-pi, notre audit de sécurité open source.

Partager :