From e7fc76bf5a66d457eeac039f2a2de3658d8163f6 Mon Sep 17 00:00:00 2001 From: Meik Date: Fri, 13 Mar 2026 17:14:57 +0100 Subject: [PATCH] Add DFS metadata classification concept --- ...FS_DFS_Metadaten_Klassifikation_Konzept.md | 396 ++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 Sonstiges/LIAM_NTFS_DFS_Metadaten_Klassifikation_Konzept.md diff --git a/Sonstiges/LIAM_NTFS_DFS_Metadaten_Klassifikation_Konzept.md b/Sonstiges/LIAM_NTFS_DFS_Metadaten_Klassifikation_Konzept.md new file mode 100644 index 0000000..e16c192 --- /dev/null +++ b/Sonstiges/LIAM_NTFS_DFS_Metadaten_Klassifikation_Konzept.md @@ -0,0 +1,396 @@ +# 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: + +```text +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.