0

I have a DFS folder path...

\\domain.name\SharesRoot\DFSShare

I need to get the actual folder target of this root link or in other words the local server path that DFSShare is targetting

The local server path/folder target is as follows

\\MyServer\Share\MyShare

I've successfully used the NetDfsGetClientInfo() method to retrieve SOME of the needed information from the given DFS path

The DFS_STORAGE_INFO object that this method populates gives me the following data

  • State: irrelevant data
  • ServerName: 'MyServer'
  • ShareName: 'SharesRoot'

While the ServerName is what I'm looking for, the ShareName does not help me at all.

I need to find out what the path is on MyServer that the DFS path \\domain.name\SharesRoot\DFSShare is targetting

NetDfsGetInfo() gives me 1168 error, so that method is not helping, and I'm yet to try NetDfsEnum(), however my hopes aren't high seeing as it populates the same structs as the NetDfsGetClientInfo()... if anyone has any other leads here I'd be incredibly grateful!

rawberry
  • 192
  • 9

2 Answers2

1

I found that on the client I needed to access the DFS path first, then something in Windows caches this, so that when I then perform the NetDfsGetClientInfo() I get the underlying path (not the dfs path)

So this is what worked for me:

string dfsPath = "\\domain.name\SharesRoot\DFSShare";

//by doing Directory.Exists we access the DFS path, causing Windows to resolve it
if (Directory.Exists(dfsPath))
{
    string dfsPath = DFS.GetSharePath(dfsPath);
    MessageBox.Show($"{dfsPath});
}

This is the class code I found elsewhere (not mine)

  class DFS
{
    #region Import
    [DllImport("Netapi32.dll", EntryPoint = "NetApiBufferFree")]
    public static extern uint NetApiBufferFree(IntPtr Buffer);

    [DllImport("Netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int NetDfsGetInfo(
        [MarshalAs(UnmanagedType.LPWStr)] string EntryPath,
        [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
        [MarshalAs(UnmanagedType.LPWStr)] string ShareName,
        int Level,
        out IntPtr Buffer);

    [DllImport("Netapi32.dll")]
    public static extern int NetDfsGetClientInfo(
        [MarshalAs(UnmanagedType.LPWStr)] string EntryPath,
        [MarshalAs(UnmanagedType.LPWStr)] string ServerName,
        [MarshalAs(UnmanagedType.LPWStr)] string ShareName,
        int Level,
        out IntPtr Buffer);

    #endregion

    #region Structures

    public struct DFS_INFO_3
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        public string EntryPath;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string Comment;
        public UInt32 State;
        public UInt32 NumberOfStorages;
        public IntPtr Storages;
    }

    public struct DFS_STORAGE_INFO
    {
        public Int32 State;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ServerName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string ShareName;
    }

    #endregion

    const int DFS_VOLUME_STATE_OK = 0x00000001;
    const int DFS_VOLUME_STATE_ONLINE = 0x00000004;
    const int DFS_STORAGE_STATE_ONLINE = 0x00000002;
    const int DFS_STORAGE_STATE_ACTIVE = 0x00000004;

    public static String GetSharePath(String DFSPath)
    {
        if (!String.IsNullOrEmpty(DFSPath))
        {
            IntPtr Buffer = IntPtr.Zero;
            try
            {
                Console.WriteLine("User : {0}, Domain= {1}", Environment.UserName, Environment.UserDomainName);
                Console.WriteLine("Path is {0}", DFSPath);
                int Error = NetDfsGetClientInfo(DFSPath, null, null, 3, out Buffer);
                Console.WriteLine("Error code {0}", Error);
                if (Error == 0)
                {
                    DFS_INFO_3 DFSInfo = (DFS_INFO_3)Marshal.PtrToStructure(Buffer, typeof(DFS_INFO_3));
                    if ((DFSInfo.State & DFS_VOLUME_STATE_OK) > 0)
                    {
                        String SubPath = DFSPath.Remove(0, 1 + DFSInfo.EntryPath.Length).TrimStart(new Char[] { '\\' });
                        for (int i = 0; i < DFSInfo.NumberOfStorages; i++)
                        {
                            IntPtr Storage = new IntPtr(DFSInfo.Storages.ToInt64() + i * Marshal.SizeOf(typeof(DFS_STORAGE_INFO)));
                            DFS_STORAGE_INFO StorageInfo = (DFS_STORAGE_INFO)Marshal.PtrToStructure(Storage, typeof(DFS_STORAGE_INFO));
                            if ((StorageInfo.State & DFS_STORAGE_STATE_ACTIVE) > 0)
                            {
                                if (String.IsNullOrEmpty(SubPath))
                                {
                                    return String.Format(@"\\{0}\{1}", StorageInfo.ServerName, StorageInfo.ShareName);
                                }
                                else
                                {
                                    return (String.Format(@"\\{0}\{1}\{2}", StorageInfo.ServerName, StorageInfo.ShareName, SubPath));
                                }
                            }
                        }
                    }
                }
                else if (Error == 2662)
                    return DFSPath;
            }
            finally
            {
                NetApiBufferFree(Buffer);
            }
        }
        return null;
    }

    public static String GetShareName(String SharePath)
    {
        if (!String.IsNullOrEmpty(SharePath))
        {
            String[] Tokens = SharePath.Trim(new Char[] { '\\' }).Split(new Char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
            if (2 <= Tokens.Length)
                return Tokens[1];
        }
        return null;
    }


}
Daniel Barnes
  • 163
  • 2
  • 9
0

So it seems the NetDfsGetClientInfo() is indeed the answer for my particular needs.

The issue is that its behavior changes based on whether it's called on a DFS client machine versus if it's called on the DFS server machine.

On a client machine the call returns a DFS_STORAGE_INFO object with the following data:

  • ServerName: 'MyServer'
  • ShareName: 'Share'

All that's needed once this is returned is to append the subpath, which can be stripped out using the entry path of the DFS_INFO_3 object and the original path used in the NetDfsGetClientInfo() call

rawberry
  • 192
  • 9