432 lines
14 KiB
Markdown
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.
|