Inhaltsverzeichnis

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

Die Dateisystem-Sammlung · std.fs.part · Guide: Fremde Dateisysteme lesen


1. Die drei Eigenheiten

Jede von ihnen ist eine eigene Fehlerquelle, und alle drei sind still:

  1. 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.
  2. 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.
  3. 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:

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.sh33 Prüfungen gegen ein erzeugtes Abbild.

Letzte Aktualisierung: 2026-09-05 — Seite neu angelegt; erhoben aus std/fs/ntfs.lyx (#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.