====== 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. → [[lyx_-_programmiersprache:units:fs:dateisysteme|Die Dateisystem-Sammlung]] · [[lyx_-_programmiersprache:units:fs:part|std.fs.part]] · [[lyx_-_programmiersprache:guides:dateisysteme-lesen|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.sh'' — **53 Prüfungen** gegen ein erzeugtes Abbild. Letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; erhoben aus ''std/fs/ext4.lyx'' ([[https://github.com/SEOLizer/LyX-Compiler/issues/1924|#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.