Files
LIAM/Sonstiges/LIAM_NTFS_DFS_Metadaten_Klassifikation_Konzept.md
2026-03-13 17:14:57 +01:00

10 KiB

LIAM NTFS / DFS Metadaten-Klassifikation - konkretes Umsetzungskonzept

Ziel

Dieses Dokument beschreibt die konkrete Zielumsetzung fuer die robuste Klassifikation von UNC-Pfaden im LIAM-NTFS-Provider.

Rahmenbedingungen:

  • keine Erweiterung der bestehenden Provider-Config
  • der Code darf nicht voraussetzen, auf dem DFS- oder SMB-Server selbst zu laufen
  • DFS-Strukturen koennen unterschiedlich aufgebaut sein
  • es muss sauber unterschieden werden zwischen:
    • klassischem SMB-Share
    • DFS-Namespace-Root
    • DFS-Link
    • echtem Folder

Die bestehende Config liefert dafuer nur den Einstiegspunkt und die Zugriffsrechte:

  • RootPath
  • Domain
  • Credential

Die eigentliche Typbestimmung muss daher zur Laufzeit ueber Metadaten des adressierten UNC-Pfads erfolgen.

Kurzfazit

Die robuste Loesung ist:

  1. UNC-Pfad normalisieren
  2. DFS-Metadaten fuer den Pfad und relevante Praefixe abfragen
  3. wenn kein DFS-Treffer vorliegt, SMB-Share-Metadaten abfragen
  4. alles unterhalb einer erkannten fachlichen Grenze als Folder behandeln

Die aktuelle Segment-Heuristik wird damit ersetzt, nicht nur verbessert.

Fachregeln

Es gelten folgende semantische Regeln:

  • \\server\\namespace ist ein DfsNamespaceRoot, wenn der Pfad als DFS-Namespace-Root aufgeloest werden kann.
  • \\server\\namespace\\link ist ein DfsLink, wenn der Pfad als DFS-Link aufgeloest werden kann.
  • \\server\\share ist ein ClassicShare, wenn der Share auf dem Server publiziert ist und kein DFS-Treffer vorliegt.
  • jeder Pfad unterhalb eines DfsLink ist ein Folder
  • jeder Pfad unterhalb eines ClassicShare ist ein Folder
  • jeder Pfad unterhalb eines DfsNamespaceRoot, aber oberhalb eines DfsLink, ist nur dann Folder, wenn er kein DFS-Link ist und fachlich wirklich Dateisystemstruktur repraesentiert; fuer den Normalfall sind direkte Kinder eines Namespace-Roots als DFS-Links zu erwarten

Verwendbare Laufzeit-Metadaten

1. DFS-Metadaten

Primaere Quelle fuer DFS:

  • Win32 DFS API, bevorzugt NetDfsGetInfo

Damit kann fuer einen UNC-Pfad geprueft werden:

  • ist der Pfad ein DFS-Objekt?
  • handelt es sich um Root oder Link?
  • welche Targets sind hinterlegt?

Wichtig:

  • diese Abfrage funktioniert remote gegen den adressierten Namespace
  • sie benoetigt keine lokale Ausfuehrung auf dem DFS-Server
  • sie ist fachlich deutlich verlaesslicher als Directory.Exists(...)

2. SMB-Share-Metadaten

Primaere Quelle fuer klassische Shares:

  • NetShareEnum
  • optional ergaenzend NetShareGetInfo

Das ist fuer LIAM bereits teilweise vorhanden:

  • cNetworkConnection.EnumNetShares(server)

Damit kann geprueft werden:

  • ob \\server\\share ein echter publizierter SMB-Share ist
  • welche sichtbaren Disk-Shares auf dem Host existieren

3. Dateisystem-Metadaten

Nur fuer Folder:

  • Directory.Exists
  • DirectoryInfo

Diese Daten duerfen nicht fuer die Unterscheidung zwischen DFS und Share benutzt werden, sondern nur nachdem bereits eine fachliche Grenze erkannt wurde.

Nicht ausreichende Datenquellen

Folgende Informationen reichen fuer die Typbestimmung nicht aus:

  • Anzahl der UNC-Segmente
  • RootPath alleine
  • Directory.Exists
  • ACLs
  • Naming Conventions
  • Custom Tags
  • Group Strategy

Diese Daten koennen ergaenzend helfen, liefern aber keine robuste Aussage ueber DFS vs. SMB.

Zielmodell

Internes Modell:

  • ServerRoot
  • ClassicShare
  • DfsNamespaceRoot
  • DfsLink
  • Folder
  • Unknown

Externe LIAM-Abbildung:

  • ClassicShare -> NtfsShare
  • DfsNamespaceRoot -> DfsNamespaceRoot
  • DfsLink -> NtfsShare
  • Folder -> NtfsFolder

Damit bleibt die fachliche Aussenwirkung stabil:

  • DFS-Link bleibt nach aussen ein share-aehnliches Objekt
  • DFS-Namespace-Root bleibt eigenstaendig sichtbar

Konkreter Klassifikationsalgorithmus

Schritt 1: Normalisierung

Jeder Eingabepfad wird zuerst vereinheitlicht:

  • Slash zu Backslash
  • doppelte Separatoren bereinigen
  • UNC-Praefix sicherstellen
  • Trailing Backslash entfernen

Beispiel:

  • \\SERVER\\share\\
  • //server/share

werden intern zu:

  • \\server\\share

fuer Vergleiche kann case-insensitive gearbeitet werden.

Schritt 2: DFS zuerst pruefen

Fuer den gesamten Pfad und seine relevanten Praefixe werden DFS-Metadaten geprueft.

Beispiele:

  • bei \\server\\namespace\\link\\folder
    • pruefe \\server\\namespace
    • pruefe \\server\\namespace\\link

Entscheidungsregel:

  • wenn der volle Pfad ein DFS-Namespace-Root ist -> DfsNamespaceRoot
  • wenn der volle Pfad ein DFS-Link ist -> DfsLink
  • wenn ein Praefix ein DFS-Link ist und der Gesamtpfad darunter liegt -> Folder
  • wenn ein Praefix ein DFS-Namespace-Root ist, aber noch kein DFS-Link erkannt wurde, wird weiter geprueft

Wichtig:

  • der tiefste erkannte DFS-Link gewinnt als fachliche Grenze
  • die Share-Grenze ist dann genau dieser Link-Pfad

Schritt 3: SMB-Share pruefen

Nur wenn kein DFS-Ergebnis vorliegt:

  • ermittle sichtbare Shares des Servers per NetShareEnum
  • pruefe, ob \\server\\share exakt ein publizierter Share ist

Entscheidungsregel:

  • wenn der volle Pfad exakt einem Share entspricht -> ClassicShare
  • wenn ein Praefix exakt einem Share entspricht und der Gesamtpfad darunter liegt -> Folder

Schritt 4: Folder nur relativ zu einer erkannten Grenze

Ein Pfad ist nur dann sicher Folder, wenn bereits eine fachliche Grenze erkannt wurde:

  • klassischer Share
  • DFS-Link

Beispiele:

  • \\server\\share\\dept -> Folder, wenn \\server\\share ein ClassicShare ist
  • \\server\\namespace\\link\\dept -> Folder, wenn \\server\\namespace\\link ein DfsLink ist

Schritt 5: Unknown statt falscher Sicherheit

Wenn weder DFS noch SMB sicher bestimmt werden koennen:

  • liefere intern Unknown
  • logge die Ursache
  • triff keine stille Segment-basierten Fallback-Entscheidung

Erforderliche Runtime-Abfragen

DFSResolver

Neue interne Hilfskomponente:

  • TryGetDfsMetadata(path, out metadata)

Rueckgabedaten:

  • Exists
  • IsNamespaceRoot
  • IsLink
  • Path
  • EntryPath
  • Targets

Empfohlene Implementierung:

  • P/Invoke gegen NetDfsGetInfo

Sinnvolle Aufrufmuster:

  • pruefe \\server\\namespace
  • pruefe \\server\\namespace\\link
  • cache Ergebnisse je Pfad

ShareResolver

Bestehende Hilfskomponente weiterverwenden:

  • EnumNetShares(server)

Ergaenzung:

  • optional Hilfsmethode IsPublishedShare(server, shareName)
  • Cache je Server

Neue zentrale Datenstruktur

Empfohlene interne Rueckgabe:

PathClassification
- NormalizedPath
- Kind
- BoundaryPath
- ParentBoundaryPath
- BackingType
- ResolvedFrom
- Diagnostics

Dabei bedeutet:

  • BoundaryPath: fachliche Share-Grenze
  • ParentBoundaryPath: Parent-Objekt fuer LIAM-ParentUID
  • BackingType: DFS oder SMB
  • ResolvedFrom: z. B. DfsLinkPrefix, SharePrefix, FullPathDfsRoot

Verhalten fuer typische Faelle

Fall A

Pfad:

  • \\SRVWSM001.imagoverum.com\\file_shares

Erwartung:

  • wenn DFS-Metadaten Root bestaetigen -> DfsNamespaceRoot

Fall B

Pfad:

  • \\SRVWSM001.imagoverum.com\\file_shares\\share2

Erwartung:

  • wenn DFS-Metadaten Link bestaetigen -> intern DfsLink, extern NtfsShare

Fall C

Pfad:

  • \\SRVWSM001.imagoverum.com\\file_shares\\share2\\test33

Erwartung:

  • wenn \\...\\file_shares\\share2 als DFS-Link erkannt wurde -> Folder

Anpassung von getDataAreasAsync()

Die Enumeration darf nicht mehr nur mit RequestFoldersListAsync(RootPath, depth) arbeiten.

Stattdessen:

  • Root zuerst klassifizieren
  • Kinder je nach Root-Typ enumerieren

Regeln:

  • ClassicShare
    • Kinder per Dateisystem-Verzeichnisliste
  • DfsNamespaceRoot
    • direkte Kinder primaer ueber DFS-Namespace-Inhaltsmetadaten
    • nur diese direkten Kinder sind fachlich Share-aehnliche Eintraege
  • DfsLink
    • Kinder per Dateisystem-Verzeichnisliste unterhalb des Link-Ziels bzw. unter dem UNC-Linkpfad
  • Folder
    • Kinder per Dateisystem-Verzeichnisliste

Der aktuelle Fehler entsteht gerade dadurch, dass direkte Kinder eines DFS-Namespace-Roots wie normale Ordner enumeriert und spaeter wieder als Folder behandelt werden.

Anpassung von LoadDataArea()

LoadDataArea() muss dieselbe zentrale Klassifikation benutzen wie getDataAreasAsync().

Wichtig:

  • keine eigene Kurzlogik
  • keine Typentscheidung ueber Segmentzahl
  • gleiche PathClassification fuer denselben Pfad wie im Listenpfad

Logging

Fuer Diagnosefaelle sollten folgende Logeintraege vorhanden sein:

  • welcher Pfad wird klassifiziert
  • welcher DFS-Check wurde ausgefuehrt
  • welcher Share-Check wurde ausgefuehrt
  • welcher Praefix als Boundary erkannt wurde
  • warum ein Pfad Unknown wurde

Beispiel:

  • Path '\\server\\namespace\\link\\team' classified as Folder via DFS link boundary '\\server\\namespace\\link'

Fehlerverhalten

Wenn DFS-Abfragen fehlschlagen:

  • Fehler loggen
  • weiter mit SMB-Pruefung

Wenn SMB-Abfragen fehlschlagen:

  • Fehler loggen
  • nicht automatisch Folder annehmen

Wenn beides fehlschlaegt:

  • Unknown

Damit wird falsche Typvergabe vermieden.

Performance

Noetige Caches:

  • DFS-Metadaten-Cache pro Pfad
  • Share-Liste pro Server

Empfehlung:

  • innerhalb eines Provider-Laufs cachen
  • keine globale Langzeitpersistenz

Konkrete Implementierungsschritte

  1. neue interne Resolver-Komponente fuer DFS-Metadaten einfuehren
  2. bestehende Share-Abfrage in dedizierte Share-Resolver-Methode kapseln
  3. ClassifyPath() auf Metadaten-basierte Entscheidung umbauen
  4. getDataAreasAsync() root-typabhaengig enumerieren
  5. LoadDataArea() auf dieselbe Klassifikation umstellen
  6. JSON-Rueckgabe unveraendert lassen:
    • DfsNamespaceRoot als eigener DataAreaType
    • DfsLink nach aussen als NtfsShare
    • Folder als NtfsFolder

Entscheidung

Die empfohlene Umsetzung ohne Config-Erweiterung ist:

  • RootPath aus der vorhandenen Config nur als Einstieg verwenden
  • DFS-Metadaten zur Laufzeit ueber API abfragen
  • SMB-Share-Metadaten zur Laufzeit ueber NetShareEnum abfragen
  • Folder ausschliesslich relativ zu einer erkannten fachlichen Grenze ableiten

Nur so koennen klassische Shares, DFS-Namespaces, DFS-Links und echte Folder belastbar unterschieden werden, auch wenn LIAM nicht auf dem Zielserver selbst laeuft.