Classify NTFS paths via DFS metadata

This commit is contained in:
Meik
2026-03-13 20:06:47 +01:00
parent e7fc76bf5a
commit f14d4ec2e6
2 changed files with 152 additions and 34 deletions

View File

@@ -47,6 +47,7 @@ namespace C4IT.LIAM
public eNtfsPathKind Kind { get; set; } = eNtfsPathKind.Unknown;
public string BoundaryPath { get; set; } = string.Empty;
public string ParentBoundaryPath { get; set; } = string.Empty;
public string ParentPath { get; set; } = string.Empty;
public string DisplayName { get; set; } = string.Empty;
public int Level { get; set; } = -1;
}
@@ -55,6 +56,7 @@ namespace C4IT.LIAM
public readonly cNtfsBase ntfsBase = new cNtfsBase();
public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase();
private readonly Dictionary<string, HashSet<string>> publishedShareCache = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, string> dfsEntryPathCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
//public readonly bool WithoutPrivateFolders = true;
@@ -274,7 +276,10 @@ namespace C4IT.LIAM
? new DirectoryInfo(classification.NormalizedPath).CreationTimeUtc.ToString("s")
: DateTime.MinValue.ToString("s")
};
var folder = new cLiamNtfsFolder(this, null, null, folderData, classification.ParentBoundaryPath);
var parentPath = !string.IsNullOrWhiteSpace(classification.ParentPath)
? classification.ParentPath
: classification.ParentBoundaryPath;
var folder = new cLiamNtfsFolder(this, null, null, folderData, parentPath);
await folder.ResolvePermissionGroupsAsync(folder.TechnicalName);
return folder;
}
@@ -303,59 +308,128 @@ namespace C4IT.LIAM
if (segments.Length < 2)
return classification;
var serverName = segments[0];
var publishedShares = GetPublishedShareNames(serverName);
var firstBoundaryPath = BuildUncPath(segments, 2);
classification.ParentPath = segments.Length > 2
? BuildUncPath(segments, segments.Length - 1)
: string.Empty;
if (segments.Length == 2)
var dfsPrefixes = GetDfsObjectPrefixes(normalizedPath);
if (dfsPrefixes.Count > 0)
{
if (publishedShares.Contains(segments[1]))
{
classification.Kind = eNtfsPathKind.ClassicShare;
classification.BoundaryPath = normalizedPath;
return classification;
}
var namespaceRootPath = dfsPrefixes[0];
var deepestDfsPath = dfsPrefixes[dfsPrefixes.Count - 1];
if (Directory.Exists(normalizedPath))
if (PathsEqual(normalizedPath, namespaceRootPath))
{
classification.Kind = eNtfsPathKind.DfsNamespaceRoot;
classification.BoundaryPath = normalizedPath;
return classification;
}
return classification;
}
if (publishedShares.Contains(segments[1]))
{
classification.Kind = eNtfsPathKind.Folder;
classification.BoundaryPath = firstBoundaryPath;
classification.ParentBoundaryPath = segments.Length == 3
? firstBoundaryPath
: BuildUncPath(segments, segments.Length - 1);
return classification;
}
if (Directory.Exists(firstBoundaryPath))
{
if (segments.Length == 3)
if (PathsEqual(normalizedPath, deepestDfsPath))
{
classification.Kind = eNtfsPathKind.DfsLink;
classification.BoundaryPath = normalizedPath;
classification.ParentBoundaryPath = firstBoundaryPath;
classification.BoundaryPath = deepestDfsPath;
classification.ParentBoundaryPath = dfsPrefixes.Count > 1
? dfsPrefixes[dfsPrefixes.Count - 2]
: namespaceRootPath;
return classification;
}
classification.Kind = eNtfsPathKind.Folder;
classification.BoundaryPath = BuildUncPath(segments, 3);
classification.ParentBoundaryPath = segments.Length == 4
? BuildUncPath(segments, 3)
: BuildUncPath(segments, segments.Length - 1);
classification.BoundaryPath = deepestDfsPath;
classification.ParentBoundaryPath = classification.ParentPath;
return classification;
}
var shareBoundaryPath = GetPublishedShareBoundaryPath(segments);
if (!string.IsNullOrWhiteSpace(shareBoundaryPath))
{
if (PathsEqual(normalizedPath, shareBoundaryPath))
{
classification.Kind = eNtfsPathKind.ClassicShare;
classification.BoundaryPath = shareBoundaryPath;
return classification;
}
classification.Kind = eNtfsPathKind.Folder;
classification.BoundaryPath = shareBoundaryPath;
classification.ParentBoundaryPath = classification.ParentPath;
return classification;
}
if (Directory.Exists(normalizedPath))
{
classification.Kind = eNtfsPathKind.Folder;
classification.ParentBoundaryPath = classification.ParentPath;
}
return classification;
}
private List<string> GetDfsObjectPrefixes(string path)
{
var normalizedPath = NormalizeUncPath(path);
var segments = GetUncSegments(normalizedPath);
var prefixes = new List<string>();
for (var segmentCount = 2; segmentCount <= segments.Length; segmentCount++)
{
var prefix = BuildUncPath(segments, segmentCount);
string entryPath;
if (!TryGetDfsEntryPath(prefix, out entryPath))
continue;
prefixes.Add(!string.IsNullOrWhiteSpace(entryPath)
? NormalizeUncPath(entryPath)
: prefix);
}
return prefixes
.Distinct(StringComparer.OrdinalIgnoreCase)
.OrderBy(i => GetUncSegments(i).Length)
.ToList();
}
private bool TryGetDfsEntryPath(string path, out string entryPath)
{
var normalizedPath = NormalizeUncPath(path);
if (dfsEntryPathCache.TryGetValue(normalizedPath, out entryPath))
return !string.IsNullOrWhiteSpace(entryPath);
string resolvedEntryPath;
if (cNetworkConnection.TryGetDfsEntryPath(normalizedPath, out resolvedEntryPath))
{
entryPath = NormalizeUncPath(string.IsNullOrWhiteSpace(resolvedEntryPath) ? normalizedPath : resolvedEntryPath);
dfsEntryPathCache[normalizedPath] = entryPath;
return true;
}
dfsEntryPathCache[normalizedPath] = string.Empty;
entryPath = string.Empty;
return false;
}
private string GetPublishedShareBoundaryPath(string[] segments)
{
if (segments == null || segments.Length < 2)
return string.Empty;
var serverName = segments[0];
var publishedShares = GetPublishedShareNames(serverName);
if (!publishedShares.Contains(segments[1]))
return string.Empty;
return BuildUncPath(segments, 2);
}
private bool PathsEqual(string left, string right)
{
return string.Equals(
NormalizeUncPath(left),
NormalizeUncPath(right),
StringComparison.OrdinalIgnoreCase);
}
private string NormalizeUncPath(string path)
{
if (string.IsNullOrWhiteSpace(path))