/*++

Copyright (c) 1991-1992 Microsoft Corporation

Module Name:

    Share.c

Abstract:

    This module contains support for the DFS catagory of APIs for the
    NT server service.

Revision History:

--*/

#include "srvsvcp.h"
#include "lmerr.h"
#include <dfsfsctl.h>
#include <dsgetdc.h>
#include <lmapibuf.h>           // NetApiBufferFree().

#define CAPTURE_STRING( Name ) \
    if( Name != NULL ) {                                \
        ULONG _size = SIZE_WSTR( Name );                \
        capture->Name = (LPWSTR)variableData;           \
        RtlCopyMemory( capture->Name, Name, _size );    \
        variableData += _size;                          \
        POINTER_TO_OFFSET( capture->Name, capture );    \
    }

#define RELATION_INFO_SIZE( RelInfo )                   \
    (sizeof( NET_DFS_ENTRY_ID_CONTAINER ) +             \
     (RelInfo->Count * sizeof(NET_DFS_ENTRY_ID)))

BOOLEAN
ValidateDfsEntryIdContainer(
    LPNET_DFS_ENTRY_ID_CONTAINER pRelationInfo);

NET_API_STATUS
DfsFsctl(
    IN  ULONG FsControlCode,
    IN  PVOID InputBuffer,
    IN  ULONG InputBufferLength,
    OUT PVOID OutputBuffer,
    IN  ULONG OutputBufferLength
)
{
    NTSTATUS status;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatus;
    HANDLE dfsHandle;
    UNICODE_STRING deviceName;

    deviceName.Buffer = DFS_SERVER_NAME;
    deviceName.MaximumLength = sizeof( DFS_SERVER_NAME );
    deviceName.Length = deviceName.MaximumLength  - sizeof(UNICODE_NULL);

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

    if( SsData.SsInitialized &&
      (status = RpcImpersonateClient(NULL)) != NO_ERROR ) {
        return (NET_API_STATUS) status;
    }

    status = NtCreateFile(
        &dfsHandle,
        SYNCHRONIZE | FILE_WRITE_DATA,
        &objectAttributes,
        &ioStatus,
        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 ( SsData.SsInitialized ) {
        (VOID)RpcRevertToSelf( );
    }

    if ( NT_SUCCESS(status) ) {
        status = ioStatus.Status;
    }

    if( !NT_SUCCESS( status ) ) {
        return (NET_API_STATUS)status;
    }

    status = NtFsControlFile(
                dfsHandle,
                NULL,       // Event,
                NULL,       // ApcRoutine,
                NULL,       // ApcContext,
                &ioStatus,
                FsControlCode,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength
            );

    if(NT_SUCCESS(status)) {
        status = ioStatus.Status;
    }

    NtClose( dfsHandle );

    return (NET_API_STATUS)status;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsGetVersion(
    IN SRVSVC_HANDLE                   ServerName,
    OUT LPDWORD                        Version)
{
    DFS_GET_VERSION_ARG arg;
    NET_API_STATUS  error;

    RtlZeroMemory( &arg, sizeof(arg) );

    error = DfsFsctl( FSCTL_DFS_GET_VERSION, &arg, sizeof( arg ), NULL, 0 );

    if (error == NERR_Success) {

        *Version = arg.Version;

    } else {

        error = ERROR_FILE_NOT_FOUND;

    }

    return( error );

}

NET_API_STATUS NET_API_FUNCTION
NetrDfsCreateLocalPartition (
    IN SRVSVC_HANDLE                   ServerName,      // Name of server for this API
    IN LPWSTR                          ShareName,       // Name of share to add to the DFS
    IN LPGUID                          EntryUid,        // unique id for this partition
    IN LPWSTR                          EntryPrefix,     // DFS entry path for this volume
    IN LPWSTR                          ShortName,       // 8.3 format of EntryPrefix
    IN LPNET_DFS_ENTRY_ID_CONTAINER    RelationInfo,
    IN BOOL                            Force            // Force knowledge into consistent state?
    )
{
    NET_API_STATUS  error;
    PDFS_CREATE_LOCAL_PARTITION_ARG capture;
    ULONG           size = sizeof( *capture );
    ULONG           i;
    PCHAR           variableData;
    PSERVER_REQUEST_PACKET  srp;
    LPSHARE_INFO_2  shareInfo2 = NULL;
    UNICODE_STRING  ntSharePath;

    if( ShareName == NULL || EntryUid == NULL ||
        EntryPrefix == NULL || RelationInfo == NULL ||
            ValidateDfsEntryIdContainer(RelationInfo) == FALSE) {
        return ERROR_INVALID_PARAMETER;
    }

    //
    // Make a call to the SMB server to find the pathname for the share.
    //
    srp = SsAllocateSrp();
    if( srp == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    };
    srp->Level = 2;
    srp->Flags = SRP_RETURN_SINGLE_ENTRY;
    srp->Parameters.Get.ResumeHandle = 0;
    RtlInitUnicodeString( &srp->Name1, ShareName );
    error = SsServerFsControlGetInfo(
                FSCTL_SRV_NET_SHARE_ENUM,
                srp,
                &shareInfo2,
                10000
            );
    if( error != NO_ERROR ) {
        SsFreeSrp( srp );
        return error;
    }

    if( srp->Parameters.Get.EntriesRead == 0 ||
        shareInfo2 == NULL ||
        shareInfo2->shi2_path == NULL ) {

        SsFreeSrp( srp );
        if( shareInfo2 != NULL ) {
            MIDL_user_free( shareInfo2 );
        }
        return ERROR_BAD_NET_NAME;
    }

    if( (shareInfo2->shi2_type & ~STYPE_SPECIAL) != STYPE_DISKTREE ) {
        SsFreeSrp( srp );
        MIDL_user_free( shareInfo2 );
        return ERROR_BAD_DEV_TYPE;
    }

    //
    // Now we need to convert the share's Win32 style pathname to an
    //  NT pathname
    //
    ntSharePath.Buffer = NULL;

    if( !RtlDosPathNameToNtPathName_U(
        shareInfo2->shi2_path,
        &ntSharePath,
        NULL,
        NULL ) ) {

        SsFreeSrp( srp );
        MIDL_user_free( shareInfo2 );
        return ERROR_INVALID_PARAMETER;
    }
    MIDL_user_free( shareInfo2 );

    //
    // Pack the data into an fsctl that can be sent to the local Dfs driver:
    //
    // First find the size...
    //
    size += SIZE_WSTR( ShareName );
    size += ntSharePath.Length + sizeof( WCHAR );
    size += SIZE_WSTR( EntryPrefix );
    size += SIZE_WSTR( ShortName );

    if( ARGUMENT_PRESENT( RelationInfo ) ) {
        size += RELATION_INFO_SIZE(RelationInfo);
        for( i = 0; i < RelationInfo->Count; i++ ) {
            size += SIZE_WSTR( RelationInfo->Buffer[i].Prefix );
        }
    }

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        SsFreeSrp( srp );
        RtlFreeUnicodeString( &ntSharePath );
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters in the capture buffer
    //
    capture->EntryUid = *EntryUid;
    capture->Force = (Force != FALSE);

    //
    // Put the variable data in the capture buffer.
    //

    variableData = (PCHAR)(capture + 1);

    if( ARGUMENT_PRESENT( RelationInfo ) ) {
        capture->RelationInfo = (LPNET_DFS_ENTRY_ID_CONTAINER)variableData;
        capture->RelationInfo->Buffer = (LPNET_DFS_ENTRY_ID)
                                            (capture->RelationInfo + 1);
        variableData += RELATION_INFO_SIZE( RelationInfo );
        for( i=0; i < RelationInfo->Count; i++ ) {
            CAPTURE_STRING( RelationInfo->Buffer[i].Prefix );
            capture->RelationInfo->Buffer[i].Uid = RelationInfo->Buffer[i].Uid;
        }

        POINTER_TO_OFFSET( capture->RelationInfo->Buffer, capture );
        POINTER_TO_OFFSET( capture->RelationInfo, capture );

    }

    CAPTURE_STRING( ShareName );
    CAPTURE_STRING( EntryPrefix );
    CAPTURE_STRING( ShortName );

    //
    // Capture the nt version of the share path
    //
    capture->SharePath = (LPWSTR)variableData;
    RtlCopyMemory( capture->SharePath, ntSharePath.Buffer, ntSharePath.Length );
    variableData += ntSharePath.Length;
    POINTER_TO_OFFSET( capture->SharePath, capture );

    *((WCHAR *)variableData) = 0;          // Null terminate the name
    variableData += sizeof( WCHAR );

    RtlFreeUnicodeString( &ntSharePath );

    //
    // First, tell the server to mark this share as being in Dfs. Note that
    //  the share name is already in srp->Name1. If we later run into an
    //  error, we'll undo the state change.
    //

    srp->Flags = SRP_SET_SHARE_IN_DFS;
    error = SsServerFsControl(
                FSCTL_SRV_SHARE_STATE_CHANGE,
                srp,
                NULL,
                0
            );
    if( error != NO_ERROR ) {
        SsFreeSrp( srp );
        MIDL_user_free( capture );
        return error;
    }

    //
    // Tell the Dfs driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_CREATE_LOCAL_PARTITION,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    if (error != NO_ERROR) {

        //
        // An error occured changing the Dfs state. So, try to undo the
        // server share state change.
        //

        NET_API_STATUS error2;

        srp->Flags = SRP_CLEAR_SHARE_IN_DFS;
        error2 = SsServerFsControl(
                    FSCTL_SRV_SHARE_STATE_CHANGE,
                    srp,
                    NULL,
                    0);

    }

    SsFreeSrp( srp );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsDeleteLocalPartition (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix
    )
{
    NET_API_STATUS error;
    PDFS_DELETE_LOCAL_PARTITION_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_DELETE_LOCAL_PARTITION,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    //
    // If there was no error, tell the server that this share
    //   is no longer in the Dfs
    //


    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsSetLocalVolumeState (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix,
    IN  ULONG                       State
    )
{
    NET_API_STATUS error;
    PDFS_SET_LOCAL_VOLUME_STATE_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;
    capture->State = State;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_SET_LOCAL_VOLUME_STATE,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsSetServerInfo (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix
    )
{
    NET_API_STATUS error;
    PDFS_SET_SERVER_INFO_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_SET_SERVER_INFO,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsCreateExitPoint (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix,
    IN  ULONG                       Type,
    IN  ULONG                       ShortPrefixLen,
    OUT LPWSTR                      ShortPrefix
    )
{
    NET_API_STATUS error;
    PDFS_CREATE_EXIT_POINT_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;
    capture->Type = Type;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_CREATE_EXIT_POINT,
                capture,
                size,
                ShortPrefix,
                ShortPrefixLen * sizeof(WCHAR)
            );

    MIDL_user_free( capture );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsDeleteExitPoint (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix,
    IN  ULONG                       Type
    )
{
    NET_API_STATUS error;
    PDFS_DELETE_EXIT_POINT_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;
    capture->Type = Type;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_DELETE_EXIT_POINT,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsModifyPrefix (
    IN  SRVSVC_HANDLE               ServerName,
    IN  LPGUID                      Uid,
    IN  LPWSTR                      Prefix
    )
{
    NET_API_STATUS error;
    PDFS_DELETE_LOCAL_PARTITION_ARG capture;
    ULONG   size = sizeof( *capture );
    PCHAR   variableData;

    //
    // Pack the args into a single buffer that can be sent to
    // the dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( Prefix );

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters into the capture buffer
    //
    capture->Uid = *Uid;

    //
    // Put the variable data in the capture buffer
    //
    variableData = (PCHAR)(capture + 1 );

    CAPTURE_STRING( Prefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_MODIFY_PREFIX,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    return error;
}

NET_API_STATUS NET_API_FUNCTION
NetrDfsFixLocalVolume (
    IN  SRVSVC_HANDLE                   ServerName,
    IN  LPWSTR                          VolumeName,
    IN  ULONG                           EntryType,
    IN  ULONG                           ServiceType,
    IN  LPWSTR                          StgId,
    IN  LPGUID                          EntryUid,       // unique id for this partition
    IN  LPWSTR                          EntryPrefix,    // path prefix for this partition
    IN  LPNET_DFS_ENTRY_ID_CONTAINER    RelationInfo,
    IN  ULONG                           CreateDisposition
    )
{
    NET_API_STATUS  error;
    PDFS_FIX_LOCAL_VOLUME_ARG capture;
    ULONG           size = sizeof( *capture );
    ULONG           i;
    PCHAR           variableData;

    if (ARGUMENT_PRESENT(RelationInfo) && ValidateDfsEntryIdContainer(RelationInfo) == FALSE)
        return ERROR_INVALID_PARAMETER;

    //
    // Pack the args into a single buffer that can be sent to the
    //  dfs driver:
    //

    //
    // First find the size...
    //
    size += SIZE_WSTR( VolumeName );
    size += SIZE_WSTR( StgId );
    size += SIZE_WSTR( EntryPrefix );

    if( ARGUMENT_PRESENT( RelationInfo ) ) {
        size += RELATION_INFO_SIZE( RelationInfo );
        for( i = 0; i < RelationInfo->Count; i++ ) {
            size += SIZE_WSTR( RelationInfo->Buffer[i].Prefix );
        }
    }

    //
    // Now allocate the memory
    //
    capture = MIDL_user_allocate( size );
    if( capture == NULL ) {
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    RtlZeroMemory( capture, size );

    //
    // Put the fixed parameters in the capture buffer
    //
    capture->EntryType = EntryType;
    capture->ServiceType = ServiceType;
    capture->EntryUid = *EntryUid;
    capture->CreateDisposition = CreateDisposition;

    //
    // Put the variable data in the capture buffer.
    //

    variableData = (PCHAR)(capture + 1);

    if( ARGUMENT_PRESENT( RelationInfo ) ) {
        capture->RelationInfo = (LPNET_DFS_ENTRY_ID_CONTAINER)variableData;
        capture->RelationInfo->Buffer = (LPNET_DFS_ENTRY_ID)
                                            (capture->RelationInfo + 1);
        variableData += RELATION_INFO_SIZE( RelationInfo );
        for( i=0; i < RelationInfo->Count; i++ ) {
            CAPTURE_STRING( RelationInfo->Buffer[i].Prefix );
            capture->RelationInfo->Buffer[i].Uid = RelationInfo->Buffer[i].Uid;
        }

        POINTER_TO_OFFSET( capture->RelationInfo->Buffer, capture );
        POINTER_TO_OFFSET( capture->RelationInfo, capture );
    }

    CAPTURE_STRING( VolumeName );
    CAPTURE_STRING( StgId );
    CAPTURE_STRING( EntryPrefix );

    //
    // Tell the driver!
    //
    error = DfsFsctl(
                FSCTL_DFS_FIX_LOCAL_VOLUME,
                capture,
                size,
                NULL,
                0
            );

    MIDL_user_free( capture );

    return error;
}

//+----------------------------------------------------------------------------
//
//  NetrDfsManagerReportSiteInfo
//
//  Sends back the site(s) this server covers.
//
//  For debugging and other purposes, we first check a registry value with
//  the servername passed in.  If we get a match, we use the list of sites
//  in that value, and don't put our site in the list.  Otherwise we always
//  return our site (if available) and the sites in the default list.
//
//+----------------------------------------------------------------------------

NET_API_STATUS NET_API_FUNCTION
NetrDfsManagerReportSiteInfo (
    IN  SRVSVC_HANDLE               ServerName,
    OUT LPDFS_SITELIST_INFO         *ppSiteInfo
    )
{

    DWORD status;
    LPWSTR ThisSite = NULL;
    LPWSTR CoveredSites = NULL;
    LPDFS_SITELIST_INFO pSiteInfo = NULL;
    ULONG Size;
    ULONG cSites;
    LPWSTR pSiteName;
    LPWSTR pNames;
    ULONG iSite;
    ULONG j;
    DWORD dwType;
    DWORD dwUnused;
    ULONG cbBuffer;
    HKEY hkey;
    BOOLEAN fUsingDefault = TRUE;

    if (ppSiteInfo == NULL || ServerName == NULL) {

        return ERROR_INVALID_PARAMETER;

    }

    status = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                REG_KEY_COVERED_SITES,
                0,
                KEY_QUERY_VALUE,
                &hkey);

    if( status == ERROR_SUCCESS ) {

        status = RegQueryInfoKey(
                    hkey,                            // Key
                    NULL,                            // Class string
                    NULL,                            // Size of class string
                    NULL,                            // Reserved
                    &dwUnused,                       // # of subkeys
                    &dwUnused,                       // max size of subkey name
                    &dwUnused,                       // max size of class name
                    &dwUnused,                       // # of values
                    &dwUnused,                       // max size of value name
                    &cbBuffer,                       // max size of value data,
                    NULL,                            // security descriptor
                    NULL);                           // Last write time

        //
        // Check if there's a value the same name as the servername passed in,
        // if so, use it.  Else default to value REG_VALUE_COVERED_SITES.
        //

        if (status == ERROR_SUCCESS) {

            CoveredSites = MIDL_user_allocate(cbBuffer);

            if (CoveredSites != NULL) {

                status = RegQueryValueEx(
                                hkey,
                                ServerName,
                                NULL,
                                &dwType,
                                (PCHAR)CoveredSites,
                                &cbBuffer);

                if (status == ERROR_SUCCESS && dwType == REG_MULTI_SZ) {

                    fUsingDefault = FALSE;

                } else { 

                    status = RegQueryValueEx(
                                    hkey,
                                    REG_VALUE_COVERED_SITES,
                                    NULL,
                                    &dwType,
                                    (PCHAR)CoveredSites,
                                    &cbBuffer);

                    if ( status != ERROR_SUCCESS || dwType != REG_MULTI_SZ) {

                        MIDL_user_free(CoveredSites);

                        CoveredSites = NULL;

                    }

                }

            }

        }

        RegCloseKey( hkey );
    }

    //
    // Size the return buffer
    //

    Size = 0;

    for (cSites = 0, pNames = CoveredSites; pNames && *pNames; cSites++) {

        Size += (wcslen(pNames) + 1) * sizeof(WCHAR);

        pNames += wcslen(pNames) + 1;

    }

    //
    // Get site we belong to, if we're using the defaults
    //

    ThisSite = NULL;

    if (fUsingDefault == TRUE) {

        status = DsGetSiteName(NULL, &ThisSite);

        if (status == NO_ERROR && ThisSite != NULL) {

            Size += (wcslen(ThisSite) + 1) * sizeof(WCHAR);

            cSites++;

        }

    }

    //
    // If no sites are configured, and we couldn't determine our site,
    // then we fail.
    //

    if (cSites == 0) {

        status = ERROR_NO_SITENAME;

        goto ErrorReturn;

    }

    Size += FIELD_OFFSET(DFS_SITELIST_INFO,Site[cSites]);

    pSiteInfo = MIDL_user_allocate(Size);

    if (pSiteInfo == NULL) {

        status =  ERROR_NOT_ENOUGH_MEMORY;

        goto ErrorReturn;

    }

    RtlZeroMemory(pSiteInfo, Size);

    pSiteInfo->cSites = cSites;

    pSiteName = (LPWSTR) ((PCHAR)pSiteInfo +
                            sizeof(DFS_SITELIST_INFO) +
                                sizeof(DFS_SITENAME_INFO) * (cSites - 1));

    //
    // Marshall the site strings into the buffer
    //

    iSite = 0;

    if (ThisSite != NULL) {

        wcscpy(pSiteName, ThisSite);

        pSiteInfo->Site[iSite].SiteFlags = DFS_SITE_PRIMARY;

        pSiteInfo->Site[iSite++].SiteName = pSiteName;

        pSiteName += wcslen(ThisSite) + 1;

    }

    for (pNames = CoveredSites; pNames && *pNames; pNames += wcslen(pNames) + 1) {

        wcscpy(pSiteName, pNames);

        pSiteInfo->Site[iSite++].SiteName = pSiteName;

        pSiteName += wcslen(pSiteName) + 1;

    }

    *ppSiteInfo = pSiteInfo;

    if (CoveredSites != NULL) {

        MIDL_user_free(CoveredSites);

    }

    if (ThisSite != NULL) {

        NetApiBufferFree(ThisSite);

    }

    return status;

ErrorReturn:

    if (pSiteInfo != NULL) {

        MIDL_user_free(pSiteInfo);

    }
    
    if (CoveredSites != NULL) {

        MIDL_user_free(CoveredSites);

    }

    if (ThisSite != NULL) {

        NetApiBufferFree(ThisSite);

    }

    return status;

}


//
// This routine returns TRUE if this machine is the root of a DFS, FALSE otherwise
//
VOID
SsSetDfsRoot()
{
    NET_API_STATUS  error;

    error = DfsFsctl( FSCTL_DFS_IS_ROOT, NULL, 0, NULL, 0 );

    SsData.IsDfsRoot = (error == NO_ERROR);
}

//
// This routine checks the LPNET_DFS_ENTRY_ID_CONTAINER container
// for correctness.
//

BOOLEAN
ValidateDfsEntryIdContainer(
    LPNET_DFS_ENTRY_ID_CONTAINER pRelationInfo)
{
    ULONG iCount;

    if (pRelationInfo->Count > 0 && pRelationInfo->Buffer == NULL)
        return FALSE;

    for (iCount = 0; iCount < pRelationInfo->Count; iCount++) {
        if (pRelationInfo->Buffer[iCount].Prefix == NULL)
            return FALSE;
    }

    return TRUE;
}