std.fs.fat — FAT12/16/32 lesen
import std.fs.fat;
Liest FAT-Dateisysteme aus einem Abbild oder von einem Blockgerät: Bootsektor auswerten, Clusterketten verfolgen, Verzeichnisse durchlaufen, Dateien lesen. Nur lesend — geschrieben wird nicht.
Die Unit arbeitet auf einem offenen Deskriptor und einem Byte-Versatz; ob dahinter eine Datei, eine Partition oder ein ganzes Gerät liegt, ist ihr gleich.
→ Die Dateisystem-Sammlung · std.fs · Units-Übersicht · Lyx OS: IOFS
1. Konstanten
| Konstante | Wert | Bedeutung |
|---|---|---|
FAT_ERR | −1 | Lesefehler oder ungültige Struktur |
FAT_EOC | −2 | Kettenende (end of chain) |
FAT_FREI | 0 | freier Cluster |
FAT_TYPE_12 · FAT_TYPE_16 · FAT_TYPE_32 | 12 · 16 · 32 | erkannter Typ in FatVolume.fatType |
FAT_ATTR_READ_ONLY … FAT_ATTR_ARCHIVE | 1, 2, 4, 8, 16, 32 | Attributbits: schreibgeschützt, versteckt, System, Datenträgerkennung, Verzeichnis, Archiv |
FAT_ATTR_LONG_NAME | 15 | Kennung eines Namensteilstücks (VFAT) |
2. Typen
pub type FatVolume = struct {
fd: int64; // offener Deskriptor
lbaOffset: int64; // Byte-Versatz des Volumes darin
bytesPerSector: int64;
sectorsPerClus: int64;
reservedSectors: int64;
numFats: int64;
rootEntCount: int64; // 0 bei FAT32
totalSectors: int64;
fatSize: int64; // Sektoren je FAT-Kopie
rootCluster: int64; // nur FAT32
fatType: int64; // 12, 16, 32 — oder FAT_ERR
firstDataSector: int64;
rootDirSectors: int64;
countOfClusters: int64;
}
pub type FatEntry = struct {
attr: int64;
cluster: int64; // erster Cluster; 0 = leere Datei
size: int64; // Bytes; bei Verzeichnissen 0
isDir: bool;
isVolume: bool;
}
pub type FatDir = struct { … } // laufender Zustand beim Durchlaufen
Der Name steht nicht imFatEntry.FatDirNextschreibt ihn in einen Puffer des Aufrufers. Ein Zeiger im Struct wäre eine Besitzfrage, die niemand beantworten will: wer gibt ihn frei, und was passiert beim nächsten Eintrag?
3. Volume
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
FatMount(fd, lbaOffset) | FatVolume | Bootsektor lesen, Typ bestimmen, Geometrie füllen |
FatVolumeValid(vol) | bool | wurde ein FAT erkannt? |
FatBytesPerCluster(vol) | int64 | bytesPerSector · sectorsPerClus |
FatClusterSector(vol, cluster) | int64 | erster Sektor eines Clusters |
Cluster 0 und 1 gibt es nicht — die beiden ersten Plätze der FAT tragen den Medientyp.
4. Clusterketten
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
FatNextCluster(vol, cluster) | int64 | nächster Cluster, FAT_EOC am Ende, FAT_ERR bei Lesefehler |
FatChainLength(vol, start) | int64 | Länge der Kette in Clustern |
Der Zugriff auf die FAT unterscheidet sich je Typ: FAT12 hat 12-Bit-Einträge, die sich Bytes teilen — jeder zweite Eintrag liegt über eine Bytegrenze hinweg. FAT16 und FAT32 lesen glatte 16 bzw. 28 Bit.
5. Verzeichnisse
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
FatDirOpen(vol, cluster) | FatDir | Verzeichnis zum Durchlaufen öffnen; cluster = 0 meint die Wurzel |
FatDirNext(vol, ref d, ref e, nameBuf, nameMax) | int64 | 1 = Eintrag geliefert, 0 = Ende, FAT_ERR = Lesefehler. nameMax muss ≥ 13 sein |
FatDirClose(vol, ref d) | void | Sektorpuffer freigeben |
FatFindInDir(vol, dirCluster, name, ref e) | int64 | einen Namen in einem Verzeichnis suchen |
FatFindPath(vol, path, ref e) | int64 | Pfad wie ORDNER/UNTER/DATEI.TXT auflösen; / und \\ gelten beide |
Lange Namen (VFAT) werden aus den vorangehenden Teilstücken zusammengesetzt; fehlen sie, gilt der 8.3-Name. Die Datenträgerkennung wird bei der Suche übersprungen — sie trägt ihren Namen im Namensfeld und würde sonst gelegentlich treffen.
Zeichen außerhalb von Latin-1 werden zu?. Ein langer Name steht als UCS-2 im Verzeichnis; die Unit setzt bei gesetztem hohem Byte ein Fragezeichen. Das ist die ehrliche Variante — das hohe Byte abzuschneiden wäre schlimmer: aus „Grüße„ würde stillschweigend etwas anderes.
6. Dateien und Zeitstempel
| Funktion | Rückgabe | Bedeutung |
|---|---|---|
FatReadFile(vol, e, buf, max) | int64 | Datei in den Puffer lesen, höchstens max Bytes; liefert die gelesene Zahl |
FatDateYear(datum) · FatDateMonth · FatDateDay | int64 | FAT-Datum zerlegen (Jahr ab 1980) |
FatTimeHour(zeit) · FatTimeMinute · FatTimeSecond | int64 | FAT-Zeit zerlegen; Sekunden in Zweierschritten |
7. Beispiel
Das Programm liest ein FAT32-Abbild, listet die Wurzel und holt eine Datei aus einem Unterverzeichnis:
import std.fs.fat;
import std.fs;
import std.io;
import std.alloc;
fn main(): int64 {
var fd: int64 := open("test.img", 0, 0);
if (fd < 0) { PrintLn("Abbild nicht lesbar"); return 1; }
var vol: FatVolume := FatMount(fd, 0);
if (!FatVolumeValid(vol)) { PrintLn("kein FAT"); close(fd); return 1; }
Print("FAT-Typ : FAT"); PrintLn(IntToStr(vol.fatType));
Print("Bytes je Sektor : "); PrintLn(IntToStr(vol.bytesPerSector));
Print("Sektoren je Cluster: "); PrintLn(IntToStr(vol.sectorsPerClus));
Print("Bytes je Cluster : "); PrintLn(IntToStr(FatBytesPerCluster(vol)));
Print("Cluster gesamt : "); PrintLn(IntToStr(vol.countOfClusters));
Print("Wurzelcluster : "); PrintLn(IntToStr(vol.rootCluster));
PrintLn("");
PrintLn("Wurzelverzeichnis:");
var d: FatDir := FatDirOpen(vol, 0); // 0 = Wurzel
var e: FatEntry;
var name: int64 := alloc(300);
while (true) {
var got: int64 := FatDirNext(vol, d, e, name, 300);
if (got != 1) { break; }
Print(" "); Print(name as pchar);
if (e.isDir) { Print(" <DIR>"); }
else { Print(" "); Print(IntToStr(e.size)); Print(" Byte"); }
Print(" Cluster "); PrintLn(IntToStr(e.cluster));
}
FatDirClose(vol, d);
PrintLn("");
var f: FatEntry;
var r: int64 := FatFindPath(vol, "ORDNER/NOTIZ.TXT", f);
Print("Pfadsuche : "); PrintLn(IntToStr(r));
if (r == 1) {
Print(" Groesse : "); PrintLn(IntToStr(f.size));
Print(" Kettenlaenge : "); PrintLn(IntToStr(FatChainLength(vol, f.cluster)));
var buf: int64 := alloc(1024);
var n: int64 := FatReadFile(vol, f, buf, 1024);
Print(" gelesen : "); Print(IntToStr(n)); PrintLn(" Byte");
Print(" Inhalt : "); Print(buf as pchar);
free(buf, 1024);
}
free(name, 300);
close(fd);
return 0;
}
Das Abbild dafür stammt aus fremden Werkzeugen — das ist Absicht, siehe Kasten unten:
dd if=/dev/zero of=test.img bs=1M count=40
mkfs.vfat -F 32 -n LYXTEST test.img
echo "Hallo aus Lyx!" > hallo.txt
echo "Zweite Datei" > notiz.txt
mcopy -i test.img hallo.txt ::/
mmd -i test.img ::/ORDNER
mcopy -i test.img notiz.txt ::/ORDNER/
FAT-Typ : FAT32
Bytes je Sektor : 512
Sektoren je Cluster: 1
Bytes je Cluster : 512
Cluster gesamt : 80628
Wurzelcluster : 2
Wurzelverzeichnis:
LYXTEST 0 Byte Cluster 0
HALLO.TXT 15 Byte Cluster 3
ORDNER <DIR> Cluster 4
Pfadsuche : 1
Groesse : 13
Kettenlaenge : 1
gelesen : 13 Byte
Inhalt : Zweite Datei
Die Datenträgerkennung LYXTEST erscheint beim Durchlaufen als Eintrag mit isVolume — bei der Suche wird sie übersprungen.
Warum das Abbild vonmkfs.vfatundmtoolskommt und nicht aus eigener Feder: Ein selbst gebautes Abbild belegt nur, dass Leser und Schreiber dieselbe Vorstellung haben — auch eine falsche. Gegen eine fremde Umsetzung gemessen steht auf der anderen Seite jemand, der die Spezifikation unabhängig gelesen hat. Genau so arbeitet auch die Testbatterie der Unit (tests/fat_test.sh, 94 Prüfungen); fehlen die Werkzeuge, wird das gesagt und übersprungen, nicht als Fehlschlag gewertet (#1911).
Letzte Aktualisierung: 2026-09-01 — Seite neu angelegt aus std/fs/fat.lyx (Commit 3065951, #1920, 94 Prüfungen); Beispiel mit lyxc 1.1.16A gegen ein mkfs.vfat-Abbild gebaut und ausgeführt.
