Pourquoi la canalisation de «tar» dans «dd» ne s'arrête-t-elle pas tant que le disque n'est pas plein?

18

J'ai une archive tar d'une seule image disque. L'image à l'intérieur de ce fichier tar fait environ 4 Go. Je dirige la sortie de tar xfdans ddpour écrire l'image disque sur une carte SD. Le vidage de disque ne s'arrête jamais tant que la carte n'est pas pleine. Voici ma session shell:

$ ls -l disk.img.tgz
-rw-r--r-- 1 confus confus 192M Okt  5 00:53

$ tar -tvf disk.img.tgz
-rw-r--r-- root/root 4294968320 2018-10-05 00:52 disk.img

$ lsblk -lb /dev/sdc
NAME MAJ:MIN RM        SIZE RO TYPE MOUNTPOINT
sdc    8:32   1 16022241280  0 disk

$ tar zxf disk.img.tgz -O | sudo dd status=progress conv=sync bs=1M of=/dev/sdc
[sudo] password for user: 
15992881152 bytes (16 GB, 15 GiB) copied, 212 s, 75,4 MB/s 
dd: error writing '/dev/sdc': No space left on device
0+15281 records in
15280+0 records out
16022241280 bytes (16 GB, 15 GiB) copied, 217,67 s, 73,6 MB/s

Pourquoi? Il devrait s'arrêter une fois que le hit a écrit l'image de 4 Go dans le panier de 16 Go et ne jamais manquer d'espace!

embrouiller
la source
Avez-vous l'espace disque pour essayer de l'exécuter ddet de l'écrire dans un autre fichier? tar zxf disk.img.tgz -O | dd status=progress conv=sync bs=1M of=/path/to/some/file/on/disk? Si oui, cela vous donne-t-il une copie exacte du fichier d'origine?
Andy Dalton
2
Pourquoi en avez-vous conv=sync? Vouliez-vous utiliser conv=fsyncpeut-être?
Ralph Rönnquist
Êtes-vous certain que c'est la vraie taille du fichier? Je sais que gzip ne dispose que de 32 bits dans lesquels stocker les tailles de fichiers, donc la taille des fichiers de plus de 4 Go est incorrecte. Je ne sais pas si tar a une limitation similaire.
David Conrad

Réponses:

50

C'est parce que tu le fais mal.

Vous utilisez, bs=1Mmais la lecture depuis stdin, pipe, aura des lectures plus petites. En fait, selon dd, vous n'avez pas obtenu une seule lecture complète.

Et puis vous avez conv=syncqui complète les lectures incomplètes avec des zéros.

0+15281 records in
15280+0 records out

dda reçu 0 lectures complètes et 15281 lectures incomplètes et a écrit 15280 blocs complets (conv = sync zéro rempli). Ainsi, la sortie est beaucoup plus grande que l'entrée, jusqu'à ce que vous n'ayez plus d'espace.

   sync   pad  every  input  block  with  NULs to ibs-size; when used with
          block or unblock, pad with spaces rather than NULs

Pour résoudre ce problème, vous pouvez supprimer conv=syncet ajouter iflag=fullblock.


Pour illustrer, considérons yes, qui par défaut crache une infinie "y \ ny \ ny \ n".

$ yes
y
y
y
^C
$ yes | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*

Avec dd bs=1M conv=synccela ressemble à ceci:

$ yes | dd bs=1M conv=sync | hexdump -C
00000000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
0001e000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00100000  79 0a 79 0a 79 0a 79 0a  79 0a 79 0a 79 0a 79 0a  |y.y.y.y.y.y.y.y.|
*
00112000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*

Il obtient donc un bloc incomplet de "y \ ny \ ny \ n" (0x00000 - 0x1e000, 122880 octets) puis écrit le 1M restant sous forme de zéros (0x01e000 - 0x100000, 925696 octets). Dans la plupart des cas, vous ne voulez pas que cela se produise. Le résultat est aléatoire de toute façon, car vous n'avez aucun contrôle réel sur le caractère incomplet de chaque lecture. Comme ici, la deuxième lecture n'est plus 122880 octets mais 73728 octets.

dd conv=syncest rarement utile et même dans les cas où cela serait le bienvenu, comme écrire des zéros lorsque vous obtenez des erreurs de lecture, les choses iront horriblement mal.

frostschutz
la source
Dans ce cas, l'exécution de la ddcommande sous strace(en supposant que Linux) aurait montré que chaque lecture courte du canal était suivie d'une écriture complète de 1 Mo.
Andrew Henle
2
@AndrewHenle n'a même pas besoin de strace pour cela, il suffit de regarder la sortie. Ajout d'une illustration
frostschutz
Cela illustre également pourquoi la ddcommande est fondamentalement rompue et inutilisable. Il est spécifié de fonctionner dans des reads et des writes individuels , mais ces opérations sont spécifiées de telle sorte qu'elles peuvent toujours produire de courtes lectures ou écritures, et ce n'est pas une erreur. Par conséquent, le comportement de dddépend d'un comportement non spécifié.
R ..
Merci pour la réponse très éducative. Comme quelqu'un l'a suggéré, j'étais un âne et j'ai mélangé les nombreuses options dd, mais cela m'a amené à apprendre quelque chose de vous. Ce dont je ne suis toujours pas complètement sûr, c'est de savoir si et quand cela ddaurait pris fin. Je suppose que oui, mais comme il écrivait en fait 1 partie de données réelles et 9 parties de zéros, il se serait arrêté après avoir écrit environ 40G. Est-ce exact?
con-f-use
@R .., cette fonctionnalité est très utile avec les pilotes de périphériques qui se soucient de la taille des blocs de lecture et d'écriture. Je me souviens avoir utilisé des lecteurs de bande qui s'en préoccupaient. Bien que dans ce cas, ce n'est évidemment pas nécessaire, on pourrait simplement rediriger directement vers le disque (sans obtenir un rapport de progression en direct, cependant)
ilkkachu