diff --git a/LiamNtfs/C4IT.LIAM.Ntfs.cs b/LiamNtfs/C4IT.LIAM.Ntfs.cs index 5f2ddfc..d7bba16 100644 --- a/LiamNtfs/C4IT.LIAM.Ntfs.cs +++ b/LiamNtfs/C4IT.LIAM.Ntfs.cs @@ -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> publishedShareCache = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary dfsEntryPathCache = new Dictionary(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 GetDfsObjectPrefixes(string path) + { + var normalizedPath = NormalizeUncPath(path); + var segments = GetUncSegments(normalizedPath); + var prefixes = new List(); + + 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)) diff --git a/LiamNtfs/C4IT_IAM_SET/cNetworkConnection.cs b/LiamNtfs/C4IT_IAM_SET/cNetworkConnection.cs index 852ce65..0e27023 100644 --- a/LiamNtfs/C4IT_IAM_SET/cNetworkConnection.cs +++ b/LiamNtfs/C4IT_IAM_SET/cNetworkConnection.cs @@ -101,6 +101,35 @@ namespace C4IT_IAM } } + public static bool TryGetDfsEntryPath(string dfsEntryPath, out string entryPath) + { + entryPath = string.Empty; + if (string.IsNullOrWhiteSpace(dfsEntryPath)) + return false; + + IntPtr buffer = IntPtr.Zero; + try + { + int result = NetDfsGetInfo(dfsEntryPath, null, null, 1, ref buffer); + if (result != NERR_Success || buffer == IntPtr.Zero) + return false; + + DFS_INFO_1 info = (DFS_INFO_1)Marshal.PtrToStructure(buffer, typeof(DFS_INFO_1)); + entryPath = info.EntryPath ?? dfsEntryPath; + return !string.IsNullOrWhiteSpace(entryPath); + } + catch (Exception ex) + { + DefaultLogger.LogException(ex); + return false; + } + finally + { + if (buffer != IntPtr.Zero) + NetApiBufferFree(buffer); + } + } + [DllImport("mpr.dll")] private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags); @@ -123,6 +152,15 @@ namespace C4IT_IAM ref int resume_handle ); + [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern int NetDfsGetInfo( + string DfsEntryPath, + string ServerName, + string ShareName, + int Level, + ref IntPtr Buffer + ); + } [StructLayout(LayoutKind.Sequential)] @@ -205,4 +243,10 @@ namespace C4IT_IAM return shi1_netname; } } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct DFS_INFO_1 + { + public string EntryPath; + } }