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