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 std.fs.fat.
→ Die Dateisystem-Sammlung · std.fs.fat · Guide: Fremde Dateisysteme lesen
1. Was anders ist als bei FAT32
- Sektor- und Clustergröße stehen als Zweierpotenz-Exponent im Bootsektor, nicht als Zahl:
9heißt 512 Byte,3heiß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 |
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 ''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 (#1922).
