//-----------------------------------------------------------------------------
//
//  Copyright (C) 1995, Microsoft Corporation
//
//  File:       dfsstub.c
//
//  Contents:   Stub file for the NetDfsXXX APIs. The stubs turn around and
//              call the NetrDfsXXX APIs on the appropriate server, or (in the
//              case of NetDfs[G/S]etClientXXX, go directly to the driver on the
//              local machine.
//
//  Classes:
//
//  Functions:  NetDfsXXX
//
//  History:    01-10-96        Milans created
//
//-----------------------------------------------------------------------------

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdlib.h>
#include <lm.h>
#include <lmdfs.h>
#include <dfsp.h>
#include <netdfs.h>
#include <dfsfsctl.h>
#include <dsrole.h>
#include <ntdsapi.h>
#include <dsgetdc.h>

#include <winldap.h>

#include <aclapi.h>
#include <permit.h>

#include "dfsacl.h"


#define MAX_DFS_LDAP_RETRY 20


#define IS_UNC_PATH(wsz, cw)                                    \
    ((cw) > 2 && (wsz)[0] == L'\\' && (wsz)[1] == L'\\')

#define IS_VALID_PREFIX(wsz, cw)                                \
    ((cw) > 1 && (wsz)[0] == L'\\' && (wsz)[1] != L'\\')

#define IS_VALID_DFS_PATH(wsz, cw)                              \
    ((cw) > 0 && (wsz)[0] != L'\\')

#define IS_VALID_STRING(wsz)                                    \
    ((wsz) != NULL && (wsz)[0] != UNICODE_NULL)

#define POINTER_TO_OFFSET(field, buffer)  \
    ( ((PCHAR)field) -= ((ULONG_PTR)buffer) )

#define OFFSET_TO_POINTER(field, buffer)  \
        ( ((PCHAR)field) += ((ULONG_PTR)buffer) )

NET_API_STATUS
DfspGetDfsNameFromEntryPath(
    LPWSTR wszEntryPath,
    DWORD cwEntryPath,
    LPWSTR *ppwszDfsName);

NET_API_STATUS
DfspGetMachineNameFromEntryPath(
    LPWSTR wszEntryPath,
    DWORD cwEntryPath,
    LPWSTR *ppwszMachineName);

NET_API_STATUS
DfspBindRpc(
    IN  LPWSTR DfsName,
    OUT RPC_BINDING_HANDLE *BindingHandle);

NET_API_STATUS
DfspBindToServer(
    IN  LPWSTR DfsName,
    OUT RPC_BINDING_HANDLE *BindingHandle);

VOID
DfspFreeBinding(
    RPC_BINDING_HANDLE BindingHandle);

NET_API_STATUS
DfspVerifyBinding();

VOID
DfspFlushPkt(
    LPWSTR DfsEntryPath);

NTSTATUS
DfspIsThisADfsPath(
    LPWSTR pwszPathName);

DWORD
DfspDfsPathToRootMachine(
    LPWSTR pwszDfsName,
    LPWSTR *ppwszMachineName);

DWORD
DfspCreateFtDfs(
    LPWSTR ServerName,
    LPWSTR DcName,
    BOOLEAN IsPdc,
    LPWSTR RootShare,
    LPWSTR FtDfsName,
    LPWSTR Comment,
    DWORD  Flags);

DWORD
DfspTearDownFtDfs(
    IN LPWSTR wszServerName,
    IN LPWSTR wszDsAddress,
    IN LPWSTR wszRootShare,
    IN LPWSTR wszFtDfsName,
    IN DWORD  dwFlags);

VOID
DfspFlushFtTable(
    LPWSTR wszDcName,
    LPWSTR wszFtDfsName);


NTSTATUS
DfspSetDomainToDc(
    LPWSTR DomainName,
    LPWSTR DcName);

DWORD
I_NetDfsIsThisADomainName(
    LPWSTR wszDomain);

DWORD
DfspIsThisADomainName(
    LPWSTR wszName,
    PWCHAR *List);

VOID
DfspNotifyFtRoot(
    LPWSTR wszServerShare,
    LPWSTR wszDcName);

DWORD
NetpDfsAdd2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName,
    LPWSTR Comment,
    DWORD Flags);

DWORD
DfspAdd2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName,
    LPWSTR Comment,
    DWORD Flags);

DWORD
NetpDfsSetInfo2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName,
    DWORD  Level,
    LPDFS_INFO_STRUCT pDfsInfo);

DWORD
DfspSetInfo2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName,
    DWORD  Level,
    LPDFS_INFO_STRUCT pDfsInfo);

DWORD
NetpDfsRemove2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName);

DWORD
DfspRemove2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName);

DWORD
DfspLdapOpen(
    LPWSTR wszDcName,
    LDAP **ppldap,
    LPWSTR *pwszDfsConfigDN);



INT
_cdecl
DfspCompareDsDomainControllerInfo1(
    const void *p1,
    const void *p2);

BOOLEAN
DfspIsInvalidName(
    LPWSTR ShareName);

static LPWSTR InvalidNames[] = {
    L"SYSVOL",
    L"PIPE",
    L"IPC$",
    L"ADMIN$",
    L"MAILSLOT",
    L"NETLOGON",
    NULL};

//
// The APIs are all single-threaded - only 1 can be outstanding at a time in
// any one process. The following critical section is used to gate the calls.
// The critical section is initialized at DLL Load time.
//

CRITICAL_SECTION NetDfsApiCriticalSection;

#define ENTER_NETDFS_API EnterCriticalSection( &NetDfsApiCriticalSection );
#define LEAVE_NETDFS_API LeaveCriticalSection( &NetDfsApiCriticalSection );

//
// The name of the Dfs configuration container
//
static WCHAR DfsConfigContainer[] = L"CN=Dfs-Configuration,CN=System";

#if DBG
ULONG DfsDebug = 0;
#endif

VOID
NetDfsApiInitialize(void)
{
#if DBG
    DWORD dwErr;
    DWORD dwType;
    DWORD cbData;
    HKEY hkey;

    dwErr = RegOpenKey( HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\Dfs", &hkey );

    if (dwErr == ERROR_SUCCESS) {

        cbData = sizeof(DfsDebug);

        dwErr = RegQueryValueEx(
                    hkey,
                    L"NetApiDfsDebug",
                    NULL,
                    &dwType,
                    (PBYTE) &DfsDebug,
                    &cbData);

        if (!(dwErr == ERROR_SUCCESS && dwType == REG_DWORD)) {

            DfsDebug = 0;

        }

        RegCloseKey(hkey);

    }

#endif
}
    


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsAdd
//
//  Synopsis:   Creates a new volume, adds a replica to an existing volume,
//              or creates a link to another Dfs.
//
//  Arguments:  [DfsEntryPath] -- Name of volume/link to create/add replica
//                      to.
//              [ServerName] -- Name of server hosting the storage, or for
//                      link, name of Dfs root.
//              [ShareName] -- Name of share hosting the storage.
//              [Flags] -- Describes what is being added.
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- DfsEntryPath and/or ServerName
//                      and/or ShareName and/or Flags are incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- DfsEntryPath does not correspond to a
//                      existing Dfs volume.
//
//              [NERR_DfsVolumeAlreadyExists] -- DFS_ADD_VOLUME was specified
//                      and a volume with DfsEntryPath already exists.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsAdd(
    IN LPWSTR DfsEntryPath,
    IN LPWSTR ServerName,
    IN LPWSTR ShareName,
    IN LPWSTR Comment,
    IN DWORD Flags)
{
    NET_API_STATUS dwErr;
    DWORD cwDfsEntryPath;
    LPWSTR pwszDfsName = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAdd(%ws,%ws,%ws,%ws,%d)\n",
                        DfsEntryPath,
                        ServerName,
                        ShareName,
                        Comment,
                        Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(DfsEntryPath) ||
            !IS_VALID_STRING(ServerName) ||
                !IS_VALID_STRING(ShareName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                DfsEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        //
        // By now, we should have a valid pwszDfsName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                dwErr = NetrDfsAdd(
                            DfsEntryPath,
                            ServerName,
                            ShareName,
                            Comment,
                            Flags);

            } RpcExcept(1) {

                dwErr = RpcExceptionCode();

            } RpcEndExcept;

            DfspFreeBinding( netdfs_bhandle );

        }

    }

    LEAVE_NETDFS_API

    //
    // If we failed with ERROR_NOT_SUPPORTED, this is an NT5+ server,
    // so we use the NetrDfsAdd2() call instead.
    //

    if (dwErr == ERROR_NOT_SUPPORTED) {

        dwErr = NetpDfsAdd2(
                        pwszDfsName,
                        DfsEntryPath,
                        ServerName,
                        ShareName,
                        Comment,
                        Flags);
                    
    }

    if (pwszDfsName != NULL)
        free(pwszDfsName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAdd returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
NetpDfsAdd2(
    LPWSTR RootName,
    LPWSTR DfsEntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName,
    LPWSTR Comment,
    DWORD Flags)
{
    NET_API_STATUS dwErr;
    ULONG i;
    ULONG NameCount;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsAdd2(%ws,%ws,%ws,%ws,%ws,%d)\n",
                 RootName,
                 DfsEntryPath,
                 ServerName,
                 ShareName,
                 Comment,
                 Flags);
#endif

    //
    // Contact the server and ask for its domain name
    //

    dwErr = DsRoleGetPrimaryDomainInformation(
                RootName,
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsRoleGetPrimaryDomainInformation returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DomainNameDns is NULL\n", NULL);
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    //
    // Get the PDC in that domain
    //

    dwErr = DsGetDcName(
                NULL,
                pPrimaryDomainInfo->DomainNameDns,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

   
    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" NetpDfsAdd2:DsGetDcName(%ws) returned %d\n",
                        pPrimaryDomainInfo->DomainNameDns,
                        dwErr);
#endif
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Call the server
    //

    dwErr = DfspAdd2(
                RootName,
                DfsEntryPath,
                &pDomainControllerInfo->DomainControllerName[2],
                ServerName,
                ShareName,
                Comment,
                Flags);

    LEAVE_NETDFS_API

Cleanup:

    if (pPrimaryDomainInfo != NULL)
        DsRoleFreeMemory(pPrimaryDomainInfo);

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsAdd2 returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
DfspAdd2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName,
    LPWSTR Comment,
    DWORD Flags)
{
    DWORD dwErr;
    PDFSM_ROOT_LIST RootList = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspAdd2(%ws,%ws,%ws,%ws,%ws,%ws,%d)\n",
            RootName,
            EntryPath,
            DcName,
            ServerName,
            ShareName,
            Comment,
            Flags);
#endif

    dwErr = DfspBindRpc( RootName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsAdd2(
                        EntryPath,
                        DcName,
                        ServerName,
                        ShareName,
                        Comment,
                        Flags,
                        &RootList);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

#if DBG
    if (DfsDebug) {
        if (dwErr == ERROR_SUCCESS && RootList != NULL) {
            ULONG n;

            DbgPrint("cEntries=%d\n", RootList->cEntries);
            for (n = 0; n < RootList->cEntries; n++)
                DbgPrint("[%d]%ws\n", n, RootList->Entry[n].ServerShare);
        }
    }
#endif

    if (dwErr == ERROR_SUCCESS && RootList != NULL) {

        ULONG n;

        for (n = 0; n < RootList->cEntries; n++) {

            DfspNotifyFtRoot(
                RootList->Entry[n].ServerShare,
                DcName);

        }

        NetApiBufferFree(RootList);

    }
#if DBG
    if (DfsDebug)
        DbgPrint("DfspAdd2 returning %d\n", dwErr);
#endif

    return dwErr;

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsAddFtRoot
//
//  Synopsis:   Creates a new FtDfs, adds a new server to an existing FtDfs.
//
//  Arguments:  [ServerName] -- Name of server to make a root, or to join to an existing FtDfs.
//              [RootShare] -- Name of share hosting the storage.
//              [FtDfsName] -- Name of FtDfs to join or create.
//              [Comment] -- Optional comment
//              [Flags] -- Flags to operation
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or RootShare are incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsAddFtRoot(
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN LPWSTR FtDfsName,
    IN LPWSTR Comment,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;
    BOOLEAN IsRoot = FALSE;
    ULONG Timeout = 0;
    ULONG i;
    ULONG NameCount;
    LPWSTR DcName = NULL;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
    PDFSM_ROOT_LIST RootList = NULL;
#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddFtRoot(%ws,%ws,%ws,%ws,%d)\n",
            ServerName,
            RootShare,
            FtDfsName,
            Comment,
            Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName) ||
        !IS_VALID_STRING(RootShare) ||
        !IS_VALID_STRING(FtDfsName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (FtDfsName[0] == L' ' || DfspIsInvalidName(FtDfsName) == TRUE) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // WE let the server add the root for us. If this fails,
    // with invalid parameter, we then get the dc name
    // and get the root list for NT5 DFS servers.
    //
    ENTER_NETDFS_API
    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );
    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsAddFtRoot(
                        ServerName,
                        L"",
                        RootShare,
                        FtDfsName,
                        (Comment != NULL) ? Comment : L"",
                        L"",
                        0,
                        Flags,
                        &RootList );
#if DBG
            if (DfsDebug)
                DbgPrint("NetrDfsAddFtRoot returned %d\n", dwErr);
#endif

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );
    }
    LEAVE_NETDFS_API

    if (dwErr != ERROR_INVALID_PARAMETER)
    {
        goto Cleanup;
    }
    //
    // Contact the server and ask for the DC to work with
    //

    dwErr = NetDfsGetDcAddress(
                ServerName,
                &DcName,
                &IsRoot,
                &Timeout);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("NetDfsGetDcAddress returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (IsRoot == TRUE) {
#if DBG
        if (DfsDebug)
            DbgPrint("Root already exists!\n");
#endif
        dwErr = ERROR_ALREADY_EXISTS;
        goto Cleanup;
    }

    //
    // Now get its domain name
    //

    dwErr = DsRoleGetPrimaryDomainInformation(
                ServerName,
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsRoleGetPrimaryDomainInformation returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsRoleGetPrimaryDomainInformation returned NULL domain name\n");
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    //
    // Get the PDC in that domain
    //

    dwErr = DsGetDcName(
                NULL,
                pPrimaryDomainInfo->DomainNameDns,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

   
    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsGetDcName(%ws) returned %d\n", pPrimaryDomainInfo->DomainNameDns, dwErr);
#endif
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Add the Ds object and tell the server to join itself
    //

    dwErr = DfspCreateFtDfs(
                ServerName,
                &pDomainControllerInfo->DomainControllerName[2],
                TRUE,
                RootShare,
                FtDfsName,
                Comment,
                Flags);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DfspCreateFtDfs returned %d\n", dwErr);
#endif
        LEAVE_NETDFS_API
        goto Cleanup;
    }

    //
    // Tell the local MUP to crack ftdfs names using the selected DC
    //

    DfspSetDomainToDc(
        pPrimaryDomainInfo->DomainNameDns,
        &pDomainControllerInfo->DomainControllerName[2]);

    if (pPrimaryDomainInfo->DomainNameFlat != NULL) {
        PWCHAR wCp = &pDomainControllerInfo->DomainControllerName[2];

        for (; *wCp != L'\0' && *wCp != L'.'; wCp++)
            /* NOTHING */;
        *wCp =  (*wCp == L'.') ? L'\0' : *wCp;
        DfspSetDomainToDc(
            pPrimaryDomainInfo->DomainNameFlat,
            &pDomainControllerInfo->DomainControllerName[2]);
    }

    LEAVE_NETDFS_API

Cleanup:

    if (pPrimaryDomainInfo != NULL)
        DsRoleFreeMemory(pPrimaryDomainInfo);

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

    if (DcName != NULL)
        NetApiBufferFree(DcName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddFtRoot returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspCompareDsDomainControllerInfo1
//
//  Synopsis:   Helper/compare func for qsort of DsGetDomainControllerInfo's results
//
//-----------------------------------------------------------------------------

INT
_cdecl
DfspCompareDsDomainControllerInfo1(
    const void *p1,
    const void *p2)
{
    PDS_DOMAIN_CONTROLLER_INFO_1 pInfo1 = (PDS_DOMAIN_CONTROLLER_INFO_1)p1;
    PDS_DOMAIN_CONTROLLER_INFO_1 pInfo2 = (PDS_DOMAIN_CONTROLLER_INFO_1)p2;
    UNICODE_STRING s1;
    UNICODE_STRING s2;

    if (pInfo1->DnsHostName == NULL || pInfo2->DnsHostName == NULL)
        return 0;

    RtlInitUnicodeString(&s1, pInfo1->DnsHostName);
    RtlInitUnicodeString(&s2, pInfo2->DnsHostName);

    return RtlCompareUnicodeString(&s1,&s2,TRUE);

}

//+----------------------------------------------------------------------------
//
//  Function:   NetDfsAddStdRoot
//
//  Synopsis:   Creates a new Std Dfs.
//
//  Arguments:  [ServerName] -- Name of server to make a root.
//                      existing Dfs.
//              [RootShare] -- Name of share hosting the storage.
//              [Comment] -- Optional comment
//              [Flags] -- Flags
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or RootShare are incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsAddStdRoot(
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN LPWSTR Comment,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddStdRoot(%ws,%ws,%ws,%d)\n",
                    ServerName,
                    RootShare,
                    Comment,
                    Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName) ||
            !IS_VALID_STRING(RootShare)) {
        return( ERROR_INVALID_PARAMETER );
    }

    ENTER_NETDFS_API

    //
    // We should have a valid ServerName. Lets try to bind to it,
    // and call the server.
    //

    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsAddStdRoot(
                        ServerName,
                        RootShare,
                        (Comment != NULL) ? Comment : L"",
                        Flags);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddStdRoot returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   NetDfsAddStdRootForced
//
//  Synopsis:   Creates a new Std Dfs, also specifying the share
//
//  Arguments:  [ServerName] -- Name of server to make a root.
//                      existing Dfs.
//              [RootShare] -- Name of share hosting the storage.
//              [Comment] -- Optional comment
//              [Share] -- Name of drive:\dir hosting the share
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or RootShare are incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsAddStdRootForced(
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN LPWSTR Comment,
    IN LPWSTR Share)
{
    NET_API_STATUS dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddStdRootForced(%ws,%ws,%ws,%ws)\n",
            ServerName,
            RootShare,
            Comment,
            Share);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName) ||
            !IS_VALID_STRING(RootShare) ||
                !IS_VALID_STRING(Share)) {
        return( ERROR_INVALID_PARAMETER );
    }

    ENTER_NETDFS_API

    //
    // We should have a valid ServerName. Lets try to bind to it,
    // and call the server.
    //

    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsAddStdRootForced(
                        ServerName,
                        RootShare,
                        (Comment != NULL) ? Comment : L"",
                        Share);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsAddStdRootForced returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsGetDcAddress
//
//  Synopsis:   Asks a server for its DC to use to place the dfs blob to make
//              the server a root.
//
//  Arguments:  [ServerName] -- Name of server we will be making an FtDfs root
//              [DcName] -- DC Name
//              [IsRoot] -- TRUE if Server is a root, FALSE otherwise
//              [Timeout] -- Timeout the server is using
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsGetDcAddress(
    IN LPWSTR ServerName,
    IN OUT LPWSTR *DcName,
    IN OUT BOOLEAN *IsRoot,
    IN OUT ULONG *Timeout)
{
    NET_API_STATUS dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetDcAddress(%ws)\n", ServerName);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName)|| DcName == NULL || IsRoot == NULL || Timeout == NULL) {
        return( ERROR_INVALID_PARAMETER );
    }

    ENTER_NETDFS_API

    //
    // We should have a valid ServerName. Lets try to bind to it,
    // and call the server.
    //

    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsGetDcAddress(
                        ServerName,
                        DcName,
                        IsRoot,
                        Timeout);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetDcAddress: returned %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsRemove
//
//  Synopsis:   Deletes a Dfs volume, removes a replica from an existing
//              volume, or removes a link to another Dfs.
//
//  Arguments:  [DfsEntryPath] -- Name of volume/link to remove.
//              [ServerName] -- Name of server hosting the storage. Must be
//                      NULL if removing Link.
//              [ShareName] -- Name of share hosting the storage. Must be
//                      NULL if removing Link.
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- DfsEntryPath is incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- DfsEntryPath does not correspond to
//                      a valid entry path.
//
//              [NERR_DfsNotALeafVolume] -- Unable to delete the volume
//                      because it is not a leaf volume.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsRemove(
    IN LPWSTR DfsEntryPath,
    IN LPWSTR ServerName,
    IN LPWSTR ShareName)
{
    NET_API_STATUS dwErr;
    DWORD cwDfsEntryPath;
    LPWSTR pwszDfsName = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemove(%ws,%ws,%ws)\n",
                DfsEntryPath,
                ServerName,
                ShareName);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(DfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                DfsEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        dwErr = DfspBindRpc(pwszDfsName, &netdfs_bhandle);

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                dwErr = NetrDfsRemove(
                            DfsEntryPath,
                            ServerName,
                            ShareName);

            } RpcExcept(1) {

                dwErr = RpcExceptionCode();

            } RpcEndExcept;

            DfspFreeBinding( netdfs_bhandle );

        }

    }

    LEAVE_NETDFS_API

    //
    // If we failed with ERROR_NOT_SUPPORTED, this is an NT5+ server,
    // so we use the NetrDfsRemove2() call instead.
    //

    if (dwErr == ERROR_NOT_SUPPORTED) {

        dwErr = NetpDfsRemove2(
                        pwszDfsName,
                        DfsEntryPath,
                        ServerName,
                        ShareName);
                    
    }

    //
    // If we removed things from a dfs, the local pkt
    // may now be out of date.  [92216]
    // Flush the local pkt
    //
    if (dwErr == NERR_Success) {

        DfspFlushPkt(DfsEntryPath);

    }

    if (pwszDfsName != NULL)
        free( pwszDfsName );

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemove returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
NetpDfsRemove2(
    LPWSTR RootName,
    LPWSTR DfsEntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName)
{
    NET_API_STATUS dwErr;
    ULONG i;
    ULONG NameCount;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDS_DOMAIN_CONTROLLER_INFO_1 pDsDomainControllerInfo1 = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
    HANDLE hDs = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsRemove2(%ws,%ws,%ws,%ws)\n",
                    RootName,
                    DfsEntryPath,
                    ServerName,
                    ShareName);
#endif

    //
    // Ask for its domain name
    //
    dwErr = DsRoleGetPrimaryDomainInformation(
                RootName,
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsRoleGetPrimaryDomainInformation returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DomainNameDns is NULL\n");
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    //
    // Get the PDC in that domain
    //

    dwErr = DsGetDcName(
                NULL,
                pPrimaryDomainInfo->DomainNameDns,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsGetDcName(%ws) returned %d\n", pPrimaryDomainInfo->DomainNameDns);
#endif
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Tell the root server to remove this server/share
    //

    dwErr = DfspRemove2(
                RootName,
                DfsEntryPath,
                &pDomainControllerInfo->DomainControllerName[2],
                ServerName,
                ShareName);

    LEAVE_NETDFS_API

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DfspRemove2 returned %d\n");
#endif
        goto Cleanup;
    }

Cleanup:



    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

    if (pPrimaryDomainInfo != NULL)
        DsRoleFreeMemory(pPrimaryDomainInfo);

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsRemove2 returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
DfspRemove2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName)
{
    DWORD dwErr;
    PDFSM_ROOT_LIST RootList = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspRemove2(%ws,%ws,%ws,%ws,%ws)\n",
                RootName,
                EntryPath,
                DcName,
                ServerName,
                ShareName);
#endif

    dwErr = DfspBindRpc( RootName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsRemove2(
                        EntryPath,
                        DcName,
                        ServerName,
                        ShareName,
                        &RootList);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

#if DBG
    if (DfsDebug) {
        if (dwErr == ERROR_SUCCESS && RootList != NULL) {
            ULONG n;

            DbgPrint("cEntries=%d\n", RootList->cEntries);
            for (n = 0; n < RootList->cEntries; n++)
                DbgPrint("[%d]%ws\n", n, RootList->Entry[n].ServerShare);
        }
    }
#endif

    if (dwErr == ERROR_SUCCESS && RootList != NULL) {

        ULONG n;

        for (n = 0; n < RootList->cEntries; n++) {

            DfspNotifyFtRoot(
                RootList->Entry[n].ServerShare,
                DcName);

        }

        NetApiBufferFree(RootList);

    }

#if DBG
    if (DfsDebug)
        DbgPrint("DfspRemove2 returning %d\n", dwErr);
#endif

    return dwErr;

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsRemoveFtRoot
//
//  Synopsis:   Deletes an FtDfs root, or unjoins a Server from an FtDfs as a root.
//
//  Arguments:  [ServerName] -- Name of server to unjoin from FtDfs.
//              [RootShare] -- Name of share hosting the storage.
//              [FtDfsName] -- Name of FtDfs to remove server from.
//              [Flags] -- Flags to operation
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or FtDfsName is incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for ServerName
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- FtDfsName does not correspond to
//                      a valid FtDfs.
//
//              [NERR_DfsNotALeafVolume] -- Unable to delete the volume
//                      because it is not a leaf volume.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsRemoveFtRoot(
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN LPWSTR FtDfsName,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;
    LPWSTR DcName = NULL;
    BOOLEAN IsRoot = FALSE;
    ULONG Timeout = 0;
    ULONG i;
    ULONG NameCount;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
    PDFSM_ROOT_LIST RootList = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveFtRoot(%ws,%ws,%ws,%d)\n",
            ServerName,
            RootShare,
            FtDfsName,
            Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName) ||
        !IS_VALID_STRING(RootShare) ||
        !IS_VALID_STRING(FtDfsName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // we first allow the server to do all the work, so pass in null
    // as dc name and root list. If that fails with error_invalid_param
    // we know we are dealing with a NT5 server, so go into compat mode.
    //
    ENTER_NETDFS_API
    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );
    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsRemoveFtRoot(
                        ServerName,
                        L"",
                        RootShare,
                        FtDfsName,
                        Flags,
                        &RootList );

#if DBG
            if (DfsDebug)
                DbgPrint("NetrDfsAddFtRoot returned %d\n", dwErr);
#endif

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );
    }
    LEAVE_NETDFS_API

    if (dwErr != ERROR_INVALID_PARAMETER)
    {
        goto Cleanup;
    }
    //
    // Contact the server and ask for the DC to work with
    //
#if 0
    dwErr = NetDfsGetDcAddress(
                ServerName,
                &DcName,
                &IsRoot,
                &Timeout);

    if (dwErr != ERROR_SUCCESS) {
        return dwErr;
    }

    if (IsRoot == FALSE) {
        dwErr = ERROR_SERVICE_DOES_NOT_EXIST;
        goto Cleanup;
    }
#endif
    //
    // Now ask it for its dns and domain names
    //

    dwErr = DsRoleGetPrimaryDomainInformation(
                ServerName,
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsRoleGetPrimaryDomainInformation returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsRoleGetPrimaryDomainInformation returned NULL domain name\n");
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    //
    // Get the PDC in that domain
    //

    dwErr = DsGetDcName(
                NULL,
                pPrimaryDomainInfo->DomainNameDns,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

   
    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsGetDcName(%ws) returned %d\n", pPrimaryDomainInfo->DomainNameDns, dwErr);
#endif
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Tell the server to unjoin and update the Ds object
    //

    dwErr = DfspTearDownFtDfs(
                ServerName,
                &pDomainControllerInfo->DomainControllerName[2],
                RootShare,
                FtDfsName,
                Flags);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DfspTearDownFtDfs returned %d\n", dwErr);
#endif
        LEAVE_NETDFS_API
        goto Cleanup;
    }

    //
    // Tell the local MUP to crack ftdfs names using the selected DC
    //

    DfspSetDomainToDc(
        pPrimaryDomainInfo->DomainNameDns,
        &pDomainControllerInfo->DomainControllerName[2]);

    if (pPrimaryDomainInfo->DomainNameFlat != NULL) {
        PWCHAR wCp = &pDomainControllerInfo->DomainControllerName[2];

        for (; *wCp != L'\0' && *wCp != L'.'; wCp++)
            /* NOTHING */;
        *wCp =  (*wCp == L'.') ? L'\0' : *wCp;
        DfspSetDomainToDc(
            pPrimaryDomainInfo->DomainNameFlat,
            &pDomainControllerInfo->DomainControllerName[2]);
    }

    LEAVE_NETDFS_API

Cleanup:

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

    if (DcName != NULL)
        NetApiBufferFree(DcName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveFtRoot returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   NetDfsRemoveFtRootForced
//
//  Synopsis:   Deletes an FtDfs root, or unjoins a Server from an FtDfs as a root.
//              Does not contact the root/server to do so - it simply updates the DS.
//
//  Arguments:  [DomainName] -- Name of domain the server is in.
//              [ServerName] -- Name of server to unjoin from FtDfs.
//              [RootShare] -- Name of share hosting the storage.
//              [FtDfsName] -- Name of FtDfs to remove server from.
//              [Flags] -- Flags to operation
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or FtDfsName is incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for ServerName
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- FtDfsName does not correspond to
//                      a valid FtDfs.
//
//              [NERR_DfsNotALeafVolume] -- Unable to delete the volume
//                      because it is not a leaf volume.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsRemoveFtRootForced(
    IN LPWSTR DomainName,
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN LPWSTR FtDfsName,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;
    LPWSTR DcName = NULL;
    BOOLEAN IsRoot = FALSE;
    ULONG Timeout = 0;
    ULONG i;
    ULONG NameCount;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveFtrootForced(%ws,%ws,%ws,%ws,%d)\n",
                    DomainName,
                    ServerName,
                    RootShare,
                    FtDfsName,
                    Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(DomainName) ||
        !IS_VALID_STRING(ServerName) ||
        !IS_VALID_STRING(RootShare) ||
        !IS_VALID_STRING(FtDfsName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Get the PDC in the domain
    //

    dwErr = DsGetDcName(
                NULL,
                DomainName,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

   
    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsGetDcName(%ws) returned %d\n", DomainName, dwErr);
#endif
        goto Cleanup;
    }

    //
    // Get the Dns name of the domain the DC is in
    //
    dwErr = DsRoleGetPrimaryDomainInformation(
                &pDomainControllerInfo->DomainControllerName[2],
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DsRoleGetPrimaryDomainInformation(%ws) returned %d\n",
                        &pDomainControllerInfo->DomainControllerName[2],
                        dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DomainNameDns is NULL\n");
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Tell the DC to remove the server from the DS objects
    //

    dwErr = DfspTearDownFtDfs(
                ServerName,
                &pDomainControllerInfo->DomainControllerName[2],
                RootShare,
                FtDfsName,
                Flags | DFS_FORCE_REMOVE);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DfspTearDownFtDfs returned %d\n", dwErr);
#endif
        LEAVE_NETDFS_API
        goto Cleanup;
    }

    //
    // Tell the local MUP to crack ftdfs names using the selected DC
    //

    DfspSetDomainToDc(
        pPrimaryDomainInfo->DomainNameDns,
        &pDomainControllerInfo->DomainControllerName[2]);

    if (pPrimaryDomainInfo->DomainNameFlat != NULL) {
        PWCHAR wCp = &pDomainControllerInfo->DomainControllerName[2];

        for (; *wCp != L'\0' && *wCp != L'.'; wCp++)
            /* NOTHING */;
        *wCp =  (*wCp == L'.') ? L'\0' : *wCp;
        DfspSetDomainToDc(
            pPrimaryDomainInfo->DomainNameFlat,
            &pDomainControllerInfo->DomainControllerName[2]);
    }

    LEAVE_NETDFS_API

Cleanup:

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

    if (pPrimaryDomainInfo != NULL)
        DsRoleFreeMemory(pPrimaryDomainInfo);

    if (DcName != NULL)
        NetApiBufferFree(DcName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveFtRootForced returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   NetDfsRemoveStdRoot
//
//  Synopsis:   Deletes a Dfs root.
//
//  Arguments:  [ServerName] -- Name of server to unjoin from Dfs.
//              [RootShare] -- Name of share hosting the storage.
//              [Flags] -- Flags to operation
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//              [ERROR_INVALID_PARAMETER] -- ServerName and/or RootShare is incorrect.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNotALeafVolume] -- Unable to delete the volume
//                      because it is not a leaf volume.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsRemoveStdRoot(
    IN LPWSTR ServerName,
    IN LPWSTR RootShare,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveStdRoot(%ws,%ws,%d)\n",
                ServerName,
                RootShare,
                Flags);
#endif

    //
    // Validate the string arguments so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName) ||
            !IS_VALID_STRING(RootShare)) {
        return( ERROR_INVALID_PARAMETER );
    }

    ENTER_NETDFS_API

    dwErr = DfspBindToServer(ServerName, &netdfs_bhandle);

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsRemoveStdRoot(
                        ServerName,
                        RootShare,
                        Flags);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsRemoveStdRoot returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsSetInfo
//
//  Synopsis:   Sets the comment or state of a Dfs volume or Replica.
//
//  Arguments:  [DfsEntryPath] -- Path to the volume. Implicityly indicates
//                      which server or domain to connect to.
//              [ServerName] -- Optional. If specified, only the state of
//                      the server supporting this volume is modified.
//              [ShareName] -- Optional. If specified, only the state of
//                      this share on the specified server is modified.
//              [Level] -- Must be 100 or 101
//              [Buffer] -- Pointer to DFS_INFO_100 or DFS_INFO_101
//
//  Returns:    [NERR_Success] -- If successfully set info.
//
//              [ERROR_INVALID_LEVEL] -- Level is not 100 or 101, 102
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
//                      or ShareName is specified but ServerName is not, or
//                      Buffer is NULL.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//              [NERR_DfsNoSuchShare] -- The indicated ServerName/ShareName do
//                      not support this Dfs volume.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetDfsSetInfo(
    IN  LPWSTR  DfsEntryPath,
    IN  LPWSTR  ServerName OPTIONAL,
    IN  LPWSTR  ShareName OPTIONAL,
    IN  DWORD   Level,
    IN  LPBYTE  Buffer)
{
    NET_API_STATUS dwErr;
    LPWSTR pwszDfsName = NULL;
    DWORD cwDfsEntryPath;
    DFS_INFO_STRUCT DfsInfo;


#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsSetInfo(%ws,%ws,%ws,%d)\n",
                DfsEntryPath,
                ServerName,
                ShareName,
                Level);
#endif

    if (!IS_VALID_STRING(DfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    if (!(Level >= 100 && Level <= 102)) {
        return( ERROR_INVALID_LEVEL );
    }

    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                DfsEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        //
        // By now, we should have a valid pwszDfsName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                DfsInfo.DfsInfo100 = (LPDFS_INFO_100) Buffer;

                dwErr = NetrDfsSetInfo(
                            DfsEntryPath,
                            ServerName,
                            ShareName,
                            Level,
                            &DfsInfo);

           } RpcExcept( 1 ) {

               dwErr = RpcExceptionCode();

           } RpcEndExcept;

           DfspFreeBinding( netdfs_bhandle );

        }

    }

    LEAVE_NETDFS_API

    //
    // If we failed with ERROR_NOT_SUPPORTED, this is an NT5+ server,
    // so we use the NetrDfsSetInfo2() call instead.
    //

    if (dwErr == ERROR_NOT_SUPPORTED) {

        dwErr = NetpDfsSetInfo2(
                        pwszDfsName,
                        DfsEntryPath,
                        ServerName,
                        ShareName,
                        Level,
                        &DfsInfo);
                    
    }

    if (pwszDfsName != NULL)
        free(pwszDfsName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsSetInfo returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
NetpDfsSetInfo2(
    LPWSTR RootName,
    LPWSTR DfsEntryPath,
    LPWSTR ServerName,
    LPWSTR ShareName,
    DWORD Level,
    LPDFS_INFO_STRUCT pDfsInfo)
{
    NET_API_STATUS dwErr;
    ULONG i;
    ULONG NameCount;
    PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPrimaryDomainInfo = NULL;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsSetInfo2(%ws,%ws,%ws,%ws,%d)\n",
                 RootName,
                 DfsEntryPath,
                 ServerName,
                 ShareName,
                 Level);
#endif

    //
    // Contact the server and ask for its domain name
    //

    dwErr = DsRoleGetPrimaryDomainInformation(
                RootName,
                DsRolePrimaryDomainInfoBasic,
                (PBYTE *)&pPrimaryDomainInfo);

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("DsRoleGetPrimaryDomainInformation returned %d\n", dwErr);
#endif
        goto Cleanup;
    }

    if (pPrimaryDomainInfo->DomainNameDns == NULL) {
#if DBG
        if (DfsDebug)
            DbgPrint(" DomainNameDns is NULL\n", NULL);
#endif
        dwErr = ERROR_CANT_ACCESS_DOMAIN_INFO;
        goto Cleanup;
    }

    //
    // Get the PDC in that domain
    //

    dwErr = DsGetDcName(
                NULL,
                pPrimaryDomainInfo->DomainNameDns,
                NULL,
                NULL,
                DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                &pDomainControllerInfo);

   
    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint(" NetpDfsSetInfo2:DsGetDcName(%ws) returned %d\n",
                        pPrimaryDomainInfo->DomainNameDns,
                        dwErr);
#endif
        goto Cleanup;
    }

    ENTER_NETDFS_API

    //
    // Call the server
    //

    dwErr = DfspSetInfo2(
                RootName,
                DfsEntryPath,
                &pDomainControllerInfo->DomainControllerName[2],
                ServerName,
                ShareName,
                Level,
                pDfsInfo);

    LEAVE_NETDFS_API

Cleanup:

    if (pPrimaryDomainInfo != NULL)
        DsRoleFreeMemory(pPrimaryDomainInfo);

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

#if DBG
    if (DfsDebug)
        DbgPrint("NetpDfsSetInfo2 returning %d\n", dwErr);
#endif

    return( dwErr );

}

DWORD
DfspSetInfo2(
    LPWSTR RootName,
    LPWSTR EntryPath,
    LPWSTR DcName,
    LPWSTR ServerName,
    LPWSTR ShareName,
    DWORD  Level,
    LPDFS_INFO_STRUCT pDfsInfo)
{
    DWORD dwErr;
    PDFSM_ROOT_LIST RootList = NULL;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspSetInfo2(%ws,%ws,%ws,%ws,%ws,%d)\n",
            RootName,
            EntryPath,
            DcName,
            ServerName,
            ShareName,
            Level);
#endif

    dwErr = DfspBindRpc( RootName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsSetInfo2(
                        EntryPath,
                        DcName,
                        ServerName,
                        ShareName,
                        Level,
                        pDfsInfo,
                        &RootList);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

#if DBG
    if (DfsDebug) {
        if (dwErr == ERROR_SUCCESS && RootList != NULL) {
            ULONG n;

            DbgPrint("cEntries=%d\n", RootList->cEntries);
            for (n = 0; n < RootList->cEntries; n++)
                DbgPrint("[%d]%ws\n", n, RootList->Entry[n].ServerShare);
        }
    }
#endif

    if (dwErr == ERROR_SUCCESS && RootList != NULL) {

        ULONG n;

        for (n = 0; n < RootList->cEntries; n++) {

            DfspNotifyFtRoot(
                RootList->Entry[n].ServerShare,
                DcName);

        }

        NetApiBufferFree(RootList);

    }
#if DBG
    if (DfsDebug)
        DbgPrint("DfspSetInfo2 returning %d\n", dwErr);
#endif

    return dwErr;

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsGetInfo
//
//  Synopsis:   Retrieves information about a particular Dfs volume.
//
//  Arguments:  [DfsEntryPath] -- Path to the volume. Implicitly indicates
//                      which server or domain to connect to.
//              [ServerName] -- Optional. If specified, indicates the
//                      server supporting DfsEntryPath.
//              [ShareName] -- Optional. If specified, indicates the share
//                      on ServerName for which info is desired.
//              [Level] -- Indicates the level of info required.
//              [Buffer] -- On successful return, will contain the buffer
//                      containing the required Info. This buffer should be
//                      freed using NetApiBufferFree.
//
//  Returns:    [NERR_Success] -- Info successfully returned.
//
//              [ERROR_INVALID_LEVEL] -- Level is not 1,2,3 or 100
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
//                      or ShareName is specified but ServerName is NULL.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetDfsGetInfo(
    IN  LPWSTR  DfsEntryPath,
    IN  LPWSTR  ServerName OPTIONAL,
    IN  LPWSTR  ShareName OPTIONAL,
    IN  DWORD   Level,
    OUT LPBYTE* Buffer)
{
    NET_API_STATUS dwErr;
    LPWSTR pwszDfsName;
    DWORD cwDfsEntryPath;
    DFS_INFO_STRUCT DfsInfo;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetInfo(%ws,%ws,%ws,%d)\n",
            DfsEntryPath,
            ServerName,
            ShareName,
            Level);
#endif

    if (!IS_VALID_STRING(DfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    if (!(Level >= 1 && Level <= 4) && Level != 100) {
        return( ERROR_INVALID_LEVEL );
    }

    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                DfsEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        //
        // By now, we should have a valid pwszDfsName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                DfsInfo.DfsInfo1 = NULL;

                dwErr = NetrDfsGetInfo(
                            DfsEntryPath,
                            ServerName,
                            ShareName,
                            Level,
                            &DfsInfo);

                if (dwErr == NERR_Success) {

                    *Buffer = (LPBYTE) DfsInfo.DfsInfo1;

                }

           } RpcExcept( 1 ) {

               dwErr = RpcExceptionCode();

           } RpcEndExcept;

           DfspFreeBinding( netdfs_bhandle );

        }

        free( pwszDfsName );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetInfo returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsGetClientInfo
//
//  Synopsis:   Retrieves information about a particular Dfs volume, from the
//              local PKT.
//
//  Arguments:  [DfsEntryPath] -- Path to the volume.
//              [ServerName] -- Optional. If specified, indicates the
//                      server supporting DfsEntryPath.
//              [ShareName] -- Optional. If specified, indicates the share
//                      on ServerName for which info is desired.
//              [Level] -- Indicates the level of info required.
//              [Buffer] -- On successful return, will contain the buffer
//                      containing the required Info. This buffer should be
//                      freed using NetApiBufferFree.
//
//  Returns:    [NERR_Success] -- Info successfully returned.
//
//              [ERROR_INVALID_LEVEL] -- Level is not 1,2,3 or 4.
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
//                      or ShareName is specified but ServerName is NULL.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//              [NERR_DfsInternalError] -- Too many fsctrl attempts
//
//-----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetDfsGetClientInfo(
    IN  LPWSTR  DfsEntryPath,
    IN  LPWSTR  ServerName OPTIONAL,
    IN  LPWSTR  ShareName OPTIONAL,
    IN  DWORD   Level,
    OUT LPBYTE*  Buffer)
{
    NET_API_STATUS dwErr;
    NTSTATUS NtStatus;
    LPWSTR pwszDfsName;
    DWORD cwDfsEntryPath;
    PDFS_GET_PKT_ENTRY_STATE_ARG OutBuffer;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    ULONG cbOutBuffer;
    ULONG cbInBuffer;
    PCHAR InBuffer;
    ULONG cRetries;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetClientInfo(%ws,%ws,%ws,%d)\n",
                DfsEntryPath,
                ServerName,
                ShareName,
                Level);
#endif

    if (!IS_VALID_STRING(DfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    if (!(Level >= 1 && Level <= 4)) {
        return( ERROR_INVALID_LEVEL );
    }

    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Calculate the size of the marshall buffer

    cbOutBuffer = sizeof(DFS_GET_PKT_ENTRY_STATE_ARG) +
                    wcslen(DfsEntryPath) * sizeof(WCHAR);

    if (ServerName) {

        cbOutBuffer += wcslen(ServerName) * sizeof(WCHAR);

    }

    if (ShareName) {

        cbOutBuffer += wcslen(ShareName) * sizeof(WCHAR);

    }


    OutBuffer = malloc(cbOutBuffer);

    if (OutBuffer == NULL) {

        return (ERROR_NOT_ENOUGH_MEMORY);

    }

    ZeroMemory(OutBuffer, cbOutBuffer);

    //
    // marshall the args
    //

    OutBuffer->DfsEntryPathLen = wcslen(DfsEntryPath) * sizeof(WCHAR);
    wcscpy(OutBuffer->Buffer, DfsEntryPath);

    if (ServerName) {

        OutBuffer->ServerNameLen = wcslen(ServerName) * sizeof(WCHAR);
        wcscat(OutBuffer->Buffer, ServerName);

    }

    if (ShareName) {

        OutBuffer->ShareNameLen = wcslen(ShareName) * sizeof(WCHAR);
        wcscat(OutBuffer->Buffer, ShareName);

    }

    //
    // Construct name for opening driver
    //

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    //
    // Open the driver
    //
    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                );

    if (NT_SUCCESS(NtStatus)) {

        //
        // Now fsctl the request down
        //
        OutBuffer->Level = Level;
        cbInBuffer = 0x400;
        NtStatus = STATUS_BUFFER_OVERFLOW;

        for (cRetries = 0;
                NtStatus == STATUS_BUFFER_OVERFLOW && cRetries < 4;
                    cRetries++) {

            dwErr = NetApiBufferAllocate(cbInBuffer, &InBuffer);

            if (dwErr != ERROR_SUCCESS) {

                free(OutBuffer);

                NtClose(DriverHandle);

                return(ERROR_NOT_ENOUGH_MEMORY);

            }

            NtStatus = NtFsControlFile(
                           DriverHandle,
                           NULL,       // Event,
                           NULL,       // ApcRoutine,
                           NULL,       // ApcContext,
                           &IoStatusBlock,
                           FSCTL_DFS_GET_PKT_ENTRY_STATE,
                           OutBuffer,
                           cbOutBuffer,
                           InBuffer,
                           cbInBuffer
                       );

            if (NtStatus == STATUS_BUFFER_OVERFLOW) {

                cbInBuffer = *((PULONG)InBuffer);

                NetApiBufferFree(InBuffer);

            }

        }

        NtClose(DriverHandle);

        //
        // Too many attempts?
        //
        if (cRetries >= 4) {

            NtStatus = STATUS_INTERNAL_ERROR;

        }

    }

    if (NT_SUCCESS(NtStatus)) {

        PDFS_INFO_3 pDfsInfo3;
        PDFS_INFO_4 pDfsInfo4;
        ULONG j;

        pDfsInfo4 = (PDFS_INFO_4)InBuffer;
        pDfsInfo3 = (PDFS_INFO_3)InBuffer;

        try {

            //
            // EntryPath is common to all DFS_INFO_X's and is in the
            // same location.
            //
            OFFSET_TO_POINTER(pDfsInfo4->EntryPath, InBuffer);

            switch (Level) {

            case 4:
                OFFSET_TO_POINTER(pDfsInfo4->Storage, InBuffer);
                for (j = 0; j < pDfsInfo4->NumberOfStorages; j++) {
                    OFFSET_TO_POINTER(pDfsInfo4->Storage[j].ServerName, InBuffer);
                    OFFSET_TO_POINTER(pDfsInfo4->Storage[j].ShareName, InBuffer);
                }
                break;

            case 3:
                OFFSET_TO_POINTER(pDfsInfo3->Storage, InBuffer);
                for (j = 0; j < pDfsInfo3->NumberOfStorages; j++) {
                    OFFSET_TO_POINTER(pDfsInfo3->Storage[j].ServerName, InBuffer);
                    OFFSET_TO_POINTER(pDfsInfo3->Storage[j].ShareName, InBuffer);
                }

            }

            *Buffer = (PBYTE)InBuffer;
            dwErr = NERR_Success;

        } except (EXCEPTION_EXECUTE_HANDLER) {

            NtStatus = GetExceptionCode();

        }

    }

    switch (NtStatus) {

    case STATUS_SUCCESS:
        dwErr = NERR_Success;
        break;

    case STATUS_OBJECT_NAME_NOT_FOUND:
        dwErr = NERR_DfsNoSuchVolume;
        NetApiBufferFree(InBuffer);
        break;

    case STATUS_INTERNAL_ERROR:
        dwErr = NERR_DfsInternalError;
        NetApiBufferFree(InBuffer);
        break;

    default:
        dwErr = ERROR_INVALID_PARAMETER;
        NetApiBufferFree(InBuffer);
        break;

    }

    free(OutBuffer);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsGetClientInfo returning %d\n", dwErr);
#endif

    return( dwErr );
}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsSetClientInfo
//
//  Synopsis:   Associates information with the local PKT.
//
//
//  Arguments:  [DfsEntryPath] -- Path to the volume.
//              [ServerName] -- Optional. If specified, indicates the
//                      server supporting DfsEntryPath.
//              [ShareName] -- Optional. If specified, indicates the share
//                      on ServerName for which info is desired.
//              [Level] -- Indicates the level of info required.
//              [Buffer] -- Pointer to buffer containing information to set.
//
//  Returns:    [NERR_Success] -- Info successfully returned.
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath is NULL,
//                      or ShareName is specified but ServerName is NULL.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//-----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetDfsSetClientInfo(
    IN  LPWSTR  DfsEntryPath,
    IN  LPWSTR  ServerName OPTIONAL,
    IN  LPWSTR  ShareName OPTIONAL,
    IN  DWORD   Level,
    IN  LPBYTE  Buffer)
{
    NET_API_STATUS dwErr;
    NTSTATUS NtStatus;
    LPWSTR pwszDfsName;
    DWORD cwDfsEntryPath;
    PDFS_SET_PKT_ENTRY_STATE_ARG OutBuffer;
    PDFS_INFO_101 pDfsInfo101;
    PDFS_INFO_102 pDfsInfo102;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    ULONG cbOutBuffer;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsSetClientInfo(%ws,%ws,%ws,%d)\n",
                DfsEntryPath,
                ServerName,
                ShareName,
                Level);
#endif

    if (!IS_VALID_STRING(DfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    if (!(Level >= 101 && Level <= 102)) {
        return( ERROR_INVALID_LEVEL );
    }
    cwDfsEntryPath = wcslen(DfsEntryPath);

    if (!IS_UNC_PATH(DfsEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(DfsEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(DfsEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (!IS_VALID_STRING(ServerName) && IS_VALID_STRING(ShareName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (Buffer == NULL) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Calculate the size of the marshall buffer
    //
    cbOutBuffer = sizeof(DFS_SET_PKT_ENTRY_STATE_ARG) +
                    wcslen(DfsEntryPath) * sizeof(WCHAR);

    if (ServerName) {

        cbOutBuffer += wcslen(ServerName) * sizeof(WCHAR);

    }

    if (ShareName) {

        cbOutBuffer += wcslen(ShareName) * sizeof(WCHAR);

    }

    OutBuffer = malloc(cbOutBuffer);

    if (OutBuffer == NULL) {

        return (ERROR_NOT_ENOUGH_MEMORY);

    }

    ZeroMemory(OutBuffer, cbOutBuffer);

    //
    // marshall the args
    //
    OutBuffer = (PDFS_SET_PKT_ENTRY_STATE_ARG) OutBuffer;
    OutBuffer->DfsEntryPathLen = wcslen(DfsEntryPath) * sizeof(WCHAR);
    wcscpy(OutBuffer->Buffer, DfsEntryPath);
    OutBuffer->Level = Level;

    if (ServerName) {

        OutBuffer->ServerNameLen = wcslen(ServerName) * sizeof(WCHAR);
        wcscat(OutBuffer->Buffer, ServerName);

    }

    if (ShareName) {

        OutBuffer->ShareNameLen = wcslen(ShareName) * sizeof(WCHAR);
        wcscat(OutBuffer->Buffer, ShareName);

    }

    switch (Level) {

    case 101:
        OutBuffer->State = ((PDFS_INFO_101)Buffer)->State;
        break;
    case 102:
        OutBuffer->Timeout = (DWORD)((PDFS_INFO_102)Buffer)->Timeout;
        break;

    }

    //
    // Communicate with the driver
    //

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE | FILE_WRITE_DATA,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                );

    if (NT_SUCCESS(NtStatus)) {

        NtStatus = NtFsControlFile(
                       DriverHandle,
                       NULL,       // Event,
                       NULL,       // ApcRoutine,
                       NULL,       // ApcContext,
                       &IoStatusBlock,
                       FSCTL_DFS_SET_PKT_ENTRY_STATE,
                       OutBuffer,
                       cbOutBuffer,
                       NULL,
                       0
                   );

        NtClose(DriverHandle);

    }

    switch (NtStatus) {

    case STATUS_SUCCESS:
        dwErr = NERR_Success;
        break;
    case STATUS_OBJECT_NAME_NOT_FOUND:
        dwErr = NERR_DfsNoSuchVolume;
        break;
    default:
        dwErr = ERROR_INVALID_PARAMETER;
        break;
    }

    free(OutBuffer);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsSetClientInfo returning %d\n", dwErr);
#endif

    return( dwErr );
}


//+----------------------------------------------------------------------------
//
//  Function
//
//  Synopsis:   Enumerates the Dfs volumes.
//
//  Arguments:  [DfsName] -- Name of server or domain whose Dfs is being
//                      enumerated. A leading \\ is optional.
//              [Level] -- Indicates the level of info needed back. Valid
//                      Levels are 1,2, and 3.
//              [PrefMaxLen] -- Preferred maximum length of return buffer.
//              [Buffer] -- On successful return, contains an array of
//                      DFS_INFO_X. This buffer should be freed with a call
//                      to NetApiBufferFree.
//              [EntriesRead] -- On successful return, contains the number
//                      of entries read (and therefore, size of the array in
//                      Buffer).
//              [ResumeHandle] -- Must be 0 on first call. On subsequent calls
//                      the value returned by the immediately preceding call.
//
//  Returns:    [NERR_Success] -- Enum data successfully returned.
//
//              [ERROR_INVALID_LEVEL] -- The Level specified in invalid.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [ERROR_NO_MORE_ITEMS] -- No more volumes to be enumerated.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetDfsEnum(
    IN      LPWSTR  DfsName,
    IN      DWORD   Level,
    IN      DWORD   PrefMaxLen,
    OUT     LPBYTE* Buffer,
    OUT     LPDWORD EntriesRead,
    IN OUT  LPDWORD ResumeHandle)
{
    NET_API_STATUS dwErr;
    LPWSTR pwszMachineName = NULL;
    LPWSTR pwszDomainName = NULL;
    DFS_INFO_ENUM_STRUCT DfsEnum;
    DFS_INFO_3_CONTAINER DfsInfo3Container;
    PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
    PWCHAR DCList;
    DWORD Version;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsEnum(%ws, %d)\n", DfsName, Level);
#endif

    if (!IS_VALID_STRING(DfsName)) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto AllDone;
    }

    //
    // Check the Level Parameter first, or RPC won't know how to marshal the
    // arguments.
    //

    if (!(Level >= 1 && Level <= 4) && (Level != 200) && (Level != 300)) {
        dwErr = ERROR_INVALID_LEVEL;
        goto AllDone;
    }
    

    //
    // Handle names with leading '\\'
    //
    while (*DfsName == L'\\') {
        DfsName++;
    }

    DfsInfo3Container.EntriesRead = 0;
    DfsInfo3Container.Buffer = NULL;
    DfsEnum.Level = Level;
    DfsEnum.DfsInfoContainer.DfsInfo3Container = &DfsInfo3Container;

    if (Level == 200) 
    {
        if (wcschr(DfsName, L'\\') == NULL) 
	{

	    //
            // Use the PDC to enum
	    //
            dwErr = DsGetDcName( NULL,
                                 DfsName,
                                 NULL,
                                 NULL,
                                 DS_PDC_REQUIRED | DS_FORCE_REDISCOVERY,
                                 &pDomainControllerInfo);

            ENTER_NETDFS_API

            if (dwErr == NERR_Success)
            {
                dwErr = DfspBindRpc(&pDomainControllerInfo->DomainControllerName[2],
                                    &netdfs_bhandle);
            }

            if (dwErr == NERR_Success)
            {

                RpcTryExcept {
                    dwErr = NetrDfsEnumEx( DfsName,
                                           Level,
                                           PrefMaxLen,
                                           &DfsEnum,
                                           ResumeHandle);
#if DBG
                    if (DfsDebug)
                        DbgPrint("NetrDfsEnumEx returned %d\n", dwErr);
#endif
                    if (dwErr == NERR_Success) {
                        *EntriesRead =DfsInfo3Container.EntriesRead;
                        *Buffer = (LPBYTE) DfsInfo3Container.Buffer;
                    }
                    if (dwErr == ERROR_UNEXP_NET_ERR)
                    {
                        dwErr = ERROR_NO_MORE_ITEMS;
                    }
                } RpcExcept( 1 ) {
                      dwErr = RpcExceptionCode();
                } RpcEndExcept;
                DfspFreeBinding( netdfs_bhandle );
            }
            LEAVE_NETDFS_API
        }
        else
        {
            dwErr = ERROR_INVALID_PARAMETER;
        }
    }
    else
    {
        dwErr = DfspGetMachineNameFromEntryPath(
                   DfsName,
                   wcslen(DfsName),
                   &pwszMachineName );

        ENTER_NETDFS_API

        if (dwErr == NERR_Success) {
            dwErr = DfspBindRpc( pwszMachineName, &netdfs_bhandle );
        }

#if DBG
        if (DfsDebug)
            DbgPrint("DfspBindRpc returned %d\n", dwErr);
#endif

        if (dwErr == NERR_Success) {

            RpcTryExcept {
                Version = NetrDfsManagerGetVersion();
            } RpcExcept( 1 ) {
                Version = 3;
            } RpcEndExcept;

            RpcTryExcept {
#if DBG
                if (DfsDebug)
                    DbgPrint("Calling NetrDfsEnumEx (%d)\n", Level);
#endif

                if (Version >= 4) 
                {
                    dwErr = NetrDfsEnumEx( DfsName,
                                           Level,
                                           PrefMaxLen,
                                           &DfsEnum,
                                           ResumeHandle );
                }
                else
                {
                    dwErr = NetrDfsEnum( Level,
                                         PrefMaxLen,
                                         &DfsEnum,
                                         ResumeHandle );

                }
            }
            RpcExcept( 1 ) {

                dwErr = RpcExceptionCode();
 #if DBG
                if (DfsDebug)
                    DbgPrint("RpcExeptionCode() err %d\n", dwErr);
 #endif

            } RpcEndExcept;

            if (dwErr == NERR_Success) {

                *EntriesRead =DfsInfo3Container.EntriesRead;

                *Buffer = (LPBYTE) DfsInfo3Container.Buffer;

            }

            DfspFreeBinding( netdfs_bhandle );
        } 
        LEAVE_NETDFS_API
    }

AllDone:

    if (pDomainControllerInfo != NULL)
        NetApiBufferFree(pDomainControllerInfo);

    if (pwszMachineName != NULL)
        free(pwszMachineName);

    if (pwszDomainName != NULL)
        free(pwszDomainName);

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsEnum returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsMove
//
//  Synopsis:   Moves a dfs volume to a new place in the Dfs hierarchy.
//
//  Arguments:  [DfsEntryPath] -- Current path to the volume.
//              [NewDfsEntryPath] -- Desired new path to the volume.
//
//  Returns:    [NERR_Success] -- Info successfully returned.
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath or
//                      NewDfsEntryPath are not valid.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsMove(
    IN LPWSTR DfsEntryPath,
    IN LPWSTR NewDfsEntryPath)
{
    NET_API_STATUS dwErr;
    DWORD cwEntryPath;
    LPWSTR pwszDfsName;

    return ERROR_NOT_SUPPORTED;
}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsRename
//
//  Synopsis:   Renames a path that is along a Dfs Volume Entry Path
//
//  Arguments:  [Path] -- Current path.
//              [NewPath] -- Desired new path.
//
//  Returns:    [NERR_Success] -- Info successfully returned.
//
//              [ERROR_INVALID_PARAMETER] -- Either DfsEntryPath or
//                      NewDfsEntryPath are not valid.
//
//              [ERROR_INVALID_NAME] -- Unable to locate server or domain.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for domain.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//              [NERR_DfsNoSuchVolume] -- No volume matches DfsEntryPath.
//
//              [NERR_DfsInternalCorruption] -- Corruption of Dfs data
//                      encountered at the server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsRename(
    IN LPWSTR Path,
    IN LPWSTR NewPath)
{
    NET_API_STATUS dwErr;
    DWORD cwPath;
    LPWSTR pwszDfsName;

    return ERROR_NOT_SUPPORTED;

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsManagerGetConfigInfo
//
//  Synopsis:   Given a DfsEntryPath and Guid of a local volume, this api
//              remotes to the root server of the entry path and retrieves
//              the config info from it.
//
//  Arguments:  [wszServer] -- Name of local machine
//              [wszLocalVolumeEntryPath] -- Entry Path of local volume.
//              [guidLocalVolume] -- Guid of local volume.
//              [ppDfsmRelationInfo] -- On successful return, contains pointer
//                      to config info at the root server. Free using
//                      NetApiBufferFree.
//
//  Returns:    [NERR_Success] -- Info returned successfully.
//
//              [ERROR_INVALID_PARAMETER] -- wszLocalVolumeEntryPath is
//                      invalid.
//
//              [ERROR_INVALID_NAME] -- Unable to parse out server/domain name
//                      from wszLocalVolumeEntryPath
//
//              [ERROR_DCNotFound] -- Unable to locate a DC for domain
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition
//
//              [NERR_DfsNoSuchVolume] -- The root server did not recognize
//                      a volume with this guid/entrypath
//
//              [NERR_DfsNoSuchServer] -- wszServer is not a valid server for
//                      wszLocalVolumeEntryPath
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsManagerGetConfigInfo(
    LPWSTR wszServer,
    LPWSTR wszLocalVolumeEntryPath,
    GUID guidLocalVolume,
    LPDFSM_RELATION_INFO *ppDfsmRelationInfo)
{
    NET_API_STATUS dwErr;
    LPWSTR pwszDfsName = NULL;
    DWORD cwDfsEntryPath;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerGetConfigInfo(%ws,%ws)\n",
            wszServer,
            wszLocalVolumeEntryPath);
#endif

    if (!IS_VALID_STRING(wszServer) ||
            !IS_VALID_STRING(wszLocalVolumeEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    cwDfsEntryPath = wcslen(wszLocalVolumeEntryPath);

    if (!IS_UNC_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                wszLocalVolumeEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        //
        // By now, we should have a valid pwszDfsName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                *ppDfsmRelationInfo = NULL;

                dwErr = NetrDfsManagerGetConfigInfo(
                            wszServer,
                            wszLocalVolumeEntryPath,
                            guidLocalVolume,
                            ppDfsmRelationInfo);

           } RpcExcept( 1 ) {

               dwErr = RpcExceptionCode();

           } RpcEndExcept;

           DfspFreeBinding( netdfs_bhandle );

        }

        free( pwszDfsName );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerGetConfigInfo returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   NetDfsManagerInitialize
//
//  Synopsis:   Reinitialize the Dfs Manager on a remote machine
//
//  Arguments:  [ServerName] -- Name of server to remote to
//              [Flags] -- Flags
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsManagerInitialize(
    IN LPWSTR ServerName,
    IN DWORD  Flags)
{
    NET_API_STATUS dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerInitialize(%ws,%d)\n",
                ServerName,
                Flags);
#endif

    //
    // Validate the string argument so RPC won't complain...
    //

    if (!IS_VALID_STRING(ServerName)) {
        return( ERROR_INVALID_PARAMETER );
    }

    ENTER_NETDFS_API

    //
    // We should have a valid ServerName. Lets try to bind to it,
    // and call the server.
    //

    dwErr = DfspBindToServer( ServerName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsManagerInitialize(
                        ServerName,
                        Flags);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerInitialize returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   NetDfsManagerSendSiteInfo
//
//  Synopsis:   Gets site information from a server
//
//  Returns:    [NERR_Success] -- Successfully completed operation.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
NetDfsManagerSendSiteInfo(
    LPWSTR wszServer,
    LPWSTR wszLocalVolumeEntryPath,
    LPDFS_SITELIST_INFO pSiteInfo)
{
    NET_API_STATUS dwErr;
    LPWSTR pwszDfsName = NULL;
    DWORD cwDfsEntryPath;

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerSendSiteInfo(%ws,%ws)\n",
            wszServer,
            wszLocalVolumeEntryPath);
#endif

    if (!IS_VALID_STRING(wszServer) ||
            !IS_VALID_STRING(wszLocalVolumeEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    //
    // Some elementary parameter checking to make sure we can proceed
    // reasonably...
    //

    cwDfsEntryPath = wcslen(wszLocalVolumeEntryPath);

    if (!IS_UNC_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
            !IS_VALID_PREFIX(wszLocalVolumeEntryPath, cwDfsEntryPath) &&
                !IS_VALID_DFS_PATH(wszLocalVolumeEntryPath, cwDfsEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    dwErr = DfspGetMachineNameFromEntryPath(
                wszLocalVolumeEntryPath,
                cwDfsEntryPath,
                &pwszDfsName);

    ENTER_NETDFS_API

    if (dwErr == NERR_Success) {

        //
        // By now, we should have a valid pwszDfsName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindRpc( pwszDfsName, &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                dwErr = NetrDfsManagerSendSiteInfo(
                            wszServer,
                            pSiteInfo);

           } RpcExcept( 1 ) {

               dwErr = RpcExceptionCode();

           } RpcEndExcept;

           DfspFreeBinding( netdfs_bhandle );

        }

        free( pwszDfsName );

    }

    LEAVE_NETDFS_API

#if DBG
    if (DfsDebug)
        DbgPrint("NetDfsManagerSendSiteInfo returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   DfspGetMachineNameFromEntryPath
//
//  Synopsis:   Given a DfsEntryPath, this routine returns the name of the
//              FtDfs Root.
//
//  Arguments:  [wszEntryPath] -- Pointer to EntryPath to parse.
//
//              [cwEntryPath] -- Length in WCHAR of wszEntryPath.
//
//              [ppwszMachineName] -- Name of a root machine; allocated using malloc;
//                                      caller resposible for freeing it.
//
//  Returns:    [NERR_Success] -- Successfully determinded
//
//              [ERROR_INVALID_NAME] -- Unable to parse wszEntryPath.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
//                      ppwszMachineName.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
DfspGetMachineNameFromEntryPath(
    LPWSTR wszEntryPath,
    DWORD cwEntryPath,
    LPWSTR *ppwszMachineName)
{
    NTSTATUS NtStatus;
    LPWSTR pwszDfsName, pwszFirst, pwszLast;
    LPWSTR pwszMachineName;
    DWORD cwDfsName;
    DWORD cwSlash;
    DWORD dwErr;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspGetMachineNameFromEntryPath(%ws,%d\n", wszEntryPath, cwEntryPath);
#endif

    if (!IS_VALID_STRING(wszEntryPath)) {
        return( ERROR_INVALID_PARAMETER );
    }

    if (IS_UNC_PATH(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[2];

    } else if (IS_VALID_PREFIX(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[1];

    } else if (IS_VALID_DFS_PATH(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[0];

    } else {

        return( ERROR_INVALID_NAME );

    }

    dwErr = DfspGetDfsNameFromEntryPath(
                wszEntryPath,
                cwEntryPath,
                &pwszMachineName);

    if (dwErr != NERR_Success) {

#if DBG
        if (DfsDebug)
            DbgPrint("DfspGetMachineNameFromEntryPath: returning %d\n", dwErr);
#endif
        return( dwErr);

    }

    for (cwDfsName = cwSlash = 0, pwszLast = pwszFirst;
            *pwszLast != UNICODE_NULL;
                pwszLast++, cwDfsName++) {
         if (*pwszLast == L'\\')
            cwSlash++;
         if (cwSlash >= 2)
            break;
    }

    if (cwSlash == 0) {

        *ppwszMachineName = pwszMachineName;
        dwErr = NERR_Success;

        return dwErr;
    }

    cwDfsName += 3;

    pwszDfsName = malloc(cwDfsName * sizeof(WCHAR));

    if (pwszDfsName != NULL) {

        ZeroMemory((PCHAR)pwszDfsName, cwDfsName * sizeof(WCHAR));

        wcscpy(pwszDfsName, L"\\\\");

        CopyMemory(&pwszDfsName[2], pwszFirst, (PCHAR)pwszLast - (PCHAR)pwszFirst);

        NtStatus = DfspIsThisADfsPath(&pwszDfsName[1]);

        if (NT_SUCCESS(NtStatus)) {
            GetFileAttributes(pwszDfsName);
        }

        dwErr = DfspDfsPathToRootMachine(pwszDfsName, ppwszMachineName);

        if (NtStatus != STATUS_SUCCESS || dwErr != NERR_Success) {

            *ppwszMachineName = pwszMachineName;
            dwErr = NERR_Success;

        } else {

            free(pwszMachineName);

        }

        free(pwszDfsName);

    } else {

        free(pwszMachineName);
        dwErr = ERROR_NOT_ENOUGH_MEMORY;

    }

#if DBG
    if (DfsDebug)
        DbgPrint("DfspGetMachineNameFromEntryPath returning %d\n", dwErr);
#endif

    return( dwErr );

}


//+----------------------------------------------------------------------------
//
//  Function:   DfspGetDfsNameFromEntryPath
//
//  Synopsis:   Given a DfsEntryPath, this routine returns the name of the
//              Dfs Root.
//
//  Arguments:  [wszEntryPath] -- Pointer to EntryPath to parse.
//
//              [cwEntryPath] -- Length in WCHAR of wszEntryPath.
//
//              [ppwszDfsName] -- Name of Dfs root is returned here. Memory
//                      is allocated using malloc; caller resposible for
//                      freeing it.
//
//  Returns:    [NERR_Success] -- Successfully parsed out Dfs Root.
//
//              [ERROR_INVALID_NAME] -- Unable to parse wszEntryPath.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Unable to allocate memory for
//                      ppwszDfsName.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
DfspGetDfsNameFromEntryPath(
    LPWSTR wszEntryPath,
    DWORD cwEntryPath,
    LPWSTR *ppwszDfsName)
{
    LPWSTR pwszDfsName, pwszFirst, pwszLast;
    DWORD cwDfsName;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspGetDfsNameFromEntryPath(%ws,%d)\n", wszEntryPath, cwEntryPath);
#endif

    if (!IS_VALID_STRING(wszEntryPath)) {
#if DBG
        if (DfsDebug)
            DbgPrint("DfspGetDfsNameFromEntryPath returning ERROR_INVALID_PARAMETER\n");
#endif
        return( ERROR_INVALID_PARAMETER );
    }

    if (IS_UNC_PATH(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[2];

    } else if (IS_VALID_PREFIX(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[1];

    } else if (IS_VALID_DFS_PATH(wszEntryPath, cwEntryPath)) {

        pwszFirst = &wszEntryPath[0];

    } else {

#if DBG
        if (DfsDebug)
            DbgPrint("DfspGetDfsNameFromEntryPath returning ERROR_INVALID_NAME\n");
#endif
        return( ERROR_INVALID_NAME );

    }

    for (cwDfsName = 0, pwszLast = pwszFirst;
            *pwszLast != UNICODE_NULL && *pwszLast != L'\\';
                pwszLast++, cwDfsName++) {
         ;
    }

    ++cwDfsName;

    pwszDfsName = malloc( cwDfsName * sizeof(WCHAR) );

    if (pwszDfsName != NULL) {

        pwszDfsName[ cwDfsName - 1 ] = 0;

        for (cwDfsName--; cwDfsName > 0; cwDfsName--) {

            pwszDfsName[ cwDfsName - 1 ] = pwszFirst[ cwDfsName - 1 ];

        }

        *ppwszDfsName = pwszDfsName;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspGetDfsNameFromEntryPath returning %ws\n", pwszDfsName);
#endif

        return( NERR_Success );

    } else {

#if DBG
    if (DfsDebug)
        DbgPrint("DfspGetDfsNameFromEntryPath returning ERROR_NOT_ENOUGH_MEMORY\n");
#endif
        return( ERROR_NOT_ENOUGH_MEMORY );

    }

}


//+----------------------------------------------------------------------------
//
//  Function:   DfspBindRpc
//
//  Synopsis:   Given a server or domain name, this API will bind to the
//              appropriate Dfs Manager service.
//
//  Arguments:  [DfsName] -- Name of domain or server. Leading \\ is optional
//
//              [BindingHandle] -- On successful return, the binding handle
//                      is returned here.
//
//  Returns:    [NERR_Success] -- Binding handle successfull returned.
//
//              [RPC_S_SERVER_NOT_AVAILABLE] -- Unable to bind to NetDfs
//                      interface on the named server or domain.
//
//              [ERROR_INVALID_NAME] -- Unable to parse DfsName as a valid
//                      server or domain name.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
DfspBindRpc(
    IN  LPWSTR DfsName,
    OUT RPC_BINDING_HANDLE *BindingHandle)
{
    LPWSTR wszProtocolSeq = L"ncacn_np";
    LPWSTR wszEndPoint = L"\\pipe\\netdfs";
    LPWSTR pwszRpcBindingString = NULL;
    LPWSTR pwszDCName = NULL;
    NET_API_STATUS dwErr;
    PWCHAR DCList = NULL;
    PWCHAR DCListToFree = NULL;
    BOOLEAN IsDomainName = FALSE;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspBindRpc(%ws)\n", DfsName);
#endif

    //
    // First, see if this is a domain name.
    //

    dwErr = DfspIsThisADomainName( DfsName, &DCListToFree );

    DCList = DCListToFree;

    if (dwErr == ERROR_SUCCESS && DCList != NULL && *DCList != UNICODE_NULL) {

        //
        // It's a domain name. Use the DC list as a list of servers to try to bind to.
        //

        IsDomainName = TRUE;
        pwszDCName = DCList + 1;    // Skip '+' or '-'

        dwErr = ERROR_SUCCESS;

    } else {

        //
        // Lets see if this is a machine-based Dfs
        //

        pwszDCName = DfsName;

        dwErr = ERROR_SUCCESS;

    }

Try_Connect:

    if (dwErr == ERROR_SUCCESS) {

#if DBG
        if (DfsDebug)
            DbgPrint("Calling RpcBindingCompose(%ws)\n", pwszDCName);
#endif

        dwErr = RpcStringBindingCompose(
                    NULL,                            // Object UUID
                    wszProtocolSeq,                  // Protocol Sequence
                    pwszDCName,                      // Network Address
                    wszEndPoint,                     // RPC Endpoint
                    NULL,                            // RPC Options
                    &pwszRpcBindingString);          // Returned binding string

        if (dwErr == RPC_S_OK) {

            dwErr = RpcBindingFromStringBinding(
                        pwszRpcBindingString,
                        BindingHandle);

#if DBG
            if (DfsDebug)
                DbgPrint("RpcBindingFromStringBinding() returned %d\n", dwErr);
#endif

            if (dwErr == RPC_S_OK) {

                dwErr = DfspVerifyBinding();
                if (dwErr != RPC_S_OK)
                {
                    DfspFreeBinding(*BindingHandle);
                }

#if DBG
                if (DfsDebug)
                    DbgPrint("DfspVerifyBinding() returned %d\n", dwErr);
#endif

            } else {

                dwErr = ERROR_INVALID_NAME;

            }
        }

    }

    if (pwszRpcBindingString != NULL) {

        RpcStringFree( &pwszRpcBindingString );

    }

    if (dwErr == RPC_S_OUT_OF_MEMORY) {
        dwErr = ERROR_NOT_ENOUGH_MEMORY;
    }

    //
    // If we couldn't connect and we have a domain name and a list of DC's,
    // try the next DC in the list.
    //

    if (dwErr != NERR_Success && DCList != NULL && IsDomainName == TRUE) {

        DCList += wcslen(DCList) + 1;

        if (*DCList != UNICODE_NULL) {

            pwszDCName = DCList + 1;
            dwErr = ERROR_SUCCESS;

            goto Try_Connect;

        }

    }

    if (DCListToFree != NULL) {

        free(DCListToFree);

    }

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspFreeBinding
//
//  Synopsis:   Frees a binding created by DfspBindRpc
//
//  Arguments:  [BindingHandle] -- The handle to free.
//
//  Returns:    Nothing
//
//-----------------------------------------------------------------------------

VOID
DfspFreeBinding(
    RPC_BINDING_HANDLE BindingHandle)
{
    DWORD dwErr;

    dwErr = RpcBindingFree( &BindingHandle );

}


//+----------------------------------------------------------------------------
//
//  Function:   DfspVerifyBinding
//
//  Synopsis:   Verifies that the binding can be used by doing a
//              NetrDfsManagerGetVersion call on the binding.
//
//  Arguments:  None
//
//  Returns:    [NERR_Success] -- Server connnected to.
//
//              [RPC_S_SERVER_UNAVAILABLE] -- The server is not available.
//
//              Other RPC error from calling the remote server.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
DfspVerifyBinding()
{
    NET_API_STATUS status = NERR_Success;
    DWORD Version;

    RpcTryExcept {

        Version = NetrDfsManagerGetVersion();

    } RpcExcept(1) {

        status = RpcExceptionCode();

    } RpcEndExcept;

    return( status );

}


//+----------------------------------------------------------------------------
//
//  Function:   DfspBindToServer
//
//  Synopsis:   Given a server name, this API will bind to the
//              appropriate Dfs Manager service.
//
//  Arguments:  [DfsName] -- Name of server. Leading \\ is optional
//
//              [BindingHandle] -- On successful return, the binding handle
//                      is returned here.
//
//  Returns:    [NERR_Success] -- Binding handle successfull returned.
//
//              [RPC_S_SERVER_NOT_AVAILABLE] -- Unable to bind to NetDfs
//                      interface on the named server or domain.
//
//              [ERROR_INVALID_NAME] -- Unable to parse DfsName as a valid
//                      server or domain name.
//
//              [ERROR_DCNotFound] -- Unable to locate DC for DfsName.
//
//              [ERROR_NOT_ENOUGH_MEMORY] -- Out of memory condition.
//
//-----------------------------------------------------------------------------

NET_API_STATUS
DfspBindToServer(
    IN  LPWSTR ServerName,
    OUT RPC_BINDING_HANDLE *BindingHandle)
{
    LPWSTR wszProtocolSeq = L"ncacn_np";
    LPWSTR wszEndPoint = L"\\pipe\\netdfs";
    LPWSTR pwszRpcBindingString = NULL;
    NET_API_STATUS dwErr;

    dwErr = RpcStringBindingCompose(
                NULL,                            // Object UUID
                wszProtocolSeq,                  // Protocol Sequence
                ServerName,                      // Network Address
                wszEndPoint,                     // RPC Endpoint
                NULL,                            // RPC Options
                &pwszRpcBindingString);          // Returned binding string

    if (dwErr == RPC_S_OK) {

        dwErr = RpcBindingFromStringBinding(
                    pwszRpcBindingString,
                    BindingHandle);

        if (dwErr == RPC_S_OK) {

            dwErr = DfspVerifyBinding();
            if (dwErr != RPC_S_OK)
            {
                DfspFreeBinding(*BindingHandle);
            }
        } else {

            dwErr = ERROR_INVALID_NAME;

        }
    }

    if (pwszRpcBindingString != NULL) {

        RpcStringFree( &pwszRpcBindingString );

    }

    if (dwErr == RPC_S_OUT_OF_MEMORY) {
        dwErr = ERROR_NOT_ENOUGH_MEMORY;
    }

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspFlushPkt
//
//  Synopsis:   Flushes the local pkt
//
//  Arguments:  DfsEntryPath or NULL
//
//  Returns:    The fsctrl's code
//
//-----------------------------------------------------------------------------

VOID
DfspFlushPkt(
    LPWSTR DfsEntryPath)
{
    NTSTATUS NtStatus;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE | FILE_WRITE_DATA,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0);

    if (NT_SUCCESS(NtStatus)) {

        if (DfsEntryPath != NULL) {

            NtStatus = NtFsControlFile(
                           DriverHandle,
                           NULL,       // Event,
                           NULL,       // ApcRoutine,
                           NULL,       // ApcContext,
                           &IoStatusBlock,
                           FSCTL_DFS_PKT_FLUSH_CACHE,
                           DfsEntryPath,
                           wcslen(DfsEntryPath) * sizeof(WCHAR),
                           NULL,
                           0);

        } else {

            NtStatus = NtFsControlFile(
                           DriverHandle,
                           NULL,       // Event,
                           NULL,       // ApcRoutine,
                           NULL,       // ApcContext,
                           &IoStatusBlock,
                           FSCTL_DFS_PKT_FLUSH_CACHE,
                           L"*",
                           sizeof(WCHAR),
                           NULL,
                           0);

        }

        NtClose(DriverHandle);

    }

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspDfsPathToRootMachine
//
//  Synopsis:   Turns a dfs root path into a machine name
//              Ex: \\jharperdomain\FtDfs -> jharperdc1
//                  \\jharpera\d -> jharpera
//
//  Arguments:  pwszDfsName - Dfs root path to get machine for
//              ppwszMachineName - The machine, if one found.  Space is
//                                  malloc'd, caller must free.
//
//  Returns:    [NERR_Success] -- Resolved ok
//
//-----------------------------------------------------------------------------

DWORD
DfspDfsPathToRootMachine(
    LPWSTR pwszDfsName,
    LPWSTR *ppwszMachineName)
{
    NTSTATUS NtStatus;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    WCHAR ServerName[0x100];
    DWORD dwErr;
    ULONG i;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspDfsPathToRootMachine(%ws)\n", pwszDfsName);
#endif

    for (i = 0; i < sizeof(ServerName) / sizeof(WCHAR); i++)
        ServerName[i] = UNICODE_NULL;

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                );

    if (NT_SUCCESS(NtStatus)) {

        NtStatus = NtFsControlFile(
                       DriverHandle,
                       NULL,       // Event,
                       NULL,       // ApcRoutine,
                       NULL,       // ApcContext,
                       &IoStatusBlock,
                       FSCTL_DFS_GET_SERVER_NAME,
                       pwszDfsName,
                       wcslen(pwszDfsName) * sizeof(WCHAR),
                       ServerName,
                       sizeof(ServerName)
                   );

        NtClose(DriverHandle);

    }

    if (NT_SUCCESS(NtStatus)) {

        LPWSTR wcpStart;
        LPWSTR wcpEnd;

        for (wcpStart = ServerName; *wcpStart == L'\\'; wcpStart++)
            ;

        for (wcpEnd = wcpStart; *wcpEnd != L'\\' && *wcpEnd != UNICODE_NULL; wcpEnd++)
            ;

        *wcpEnd = UNICODE_NULL;

        *ppwszMachineName = malloc((wcslen(wcpStart) + 1) * sizeof(WCHAR));

        if (*ppwszMachineName != NULL) {

            wcscpy(*ppwszMachineName, wcpStart);

            dwErr = NERR_Success;

        } else {

            dwErr = ERROR_NOT_ENOUGH_MEMORY;

        }

    } else {

#if DBG
        if (DfsDebug)
            DbgPrint("DfspDfsPathToRootMachine NtStatus=0x%x\n", NtStatus);
#endif

        dwErr = ERROR_INVALID_PARAMETER;

    }

#if DBG
    if (DfsDebug) {
        if (dwErr == NERR_Success)
            DbgPrint("DfspDfsPathToRootMachine returning %ws\n", *ppwszMachineName);
         else
            DbgPrint("DfspDfsPathToRootMachine returning %d\n", dwErr);
    }
#endif

    return dwErr;

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspIsThisADfsPath
//
//  Synopsis:   Checks (via IOCTL to driver) if the path passed in
//              is a Dfs path.
//
//  Arguments:  pwszPathName - Path to check (ex: \ntbuilds\release)
//
//  Returns:    NTSTATUS of the call (STATUS_SUCCESS or error)
//
//-----------------------------------------------------------------------------

NTSTATUS
DfspIsThisADfsPath(
    LPWSTR pwszPathName)
{
    NTSTATUS NtStatus;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    PDFS_IS_VALID_PREFIX_ARG pPrefixArg = NULL;
    ULONG Size;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspIsThisADfsPath(%ws)\n", pwszPathName);
#endif

    Size = sizeof(DFS_IS_VALID_PREFIX_ARG) +
                (wcslen(pwszPathName) + 1) * sizeof(WCHAR);

    pPrefixArg = (PDFS_IS_VALID_PREFIX_ARG) malloc(Size);

    if (pPrefixArg == NULL) {

        NtStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto exit_with_status;

    }

    pPrefixArg->CSCAgentCreate = FALSE;
    pPrefixArg->RemoteNameLen = wcslen(pwszPathName) * sizeof(WCHAR);
    wcscpy(&pPrefixArg->RemoteName[0], pwszPathName);

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                );

    if (NT_SUCCESS(NtStatus)) {

        NtStatus = NtFsControlFile(
                       DriverHandle,
                       NULL,       // Event,
                       NULL,       // ApcRoutine,
                       NULL,       // ApcContext,
                       &IoStatusBlock,
                       FSCTL_DFS_IS_VALID_PREFIX,
                       pPrefixArg,
                       Size,
                       NULL,
                       0
                   );

        NtClose(DriverHandle);

    }

exit_with_status:

    if (pPrefixArg != NULL) {

        free(pPrefixArg);

    }

#if DBG
    if (DfsDebug)
        DbgPrint("DfspIsThisADfsPath returning 0x%x\n", NtStatus);
#endif

    return NtStatus;

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspCreateFtDfs
//
//  Synopsis:   Creates/updates a Ds object representing the FtDfs, then rpc's
//              to the server and has it update the DS object, thus completing
//              the setup.
//
//  Arguments:  wszServerName - Name of server we'll be adding
//              wszDcName - DC to use
//              fIsPdc - TRUE if DC is the PDC
//              wszRootShare - Share to become the root share
//              wszFtDfsName - Name of FtDfs we are creating
//              wszComment -- Comment for the root
//              dwFlags - 0
//
//  Returns:    NTSTATUS of the call (STATUS_SUCCESS or error)
//
//-----------------------------------------------------------------------------

DWORD
DfspCreateFtDfs(
    LPWSTR wszServerName,
    LPWSTR wszDcName,
    BOOLEAN fIsPdc,
    LPWSTR wszRootShare,
    LPWSTR wszFtDfsName,
    LPWSTR wszComment,
    DWORD  dwFlags)
{
    DWORD dwErr = ERROR_SUCCESS;
    DWORD dwErr2 = ERROR_SUCCESS;
    DWORD i, j;

    LPWSTR wszDfsConfigDN = NULL;
    LPWSTR wszConfigurationDN = NULL;
    PDFSM_ROOT_LIST RootList = NULL;

    LDAP *pldap = NULL;
    PLDAPMessage pMsg = NULL;

    LDAPModW ldapModClass, ldapModCN, ldapModPkt, ldapModPktGuid, ldapModServer;
    LDAP_BERVAL ldapPkt, ldapPktGuid;
    PLDAP_BERVAL rgModPktVals[2];
    PLDAP_BERVAL rgModPktGuidVals[2];
    LPWSTR rgModClassVals[2];
    LPWSTR rgModCNVals[2];
    LPWSTR rgModServerVals[5];
    LPWSTR rgAttrs[5];
    PLDAPModW rgldapMods[6];
    BOOLEAN fNewFTDfs = FALSE;

    LDAPMessage *pmsgServers;
    PWCHAR *rgServers;
    DWORD cServers;
    ULONG Size;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspCreateFtDfs(%ws,%ws,%s,%ws,%ws,%ws,%d)\n",
                    wszServerName,
                    wszDcName,
                    fIsPdc ? "TRUE" : "FALSE",
                    wszRootShare,
                    wszFtDfsName,
                    wszComment,
                    dwFlags);
#endif

    dwErr = DfspLdapOpen(
                wszDcName,
                &pldap,
                &wszConfigurationDN);
    if (dwErr != ERROR_SUCCESS) {

        goto Cleanup;

    }

    //
    // See if the DfsConfiguration object exists; if not and this is the 
    // PDC, create it.
    //

    rgAttrs[0] = L"cn";
    rgAttrs[1] = NULL;

    dwErr = ldap_search_sW(
                pldap,
                wszConfigurationDN,
                LDAP_SCOPE_BASE,
                L"(objectClass=*)",
                rgAttrs,
                0,
                &pMsg);

    if (pMsg != NULL) {
        ldap_msgfree(pMsg);
        pMsg = NULL;
    }

#if DBG
    if (DfsDebug)
        DbgPrint("ldap_search_sW(1) returned 0x%x\n", dwErr);
#endif

    if (dwErr == LDAP_NO_SUCH_OBJECT && fIsPdc == TRUE) {

        rgModClassVals[0] = L"dfsConfiguration";
        rgModClassVals[1] = NULL;

        ldapModClass.mod_op = LDAP_MOD_ADD;
        ldapModClass.mod_type = L"objectClass";
        ldapModClass.mod_vals.modv_strvals = rgModClassVals;

        rgModCNVals[0] = L"Dfs-Configuration";
        rgModCNVals[1] = NULL;

        ldapModCN.mod_op = LDAP_MOD_ADD;
        ldapModCN.mod_type = L"cn";
        ldapModCN.mod_vals.modv_strvals = rgModCNVals;

        rgldapMods[0] = &ldapModClass;
        rgldapMods[1] = &ldapModCN;
        rgldapMods[2] = NULL;

        dwErr = ldap_add_sW(
                    pldap,
                    wszConfigurationDN,
                    rgldapMods);

#if DBG
        if (DfsDebug)
            DbgPrint("ldap_add_sW(1) returned 0x%x\n", dwErr);
#endif

    }

    if (dwErr != LDAP_SUCCESS) {

        dwErr = LdapMapErrorToWin32(dwErr);
        goto Cleanup;

    }

    //
    // Check to see if we are joining an FTDfs or creating a new one.
    //

    Size =  wcslen(L"CN=") +
               wcslen(wszFtDfsName) +
                   wcslen(L",") +
                       wcslen(wszConfigurationDN);
    if (Size > MAX_PATH) {
        dwErr = ERROR_DS_NAME_TOO_LONG;
        goto Cleanup;
    }
    wszDfsConfigDN = malloc((Size+1) * sizeof(WCHAR));
    if (wszDfsConfigDN == NULL) {
        dwErr = ERROR_OUTOFMEMORY;
        goto Cleanup;
    }
    wcscpy(wszDfsConfigDN,L"CN=");
    wcscat(wszDfsConfigDN,wszFtDfsName);
    wcscat(wszDfsConfigDN,L",");
    wcscat(wszDfsConfigDN,wszConfigurationDN);

    rgAttrs[0] = L"remoteServerName";
    rgAttrs[1] = NULL;

    dwErr = ldap_search_sW(
                pldap,
                wszDfsConfigDN,
                LDAP_SCOPE_BASE,
                L"(objectClass=*)",
                rgAttrs,
                0,
                &pMsg);

#if DBG
    if (DfsDebug)
        DbgPrint("ldap_search_sW(2) returned 0x%x\n", dwErr);
#endif

    if (dwErr == LDAP_NO_SUCH_OBJECT) {

        GUID idPkt;
        DWORD dwPktVersion = 1;

        //
        // We are creating a new FTDfs, create a container to hold the Dfs
        // configuration for it.
        //

        fNewFTDfs = TRUE;

        //
        // Generate the class and CN attributes
        //

        rgModClassVals[0] = L"ftDfs";
        rgModClassVals[1] = NULL;

        ldapModClass.mod_op = LDAP_MOD_ADD;
        ldapModClass.mod_type = L"objectClass";
        ldapModClass.mod_vals.modv_strvals = rgModClassVals;

        rgModCNVals[0] = wszFtDfsName;
        rgModCNVals[1] = NULL;

        ldapModCN.mod_op = LDAP_MOD_ADD;
        ldapModCN.mod_type = L"cn";
        ldapModCN.mod_vals.modv_strvals = rgModCNVals;

        //
        // Generate the null PKT attribute
        //

        ldapPkt.bv_len = sizeof(DWORD);
        ldapPkt.bv_val = (PCHAR) &dwPktVersion;

        rgModPktVals[0] = &ldapPkt;
        rgModPktVals[1] = NULL;

        ldapModPkt.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
        ldapModPkt.mod_type = L"pKT";
        ldapModPkt.mod_vals.modv_bvals = rgModPktVals;

        //
        // Generate a PKT Guid attribute
        //

        UuidCreate( &idPkt );

        ldapPktGuid.bv_len = sizeof(GUID);
        ldapPktGuid.bv_val = (PCHAR) &idPkt;

        rgModPktGuidVals[0] = &ldapPktGuid;
        rgModPktGuidVals[1] = NULL;

        ldapModPktGuid.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
        ldapModPktGuid.mod_type = L"pKTGuid";
        ldapModPktGuid.mod_vals.modv_bvals = rgModPktGuidVals;

        //
        // Generate a Remote-Server-Name attribute
        //

        rgModServerVals[0] = L"*";
        rgModServerVals[1] = NULL;

        ldapModServer.mod_op = LDAP_MOD_ADD;
        ldapModServer.mod_type = L"remoteServerName";
        ldapModServer.mod_vals.modv_strvals = rgModServerVals;

        //
        // Assemble all the LDAPMod structures
        //

        rgldapMods[0] = &ldapModClass;
        rgldapMods[1] = &ldapModCN;
        rgldapMods[2] = &ldapModPkt;
        rgldapMods[3] = &ldapModPktGuid;
        rgldapMods[4] = &ldapModServer;
        rgldapMods[5] = NULL;

        //
        // Create the Dfs metadata object.
        //

        dwErr = ldap_add_sW( pldap, wszDfsConfigDN, rgldapMods );

#if DBG
        if (DfsDebug)
            DbgPrint("ldap_add_sW(2) returned 0x%x\n", dwErr);
#endif


    }

    if (dwErr != LDAP_SUCCESS) {
        dwErr = LdapMapErrorToWin32(dwErr);
        goto Cleanup;
    }

    //
    // Create a machine ACE
    //

    dwErr = DfsAddMachineAce(
                pldap,
                wszDcName,
                wszDfsConfigDN,
                wszServerName);

    if (dwErr != ERROR_SUCCESS) 
    {
        goto Cleanup;
    }
    //
    // Tell the server to add itself to the object
    //

    dwErr = DfspBindToServer( wszServerName, &netdfs_bhandle );

#if DBG
    if (DfsDebug)
        DbgPrint("DfspBindToServer returned %d\n", dwErr);
#endif

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsAddFtRoot(
                        wszServerName,
                        wszDcName,
                        wszRootShare,
                        wszFtDfsName,
                        (wszComment != NULL) ? wszComment : L"",
                        wszDfsConfigDN,
                        fNewFTDfs,
                        dwFlags,
                        &RootList);

#if DBG
            if (DfsDebug)
                DbgPrint("NetrDfsAddFtRoot returned %d\n", dwErr);
#endif

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

#if DBG
    if (DfsDebug) {
        if (dwErr == ERROR_SUCCESS && RootList != NULL) {
            ULONG n;

            DbgPrint("cEntries=%d\n", RootList->cEntries);
            for (n = 0; n < RootList->cEntries; n++)
                DbgPrint("[%d]%ws\n", n, RootList->Entry[n].ServerShare);
        }
    }
#endif

    if (dwErr == ERROR_SUCCESS && RootList != NULL) {
        ULONG n;

        for (n = 0; n < RootList->cEntries; n++) {
            DfspNotifyFtRoot(
                RootList->Entry[n].ServerShare,
                wszDcName);
        }
        NetApiBufferFree(RootList);
    }

    if (dwErr == ERROR_ALREADY_EXISTS) {
        goto Cleanup;
    } else if (dwErr != ERROR_SUCCESS) {
        goto TearDown;
    }

    //
    // Have the DC flush the Ft table
    //

    DfspFlushFtTable(
        wszDcName,
        wszFtDfsName);

    //
    // Flush the local Pkt
    //

    DfspFlushPkt(NULL);

    goto Cleanup;

TearDown:

    //
    // At this point we have added an ACE to the acl list to allow
    // this machine to write the Dfs BLOB.  But the add failed, so we
    // need to remove the ACE we set earlier.  If this fails we continue
    // on anyway.
    //
    dwErr2 = DfsRemoveMachineAce(
                pldap,
                wszDcName,
                wszDfsConfigDN,
                wszServerName);

    rgAttrs[0] = L"remoteServerName";
    rgAttrs[1] = NULL;

    if (pMsg != NULL) {
        ldap_msgfree(pMsg);
        pMsg = NULL;
    }

    dwErr2 = ldap_search_sW(
                pldap,
                wszDfsConfigDN,
                LDAP_SCOPE_BASE,
                L"(objectClass=*)",
                rgAttrs,
                0,
                &pMsg);

    if (dwErr2 != LDAP_SUCCESS) {
        dwErr2 = LdapMapErrorToWin32(dwErr2);
        goto Cleanup;
    }

    dwErr2 = ERROR_SUCCESS;

    pmsgServers = ldap_first_entry(pldap, pMsg);

    if (pmsgServers != NULL) {

        rgServers = ldap_get_valuesW(
                        pldap,
                        pmsgServers,
                        L"remoteServerName");

        if (rgServers != NULL) {
            cServers = ldap_count_valuesW( rgServers );
            if (cServers == 1) {
                //
                // Delete the Dfs metadata object.
                //
                ULONG RetryCount = MAX_DFS_LDAP_RETRY;

                do
                {
                    dwErr2 = ldap_delete_sW( pldap, wszDfsConfigDN);
#if DBG
                    if (dwErr2 == LDAP_BUSY)
                    {
                        if (DfsDebug)
                            DbgPrint("delete object returning %d\n", dwErr2);
                    }
#endif
                } while ( RetryCount-- && (dwErr2 == LDAP_BUSY) );
            }
            ldap_value_freeW( rgServers );
        } else {
            dwErr2 = ERROR_OUTOFMEMORY;
        }
    } else {
        dwErr2 = ERROR_OUTOFMEMORY;
    }

    if (dwErr2 != ERROR_SUCCESS) {
        goto Cleanup;
    }

    ldap_msgfree(pMsg);
    pMsg = NULL;

Cleanup:

    if (pMsg != NULL)
        ldap_msgfree(pMsg);

    if (pldap != NULL)
        ldap_unbind( pldap );

    if (wszConfigurationDN != NULL)
        free(wszConfigurationDN);

    if (wszDfsConfigDN != NULL)
        free(wszDfsConfigDN);

#if DBG
    if (DfsDebug)
        DbgPrint("DfspCreateFtDfs returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspTearDownFtDfs
//
//  Synopsis:   Updates/deletes the Ds object representing the FtDfs
//
//  Arguments:  wszServerName - Name of server we're removing
//              wszDcName - DC to use
//              wszRootShare - Root share
//              wszFtDfsName - Name of FtDfs we are modifying
//              dwFlags - 0
//
//  Returns:    NTSTATUS of the call (STATUS_SUCCESS or error)
//
//-----------------------------------------------------------------------------

DWORD
DfspTearDownFtDfs(
    IN LPWSTR wszServerName,
    IN LPWSTR wszDcName,
    IN LPWSTR wszRootShare,
    IN LPWSTR wszFtDfsName,
    IN DWORD  dwFlags)
{
    DWORD dwErr = ERROR_SUCCESS;

    LPWSTR wszDfsConfigDN = NULL;
    LPWSTR wszConfigurationDN = NULL;
    PDFSM_ROOT_LIST RootList = NULL;

    LDAP *pldap = NULL;
    PLDAPMessage pMsg = NULL;

    LDAPModW ldapModServer;
    LPWSTR rgAttrs[5];
    PLDAPModW rgldapMods[6];

    LDAPMessage *pmsgServers;
    PWCHAR *rgServers;
    DWORD cServers;
    ULONG Size;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspTearDownFtDfs(%ws,%ws,%ws,%ws,%d)\n",
                    wszServerName,
                    wszDcName,
                    wszRootShare,
                    wszFtDfsName,
                    dwFlags);
#endif

    dwErr = DfspLdapOpen(
                wszDcName,
                &pldap,
                &wszConfigurationDN);

    if (dwErr != ERROR_SUCCESS) {

        goto Cleanup;

    }

    if ((dwFlags & DFS_FORCE_REMOVE) != 0) {

        dwErr = DfspBindToServer(wszDcName, &netdfs_bhandle);

    } else {

        dwErr = DfspBindToServer(wszServerName, &netdfs_bhandle);

    }

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsRemoveFtRoot(
                        wszServerName,
                        wszDcName,
                        wszRootShare,
                        wszFtDfsName,
                        dwFlags,
                        &RootList);


#if DBG
            if (DfsDebug)
                DbgPrint("NetrDfsRemoveFtRoot returned %d\n", dwErr);
#endif

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }


#if DBG
    if (DfsDebug) {
        if (dwErr == ERROR_SUCCESS && RootList != NULL) {
            ULONG n;

            DbgPrint("cEntries=%d\n", RootList->cEntries);
            for (n = 0; n < RootList->cEntries; n++)
                DbgPrint("[%d]%ws\n", n, RootList->Entry[n].ServerShare);
        }
    }
#endif


    if (dwErr == ERROR_SUCCESS && RootList != NULL) {

        ULONG n;

        for (n = 0; n < RootList->cEntries; n++) {

            DfspNotifyFtRoot(
                RootList->Entry[n].ServerShare,
                wszDcName);

        }

        NetApiBufferFree(RootList);

    }


    if (dwErr != ERROR_SUCCESS) {

        goto Cleanup;

    }



    //
    // Build the name of the object representing the FtDfs
    //

    Size =  wcslen(L"CN=") +
               wcslen(wszFtDfsName) +
                   wcslen(L",") + 
                       wcslen(wszConfigurationDN);
    if (Size > MAX_PATH) {
        dwErr = ERROR_DS_NAME_TOO_LONG;
        goto Cleanup;
    }
    wszDfsConfigDN = malloc((Size+1) * sizeof(WCHAR));
    if (wszDfsConfigDN == NULL) {
        dwErr = ERROR_OUTOFMEMORY;
        goto Cleanup;
    }
    wcscpy(wszDfsConfigDN,L"CN=");
    wcscat(wszDfsConfigDN,wszFtDfsName);
    wcscat(wszDfsConfigDN,L",");
    wcscat(wszDfsConfigDN,wszConfigurationDN);

    //
    // Remove machine ACE
    //

    dwErr = DfsRemoveMachineAce(
                pldap,
                wszDcName,
                wszDfsConfigDN,
                wszServerName);

    //
    // If this was the last root, remove DS obj representing this FtDfs
    //

    rgAttrs[0] = L"remoteServerName";
    rgAttrs[1] = NULL;

    dwErr = ldap_search_sW(
                pldap,
                wszDfsConfigDN,
                LDAP_SCOPE_BASE,
                L"(objectClass=*)",
                rgAttrs,
                0,
                &pMsg);

    if (dwErr != LDAP_SUCCESS) {

        dwErr = LdapMapErrorToWin32(dwErr);

        goto Cleanup;

    }

    dwErr = ERROR_SUCCESS;

    pmsgServers = ldap_first_entry(pldap, pMsg);

    if (pmsgServers != NULL) {

        rgServers = ldap_get_valuesW(
                        pldap,
                        pmsgServers,
                        L"remoteServerName");

        if (rgServers != NULL) {

            cServers = ldap_count_valuesW( rgServers );

            if (cServers == 1) {
                //
                // Delete the Dfs metadata object.
                //
                ULONG RetryCount = MAX_DFS_LDAP_RETRY;

                do
                {
                    dwErr = ldap_delete_sW( pldap, wszDfsConfigDN);
#if DBG
                    if (dwErr == LDAP_BUSY)
                    {
                        if (DfsDebug)
                            DbgPrint("delete object returning %d\n", dwErr);
                    }
#endif
                } while ( RetryCount-- && (dwErr == LDAP_BUSY) );

                if (dwErr != LDAP_SUCCESS) {
                    dwErr = LdapMapErrorToWin32(dwErr);

                } else {
                    dwErr = ERROR_SUCCESS;
                }
            }

            ldap_value_freeW( rgServers );

        } else {

            dwErr = ERROR_OUTOFMEMORY;

        }

    } else {

        dwErr = ERROR_OUTOFMEMORY;

    }

    ldap_msgfree( pMsg );
    pMsg = NULL;

    if (dwErr != ERROR_SUCCESS) {

        goto Cleanup;

    }

    //
    // Have the DC flush the Ft table
    //

    DfspFlushFtTable(
        wszDcName,
        wszFtDfsName);

    //
    // Flush the local Pkt
    //

    DfspFlushPkt(NULL);

Cleanup:

#if DBG
    if (DfsDebug)
        DbgPrint("DfspTearDownFtDfs at Cleanup:\n");
#endif

    if (pMsg != NULL)
        ldap_msgfree( pMsg );

    if (pldap != NULL)
        ldap_unbind( pldap );

    if (wszConfigurationDN != NULL)
        free(wszConfigurationDN);

    if (wszDfsConfigDN != NULL)
        free(wszDfsConfigDN);

#if DBG
    if (DfsDebug)
        DbgPrint("DfspTearDownFtDfs returning %d\n", dwErr);
#endif

    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspFlushFtTable
//
//  Synopsis:   Goes to a DC and flushes an entry from its FtDfs name cache
//
//  Arguments:  wszDcName - Name of DC
//              wszFtDfsName - The FtDfs name to flush
//
//  Returns:    NTSTATUS of the call (STATUS_SUCCESS or error)
//
//-----------------------------------------------------------------------------

VOID
DfspFlushFtTable(
    LPWSTR wszDcName,
    LPWSTR wszFtDfsName)
{
    DWORD dwErr;

    //
    // We should have a valid ServerName. Lets try to bind to it,
    // and call the server.
    //

    dwErr = DfspBindToServer( wszDcName, &netdfs_bhandle );

    if (dwErr == NERR_Success) {

        RpcTryExcept {

            dwErr = NetrDfsFlushFtTable(
                        wszDcName,
                        wszFtDfsName);

        } RpcExcept(1) {

            dwErr = RpcExceptionCode();

        } RpcEndExcept;

        DfspFreeBinding( netdfs_bhandle );

    }

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspSetDomainToDc
//
//  Synopsis:   Sets a DC in the special table to 'active'.
//
//  Arguments:  DomainName -- Domain of DC to set active
//              DcName -- Dc to make active
//
//  Returns:    NTSTATUS of the call (STATUS_SUCCESS or error)
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspSetDomainToDc(
    LPWSTR DomainName,
    LPWSTR DcName)
{
    PDFS_SPECIAL_SET_DC_INPUT_ARG arg = NULL;
    NTSTATUS NtStatus;
    HANDLE DriverHandle = NULL;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    PDFS_IS_VALID_PREFIX_ARG pPrefixArg = NULL;
    PCHAR cp;
    ULONG Size;

    if (DomainName == NULL || DcName == NULL) {

        NtStatus = STATUS_INVALID_PARAMETER;
        goto exit_with_status;

    }

    Size = sizeof(DFS_SPECIAL_SET_DC_INPUT_ARG) +
                wcslen(DomainName) * sizeof(WCHAR) +
                    wcslen(DcName) * sizeof(WCHAR);

    arg = (PDFS_SPECIAL_SET_DC_INPUT_ARG) malloc(Size);

    if (arg == NULL) {

        NtStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto exit_with_status;

    }

    RtlZeroMemory(arg, Size);

    arg->SpecialName.Length = wcslen(DomainName) * sizeof(WCHAR);
    arg->SpecialName.MaximumLength = arg->SpecialName.Length;

    arg->DcName.Length = wcslen(DcName) * sizeof(WCHAR);
    arg->DcName.MaximumLength = arg->DcName.Length;

    cp = (PCHAR)arg + sizeof(DFS_SPECIAL_SET_DC_INPUT_ARG);

    arg->SpecialName.Buffer = (WCHAR *)cp;
    RtlCopyMemory(cp, DomainName, arg->SpecialName.Length);
    cp += arg->SpecialName.Length;

    arg->DcName.Buffer = (WCHAR *)cp;
    RtlCopyMemory(cp, DcName, arg->DcName.Length);

    POINTER_TO_OFFSET(arg->SpecialName.Buffer, arg);
    POINTER_TO_OFFSET(arg->DcName.Buffer, arg);

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE | FILE_WRITE_DATA,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0);

    if (NT_SUCCESS(NtStatus)) {

        NtStatus = NtFsControlFile(
                       DriverHandle,
                       NULL,       // Event,
                       NULL,       // ApcRoutine,
                       NULL,       // ApcContext,
                       &IoStatusBlock,
                       FSCTL_DFS_SPECIAL_SET_DC,
                       arg,
                       Size,
                       NULL,
                       0);

        NtClose(DriverHandle);

    }

exit_with_status:

    if (arg != NULL) {

        free(arg);

    }

    return NtStatus;

}

//+----------------------------------------------------------------------------
//
//  Function:   I_NetDfsIsThisADomainName
//
//  Synopsis:   Checks the special name table to see if the
//              name matches a domain name.
//
//  Arguments:  [wszName] -- Name to check
//
//  Returns:    [ERROR_SUCCESS] -- Name is indeed a domain name.
//
//              [ERROR_FILE_NOT_FOUND] -- Name is not a domain name
//
//-----------------------------------------------------------------------------

DWORD
I_NetDfsIsThisADomainName(
    LPWSTR wszName)
{
    DWORD dwErr;
    PWCHAR DCList = NULL;

    dwErr = DfspIsThisADomainName(
                wszName,
                &DCList);

    if (DCList != NULL) {

        free(DCList);

    }

    return dwErr;

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspNotifyFtRoot
//
//  Synopsis:   Rpc's to a supposed FtDfs root
//              and tells it a DC to reinit from.
//
//  Arguments:  wszServerShare - The server to go to, in a form of \\server\share
//              wszDcName - DC to use
//
//  Returns:    Nothing
//
//-----------------------------------------------------------------------------

VOID
DfspNotifyFtRoot(
    LPWSTR wszServerShare,
    LPWSTR wszDcName)
{
    DWORD dwErr;
    ULONG i;

#if DBG
    if (DfsDebug)
        DbgPrint("DfspNotifyFtRoot(%ws,%ws)\n",
                wszServerShare,
                wszDcName);
#endif

    if (wszServerShare == NULL || wszServerShare[1] != L'\\') {

        return;

    }

    for (i = 2; wszServerShare[i] != UNICODE_NULL && wszServerShare[i] != L'\\'; i++) {

        NOTHING;

    }

    if (wszServerShare[i] == L'\\') {

        wszServerShare[i] = UNICODE_NULL;
        //
        // We should have a valid ServerName. Lets try to bind to it,
        // and call the server.
        //

        dwErr = DfspBindToServer( &wszServerShare[2], &netdfs_bhandle );

        if (dwErr == NERR_Success) {

            RpcTryExcept {

                dwErr = NetrDfsSetDcAddress(
                            &wszServerShare[2],
                            wszDcName,
                            60 * 60 * 2,    // 2 hours
                            (NET_DFS_SETDC_TIMEOUT | NET_DFS_SETDC_INITPKT)
                            );


            } RpcExcept(1) {

                dwErr = RpcExceptionCode();

            } RpcEndExcept;

            DfspFreeBinding( netdfs_bhandle );

        }

        wszServerShare[i] = L'\\';

    }

#if DBG
    if (DfsDebug)
        DbgPrint("DfspNotifyFtRoot dwErr=%d\n", dwErr);
#endif

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspIsThisADomainName
//
//  Synopsis:   Calls the mup to have it check the special name table to see if the
//              name matches a domain name.  Returns a list of DC's in the domain,
//              as a list of strings.  The list is terminated with a double-null.
//
//  Arguments:  [wszName] -- Name to check
//              [ppList]  -- Pointer to pointer for results.
//
//  Returns:    [ERROR_SUCCESS] -- Name is indeed a domain name.
//
//              [ERROR_FILE_NOT_FOUND] -- Name is not a domain name
//
//-----------------------------------------------------------------------------

DWORD
DfspIsThisADomainName(
    LPWSTR wszName,
    PWCHAR *ppList)
{
    NTSTATUS NtStatus;
    IO_STATUS_BLOCK IoStatusBlock;
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING DfsDriverName;
    HANDLE DriverHandle = NULL;
    DWORD dwErr;
    PCHAR OutBuf = NULL;
    ULONG Size = 0x100;
    ULONG Count = 0;

    RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);

    InitializeObjectAttributes(
        &objectAttributes,
        &DfsDriverName,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    NtStatus = NtCreateFile(
                    &DriverHandle,
                    SYNCHRONIZE,
                    &objectAttributes,
                    &IoStatusBlock,
                    NULL,
                    FILE_ATTRIBUTE_NORMAL,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    FILE_OPEN_IF,
                    FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
                    NULL,
                    0
                );

    if (!NT_SUCCESS(NtStatus)) {
        return ERROR_FILE_NOT_FOUND;
    }

Retry:

    OutBuf = malloc(Size);

    if (OutBuf == NULL) {

        NtClose(DriverHandle);
        return ERROR_NOT_ENOUGH_MEMORY;

    }

    NtStatus = NtFsControlFile(
                   DriverHandle,
                   NULL,       // Event,
                   NULL,       // ApcRoutine,
                   NULL,       // ApcContext,
                   &IoStatusBlock,
                   FSCTL_DFS_GET_SPC_TABLE,
                   wszName,
                   (wcslen(wszName) + 1) * sizeof(WCHAR),
                   OutBuf,
                   Size
               );

    if (NtStatus == STATUS_SUCCESS) {

        dwErr = ERROR_SUCCESS;

    } else if (NtStatus == STATUS_BUFFER_OVERFLOW && ++Count < 5) {

        Size = *((ULONG *)OutBuf);
        free(OutBuf);
        goto Retry;
    
    } else {

        dwErr = ERROR_FILE_NOT_FOUND;

    }

    NtClose(DriverHandle);

    *ppList = (WCHAR *)OutBuf;

    return dwErr;
}

DWORD
DfspLdapOpen(
    LPWSTR wszDcName,
    LDAP **ppldap,
    LPWSTR *pwszObjectName)
{
    DWORD dwErr;
    DWORD i;
    ULONG Size;
    ULONG Len;

    PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
    PLDAPMessage pMsg = NULL;

    LDAP *pldap = NULL;

    LPWSTR wszConfigurationDN = NULL;

    LPWSTR rgAttrs[5];

    if (wszDcName == NULL ||
        wcslen(wszDcName) == 0 ||
        ppldap == NULL ||
        pwszObjectName == NULL
    ) {
        dwErr = ERROR_INVALID_PARAMETER;
        goto Cleanup;
    }

#if DBG
    if (DfsDebug)
        DbgPrint("DfspLdapOpen(%ws)\n", wszDcName);
#endif
    pldap = ldap_init(wszDcName, LDAP_PORT);

    if (pldap == NULL) {

#if DBG
        if (DfsDebug)
            DbgPrint("DfspLdapOpen:ldap_init failed\n");
#endif
        dwErr = ERROR_INVALID_NAME;
        goto Cleanup;

    }

    dwErr = ldap_set_option(pldap, LDAP_OPT_AREC_EXCLUSIVE, LDAP_OPT_ON);
	    
    if (dwErr != LDAP_SUCCESS) {
	pldap = NULL;
	goto Cleanup;
    }

    dwErr = ldap_bind_s(pldap, NULL, NULL, LDAP_AUTH_SSPI);

    if (dwErr != LDAP_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("ldap_bind_s failed with ldap error %d\n", dwErr);
#endif
        pldap = NULL;
        dwErr = LdapMapErrorToWin32(dwErr);
        goto Cleanup;
    }

    //
    // Get attribute "defaultNameContext" containing name of entry we'll be
    // using for our DN
    //

    rgAttrs[0] = L"defaultnamingContext";
    rgAttrs[1] = NULL;

    dwErr = ldap_search_s(
                pldap,
                L"",
                LDAP_SCOPE_BASE,
                L"(objectClass=*)",
                rgAttrs,
                0,
                &pMsg);

    if (dwErr == LDAP_SUCCESS) {

        PLDAPMessage pEntry = NULL;
        PWCHAR *rgszNamingContexts = NULL;
        DWORD i, cNamingContexts;

        dwErr = ERROR_SUCCESS;

        if ((pEntry = ldap_first_entry(pldap, pMsg)) != NULL &&
                (rgszNamingContexts = ldap_get_values(pldap, pEntry, rgAttrs[0])) != NULL &&
                    (cNamingContexts = ldap_count_values(rgszNamingContexts)) > 0) {

            wszConfigurationDN = malloc((wcslen(rgszNamingContexts[0]) + 1) * sizeof(WCHAR));
            if (wszConfigurationDN != NULL)
                wcscpy( wszConfigurationDN, rgszNamingContexts[0]);
            else
                dwErr = ERROR_OUTOFMEMORY;
        } else {
            dwErr = ERROR_UNEXP_NET_ERR;
        }

        if (rgszNamingContexts != NULL)
            ldap_value_free( rgszNamingContexts );

    } else {

        dwErr = LdapMapErrorToWin32(dwErr);

    }

    if (dwErr != ERROR_SUCCESS) {
#if DBG
        if (DfsDebug)
            DbgPrint("Unable to find Configuration naming context\n");
#endif
        goto Cleanup;
    }

    //
    // Create string with full object name
    //

    Size = wcslen(DfsConfigContainer) * sizeof(WCHAR) +
                sizeof(WCHAR) +
                    wcslen(wszConfigurationDN) * sizeof(WCHAR) +
                        sizeof(WCHAR);

    *pwszObjectName = malloc(Size);

    if (*pwszObjectName == NULL) {
        dwErr = ERROR_OUTOFMEMORY;
        goto Cleanup;
     }

    wcscpy(*pwszObjectName,DfsConfigContainer);
    wcscat(*pwszObjectName,L",");
    wcscat(*pwszObjectName,wszConfigurationDN);

#if DBG
    if (DfsDebug)
        DbgPrint("DfsLdapOpen:object name=[%ws]\n", *pwszObjectName);
#endif

Cleanup:

    if (pDCInfo != NULL)
        NetApiBufferFree( pDCInfo );

    if (dwErr != ERROR_SUCCESS) {
        ldap_unbind( pldap );
        pldap = NULL;
    }

    if (wszConfigurationDN != NULL)
        free(wszConfigurationDN);

    if (pMsg != NULL)
        ldap_msgfree(pMsg);

    *ppldap = pldap;

#if DBG
    if (DfsDebug)
        DbgPrint("DfsLdapOpen:returning %d\n", dwErr);
#endif
    return( dwErr );

}

//+----------------------------------------------------------------------------
//
//  Function:   DfspIsInvalidName, local
//
//  Synopsis:   Sees if a DomDfs name is Invalid
//
//  Arguments:  [DomDfsName] -- Name test.
//
//  Returns:    TRUE if invalid, FALSE otherwise.
//
//-----------------------------------------------------------------------------
BOOLEAN
DfspIsInvalidName(
    LPWSTR ShareName)
{
    ULONG i;

    for (i = 0; InvalidNames[i] != NULL; i++) {

        if (_wcsicmp(InvalidNames[i], ShareName) == 0) {
            return TRUE;
        }

    }

    return FALSE;
}