Version courte: Dans quelles circonstances peut-on dd
utiliser en toute sécurité pour copier des données, ce qui signifie qu'il n'y a aucun risque de corruption dû à une lecture ou à une écriture partielle?
Version longue - préambule: dd
est souvent utilisé pour copier des données, en particulier depuis ou vers un périphérique ( exemple ). On lui attribue parfois des propriétés mystiques de pouvoir accéder à des périphériques à un niveau inférieur à celui d'autres outils (alors que c'est le fichier de périphérique qui fait la magie) - c'est pourtant dd if=/dev/sda
la même chose que cat /dev/sda
. dd
est parfois pensé pour être plus rapide, mais cat
peut le battre dans la pratique . Néanmoins, dd
possède des propriétés uniques qui le rendent réellement utile parfois .
Problème: dd if=foo of=bar
n'est pas, en fait, le même que cat <foo >bar
. Sur la plupart des bureaux¹, dd
fait un appel unique à read()
. (Je trouve POSIX flou sur ce qui constitue "la lecture d'un bloc d'entrée" dd
.) Si read()
renvoie un résultat partiel (qui, selon POSIX et d'autres documents de référence, est autorisé à moins que la documentation d'implémentation ne l'indique autrement), un bloc partiel est copié. Exactement le même problème existe pour write()
.
Observations : Dans la pratique, j'ai constaté que je pouvais dd
gérer les périphériques en mode bloc et les fichiers normaux, mais c'est peut-être simplement que je ne l'ai pas beaucoup utilisé. En ce qui concerne les tuyaux, il n’est pas difficile de s’y prendre dd
; par exemple essayez ce code :
yes | dd of=out bs=1024k count=10
et vérifiez la taille du out
fichier (il sera probablement bien en dessous de 10 Mo).
Question : Dans quelles circonstances peut-on dd
utiliser en toute sécurité pour copier des données? En d'autres termes, quelles conditions sur la taille des blocs, sur l'implémentation, sur les types de fichiers, etc., peuvent garantir que dd
toutes les données seront copiées?
( GNU dd a un fullblock
drapeau pour lui dire d’appeler read()
ou write()
dans une boucle afin de transférer un bloc complet. Donc, cela dd iflag=fullblock
est toujours sûr. Ma question concerne le cas où ces drapeaux (qui n’existent pas sur d’autres implémentations) ne sont pas utilisés .)
¹ J'ai vérifié sur OpenBSD, GNU coreutils et BusyBox.
count
, leiflag=fullblock
est obligatoire (ou alternativementiflag=count_bytes
). Il n'y a pasoflag=fullblock
.Réponses:
De la spec :
bs=
expr
opérande est spécifié et qu'aucune conversion autre quesync
,noerror
ounotrunc
n'est demandée, les données renvoyées par chaque bloc d'entrée doivent être écrites comme un bloc de sortie séparé. si le nombreread()
retourné est inférieur à un bloc complet et que lasync
conversion n'est pas spécifiée, le bloc de sortie résultant aura la même taille que le bloc d'entrée.Donc, c'est probablement ce qui cause votre confusion. Oui, car il
dd
est conçu pour le blocage, lesread()
s partiels sont par défaut mappés de 1: 1 àwrite()
s partiels , ou biensync
d out sur le remplissage de queue NUL ou les caractères d'espace à labs=
tailleconv=sync
spécifiée.Cela signifie que
dd
est sûr à utiliser pour la copie des données (w / aucun risque de corruption en raison d'une lecture partielle ou écriture) dans tous les cas , mais celui dans lequel il est arbitrairement limité par uncount=
argument parce que sinon sedd
fera un plaisir dewrite()
sa sortie en blocs de taille identique à ceux dans lesquels son entrée a étéread()
jusqu’à ce qu’elle soitread()
complètement passée à travers elle. Et même cette mise en garde est que vrai quandbs=
est spécifié ouobs=
est non spécifié, comme la phrase suivante dans les états spécifications:bs=
expr
opérande n'est pas spécifié, ou si une conversion autre quesync
,noerror
ounotrunc
est demandée, l'entrée doit être traitée et collectée dans des blocs de sortie de taille normale jusqu'à la fin de l'entrée.Sans
ibs=
et / ouobs=
arguments, cela n'a pas d'importance -ibs
etobs
ont la même taille par défaut. Cependant, vous pouvez expliquer explicitement la mise en mémoire tampon des entrées en spécifiant des tailles différentes pour l'une ou l'autre et non pour la spécificationbs=
(car elle est prioritaire) .Par exemple, si vous le faites:
... alors un POSIX
dd
serawrite()
composé de 512 octets en regroupant chaqueread()
octet unique dans un seul bloc de sortie.Sinon, si vous le faites ...
... un Posix
dd
seraread()
au maximum de 512 octets à la fois, maiswrite()
chaque méga - octet de taille bloc de sortie (noyau permettant et sauf peut - être la dernière - parce que ce EOF) dans son intégralité par la collecte d' entrée en blocs de sortie pleine grandeur .Aussi de la spécification, cependant:
count=n
count=
mappe à desi?bs=
blocs, et donc afin de gérer une limite arbitraire surcount=
portable, vous aurez besoin de deuxdd
s. La manière la plus pratique de le faire avec deuxdd
s est de canaliser la sortie de l'un vers l'entrée de l'autre, ce qui nous place sûrement dans le domaine de la lecture / écriture d'un fichier spécial, quel que soit le type d'entrée d'origine.Un canal IPC signifie que lorsque vous spécifiez des
[io]bs=
arguments, vous devez conserver ces valeurs dans lesPIPE_BUF
limites définies par le système, de manière sécurisée . POSIX indique que le noyau système ne doit garantir que lesread()
s atomiques et leswrite()
s dans les limitesPIPE_BUF
définies parlimits.h
. POSIX garantit que cePIPE_BUF
soit au moins ...{_POSIX_PIPE_BUF}
... (qui se trouve être également la
dd
taille de bloc par défaut d'e / s) , mais la valeur réelle est généralement d'au moins 4k. Sur un système Linux à jour, il est de 64k par défaut.Ainsi, lorsque vous configurez vos
dd
processus, vous devez le faire sur un facteur de blocage basé sur trois valeurs:PIPE_BUF
ou moins)Comme:
Vous devez synchroniser les
dd
entrées / sorties / pour gérer les entrées non recherchées. En d'autres termes, explicitez les tampons de canaux et ces problèmes cessent. C'est pour çadd
. La quantité inconnue ici estyes
la taille du tampon de - mais si vous bloquez cette quantité avec une autre quantité connue,dd
une multiplication un peu informée peut êtredd
utilisée de manière sûre pour la copie de données (sans risque de corruption dû à une lecture ou une écriture partielle). même lorsque vous limitez arbitrairement l'entrée aveccount=
n'importe quel type d'entrée sur n'importe quel système POSIX et sans manquer un seul octet.Voici un extrait de la spécification POSIX :
ibs=
expr
expr
obs=
expr
expr
bs=
expr
expr
octets, remplaçantsibs=
etobs=
. Si aucune conversion autre quesync
,noerror
etnotrunc
n'est spécifiée, chaque bloc d'entrée doit être copié dans la sortie sous la forme d'un bloc unique sans agréger les blocs courts.Vous trouverez également une partie de ceci expliqué mieux ici .
la source
Avec les sockets, les pipes ou les ttys, read () et write () peuvent transférer moins que la taille demandée. Ainsi, lorsque vous utilisez dd sur ces derniers, vous avez besoin de l'indicateur fullblock. Cependant, avec des fichiers normaux et des périphériques en mode bloc, ils ne peuvent effectuer qu’une courte lecture / écriture: lorsque vous atteignez EOF ou en cas d’erreur. C’est pourquoi les anciennes implémentations de dd sans l’indicateur fullblock pouvaient être utilisées en toute sécurité pour la duplication de disque.
la source
mke2fs
ne me souviens pas d’échouer en silence car il appellewrite()
avec une taille non-power-of-2 (3kB IIRC) et le noyau est arrondi. jusqu'à une puissance de 2.)cat </dev/sda >/dev/sdb
fonctionne très bien pour cloner un disque.cat
choisit une taille de tampon pour la performance; il ne reçoit aucune information relative au périphérique du noyau. En dehors des bandes, vous pouvezread()
etwrite()
à un dispositif de bloc avec toute taille. Sous Linux au moins, celast_blksize
ne dépend que du système de fichiers où se trouve l'inode du périphérique en mode bloc, et non du périphérique sous-jacent.