Comment `dd` peut-il être utilisé pour déplacer à droite des blocs de données?

10

Considérez un périphérique de bloc brut de 100 Mo comme exemple simple. Soit 204800 blocs de 512 octets chacun pour un total de 102760448 octets.

Le défi est de décaler les premiers 98 Mo (blocs 200704) de sorte qu'il y ait un écart de 2 Mo (4096 blocs) devant lui. Pour le faire sur place, rien n'est écrit dans un secteur qui n'a pas été lu. Une façon d'y parvenir consiste à introduire un tampon:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 | dd of=/dev/sdj2 seek=4096

On s'attend à ce mbufferqu'il stocke 4096 blocs avant de transmettre quoi que ce soit à l'écrivain, garantissant ainsi que rien n'est écrit dans une zone qui n'a pas été lue et que l'écrivain accuse un retard sur le lecteur par la taille du tampon. Le tampon doit permettre au lecteur et à l'écrivain de fonctionner aussi rapidement que possible dans ces constriants.

Cependant, cela ne semble pas fonctionner de manière fiable. J'ai essayé d'utiliser de vrais appareils mais ça ne marche jamais sur eux, alors que les expériences avec un fichier fonctionnaient sur ma box 64 bits mais pas sur ma box 32 bits.

Tout d'abord, quelques préparatifs:

$ dd if=/dev/sdj2 count=200704 | md5sum
0f0727f6644dac7a6ec60ea98ffc6da9
$ dd if=/dev/sdj2 count=200704 of=testfile

Cela ne fonctionne pas:

$ dd if=/dev/sdj2 count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=/dev/sdj2 seek=4096
summary: 98.0 MiByte in  4.4sec - average of 22.0 MiB/s
md5 hash: 3cbf1ca59a250d19573285458e320ade

Cela fonctionne sur un système 64 bits mais pas sur un système 32 bits:

$ dd if=testfile count=200704 | mbuffer -s 512 -b 4096 -P 100 -H | dd of=testfile seek=4096 conv=notrunc
summary: 98.0 MiByte in  0.9sec - average of  111 MiB/s
md5 hash: 0f0727f6644dac7a6ec60ea98ffc6da9

Comment cela peut-il être fait de manière fiable?


Remarques

J'ai lu d'autres questions sur la mise en mémoire tampon et regardé pv, bufferet mbuffer. Je ne pouvais que faire fonctionner ce dernier avec la taille de tampon requise.

L'utilisation du stockage intermédiate est une solution évidente au problème qui fonctionne toujours, mais ce n'est pas pratique quand une capacité de réserve suffisante n'est pas disponible.

Testez les plates-formes exécutant Arch Linux avec la mbufferversion 20140302.

étoilé
la source
Je ne suppose pas que cela résoudrait le problème, mais par curiosité pourquoi l'utiliser mbufferdu tout? Pourquoi ne pas plutôt faire ddlire le contenu entier du périphérique de bloc en une seule fois en utilisant dd bs=102760448? Bien sûr, d'une manière ou d'une autre, il est mis en mémoire tampon dans la RAM.
Celada
@Celada - l'exemple de 100 Mo n'était qu'un exemple. La lecture de 1 To, par exemple, en une seule fois ne serait pas une si bonne idée.
Starfry
2
Ah, je comprends maintenant, merci. Le mbufferdevrait en fait forcer le second ddà prendre du retard pour le premier et vous n'avez besoin que de suffisamment de RAM pour tamponner la taille du décalage. Dommage ddne prend pas en charge la lecture et l'écriture des blocs dans l'ordre inverse car cela éliminerait le problème!
Celada
Vous n'avez pas
indiqué
@psusi, le second md5 est sorti par mbuffer (son -Hargument active cette fonctionnalité).
Starfry

Réponses:

2

Sans tampon, vous pouvez revenir en arrière, un bloc à la fois.

for i in $(seq 100 -1 0)
do
    dd if=/dev/thing of=/dev/thing \
       bs=1M skip=$i seek=$(($i+2)) count=1
done

Veuillez noter que cet exemple est dangereux en raison du manque de vérification des erreurs.

Il est également lent en raison de la quantité d' ddappels. Si vous avez de la mémoire disponible, vous pouvez utiliser une taille de bloc plus grande.

Avec un tampon, méfiez - vous des pièges . Il ne suffit pas de garantir un pré-remplissage à 100%. Ce dont vous avez besoin, c'est d'un remplissage minimum tout au long du processus. Le tampon ne doit jamais descendre en dessous 2Mcar sinon vous aurez à nouveau écrasé vos données encore à lire.

Donc, en théorie, vous pouvez vous passer de tout type de tampon et simplement enchaîner dd:

dd if=/dev/thing bs=1M | \
dd bs=1M iflag=fullblock | \
dd bs=1M iflag=fullblock | \
dd of=/dev/thing bs=1M seek=2

En pratique, cela ne fonctionne pas de manière fiable car il n'y a aucune garantie que le premier ddparvienne à continuer à lire les données, tandis que le dernier dd(avec 2M"tampon" entre les deux) écrit déjà.

Vous pouvez augmenter considérablement vos chances en augmentant considérablement la taille du tampon intermédiaire, mais ce n'est pas fiable.

Malheureusement, je ne connais pas un bon programme tampon avec une propriété de remplissage minimum. Vous en avez besoin d'un qui arrête la sortie tant qu'il reste moins que votre marge de sécurité dans le tampon.

frostschutz
la source
J'ai accepté cela car il répond à la question d'origine en montrant comment ddl'utiliser. Je pense, cependant, que la vraie solution n'est pas d'utiliser, ddmais plutôt d'opter pour quelque chose qui est conçu pour fonctionner à l'envers comme ddrescue. J'ai décrit un moyen de le faire dans une réponse.
Starfry
1
@starfry: bien sûr, un programme qui le fait sera une bonne solution. Cependant, je ne suis pas du tout sûr ddrescueici. Pas s'il s'attend à fonctionner sur différents appareils et que vous devez l'inciter à accepter vos arguments. Il pourrait ne pas avoir la propriété "minimum buffer fill" en interne non plus (car avec différents appareils, ce n'est pas nécessaire), donc encore une fois, cela pourrait corrompre vos données. Vous devez vérifier dans le code source s'il est réellement conçu pour votre cas d'utilisation.
frostschutz
1

Vous lisez 4096 blocs, puis écrivez ces blocs 4096 dans les blocs 4096 suivants du disque, écrasant ainsi les deuxièmes blocs 4096 avant de pouvoir les lire. Vous devez lire 8129 blocs pour obtenir ces seconds 4096 avant de commencer toute écriture, puis vous devez uniquement écrire 4096 blocs avant de lire le 4096 suivant.

Vous n'avez pas mentionné de quel type de système de fichiers il s'agit. Si elle est ext [234] et que vous disposez d'une version récente de e2fsprogs, vous pouvez l'utiliser e2image -ra -O 512 /dev/sdj2. Cela a également l'avantage supplémentaire d'être suffisamment intelligent pour ignorer l'espace libre dans le volume.

psusi
la source
Cela a du sens en le lisant et je vais y jeter un autre regard. Mais cela n'explique pas pourquoi cela a fonctionné sur le fichier de test.
Starfry
Concernant le système de fichiers, faites-vous référence au système de fichiers contenant mon fichier de test? Ce n'est ext4que pour la copie de périphérique de bloc, tout système de fichiers ne devrait pas être pertinent.
Starfry
@starfry, la seule façon que je sache de le faire de manière générique est d'utiliser l'algorithme suggéré par Emmanuel (travailler à l'envers depuis la fin), ce que fait gparted.
psusi
Concernant la taille des blocs, j'avais essayé des blocs plus gros (j'aurais dû écrire cela dans la question). J'ai trouvé qu'il n'était pas devenu plus fiable, même un tampon de secteur 64K. La solution fiable est de fonctionner en arrière, quelque chose qui ddne fait pas.
Starfry
1

Une solution fiable requiert que vous vous assuriez que rien n'écrit dans une zone qui n'a peut-être pas été lue et que la seule véritable façon d'y parvenir est d'effectuer la copie dans le sens inverse.

L' ddrescueoutil peut fonctionner en sens inverse mais il refuse de fonctionner avec l'entrée et la sortie identiques. Cependant, il est possible de le tromper en dupliquant le nœud du périphérique.

J'ai effectué quelques expériences rapides et cela semble fonctionner. La ligne de commande est:

$ ddrescue -f -R -s 200704s -o 4096s /dev/sdj11 /dev/sdj11_copy

Les arguments sont

  • -f est nécessaire pour le forcer à écrire sur un périphérique de sortie existant
  • -R lui dit de travailler dans le sens inverse
  • -slui indique la quantité d'entrée à copier (j'ai utilisé le ssuffixe pour spécifier le nombre de secteurs)
  • -olui dit de rechercher vers l'avant dans le périphérique de sortie avant d'écrire (spécifié à nouveau dans les secteurs avec le ssuffixe)
  • /dev/sdj11 est le périphérique bloc à lire
  • /dev/sdj11_copy est le périphérique de bloc pour écrire

J'ai créé /dev/sdj11_copyavec mknodpour correspondre aux paramètres de /dev/sdj11.

Je n'ai fait que des tests très rapides, mais cela semble fonctionner correctement pour copier un périphérique brut. Cela ne fonctionne pas sur un fichier (je ne pouvais pas l'amener à aller au-delà des fichiers étant les mêmes)

Cela ne répond pas à ma question initiale qui demandait comment y parvenir, ddmais je pense qu'après avoir lu les autres réponses, la réponse est que ddcela ne peut pas être fait.

étoilé
la source
Que se passe-t-il si ddrescuedécouvre un mauvais bloc dans ce scénario? S'il saute vers une autre zone du disque (pour éviter les blocs défectueux) et continue de copier à partir de là, il écraserait à nouveau les parties non encore copiées de vos données. S'il ne s'attend pas à travailler avec le même appareil, il n'a aucune raison de prendre des mesures spéciales pour éviter divers cas possibles de corruption de données.
frostschutz
Je suis d'accord que c'est un problème potentiel, mais je n'ai pas examiné les cas marginaux, car j'ai pu l'utiliser pour faire ce dont j'avais besoin. Il existe des ddrescueoptions pour limiter ses tentatives de récupération de données incorrectes, mais je n'ai pas cherché à les utiliser.
Starfry
Le fait qu'il refuse de fonctionner si l'entrée et la sortie sont identiques est probablement une bonne indication qu'il n'est pas sûr.
psusi