397 lines
10 KiB
Markdown
397 lines
10 KiB
Markdown
# 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.
|