Files
LIAM/Sonstiges/LIAM_NTFS_DFS_Pfadklassifikation_Konzept.md
2026-03-11 17:09:46 +01:00

432 lines
14 KiB
Markdown

# LIAM NTFS / DFS Pfadklassifikation - Zielkonzept
## Ziel
Dieses Dokument beschreibt ein belastbares Zielkonzept fuer die Klassifikation von UNC-Pfaden im LIAM-NTFS-Provider.
Der aktuelle Code behandelt die Unterscheidung zwischen Share und Folder im Wesentlichen ueber die Pfadtiefe. Das ist fuer einfache klassische UNC-Freigaben teilweise ausreichend, aber fachlich nicht robust genug.
Insbesondere folgende Faelle sollen perspektivisch sauber unterstuetzt werden:
- `\\server`
- `\\server\share`
- `\\server\share\folder`
- `\\server\namespace`
- `\\server\namespace\link`
- `\\server\namespace\link\folder`
Das Konzept ist bewusst noch keine Implementierung. Es beschreibt, welche semantischen Informationen benoetigt werden und wie die Entscheidung logisch aufgebaut sein sollte.
## Problem des aktuellen Ansatzes
Der bestehende NTFS-Provider leitet den Objekttyp im Kern aus der Anzahl der UNC-Segmente ab.
Beispiel:
- `\\server\share` -> wird als Share behandelt
- `\\server\share\folder` -> wird als Folder behandelt
Das hat mehrere fachliche Schwaechen:
- Ein DFS-Link wie `\\server\namespace\link` ist semantisch oft ein Share-aehnlicher Einstiegspunkt, hat aber drei Segmente.
- Ein Root wie `\\server` ist mit der bisherigen Logik ueberhaupt nicht sinnvoll modellierbar.
- `LoadDataArea()` und `getDataAreasAsync()` arbeiten nicht zwingend mit derselben Semantik.
- Die Klassifikation basiert auf Syntax statt auf den tatsaechlichen Freigabe- und Namespace-Metadaten.
Die Folge ist, dass bei DFS und bei kuenftigem Server-Root-Support keine verlaessliche Typbestimmung moeglich ist.
## Grundprinzip der richtigen Loesung
Die Klassifikation darf nicht von der Pfadtiefe abhaengen, sondern muss aus der tatsaechlichen Bedeutung des Pfads ermittelt werden.
Das bedeutet:
- Ein klassischer Windows-Share wird ueber Share-Metadaten erkannt.
- Ein DFS-Namespace oder DFS-Link wird ueber DFS-Metadaten erkannt.
- Ein Folder ist ein Pfad, der unterhalb eines Share- oder Link-Grenzpunkts liegt.
Die zentrale Regel lautet daher:
Nicht fragen "Wie tief liegt der Pfad?", sondern "Welches Namensobjekt repraesentiert dieser Pfad fachlich?".
## Fachliche Objektarten
Fuer eine robuste interne Modellierung sollten mehr Typen unterschieden werden als nur `Share` und `Folder`.
Empfohlenes internes Zielmodell:
- `ServerRoot`
- `ClassicShare`
- `DfsNamespaceRoot`
- `DfsLink`
- `Folder`
- `Unknown`
Nach aussen kann das bei Bedarf weiter auf bestehende LIAM-Typen gemappt werden:
- `ClassicShare` -> `NtfsShare`
- `DfsNamespaceRoot` -> je nach fachlicher Entscheidung `NtfsShare` oder Container-Typ
- `DfsLink` -> `NtfsShare`
- `Folder` -> `NtfsFolder`
Der entscheidende Punkt ist, dass intern sauber unterschieden wird, auch wenn die externe API zunaechst kompatibel bleiben soll.
## Semantische Datenquellen
### 1. Klassische Windows-Freigaben
Fuer klassische Shares ist die sichere Quelle die Share-Konfiguration des Zielservers.
Fachlich wird geprueft:
- Welche Shares existieren auf dem Server?
- Welcher UNC-Pfad repraesentiert exakt einen Share?
- Auf welchen lokalen Zielpfad zeigt der Share?
Aus dieser Sicht gilt:
- `\\server\share` ist ein Share, wenn auf dem Server genau dieser Share publiziert ist.
- Jeder Pfad unterhalb davon ist ein Folder.
### 2. DFS
Fuer DFS reicht die Share-Liste nicht aus.
Hier muss zusaetzlich geprueft werden:
- Ist `\\server\namespace` ein DFS-Namespace-Root?
- Ist `\\server\namespace\link` ein DFS-Link?
- Welche Link-Ziele sind hinterlegt?
Aus dieser Sicht gilt:
- `\\server\namespace` ist ein Namespace-Objekt, nicht einfach nur ein Folder.
- `\\server\namespace\link` ist fachlich ein Share-aehnlicher Einstiegspunkt.
- Jeder Pfad unterhalb eines DFS-Links ist ein Folder.
## Zielbild fuer Roots
### Fall 1: Root ist `\\server`
Wenn `\\server` als Root erlaubt sein soll, muss die erste Ebene alle publizierten Einstiegspunkte des Servers liefern.
Das bedeutet:
- klassische Shares des Servers
- DFS-Namespaces, die ueber diesen Server angesprochen werden koennen
- administrative Shares wie `C$`, `ADMIN$`, `IPC$` sollten in der Regel herausgefiltert werden
Unter `\\server` duerfen also nicht einfach Dateisystemordner enumeriert werden. Stattdessen werden Namensobjekte auf Server-Ebene enumeriert.
Beispiele:
- `\\server\shareA` -> `ClassicShare`
- `\\server\dfs` -> `DfsNamespaceRoot`
- `\\server\shareA\team1` -> `Folder`
- `\\server\dfs\link1` -> `DfsLink`
### Fall 2: Root ist `\\server\share`
Bei einem klassischen Share als Root ist die Bedeutung eindeutig:
- Root selbst ist `ClassicShare`
- alle darunter liegenden Eintraege sind `Folder`
Hier ist die fachliche Share-Grenze der Root-Pfad selbst.
### Fall 3: Root ist `\\server\namespace`
Bei einem DFS-Namespace als Root muss die erste Ebene nicht als Folderliste, sondern als Namespace-Inhaltsliste verstanden werden.
Typischerweise bedeutet das:
- Root selbst ist `DfsNamespaceRoot`
- direkte Kinder koennen `DfsLink`-Objekte sein
- alles unterhalb eines `DfsLink` ist `Folder`
Ob der Namespace-Root selbst nach aussen als `NtfsShare` oder als eigener Container-Typ behandelt wird, ist eine fachliche API-Entscheidung.
### Fall 4: Root ist `\\server\namespace\link`
Wenn der Root-Pfad bereits ein DFS-Link ist, dann gilt:
- Root selbst ist `DfsLink`
- alles darunter ist `Folder`
Fuer die bestehende LIAM-Semantik waere das nach aussen typischerweise ein `NtfsShare`.
## Empfohlene zentrale Klassifikationslogik
Die Klassifikation sollte in einer einzigen zentralen Komponente gebuendelt werden.
Wichtig ist:
- `getDataAreasAsync()` muss dieselbe Logik verwenden wie `LoadDataArea()`
- die UI, Workflow-Aktivitaeten und Web-API duerfen nicht jeweils eigene Heuristiken haben
Empfohlenes konzeptionelles Interface:
```text
ClassifyPath(path) -> PathClassification
EnumerateChildren(path) -> List<PathClassification>
```
Eine `PathClassification` sollte mindestens enthalten:
- `NormalizedPath`
- `Kind`
- `DisplayName`
- `BoundaryPath`
- `ParentBoundaryPath`
- `BackingType` wie `SMB` oder `DFS`
- optionale Diagnoseinformationen zur Herleitung
## Empfohlene Entscheidungsreihenfolge
Die Aufloesung eines Pfads sollte in einer festen Reihenfolge passieren.
### Schritt 1: Pfad normalisieren
Vor jeder Klassifikation:
- UNC-Pfad vereinheitlichen
- doppelte Separatoren bereinigen
- optional Gross-/Kleinschreibung fuer Vergleiche normalisieren
- Trailing Backslash konsistent behandeln
Beispiele:
- `\\server\share\`
- `\\SERVER\share`
- `\\server\\share`
sollten intern auf einen stabilen Vergleichswert normalisiert werden.
### Schritt 2: Root-Typ erkennen
Zuerst wird geprueft, ob der Pfad einer dieser Grundtypen ist:
- Server-Root
- klassischer Share
- DFS-Namespace-Root
- DFS-Link
- Folder unterhalb eines bekannten Grenzpunkts
### Schritt 3: DFS zuerst pruefen
Fuer UNC-Pfade mit mehreren Segmenten sollte zuerst geprueft werden, ob ein Pfad oder ein Pfadpraefix ein DFS-Namespace oder DFS-Link ist.
Der Grund:
- DFS kann Pfade erzeugen, die syntaktisch wie normale Ordner wirken
- semantisch sind sie aber eigenstaendige Einstiegspunkte
Wenn ein Praefix als DFS-Link identifiziert wird, ist dieses Praefix die fachliche Share-Grenze.
Beispiel:
- Pfad: `\\server\namespace\link\teamA\finance`
- erkannter DFS-Link: `\\server\namespace\link`
- Klassifikation des Gesamtpfads: `Folder`
- fachlicher Parent-Share: `\\server\namespace\link`
### Schritt 4: Klassische Shares pruefen
Wenn kein DFS-Fall vorliegt, wird geprueft, ob der Pfad oder ein Praefix einem klassischen Share entspricht.
Beispiel:
- Pfad: `\\server\share\dept\project`
- erkannter Share: `\\server\share`
- Klassifikation des Gesamtpfads: `Folder`
- fachlicher Parent-Share: `\\server\share`
### Schritt 5: Fallback nur kontrolliert verwenden
Wenn weder DFS noch klassischer Share sicher ermittelt werden kann:
- nicht blind ueber Segmentzahl entscheiden
- stattdessen `Unknown` liefern oder einen explizit konfigurierten Fallback nutzen
Das ist wichtig, damit Unsicherheit nicht als scheinbar sichere Fachlogik maskiert wird.
## Share-Grenze als zentrales Konzept
Ein hilfreiches Kernmodell ist die sogenannte Share-Grenze.
Die Share-Grenze ist der fachliche Einstiegspunkt, unterhalb dessen normale Folder-Hierarchie beginnt.
Beispiele:
- `\\server\share` -> Share-Grenze bei klassischem SMB-Share
- `\\server\namespace\link` -> Share-Grenze bei DFS-Link
Ein Pfad wird damit nicht nur als `Share` oder `Folder` klassifiziert, sondern auch relativ zu seiner Share-Grenze verstanden.
Das ist fuer mehrere Dinge wichtig:
- korrektes Setzen von `ParentUID`
- konsistente Anzeige des Root-Objekts
- korrektes Nachladen von Kindern
- spaetere Berechtigungs- oder Traverselogik
## Zielverhalten fuer typische Beispiele
### Beispiel A: Klassische Freigabe
- `\\filesrv\finance` -> `ClassicShare`
- `\\filesrv\finance\2026` -> `Folder`
- `\\filesrv\finance\2026\plan.xlsx` -> fuer LIAM ggf. ausserhalb des Scope, falls nur Ordner modelliert werden
### Beispiel B: DFS-Namespace
- `\\dfs01\file_shares` -> `DfsNamespaceRoot`
- `\\dfs01\file_shares\shareA` -> `DfsLink`
- `\\dfs01\file_shares\shareA\team1` -> `Folder`
### Beispiel C: Server-Root
- `\\dfs01` -> `ServerRoot`
- Kind `\\dfs01\file_shares` -> `DfsNamespaceRoot`
- Kind `\\dfs01\public` -> `ClassicShare`
### Beispiel D: Gemischte Landschaft
Ein Server kann gleichzeitig hosten:
- klassische Shares
- DFS-Namespaces
- administrative Shares
Die Enumeration unter `\\server` muss daher bewusst entscheiden, welche publizierten Namensobjekte sichtbar gemacht werden und welche nicht.
## Konsequenzen fuer die LIAM-Architektur
### 1. Eine gemeinsame Path-Classification-Schicht
Die Logik darf nicht in mehreren Stellen verteilt bleiben.
Stattdessen sollte es eine zentrale Schicht geben, die:
- Pfade klassifiziert
- Kindobjekte enumeriert
- die Share-Grenze mitliefert
- Diagnoseinformationen fuer Logging bereitstellt
### 2. `LoadDataArea()` und `getDataAreasAsync()` muessen dieselbe Semantik teilen
Beide Methoden muessen dieselbe Klassifikation verwenden.
Sonst entsteht wieder ein Fehlerbild wie heute:
- Listenansicht liefert einen Typ
- Einzelaufladung liefert einen anderen Typ
Das ist fachlich unzulaessig.
### 3. Die Konstruktion von LIAM-Datenobjekten muss auf Klassifikation folgen
Nicht:
- Pfadtiefe bestimmen
- direkt `cLiamNtfsShare` oder `cLiamNtfsFolder` erzeugen
Sondern:
1. Pfad klassifizieren
2. daraus internes `Kind` bestimmen
3. danach passendes LIAM-Objekt erzeugen
### 4. Nach aussen kann die API zunaechst kompatibel bleiben
Wenn eine API-Aenderung vermieden werden soll, koennen intern mehrere Arten von Share-artigen Objekten existieren, die nach aussen zunaechst auf `NtfsShare` gemappt werden.
Beispiel:
- `ClassicShare` -> `NtfsShare`
- `DfsLink` -> `NtfsShare`
- `DfsNamespaceRoot` -> je nach Entscheidung ebenfalls `NtfsShare` oder eigener Typ spaeter
So laesst sich die Semantik verbessern, ohne sofort alle Konsumenten anpassen zu muessen.
## Technische Grenzen und Realitaet
Die sichere Ermittlung ist programmatisch grundsaetzlich moeglich, aber an Infrastrukturbedingungen gebunden.
Moegliche Einschraenkungen:
- fehlende Rechte fuer Share- oder DFS-Abfragen
- Firewall- oder RPC-Beschraenkungen
- temporaer nicht verfuegbare Server
- unterschiedliche Betriebsweisen in der Kundenumgebung
Deshalb ist ein kontrollierter Unsicherheitszustand wichtig.
Empfohlene Regel:
- wenn die Semantik sicher ermittelt werden kann, klaren Typ liefern
- wenn die Semantik nicht sicher ermittelt werden kann, `Unknown` oder einen expliziten diagnostischen Fehler liefern
- nicht stillschweigend auf reine Pfadtiefe zurueckfallen, wenn dadurch fachlich falsche Typen entstehen
## Empfohlene Implementierungsstrategie in Etappen
### Etappe 1: Fachmodell einziehen
Intern neue Klassifikationsarten definieren:
- `ServerRoot`
- `ClassicShare`
- `DfsNamespaceRoot`
- `DfsLink`
- `Folder`
- `Unknown`
### Etappe 2: Zentrale Klassifikationskomponente bauen
Eine zentrale Komponente einfuehren, die:
- Pfade normalisiert
- Typen klassifiziert
- Share-Grenzen bestimmt
- Kinder kontextabhaengig enumeriert
### Etappe 3: `LoadDataArea()` umstellen
`LoadDataArea()` sollte zuerst die neue Klassifikation verwenden, weil dort der heutige Fehlpfad am deutlichsten sichtbar ist.
### Etappe 4: `getDataAreasAsync()` umstellen
Danach den Listenpfad auf dieselbe Klassifikation bringen.
### Etappe 5: API-Mapping pruefen
Danach bewerten:
- reicht extern weiter `NtfsShare` / `NtfsFolder`
- oder wird spaeter ein eigener Typ fuer Namespace-Container benoetigt
## Entscheidungspunkte, die fachlich geklaert werden muessen
Vor einer spaeteren Implementierung sollten einige Punkte explizit entschieden werden:
- Soll `\\server` als sichtbares DataArea-Objekt existieren oder nur als Enumerationsroot?
- Soll ein DFS-Namespace-Root nach aussen als `NtfsShare` gelten oder als eigener Container?
- Sollen nur DFS-Links als Share-aehnliche Objekte gelten?
- Sollen administrative Shares immer ausgeblendet werden?
- Wie soll sich das System bei fehlenden Rechten fuer DFS-/Share-Abfragen verhalten?
Ohne diese Entscheidungen wird die technische Umsetzung sonst schnell inkonsistent.
## Fazit
Die bisherige Heuristik "Pfadtiefe bestimmt Share oder Folder" ist fuer eine Umgebung mit klassischen Shares, DFS und kuenftigem `\\server`-Root nicht ausreichend.
Das richtige Zielbild ist:
- zentrale semantische Pfadklassifikation
- Unterscheidung von Server, klassischem Share, DFS-Namespace, DFS-Link und Folder
- gemeinsame Logik fuer Listen- und Einzel-Ladepfad
- externe Rueckgabe weiterhin kompatibel, intern aber fachlich korrekt modelliert
Nur mit diesem Ansatz koennen spaeter sowohl klassische Freigaben als auch DFS-Strukturen sauber und nachvollziehbar behandelt werden.