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.
570 lines
18 KiB
570 lines
18 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 1992, Microsoft Corporation
|
|
//
|
|
// File: rpselect.c
|
|
//
|
|
// Contents: Routines to select and walk down a PKT Entry's svc list.
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions: ReplFindFirstProvider - find first appropriate provider
|
|
// ReplFindNextProvider - walk the list of providers.
|
|
//
|
|
// ReplFindRemoteService - Helper function to find a remote
|
|
// (ie, not local) service.
|
|
//
|
|
// History: 31 Aug 92 MilanS created.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "dfsprocs.h"
|
|
#include "rpselect.h"
|
|
#include "mupwml.h"
|
|
|
|
#define Dbg (DEBUG_TRACE_DNR)
|
|
|
|
NTSTATUS ReplFindRemoteService(
|
|
IN PDFS_PKT_ENTRY pPktEntry,
|
|
IN PREPL_SELECT_CONTEXT pSelectContext,
|
|
OUT ULONG *piSvc);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( PAGE, ReplFindFirstProvider )
|
|
#pragma alloc_text( PAGE, ReplFindNextProvider )
|
|
#pragma alloc_text( PAGE, ReplLookupProvider )
|
|
#pragma alloc_text( PAGE, ReplFindRemoteService )
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: ReplFindFirstProvider
|
|
//
|
|
// Synopsis: Supports the abstraction that a PKT Entry's service list is an
|
|
// ORDERED list, with a distinguished "first" element. This
|
|
// function returns that first element.
|
|
//
|
|
// Arguments: [pPktEntry] - Contains the Service List.
|
|
// [pidPrincipal] - Look for a service with this machine id
|
|
// [pustrPrincipal] - Look for a service with this principal name
|
|
// [ppService] - Will be set to point to the Service Structure
|
|
// [pSelectContext] - An opaque structure that will get initialized
|
|
// properly for future calls to
|
|
// ReplFindNextProvider().
|
|
// [pLastEntry] - TRUE if last entry, FALSE otherwise
|
|
//
|
|
// Notes: Assumes PktEntry is locked.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- If provider found.
|
|
//
|
|
// [STATUS_NO_MORE_ENTRIES] -- If no provider found.
|
|
//
|
|
// [STATUS_OBJECT_NAME_NOT_FOUND] if prin. name spec'd but no
|
|
// service has that name.
|
|
//
|
|
// [STATUS_OBJECT_TYPE_MISMATCH] if prin. name spec'd and matched
|
|
// with service, but service can't be used because of
|
|
// type or provider incompatibility.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
ReplFindFirstProvider(
|
|
IN PDFS_PKT_ENTRY pPktEntry,
|
|
IN GUID *pidPrincipal,
|
|
IN PUNICODE_STRING pustrPrincipal,
|
|
OUT PDFS_SERVICE *ppService,
|
|
OUT PREPL_SELECT_CONTEXT pSelectContext,
|
|
OUT BOOLEAN *pLastEntry
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
PDFS_SERVICE psvcFirst = NULL;
|
|
ULONG iSvc;
|
|
|
|
ASSERT(pPktEntry != NULL);
|
|
|
|
DfsDbgTrace(+1, Dbg, "ReplFindFirstProvider Entered.\n", 0);
|
|
|
|
*pLastEntry = FALSE;
|
|
|
|
//
|
|
// See if the user wants a service with a specific machine id
|
|
//
|
|
|
|
ASSERT( pidPrincipal == NULL );
|
|
|
|
//
|
|
// See if the user wants us to pick a particular server
|
|
//
|
|
|
|
ASSERT( pustrPrincipal == NULL );
|
|
|
|
// Initialize the SelectContext
|
|
|
|
if ((pSelectContext->Flags & REPL_UNINITIALIZED) == 0) {
|
|
pSelectContext->Flags = REPL_UNINITIALIZED;
|
|
pSelectContext->iFirstSvcIndex = 0;
|
|
}
|
|
|
|
//
|
|
// Check to see if Entry has a local service that will do.
|
|
//
|
|
|
|
if (pPktEntry->LocalService != NULL) {
|
|
|
|
ASSERT(pPktEntry->LocalService->pProvider != NULL);
|
|
|
|
DfsDbgTrace(0, Dbg, "Selecting Local Svc\n", 0);
|
|
|
|
psvcFirst = pPktEntry->LocalService;
|
|
|
|
pSelectContext->Flags = REPL_SVC_IS_LOCAL;
|
|
|
|
//
|
|
// pSelectContext->iSvcIndex and iFirstSvcIndex are meaningless
|
|
// because of REPL_SVC_IS_LOCAL flag above. Leave them at unknown
|
|
// values.
|
|
//
|
|
|
|
}
|
|
|
|
if (psvcFirst == NULL) {
|
|
// No local service, or local service not sufficient, lets find a
|
|
// remote service.
|
|
Status = ReplFindRemoteService(
|
|
pPktEntry,
|
|
pSelectContext,
|
|
&iSvc);
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
pSelectContext->Flags = REPL_SVC_IS_REMOTE;
|
|
pSelectContext->iFirstSvcIndex = pSelectContext->iSvcIndex = iSvc;
|
|
psvcFirst = &pPktEntry->Info.ServiceList[iSvc];
|
|
}
|
|
}
|
|
|
|
if (psvcFirst != NULL) {
|
|
|
|
DfsDbgTrace(-1, Dbg, "ReplFindFirstProvider: Found service %8lx\n",
|
|
psvcFirst);
|
|
ASSERT(psvcFirst->pProvider != NULL);
|
|
*ppService = psvcFirst;
|
|
|
|
Status = ReplFindRemoteService(
|
|
pPktEntry,
|
|
pSelectContext,
|
|
&iSvc);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
*pLastEntry = TRUE;
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
|
|
//
|
|
// An appropriate provider or referral was not found.
|
|
//
|
|
|
|
DfsDbgTrace(-1, Dbg,
|
|
"ReplFindFirstProvider: no service or provider found, "
|
|
"pPktEntry = %x\n", pPktEntry);
|
|
*ppService = NULL;
|
|
|
|
RtlZeroMemory(pSelectContext, sizeof(REPL_SELECT_CONTEXT));
|
|
|
|
pSelectContext->Flags = REPL_NO_MORE_ENTRIES;
|
|
Status = STATUS_NO_MORE_ENTRIES;
|
|
MUP_TRACE_HIGH(ERROR, ReplFindFirstProvider_Error_NotFound,
|
|
LOGSTATUS(Status));
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: ReplFindNextProvider
|
|
//
|
|
// Synopsis: Supports the abstraction that a PktEntry's service list is an
|
|
// ORDERED list. Based on the SELECT_TOKEN (which the caller is
|
|
// required to have initialized by a call to ReplFindFirstProvider)
|
|
// this call returns the next provider in the ordered sequence.
|
|
//
|
|
// Arguments: [pPktEntry] - Contains the service list to operate on
|
|
// [ppService] - The next service.
|
|
// [pSelectContext] - The context
|
|
// [pLastEntry] - TRUE if last entry, FALSE otherwise
|
|
//
|
|
// Notes: Caller is required to have called ReplFindFirstProvider() before
|
|
// calling this.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- *ppService is the lucky winner.
|
|
//
|
|
// [STATUS_NO_MORE_ENTRIES] -- End of ordered sequence.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
ReplFindNextProvider(
|
|
IN PDFS_PKT_ENTRY pPktEntry,
|
|
OUT PDFS_SERVICE *ppService,
|
|
IN OUT PREPL_SELECT_CONTEXT pSelectContext,
|
|
OUT BOOLEAN *pLastEntry
|
|
) {
|
|
|
|
NTSTATUS Status;
|
|
PDFS_SERVICE psvcNext = NULL; // The one we will return
|
|
ULONG iSvc; // index into ServiceList
|
|
|
|
DfsDbgTrace( 0, Dbg, "ReplFindNextProvider Entered.\n", 0);
|
|
|
|
*pLastEntry = FALSE;
|
|
|
|
//
|
|
// First, check to see if we have previously determined that the list
|
|
// is exhausted.
|
|
//
|
|
|
|
if (pSelectContext->Flags & REPL_NO_MORE_ENTRIES ||
|
|
pSelectContext->Flags & REPL_PRINCIPAL_SPECD) {
|
|
|
|
if (pSelectContext->Flags & REPL_PRINCIPAL_SPECD) {
|
|
DfsDbgTrace(0, Dbg,
|
|
"ReplFindNextProvider called for open with principal", 0);
|
|
}
|
|
|
|
*pLastEntry = TRUE;
|
|
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
//
|
|
// This routine will never return the local service; if the local service
|
|
// were an appropriate choice, it would be returned by ReplFindFirstProvider
|
|
// So here, we simply find the next appropriate remote service, and adjust
|
|
// pSelectContext accordingly.
|
|
//
|
|
|
|
Status = ReplFindRemoteService(
|
|
pPktEntry,
|
|
pSelectContext,
|
|
&iSvc);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DfsDbgTrace( 0, Dbg, "ReplFindNextProvider: No more services.\n", 0);
|
|
|
|
pSelectContext->Flags = REPL_NO_MORE_ENTRIES;
|
|
*ppService = NULL;
|
|
*pLastEntry = TRUE;
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
// Service and provider found. Update pSelectContext and return.
|
|
|
|
ASSERT(iSvc <= pPktEntry->Info.ServiceCount);
|
|
psvcNext = &pPktEntry->Info.ServiceList[iSvc];
|
|
DfsDbgTrace( 0, Dbg, "ReplFindNextProvider: Found svc %8lx\n", psvcNext);
|
|
|
|
if (pSelectContext->Flags & REPL_SVC_IS_LOCAL) {
|
|
pSelectContext->iFirstSvcIndex = iSvc;
|
|
}
|
|
|
|
pSelectContext->Flags = REPL_SVC_IS_REMOTE;
|
|
pSelectContext->iSvcIndex = iSvc; // Record Svc for next time
|
|
|
|
ASSERT(psvcNext->pProvider != NULL);
|
|
*ppService = psvcNext;
|
|
|
|
Status = ReplFindRemoteService(
|
|
pPktEntry,
|
|
pSelectContext,
|
|
&iSvc);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
*pLastEntry = TRUE;
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: ReplLookupProvider, local
|
|
// (formerly DnrLookupProvider)
|
|
//
|
|
// Synopsis: This routine looks up a provider given a provider ID.
|
|
//
|
|
// Arguments: [ProviderID] -- The ID of the provider to be looked up
|
|
//
|
|
// Returns: [PPROVIDER_DEF] -- the provider found, or NULL
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
PPROVIDER_DEF
|
|
ReplLookupProvider(
|
|
ULONG ProviderId
|
|
) {
|
|
NTSTATUS Status;
|
|
PPROVIDER_DEF pProv;
|
|
HANDLE hProvider = NULL;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
OBJECT_HANDLE_INFORMATION handleInformation;
|
|
PFILE_OBJECT fileObject;
|
|
int i;
|
|
|
|
DfsDbgTrace(+1, Dbg, "ReplLookupProvider Entered: id = %x\n", ULongToPtr(ProviderId) );
|
|
|
|
for (pProv = DfsData.pProvider, i=0; i<DfsData.cProvider; pProv++, i++) {
|
|
|
|
if (ProviderId == pProv->eProviderId) {
|
|
|
|
if (pProv->FileObject == NULL) {
|
|
|
|
DfsDbgTrace(0, Dbg, "Provider device not been referenced yet\n", 0);
|
|
|
|
//
|
|
// We haven't opened a handle to the provider yet - so
|
|
// lets try to.
|
|
//
|
|
|
|
if (pProv->DeviceName.Buffer) {
|
|
|
|
//
|
|
// Get a handle to the provider.
|
|
//
|
|
|
|
DfsDbgTrace(0, Dbg, "About to open %wZ\n", &pProv->DeviceName);
|
|
|
|
InitializeObjectAttributes(
|
|
&objectAttributes,
|
|
&pProv->DeviceName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, // Attributes
|
|
0, // Root Directory
|
|
NULL // Security
|
|
);
|
|
|
|
Status = ZwOpenFile(
|
|
&hProvider,
|
|
FILE_TRAVERSE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_DIRECTORY_FILE
|
|
);
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
Status = ioStatusBlock.Status;
|
|
}
|
|
|
|
DfsDbgTrace(0, Dbg, "Open returned %08lx\n", ULongToPtr(Status) );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// Increment ref count on objects
|
|
//
|
|
|
|
//
|
|
// 426184, need to check return code for errors.
|
|
//
|
|
Status = ObReferenceObjectByHandle(
|
|
hProvider,
|
|
0,
|
|
NULL,
|
|
KernelMode,
|
|
(PVOID *)&fileObject,
|
|
&handleInformation );
|
|
|
|
ZwClose(hProvider);
|
|
}
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
|
|
//
|
|
// We have to do this because the pProv structure is in paged
|
|
// pool, and ObReferenceObjectByHandle requires the fileObject
|
|
// argument in NonPaged memory. So, we pass in a stack variable
|
|
// to ObReferenceObjectByHandle, then copy it to pProv->FileObject
|
|
//
|
|
|
|
pProv->FileObject = fileObject;
|
|
|
|
ASSERT( NT_SUCCESS( Status ) );
|
|
|
|
pProv->DeviceObject = IoGetRelatedDeviceObject(
|
|
pProv->FileObject
|
|
);
|
|
Status = ObReferenceObjectByPointer(
|
|
pProv->DeviceObject,
|
|
0,
|
|
NULL,
|
|
KernelMode
|
|
);
|
|
|
|
|
|
ASSERT( pProv->DeviceObject->StackSize < 6 ); // see dsinit.c
|
|
|
|
DfsDbgTrace(-1, Dbg, "ReplLookupProvider Exited: "
|
|
"Provider Def @ %08lx\n", pProv);
|
|
return pProv;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DfsDbgTrace(-1, Dbg, "ReplLookupProvider Exited: "
|
|
"Provider Def @ %08lx\n", pProv);
|
|
return pProv;
|
|
|
|
} // If pProv->FileObject == NULL
|
|
|
|
} // If ProviderId == pProv->eProviderId
|
|
|
|
} // For all provider defs
|
|
|
|
DfsDbgTrace(-1, Dbg, "ReplLookupProvider Exited: Failed!", 0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Function: ReplFindRemoteService
|
|
//
|
|
// Synopsis: This routine is a worker used by both ReplFindFirstProvider
|
|
// and ReplFindNextProvider to find a !remote! service. It
|
|
// completely ignores the local service, if any.
|
|
//
|
|
// For now, it will simply scan the list sequentially. Later,
|
|
// this routine can be modified to call a separate
|
|
// component that will compute the transport cost, given the set
|
|
// of network addresses in the service list.
|
|
//
|
|
// Arguments: [pPktEntry] -- The entry for for which a remote provider is
|
|
// to be selected.
|
|
//
|
|
// [pSelectContext] -- The status of replica selection so far.
|
|
//
|
|
// [piSvc] -- On successful return, the index in the service list
|
|
// of the selected service.
|
|
//
|
|
// Returns: [STATUS_SUCCESS] -- ServiceList[*piSvc] is the lucky winner.
|
|
//
|
|
// [STATUS_NO_MORE_ENTRIES] -- Either service list is empty, or
|
|
// none of the services in the service list will do.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NTSTATUS ReplFindRemoteService(
|
|
IN PDFS_PKT_ENTRY pPktEntry,
|
|
IN PREPL_SELECT_CONTEXT pSelectContext,
|
|
OUT ULONG *piSvc)
|
|
{
|
|
ULONG iSvc;
|
|
BOOLEAN bFound = FALSE;
|
|
|
|
DfsDbgTrace(+1, Dbg, "ReplFindRemoteService: Entered\n", 0);
|
|
|
|
if ( pPktEntry->Info.ServiceCount == 0 ) {
|
|
DfsDbgTrace(0, Dbg, "ReplFindRemoteService: No svcs in pkt entry\n", 0);
|
|
DfsDbgTrace(-1, Dbg, "ReplFindRemoteService: returning %08lx\n",
|
|
ULongToPtr(STATUS_NO_MORE_ENTRIES) );
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
|
|
if (pSelectContext->Flags & REPL_SVC_IS_LOCAL ||
|
|
pSelectContext->Flags & REPL_UNINITIALIZED) {
|
|
|
|
//
|
|
// We haven't looked at a remote service yet. Start from the active
|
|
// service or the first service in the svc list.
|
|
//
|
|
|
|
PDFS_SERVICE pSvc;
|
|
|
|
if (pPktEntry->ActiveService) {
|
|
DfsDbgTrace(0, Dbg, "Starting search at active svc\n", 0);
|
|
pSvc = pPktEntry->ActiveService;
|
|
} else {
|
|
|
|
DfsDbgTrace(0, Dbg, "Starting search at first svc\n", 0);
|
|
pSvc = &pPktEntry->Info.ServiceList[ 0 ];
|
|
}
|
|
|
|
iSvc = (ULONG)(pSvc - &pPktEntry->Info.ServiceList[0]);
|
|
|
|
if (pSvc->pProvider == NULL) {
|
|
pSvc->pProvider = ReplLookupProvider(pSvc->ProviderId);
|
|
}
|
|
|
|
if ( pSvc->pProvider != NULL ) {
|
|
bFound = TRUE;
|
|
} else {
|
|
iSvc = (iSvc + 1) % pPktEntry->Info.ServiceCount;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// We have already looked at some remote services, lets continue where
|
|
// we left off.
|
|
//
|
|
|
|
ASSERT(pPktEntry->Info.ServiceCount != 0);
|
|
iSvc = (pSelectContext->iSvcIndex + 1) % pPktEntry->Info.ServiceCount;
|
|
DfsDbgTrace(0, Dbg, "Continuing search at svc # %d\n", ULongToPtr(iSvc) );
|
|
|
|
}
|
|
|
|
//
|
|
// We know where to begin looking and where to stop.
|
|
//
|
|
|
|
while ( (iSvc != pSelectContext->iFirstSvcIndex) && !bFound) {
|
|
|
|
register PDFS_SERVICE pSvc = &pPktEntry->Info.ServiceList[iSvc];
|
|
|
|
if (pSvc->pProvider == NULL) {
|
|
pSvc->pProvider = ReplLookupProvider(pSvc->ProviderId);
|
|
}
|
|
|
|
if ( pSvc->pProvider != NULL ) {
|
|
DfsDbgTrace(0, Dbg, "Found svc # %d\n", ULongToPtr(iSvc) );
|
|
bFound = TRUE;
|
|
} else {
|
|
DfsDbgTrace(0, Dbg, "No provider for svc # %d\n", ULongToPtr(iSvc) );
|
|
iSvc = (iSvc + 1) % pPktEntry->Info.ServiceCount;
|
|
}
|
|
|
|
}
|
|
|
|
if (bFound) {
|
|
*piSvc = iSvc;
|
|
DfsDbgTrace(-1, Dbg, "ReplFindRemoteService: returning svc %08lx\n",
|
|
&pPktEntry->Info.ServiceList[iSvc]);
|
|
return(STATUS_SUCCESS);
|
|
} else {
|
|
DfsDbgTrace(-1, Dbg, "ReplFindRemoteService: Exited-> %08lx\n",
|
|
ULongToPtr(STATUS_NO_MORE_ENTRIES) );
|
|
return(STATUS_NO_MORE_ENTRIES);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|