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
}
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 (#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.
