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