You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1575 lines
50 KiB
1575 lines
50 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 2000, Microsoft Corporation
|
|
//
|
|
// File: DfsReferral.cxx
|
|
//
|
|
// Contents: This file contains the functionality to generate a referral
|
|
//
|
|
//
|
|
// History: Jan 16 2001, Authors: RohanP/UdayH
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "DfsReferral.hxx"
|
|
#include "Align.h"
|
|
#include "dfstrusteddomain.hxx"
|
|
#include "dfsadsiapi.hxx"
|
|
#include "DfsDomainInformation.hxx"
|
|
#include "DomainControllerSupport.hxx"
|
|
#include "dfscompat.hxx"
|
|
|
|
#include "dfsreferral.tmh" // logging
|
|
|
|
LONG ShuffleFudgeFactor = 0;
|
|
|
|
|
|
extern "C" {
|
|
DWORD
|
|
I_NetDfsIsThisADomainName(
|
|
IN LPWSTR wszName);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsGetRootFolder
|
|
//
|
|
// Arguments: pName - The logical name
|
|
// pRemainingName - the name beyond the root
|
|
// ppRoot - the Dfs root found.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code otherwise
|
|
//
|
|
//
|
|
// Description: This routine runs through all the stores and looks up
|
|
// a root with the matching name context and share name.
|
|
// If multiple stores have the same share, the highest
|
|
// priority store wins (the store registered first is the
|
|
// highest priority store)
|
|
// A referenced root is returned, and the caller is
|
|
// responsible for releasing the reference.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsGetRootFolder(
|
|
IN PUNICODE_STRING pName,
|
|
OUT PUNICODE_STRING pRemainingName,
|
|
OUT DfsRootFolder **ppRoot )
|
|
{
|
|
|
|
DFSSTATUS Status;
|
|
UNICODE_STRING DfsNameContext, LogicalShare;
|
|
|
|
// First we breakup the name into the name component, the logica
|
|
// share and the rest of the name
|
|
|
|
Status = DfsGetPathComponents(pName,
|
|
&DfsNameContext,
|
|
&LogicalShare,
|
|
pRemainingName );
|
|
|
|
// If either the name component or the logical share is empty, error.
|
|
//
|
|
if (Status == ERROR_SUCCESS) {
|
|
if ((DfsNameContext.Length == 0) || (LogicalShare.Length == 0)) {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetRootFolder( &DfsNameContext,
|
|
&LogicalShare,
|
|
pRemainingName,
|
|
ppRoot );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGetRootFolder(
|
|
IN PUNICODE_STRING pNameContext,
|
|
IN PUNICODE_STRING pLogicalShare,
|
|
IN PUNICODE_STRING pRemains,
|
|
OUT DfsRootFolder **ppRoot )
|
|
{
|
|
DFSSTATUS Status;
|
|
DfsStore *pStore;
|
|
|
|
UNREFERENCED_PARAMETER(pRemains);
|
|
|
|
// Assume we are not going to find a root.
|
|
//
|
|
Status = ERROR_NOT_FOUND;
|
|
|
|
//
|
|
// For each store registered, see if we find a matching root. The
|
|
// first matching root wins.
|
|
//
|
|
for (pStore = DfsServerGlobalData.pRegisteredStores;
|
|
pStore != NULL;
|
|
pStore = pStore->pNextRegisteredStore) {
|
|
|
|
|
|
Status = pStore->LookupRoot( pNameContext,
|
|
pLogicalShare,
|
|
ppRoot );
|
|
if (Status == ERROR_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsGetRootFolder
|
|
//
|
|
// Arguments: pName - The logical name
|
|
// pRemainingName - the name beyond the root
|
|
// ppRoot - the Dfs root found.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// Error code otherwise
|
|
//
|
|
//
|
|
// Description: This routine runs through all the stores and looks up
|
|
// a root with the matching name context and share name.
|
|
// If multiple stores have the same share, the highest
|
|
// priority store wins (the store registered first is the
|
|
// highest priority store)
|
|
// A referenced root is returned, and the caller is
|
|
// responsible for releasing the reference.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsGetOnlyRootFolder(
|
|
OUT DfsRootFolder **ppRoot )
|
|
{
|
|
DfsStore *pStore;
|
|
DFSSTATUS Status;
|
|
DfsStore *pFoundStore = NULL;
|
|
ULONG RootCount;
|
|
// Assume we are not going to find a root.
|
|
//
|
|
Status = ERROR_NOT_FOUND;
|
|
|
|
//
|
|
// For each store registered, see if we find a matching root. The
|
|
// first matching root wins.
|
|
//
|
|
for (pStore = DfsServerGlobalData.pRegisteredStores;
|
|
pStore != NULL;
|
|
pStore = pStore->pNextRegisteredStore) {
|
|
|
|
Status = pStore->GetRootCount(&RootCount);
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if ((RootCount > 1) ||
|
|
(RootCount && pFoundStore))
|
|
{
|
|
Status = ERROR_DEVICE_NOT_AVAILABLE;
|
|
break;
|
|
}
|
|
|
|
if (RootCount == 1)
|
|
{
|
|
pFoundStore = pStore;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (pFoundStore == NULL)
|
|
{
|
|
Status = ERROR_NOT_FOUND;
|
|
}
|
|
}
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pFoundStore->FindFirstRoot( ppRoot );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsLookupFolder
|
|
//
|
|
// Arguments: pName - name to lookup
|
|
// pRemainingName - the part of the name that was unmatched
|
|
// ppFolder - the folder for the matching part of the name.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// ERROR code otherwise
|
|
//
|
|
// Description: This routine finds the folder for the maximal path
|
|
// that can be matched, and return the referenced folder
|
|
// along with the remaining part of the name that had
|
|
// no match.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsLookupFolder(
|
|
PUNICODE_STRING pName,
|
|
PUNICODE_STRING pRemainingName,
|
|
DfsFolder **ppFolder )
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
DfsRootFolder *pRoot = NULL;
|
|
DfsFolder *pFolder = NULL;
|
|
UNICODE_STRING LinkName, Remaining;
|
|
UNICODE_STRING LongName;
|
|
UNICODE_STRING NotUsed;
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsLookupFolder Name %wZ\n", pName);
|
|
|
|
LongName.Length = LongName.MaximumLength = 0;
|
|
LongName.Buffer = NULL;
|
|
|
|
//
|
|
// Get a root folder
|
|
//
|
|
Status = DfsGetRootFolder( pName,
|
|
&LinkName,
|
|
&pRoot );
|
|
|
|
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "get referral data, lookup folder %p, Status %x\n",
|
|
pRoot, Status);
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// we now check if the root folder is available for referral
|
|
// requests. If not, return error.
|
|
//
|
|
if (pRoot->IsRootFolderAvailableForReferral() == FALSE)
|
|
{
|
|
Status = ERROR_DEVICE_NOT_AVAILABLE;
|
|
pRoot->ReleaseReference();
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we got a root folder, see if there is a link that matches
|
|
// the rest of the name beyond the root.
|
|
//
|
|
if (Status == ERROR_SUCCESS) {
|
|
if (LinkName.Length != 0) {
|
|
Status = pRoot->LookupFolderByLogicalName( &LinkName,
|
|
&Remaining,
|
|
&pFolder );
|
|
if(Status == ERROR_NOT_FOUND)
|
|
{
|
|
//see if it's a short name, in the short name
|
|
//case, we have to calculate the remaining name
|
|
//ourselves
|
|
Status = pRoot->ExpandShortName(&LinkName,
|
|
&LongName,
|
|
&Remaining);
|
|
if(Status == ERROR_SUCCESS)
|
|
{
|
|
//NotUsed just takes the place of Remaining
|
|
//since it's really not used here
|
|
Status = pRoot->LookupFolderByLogicalName( &LongName,
|
|
&NotUsed,
|
|
&pFolder );
|
|
}
|
|
|
|
pRoot->FreeShortNameData(&LongName);
|
|
}
|
|
}
|
|
else {
|
|
Status = ERROR_NOT_FOUND;
|
|
|
|
}
|
|
|
|
//
|
|
// If no link was found beyond the root, we are interested
|
|
// in the root folder itself. Return the root folder.
|
|
// If we did find a link, we have a referenced link folder.
|
|
// Release the root and we are done.
|
|
//
|
|
if (Status == ERROR_NOT_FOUND) {
|
|
pFolder = pRoot;
|
|
Remaining = LinkName;
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
else {
|
|
pRoot->ReleaseReference();
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
*ppFolder = pFolder;
|
|
*pRemainingName = Remaining;
|
|
}
|
|
|
|
|
|
DFS_TRACE_ERROR_NORM(Status, REFERRAL, "DfsLookupFolder Exit %x name %wZ\n",
|
|
Status, pName);
|
|
return Status;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsGetReferralData
|
|
//
|
|
// Arguments: pName - name we are interested in.
|
|
// pRemainingName - the name that was unmatched.
|
|
// ppReferralData - the referral data for the matching portion.
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// error code otherwise
|
|
//
|
|
//
|
|
// Description: This routine looks up the folder for the passed in name,
|
|
// and loads the referral data for the folder and returns
|
|
// a referenced FolderReferralData to the caller.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsGetReferralData(
|
|
PUNICODE_STRING pName,
|
|
PUNICODE_STRING pRemainingName,
|
|
DfsFolderReferralData **ppReferralData,
|
|
PBOOLEAN pCacheHit )
|
|
{
|
|
DFSSTATUS Status;
|
|
DfsFolder *pFolder;
|
|
BOOLEAN CacheHit = FALSE;
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsGetReferralData Name %wZ\n", pName);
|
|
|
|
Status = DfsLookupFolder( pName,
|
|
pRemainingName,
|
|
&pFolder );
|
|
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "get referral data, lookup folder %p, Status %x\n",
|
|
pFolder, Status);
|
|
|
|
if (Status == ERROR_SUCCESS) {
|
|
|
|
Status = pFolder->GetReferralData( ppReferralData,
|
|
&CacheHit );
|
|
|
|
DFS_TRACE_LOW(REFERRAL, "Loaded %p Status %x\n", *ppReferralData, Status );
|
|
|
|
pFolder->ReleaseReference();
|
|
}
|
|
|
|
if (pCacheHit != NULL)
|
|
{
|
|
*pCacheHit = CacheHit;
|
|
}
|
|
|
|
DFS_TRACE_ERROR_LOW( Status, REFERRAL, "DfsGetReferralData Name %wZ Status %x\n",
|
|
pName, Status );
|
|
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
ULONG
|
|
DfsGetInterSiteCost(
|
|
PUNICODE_STRING pSiteFrom,
|
|
PUNICODE_STRING pSiteTo)
|
|
{
|
|
ULONG Cost = 100;
|
|
|
|
if ((IsEmptyString(pSiteFrom->Buffer) == FALSE) &&
|
|
(IsEmptyString(pSiteTo->Buffer) == FALSE))
|
|
{
|
|
if (RtlEqualUnicodeString(pSiteFrom, pSiteTo, TRUE ))
|
|
{
|
|
Cost = 0;
|
|
}
|
|
}
|
|
|
|
return Cost;
|
|
}
|
|
*/
|
|
|
|
|
|
VOID
|
|
DfsShuffleReplicas(
|
|
REPLICA_COST_INFORMATION * pReplicaCosts,
|
|
ULONG nStart,
|
|
ULONG nEnd)
|
|
{
|
|
ULONG i = 0;
|
|
ULONG j = 0;
|
|
ULONG nRemaining = 0;
|
|
ULONG CostTemp = 0;
|
|
LONG NewFactor = 0;
|
|
DfsReplica * pTempReplica = NULL;
|
|
LARGE_INTEGER Seed;
|
|
|
|
for (i = nStart; i < nEnd; i++)
|
|
{
|
|
NtQuerySystemTime( &Seed );
|
|
NewFactor = InterlockedIncrement(&ShuffleFudgeFactor);
|
|
|
|
Seed.LowPart += (NewFactor + GetTickCount());
|
|
|
|
|
|
DFS_TRACE_LOW(REFERRAL, "Shuffling %d to %d, seed %d (%x)\n",
|
|
nStart, nEnd, Seed.LowPart, Seed.LowPart );
|
|
|
|
//
|
|
// Exchange the current entry with one in the remaining portion.
|
|
// Make sure the entry doesn't get swapped with itself.
|
|
//
|
|
j = (RtlRandomEx( &Seed.LowPart ) % (nEnd - i)) + i;
|
|
|
|
//
|
|
// Give up.
|
|
//
|
|
if (j == i)
|
|
{
|
|
DFS_TRACE_LOW(REFERRAL_SERVER, "NOT Shuffling %d with %d\n",
|
|
i, j);
|
|
continue;
|
|
}
|
|
DFS_TRACE_LOW(REFERRAL_SERVER, "Shuffling %d with %d\n",
|
|
i, j);
|
|
|
|
CostTemp = (&pReplicaCosts[i])->ReplicaCost;
|
|
pTempReplica = (&pReplicaCosts[i])->pReplica;
|
|
|
|
(&pReplicaCosts[i])->pReplica = (&pReplicaCosts[j])->pReplica;
|
|
(&pReplicaCosts[i])->ReplicaCost = (&pReplicaCosts[j])->ReplicaCost;
|
|
|
|
(&pReplicaCosts[j])->pReplica = pTempReplica;
|
|
(&pReplicaCosts[j])->ReplicaCost = CostTemp;
|
|
|
|
}
|
|
DFS_TRACE_LOW(REFERRAL, "Shuffling done\n");
|
|
}
|
|
|
|
VOID
|
|
DfsSortReplicas(
|
|
REPLICA_COST_INFORMATION * pReplicaCosts,
|
|
ULONG NumReplicas)
|
|
{
|
|
LONG LoopVar = 0;
|
|
LONG InnerLoop = 0;
|
|
ULONG CostTemp = 0;
|
|
DfsReplica * pTempReplica = NULL;
|
|
|
|
for (LoopVar = 1; LoopVar < (LONG) NumReplicas; LoopVar++)
|
|
{
|
|
CostTemp = (&pReplicaCosts[LoopVar])->ReplicaCost;
|
|
pTempReplica = (&pReplicaCosts[LoopVar])->pReplica;
|
|
|
|
for(InnerLoop = LoopVar - 1; InnerLoop >= 0; InnerLoop--)
|
|
{
|
|
if((&pReplicaCosts[InnerLoop])->ReplicaCost > CostTemp)
|
|
{
|
|
(&pReplicaCosts[InnerLoop + 1])->ReplicaCost = (&pReplicaCosts[InnerLoop])->ReplicaCost;
|
|
(&pReplicaCosts[InnerLoop + 1])->pReplica = (&pReplicaCosts[InnerLoop])->pReplica;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
(&pReplicaCosts[InnerLoop + 1])->ReplicaCost = CostTemp;
|
|
(&pReplicaCosts[InnerLoop + 1])->pReplica = pTempReplica;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DfsShuffleAndSortReferralInformation(
|
|
PREFERRAL_INFORMATION pReferralInformation )
|
|
{
|
|
DfsShuffleReplicas( &pReferralInformation->ReplicaCosts[0], 0, pReferralInformation->NumberOfReplicas);
|
|
DfsSortReplicas( &pReferralInformation->ReplicaCosts[0], pReferralInformation->NumberOfReplicas);
|
|
}
|
|
|
|
VOID
|
|
DfsGetDefaultInterSiteCost(
|
|
IN DfsSite *pReferralSite,
|
|
IN DfsReplica *pReplica,
|
|
OUT PULONG pCost)
|
|
{
|
|
pReferralSite->GetDefaultSiteCost( pReplica->GetSite(), pCost );
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
DfsReleaseReferralInformation(
|
|
PREFERRAL_INFORMATION pReferralInfo )
|
|
{
|
|
delete [] (PBYTE)(pReferralInfo);
|
|
|
|
return NOTHING;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsGetDsInterSiteCost(
|
|
IN DfsSite *pReferralSite,
|
|
IN DfsReferralData *pReferralData,
|
|
IN DfsReplica *pReplica,
|
|
IN OUT PBOOLEAN pRetry,
|
|
OUT PULONG pCost)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
BOOLEAN CostMatrixGenerated = *pRetry;
|
|
BOOLEAN UseDefault = FALSE;
|
|
DFSSTATUS RetStatus = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Lookup the cost of going from the ReferralSite to the Replica Site
|
|
// in our cache.
|
|
//
|
|
Status = pReferralSite->GetRealSiteCost( pReplica->GetSite(), pCost );
|
|
|
|
// Try only once.
|
|
if (CostMatrixGenerated)
|
|
{
|
|
*pRetry = FALSE;
|
|
}
|
|
|
|
// Ruminate over our choices based on what GetCost returned.
|
|
do {
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
// we are all set
|
|
*pRetry = FALSE;
|
|
break;
|
|
}
|
|
|
|
// We can get memory allocation failures at this point.
|
|
if (CostMatrixGenerated || Status != ERROR_NOT_FOUND)
|
|
{
|
|
//
|
|
// We've tried generating the inter-site cost once, but still
|
|
// we haven't been able to find what we need. So use the default scheme.
|
|
//
|
|
UseDefault = TRUE;
|
|
break;
|
|
}
|
|
|
|
// We try to generate the cost matrix at most once per referral.
|
|
// This is the first time around.
|
|
|
|
ASSERT(Status == ERROR_NOT_FOUND);
|
|
ASSERT(CostMatrixGenerated == FALSE);
|
|
|
|
Status = pReferralData->GenerateCostMatrix( pReferralSite );
|
|
|
|
//
|
|
// If our call to the DS failed, then we just have to fall back on our default.
|
|
//
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
UseDefault = TRUE;
|
|
RetStatus = Status;
|
|
break;
|
|
}
|
|
|
|
// Caller needs to iterate over all the referrals again.
|
|
*pRetry = TRUE;
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Somehow we haven't been able to find the real inter-site cost.
|
|
// Use the default cost. This call won't fail.
|
|
//
|
|
if (UseDefault)
|
|
{
|
|
*pRetry = FALSE;
|
|
pReferralSite->GetDefaultSiteCost( pReplica->GetSite(), pCost );
|
|
}
|
|
|
|
//
|
|
// The only errors that we propogate back are those we get from the DS.
|
|
// They indicate that the cost matrix didn't get generated so we know not
|
|
// to retry this call for all the link targets.
|
|
//
|
|
return RetStatus;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsGetReferralInformation
|
|
//
|
|
// Arguments: pReferralData - the referral data
|
|
// NumReplicasToReturn - Number of replicas to return
|
|
//
|
|
// Returns: ERROR_SUCCESS
|
|
// ERROR_NOT_ENOUGH_MEMORY
|
|
//
|
|
//
|
|
// Description: This routine generates the cost of reaching each replica
|
|
//
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsGetReferralInformation(
|
|
PUNICODE_STRING pUseTargetServer,
|
|
PUNICODE_STRING pUseFolder,
|
|
DfsSite *pReferralSite,
|
|
DfsReferralData *pReferralData,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
PREFERRAL_INFORMATION *ppReferralInformation )
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
ULONG NumReplicas = 0;
|
|
ULONG TotalSize =0;
|
|
ULONG SizeOfStrings = 0;
|
|
ULONG Cost = 0;
|
|
DfsReplica *pReplica = NULL;
|
|
PREFERRAL_INFORMATION pReferralInfo = NULL;
|
|
BOOLEAN IsSiteCostingEnabled = FALSE;
|
|
BOOLEAN Retrying = FALSE;
|
|
BOOLEAN DsErrorHit = FALSE;
|
|
BOOLEAN DomainReplica = FALSE;
|
|
|
|
//
|
|
// Give out Insite referrals only, if we are not pointing to
|
|
// an interlink with a single target (this is a clue to us that
|
|
// this is a potential domain DFS, where insite does not make
|
|
// sense)
|
|
//
|
|
|
|
if (pReferralData->IsOutOfDomain() && (pReferralData->ReplicaCount == 1))
|
|
{
|
|
pReplica = &pReferralData->pReplicas[ 0 ];
|
|
if (pReplica->IsTargetAvailable())
|
|
{
|
|
PUNICODE_STRING pServer = NULL;
|
|
DFSSTATUS DomainStatus = ERROR_SUCCESS;
|
|
|
|
pServer = pReplica->GetTargetServer();
|
|
|
|
DomainStatus = I_NetDfsIsThisADomainName(pServer->Buffer);
|
|
if (DomainStatus == ERROR_SUCCESS)
|
|
{
|
|
DomainReplica = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (DomainReplica == FALSE)
|
|
{
|
|
if (pReferralData->IsRestrictToSite())
|
|
{
|
|
CostLimit = 0;
|
|
}
|
|
|
|
IsSiteCostingEnabled = pReferralData->DoSiteCosting;
|
|
}
|
|
|
|
//allocate the buffer
|
|
TotalSize = sizeof(REFERRAL_INFORMATION) + (pReferralData->ReplicaCount * sizeof(REPLICA_COST_INFORMATION));
|
|
pReferralInfo = (PREFERRAL_INFORMATION) new BYTE[TotalSize];
|
|
|
|
DFS_TRACE_LOW(REFERRAL, "Client Site %wZ\n", pReferralSite->SiteName());
|
|
|
|
if (pReferralInfo == NULL)
|
|
{
|
|
*ppReferralInformation = NULL;
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Make sure the site is ready to receive data. This means it is not
|
|
// going to throw its SiteCostCache away while we are trying to add to it, or
|
|
// read from it. This is a NO-OP when site costing is disabled.
|
|
//
|
|
pReferralSite->StartPrepareForCostGeneration( IsSiteCostingEnabled );
|
|
|
|
do {
|
|
|
|
RtlZeroMemory(pReferralInfo, TotalSize);
|
|
pReferralInfo->pUseTargetServer = pUseTargetServer;
|
|
pReferralInfo->pUseTargetFolder = pUseFolder;
|
|
DsErrorHit = FALSE;
|
|
|
|
for (NumReplicas = 0; NumReplicas < pReferralData->ReplicaCount; NumReplicas++)
|
|
{
|
|
pReplica = &pReferralData->pReplicas[ NumReplicas ];
|
|
if (pReplica->IsTargetAvailable() == FALSE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!IsSiteCostingEnabled || DsErrorHit)
|
|
{
|
|
// The cost is going to be either Zero or ULONG_MAX.
|
|
DfsGetDefaultInterSiteCost(pReferralSite,
|
|
pReplica,
|
|
&Cost);
|
|
ASSERT(Retrying == FALSE);
|
|
ASSERT(Status == ERROR_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find the cost between the referral site and this potential destination replica.
|
|
// If Site-Costing is turned on, we attempt to get the intersite cost from
|
|
// our site-cost cache(s). If the information isn't cached we try to get the cost matrix
|
|
// from an ISTG nearby. The protocol here is to RETRY this entire loop just once
|
|
// if the DfsGetInterSiteCost call below returns Retrying = TRUE.
|
|
//
|
|
if (DfsGetDsInterSiteCost(pReferralSite,
|
|
pReferralData,
|
|
pReplica,
|
|
&Retrying,
|
|
&Cost) != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Remember the fact that we got an error trying to generate
|
|
// the cost matrix. This way we won't try to do this DS call
|
|
// for every replica. However, it is entirely possible for another
|
|
// thread to try to generate the cost in the meantime.
|
|
//
|
|
DsErrorHit = TRUE;
|
|
ASSERT(Retrying == FALSE);
|
|
}
|
|
|
|
//
|
|
// If we don't find this, then we know we should retry.
|
|
// So start this all over again.
|
|
// We only retry once.
|
|
//
|
|
if (Retrying)
|
|
{
|
|
DFS_TRACE_LOW(REFERRAL, "Intersite cost for <%ws, %ws> not found. Retrying the referral\n",
|
|
pReferralSite->SiteNameString(),
|
|
pReplica->GetSite()->SiteNameString());
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
DFS_TRACE_LOW(REFERRAL, "REplica %wZ, Inter site <%ws, %ws> Cost %d (Limit = %d)\n",
|
|
pReplica->GetTargetServer(),
|
|
pReferralSite->SiteNameString(),
|
|
pReplica->GetSite()->SiteNameString(),
|
|
Cost,
|
|
CostLimit);
|
|
if (Cost <= CostLimit)
|
|
{
|
|
PUNICODE_STRING pTargetServer = (pUseTargetServer == NULL)? pReplica->GetTargetServer() : pUseTargetServer;
|
|
PUNICODE_STRING pTargetFolder = (pUseFolder == NULL) ? pReplica->GetTargetFolder() : pUseFolder;
|
|
|
|
pReferralInfo->ReplicaCosts[pReferralInfo->NumberOfReplicas].ReplicaCost = Cost;
|
|
pReferralInfo->ReplicaCosts[pReferralInfo->NumberOfReplicas].pReplica = pReplica;
|
|
|
|
SizeOfStrings = (sizeof(UNICODE_PATH_SEP) +
|
|
pTargetServer->Length +
|
|
sizeof(UNICODE_PATH_SEP) +
|
|
pTargetFolder->Length );
|
|
|
|
SizeOfStrings = ROUND_UP_COUNT(SizeOfStrings, ALIGN_LONG);
|
|
|
|
pReferralInfo->TotalReplicaStringLength += SizeOfStrings;
|
|
pReferralInfo->NumberOfReplicas++;
|
|
}
|
|
}
|
|
|
|
} while (Retrying);
|
|
|
|
pReferralSite->EndPrepareForCostGeneration( IsSiteCostingEnabled );
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (pReferralInfo->NumberOfReplicas > 1)
|
|
{
|
|
DfsShuffleAndSortReferralInformation( pReferralInfo );
|
|
}
|
|
if (pReferralInfo->NumberOfReplicas > NumReplicasToReturn)
|
|
{
|
|
pReferralInfo->NumberOfReplicas = NumReplicasToReturn;
|
|
}
|
|
|
|
*ppReferralInformation = pReferralInfo;
|
|
|
|
{
|
|
ULONG i;
|
|
DFS_TRACE_LOW(REFERRAL, "Final Referral for Client Site %ws offline status is %ws\n", pReferralSite->SiteNameString(),
|
|
(pReferralData->FolderOffLine == TRUE) ? L"OFFLINE":L"ONLINE" );
|
|
|
|
for (i=0 ; i<pReferralInfo->NumberOfReplicas; i++)
|
|
{
|
|
DFS_TRACE_LOW(REFERRAL, "%d. Target %wZ, Site %ws, Cost %d\n",
|
|
i,
|
|
pReferralInfo->ReplicaCosts[i].pReplica->GetTargetServer(),
|
|
pReferralInfo->ReplicaCosts[i].pReplica->GetSite()->SiteNameString(),
|
|
pReferralInfo->ReplicaCosts[i].ReplicaCost);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
DfsReleaseReferralInformation( pReferralInfo );
|
|
*ppReferralInformation = NULL;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsExtractReplicaData -
|
|
//
|
|
// Arguments: pReferralData - the referral data
|
|
// NumReplicasToReturn - Number of replicas to return
|
|
// CostLimit - maximum cost caller is willing to accept
|
|
// Name - link name
|
|
// pReplicaCosts - array of replicas with cost info
|
|
// ppReferralHeader - address of buffer to accept replica info
|
|
//
|
|
// Returns: Status
|
|
// ERROR_SUCCESS
|
|
// ERROR_NOT_ENOUGH_MEMORY
|
|
// others
|
|
//
|
|
//
|
|
// Description: This routine formats the replicas into the format
|
|
// the client expects. Which is a REFERRAL_HEADER followed
|
|
// by an array of REPLICA_INFORMATIONs
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsExtractReferralData(
|
|
PUNICODE_STRING pName,
|
|
PREFERRAL_INFORMATION pReferralInformation,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
ULONG NumReplicas = 0;
|
|
ULONG TotalSize = 0;
|
|
ULONG HeaderBaseLength = 0;
|
|
ULONG BaseLength = 0;
|
|
ULONG LinkNameLength = 0;
|
|
PREFERRAL_HEADER pHeader = NULL;
|
|
ULONG CurrentNameLength =0;
|
|
ULONG CurrentEntryLength = 0;
|
|
ULONG NextEntry = 0;
|
|
DfsReplica *pReplica = NULL;
|
|
PUNICODE_STRING pUseTargetFolder = pReferralInformation->pUseTargetFolder;
|
|
PUNICODE_STRING pUseTargetServer = pReferralInformation->pUseTargetServer;
|
|
PUCHAR ReferralBuffer = NULL;
|
|
PUCHAR pReplicaBuffer = NULL;
|
|
PWCHAR ReturnedName = NULL;
|
|
PUNICODE_STRING pTargetServer = NULL;
|
|
PUNICODE_STRING pTargetFolder = NULL;
|
|
|
|
DFS_TRACE_LOW(REFERRAL, "Entering DfsExtractReferralData");
|
|
|
|
//calculate size of header base structure
|
|
HeaderBaseLength = FIELD_OFFSET( REFERRAL_HEADER, LinkName[0] );
|
|
|
|
//calculate link name
|
|
LinkNameLength = pName->Length;
|
|
|
|
//calculate size of base replica structure
|
|
BaseLength = FIELD_OFFSET( REPLICA_INFORMATION, ReplicaName[0] );
|
|
|
|
//the total size of the data to be returned is the sum of all the
|
|
//above calculated sizes
|
|
TotalSize = ROUND_UP_COUNT((HeaderBaseLength + LinkNameLength), ALIGN_LONG) +
|
|
(pReferralInformation->NumberOfReplicas * ROUND_UP_COUNT(BaseLength, ALIGN_LONG)) +
|
|
pReferralInformation->TotalReplicaStringLength +
|
|
sizeof(DWORD); // null termination at the end.
|
|
|
|
//allocate the buffer
|
|
ReferralBuffer = new BYTE[ TotalSize ];
|
|
if (ReferralBuffer != NULL)
|
|
{
|
|
RtlZeroMemory( ReferralBuffer, TotalSize );
|
|
|
|
pHeader = (PREFERRAL_HEADER) ReferralBuffer;
|
|
pHeader->VersionNumber = CURRENT_DFS_REPLICA_HEADER_VERSION;
|
|
pHeader->ReplicaCount = pReferralInformation->NumberOfReplicas;
|
|
pHeader->OffsetToReplicas = ROUND_UP_COUNT((HeaderBaseLength + LinkNameLength), ALIGN_LONG);
|
|
pHeader->LinkNameLength = LinkNameLength;
|
|
pHeader->TotalSize = TotalSize;
|
|
pHeader->ReferralFlags = 0;
|
|
|
|
//copy the link name at the end of the header
|
|
RtlCopyMemory(&ReferralBuffer[HeaderBaseLength], pName->Buffer, LinkNameLength);
|
|
|
|
//place the replicas starting here
|
|
pReplicaBuffer = (PUCHAR) ((PBYTE)ReferralBuffer + pHeader->OffsetToReplicas);
|
|
|
|
//format the replicas in the output buffer
|
|
for ( NumReplicas = 0; NumReplicas < pReferralInformation->NumberOfReplicas ; NumReplicas++ )
|
|
{
|
|
NextEntry += (ULONG)( CurrentEntryLength );
|
|
pReplica = pReferralInformation->ReplicaCosts[NumReplicas].pReplica;
|
|
|
|
pTargetServer = (pUseTargetServer == NULL) ? pReplica->GetTargetServer() : pUseTargetServer;
|
|
|
|
pTargetFolder = (pUseTargetFolder == NULL) ? pReplica->GetTargetFolder() : pUseTargetFolder;
|
|
|
|
|
|
CurrentNameLength = 0;
|
|
ReturnedName = (PWCHAR) &pReplicaBuffer[NextEntry + BaseLength];
|
|
|
|
//
|
|
// Start with the leading path seperator
|
|
//
|
|
ReturnedName[ CurrentNameLength / sizeof(WCHAR) ] = UNICODE_PATH_SEP;
|
|
CurrentNameLength += sizeof(UNICODE_PATH_SEP);
|
|
|
|
//
|
|
// next copy the server name.
|
|
//
|
|
RtlMoveMemory( &ReturnedName[ CurrentNameLength / sizeof(WCHAR) ],
|
|
pTargetServer->Buffer,
|
|
pTargetServer->Length);
|
|
CurrentNameLength += pTargetServer->Length;
|
|
|
|
if (pTargetFolder->Length > 0)
|
|
{
|
|
//
|
|
// insert the unicode path seperator.
|
|
//
|
|
|
|
ReturnedName[ CurrentNameLength / sizeof(WCHAR) ] = UNICODE_PATH_SEP;
|
|
CurrentNameLength += sizeof(UNICODE_PATH_SEP);
|
|
|
|
RtlMoveMemory( &ReturnedName[ CurrentNameLength / sizeof(WCHAR) ],
|
|
pTargetFolder->Buffer,
|
|
pTargetFolder->Length);
|
|
CurrentNameLength += pTargetFolder->Length;
|
|
}
|
|
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaFlags = pReplica->GetReplicaFlags();
|
|
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaCost = pReferralInformation->ReplicaCosts[NumReplicas].ReplicaCost;
|
|
((PREPLICA_INFORMATION)&pReplicaBuffer[NextEntry])->ReplicaNameLength = CurrentNameLength;
|
|
|
|
CurrentEntryLength = ROUND_UP_COUNT((CurrentNameLength + BaseLength), ALIGN_LONG);
|
|
|
|
//setup the offset to the next entry
|
|
*((PULONG)(&pReplicaBuffer[NextEntry])) = pHeader->OffsetToReplicas + NextEntry + CurrentEntryLength;
|
|
}
|
|
*((PULONG)(&pReplicaBuffer[NextEntry])) = 0;
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
*ppReferralHeader = pHeader;
|
|
}
|
|
|
|
|
|
DFS_TRACE_ERROR_HIGH(Status, REFERRAL, "Leaving DfsExtractReferralData, Status %x",
|
|
Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateReferralFromData(
|
|
PUNICODE_STRING pName,
|
|
PUNICODE_STRING pUseTargetServer,
|
|
PUNICODE_STRING pUseFolder,
|
|
DfsSite *pSite,
|
|
DfsReferralData *pReferralData,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status;
|
|
REFERRAL_INFORMATION *pReferralInformation;
|
|
|
|
//make sure the user doesn't over step his bounds
|
|
if( (NumReplicasToReturn > pReferralData->ReplicaCount) ||
|
|
(NumReplicasToReturn == 0) )
|
|
{
|
|
NumReplicasToReturn = pReferralData->ReplicaCount;
|
|
}
|
|
|
|
Status = DfsGetReferralInformation( pUseTargetServer,
|
|
pUseFolder,
|
|
pSite,
|
|
pReferralData,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
&pReferralInformation );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsExtractReferralData( pName,
|
|
pReferralInformation,
|
|
ppReferralHeader);
|
|
|
|
if(Status == STATUS_SUCCESS)
|
|
{
|
|
(*ppReferralHeader)->Timeout = pReferralData->Timeout;
|
|
}
|
|
|
|
DfsReleaseReferralInformation( pReferralInformation );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateADBlobReferral(
|
|
PUNICODE_STRING pName,
|
|
PUNICODE_STRING pShare,
|
|
DfsSite *pReferralSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status;
|
|
DfsReferralData *pReferralData;
|
|
UNICODE_STRING ShareName;
|
|
|
|
Status = DfsCreateUnicodeString( &ShareName, pShare );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetADRootReferralData( pName,
|
|
&pReferralData );
|
|
|
|
DfsFreeUnicodeString( &ShareName );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGenerateReferralFromData( pName,
|
|
NULL,
|
|
NULL,
|
|
pReferralSite,
|
|
pReferralData,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
|
|
}
|
|
pReferralData->ReleaseReference();
|
|
}
|
|
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateDomainDCReferral(
|
|
PUNICODE_STRING pDomainName,
|
|
DfsSite *pReferralSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsReferralData *pReferralData = NULL;
|
|
BOOLEAN CacheHit;
|
|
DfsDomainInformation *pDomainInfo;
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsGenerateDomainDcReferral for Domain %wZ\n",
|
|
pDomainName);
|
|
|
|
|
|
Status = DfsAcquireDomainInfo( &pDomainInfo );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pDomainInfo->GetDomainDcReferralInfo( pDomainName,
|
|
&pReferralData,
|
|
&CacheHit );
|
|
|
|
DfsReleaseDomainInfo (pDomainInfo );
|
|
}
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGenerateReferralFromData( pDomainName,
|
|
NULL,
|
|
NULL,
|
|
pReferralSite,
|
|
pReferralData,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_DOMAIN_DC_REFERRAL;
|
|
}
|
|
|
|
pReferralData->ReleaseReference();
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateNormalReferral(
|
|
LPWSTR LinkName,
|
|
DfsSite *pSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsFolderReferralData *pReferralData = NULL;
|
|
BOOLEAN CacheHit = TRUE;
|
|
DFSSTATUS GetStatus = ERROR_SUCCESS;
|
|
PUNICODE_STRING pUseTargetServer = NULL;
|
|
UNICODE_STRING ServerComponent, ShareComponent, RemainingName;
|
|
DfsRootFolder *pRoot = NULL;
|
|
UNICODE_STRING LinkRemains;
|
|
UNICODE_STRING Name, Remaining;
|
|
|
|
ULONG StartTime, EndTime;
|
|
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsGenerateReferral for Link %ws\n",
|
|
LinkName);
|
|
|
|
DfsGetTimeStamp( &StartTime );
|
|
|
|
Status = DfsRtlInitUnicodeStringEx(&Name, LinkName);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsGetReferralData( &Name,
|
|
&Remaining,
|
|
&pReferralData,
|
|
&CacheHit );
|
|
|
|
//
|
|
// DFSDEV: this is necessary to support clusters: the api request will
|
|
// neveer come to the dfs server when the VS name has failed.
|
|
// The cluster service retries the api request with the machine name,
|
|
// the dfs api still goes to the vs name due to the way we pack the
|
|
// referral: this special cases clusters.
|
|
// if the request comes in with a machine name, return the machine
|
|
// name.
|
|
//
|
|
|
|
if ((Status == ERROR_SUCCESS) &&
|
|
DfsIsMachineCluster())
|
|
{
|
|
Status = DfsGetPathComponents( &Name,
|
|
&ServerComponent,
|
|
&ShareComponent,
|
|
&RemainingName );
|
|
if ((Status == ERROR_SUCCESS) &&
|
|
(RemainingName.Length == 0))
|
|
{
|
|
UNICODE_STRING MachineName;
|
|
|
|
Status = DfsGetMachineName( &MachineName );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if ( (ServerComponent.Length == MachineName.Length) &&
|
|
(_wcsnicmp( ServerComponent.Buffer,
|
|
MachineName.Buffer,
|
|
MachineName.Length/sizeof(WCHAR)) == 0) )
|
|
{
|
|
pUseTargetServer = &ServerComponent;
|
|
}
|
|
DfsReleaseMachineName( &MachineName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Name.Length -= Remaining.Length;
|
|
if (Name.Length && (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1] == UNICODE_PATH_SEP))
|
|
{
|
|
Name.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
Status = DfsGenerateReferralFromData( &Name,
|
|
pUseTargetServer,
|
|
NULL,
|
|
pSite,
|
|
pReferralData,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader);
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (pReferralData->IsRootReferral())
|
|
{
|
|
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
|
|
}
|
|
if (pReferralData->IsOutOfDomain())
|
|
{
|
|
(*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_OUT_OF_DOMAIN;
|
|
}
|
|
}
|
|
|
|
pReferralData->ReleaseReference();
|
|
}
|
|
|
|
DfsGetTimeStamp( &EndTime );
|
|
|
|
|
|
//
|
|
// Get a root folder
|
|
//
|
|
GetStatus = DfsGetRootFolder( &Name,
|
|
&LinkRemains,
|
|
&pRoot );
|
|
|
|
if (GetStatus == ERROR_SUCCESS)
|
|
{
|
|
|
|
pRoot->pStatistics->UpdateReferralStat( CacheHit,
|
|
EndTime - StartTime,
|
|
Status );
|
|
pRoot->ReleaseReference();
|
|
}
|
|
|
|
done:
|
|
|
|
DFS_TRACE_ERROR_HIGH( Status, REFERRAL, "DfsGenerateReferral for Link %ws CacheHit %d Status %x\n",
|
|
LinkName, CacheHit, Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateSpecialShareReferral(
|
|
PUNICODE_STRING pName,
|
|
PUNICODE_STRING pDomainName,
|
|
PUNICODE_STRING pShareName,
|
|
DfsSite *pSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsReferralData *pReferralData = NULL;
|
|
BOOLEAN CacheHit;
|
|
DfsDomainInformation *pDomainInfo;
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsGenerateDomainDcReferral for Domain %wZ\n",
|
|
pDomainName);
|
|
|
|
|
|
Status = DfsAcquireDomainInfo( &pDomainInfo );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pDomainInfo->GetDomainDcReferralInfo( pDomainName,
|
|
&pReferralData,
|
|
&CacheHit );
|
|
|
|
DfsReleaseDomainInfo (pDomainInfo );
|
|
}
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGenerateReferralFromData( pName,
|
|
NULL,
|
|
pShareName,
|
|
pSite,
|
|
pReferralData,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
|
|
//
|
|
// Do not enable this ROOT_REFERRAL for special shares.
|
|
// These are not treated as normal DFS shares, and for compat
|
|
// purposes, it is necessary that this flag be turned off.
|
|
//
|
|
//
|
|
// if (Status == ERROR_SUCCESS) {
|
|
// (*ppReferralHeader)->ReferralFlags |= DFS_REFERRAL_DATA_ROOT_REFERRAL;
|
|
// }
|
|
|
|
pReferralData->ReleaseReference();
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsGenerateDcReferral(
|
|
LPWSTR LinkNameString,
|
|
DfsSite *pReferralSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsDomainInformation *pDomainInfo = NULL;
|
|
UNICODE_STRING NameContext;
|
|
UNICODE_STRING ShareName;
|
|
UNICODE_STRING RemainingName;
|
|
UNICODE_STRING LinkName;
|
|
|
|
Status = DfsRtlInitUnicodeStringEx( &LinkName, LinkNameString );
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
RtlInitUnicodeString(&NameContext, NULL);
|
|
|
|
if (LinkName.Length > 0)
|
|
{
|
|
Status = DfsGetPathComponents( &LinkName,
|
|
&NameContext,
|
|
&ShareName,
|
|
&RemainingName );
|
|
}
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (NameContext.Length == 0)
|
|
{
|
|
Status = DfsAcquireDomainInfo( &pDomainInfo );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = pDomainInfo->GenerateDomainReferral( ppReferralHeader );
|
|
DfsReleaseDomainInfo( pDomainInfo );
|
|
}
|
|
}
|
|
else if (ShareName.Length == 0)
|
|
{
|
|
Status = DfsGenerateDomainDCReferral( &NameContext,
|
|
pReferralSite,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
}
|
|
else if ( (RemainingName.Length == 0) )
|
|
//
|
|
// Commenting out the rest of the IF statement for win9x
|
|
// compatibility.
|
|
// (DfsIsNameContextDomainName(&NameContext)) )
|
|
//
|
|
|
|
{
|
|
if (DfsIsSpecialDomainShare(&ShareName))
|
|
{
|
|
Status = DfsGenerateSpecialShareReferral( &LinkName,
|
|
&NameContext,
|
|
&ShareName,
|
|
pReferralSite,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
}
|
|
else
|
|
{
|
|
Status = DfsGenerateADBlobReferral( &LinkName,
|
|
&ShareName,
|
|
pReferralSite,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
if (DfsIsNameContextDomainName(&NameContext) == FALSE)
|
|
{
|
|
Status = ERROR_NOT_FOUND;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
done:
|
|
|
|
return Status;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetReplicaData -
|
|
|
|
//
|
|
// Arguments: LinkName - pointer to link name
|
|
// Sitename - pointer to site name.
|
|
// NumReplicasToReturn - Number of replicas to return
|
|
// CostLimit - maximum cost caller is willing to accept
|
|
// ppReferralHeader - address of buffer to accept replica info
|
|
//
|
|
// Returns: Status
|
|
// ERROR_SUCCESS
|
|
// ERROR_NOT_ENOUGH_MEMORY
|
|
// others
|
|
//
|
|
//
|
|
// Description: This routine extracts the replicas from the referral
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
DFSSTATUS
|
|
DfsGenerateReferral(
|
|
LPWSTR LinkName,
|
|
DfsSite *pReferralSite,
|
|
DWORD NumReplicasToReturn,
|
|
ULONG CostLimit,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status;
|
|
|
|
//
|
|
// First check if this machine is a DC. If it is, it has special
|
|
// responsibility in the referral process. Check if it needs to
|
|
// provide a referral as a DC.
|
|
//
|
|
//
|
|
if (DfsIsMachineDC())
|
|
{
|
|
Status = DfsGenerateDcReferral( LinkName,
|
|
pReferralSite,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
|
|
if (Status != ERROR_NOT_FOUND)
|
|
{
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we are here, we are either an ordinary machine or a DC
|
|
// and this referral is not a DC type referral.
|
|
// Try to treat this machine as a normal machine, and generate a
|
|
// referral based on what this machine knows about.
|
|
//
|
|
Status = DfsGenerateNormalReferral( LinkName,
|
|
pReferralSite,
|
|
NumReplicasToReturn,
|
|
CostLimit,
|
|
ppReferralHeader );
|
|
|
|
//
|
|
// if we still failed, and we are a DC, it is possible that a old
|
|
// win9x client is coming in and we try to generate a referral
|
|
// for compat.
|
|
//
|
|
|
|
if ((DfsIsMachineDC()) &&
|
|
(Status != ERROR_SUCCESS))
|
|
{
|
|
DFSSTATUS CompatStatus;
|
|
|
|
CompatStatus = DfsGenerateCompatReferral( LinkName,
|
|
pReferralSite,
|
|
ppReferralHeader );
|
|
if (CompatStatus == ERROR_SUCCESS)
|
|
{
|
|
Status = CompatStatus;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DfsReleaseReferral(
|
|
REFERRAL_HEADER *pReferralHeader)
|
|
{
|
|
delete [] (PBYTE)pReferralHeader;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateCompatReferral(
|
|
LPWSTR LinkName,
|
|
DfsSite *pReferralSite,
|
|
REFERRAL_HEADER ** ppReferralHeader)
|
|
{
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
DfsFolderReferralData *pReferralData = NULL;
|
|
BOOLEAN CacheHit = TRUE;
|
|
UNICODE_STRING Name, Remaining;
|
|
|
|
DFS_TRACE_LOW( REFERRAL, "DfsGenerateCompatReferral for Link %ws\n",
|
|
LinkName);
|
|
|
|
Status = DfsRtlInitUnicodeStringEx(&Name, LinkName);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
Status = DfsGetCompatReferralData( &Name,
|
|
&Remaining,
|
|
&pReferralData,
|
|
&CacheHit );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Name.Length -= Remaining.Length;
|
|
if (Name.Length && (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1] == UNICODE_PATH_SEP))
|
|
{
|
|
Name.Length -= sizeof(WCHAR);
|
|
}
|
|
|
|
Status = DfsGenerateReferralFromData( &Name,
|
|
NULL,
|
|
NULL,
|
|
pReferralSite,
|
|
pReferralData,
|
|
1000,
|
|
DFS_MAX_COST,
|
|
ppReferralHeader);
|
|
|
|
pReferralData->ReleaseReference();
|
|
}
|
|
|
|
|
|
done:
|
|
|
|
DFS_TRACE_ERROR_HIGH( Status, REFERRAL, "DfsGenerateCompatReferral for Link %ws CacheHit %d Status %x\n",
|
|
LinkName, CacheHit, Status);
|
|
|
|
return Status;
|
|
}
|
|
|