Wie kann `dd` verwendet werden, um Datenblöcke nach rechts zu verschieben?

10

Betrachten Sie ein 100-MB-Raw-Block-Gerät als einfaches Beispiel. Das sind 204800 Blöcke mit jeweils 512 Bytes für insgesamt 102760448 Bytes.

Die Herausforderung besteht darin, die ersten 98 MB (200704 Blöcke) so zu verschieben, dass eine Lücke von 2 MB (4096 Blöcke) davor liegt. Um dies direkt zu tun, muss nichts in einen Sektor geschrieben werden, der nicht gelesen wurde. Eine Möglichkeit, dies zu erreichen, besteht darin, einen Puffer einzuführen:

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

Die Erwartung ist, dass mbuffer4096 Blöcke gespeichert werden, bevor etwas an den Schreiber übergeben wird, wodurch sichergestellt wird, dass nichts in einen Bereich geschrieben wird, der nicht gelesen wurde, und dass der Schreiber dem Leser um die Größe des Puffers hinterherhinkt. Der Puffer sollte es dem Leser und dem Schreiber ermöglichen, innerhalb dieser Konstrianten so schnell wie möglich zu arbeiten.

Es scheint jedoch nicht zuverlässig zu funktionieren. Ich habe versucht, echte Geräte zu verwenden, aber es funktioniert nie auf ihnen, während Experimente mit einer Datei auf meiner 64-Bit-Box, aber nicht auf meiner 32-Bit-Box funktionierten.

Zunächst einige Vorbereitungen:

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

Das funktioniert nicht:

$ 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

Dies funktioniert auf einem 64-Bit-System, jedoch nicht auf einem 32-Bit-System:

$ 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

Wie geht das zuverlässig?


Anmerkungen

Ich habe andere Fragen zum Puffern gelesen und angeschaut pv, bufferund mbuffer. Letzteres konnte ich nur mit der erforderlichen Puffergröße zum Laufen bringen.

Die Verwendung von Intermetiate-Speicher ist eine offensichtliche Lösung für das Problem, das immer funktioniert, aber nicht praktikabel ist, wenn nicht genügend freie Kapazität verfügbar ist.

Testplattformen unter Arch Linux mit mbufferVersion 20140302.

Sternenhimmel
quelle
Ich nehme nicht an, dass es das Problem lösen würde, aber aus Neugier, warum mbufferüberhaupt verwenden? Warum nicht stattdessen ddden gesamten Inhalt des Blockgeräts auf einmal lesen lassen dd bs=102760448? Natürlich ist es auf die eine oder andere Weise im RAM gepuffert.
Celada
@ Celada - das 100 MB Beispiel war nur ein Beispiel. Zum Beispiel wäre es keine so gute Idee, 1 TB auf einmal zu lesen.
Sternenfrucht
2
Ah, ich verstehe jetzt, danke. Das mbuffersollte tatsächlich den zweiten zwingen dd, für den ersten zurückzubleiben, und Sie benötigen nur genügend RAM, um die Größe der Verschiebung zu puffern. Schade, dass dddas Lesen und Schreiben von Blöcken in umgekehrter Reihenfolge nicht unterstützt wird, da dies das Problem beseitigen würde!
Celada
Sie haben nicht aufgelistet, wie Sie die zweite md5sum
psusi
@psusi, der zweite md5 wird von mbuffer ausgegeben (sein -HArgument aktiviert diese Funktion).
Sternenfrucht

Antworten:

2

Ohne Puffer können Sie Block für Block rückwärts gehen.

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

Bitte beachten Sie, dass dieses Beispiel aufgrund fehlender Fehlerprüfung gefährlich ist.

Es ist auch langsam aufgrund der Anzahl der ddAnrufe. Wenn Sie Speicherplatz zur Verfügung haben, können Sie eine größere Blockgröße verwenden.

Achten Sie mit einem Puffer auf Fallstricke . Es reicht nicht aus, eine 100% ige Vorfüllung zu gewährleisten. Was Sie brauchen, ist eine minimale Füllung während des gesamten Prozesses. Der Puffer darf niemals unterschreiten, 2Mda Sie sonst Ihre noch zu lesenden Daten erneut überschrieben haben.

Theoretisch könnte man also auf jede Art von Puffer verzichten und nur verketten 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

In der Praxis funktioniert dies nicht zuverlässig, da es keine Garantie gibt, dass der erste ddDaten weiter liest, während der letzte dd(mit 2M"Puffer" dazwischen) bereits schreibt.

Sie können Ihre Chancen erheblich erhöhen, indem Sie den Zwischenpuffer erheblich vergrößern. Trotzdem ist er nicht zuverlässig.

Leider kenne ich kein gutes Pufferprogramm mit minimaler Fülleigenschaft. Sie benötigen eine, die die Ausgabe stoppt, solange sich weniger als Ihr Sicherheitsspielraum im Puffer befindet.

Frostschutz
quelle
Ich habe dies akzeptiert, weil es die ursprüngliche Frage beantwortet, indem es zeigt, wie ddes verwendet werden kann. Ich denke jedoch, dass die wirkliche Lösung nicht darin besteht dd, etwas zu verwenden, sondern sich für etwas zu entscheiden, das so konzipiert ist, dass es rückwärts läuft ddrescue. Ich habe in einer Antwort einen Weg beschrieben, dies zu tun.
Sternenfrucht
1
@starfry: Sicher, ein Programm, das es einfach macht, wird eine schöne Lösung sein. Allerdings bin ich mir hier überhaupt nicht sicher ddrescue. Nicht, wenn erwartet wird, dass es auf verschiedenen Geräten funktioniert, und Sie es dazu bringen müssen, Ihre Argumente zu akzeptieren. Möglicherweise verfügt es auch intern nicht über die Eigenschaft "Minimum Buffer Fill" (da dies bei verschiedenen Geräten nicht erforderlich ist), sodass Ihre Daten erneut beschädigt werden können. Sie müssten den Quellcode einchecken, ob er tatsächlich für Ihren Anwendungsfall entwickelt wurde.
Frostschutz
1

Sie lesen 4096 Blöcke und schreiben diese 4096 Blöcke dann in die nächsten 4096 Blöcke der Festplatte, wodurch die zweiten 4096 Blöcke überschrieben werden, bevor sie gelesen werden können. Sie müssen 8129 Blöcke lesen, um diese zweiten 4096 zu erhalten, bevor Sie mit dem Schreiben beginnen, und dann müssen Sie nur 4096 Blöcke schreiben, bevor Sie die nächsten 4096 lesen.

Sie haben nicht erwähnt, um welche Art von Dateisystem es sich handelt. Wenn es ext [234] ist und Sie eine aktuelle Version von e2fsprogs haben, können Sie verwenden e2image -ra -O 512 /dev/sdj2. Dies hat auch den zusätzlichen Vorteil, dass es intelligent genug ist, um den freien Speicherplatz im Volume zu überspringen.

psusi
quelle
Das macht Sinn, wenn ich es lese, und ich werde darauf basierend einen weiteren Blick werfen. Es erklärt jedoch nicht, warum es in der Testdatei funktioniert hat.
Sternenfrucht
Beziehen Sie sich beim Dateisystem auf das Dateisystem, das meine Testdatei enthält? Das heißt ext4, für die Blockgerätekopie sollte jedes Dateisystem irrelevant sein.
Sternenfrucht
@starfry, der einzige Weg, den ich kenne, um dies generisch zu tun, ist die Verwendung des von Emmanuel vorgeschlagenen Algorithmus (vom Ende rückwärts arbeiten), was gparted tut.
Psusi
In Bezug auf die Blockgröße hatte ich größere Blöcke ausprobiert (das hätte ich in die Frage schreiben sollen). Ich fand heraus, dass es selbst mit einem 64K-Sektorpuffer nicht zuverlässiger wurde. Die zuverlässige Lösung besteht darin, rückwärts zu laufen, was ddnicht funktioniert.
Sternenfrucht
1

Für eine zuverlässige Lösung müssen Sie sicherstellen, dass nichts in einen Bereich geschrieben wird, der möglicherweise nicht gelesen wurde. Der einzige wirkliche Weg, dies zu erreichen, besteht darin, die Kopie in umgekehrter Richtung auszuführen.

Das ddrescueTool kann in umgekehrter Richtung arbeiten, weigert sich jedoch, mit gleicher Eingabe und Ausgabe zu arbeiten. Es ist jedoch möglich, es durch Duplizieren des Geräteknotens auszutricksen.

Ich habe einige schnelle Experimente durchgeführt und es scheint zu funktionieren. Die Befehlszeile lautet:

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

Die Argumente sind

  • -f ist erforderlich, um das Schreiben auf ein vorhandenes Ausgabegerät zu erzwingen
  • -R sagt ihm, er solle in umgekehrter Richtung arbeiten
  • -sgibt an, wie viel von der Eingabe kopiert werden soll (ich habe das sSuffix verwendet, um die Anzahl der Sektoren anzugeben)
  • -oweist es an, vor dem Schreiben im Ausgabegerät nach vorne zu suchen (erneut in Sektoren mit dem sSuffix angegeben)
  • /dev/sdj11 ist das zu lesende Blockgerät
  • /dev/sdj11_copy ist das zu schreibende Blockgerät

Ich habe /dev/sdj11_copymit mknodden Parameter übereinstimmen /dev/sdj11.

Ich habe nur einige sehr schnelle Tests durchgeführt, aber dies scheint in Ordnung zu sein, um ein unformatiertes Gerät zu kopieren. Es funktioniert nicht mit einer Datei (ich konnte es nicht dazu bringen, über die gleichen Dateien hinauszugehen)

Dies beantwortet nicht meine ursprüngliche Frage, mit der gefragt wurde, wie dies erreicht werden soll, ddaber ich denke, nachdem ich die anderen Antworten gelesen habe, ist die Antwort darauf, dass dddies nicht möglich ist.

Sternenhimmel
quelle
Was passiert, wenn ddrescuein diesem Szenario ein fehlerhafter Block entdeckt wird? Wenn es in einen anderen Bereich der Festplatte springt (um fehlerhafte Blöcke zu vermeiden) und von dort aus weiter kopiert, werden noch nicht kopierte Teile Ihrer Daten erneut überschrieben. Wenn nicht erwartet wird, dass es mit demselben Gerät funktioniert, gibt es keinen Grund, besondere Maßnahmen zu ergreifen, um verschiedene mögliche Fälle von Datenkorruption zu verhindern.
Frostschutz
Ich bin damit einverstanden, dass dies ein potenzielles Problem ist, aber ich habe mir die Randfälle nicht angesehen, da ich damit das tun konnte, was ich brauchte. Es gibt ddrescueOptionen, um die Versuche, fehlerhafte Daten wiederherzustellen, einzuschränken, aber ich habe nicht versucht, sie zu verwenden.
Sternenfrucht
Die Tatsache, dass es sich weigert zu arbeiten, wenn Eingang und Ausgang gleich sind, ist wahrscheinlich ein guter Hinweis darauf, dass es nicht sicher ist.
Psusi