|
|
//+----------------------------------------------------------------------------
//
// 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); }
}
|