====== std.fs.exfat — exFAT lesen ======
''import std.fs.exfat;''
Liest exFAT-Dateisysteme: Bootsektor auswerten, Clusterketten verfolgen, Verzeichnisse durchlaufen, Dateien lesen. **Nur lesend.**
exFAT teilt mit FAT32 den Namen und die Grundidee — Cluster, eine Zuordnungstabelle —, aber **kaum ein Strukturfeld**. Deshalb eine eigene Unit und kein Zweig in [[lyx_-_programmiersprache:units:fs:fat|std.fs.fat]].
→ [[lyx_-_programmiersprache:units:fs:dateisysteme|Die Dateisystem-Sammlung]] · [[lyx_-_programmiersprache:units:fs:fat|std.fs.fat]] · [[lyx_-_programmiersprache:guides:dateisysteme-lesen|Guide: Fremde Dateisysteme lesen]]
----
===== 1. Was anders ist als bei FAT32 =====
* **Sektor- und Clustergröße stehen als Zweierpotenz-Exponent** im Bootsektor, nicht als Zahl: ''9'' heißt 512 Byte, ''3'' heißt 8 Sektoren je Cluster. Wer die Zahlen direkt liest, bekommt eine Sektorgröße von 9 Byte und rechnet ab da alles falsch — **unauffällig**, denn die Adressen bleiben plausibel.
* **Keine feste Wurzel, keine 8.3-Namen.** Namen sind UTF-16 und stehen in eigenen Eintragssätzen.
* **Zusammenhängende Dateien stehen gar nicht in der Tabelle.** Sie tragen das Kennzeichen ''NoFatChain''; ihre Cluster folgen einfach aufeinander, und ein Blick in die FAT liefert dort nichts.
* **Ein Verzeichniseintrag ist ein Satz aus mehreren Einträgen**: Dateieintrag (''0x85''), Stromeintrag (''0xC0'') und so viele Namenseinträge (''0xC1''), wie der Name braucht.
----
===== 2. Konstanten =====
^ Konstante ^ Wert ^ Bedeutung ^
| ''EXFAT_ERR'' | −1 | Lesefehler oder ungültige Struktur |
| ''EXFAT_EOC'' | −2 | Kettenende |
| ''EXFAT_FREI'' | 0 | freier Cluster |
| ''EXFAT_ET_BITMAP'' · ''EXFAT_ET_UPCASE'' · ''EXFAT_ET_LABEL'' | 0x81 · 0x82 · 0x83 | Systemeinträge |
| ''EXFAT_ET_FILE'' · ''EXFAT_ET_STREAM'' · ''EXFAT_ET_NAME'' | 0x85 · 0xC0 · 0xC1 | die drei Teile eines Dateisatzes |
| ''EXFAT_ATTR_READ_ONLY'' … ''EXFAT_ATTR_ARCHIVE'' | 1, 2, 4, 16, 32 | schreibgeschützt, versteckt, System, Verzeichnis, Archiv |
**Das oberste Bit der Eintragsart sagt „in Gebrauch".** Ein gelöschter Eintrag trägt dieselbe Art mit gelöschtem Bit 7 — ''0x05'' statt ''0x85''. Wer nur auf Gleichheit prüft, übersieht gelöschte Einträge nicht, sondern **liest sie mit**.
----
===== 3. Typen =====
pub type ExfatVolume = struct {
fd: int64;
partOffset: int64; // Byte-Versatz des Volumes im Deskriptor
bytesPerSector: int64; // schon aus dem Exponenten gerechnet
sectorsPerClus: int64;
fatOffset: int64; // Sektoren
fatLength: int64; // Sektoren
heapOffset: int64; // Sektoren
clusterCount: int64;
rootCluster: int64;
numFats: int64;
gueltig: bool;
}
pub type ExfatEntry = struct {
attr: int64;
cluster: int64;
size: int64;
isDir: bool;
noFatChain: bool; // Strom liegt zusammenhängend, die FAT ist unbeteiligt
}
pub type ExfatDir = struct {
cluster: int64;
sectorInCl: int64;
entryInSec: int64;
fertig: bool;
sekPuffer: int64;
}
----
===== 4. Volume =====
^ Signatur ^ Zweck ^
| ''ExfatMount(fd: int64, byteOffset: int64): ExfatVolume'' | Bootsektor auswerten und Volume aufbauen |
| ''ExfatValid(vol: ExfatVolume): bool'' | Wurde ein gültiges exFAT gefunden? |
| ''ExfatBytesPerCluster(vol: ExfatVolume): int64'' | Clustergröße in Byte |
| ''ExfatClusterSector(vol: ExfatVolume, cluster: int64): int64'' | Erster Sektor eines Clusters |
| ''ExfatVolumeLabel(vol: ExfatVolume, buf: int64, max: int64): int64'' | Datenträgerkennung; Rückgabe: Zeichenzahl, 0 = keine |
''byteOffset'' kommt üblicherweise aus [[lyx_-_programmiersprache:units:fs:part|''PartByteOffset'']].
----
===== 5. Clusterkette =====
^ Signatur ^ Zweck ^
| ''ExfatNextCluster(vol: ExfatVolume, cluster: int64): int64'' | Nächster Cluster, ''EXFAT_EOC'' am Ende |
Bei Dateien mit ''noFatChain'' wird die Kette **nicht** gebraucht: die Cluster folgen aufeinander. ''ExfatReadFile'' berücksichtigt beide Fälle.
----
===== 6. Verzeichnisse =====
^ Signatur ^ Zweck ^
| ''ExfatDirOpen(vol: ExfatVolume, cluster: int64): ExfatDir'' | Verzeichnis ab diesem Cluster öffnen |
| ''ExfatDirNext(vol, ref d: ExfatDir, ref e: ExfatEntry, nameBuf: int64, nameMax: int64): int64'' | Nächster Eintrag; Name nach ''nameBuf'' |
| ''ExfatDirClose(vol: ExfatVolume, ref d: ExfatDir): void'' | Puffer freigeben |
| ''ExfatFindInDir(vol, dirCluster: int64, name: pchar, ref e: ExfatEntry): int64'' | Eintrag im Verzeichnis suchen |
| ''ExfatFindPath(vol: ExfatVolume, path: pchar, ref e: ExfatEntry): int64'' | Pfad ab der Wurzel auflösen |
**Bitmap, Upcase-Tabelle und Datenträgerkennung werden übergangen** — sie sind keine Dateien. Wer sie mitliefert, hat in jedem Verzeichnislisting drei Geistereinträge.
----
===== 7. Dateien und Prüfsummen =====
^ Signatur ^ Zweck ^
| ''ExfatReadFile(vol, e: ExfatEntry, buf: int64, max: int64): int64'' | Datei in den Puffer lesen |
| ''ExfatSystemStream(vol, art: int64, ref e: ExfatEntry): int64'' | Ersten Cluster und Länge eines Systemstroms (''EXFAT_ET_BITMAP'' oder ''EXFAT_ET_UPCASE'') |
| ''ExfatUpcaseStoredChecksum(vol: ExfatVolume): int64'' | Die im Verzeichnis abgelegte Prüfsumme der Upcase-Tabelle |
| ''ExfatChecksum32(daten: int64, len: int64): int64'' | Prüfsumme nach exFAT-Vorschrift: Rechtsrotation um ein Bit, dann das Byte addieren |
----
===== 8. Beispiel =====
unit main;
import std.fs.part;
import std.fs.exfat;
import std.alloc;
import std.io;
import std.string;
fn main(): int64 {
var fd: int64 := open("/tmp/exfat.img"c, 0, 0);
if (fd < 0) { return 1; }
var vol: ExfatVolume := ExfatMount(fd, 0);
if (!ExfatValid(vol)) { PrintLn("kein exFAT"); close(fd); return 1; }
var name: int64 := alloc(300);
var d: ExfatDir := ExfatDirOpen(vol, vol.rootCluster);
var e: ExfatEntry;
while (ExfatDirNext(vol, d, e, name, 300) > 0) {
PrintLn(StrConcat(name as pchar, StrConcat(" ", IntToStr(e.size))));
}
ExfatDirClose(vol, d);
free(name, 300);
close(fd);
return 0;
}
----
===== 9. Grenzen =====
**Nur lesen** — aus denselben Gründen wie bei FAT, und hier noch etwas mehr: exFAT verlangt beim Schreiben zusätzlich die Pflege der Belegungsbitmap und Prüfsummen über jeden Eintragssatz.
Prüfstand: ''tests/exfat_test.sh'' — **42 Prüfungen** gegen ein erzeugtes Abbild.
Letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; erhoben aus ''std/fs/exfat.lyx'' ([[https://github.com/SEOLizer/LyX-Compiler/issues/1922|#1922]]).
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.