571 lines
18 KiB
C
571 lines
18 KiB
C
//+----------------------------------------------------------------------------
|
||
//
|
||
// 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, // 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);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|