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.
1746 lines
57 KiB
1746 lines
57 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
RxConnct.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the nt version of the high level routines dealing with
|
|
connections including both the routines for establishing connections and the
|
|
winnet connection apis.
|
|
|
|
Author:
|
|
|
|
Joe Linn [JoeLinn] 1-mar-95
|
|
|
|
Revision History:
|
|
|
|
Balan Sethu Raman [SethuR] --
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "prefix.h"
|
|
#include "secext.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxExtractServerName)
|
|
#pragma alloc_text(PAGE, RxFindOrCreateConnections)
|
|
#pragma alloc_text(PAGE, RxCreateNetRootCallBack)
|
|
#pragma alloc_text(PAGE, RxConstructSrvCall)
|
|
#pragma alloc_text(PAGE, RxConstructNetRoot)
|
|
#pragma alloc_text(PAGE, RxConstructVirtualNetRoot)
|
|
#pragma alloc_text(PAGE, RxFindOrConstructVirtualNetRoot)
|
|
#endif
|
|
|
|
//
|
|
// The local trace mask for this part of the module
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_CONNECT)
|
|
|
|
|
|
BOOLEAN RxSrvCallConstructionDispatcherActive = FALSE;
|
|
|
|
//
|
|
// Internal helper functions for establishing connections through mini redirectors
|
|
//
|
|
|
|
VOID
|
|
RxCreateNetRootCallBack (
|
|
IN PMRX_CREATENETROOT_CONTEXT Context
|
|
);
|
|
|
|
VOID
|
|
RxCreateSrvCallCallBack (
|
|
IN PMRX_SRVCALL_CALLBACK_CONTEXT Context
|
|
);
|
|
|
|
VOID
|
|
RxExtractServerName (
|
|
IN PUNICODE_STRING FilePathName,
|
|
OUT PUNICODE_STRING SrvCallName,
|
|
OUT PUNICODE_STRING RestOfName OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the input name into the srv call name and the
|
|
rest. any of the output can be null
|
|
|
|
Arguments:
|
|
|
|
FilePathName -- the given file name
|
|
|
|
SrvCallName -- the srv call name
|
|
|
|
RestOfName -- the remaining portion of the name
|
|
|
|
--*/
|
|
{
|
|
ULONG Length = FilePathName->Length;
|
|
PWCH Buffer = FilePathName->Buffer;
|
|
PWCH Limit = (PWCH)Add2Ptr( Buffer, Length );
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( SrvCallName );
|
|
|
|
|
|
for (SrvCallName->Buffer = Buffer;
|
|
(Buffer < Limit) && ((*Buffer != OBJ_NAME_PATH_SEPARATOR) || (Buffer == FilePathName->Buffer));
|
|
Buffer++) {
|
|
}
|
|
|
|
SrvCallName->Length = SrvCallName->MaximumLength = (USHORT)((PCHAR)Buffer - (PCHAR)FilePathName->Buffer);
|
|
|
|
if (ARGUMENT_PRESENT( RestOfName )) {
|
|
|
|
RestOfName->Buffer = Buffer;
|
|
RestOfName->Length = RestOfName->MaximumLength
|
|
= (USHORT)((PCHAR)Limit - (PCHAR)Buffer);
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, (" RxExtractServerName FilePath=%wZ\n", FilePathName) );
|
|
RxDbgTrace( 0, Dbg, (" Srv=%wZ,Rest=%wZ\n", SrvCallName, RestOfName) );
|
|
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxFindOrCreateConnections (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PIRP Irp,
|
|
IN PUNICODE_STRING CanonicalName,
|
|
IN NET_ROOT_TYPE NetRootType,
|
|
IN BOOLEAN TreeConnect,
|
|
OUT PUNICODE_STRING LocalNetRootName,
|
|
OUT PUNICODE_STRING FilePathName,
|
|
IN OUT PLOCK_HOLDING_STATE LockState,
|
|
IN PRX_CONNECTION_ID RxConnectionId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the call down from the MUP to claim a name or from the
|
|
create path. If we don't find the name in the netname table, we pass the name
|
|
down to the minirdrs to be connected. in the few places where it matters, we use
|
|
the majorcode to distinguish between in MUP and create cases. there are a million
|
|
cases depending on what we find on the initial lookup.
|
|
|
|
these are the cases:
|
|
|
|
found nothing (1)
|
|
found intransition srvcall (2)
|
|
found stable/nongood srvcall (3)
|
|
found good srvcall (4&0)
|
|
found good netroot on good srvcall (0)
|
|
found intransition netroot on good srvcall (5)
|
|
found bad netroot on good srvcall (6)
|
|
found good netroot on bad srvcall (3)
|
|
found intransition netroot on bad srvcall (3)
|
|
found bad netroot on bad srvcall (3)
|
|
found good netroot on intransition srvcall (2)
|
|
found intransition netroot on intransition srvcall (2)
|
|
found bad netroot on intransition srvcall (2)
|
|
|
|
(x) means that the code to handle that case has a marker
|
|
like "case (x)". could be a comment....could be a debugout.
|
|
|
|
Arguments:
|
|
|
|
RxContext --
|
|
|
|
CanonicalName --
|
|
|
|
NetRootType --
|
|
|
|
LocalNetRootName --
|
|
|
|
FilePathName --
|
|
|
|
LockHoldingState --
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_UNSUCCESSFUL;
|
|
|
|
UNICODE_STRING UnmatchedName;
|
|
|
|
PVOID Container = NULL;
|
|
PSRV_CALL SrvCall = NULL;
|
|
PNET_ROOT NetRoot = NULL;
|
|
PV_NET_ROOT VNetRoot = NULL;
|
|
|
|
PRX_PREFIX_TABLE NameTable = RxContext->RxDeviceObject->pRxNetNameTable;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(0, Dbg, ("RxFindOrCreateConnections -> %08lx\n", RxContext));
|
|
|
|
//
|
|
// Parse the canonical name into the local net root name and file path name
|
|
//
|
|
|
|
*FilePathName = *CanonicalName;
|
|
LocalNetRootName->Length = 0;
|
|
LocalNetRootName->MaximumLength = 0;
|
|
LocalNetRootName->Buffer = CanonicalName->Buffer;
|
|
|
|
if (FilePathName->Buffer[1] == L';') {
|
|
|
|
PWCHAR FilePath = &FilePathName->Buffer[2];
|
|
BOOLEAN SeparatorFound = FALSE;
|
|
ULONG PathLength = 0;
|
|
|
|
if (FilePathName->Length > sizeof( WCHAR ) * 2) {
|
|
PathLength = FilePathName->Length - sizeof( WCHAR ) * 2;
|
|
}
|
|
|
|
while (PathLength > 0) {
|
|
if (*FilePath == L'\\') {
|
|
SeparatorFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
PathLength -= sizeof( WCHAR );
|
|
FilePath += 1;
|
|
}
|
|
|
|
if (!SeparatorFound) {
|
|
return STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
FilePathName->Buffer = FilePath;
|
|
LocalNetRootName->Length = (USHORT)((PCHAR)FilePath - (PCHAR)CanonicalName->Buffer);
|
|
|
|
LocalNetRootName->MaximumLength = LocalNetRootName->Length;
|
|
FilePathName->Length -= LocalNetRootName->Length;
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxFindOrCreateConnections Path = %wZ\n", FilePathName) );
|
|
|
|
try {
|
|
|
|
UNICODE_STRING SrvCallName;
|
|
UNICODE_STRING NetRootName;
|
|
|
|
RETRY_LOOKUP:
|
|
|
|
ASSERT( *LockState != LHS_LockNotHeld );
|
|
|
|
if (Container != NULL) {
|
|
|
|
//
|
|
// This is the subsequent pass of a lookup after waiting for the transition
|
|
// to the stable state of a previous lookup.
|
|
// Dereference the result of the earlier lookup.
|
|
//
|
|
|
|
switch (NodeType( Container )) {
|
|
|
|
case RDBSS_NTC_V_NETROOT:
|
|
|
|
RxDereferenceVNetRoot( (PV_NET_ROOT)Container, *LockState );
|
|
break;
|
|
|
|
case RDBSS_NTC_SRVCALL:
|
|
|
|
RxDereferenceSrvCall( (PSRV_CALL)Container, *LockState );
|
|
break;
|
|
|
|
case RDBSS_NTC_NETROOT:
|
|
|
|
RxDereferenceNetRoot( (PNET_ROOT)Container, *LockState );
|
|
break;
|
|
|
|
default:
|
|
|
|
DbgPrint( "RxFindOrCreateConnections -- Invalid Container Type\n" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
Container = RxPrefixTableLookupName( NameTable,
|
|
FilePathName,
|
|
&UnmatchedName,
|
|
RxConnectionId );
|
|
RxLog(( "FOrCC1 %x %x %wZ \n", RxContext, Container, FilePathName ));
|
|
RxWmiLog( LOG,
|
|
RxFindOrCreateConnections_1,
|
|
LOGPTR( RxContext )
|
|
LOGPTR( Container )
|
|
LOGUSTR( *FilePathName ) );
|
|
|
|
RETRY_AFTER_LOOKUP:
|
|
|
|
NetRoot = NULL;
|
|
SrvCall = NULL;
|
|
VNetRoot = NULL;
|
|
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
RxContext->Create.pNetRoot = NULL;
|
|
RxContext->Create.pSrvCall = NULL;
|
|
RxContext->Create.Type = NetRootType;
|
|
|
|
if (Container) {
|
|
|
|
if (NodeType( Container ) == RDBSS_NTC_V_NETROOT) {
|
|
|
|
VNetRoot = (PV_NET_ROOT)Container;
|
|
NetRoot = (PNET_ROOT)VNetRoot->NetRoot;
|
|
SrvCall = (PSRV_CALL)NetRoot->SrvCall;
|
|
|
|
if (NetRoot->Condition == Condition_InTransition) {
|
|
|
|
RxReleasePrefixTableLock( NameTable );
|
|
RxWaitForStableNetRoot( NetRoot, RxContext );
|
|
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
|
|
//
|
|
// Since we had to drop the table lock and reacquire it,
|
|
// our NetRoot pointer may be stale. Look it up again before
|
|
// using it.
|
|
//
|
|
// NOTE: The NetRoot is still referenced, so it is safe to
|
|
// look at its condition.
|
|
//
|
|
|
|
if (NetRoot->Condition == Condition_Good) {
|
|
goto RETRY_LOOKUP;
|
|
}
|
|
}
|
|
|
|
if ((NetRoot->Condition == Condition_Good) &&
|
|
(SrvCall->Condition == Condition_Good) &&
|
|
(SrvCall->RxDeviceObject == RxContext->RxDeviceObject) ) {
|
|
|
|
//
|
|
// case (0)...the good case...see comments below
|
|
//
|
|
|
|
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
|
|
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
|
|
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
|
|
|
|
try_return( Status = STATUS_CONNECTION_ACTIVE );
|
|
|
|
} else {
|
|
|
|
if (VNetRoot->ConstructionStatus == STATUS_SUCCESS) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
} else {
|
|
Status = VNetRoot->ConstructionStatus;
|
|
}
|
|
RxDereferenceVNetRoot( VNetRoot, *LockState );
|
|
try_return ( Status );
|
|
}
|
|
} else {
|
|
|
|
ASSERT( NodeType( Container ) == RDBSS_NTC_SRVCALL );
|
|
SrvCall = (PSRV_CALL)Container;
|
|
|
|
//
|
|
// The associated SRV_CALL is in the process of construction.
|
|
// await the result.
|
|
//
|
|
|
|
|
|
if (SrvCall->Condition == Condition_InTransition) {
|
|
|
|
RxDbgTrace( 0, Dbg, (" Case(3)\n", 0) );
|
|
RxReleasePrefixTableLock( NameTable );
|
|
|
|
RxWaitForStableSrvCall( SrvCall, RxContext );
|
|
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
|
|
if (SrvCall->Condition == Condition_Good) {
|
|
goto RETRY_LOOKUP;
|
|
}
|
|
}
|
|
|
|
if (SrvCall->Condition != Condition_Good) {
|
|
|
|
if (SrvCall->Status == STATUS_SUCCESS) {
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
} else {
|
|
Status = SrvCall->Status;
|
|
}
|
|
|
|
//
|
|
// in changing this...remember precious servers.......
|
|
//
|
|
RxDereferenceSrvCall( SrvCall, *LockState );
|
|
try_return( Status );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((SrvCall != NULL) &&
|
|
(SrvCall->Condition == Condition_Good) &&
|
|
(SrvCall->RxDeviceObject != RxContext->RxDeviceObject) ) {
|
|
|
|
RxDereferenceSrvCall( SrvCall, *LockState );
|
|
try_return( Status = STATUS_BAD_NETWORK_NAME );
|
|
}
|
|
|
|
if (*LockState == LHS_SharedLockHeld) {
|
|
|
|
//
|
|
// Upgrade the lock to an exclusive lock
|
|
//
|
|
|
|
if (!RxAcquirePrefixTableLockExclusive( NameTable, FALSE )) {
|
|
|
|
RxReleasePrefixTableLock( NameTable );
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
|
|
goto RETRY_LOOKUP;
|
|
} else {
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
}
|
|
}
|
|
|
|
ASSERT( *LockState == LHS_ExclusiveLockHeld );
|
|
|
|
//
|
|
// A prefix table entry was found. Further construction is required
|
|
// if either a SRV_CALL was found or a SRV_CALL/NET_ROOT/V_NET_ROOT
|
|
// in a bad state was found.
|
|
//
|
|
|
|
if (Container) {
|
|
|
|
RxDbgTrace( 0, Dbg, (" SrvCall=%08lx\n", SrvCall) );
|
|
ASSERT( (NodeType( SrvCall ) == RDBSS_NTC_SRVCALL) &&
|
|
(SrvCall->Condition == Condition_Good) );
|
|
ASSERT( (NetRoot == NULL) && (VNetRoot == NULL) );
|
|
|
|
RxDbgTrace( 0, Dbg, (" Case(4)\n", 0) );
|
|
ASSERT( SrvCall->RxDeviceObject == RxContext->RxDeviceObject );
|
|
|
|
SrvCall->RxDeviceObject->Dispatch->MRxExtractNetRootName( FilePathName,
|
|
(PMRX_SRV_CALL)SrvCall,
|
|
&NetRootName,
|
|
NULL );
|
|
|
|
NetRoot = RxCreateNetRoot( SrvCall,
|
|
&NetRootName,
|
|
0,
|
|
RxConnectionId );
|
|
|
|
if (NetRoot == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return( Status );
|
|
}
|
|
|
|
NetRoot->Type = NetRootType;
|
|
|
|
//
|
|
// Decrement the reference created by lookup. Since the newly created
|
|
// netroot holds onto a reference it is safe to do so.
|
|
//
|
|
|
|
RxDereferenceSrvCall( SrvCall, *LockState );
|
|
|
|
//
|
|
// Also create the associated default virtual net root
|
|
//
|
|
|
|
VNetRoot = RxCreateVNetRoot( RxContext,
|
|
NetRoot,
|
|
CanonicalName,
|
|
LocalNetRootName,
|
|
FilePathName,
|
|
RxConnectionId );
|
|
|
|
if (VNetRoot == NULL) {
|
|
RxFinalizeNetRoot( NetRoot, TRUE, TRUE );
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// Reference the VNetRoot
|
|
//
|
|
|
|
RxReferenceVNetRoot( VNetRoot );
|
|
|
|
NetRoot->Condition = Condition_InTransition;
|
|
|
|
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)SrvCall;
|
|
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)NetRoot;
|
|
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
|
|
|
|
Status = RxConstructNetRoot( RxContext,
|
|
SrvCall,
|
|
NetRoot,
|
|
VNetRoot,
|
|
LockState );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
ASSERT( *LockState == LHS_ExclusiveLockHeld );
|
|
|
|
if (!TreeConnect) {
|
|
|
|
//
|
|
// do not release the lock acquired by the callback routine ....
|
|
//
|
|
|
|
RxExclusivePrefixTableLockToShared( NameTable );
|
|
*LockState = LHS_SharedLockHeld;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Dereference the Virtual net root
|
|
//
|
|
|
|
RxTransitionVNetRoot( VNetRoot, Condition_Bad );
|
|
RxLog(( "FOrCC %x %x Failed %x VNRc %d \n", RxContext, VNetRoot, Status, VNetRoot->Condition ));
|
|
RxWmiLog( LOG,
|
|
RxFindOrCreateConnections_2,
|
|
LOGPTR( RxContext )
|
|
LOGPTR( VNetRoot )
|
|
LOGULONG( Status )
|
|
LOGULONG( VNetRoot->Condition ) );
|
|
|
|
RxDereferenceVNetRoot( VNetRoot, *LockState );
|
|
|
|
RxContext->Create.pNetRoot = NULL;
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
}
|
|
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// No prefix table entry was found. A new SRV_CALL instance needs to be
|
|
// constructed.
|
|
//
|
|
|
|
ASSERT( Container == NULL );
|
|
|
|
RxExtractServerName( FilePathName, &SrvCallName, NULL );
|
|
SrvCall = RxCreateSrvCall( RxContext, &SrvCallName, NULL, RxConnectionId );
|
|
if (SrvCall == NULL) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return( Status );
|
|
}
|
|
|
|
RxReferenceSrvCall( SrvCall );
|
|
|
|
RxContext->Create.Type = NetRootType;
|
|
RxContext->Create.pSrvCall = NULL;
|
|
RxContext->Create.pNetRoot = NULL;
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
|
|
Status = RxConstructSrvCall( RxContext,
|
|
Irp,
|
|
SrvCall,
|
|
LockState );
|
|
|
|
ASSERT( (Status != STATUS_SUCCESS) || RxIsPrefixTableLockAcquired( NameTable ) );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
if (SrvCall != NULL) {
|
|
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
RxDereferenceSrvCall( SrvCall, LHS_ExclusiveLockHeld );
|
|
RxReleasePrefixTableLock( NameTable );
|
|
}
|
|
|
|
try_return( Status );
|
|
|
|
} else {
|
|
|
|
Container = SrvCall;
|
|
goto RETRY_AFTER_LOOKUP;
|
|
|
|
}
|
|
|
|
try_exit: NOTHING;
|
|
|
|
} finally {
|
|
if ((Status != (STATUS_SUCCESS)) &&
|
|
(Status != (STATUS_CONNECTION_ACTIVE))) {
|
|
|
|
if (*LockState != LHS_LockNotHeld) {
|
|
RxReleasePrefixTableLock( NameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT( (Status != STATUS_SUCCESS) || RxIsPrefixTableLockAcquired( NameTable ) );
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
RxCreateNetRootCallBack (
|
|
IN PMRX_CREATENETROOT_CONTEXT Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets called when the minirdr has finished processing on
|
|
a CreateNetRoot calldown. It's exact function depends on whether the context
|
|
describes IRP_MJ_CREATE or an IRP_MJ_IOCTL.
|
|
|
|
Arguments:
|
|
|
|
NetRoot - describes the Net_Root.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxCreateNetRootCallBack Context = %08lx\n", Context) );
|
|
KeSetEvent( &Context->FinishEvent, IO_NETWORK_INCREMENT, FALSE );
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RxFinishSrvCallConstruction (
|
|
IN OUT PMRX_SRVCALLDOWN_STRUCTURE CalldownStructure
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the construction of the srv call instance in an
|
|
asynchronous manner
|
|
|
|
Arguments:
|
|
|
|
SCCBC -- Call back structure
|
|
|
|
--*/
|
|
{
|
|
PRX_CONTEXT RxContext;
|
|
RX_BLOCK_CONDITION SrvCallCondition;
|
|
NTSTATUS Status;
|
|
PSRV_CALL SrvCall;
|
|
PRX_PREFIX_TABLE NameTable;
|
|
|
|
RxContext = CalldownStructure->RxContext;
|
|
NameTable = RxContext->RxDeviceObject->pRxNetNameTable;
|
|
SrvCall = (PSRV_CALL)CalldownStructure->SrvCall;
|
|
|
|
if (CalldownStructure->BestFinisher == NULL) {
|
|
|
|
SrvCallCondition = Condition_Bad;
|
|
Status = CalldownStructure->CallbackContexts[0].Status;
|
|
|
|
} else {
|
|
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT CallbackContext;
|
|
|
|
//
|
|
// Notify the Winner
|
|
//
|
|
|
|
CallbackContext = &(CalldownStructure->CallbackContexts[CalldownStructure->BestFinisherOrdinal]);
|
|
|
|
RxLog(( "WINNER %x %wZ\n", CallbackContext, &CalldownStructure->BestFinisher->DeviceName) );
|
|
RxWmiLog( LOG,
|
|
RxFinishSrvCallConstruction,
|
|
LOGPTR( CallbackContext )
|
|
LOGUSTR( CalldownStructure->BestFinisher->DeviceName ) );
|
|
ASSERT( SrvCall->RxDeviceObject == CalldownStructure->BestFinisher );
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
CalldownStructure->BestFinisher->Dispatch,
|
|
MRxSrvCallWinnerNotify,
|
|
((PMRX_SRV_CALL)SrvCall,
|
|
TRUE,
|
|
CallbackContext->RecommunicateContext) );
|
|
|
|
if (STATUS_SUCCESS != Status) {
|
|
SrvCallCondition = Condition_Bad;
|
|
} else {
|
|
SrvCallCondition = Condition_Good;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transition the SrvCall instance ...
|
|
//
|
|
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
|
|
RxTransitionSrvCall( SrvCall, SrvCallCondition );
|
|
|
|
RxFreePool( CalldownStructure );
|
|
|
|
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) {
|
|
|
|
RxReleasePrefixTableLock( NameTable );
|
|
|
|
//
|
|
// Resume the request that triggered the construction of the SrvCall ...
|
|
//
|
|
|
|
if (FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED )) {
|
|
Status = STATUS_CANCELLED;
|
|
}
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_CREATE) {
|
|
RxpPrepareCreateContextForReuse( RxContext );
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = RxContext->ResumeRoutine( RxContext, RxContext->CurrentIrp );
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
RxCompleteRequest( RxContext, Status );
|
|
}
|
|
} else {
|
|
|
|
PIRP Irp = RxContext->CurrentIrp;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
RxContext->MajorFunction = IrpSp->MajorFunction;
|
|
|
|
if (RxContext->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
|
|
|
if (RxContext->PrefixClaim.SuppliedPathName.Buffer != NULL) {
|
|
|
|
RxFreePool( RxContext->PrefixClaim.SuppliedPathName.Buffer );
|
|
RxContext->PrefixClaim.SuppliedPathName.Buffer = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
RxCompleteRequest( RxContext, Status );
|
|
}
|
|
}
|
|
|
|
RxDereferenceSrvCall( SrvCall, LHS_LockNotHeld );
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
RxFinishSrvCallConstructionDispatcher (
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine provides us with a throttling mechanism for controlling
|
|
the number of threads that can be consumed by srv call construction in the
|
|
thread pool. Currently this limit is set at 1.
|
|
gets called when a minirdr has finished processing on
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
BOOLEAN RemainingRequestsForProcessing;
|
|
BOOLEAN ResumeRequestsOnDispatchError;
|
|
|
|
ResumeRequestsOnDispatchError = (Context == NULL);
|
|
|
|
for (;;) {
|
|
PLIST_ENTRY Entry;
|
|
PMRX_SRVCALLDOWN_STRUCTURE CalldownStructure;
|
|
|
|
KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql );
|
|
|
|
Entry = RemoveHeadList( &RxSrvCalldownList );
|
|
|
|
if (Entry != &RxSrvCalldownList) {
|
|
RemainingRequestsForProcessing = TRUE;
|
|
} else {
|
|
RemainingRequestsForProcessing = FALSE;
|
|
RxSrvCallConstructionDispatcherActive = FALSE;
|
|
}
|
|
|
|
KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql );
|
|
|
|
if (!RemainingRequestsForProcessing) {
|
|
break;
|
|
}
|
|
|
|
CalldownStructure = (PMRX_SRVCALLDOWN_STRUCTURE) CONTAINING_RECORD( Entry, MRX_SRVCALLDOWN_STRUCTURE, SrvCalldownList );
|
|
|
|
if (ResumeRequestsOnDispatchError) {
|
|
CalldownStructure->BestFinisher = NULL;
|
|
}
|
|
|
|
RxFinishSrvCallConstruction( CalldownStructure );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
RxCreateSrvCallCallBack (
|
|
IN PMRX_SRVCALL_CALLBACK_CONTEXT CallbackContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets called when a minirdr has finished processing on
|
|
a CreateSrvCall calldown. The minirdr will have set the status in the passed
|
|
context to indicate success or failure. what we have to do is
|
|
1) decrease the number of outstanding requests and set the event
|
|
if this is the last one.
|
|
2) determine whether this guy is the winner of the call.
|
|
|
|
the minirdr must get the strucsupspinlock in order to call this routine; this routine
|
|
must NOT be called if the minirdr's call was successfully canceled.
|
|
|
|
|
|
Arguments:
|
|
|
|
CallbackContext -- Call back structure
|
|
|
|
--*/
|
|
{
|
|
KIRQL SavedIrql;
|
|
PMRX_SRVCALLDOWN_STRUCTURE CalldownStructure = (PMRX_SRVCALLDOWN_STRUCTURE)(CallbackContext->SrvCalldownStructure);
|
|
PSRV_CALL SrvCall = (PSRV_CALL)CalldownStructure->SrvCall;
|
|
|
|
ULONG MiniRedirectorsRemaining;
|
|
BOOLEAN Cancelled;
|
|
|
|
KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql );
|
|
|
|
RxDbgTrace(0, Dbg, (" RxCreateSrvCallCallBack SrvCall = %08lx\n", SrvCall) );
|
|
|
|
if (CallbackContext->Status == STATUS_SUCCESS) {
|
|
CalldownStructure->BestFinisher = CallbackContext->RxDeviceObject;
|
|
CalldownStructure->BestFinisherOrdinal = CallbackContext->CallbackContextOrdinal;
|
|
}
|
|
|
|
CalldownStructure->NumberRemaining -= 1;
|
|
MiniRedirectorsRemaining = CalldownStructure->NumberRemaining;
|
|
SrvCall->Status = CallbackContext->Status;
|
|
|
|
KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql );
|
|
|
|
if (MiniRedirectorsRemaining == 0) {
|
|
|
|
if (!FlagOn( CalldownStructure->RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) {
|
|
|
|
KeSetEvent( &CalldownStructure->FinishEvent, IO_NETWORK_INCREMENT, FALSE );
|
|
|
|
} else if (FlagOn( CalldownStructure->RxContext->Flags, RX_CONTEXT_FLAG_CREATE_MAILSLOT )) {
|
|
|
|
RxFinishSrvCallConstruction( CalldownStructure );
|
|
|
|
} else {
|
|
|
|
KIRQL SavedIrql;
|
|
BOOLEAN DispatchRequest;
|
|
|
|
KeAcquireSpinLock( &RxStrucSupSpinLock, &SavedIrql );
|
|
|
|
InsertTailList( &RxSrvCalldownList, &CalldownStructure->SrvCalldownList );
|
|
|
|
DispatchRequest = !RxSrvCallConstructionDispatcherActive;
|
|
|
|
if (!RxSrvCallConstructionDispatcherActive) {
|
|
RxSrvCallConstructionDispatcherActive = TRUE;
|
|
}
|
|
|
|
KeReleaseSpinLock( &RxStrucSupSpinLock, SavedIrql );
|
|
|
|
if (DispatchRequest) {
|
|
NTSTATUS DispatchStatus;
|
|
|
|
DispatchStatus = RxDispatchToWorkerThread( RxFileSystemDeviceObject,
|
|
CriticalWorkQueue,
|
|
RxFinishSrvCallConstructionDispatcher,
|
|
&RxSrvCalldownList );
|
|
|
|
if (DispatchStatus != STATUS_SUCCESS) {
|
|
RxFinishSrvCallConstructionDispatcher( NULL );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RxConstructSrvCall (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PIRP Irp,
|
|
IN PSRV_CALL SrvCall,
|
|
OUT PLOCK_HOLDING_STATE LockState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs a srv call by invoking the registered mini redirectors
|
|
|
|
Arguments:
|
|
|
|
SrvCall -- the server call whose construction is to be completed
|
|
|
|
LockState -- the prefix table lock holding status
|
|
|
|
Return Value:
|
|
|
|
the appropriate status value
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMRX_SRVCALLDOWN_STRUCTURE CalldownCtx;
|
|
BOOLEAN Wait;
|
|
|
|
PMRX_SRVCALL_CALLBACK_CONTEXT CallbackCtx;
|
|
PRDBSS_DEVICE_OBJECT RxDeviceObject = RxContext->RxDeviceObject;
|
|
PRX_PREFIX_TABLE NameTable = RxDeviceObject->pRxNetNameTable;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( *LockState == LHS_ExclusiveLockHeld );
|
|
|
|
if (!FlagOn( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION )) {
|
|
Wait = TRUE;
|
|
} else {
|
|
Wait = FALSE;
|
|
}
|
|
|
|
CalldownCtx = RxAllocatePoolWithTag( NonPagedPool,
|
|
sizeof( MRX_SRVCALLDOWN_STRUCTURE ) + (sizeof(MRX_SRVCALL_CALLBACK_CONTEXT) * 1), // one minirdr in this call
|
|
'CSxR' );
|
|
|
|
if (CalldownCtx == NULL) {
|
|
|
|
SrvCall->Condition = Condition_Bad;
|
|
SrvCall->Context = NULL;
|
|
RxReleasePrefixTableLock( NameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory( CalldownCtx, sizeof( MRX_SRVCALLDOWN_STRUCTURE ) + sizeof( MRX_SRVCALL_CALLBACK_CONTEXT ) * 1 );
|
|
|
|
SrvCall->Condition = Condition_InTransition;
|
|
SrvCall->Context = NULL;
|
|
|
|
//
|
|
// Drop the prefix table lock before calling the mini redirectors.
|
|
//
|
|
|
|
RxReleasePrefixTableLock( NameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
|
|
//
|
|
// use the first and only context
|
|
//
|
|
|
|
CallbackCtx = &(CalldownCtx->CallbackContexts[0]);
|
|
RxLog(( "Calldwn %lx %wZ", CallbackCtx, &RxDeviceObject->DeviceName ));
|
|
RxWmiLog( LOG,
|
|
RxConstructSrvCall,
|
|
LOGPTR( CallbackCtx )
|
|
LOGUSTR( RxDeviceObject->DeviceName ) );
|
|
|
|
CallbackCtx->SrvCalldownStructure = CalldownCtx;
|
|
CallbackCtx->CallbackContextOrdinal = 0;
|
|
CallbackCtx->RxDeviceObject = RxDeviceObject;
|
|
|
|
//
|
|
// This reference is taken away by the RxFinishSrvCallConstruction routine.
|
|
// This reference enables us to deal with synchronous/asynchronous processing
|
|
// of srv call construction requests in an identical manner.
|
|
//
|
|
|
|
RxReferenceSrvCall( SrvCall );
|
|
|
|
if (!Wait) {
|
|
RxPrePostIrp( RxContext, Irp );
|
|
} else {
|
|
KeInitializeEvent( &CalldownCtx->FinishEvent, SynchronizationEvent, FALSE );
|
|
}
|
|
|
|
CalldownCtx->NumberToWait = 1;
|
|
CalldownCtx->NumberRemaining = CalldownCtx->NumberToWait;
|
|
CalldownCtx->SrvCall = (PMRX_SRV_CALL)SrvCall;
|
|
CalldownCtx->CallBack = RxCreateSrvCallCallBack;
|
|
CalldownCtx->BestFinisher = NULL;
|
|
CalldownCtx->RxContext = RxContext;
|
|
CallbackCtx->Status = STATUS_BAD_NETWORK_PATH;
|
|
|
|
InitializeListHead( &CalldownCtx->SrvCalldownList );
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
RxDeviceObject->Dispatch,
|
|
MRxCreateSrvCall,
|
|
((PMRX_SRV_CALL)SrvCall, CallbackCtx) );
|
|
ASSERT( Status == STATUS_PENDING );
|
|
|
|
if (Wait) {
|
|
|
|
KeWaitForSingleObject( &CalldownCtx->FinishEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL );
|
|
|
|
Status = RxFinishSrvCallConstruction( CalldownCtx );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxReleasePrefixTableLock( NameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
} else {
|
|
ASSERT( RxIsPrefixTableLockAcquired( NameTable ) );
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
}
|
|
} else {
|
|
Status = STATUS_PENDING;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxConstructNetRoot (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PSRV_CALL SrvCall,
|
|
IN PNET_ROOT NetRoot,
|
|
IN PV_NET_ROOT VNetRoot,
|
|
OUT PLOCK_HOLDING_STATE LockState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs a net root by invoking the registered mini redirectors
|
|
|
|
Arguments:
|
|
|
|
RxContext -- the RDBSS context
|
|
|
|
SrvCall -- the server call associated with the net root
|
|
|
|
NetRoot -- the net root instance to be constructed
|
|
|
|
pVirtualNetRoot -- the virtual net root instance to be constructed
|
|
|
|
LockState -- the prefix table lock holding status
|
|
|
|
Return Value:
|
|
|
|
the appropriate status value
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PMRX_CREATENETROOT_CONTEXT Context;
|
|
|
|
RX_BLOCK_CONDITION NetRootCondition = Condition_Bad;
|
|
RX_BLOCK_CONDITION VNetRootCondition = Condition_Bad;
|
|
|
|
PRX_PREFIX_TABLE NameTable = RxContext->RxDeviceObject->pRxNetNameTable;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( *LockState == LHS_ExclusiveLockHeld );
|
|
|
|
Context = (PMRX_CREATENETROOT_CONTEXT) RxAllocatePoolWithTag( NonPagedPool,
|
|
sizeof( MRX_CREATENETROOT_CONTEXT ),
|
|
'CSxR' );
|
|
if (Context == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RxReleasePrefixTableLock( NameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
|
|
RtlZeroMemory( Context, sizeof( MRX_CREATENETROOT_CONTEXT ) );
|
|
|
|
KeInitializeEvent( &Context->FinishEvent, SynchronizationEvent, FALSE );
|
|
|
|
Context->Callback = RxCreateNetRootCallBack;
|
|
Context->RxContext = RxContext;
|
|
Context->pVNetRoot = VNetRoot;
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
SrvCall->RxDeviceObject->Dispatch,
|
|
MRxCreateVNetRoot,
|
|
(Context) );
|
|
|
|
ASSERT( Status == STATUS_PENDING );
|
|
|
|
KeWaitForSingleObject( &Context->FinishEvent, Executive, KernelMode, FALSE, NULL );
|
|
|
|
if ((Context->NetRootStatus == STATUS_SUCCESS) &&
|
|
(Context->VirtualNetRootStatus == STATUS_SUCCESS)) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("Return to open, good netroot...%wZ\n", &NetRoot->PrefixEntry.Prefix) );
|
|
|
|
NetRootCondition = Condition_Good;
|
|
VNetRootCondition = Condition_Good;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
if (Context->NetRootStatus == STATUS_SUCCESS) {
|
|
NetRootCondition = Condition_Good;
|
|
Status = Context->VirtualNetRootStatus;
|
|
} else {
|
|
Status = Context->NetRootStatus;
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("Return to open, bad netroot...%wZ\n", &NetRoot->PrefixEntry.Prefix) );
|
|
}
|
|
|
|
RxAcquirePrefixTableLockExclusive( NameTable, TRUE );
|
|
|
|
RxTransitionNetRoot( NetRoot, NetRootCondition );
|
|
RxTransitionVNetRoot( VNetRoot, VNetRootCondition );
|
|
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
|
|
RxFreePool( Context );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RxConstructVirtualNetRoot (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PIRP Irp,
|
|
IN PUNICODE_STRING CanonicalName,
|
|
IN NET_ROOT_TYPE NetRootType,
|
|
IN BOOLEAN TreeConnect,
|
|
OUT PV_NET_ROOT *VNetRoot,
|
|
OUT PLOCK_HOLDING_STATE LockState,
|
|
OUT PRX_CONNECTION_ID RxConnectionId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine constructs a VNetRoot (View of a net root) by invoking the registered mini
|
|
redirectors
|
|
|
|
Arguments:
|
|
|
|
RxContext -- the RDBSS context
|
|
|
|
CanonicalName -- the canonical name associated with the VNetRoot
|
|
|
|
NetRootType -- the type of the virtual net root
|
|
|
|
VNetRoot -- placeholder for the virtual net root instance to be constructed
|
|
|
|
LockState -- the prefix table lock holding status
|
|
|
|
RxConnectionId -- The ID used for multiplex control
|
|
|
|
Return Value:
|
|
|
|
the appropriate status value
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RX_BLOCK_CONDITION Condition = Condition_Bad;
|
|
|
|
UNICODE_STRING FilePath;
|
|
UNICODE_STRING LocalNetRootName;
|
|
|
|
PV_NET_ROOT ThisVNetRoot = NULL;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxConstructVirtualNetRoot -- Entry\n") );
|
|
|
|
ASSERT( *LockState != LHS_LockNotHeld );
|
|
|
|
Status = RxFindOrCreateConnections( RxContext,
|
|
Irp,
|
|
CanonicalName,
|
|
NetRootType,
|
|
TreeConnect,
|
|
&LocalNetRootName,
|
|
&FilePath,
|
|
LockState,
|
|
RxConnectionId );
|
|
|
|
if (Status == STATUS_CONNECTION_ACTIVE) {
|
|
|
|
PV_NET_ROOT ActiveVNetRoot = (PV_NET_ROOT)(RxContext->Create.pVNetRoot);
|
|
PNET_ROOT NetRoot = (PNET_ROOT)ActiveVNetRoot->NetRoot;
|
|
|
|
RxDbgTrace( 0, Dbg, (" RxConstructVirtualNetRoot -- Creating new VNetRoot\n") );
|
|
RxDbgTrace( 0, Dbg, ("RxCreateTreeConnect netroot=%wZ\n", &NetRoot->PrefixEntry.Prefix) );
|
|
|
|
//
|
|
// The NetRoot has been previously constructed. A subsequent VNetRoot
|
|
// construction is required since the existing VNetRoot's do not satisfy
|
|
// the given criterion ( currently smae Logon Id's).
|
|
//
|
|
|
|
ThisVNetRoot = RxCreateVNetRoot( RxContext,
|
|
NetRoot,
|
|
CanonicalName,
|
|
&LocalNetRootName,
|
|
&FilePath,
|
|
RxConnectionId );
|
|
|
|
//
|
|
// The skeleton VNetRoot has been constructed. ( As part of this construction
|
|
// the underlying NetRoot and SrvCall has been referenced).
|
|
//
|
|
|
|
if (ThisVNetRoot != NULL) {
|
|
RxReferenceVNetRoot( ThisVNetRoot );
|
|
}
|
|
|
|
//
|
|
// Dereference the VNetRoot returned as part of the lookup.
|
|
//
|
|
|
|
RxDereferenceVNetRoot( ActiveVNetRoot, LHS_LockNotHeld );
|
|
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
RxContext->Create.pNetRoot = NULL;
|
|
RxContext->Create.pSrvCall = NULL;
|
|
|
|
if (ThisVNetRoot != NULL) {
|
|
Status = RxConstructNetRoot( RxContext,
|
|
(PSRV_CALL)ThisVNetRoot->NetRoot->SrvCall,
|
|
(PNET_ROOT)ThisVNetRoot->NetRoot,
|
|
ThisVNetRoot,
|
|
LockState );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Condition = Condition_Good;
|
|
} else {
|
|
Condition = Condition_Bad;
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
} else if (Status == STATUS_SUCCESS) {
|
|
|
|
*LockState = LHS_ExclusiveLockHeld;
|
|
Condition = Condition_Good;
|
|
ThisVNetRoot = (PV_NET_ROOT)(RxContext->Create.pVNetRoot);
|
|
|
|
} else {
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxConstructVirtualNetRoot -- RxFindOrCreateConnections Status %lx\n", Status) );
|
|
}
|
|
|
|
if ((ThisVNetRoot != NULL) &&
|
|
!StableCondition( ThisVNetRoot->Condition )) {
|
|
|
|
RxTransitionVNetRoot( ThisVNetRoot, Condition );
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
if (ThisVNetRoot != NULL) {
|
|
|
|
ASSERT( *LockState != LHS_LockNotHeld );
|
|
RxDereferenceVNetRoot( ThisVNetRoot, *LockState );
|
|
ThisVNetRoot = NULL;
|
|
}
|
|
|
|
if (*LockState != LHS_LockNotHeld) {
|
|
RxReleasePrefixTableLock( RxContext->RxDeviceObject->pRxNetNameTable );
|
|
*LockState = LHS_LockNotHeld;
|
|
}
|
|
}
|
|
|
|
*VNetRoot = ThisVNetRoot;
|
|
|
|
RxDbgTrace( 0, Dbg, ("RxConstructVirtualNetRoot -- Exit Status %lx\n", Status) );
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCheckVNetRootCredentials (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PIRP Irp,
|
|
IN PV_NET_ROOT VNetRoot,
|
|
IN PLUID Luid,
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING Password,
|
|
IN ULONG Flags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks a given vnetroot and sees if it has matching credentials i.e
|
|
its a connection for the same user
|
|
|
|
Arguments:
|
|
|
|
RxContext -- the RDBSS context
|
|
|
|
|
|
Return Value:
|
|
|
|
the appropriate status value
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN UNCName;
|
|
BOOLEAN TreeConnect;
|
|
PSECURITY_USER_DATA SecurityData = NULL;
|
|
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNCName = BooleanFlagOn( RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME );
|
|
TreeConnect = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_CREATE_TREE_CONNECTION );
|
|
|
|
//
|
|
// only for UNC names do we do the logic below
|
|
//
|
|
|
|
if (FlagOn( RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME ) &&
|
|
(FlagOn( VNetRoot->Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE ) != FlagOn( Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE ))) {
|
|
|
|
//
|
|
// mismatched csc agent flags, not collapsing
|
|
//
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// The for loop is a scoping construct to join together the
|
|
// multitiude of failure cases in comparing the EA parameters
|
|
// with the original parameters supplied in the create request.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
if (RtlCompareMemory( &VNetRoot->LogonId, Luid, sizeof( LUID ) ) == sizeof( LUID )) {
|
|
|
|
PUNICODE_STRING TempUserName;
|
|
PUNICODE_STRING TempDomainName;
|
|
|
|
//
|
|
// If no EA parameters are specified by the user, the existing
|
|
// V_NET_ROOT instance as used. This is the common case when
|
|
// the user specifies the credentials for establishing a
|
|
// persistent connection across processes and reuses them.
|
|
//
|
|
|
|
if ((UserName == NULL) &&
|
|
(DomainName == NULL) &&
|
|
(Password == NULL)) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TempUserName = VNetRoot->pUserName;
|
|
TempDomainName = VNetRoot->pUserDomainName;
|
|
|
|
if (TempUserName == NULL ||
|
|
TempDomainName == NULL) {
|
|
|
|
Status = GetSecurityUserInfo( Luid,
|
|
UNDERSTANDS_LONG_NAMES,
|
|
&SecurityData );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (TempUserName == NULL) {
|
|
TempUserName = &SecurityData->UserName;
|
|
}
|
|
|
|
if (TempDomainName == NULL) {
|
|
TempDomainName = &SecurityData->LogonDomainName;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The logon ids match. The user has supplied EA parameters
|
|
// which can either match with the existing credentials or
|
|
// result in a conflict with the existing credentials. In all
|
|
// such cases the outcome will either be a reuse of the
|
|
// existing V_NET_ROOT instance or a refusal of the new connection
|
|
// attempt.
|
|
// The only exception to the above rule is in the case of
|
|
// regular opens (FILE_CREATE_TREE_CONNECTION is not
|
|
// specified for UNC names. In such cases the construction of a
|
|
// new V_NET_ROOT is initiated which will be torn down
|
|
// when the associated file is closed
|
|
//
|
|
|
|
if (UNCName && !TreeConnect) {
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else {
|
|
Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
|
|
}
|
|
|
|
if ((UserName != NULL) &&
|
|
(TempUserName != NULL) &&
|
|
!RtlEqualUnicodeString( TempUserName, UserName, TRUE )) {
|
|
break;
|
|
}
|
|
|
|
if ((DomainName != NULL) &&
|
|
!RtlEqualUnicodeString( TempDomainName, DomainName, TRUE )) {
|
|
break;
|
|
}
|
|
|
|
if ((VNetRoot->pPassword != NULL) &&
|
|
(Password != NULL)) {
|
|
|
|
if (!RtlEqualUnicodeString( VNetRoot->pPassword, Password, FALSE )) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We use existing session if either the stored or new password is NULL.
|
|
// Later, a new security API will be created for verify the password based
|
|
// on the logon ID.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SecurityData != NULL) {
|
|
LsaFreeReturnBuffer( SecurityData );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
RxFindOrConstructVirtualNetRoot (
|
|
IN PRX_CONTEXT RxContext,
|
|
IN PIRP Irp,
|
|
IN PUNICODE_STRING CanonicalName,
|
|
IN NET_ROOT_TYPE NetRootType,
|
|
IN PUNICODE_STRING RemainingName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds or constructs a VNetRoot (View of a net root)
|
|
|
|
Arguments:
|
|
|
|
RxContext -- the RDBSS context
|
|
|
|
CanonicalName -- the canonical name associated with the VNetRoot
|
|
|
|
NetRootType -- the type of the virtual net root
|
|
|
|
RemainingName -- the portion of the name that was not found in the prefix table
|
|
|
|
Return Value:
|
|
|
|
the appropriate status value
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
LOCK_HOLDING_STATE LockState;
|
|
|
|
BOOLEAN Wait = BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_WAIT );
|
|
BOOLEAN InFSD = !BooleanFlagOn( RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP );
|
|
|
|
BOOLEAN UNCName;
|
|
BOOLEAN TreeConnect;
|
|
|
|
PVOID Container;
|
|
|
|
PV_NET_ROOT VNetRoot;
|
|
PRX_PREFIX_TABLE RxNetNameTable = RxContext->RxDeviceObject->pRxNetNameTable;
|
|
ULONG Flags = 0;
|
|
RX_CONNECTION_ID RxConnectionId;
|
|
PRDBSS_DEVICE_OBJECT RxDeviceObject = RxContext->RxDeviceObject;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
PAGED_CODE();
|
|
|
|
MINIRDR_CALL_THROUGH( Status,
|
|
RxDeviceObject->Dispatch,
|
|
MRxGetConnectionId,
|
|
(RxContext,&RxConnectionId) );
|
|
|
|
if (Status == STATUS_NOT_IMPLEMENTED) {
|
|
|
|
RtlZeroMemory( &RxConnectionId, sizeof( RX_CONNECTION_ID ) );
|
|
|
|
} else if(!NT_SUCCESS( Status )) {
|
|
|
|
DbgPrint( "MRXSMB: Failed to initialize Connection ID\n" );
|
|
ASSERT( FALSE );
|
|
RtlZeroMemory( &RxConnectionId, sizeof( RX_CONNECTION_ID ) );
|
|
}
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
UNCName = BooleanFlagOn( RxContext->Create.Flags, RX_CONTEXT_CREATE_FLAG_UNC_NAME );
|
|
TreeConnect = BooleanFlagOn( IrpSp->Parameters.Create.Options, FILE_CREATE_TREE_CONNECTION );
|
|
|
|
//
|
|
// deleterxcontext stuff will deref wherever this points.......
|
|
//
|
|
|
|
RxContext->Create.NetNamePrefixEntry = NULL;
|
|
|
|
RxAcquirePrefixTableLockShared( RxNetNameTable, TRUE );
|
|
LockState = LHS_SharedLockHeld;
|
|
|
|
for(;;) {
|
|
|
|
//
|
|
// This for loop actually serves as a simple scoping construct for executing
|
|
// the same piece of code twice, once with a shared lock and once with an
|
|
// exclusive lock. In the interests of maximal concurrency a shared lock is
|
|
// accquired for the first pass and subsequently upgraded. If the search
|
|
// succeeds with a shared lock the second pass is skipped.
|
|
//
|
|
|
|
Container = RxPrefixTableLookupName( RxNetNameTable, CanonicalName, RemainingName, &RxConnectionId );
|
|
|
|
if (Container != NULL ) {
|
|
if (NodeType( Container ) == RDBSS_NTC_V_NETROOT) {
|
|
|
|
PV_NET_ROOT TempVNetRoot = NULL;
|
|
PNET_ROOT NetRoot;
|
|
ULONG SessionId;
|
|
|
|
VNetRoot = (PV_NET_ROOT)Container;
|
|
NetRoot = (PNET_ROOT)VNetRoot->NetRoot;
|
|
|
|
//
|
|
// Determine if a virtual net root with the same logon id. already exists.
|
|
// If not a new virtual net root has to be constructed.
|
|
// traverse the list of virtual net roots associated with a net root.
|
|
// Note that the list of virtual net roots associated with a net root cannot be empty
|
|
// since the construction of the default virtual net root coincides with the creation
|
|
// of the net root.
|
|
//
|
|
|
|
if (((NetRoot->Condition == Condition_Good) ||
|
|
(NetRoot->Condition == Condition_InTransition)) &&
|
|
(NetRoot->SrvCall->RxDeviceObject == RxContext->RxDeviceObject)) {
|
|
|
|
LUID LogonId;
|
|
PUNICODE_STRING UserName;
|
|
PUNICODE_STRING UserDomainName;
|
|
PUNICODE_STRING Password;
|
|
|
|
//
|
|
// Extract the VNetRoot parameters from the IRP to map one of
|
|
// the existing VNetRoots if possible. The algorithm for
|
|
// determining this mapping is very simplistic. If no Ea
|
|
// parameters are specified a VNetRoot with a matching Logon
|
|
// id. is choosen. if Ea parameters are specified then a
|
|
// VNetRoot with identical parameters is choosen. The idea
|
|
// behind this simplistic algorithm is to let the mini redirectors
|
|
// determine the mapping policy and not prefer one mini
|
|
// redirectors policy over another.
|
|
//
|
|
|
|
Status = RxInitializeVNetRootParameters( RxContext,
|
|
&LogonId,
|
|
&SessionId,
|
|
&UserName,
|
|
&UserDomainName,
|
|
&Password,
|
|
&Flags );
|
|
|
|
//
|
|
// Walk list of vnetroots and check for a match starting
|
|
// optimistically with the one found
|
|
//
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
TempVNetRoot = VNetRoot;
|
|
|
|
do {
|
|
|
|
Status = RxCheckVNetRootCredentials( RxContext,
|
|
Irp,
|
|
TempVNetRoot,
|
|
&LogonId,
|
|
UserName,
|
|
UserDomainName,
|
|
Password,
|
|
Flags );
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
TempVNetRoot = (PV_NET_ROOT)CONTAINING_RECORD( TempVNetRoot->NetRootListEntry.Flink,
|
|
V_NET_ROOT,
|
|
NetRootListEntry);
|
|
}
|
|
|
|
} while ((Status == STATUS_MORE_PROCESSING_REQUIRED) && (TempVNetRoot != VNetRoot));
|
|
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
TempVNetRoot = NULL;
|
|
} else {
|
|
|
|
//
|
|
// Reference the found vnetroot on success
|
|
//
|
|
|
|
RxReferenceVNetRoot( TempVNetRoot );
|
|
}
|
|
|
|
RxUninitializeVNetRootParameters( UserName, UserDomainName, Password, &Flags );
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
TempVNetRoot = NULL;
|
|
}
|
|
|
|
RxDereferenceVNetRoot( VNetRoot, LockState );
|
|
VNetRoot = TempVNetRoot;
|
|
|
|
} else {
|
|
|
|
ASSERT( NodeType( Container ) == RDBSS_NTC_SRVCALL );
|
|
RxDereferenceSrvCall( (PSRV_CALL)Container, LockState );
|
|
}
|
|
}
|
|
|
|
if ((Status == STATUS_MORE_PROCESSING_REQUIRED) && (LockState == LHS_SharedLockHeld)) {
|
|
|
|
//
|
|
// Release the shared lock and acquire it in an exclusive mode.
|
|
// Upgrade the lock to an exclusive lock
|
|
//
|
|
|
|
if (!RxAcquirePrefixTableLockExclusive( RxNetNameTable, FALSE )) {
|
|
|
|
RxReleasePrefixTableLock( RxNetNameTable );
|
|
RxAcquirePrefixTableLockExclusive( RxNetNameTable, TRUE );
|
|
LockState = LHS_ExclusiveLockHeld;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The lock was upgraded from a shared mode to an exclusive mode without
|
|
// losing it. Therefore there is no need to search the table again. The
|
|
// construction of the new V_NET_ROOT can proceed.
|
|
|
|
LockState = LHS_ExclusiveLockHeld;
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// At this point either the lookup was successful ( with a shared/exclusive lock )
|
|
// or exclusive lock has been obtained.
|
|
// No virtual net root was found in the prefix table or the net root that was found is bad.
|
|
// The construction of a new virtual netroot needs to be undertaken.
|
|
//
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
ASSERT( LockState == LHS_ExclusiveLockHeld );
|
|
Status = RxConstructVirtualNetRoot( RxContext,
|
|
Irp,
|
|
CanonicalName,
|
|
NetRootType,
|
|
TreeConnect,
|
|
&VNetRoot,
|
|
&LockState,
|
|
&RxConnectionId );
|
|
|
|
ASSERT( (Status != STATUS_SUCCESS) || (LockState != LHS_LockNotHeld) );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
|
|
ASSERT( CanonicalName->Length >= VNetRoot->PrefixEntry.Prefix.Length );
|
|
|
|
RemainingName->Buffer = (PWCH)Add2Ptr( CanonicalName->Buffer, VNetRoot->PrefixEntry.Prefix.Length );
|
|
RemainingName->Length = CanonicalName->Length - VNetRoot->PrefixEntry.Prefix.Length;
|
|
RemainingName->MaximumLength = RemainingName->Length;
|
|
|
|
if (FlagOn( Flags, VNETROOT_FLAG_CSCAGENT_INSTANCE )) {
|
|
RxLog(( "FOrCVNR CSC instance %x\n", VNetRoot ));
|
|
RxWmiLog( LOG,
|
|
RxFindOrConstructVirtualNetRoot,
|
|
LOGPTR( VNetRoot ) );
|
|
}
|
|
SetFlag( VNetRoot->Flags, Flags );
|
|
}
|
|
}
|
|
|
|
if (LockState != LHS_LockNotHeld) {
|
|
RxReleasePrefixTableLock( RxNetNameTable );
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RxWaitForStableVNetRoot( VNetRoot, RxContext );
|
|
|
|
if (VNetRoot->Condition == Condition_Good) {
|
|
|
|
RxContext->Create.pVNetRoot = (PMRX_V_NET_ROOT)VNetRoot;
|
|
RxContext->Create.pNetRoot = (PMRX_NET_ROOT)VNetRoot->NetRoot;
|
|
RxContext->Create.pSrvCall = (PMRX_SRV_CALL)VNetRoot->NetRoot->SrvCall;
|
|
|
|
} else {
|
|
RxDereferenceVNetRoot( VNetRoot, LHS_LockNotHeld );
|
|
RxContext->Create.pVNetRoot = NULL;
|
|
Status = STATUS_BAD_NETWORK_PATH;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|