====== 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.