Archiv für den Monat: März 2013

Kopieren eines gesamten Laufwerks über eine DSL-Verbindung

Kürzlich wollte ich ein großes Laufwerk (genauer: ein Logical Volume) von einem lokalen auf einen entfernten Linux-Server kopieren. Problem: Der lokale Server ist nur mittels DSL angebunden, und das Volume ist 20 GB groß …

Da das Kopieren deutlich länger als 24 Stunden dauern würde, musste ich mit mindestens einem DSL-Disconnect rechnen. Somit konnte ich nicht einfach einen Kopierjob starten und das Ding beliebig lange laufen lassen. Andererseits wollte ich mir aber auch nicht die Mühe machen, einen Download-Manager (oder in diesem Fall: Upload-Manager) zu installieren.

Mittels ein paar einfache Linux-Befehlen ließ sich das Problem dann aber doch ganz elegant lösen. Das Kopieren erledigte folgender Befehl:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    dd if=/dev/vg0/foo bs=100M count=1 skip=$i | gzip -c > $file
    md5sum -b $file >> md5.txt
    scp -i foo.key $file remote:/mnt/upload
    rm $file
done

Die Idee: Die 20 GB an Daten werden in 200 Pakete zu je 100 MB zerlegt. Jedes Paket wird mittels dd gelesen, wobei die Option skip dazu dient, an die richtige Stelle zu springen. Die Pakete werden dann gepackt, und zur späteren Kontrolle wird eine MD5-Prüfsumme berechnet. Anschließend kann das Paket zum Server kopiert und lokal gelöscht werden.

Nachdem das Kopieren abgeschlossen war, konnte ich auf dem Server zunächst prüfen, welche Pakete nicht sauber kopiert wurden. Dazu dient der folgende einfache Befehl:

md5sum -c md5.txt

Wie erwartet war genau das Paket kaputt, während dessen Übertragung der DSL-Reconnect erfolgt war. Das Paket konnte ich dann leicht noch einmal übertragen.

Zum Zusammensetzen auf dem Server dient der folgende Befehl:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    gunzip -c $file | dd of=/dev/vg0/foo obs=100M seek=$i
done

Wichtig ist, dass hier seek anstelle von skip verwendet wird, um auf dem Ausgabegerät an die richtige Stelle zu springen. Außerdem darf nicht mit bs gearbeitet werden. Stattdessen kommt obs zum Einsatz. Die Option bs setzt sowohl die Input Block Size (ibs) als auch die Output Block Size (obs). Eine große Input Block Size führt jedoch dazu, dass nur ein Bruchteil der Daten verarbeitet wird, da der gunzip-Befehl nicht auf einen Schlag alle Daten liefern kann. Alternativ hätte man die Daten zunächst lokal entpacken können:

for i in $(seq -w 0 199); do
    file=$i.gz
    echo $file
    gunzip $file
    dd if=$file of=/dev/vg0/foo bs=100M seek=$i
    rm $file
done

Da das andere Verfahren aber ebenso gut funktioniert, habe ich mich für die Lösung entschieden.