std.fs.ext4 — ext2, ext3 und ext4 lesen

import std.fs.ext4;

Liest die ext-Familie: Superblock und Gruppendeskriptoren auswerten, Inodes lesen, Verzeichnisse durchlaufen, Dateien und symbolische Verweise lesen. Nur lesend.

Anders als FAT und exFAT hat diese Familie eine Inode-Tabelle: der Verzeichniseintrag trägt nur einen Namen und eine Nummer, alles Weitere — Größe, Rechte, Zeiten und der Weg zu den Blöcken — steht im Inode.

Die Dateisystem-Sammlung · std.fs.part · Guide: Fremde Dateisysteme lesen


1. Konstanten

Konstante Wert Bedeutung
EXT4_ERR −1 Lesefehler oder ungültige Struktur
EXT4_ROOT_INO 2 Inode-Nummer des Wurzelverzeichnisses
EXT4_S_IFMT 0xF000 Maske für die Art im mode-Feld
EXT4_S_IFREG · EXT4_S_IFDIR · EXT4_S_IFLNK 0x8000 · 0x4000 · 0xA000 Datei, Verzeichnis, Verweis
EXT4_FL_EXTENTS 0x80000 Inode benutzt Extent-Baum statt Blockliste
EXT4_FL_INLINE_DATA 0x10000000 Daten stehen im Inode selbst
EXT4_FL_ENCRYPT 0x800 verschlüsselt
EXT4_FT_REG · EXT4_FT_DIR · EXT4_FT_LNK 1 · 2 · 7 Art im Verzeichniseintrag

Die Art steht an zwei Stellen: im Verzeichniseintrag (EXT4_FT_*, schnell) und im Inode (EXT4_S_IF*, verbindlich). Bei alten Dateisystemen ohne das Merkmal filetype ist das Feld im Verzeichnis 0 — dann entscheidet der Inode.


2. Typen

pub type Ext4Volume = struct {
  fd:             int64;
  partOffset:     int64;
  blockSize:      int64;
  inodeSize:      int64;
  descSize:       int64;   // 32 oder 64 (Eigenschaft 64bit)
  blocksPerGroup: int64;
  inodesPerGroup: int64;
  firstDataBlock: int64;
  inodeCount:     int64;
  blockCount:     int64;
  featIncompat:   int64;
  gdtBlock:       int64;   // erster Block der Gruppendeskriptoren
  gueltig:        bool;
}

pub type Ext4Inode = struct {
  ino:    int64;
  mode:   int64;
  size:   int64;
  flags:  int64;
  links:  int64;
  isDir:  bool;
  isFile: bool;
  isLink: bool;
  iblock: int64;   // Kopie der 60 Byte aus i_block — vom Aufrufer freizugeben
}

 
Reihenfolge beim Freigeben: Ein Ext4Dir enthält den Inode als Kopie — sein iblock-Zeiger aber nicht, den teilen beide. Wer den ursprünglichen Inode freigibt, während das Verzeichnis offen ist, zieht dem Durchlauf den Boden weg. Also immer erst Ext4DirClose, dann Ext4InodeFree.

3. Volume und Inodes

Signatur Zweck
Ext4Mount(fd: int64, byteOffset: int64): Ext4Volume Superblock lesen, Volume aufbauen
Ext4Valid(vol: Ext4Volume): bool Wurde ein gültiges ext-Dateisystem gefunden?
Ext4ReadInode(vol: Ext4Volume, nr: int64, ref out: Ext4Inode): int64 Inode nr lesen (Wurzel: EXT4_ROOT_INO)
Ext4InodeFree(ino: Ext4Inode): void Gibt die iblock-Kopie frei
Ext4BlockOf(vol, ino: Ext4Inode, logisch: int64): int64 Logischen Block → physischen Block
Ext4VolumeLabel(vol, buf: int64, max: int64): int64 Datenträgerkennung

Ext4BlockOf geht beide Wege: Extent-Baum (ext4, Merkmal EXT4_FL_EXTENTS) und die klassische Blockliste mit einfacher Indirektion (ext2/ext3).


4. Verzeichnisse

Signatur Zweck
Ext4DirOpen(vol: Ext4Volume, ino: Ext4Inode): Ext4Dir Verzeichnis öffnen
Ext4DirNext(vol, ref d: Ext4Dir, ref e: Ext4Entry, nameBuf: int64, nameMax: int64): int64 Nächster Eintrag; Name nach nameBuf
Ext4DirClose(vol: Ext4Volume, ref d: Ext4Dir): void Puffer freigeben
Ext4FindInDir(vol, dir: Ext4Inode, name: pchar): int64 Inode-Nummer eines Namens, oder EXT4_ERR
Ext4FindPath(vol: Ext4Volume, path: pchar): int64 Pfad ab der Wurzel auflösen → Inode-Nummer

5. Dateien

Signatur Zweck
Ext4ReadFile(vol, ino: Ext4Inode, buf: int64, max: int64): int64 Datei in den Puffer lesen
Ext4ReadLink(vol, ino: Ext4Inode, buf: int64, max: int64): int64 Ziel eines symbolischen Verweises

Kurze Verweisziele stehen im Inode selbst (in den 60 Byte von i_block), längere in einem Datenblock — Ext4ReadLink behandelt beide Fälle.


6. Beispiel

unit main;
import std.fs.ext4;
import std.alloc;
import std.io;
import std.string;

fn main(): int64 {
    var fd: int64 := open("/tmp/ext4.img"c, 0, 0);
    if (fd < 0) { return 1; }

    var vol: Ext4Volume := Ext4Mount(fd, 0);
    if (!Ext4Valid(vol)) { PrintLn("kein ext-Dateisystem"); close(fd); return 1; }

    var wurzel: Ext4Inode;
    Ext4ReadInode(vol, EXT4_ROOT_INO, wurzel);

    var name: int64 := alloc(300);
    var d: Ext4Dir := Ext4DirOpen(vol, wurzel);
    var e: Ext4Entry;

    while (Ext4DirNext(vol, d, e, name, 300) > 0) {
        PrintLn(StrConcat(name as pchar, StrConcat("  Inode ", IntToStr(e.ino))));
    }

    Ext4DirClose(vol, d);       // erst das Verzeichnis …
    Ext4InodeFree(wurzel);      // … dann der Inode
    free(name, 300);
    close(fd);
    return 0;
}


7. Grenzen

  • Die doppelt indirekte Blockzuordnung ist nicht umgesetzt und wird gemeldet. Sie beginnt bei (12 + n + n²) Blöcken, was bei 4-KiB-Blöcken erst jenseits von 4 GiB eintritt — still das Falsche zu liefern wäre schlimmer als die Meldung.
  • Nur lesen. Schreiben verlangt hier zusätzlich Journalpflege: ext3 und ext4 sind Journaling-Dateisysteme, und ein Schreiber, der das Journal nicht führt, macht aus einem Absturz einen Datenverlust.
  • Verschlüsselte Inodes (EXT4_FL_ENCRYPT) werden erkannt, aber nicht entschlüsselt.

Prüfstand: tests/ext4_test.sh53 Prüfungen gegen ein erzeugtes Abbild.

Letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; erhoben aus std/fs/ext4.lyx (#1924).

Prüflauf 2026-09-05 mit doku-pruefer.py (Repo-Compiler lyxc 1.2.2A): 7 Vollprogramme der Sammlung, 7 übersetzen, 0 echte Fehler; 53 Aufrufe gegen die pub fn-Signaturen gehalten, 0 Abweichungen.