Kurzfassung: Unter welchen Umständen ist dd
sicher zum Kopieren von Daten zu verwenden, sicher bedeutet, dass kein Risiko einer Beschädigung aufgrund eines teilweisen Lesens oder Schreibens besteht?
Langfassung – Präambel: dd
wird häufig verwendet, um Daten zu kopieren, insbesondere von oder zu einem Gerät (Beispiel). Ihm wird manchmal die mystische Eigenschaft zugeschrieben, auf Geräte auf einer niedrigeren Ebene als andere Tools zugreifen zu können (wobei tatsächlich die Gerätedatei die Magie ausübt) – dennoch dd if=/dev/sda
ist dasselbe wie cat /dev/sda
. dd
wird manchmal für schneller gehalten, aber cat
kann es in der Praxis schlagen. Trotzdem dd
hat einzigartige Eigenschaften, die es manchmal wirklich nützlich machen.
Problem: dd if=foo of=bar
ist tatsächlich nicht dasselbe wie cat <foo >bar
. Auf den meisten Unices¹, dd
führt einen einzelnen Aufruf von read()
durch . (Ich finde POSIX verschwommen, was das „Lesen eines Eingabeblocks“ in dd
ausmacht .) Wenn read()
ein Teilergebnis zurückgibt (was laut POSIX und anderen Referenzdokumenten erlaubt ist, sofern die Implementierungsdokumentation nichts anderes sagt), wird ein Teilblock kopiert. Genau das gleiche Problem besteht für write()
.
Beobachtungen :In der Praxis habe ich das dd
gefunden kann mit Blockgeräten und regulären Dateien umgehen, aber das kann nur daran liegen, dass ich es nicht viel trainiert habe. Wenn es um Pipes geht, ist es nicht schwierig, dd
zu setzen schuld; Versuchen Sie zum Beispiel diesen Code:
yes | dd of=out bs=1024k count=10
und überprüfen Sie die Größe des out
Datei (es ist wahrscheinlich weit unter 10 MB).
Frage :Unter welchen Umständen ist dd
sicher zum Kopieren von Daten? Mit anderen Worten, welche Bedingungen an die Blockgrößen, an die Implementierung, an die Dateitypen usw. können dafür sorgen, dass dd
werden alle Daten kopiert?
(GNU dd hat einen fullblock
-Flag, um es anzuweisen, read()
aufzurufen oder write()
in einer Schleife, um einen ganzen Block zu übertragen. Also dd iflag=fullblock
ist immer sicher. Meine Frage bezieht sich auf den Fall, wenn diese Flags (die in anderen Implementierungen nicht vorhanden sind) nicht verwendet werden.)
¹
Ich habe OpenBSD, GNU Coreutils und BusyBox überprüft.
Akzeptierte Antwort:
Aus der Spezifikation:
- Falls
bs=
expr
Operand ist angegeben und keine anderen Konvertierungen alssync
,noerror
, odernotrunc
angefordert werden, müssen die von jedem Eingangsblock zurückgegebenen Daten als separater Ausgangsblock geschrieben werden; wenn derread()
gibt weniger als einen vollen Block und densync
zurück Konvertierung nicht angegeben ist, muss der resultierende Ausgabeblock die gleiche Größe wie der Eingabeblock haben.
Das ist also wahrscheinlich der Grund für Ihre Verwirrung. Ja, weil dd
ist entworfen zum Blockieren standardmäßig teilweise read()
s werden 1:1 auf partielles write()
abgebildet s oder sync
d out on tail padding NUL oder space chars to bs=
Größe, wenn conv=sync
angegeben ist.
Das bedeutet, dass dd
ist sicher zum Kopieren von Daten (ohne Risiko einer Beschädigung durch teilweises Lesen oder Schreiben) in jedem Fall außer einem, in dem es willkürlich durch count=
begrenzt wird Argument, weil sonst dd
wird gerne write()
seine Ausgabe in gleichgroßen Blöcken zu denen, in denen seine Eingabe read()
war bis es read()
ist komplett durch. Und selbst dieser Vorbehalt ist nur wahr wenn bs=
angegeben ist oder obs=
ist nicht angegeben, wie der nächste Satz in der Spezifikation besagt:
- Falls
bs=
expr
Operand ist nicht angegeben, oder eine andere Konvertierung alssync
,noerror
, odernotrunc
angefordert wird, wird die Eingabe verarbeitet und in Ausgabeblöcken voller Größe gesammelt bis das Ende der Eingabe erreicht ist.
Ohne ibs=
und/oder obs=
Argumenten kann das egal sein – weil ibs
und obs
sind beide standardmäßig gleich groß. Sie können jedoch explizit werden über die Eingabepufferung durch Angabe verschiedener Größen für entweder und nicht Angabe von bs=
(weil es Vorrang hat) .
Wenn Sie beispielsweise Folgendes tun:
IN| dd ibs=1| OUT
…dann ein POSIX dd
wird write()
in Blöcken von 512 Byte durch Sammeln jedes einzelne read()
Byte in einen einzelnen Ausgabeblock.
Andernfalls, falls doch …
IN| dd obs=1kx1k| OUT
…ein POSIX dd
wird read()
maximal 512 Bytes auf einmal, aber write()
jeden Megabyte großen Ausgabeblock (Kernel erlaubt und ausgenommen möglicherweise den letzten – weil das EOF ist) vollständig durch Sammeln von Eingaben in Ausgabeblöcken in voller Größe .
Aber auch aus der Spezifikation:
count=n
- Nur n kopieren Eingabeblöcke.
count=
wird auf i?bs=
abgebildet Blöcke usw., um eine willkürliche Begrenzung von count=
zu handhaben portabel benötigen Sie zwei dd
s. Am praktischsten geht das mit zwei dd
s besteht darin, die Ausgabe des einen in die Eingabe des anderen zu leiten, was uns sicherlich in den Bereich des Lesens/Schreibens einer speziellen Datei versetzt unabhängig vom ursprünglichen Eingabetyp.
Eine IPC-Pipe bedeutet, dass bei Angabe von [io]bs=
argumentiert, dass Sie, um dies sicher zu tun, solche Werte innerhalb des vom System definierten PIPE_BUF
halten müssen Grenze. POSIX gibt an, dass der Systemkern nur atomisches read()
garantieren muss s und write()
s innerhalb der Grenzen von PIPE_BUF
wie in limits.h
definiert . POSIX garantiert, dass PIPE_BUF
mindestens sein …
{_POSIX_PIPE_BUF}
- Maximale Anzahl von Bytes, die beim Schreiben in eine Pipe garantiert atomar sind.
- Wert:512
…(was auch der voreingestellte dd
ist I/O-Blockgröße) , aber der tatsächliche Wert beträgt normalerweise mindestens 4k. Auf einem aktuellen Linux-System sind es standardmäßig 64 KB.
Wenn Sie also Ihren dd
einrichten Prozesse sollten Sie es auf einem Block Faktor tun basierend auf drei Werten:
- bs =( obs =
PIPE_BUF
oder weniger ) - n =gewünschte Gesamtzahl gelesener Bytes
- count =n / bs
Wie:
yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s
Sie müssen i/o mit dd
synchronisieren um nicht durchsuchbare Eingaben zu behandeln. Mit anderen Worten, machen Sie Pipe-Puffer explizit und sie stellen kein Problem mehr dar. Das ist dd
ist für. Die unbekannte Größe ist hier yes
Puffergröße – aber wenn Sie das für einen bekannten ausschließen Menge mit einem anderen dd
dann kann eine wenig informierte Multiplikation dd
ergeben sicher zum Kopieren von Daten (ohne Risiko der Beschädigung durch teilweises Lesen oder Schreiben) selbst wenn die Eingabe willkürlich mit count=
eingeschränkt wird mit jedem beliebigen Eingabetyp auf jedem POSIX-System und ohne ein einziges Byte zu verpassen.
Hier ist ein Ausschnitt aus der POSIX-Spezifikation:
ibs=
expr
- Geben Sie die Eingangsblockgröße in Bytes mit
expr
an (Standard ist 512) .
- Geben Sie die Eingangsblockgröße in Bytes mit
obs=
expr
- Geben Sie die Ausgabeblockgröße in Bytes mit
expr
an (Standard ist 512) .
- Geben Sie die Ausgabeblockgröße in Bytes mit
bs=
expr
- Stellen Sie sowohl die Eingangs- als auch die Ausgangsblockgröße auf
expr
ein Bytes, ersetztibs=
undobs=
. Wenn keine Konvertierung außersync
,noerror
, undnotrunc
angegeben ist, wird jeder Eingabeblock als einzelner Block in die Ausgabe kopiert, ohne kurze Blöcke zu aggregieren.
- Stellen Sie sowohl die Eingangs- als auch die Ausgangsblockgröße auf
Einiges davon wird hier auch besser erklärt.