Wer Backups macht, ist nur zu faul zum Neu-Aufsetzen eines Systems. Und so erstelle auch ich immer wieder mal in loser Folge Images von meinen SD-Cards, die ich auf meinen Raspberries zu verschiedenen Produktiv-Zwecken im Einsatz habe. Nun ist es mir jedoch schon mehrfach vorgekommen, dass z. B. ein Image einer 8-GB-Karte nicht auf eine andere (neue) 8-GB-Karte passt, weil auf dieser einige wenige Byte an Speicherplatz fehlen (insbesondere SDHC-Karten von Intenso und CnMemory sind häufig von wenigen kB bis mehreren MB kleiner als ihre Kollegen von z. B. SanDisk oder Transcend). Nachdem ich kürzlich eine neue Karte nicht mit dem Image eines dringend benötigten Backups beschreiben konnte, stand ich erst mal blöd da. Speicherkarten sind bei mir nicht gerade Kartonware 😉
Nun habe ich aber einen Weg gefunden, wie man unter Linux mit dem eigentlich genialen Partitionierungstool GParted ein SD-Card-Image, das ich z. B. mit dd (Linux) oder Win32DiskImager erzeugt habe, verkleinern kann. Eine Datenpartition ist ja in den seltensten Fällen bis zum letzten Bit mit Nutzerdaten aufgefüllt, so dass sich diese problemlos verkleinern lässt, damit sie auf eine neue SD-Karte passt. Nun ist es jedoch so, dass GParted nur auf physische Geräte (Festplatten, Memory-Cards usw.) schreiben kann, nicht jedoch auf Image-Dateien. Wie reduziert man also Image-Dateien bzw. die darin enthaltenen Partitionen?
Um Image-Dateien und die darin enthaltenen Partitionen zu verkleinern, braucht man nur drei Tools, die in den meisten Linux-Distrtibutionen bereits enthalten sind: 1. GParted, 2. fdisk und 3. truncate. Wie gesagt – GParted kann nur auf physischen Geräten (Devices) arbeiten, nicht jedoch auf Images. Deshalb müssen wir als Erstes ein Device für das Image erstellen. Dafür nutzen wir die Loopback-Funktion unter Linux. Falls diese noch nicht aktiviert sein sollte, tun wir dies mit
sudo modprobe loop
Nun kann man mit
sudo losetup -f
das Device /dev/loop0 bereitstellen. Nun erstellen wir aus der Imagedatei.img das Device /dev/loop0:
sudo losetup /dev/loop0 Imagedatei.img
Soooo – das Device /dev/loop0 ist somit schon mal vorhanden, es enthält den Gesamtinhalt von Imagedatei.img. Wir wollen jedoch die einzelnen Partitionen bearbeiten, die darin enthalten sind. Also geben wir diese dem Kernel wie folgt bekannt:
sudo partprobe /dev/loop0
Dieser Befehl gibt uns/dev/loop0p2
zurück – die zweite verfügbare Partition in der Imagedatei.img. Dies ist das Device (= Partition), mit dem GParted arbeiten kann (die /dev/loop0p1 ist die kleine FAT16-Partition, die der Raspberry Pi zum Booten benötigt – die belassen wir so, wie sie ist).
Nun geht es endlich daran, die Partition zu verkleinern. Dazu starten wir GParted wie folgt:
sudo gparted /dev/loop0
Hier klicken wir oben auf die Partition /dev/loop0p2 (Aktivieren) und anschließend auf den Button „Verändern/Verschieben“ (die Schaltfläche, die so ähnlich aussieht wie ein Play-Button). In dem Fenster, das nun erscheint, sehen wir einen Balken, der die Größe der Partition sowie den tatsächlich genutzen Speicherplatz anzeigt. Den rechten Rand des Balkens kann man jetzt mit der Maus nach links ziehen – bis zur gewünschten reduzierten Partitionsgröße. Anschließend klickt man auf „Ändern/Verschieben“. Nun sind wir wieder im Hauptfenster (s. o.) und siehe da: Die Partition ist jetzt kleiner und es wird der neu gewonnene freie Speicherplatz angezeigt – so sieht es zumindest aus. Jetzt noch oben in der Button-Leiste auf den Haken klicken – und los geht’s. Wenn alles gut geht (d. h. wenn die Imagedatei keine Fehler enthält), wird jetzt ca. 2 – 3 Minuten lang aufgeräumt und umsortiert. Sobald diese Prozedur abgeschlossen ist, können wir GParted schließen. Das Loopback-Device /dev/loop0 brauchen wir nicht mehr, also geben wir das Device wieder frei mit
sudo losetup -d /dev/loop0
Nun ist bereits viel erreicht, aber wenn wir uns die Imagedatei.img mit ls -l ansehen, so sehen wir, das sie immer noch genauso groß ist wie zuvor. Wir müssen noch den durch die Partitionsverkleinerung freigewordenen Speicherplatz freigeben, damit das Image auf eine neue, kleinere SD-Karte passt. Dazu müssen wir wissen, auf welchen Speicherblöcken die Partition beginnt und endet. Hierfür benutzen wir fdisk:
fdisk -l Imagedatei.img
Hier sehen wir in etwa Folgendes:
Hier achten wir auf das Gerät mit der Bezeichnung Imagedatei.img2 in der untersten Zeile. Im Bild endet es im Sektor 9404415 (abhängig jeweils davon, um wieviel die Partition in GParted verkleinert wurde). Oben sehen wir, dass jeder Sektor 512 Byte groß ist. Das Beispiel im Bild zeigt, dass unsere (verkleinerte) Partition an Byte 9404415 x 512 endet. Alles, was dahinter kommt, ist unser freier und ungenutzter Speicherplatz, um den wir die Imagedatei.img verkleinern wollen.
Dazu nutzen wir abschließend das Tool truncate. Als Parameter benötigen wir wiederum den End-Block der Partition 2, in diesem Beispiel also 9404415 und die Blockgröße 512 Byte. In der Praxis sieht der Befehl so aus:
truncate –size=$[(9404415+1)*512] Imagedatei.img
Nun ist das Ziel erreicht: Die Imagedatei hat jetzt die reduzierte Größe und wir können das Image auf eine neue SD-Card schreiben. Das mache ich mit
sudo dd if=./Imagedatei.img of=/dev/mmcblk0 bs=1M
Überzeugen Sie sich zuvor mit „df“, ob /dev/mmcblk0 wirklich Ihr SD-Card-Device ist!