Das Programm Debugfs gewährt nicht nur einen tiefen Einblick in die Welt des Ext4-Filesystems, es hilft auch beim Retten gelöschter Dateien und in der digitalen Forensik. Gehen Sie daher mit auf eine kleine Spurensuche im Ext4-Filesystem.
Als Datei- oder Filesystem bezeichnet man die Organisationsstruktur, die Daten hierarchisch über Verzeichnisse und Dateien verwaltet. Linux unterstützt einen großen Reigen solcher Dateisysteme – vom klassischen FAT über NTFS bis hin zu spezifischen Flash-Filesystemen wie JFFS2 oder Btrfs. Allerdings gibt es kein “one size fits all” (Tabelle “Kenndaten aktueller Dateisysteme”).
FAT beispielsweise ist sehr einfach gestrickt, benötigt damit nur ein wenig Schmalspur-Code und wird – da es uralt ist – von jedem Smartphone, Smarthome-Gerät und selbstverständlich von jedem Rechner unterstützt. Dafür kennt es keine Zugriffsrechte, kann nicht mit Dateien größer als 4 GByte umgehen und unterliegt noch einigen weiteren Einschränkungen. Microsofts NTFS oder Apples APFS sind wiederum auf die jeweiligen Betriebssysteme abgestimmt.
Mit Ext4 wurde auch Linux ein Filesystem auf den Leib geschneidert. Es kann mit riesigen Datenmengen sowie diversen Zugriffsrechten umgehen und bietet schnelle Reparaturmöglichkeiten. Darüber hinaus fallen die Anpassungen [1] an die interne Kernel-Schnittstelle (Virtual File System, VFS) minimal aus, sodass es ausgesprochen performant arbeitet. Aus diesen Gründen kommt es heutzutage immer noch häufig als Standard-Filesystem zum Einsatz. Und es gibt noch einen Vorteil: Mit Debugfs existiert ein Werkzeug, über das Sie die Technik hinter Ext4 erkunden und das Dateisystem forensisch untersuchen können.
|
Eigenschaft |
FAT32 |
Ext4 (Linux) |
NTFS (Windows) |
APFS (Apple) |
ZFS (Solaris/Linux/BSD) |
Btrfs |
|---|---|---|---|---|---|---|
|
max. Dateigröße |
4 GiByte |
16 TiByte (4K-Blöcke) |
256 TByte |
8 EiByte |
16 EiByte |
16 EiByte |
|
max. Volume-Größe |
2 TiByte |
1 EiByte |
256 TByte |
8 EiByte |
256 ZiByte |
16 EiByte |
|
Dateinamen |
8.3 (Legacy), 255 LFN |
255 UTF-8 |
255 Unicode 255 Unicode |
255 Unicode 255 UTF-8 |
||
|
Journaling |
nein |
ja (optional) |
ja |
ja |
ja |
ja |
|
Verschlüsselung |
nein |
ja (fscrypt) |
ja (BitLocker) |
ja (FileVault) |
ja (native) |
ja |
|
Betriebssystem |
alle |
Linux |
Windows, Linux |
macOS/iOS |
Solaris, Linux, FreeBSD |
Linux |
|
typische Nutzung |
USB-Sticks, SD-Karten |
Linux-Systempartitionen |
Windows-Systempartitionen |
macOS/iOS-Laufwerke |
Enterprise |
Linux |
Lese-Schreib-Schwäche
SSDs, Festplatten, USB-Sticks und SD-Karten gehören zur Gruppe der sogenannten Hintergrundspeicher (Secondary Storage). Auf solche Speichermedien kann man ausschließlich blockweise zugreifen. Oder anders ausgedrückt: Von einer SSD kann man immer nur einen Block, beispielsweise 512 Byte oder 4 KByte, lesen oder schreiben.
Selbst wenn man bloß ein einzelnes Byte benötigt, muss man den kompletten Block, in dem sich das gesuchte Byte befindet, in den Speicher wuchten. Erst dann kann man auf das Byte zugreifen. Beim Schreiben wird es noch etwas komplizierter: Da man kein einzelnes Byte schreiben kann, muss man erst den ganzen Block einlesen, in dem das Byte liegt, dann in der Kopie das Byte ändern und schließlich den Block zurückschreiben.
Das Betriebssystem adressiert die Speichermedien auf unterster Systemebene ebenfalls blockweise. Die laufenden Applikationen greifen jedoch über Dateinamen auf die Daten zu. Das Betriebssystem muss folglich zu einem gegebenen Namen die zugehörigen Blöcke herausfinden beziehungsweise adressieren. Obendrein besteht eine Datei nicht allein aus Daten, sondern auch aus Metadaten.
Meta-Ebene
Zu den Metadaten gehören unter anderem die Dateilänge, die Zugriffsrechte, der Dateityp und fünf Zeitstempel: der Zeitpunkt, an dem die Datei angelegt wurde (crtime), der Zeitpunkt, an dem auf die Datei das letzte Mal zugegriffen wurde (atime), der Zeitpunkt, an dem die Daten verändert wurden (mtime), der Zeitpunkt, an dem die Metadaten angepasst wurden (ctime) und der Zeitpunkt, an dem eine Datei gelöscht wurde (dtime). Falls Sie vom Löschzeitpunkt noch nichts gehört haben: Mit ihm kommt man im Alltag selten in Kontakt. Die herkömmlichen Shell-Kommandos wie »ls« oder »stat« unterschlagen diese Zeit konsequent, da das zugehörige Objekt – die Datei – ja im Prinzip nicht mehr existiert.
Per Debugfs können Sie alle Metadaten beispielsweise für die Datei »/etc/passwd« anzeigen lassen. Dazu benötigen Sie zusätzlich den Namen der Gerätedatei von der Partition, auf der die Datei liegt. »/etc/passwd« residiert typischerweise im Root-Filesystem, das im Verzeichnis »/« eingehängt ist. Die zugehörige Gerätedatei verrät der Befehl »lsblk«.
Himbeerteilchen
Auf einem Raspberry Pi finden Sie auf der SD-Karte typischerweise zwei Partitionen. Die erste Partition nutzt ein FAT-Filesystem und enthält alle zum Hochfahren (Booten) notwendigen Daten. Die zweite Partition stellt das eigentliche System bereit (Root-Filesystem). Die Gerätedatei »/dev/mmcblk0« repräsentiert die gesamte SD-Karte, »/dev/mmcblk0p1« die Boot-Partition mit dem FAT-Filesystem und »/dev/mmcblk0p2« die Root-Partition mit dem Ext4-Dateisystem. Auf einem PC hängt der Name der Gerätedatei von der technischen Anbindung des Massenspeichers ab. Typische Namen sind beispielsweise »/dev/sda« oder »/dev/nvme0n1« für den kompletten Datenträger und »/dev/sda1« oder »/dev/nvme0n1p3« für die Root-Partition.
Listing 1 zeigt den kompletten Debugfs-Befehl sowie die Metainformationen zur Datei »/etc/passwd« auf einem Raspberry Pi. Beim Aufruf von Debugfs verraten Sie dem Tool zunächst die Gerätedatei. Es folgt der Parameter »-R« mit einem sogenannten Kommando, das Debugfs die durchzuführende Aktion verrät.
Im Beispiel liefert das Werkzeug Informationen (»stat«) zur Datei »/etc/passwd«. Vorangestellt sorgt »PAGER=cat« schließlich dafür, dass »cat« die von Debugfs gelieferten Ausgaben auf dem Bildschirm anzeigt. Wenn Sie den Debugfs-Befehl auf Ihrem Rechner ausführen, weichen die Zahlenwerte in der Ausgabe ab – schon alleine wegen der unterschiedlichen Zugriffszeiten.
Listing 1
Inode der Datei /etc/passwd
quade@fourtytwo:~ $ sudo su root@fourtytwo:/home/quade# PAGER=cat debugfs /dev/mmcblk0p2 -R "stat /etc/passwd" debugfs 1.44.5 (15-Dec-2018) Inode: 137406 Type: regular Mode: 0644 Flags: 0x80000 Generation: 116835765 Version: 0x00000000:00000001 User: 0 Group: 0 Project: 0 Size: 1719 File ACL: 0 Links: 1 Blockcount: 8 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x68134853:2b4f9550 -- Thu May 1 11:09:23 2025 atime: 0x68134853:28ed3bcc -- Thu May 1 11:09:23 2025 mtime: 0x68134853:28ed3bcc -- Thu May 1 11:09:23 2025 crtime: 0x68134853:28ed3bcc -- Thu May 1 11:09:23 2025 Size of extra inode fields: 32 Inode checksum: 0x664ea5a9 EXTENTS: (0):590292
No Name
Die Metadaten der Dateien lagern in eigenen Speicherbereichen auf dem Datenträger. Diese Inodes (kurz für Index Nodes) sind durchnummeriert. In Listing 1 stecken die Metadaten im Inode 137 406. Es mag erst einmal überraschen und ist für die Wiederherstellung von Dateien gut zu wissen: Der Name einer Datei gehört nicht zu den Metadaten und findet sich folglich nicht im Inode!
Das Attribut Links in Listing 1 informiert zudem, wie häufig auf die Datei referenziert wird, oder anders ausgedrückt, unter wie vielen (unterschiedlichen) Namen die Datei im Filesystem zu sehen ist. Ein und dieselbe Datei kann dementsprechend unter mehreren Namen auftauchen.
Den Ablageort der Daten finden Sie in der Rubrik EXTENTS, die direkt die entsprechenden Blocknummern auflistet. Im Beispiel gibt es nur einen Block mit der Nummer 590 292. Das Debugfs-Kommando »block_dump 590292« zeigt als Hexdump den Inhalt des Blocks, also den Inhalt der Passwd-Datei. Angeben können Sie diesen Befehl wieder hinter dem Parameter »-R«.
Alles ist eine Datei
Wie aber findet das System den Inode, der zur Passwd-Datei gehört? Dazu rufen Sie sich zunächst ins Gedächtnis, dass gemäß der Unix-Philosophie alles eine Datei ist. Das gilt gleichermaßen für Verzeichnisse. Diese Verzeichnisdateien bestehen etwas vereinfachend lediglich aus einer Liste von Namen und deren Inodes. Der Inode 2 enthält dabei grundsätzlich stets die Informationen zum Wurzelverzeichnis einer Partition.
Um die Daten des Inodes zu lesen, geben Sie im Debugfs-Kommando »stat« die Nummer in spitzen Klammern an – für das Wurzelverzeichnis also »stat <2>«. Das Ergebnis könnte beispielsweise enthüllen, dass die Verzeichniseinträge (Abbildung 1) auf der untersuchten Maschine in Block 9000 stecken. Daraufhin liest man besagten Block 9000 aus und interpretiert die darin abgelegten Einträge.
Die könnten dann etwa verraten, dass der Inode des gesuchten Eintrags »etc« (von »/etc/passwd«) die Nummer »01 fb 02«, also dezimal 129 794 besitzt. Diesem Inode vom Typ Verzeichnis (Directory, Dirent) lässt wiederum entnehmen, dass sich die zugehörigen Daten in Block 532 433 befinden. Und tatsächlich zeigt ein Blockdump den Eintrag für »passwd« mit der zugehörigen Inode-Nummer 137 406 an, hexadezimal »02 be 18«.

Abbildung 1: Die Verzeichniseinträge liefern mehrere wertvolle Informationen, wie etwa den Dateinamen und den Dateityp.
Auf diese Weise hangelt sich also Linux vom Root-Verzeichnis hierarchisch durch die Unterverzeichnisse (Abbildung 2) im Dateisystem, bis es beim gesuchten Objekt, beispielsweise der Datei »passwd«, angekommen ist. Da die Inode-Nummer jedoch nicht mir der Blocknummer identisch ist, stellt sich die Frage, in welchem Block auf dem Speichermedium oder genauer der Partition die Daten von Inode 2 lagern.
Superkraft
Die Antwort dazu gibt der sogenannte Superblock. Er ist grundsätzlich der erste Block auf dem Massenspeicher – der Block mit der Nummer Null. Ihn liest Linux beim Einhängen (mounten) ein. Er enthält sämtliche notwendigen Basisinformationen zum Filesystem, die wiederum Debugfs mit dem Kommando »show_super_stats« liefert. Auch das geben Sie wieder hinter dem Parameter »-R« an.
Interaktiver Modus
Wenn Sie den Parameter »-R« weglassen, öffnet Debugfs eine eigene Kommandozeile. In diesem interaktiven Modus können Sie alle Befehle direkt absetzen. Das ist vor allem dann nützlich, wenn Sie mehrere, aufeinander aufbauende Kommandos aufrufen möchten.
Wenn Sie Debugfs im interaktiven Modus verwenden, müssen Sie allerdings auf eine kleine Stolperfalle achtgeben: Ist das untersuchte Dateisystem in aktiver Verwendung, zeigt Debugfs Änderungen dank diverser Caches meist nicht an. Nutzen Sie deshalb bei Dateisystemoperationen möglichst den nicht-interaktiven Modus (Option »-R«). In jedem Fall verlassen Sie den interaktiven Modus über »quit«.
Listing 2 veranschaulicht eine gekürzte Ausgabe: Neben den Eigenschaften (Features) des Filesystems und des Typs (Linux Ext4) sind die Angaben Block count und Inode count interessant. Bei einem Block count von 1 751 040 und Blöcken mit 4 KByte Größe ergibt sich eine Kapazität von 7 004 160 KByte, also gute 7 GByte. Inode count definiert, wie viele Dateien auf dem Datenträger existieren können, hier maximal 438 048.
Der Ausgabe zufolge besitzt der erste verfügbare Inode die Nummer 11. Tatsächlich sind die ersten zehn Inodes bereits belegt: Inode 0 ist nonexistent und ungültig. 1 ist für Bad Blocks reserviert, 2 enthält das Wurzelverzeichnis (»/«), während 3 bis 10 systeminternen Zuständen vorbehalten bleiben.
Beispielsweise repräsentiert Inode 8 das Journal – dazu gleich mehr. Des Weiteren ist jeder Inode immer 256 Byte groß. Die Inode-Tabelle merkt sich sämtliche Inodes und ist wiederum Teil der Blockgruppe (Abbildung 3). Der Superblock offenbart, dass beispielsweise die Inode-Tabelle ab Block 888 in der Partition liegt. Der Inode 2 wartet demnach im Block 888 ab Adresse 512 (2 x 256 Byte).
Listing 2
Der Superblock (Ausschnitt)
debugfs: show_super_stats Filesystem volume name: rootfs Last mounted on: / Filesystem UUID: 4bd4e068-37df-44c4-ac7a-5f9863232451 Filesystem magic number: 0xEF53 Filesystem revision #: 1 (dynamic) Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum Filesystem flags: signed_directory_hash Default mount options: user_xattr acl Filesystem state: clean Errors behavior: Continue Filesystem OS type: Linux Inode count: 438048 Block count: 1751040 Reserved block count: 87552 Overhead blocks: 51574 Free blocks: 1123001 Free inodes: 370318 First block: 0 Block size: 4096 Fragment size: 4096 Group descriptor size: 64 Reserved GDT blocks: 854 Blocks per group: 32768 ...

Abbildung 3: Zur Blockgruppe gehört unter anderem auch die Inode-Tabelle, die in diesem Fall ab Block 888 wartet.
Organisationstalent
In jedem Ext4-Dateisystem schließen sich nach dem Superblock die Gruppenbeschreibungen (Abbildung 4) an. Sie setzen sich aus einer Block- und einer Inode-Bitmap, aus der erwähnten Inode-Tabelle und aus den zugehörigen Datenblöcken zusammen.

Abbildung 4: Das Ext4-Dateisystem besitzt einen fest vorgegebenen Aufbau: Am Anfang steht der Superblock, dem Blockgruppen folgen.
Im Gegensatz zu den älteren Ext2- und Ext3-Versionen des Dateisystems setzt Ext4 eine blockbasierte Adressierung über Extents ein. Dahinter verbergen sich zusammenhängende Blockbereiche, die jeweils ein Startblock und die Blockanzahl beschreiben. Die Bündelung reduziert den Overhead bei großen Dateien. Das Dateisystem eines Raspberry Pis könnte zum Beispiel in der Summe 54 Blockgruppen enthalten, die jeweils 32 768 Blöcke umfassen.
Wegzaubern
Beim Löschen einer Datei überschreibt das System nicht zwangsläufig die Daten auf dem Hintergrundspeicher. Vielmehr kennzeichnet das System lediglich die zugehörigen Datenblöcke und den Inode als verfügbar. Bei einer modernen SSD mit sogenannter Trim-Funktion lässt sich eine solche Datei dennoch kaum retten. Dort kommt beim Löschen der Systemcall »unlink« zum Zug.
Er setzt im Inode die »i_dtime« auf die aktuelle Zeit und den Link Count auf Null – zur Erinnerung: Der Link Count gibt an, unter wie vielen Namen der Inode referenziert wird. Anschließend markiert das System die Extents als frei. Benötigt das Betriebssystem einen neuen Inode, recycelt es einen mit einer »i_dtime« größer Null.
Debugfs kennt das Kommando »lsdel«, das gelöschte Dateien hervorkramt. Allerdings zeigt das Tool die zugehörigen Inodes nur an, wenn es passende Blocknummern (Extents) und damit eine gute Chance für eine Wiederherstellung gibt. Das ist typischerweise lediglich kurzzeitig nach dem Löschen der Fall. Mit dem Kommando »undel« und unter Angabe eines Dateinamens lässt sich die Datei dann wiederherstellen.
Filesystem manipulieren
Übrigens können Sie eine Datei auch per Debugfs löschen. Listing 3 enthält ein Beispiel dazu: Der erste Befehl legt die Datei »/root/foo« an, der zweite forciert das Schreiben der Daten auf den Hintergrundspeicher. Danach liest Debugfs via »stat« die Metadaten aus, löscht mit dem Kommando »rm« die Datei und stellt sie zum Schluss per »undel« wieder her. Debugfs liefert dabei außerdem den Löschzeitpunkt.
Listing 3
Löschen und Wiederherstellen einer Datei
root@fourtytwo:~# echo "hello, world" > /root/foo root@fourtytwo:~# sync root@fourtytwo:~# debugfs /dev/mmcblk0p2 -R "stat /root/foo" debugfs 1.44.5 (15-Dec-2018) Inode: 261802 Type: regular Mode: 0644 Flags: 0x80000 Generation: 3412351752 Version: 0x00000000:00000001 User: 0 Group: 0 Project: 0 Size: 13 File ACL: 0 Links: 1 Blockcount: 8 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 atime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 mtime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 crtime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 Size of extra inode fields: 32 Inode checksum: 0xda5ba8bf EXTENTS: (0):1109876 root@fourtytwo:~# debugfs -w /dev/mmcblk0p2 -R "rm /root/foo" debugfs 1.44.5 (15-Dec-2018) root@fourtytwo:~# debugfs /dev/mmcblk0p2 -R "lsdel" debugfs 1.44.5 (15-Dec-2018) Inode Owner Mode Size Blocks Time deleted 261802 0 100644 13 1/ 1 Thu May 1 18:31:20 2025 1 deleted inodes found. root@fourtytwo:~# debugfs /dev/mmcblk0p2 -R "stat <261802>" debugfs 1.44.5 (15-Dec-2018) Inode: 261802 Type: regular Mode: 0644 Flags: 0x80000 Generation: 3412351752 Version: 0x00000000:00000001 User: 0 Group: 0 Project: 0 Size: 13 File ACL: 0 Links: 0 Blockcount: 8 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 atime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 mtime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 crtime: 0x6813afcd:209b08a4 -- Thu May 1 18:30:53 2025 dtime: 0x6813afe8:(209b08a4) -- Thu May 1 18:31:20 2025 Size of extra inode fields: 32 Inode checksum: 0xc475ca73 EXTENTS: (0):1109876 root@fourtytwo:~# debugfs -w /dev/mmcblk0p2 -R "undel <261802> /root/foo.recovered" debugfs 1.44.5 (15-Dec-2018) root@fourtytwo:~# cat /root/foo.recovered hello, world root@fourtytwo:~#
Möchten Sie eine Datei unwiederbringlich vernichten, greifen Sie zu »shred«. Der Befehl überschreibt die Daten mehrfach, bevor es die Datei durch Aufruf von »unlink« als gelöscht kennzeichnet. Modifikationen an Zeitstempeln nehmen Sie über das Kommando »set_inode_field« vor.
Möchten Sie eine Datei per Debugfs löschen, Zeitstempel anpassen oder andere Änderungen am Dateisystem vornehmen, rufen Sie Debugfs wie in Listing 3 mit der Option »-w« auf. Das Werkzeug öffnet dann das Dateisystem im Read-Write-Mode. Alle angesprochenen und die im Alltag wichtigsten Debugfs-Kommandos fasst noch einmal die gleichnamige Tabelle zusammen.
|
Befehl |
Beschreibung |
|---|---|
|
»show_super_stats« |
Superblock anzeigen |
|
»icheck <block>« |
zeigt den Inode bei einer gegebenen Blocknummer |
|
»ncheck <inode>« |
findet Dateiname bei gegebenem Inode |
|
»ls -l <dir>« |
listet Verzeichnisinhalte mit Inodes auf |
|
»stat <file>« |
zeigt Inode-Informationen |
|
»rm <file>« |
löscht <file> |
|
»lsdel« |
zeigt gelöschte Dateien |
|
»undel <inode> <newname>« |
stellt eine gelöschte Datei unter dem Namen <newname> wieder her |
Zu Protokoll
Unter den vielen weiteren Eigenschaften des Ext4-Dateisystems sticht das Journaling (Abbildung 5) heraus. Es protokolliert alle Schreibzugriffe. Bei einem Stromausfall oder bei einem unerwarteten Systemabsturz mit sehr wenigen Aktionen lässt sich der Datenträger dadurch wieder in einen konsistenten Zustand versetzen. Im standardmäßig genutzten Journaling-Modus »ordered« erfasst Ext4 nur die Modifikationen an den Metadaten. Das eigentliche Journaling läuft in fünf Schritten ab.
Zunächst ändert das System die Metadaten in den Hauptspeicherkopien. Beim Anlegen einer neuen Datei reserviert und beschreibt es beispielsweise einen Inode und weist die benötigten Speicherblöcke (Inode-Bitmap, Inode, Block-Bitmap) zu. Im nächsten Schritt hält es diese Daten im Journal auf dem Speichermedium fest. Als Drittes schreibt das System die eigentlichen Daten auf das Speichermedium und aktualisiert die Metadaten auf dem Speichermedium. Ist das erledigt, markiert es den Eintrag im Journal so, dass er mit einem folgenden Zugriff wieder überschrieben wird.
Verwechslungsgefahr
Der Name Debugfs kommt bei Linux in zwei komplett unterschiedlichen Anwendungen vor: Debugfs bezeichnet einmal das hier gezeigte Programm, um ein Ext4-Dateisystem zu analysieren und zum anderen ein eigenständiges Dateisystem, um mit dem Kernel kleinere und größere Datenmengen auszutauschen. Dazu an dieser Stelle demnächst mehr.
Fazit
Debugfs gewährt schnell einen tieferen Einblick in den Aufbau eines Ext4-Dateisystems. Mit seiner Hilfe lernen Sie die Geheimnisse des Filesystems kennen, stellen im Ernstfall versehentlich gelöschte Dateien wieder her und kommen Fehlerquellen auf die Spur. Es lohnt sich folglich, Debugfs und seine Kommandos im Hinterkopf zu behalten. (tsc)
Infos
- Linux-Kernel Dokumentation: ext4 Data Structures and Algorithms: https://docs.kernel.org/filesystems/ext4/







