====== std.fs.ntfs — NTFS lesen ======
''import std.fs.ntfs;''
Liest NTFS: Bootsektor auswerten, MFT-Sätze lesen, Attribute finden, Verzeichnisse durchsuchen, Dateien lesen. **Nur lesend.**
Diese Unit hat den größten Abstand zu ihren Schwestern. FAT, exFAT und ext4 haben alle irgendwo eine Tabelle, die Blöcke aufzählt. NTFS hat stattdessen die **Master File Table**: jede Datei ist dort ein Satz aus **Attributen**, und die Daten sind nur eines davon (''$DATA'').
→ [[lyx_-_programmiersprache:units:fs:dateisysteme|Die Dateisystem-Sammlung]] · [[lyx_-_programmiersprache:units:fs:part|std.fs.part]] · [[lyx_-_programmiersprache:guides:dateisysteme-lesen|Guide: Fremde Dateisysteme lesen]]
----
===== 1. Die drei Eigenheiten =====
Jede von ihnen ist eine eigene Fehlerquelle, und alle drei sind still:
- **Fixup-Werte.** Jeder MFT-Satz und jeder Indexblock trägt am Ende jedes Sektors zwei Byte, die vor dem Auswerten zurückgetauscht werden müssen. Bei kleinen Sätzen fällt das nicht auf, weil dort nur der erste Sektor benutzt wird — bei großen liest man ohne Rücktausch Müll mitten aus der Struktur.
- **Datenläufe** (//mapping pairs//). Ein nicht-residentes Attribut nennt seine Cluster als Folge von Paaren aus Länge und **relativem** Versatz. Der Versatz ist **vorzeichenbehaftet** und baut auf dem vorigen auf — ein Lauf kann rückwärts zeigen. Wer ihn ohne Vorzeichen liest, landet irgendwo auf dem Datenträger.
- **Residente Attribute.** Eine Datei unter etwa 700 Byte hat **gar keine Cluster**; ihr Inhalt liegt im MFT-Satz. Wer nur den nicht-residenten Fall kann, liest für sie Nullen — und die sehen aus wie eine leere Datei.
----
===== 2. Konstanten =====
^ Konstante ^ Wert ^ Bedeutung ^
| ''NTFS_ERR'' | −1 | Lesefehler oder ungültige Struktur |
| ''NTFS_MFT_SELF'' | 0 | die MFT selbst |
| ''NTFS_MFT_VOLUME'' | 3 | trägt ''$VOLUME_NAME'' |
| ''NTFS_MFT_ROOT'' | 5 | das Wurzelverzeichnis |
| ''NTFS_AT_STANDARD'' | 0x10 | ''$STANDARD_INFORMATION'' |
| ''NTFS_AT_FILE_NAME'' | 0x30 | ''$FILE_NAME'' |
| ''NTFS_AT_VOLUME_NAME'' | 0x60 | ''$VOLUME_NAME'' |
| ''NTFS_AT_DATA'' | 0x80 | ''$DATA'' — der Inhalt |
| ''NTFS_AT_INDEX_ROOT'' · ''NTFS_AT_INDEX_ALLOC'' | 0x90 · 0xA0 | Verzeichnisindex |
| ''NTFS_AT_END'' | 0xFFFFFFFF | Ende der Attributliste |
| ''NTFS_FLAG_INUSE'' · ''NTFS_FLAG_DIR'' | 1 · 2 | Satz in Gebrauch, Satz ist Verzeichnis |
| ''NTFS_LCN_LOCH'' | −1 | Loch in einer Lauffolge (spärliche Datei) |
----
===== 3. Typen =====
pub type NtfsVolume = struct {
fd: int64;
partOffset: int64;
bytesPerSector: int64;
sectorsPerClus: int64;
clusterBytes: int64;
totalSectors: int64;
mftLcn: int64;
recordSize: int64; // Bytes je MFT-Satz
indexSize: int64; // Bytes je Indexblock
mftRuns: int64; // Laufliste der MFT: je 24 Byte (vcn, lcn, len)
mftRunCount: int64;
gueltig: bool;
}
pub type NtfsAttr = struct {
typ: int64;
nonResident: bool;
wertPtr: int64; // resident: Zeiger auf den Wert IM Satzpuffer
wertLen: int64;
runs: int64; // nicht-resident: Laufliste (je 24 Byte)
runCount: int64;
realSize: int64;
gefunden: bool;
}
pub type NtfsEntry = struct {
mftRef: int64;
size: int64;
isDir: bool;
}
Die Trennung in ''wertPtr''/''wertLen'' gegen ''runs''/''runCount'' bildet genau den residenten und den nicht-residenten Fall ab — ''nonResident'' sagt, welche Felder gelten.
----
===== 4. Volume und Sätze =====
^ Signatur ^ Zweck ^
| ''NtfsMount(fd: int64, byteOffset: int64): NtfsVolume'' | Bootsektor lesen, MFT-Laufliste aufbauen |
| ''NtfsUnmount(ref vol: NtfsVolume): void'' | Laufliste freigeben |
| ''NtfsValid(vol: NtfsVolume): bool'' | Wurde ein gültiges NTFS gefunden? |
| ''NtfsReadRecord(vol, nr: int64, puffer: int64): int64'' | MFT-Satz ''nr'' lesen — **mit Fixup-Rücktausch** |
| ''NtfsRecordInUse(satz: int64): bool'' | Ist der Satz belegt? |
| ''NtfsRecordIsDir(satz: int64): bool'' | Ist es ein Verzeichnis? |
| ''NtfsVolumeLabel(vol, buf: int64, max: int64): int64'' | Datenträgerkennung aus Satz 3 |
----
===== 5. Attribute =====
^ Signatur ^ Zweck ^
| ''NtfsFindAttr(vol, satz: int64, typ: int64, runPuffer: int64, ref a: NtfsAttr): int64'' | Attribut im Satz suchen; Lauflisten landen in ''runPuffer'' |
| ''NtfsReadAttr(vol, a: NtfsAttr, buf: int64, max: int64): int64'' | Wert des Attributs lesen — resident wie nicht-resident |
----
===== 6. Dateien und Verzeichnisse =====
^ Signatur ^ Zweck ^
| ''NtfsReadFile(vol, mftNr: int64, buf: int64, max: int64): int64'' | Datei über ihre MFT-Nummer lesen |
| ''NtfsFileSize(vol: NtfsVolume, mftNr: int64): int64'' | Größe der Datei |
| ''NtfsDirCount(vol: NtfsVolume, dirNr: int64): int64'' | Zahl der Einträge eines Verzeichnisses |
| ''NtfsFindInDir(vol, dirNr: int64, name: pchar, ref e: NtfsEntry): int64'' | Eintrag im Verzeichnis suchen |
| ''NtfsFindPath(vol: NtfsVolume, path: pchar): int64'' | Pfad ab der Wurzel auflösen → MFT-Nummer |
Ein Verzeichnis ist in NTFS ein **B-Baum** aus ''$INDEX_ROOT'' und ''$INDEX_ALLOCATION''; die Unit durchläuft beide.
----
===== 7. Beispiel =====
unit main;
import std.fs.ntfs;
import std.alloc;
import std.io;
import std.string;
fn main(): int64 {
var fd: int64 := open("/tmp/ntfs.img"c, 0, 0);
if (fd < 0) { return 1; }
var vol: NtfsVolume := NtfsMount(fd, 0);
if (!NtfsValid(vol)) { PrintLn("kein NTFS"); close(fd); return 1; }
var nr: int64 := NtfsFindPath(vol, "/ordner/datei.txt"c);
if (nr == NTFS_ERR) { PrintLn("nicht gefunden"); }
else {
var groesse: int64 := NtfsFileSize(vol, nr);
var buf: int64 := alloc(groesse + 1);
NtfsReadFile(vol, nr, buf, groesse);
PrintLn(StrConcat("gelesen: ", IntToStr(groesse)));
free(buf, groesse + 1);
}
NtfsUnmount(vol);
close(fd);
return 0;
}
----
===== 8. Grenzen =====
**Nicht enthalten — und im Zweifel gemeldet, nicht geraten:**
* komprimierte und verschlüsselte Attribute
* alternative Datenströme unter Namen
* ''$ATTRIBUTE_LIST'' — ein Satz, dessen Attribute nicht mehr in einen Satz passen
* Reparse Points
* ''$LogFile''-Wiedergabe
**Namen werden nach ASCII gelesen**; alles darüber wird zu ''?'' — dieselbe Angabe wie in den Schwester-Units.
**Nur lesen.** Schreiben verlangt zusätzlich die Pflege des ''$LogFile'' (NTFS führt ein Journal), der ''$Bitmap'' und der Indexbäume.
Prüfstand: ''tests/ntfs_test.sh'' — **33 Prüfungen** gegen ein erzeugtes Abbild.
Letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; erhoben aus ''std/fs/ntfs.lyx'' ([[https://github.com/SEOLizer/LyX-Compiler/issues/1926|#1926]]).
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.