/*++ Copyright (c) 1989 Microsoft Corporation Module Name: FcbStruc.c Abstract: This module implements functions for to create and dereference fcbs and all of the surrounding paraphenalia. Please read the abstract in fcb.h. Please see the note about what locks to need to call what. There are asserts to enforce these conventions. Author: Joe Linn (JoeLinn) 8-8-94 Revision History: Balan Sethu Raman -- --*/ #include "precomp.h" #pragma hdrstop #include #include #ifdef RDBSSLOG #include #endif #include #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, RxDereference) #pragma alloc_text(PAGE, RxReference) #pragma alloc_text(PAGE, RxpReferenceNetFcb) #pragma alloc_text(PAGE, RxpDereferenceNetFcb) #pragma alloc_text(PAGE, RxpDereferenceAndFinalizeNetFcb) #pragma alloc_text(PAGE, RxWaitForStableCondition) #pragma alloc_text(PAGE, RxUpdateCondition) #pragma alloc_text(PAGE, RxAllocateObject) #pragma alloc_text(PAGE, RxFreeObject) #pragma alloc_text(PAGE, RxFinalizeNetTable) #pragma alloc_text(PAGE, RxFinalizeConnection) #pragma alloc_text(PAGE, RxInitializeSrvCallParameters) #pragma alloc_text(PAGE, RxCreateSrvCall) #pragma alloc_text(PAGE, RxSetSrvCallDomainName) #pragma alloc_text(PAGE, RxFinalizeSrvCall) #pragma alloc_text(PAGE, RxCreateNetRoot) #pragma alloc_text(PAGE, RxFinalizeNetRoot) #pragma alloc_text(PAGE, RxAddVirtualNetRootToNetRoot) #pragma alloc_text(PAGE, RxRemoveVirtualNetRootFromNetRoot) #pragma alloc_text(PAGE, RxInitializeVNetRootParameters) #pragma alloc_text(PAGE, RxUninitializeVNetRootParameters) #pragma alloc_text(PAGE, RxCreateVNetRoot) #pragma alloc_text(PAGE, RxOrphanSrvOpens) #pragma alloc_text(PAGE, RxFinalizeVNetRoot) #pragma alloc_text(PAGE, RxAllocateFcbObject) #pragma alloc_text(PAGE, RxFreeFcbObject) #pragma alloc_text(PAGE, RxCreateNetFcb) #pragma alloc_text(PAGE, RxInferFileType) #pragma alloc_text(PAGE, RxFinishFcbInitialization) #pragma alloc_text(PAGE, RxRemoveNameNetFcb) #pragma alloc_text(PAGE, RxPurgeFcb) #pragma alloc_text(PAGE, RxFinalizeNetFcb) #pragma alloc_text(PAGE, RxSetFileSizeWithLock) #pragma alloc_text(PAGE, RxGetFileSizeWithLock) #pragma alloc_text(PAGE, RxCreateSrvOpen) #pragma alloc_text(PAGE, RxFinalizeSrvOpen) #pragma alloc_text(PAGE, RxCreateNetFobx) #pragma alloc_text(PAGE, RxFinalizeNetFobx) #pragma alloc_text(PAGE, RxCheckFcbStructuresForAlignment) #pragma alloc_text(PAGE, RxOrphanThisFcb) #pragma alloc_text(PAGE, RxOrphanSrvOpensForThisFcb) #pragma alloc_text(PAGE, RxForceFinalizeAllVNetRoots) #endif // // The Bug check file id for this module // #define BugCheckFileId (RDBSS_BUG_CHECK_FCBSTRUC) //struct _RX_PREFIX_TABLE RxNetNameTable; ULONG SerialNumber = 1; //zero doesn't work!!! // // The debug trace level // #define Dbg (DEBUG_TRACE_FCBSTRUCTS) // SRV_CALL,NET_ROOT,VNET_ROOT,FCB,SRV_OPEN,FOBX are the six key data structures in the RDBSS // They are organized in the following hierarchy // // SRV_CALL // NET_ROOT // VNET_ROOT // FCB // SRV_OPEN // FOBX // // All these data structures are reference counted. The reference count associated with // any data structure is atleast 1 + the number of instances of the data structure at the next // level associated with it, e.g., the reference count associated with a SRV_CALL which // has two NET_ROOT's associated with it is atleast 3. In addition to the references held // by the NameTable and the data structure at the next level there are additional references // acquired as and when required. // // These restrictions ensure that a data structure at any given level cannot be finalized till // all the data structures at the next level have been finalized or have released their // references, i.e., if a reference to a FCB is held, then it is safe to access the VNET_ROOT, // NET_ROOT and SRV_CALL associated with it. // // The SRV_CALL,NET_ROOT and VNET_ROOT creation/finalization are governed by the acquistion/ // release of the RxNetNameTable lock. // // The FCB creation/finalization is governed by the acquistion/release of the NetNameTable // lock associated with the NET_ROOT. // // The FOBX/SRVOPEN creation/finalization is governed by the acquistion/release of the FCB // resource. // // The following table summarizes the locks and the modes in which they need to be acquired // for creation/finalization of the various data structures. // // // L O C K I N G R E Q U I R E M E N T S // // Locking requirements are as follows: // // where Xy means Exclusive-on-Y, Sy mean at least Shared-on-Y // and NNT means global NetNameTable, TL means NetRoot TableLock, and FCB means FCBlock // // // // SRVCALL NETROOT FCB SRVOPEN FOBX // // Create XNNT XNNT XTL XFCB XFCB // Finalize XNNT XNNT XFCB XFCB XFCB // & XTL // // Referencing and Dereferencing these data structures need to adhere to certain conventions // as well. // // When the reference count associated with any of these data structures drops to 1 ( the sole // reference being held by the name table in most cases) the data structure is a potential // candidate for finalization. The data structure can be either finalized immediately or it // can be marked for scavenging. Both of these methods are implemented. When the locking // requirements are met during dereferencing the data structures are finalized immediately // ( the one exception being that when delayed operation optimization is implemented, e.g., FCB) // otherwise the data structure is marked for scavenging. // // // You are supposed to have the tablelock exclusive to be calling this routine.......I can't // take it here because you are already supposed to have it. To do a create, you should // done something like // // getshared();lookup(); // if (failed) { // release(); getexclusive(); lookup(); // if ((failed) { create(); } // } // deref(); // release(); // // so you will already have the lock. what you do is to insert the node into the table, release // the lock, and then go and see if the server's there. if so, set up the rest of the stuff and unblock // anyone who's waiting on the same server (or netroot)...i guess i could enforce this by releasing here // but i do not. // Forward declarations -- These routines are not meant for use in other // modules BOOLEAN RxFinalizeNetFcb ( OUT PFCB ThisFcb, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize, IN LONG ReferenceCount ); VOID RxPurgeFcb( IN PFCB pFcb); BOOLEAN RxIsThisACscAgentOpen( IN PRX_CONTEXT RxContext ); VOID RxDereference( IN OUT PVOID pInstance, IN LOCK_HOLDING_STATE LockHoldingState) /*++ Routine Description: The routine adjust the reference count on an instance of the reference counted data structures in RDBSS exlcuding the FCB. Arguments: pInstance - the instance being dereferenced LockHoldingState - the mode in which the appropriate lock is held. Return Value: none. --*/ { LONG FinalRefCount; NODE_TYPE_CODE_AND_SIZE *pNode = (PNODE_TYPE_CODE_AND_SIZE)pInstance; BOOLEAN fFinalizeInstance = FALSE; PAGED_CODE(); RxAcquireScavengerMutex(); ASSERT((NodeType(pInstance) == RDBSS_NTC_SRVCALL ) || (NodeType(pInstance) == RDBSS_NTC_NETROOT ) || (NodeType(pInstance) == RDBSS_NTC_V_NETROOT ) || (NodeType(pInstance) == RDBSS_NTC_SRVOPEN ) || (NodeType(pInstance) == RDBSS_NTC_FOBX )); FinalRefCount = InterlockedDecrement(&pNode->NodeReferenceCount); ASSERT(FinalRefCount >= 0); IF_DEBUG { switch (NodeType(pInstance)) { case RDBSS_NTC_SRVCALL : { PSRV_CALL ThisSrvCall = (PSRV_CALL)pInstance; PRINT_REF_COUNT(SRVCALL,ThisSrvCall->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceSrvCall %08lx %wZ RefCount=%lx\n", ThisSrvCall , &ThisSrvCall->PrefixEntry.Prefix , ThisSrvCall->NodeReferenceCount)); } break; case RDBSS_NTC_NETROOT : { PNET_ROOT ThisNetRoot = (PNET_ROOT)pInstance; PRINT_REF_COUNT(NETROOT,ThisNetRoot->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceNetRoot %08lx %wZ RefCount=%lx\n", ThisNetRoot , &ThisNetRoot->PrefixEntry.Prefix , ThisNetRoot->NodeReferenceCount)); } break; case RDBSS_NTC_V_NETROOT: { PV_NET_ROOT ThisVNetRoot = (PV_NET_ROOT)pInstance; PRINT_REF_COUNT(VNETROOT,ThisVNetRoot->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceVNetRoot %08lx %wZ RefCount=%lx\n", ThisVNetRoot , &ThisVNetRoot->PrefixEntry.Prefix , ThisVNetRoot->NodeReferenceCount)); } break; case RDBSS_NTC_SRVOPEN : { PSRV_OPEN ThisSrvOpen = (PSRV_OPEN)pInstance; PRINT_REF_COUNT(SRVOPEN,ThisSrvOpen->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceSrvOpen %08lx %wZ RefCount=%lx\n", ThisSrvOpen , &ThisSrvOpen->Fcb->FcbTableEntry.Path , ThisSrvOpen->NodeReferenceCount)); } break; case RDBSS_NTC_FOBX: { PFOBX ThisFobx = (PFOBX)pInstance; PRINT_REF_COUNT(NETFOBX,ThisFobx->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceFobx %08lx %wZ RefCount=%lx\n", ThisFobx , &ThisFobx->SrvOpen->Fcb->FcbTableEntry.Path , ThisFobx->NodeReferenceCount)); } break; default: break; } } // if the final reference count was greater then one no finalization is required. if (FinalRefCount <= 1) { if (LockHoldingState == LHS_ExclusiveLockHeld) { // if the reference count was 1 and the lock modes were satisfactory, // the instance can be finalized immediately. fFinalizeInstance = TRUE; if ((pNode->NodeTypeCode & RX_SCAVENGER_MASK) != 0) { RxpUndoScavengerFinalizationMarking(pInstance); } } else { switch (NodeType(pInstance)) { case RDBSS_NTC_FOBX: if (FinalRefCount != 0) { break; } // fall thru intentional if refcount == 1 for FOBXs case RDBSS_NTC_SRVCALL: case RDBSS_NTC_NETROOT: case RDBSS_NTC_V_NETROOT: // the data structure cannot be freed at this time owing to the mode in which // the lock has been acquired ( or not having the lock at all ). RxpMarkInstanceForScavengedFinalization(pInstance); break; default: break; } } } RxReleaseScavengerMutex(); if (fFinalizeInstance) { switch (NodeType(pInstance)) { case RDBSS_NTC_SRVCALL: { //ASSERT( RxIsPrefixTableLockAcquired( &RxNetNameTable )); IF_DEBUG { PRDBSS_DEVICE_OBJECT RxDeviceObject = ((PSRV_CALL)pInstance)->RxDeviceObject; ASSERT( RxDeviceObject != NULL ); ASSERT( RxIsPrefixTableLockAcquired( RxDeviceObject->pRxNetNameTable )); } RxFinalizeSrvCall((PSRV_CALL)pInstance,TRUE,TRUE); } break; case RDBSS_NTC_NETROOT: { //ASSERT( RxIsPrefixTableLockAcquired( &RxNetNameTable )); IF_DEBUG { PSRV_CALL SrvCall = ((PNET_ROOT)pInstance)->SrvCall; PRDBSS_DEVICE_OBJECT RxDeviceObject = SrvCall->RxDeviceObject; ASSERT( RxDeviceObject != NULL ); ASSERT( RxIsPrefixTableLockAcquired( RxDeviceObject->pRxNetNameTable )); } RxFinalizeNetRoot((PNET_ROOT)pInstance,TRUE,TRUE); } break; case RDBSS_NTC_V_NETROOT: { //ASSERT( RxIsPrefixTableLockAcquired( &RxNetNameTable )); IF_DEBUG { PSRV_CALL SrvCall = ((PV_NET_ROOT)pInstance)->NetRoot->SrvCall; PRDBSS_DEVICE_OBJECT RxDeviceObject = SrvCall->RxDeviceObject; ASSERT( RxDeviceObject != NULL ); ASSERT( RxIsPrefixTableLockAcquired( RxDeviceObject->pRxNetNameTable )); } RxFinalizeVNetRoot((PV_NET_ROOT)pInstance,TRUE,TRUE); } break; case RDBSS_NTC_SRVOPEN: { PSRV_OPEN ThisSrvOpen = (PSRV_OPEN)pInstance; ASSERT(RxIsFcbAcquired(ThisSrvOpen->Fcb)); if (ThisSrvOpen->OpenCount == 0) { RxFinalizeSrvOpen(ThisSrvOpen,FALSE,FALSE); } } break; case RDBSS_NTC_FOBX: { PFOBX ThisFobx = (PFOBX)pInstance; ASSERT(RxIsFcbAcquired( ThisFobx->SrvOpen->Fcb)); RxFinalizeNetFobx(ThisFobx,TRUE,FALSE); } break; default: break; } } } VOID RxReference( OUT PVOID pInstance) /*++ Routine Description: The routine adjusts the reference count on the instance. Arguments: pInstance - the instance being referenced Return Value: RxStatus(SUCESS) is successful RxStatus(UNSUCCESSFUL) otherwise. --*/ { LONG FinalRefCount; NODE_TYPE_CODE_AND_SIZE *pNode = (PNODE_TYPE_CODE_AND_SIZE)pInstance; USHORT InstanceType; PAGED_CODE(); RxAcquireScavengerMutex(); InstanceType = pNode->NodeTypeCode & ~RX_SCAVENGER_MASK; ASSERT((InstanceType == RDBSS_NTC_SRVCALL ) || (InstanceType == RDBSS_NTC_NETROOT ) || (InstanceType == RDBSS_NTC_V_NETROOT ) || (InstanceType == RDBSS_NTC_SRVOPEN ) || (InstanceType == RDBSS_NTC_FOBX )); FinalRefCount = InterlockedIncrement(&pNode->NodeReferenceCount); IF_DEBUG { if (pNode->NodeTypeCode & RX_SCAVENGER_MASK) { RxDbgTrace(0,Dbg,("Referencing Scavenged instance -- Type %lx\n",InstanceType)); } switch (InstanceType) { case RDBSS_NTC_SRVCALL : { PSRV_CALL ThisSrvCall = (PSRV_CALL)pInstance; PRINT_REF_COUNT(SRVCALL,ThisSrvCall->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceSrvCall %08lx %wZ RefCount=%lx\n", ThisSrvCall , &ThisSrvCall->PrefixEntry.Prefix , ThisSrvCall->NodeReferenceCount)); } break; case RDBSS_NTC_NETROOT : { PNET_ROOT ThisNetRoot = (PNET_ROOT)pInstance; PRINT_REF_COUNT(NETROOT,ThisNetRoot->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceNetRoot %08lx %wZ RefCount=%lx\n", ThisNetRoot , &ThisNetRoot->PrefixEntry.Prefix , ThisNetRoot->NodeReferenceCount)); } break; case RDBSS_NTC_V_NETROOT: { PV_NET_ROOT ThisVNetRoot = (PV_NET_ROOT)pInstance; PRINT_REF_COUNT(VNETROOT,ThisVNetRoot->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceVNetRoot %08lx %wZ RefCount=%lx\n", ThisVNetRoot , &ThisVNetRoot->PrefixEntry.Prefix , ThisVNetRoot->NodeReferenceCount)); } break; case RDBSS_NTC_SRVOPEN : { PSRV_OPEN ThisSrvOpen = (PSRV_OPEN)pInstance; PRINT_REF_COUNT(SRVOPEN,ThisSrvOpen->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceSrvOpen %08lx %wZ RefCount=%lx\n", ThisSrvOpen , &ThisSrvOpen->Fcb->FcbTableEntry.Path , ThisSrvOpen->NodeReferenceCount)); } break; case RDBSS_NTC_FOBX: { PFOBX ThisFobx = (PFOBX)pInstance; PRINT_REF_COUNT(NETFOBX,ThisFobx->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceFobx %08lx %wZ RefCount=%lx\n", ThisFobx , &ThisFobx->SrvOpen->Fcb->FcbTableEntry.Path , ThisFobx->NodeReferenceCount)); } break; default: { ASSERT(!"Valid node type for referencing"); } break; } } RxpUndoScavengerFinalizationMarking(pInstance); RxReleaseScavengerMutex(); } VOID RxpReferenceNetFcb( PFCB pFcb) /*++ Routine Description: The routine adjusts the reference count on the FCB. Arguments: pFcb - the SrvCall being referenced Return Value: RxStatus(SUCESS) is successful RxStatus(UNSUCCESSFUL) otherwise. --*/ { LONG FinalRefCount; PAGED_CODE(); ASSERT(NodeTypeIsFcb(pFcb)); FinalRefCount = InterlockedIncrement(&pFcb->NodeReferenceCount); IF_DEBUG { PRINT_REF_COUNT(NETFCB,pFcb->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxReferenceNetFcb %08lx %wZ RefCount=%lx\n", pFcb , &pFcb->FcbTableEntry.Path , pFcb->NodeReferenceCount)); } } LONG RxpDereferenceNetFcb( PFCB pFcb) /*++ Routine Description: The routine adjust the reference count on an instance of the reference counted data structures in RDBSS exlcuding the FCB. Arguments: pFcb -- the FCB being dereferenced Return Value: none. Notes: The referencing and dereferencing of FCB's is different from those of the other data structures because of the embedded resource in the FCB. This implies that the caller requires information regarding the status of the FCB ( whether it was finalized or not ) In order to finalize the FCB two locks need to be held, the NET_ROOT's name table lock as well as the FCB resource. These considerations lead us to adopt a different approach in dereferencing FCB's. The dereferencing routine does not even attempt to finalize the FCB --*/ { LONG FinalRefCount; PAGED_CODE(); ASSERT(NodeTypeIsFcb(pFcb)); FinalRefCount = InterlockedDecrement(&pFcb->NodeReferenceCount); ASSERT(FinalRefCount >= 0); IF_DEBUG { PRINT_REF_COUNT(NETFCB,pFcb->NodeReferenceCount); RxDbgTrace( 0, Dbg, (" RxDereferenceNetFcb %08lx %wZ RefCount=%lx\n", pFcb , &pFcb->FcbTableEntry.Path , pFcb->NodeReferenceCount)); } return(FinalRefCount); } BOOLEAN RxpDereferenceAndFinalizeNetFcb( PFCB pFcb, PRX_CONTEXT RxContext, BOOLEAN RecursiveFinalize, BOOLEAN ForceFinalize) /*++ Routine Description: The routine adjust the reference count aw well as finalizes the FCB if required Arguments: pFcb -- the FCB being dereferenced RxContext -- the context for releasing/acquiring FCB. RecursiveFinalize -- recursive finalization ForceFinalize -- force finalization Return Value: none. Notes: The referencing and dereferencing of FCB's is different from those of the other data structures because of the embedded resource in the FCB. This implies that the caller requires information regarding the status of the FCB ( whether it was finalized or not ) In order to finalize the FCB two locks need to be held, the NET_ROOT's name table lock as well as the FCB resource. This routine acquires the additional lock if required. --*/ { BOOLEAN NodeActuallyFinalized = FALSE; LONG FinalRefCount; PAGED_CODE(); ASSERT(!ForceFinalize); ASSERT(NodeTypeIsFcb(pFcb)); ASSERT(RxIsFcbAcquiredExclusive(pFcb)); FinalRefCount = InterlockedDecrement(&pFcb->NodeReferenceCount); if (ForceFinalize || RecursiveFinalize || ((pFcb->OpenCount == 0) && (pFcb->UncleanCount == 0) && (FinalRefCount <= 1))) { BOOLEAN PrefixTableLockAcquired = FALSE; PNET_ROOT pNetRoot; NTSTATUS Status = (STATUS_SUCCESS); if (!FlagOn(pFcb->FcbState,FCB_STATE_ORPHANED)) { pNetRoot = (PNET_ROOT)pFcb->VNetRoot->NetRoot; // An insurance reference to ensure that the NET ROOT does not dissapear RxReferenceNetRoot(pNetRoot); // In all these cases the FCB is likely to be finalized if (!RxIsFcbTableLockExclusive(&pNetRoot->FcbTable)) { RxReferenceNetFcb(pFcb); // get ready to refresh the finalrefcount after we get the tablelock if (!RxAcquireFcbTableLockExclusive(&pNetRoot->FcbTable, FALSE) ) { if ((RxContext != NULL) && (RxContext != CHANGE_BUFFERING_STATE_CONTEXT) && (RxContext != CHANGE_BUFFERING_STATE_CONTEXT_WAIT)) { SetFlag(RxContext->Flags,RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK); } RxReleaseFcb(RxContext,pFcb ); (VOID)RxAcquireFcbTableLockExclusive(&pNetRoot->FcbTable, TRUE); Status = RxAcquireExclusiveFcb(RxContext, pFcb); } FinalRefCount = RxDereferenceNetFcb(pFcb); PrefixTableLockAcquired = TRUE; } } else { pNetRoot = NULL; } if (Status == (STATUS_SUCCESS)) { NodeActuallyFinalized = RxFinalizeNetFcb(pFcb,RecursiveFinalize,ForceFinalize,FinalRefCount); } if (PrefixTableLockAcquired) { RxReleaseFcbTableLock(&pNetRoot->FcbTable); } if (pNetRoot != NULL) { RxDereferenceNetRoot(pNetRoot,LHS_LockNotHeld); } } return NodeActuallyFinalized; } VOID RxWaitForStableCondition( IN PRX_BLOCK_CONDITION pCondition, IN OUT PLIST_ENTRY pTransitionWaitList, IN OUT PRX_CONTEXT pRxContext, OUT NTSTATUS *AsyncStatus OPTIONAL) /*++ Routine Description: The routine checks to see if the condition is stable. If not, it is suspended till a stable condition is attained. when a stable condition is obtained, either the rxcontext sync event is set or the context is posted...depending on the POST_ON_STABLE_CONDITION context flag. the flag is cleared on a post. Arguments: Condition - the condition variable we're waiting on Resource - the resrouce used to control access to the containing block RxContext - the RX context Return Value: RXSTATUS - PENDING if notstable and the context will be posted SUCCESS otherwise --*/ { NTSTATUS DummyStatus; BOOLEAN Wait = FALSE; PAGED_CODE(); if (AsyncStatus == NULL) { AsyncStatus = &DummyStatus; } *AsyncStatus = (STATUS_SUCCESS); if (StableCondition(*pCondition)) return; //early out could macroize #ifndef WIN9X RxAcquireSerializationMutex(); #endif if (!StableCondition(*pCondition)) { RxInsertContextInSerializationQueue(pTransitionWaitList,pRxContext); if (!FlagOn(pRxContext->Flags,RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION)){ Wait = TRUE; } else { *AsyncStatus = (STATUS_PENDING); } } #ifndef WIN9X RxReleaseSerializationMutex(); #endif WIN9X if (Wait) { RxWaitSync(pRxContext); } return; } VOID RxUpdateCondition ( IN RX_BLOCK_CONDITION NewCondition, OUT PRX_BLOCK_CONDITION pCondition, IN OUT PLIST_ENTRY pTransitionWaitList) /*++ Routine Description: The routine unwaits the guys waiting on the transition event and the condition is set according to the parameter passed. Arguments: NewConditionValue - the new value of the condition variable pCondition - variable (i.e. a ptr) to the transitioning condition pTransitionWaitList - list of contexts waiting for the transition. Notes: The resource associated with the data structure instance being modified must have been acquired exclusively before invoking this routine, i.e., for SRV_CALL,NET_ROOT and V_NET_ROOT the net name table lock must be acquired and for FCB's the associated resource. --*/ { LIST_ENTRY TargetListHead; PRX_CONTEXT pRxContext; PAGED_CODE(); #ifndef WIN9X RxAcquireSerializationMutex(); #endif ASSERT(NewCondition != Condition_InTransition); *pCondition = NewCondition; RxTransferList(&TargetListHead,pTransitionWaitList); #ifndef WIN9X RxReleaseSerializationMutex(); #endif while (pRxContext = RxRemoveFirstContextFromSerializationQueue(&TargetListHead)) { if (!FlagOn(pRxContext->Flags,RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION)){ RxSignalSynchronousWaiter(pRxContext); } else { ClearFlag(pRxContext->Flags,RX_CONTEXT_FLAG_POST_ON_STABLE_CONDITION); RxFsdPostRequest(pRxContext); } } } PVOID RxAllocateObject( NODE_TYPE_CODE NodeType, PMINIRDR_DISPATCH pMRxDispatch, ULONG NameLength) /*++ Routine Description: The routine allocates and constructs the skeleton of a SRV_CALL/NET_ROOT/V_NET_ROOT instance. Arguments: NodeType - the node type pMRxDispatch - the Mini redirector dispatch vector NameLength - name size. Notes: The reasons as to why the allocation/freeing of these data structures have been centralized are as follows 1) The construction of these three data types have a lot in common with the exception of the initial computation of sizes. Therefore centralization minimizes the footprint. 2) It allows us to experiment with different clustering/allocation strategies. 3) It allows the incorporation of debug support in an easy way. There are two special cases of interest in the allocation strategy ... 1) The data structures for the wrapper as well as the corresponding mini redirector are allocated adjacent to each other. This ensures spatial locality. 2) The one exception to the above rule is the SRV_CALL data structure. This is because of the bootstrapping problem. A SRV_CALL skeleton needs to be created which is then passed around to each of the mini redirectors. Therefore adoption of rule (1) is not possible. Further there can be more than one mini redirector laying claim to a particular server. In consideration of these things SRV_CALL's need to be treated as an exception to (1). However once a particular mini redirector has been selected as the winner it would be advantageous to colocate the data structure to derive the associated performance benefits. This has not been implemented as yet. --*/ { ULONG PoolTag; ULONG RdbssNodeSize,MRxNodeSize; BOOLEAN fInitializeContextFields = FALSE; PNODE_TYPE_CODE_AND_SIZE pNode; PAGED_CODE(); RdbssNodeSize = MRxNodeSize = 0; switch (NodeType) { case RDBSS_NTC_SRVCALL : { PoolTag = RX_SRVCALL_POOLTAG; RdbssNodeSize = QuadAlign(sizeof(SRV_CALL)); if (pMRxDispatch != NULL) { if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_SRV_CALL_EXTENSION) { MRxNodeSize = QuadAlign(pMRxDispatch->MRxSrvCallSize); } } } break; case RDBSS_NTC_NETROOT: { PoolTag = RX_NETROOT_POOLTAG; RdbssNodeSize = QuadAlign(sizeof(NET_ROOT)); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_NET_ROOT_EXTENSION) { MRxNodeSize = QuadAlign(pMRxDispatch->MRxNetRootSize); } } break; case RDBSS_NTC_V_NETROOT: { PoolTag = RX_V_NETROOT_POOLTAG; RdbssNodeSize = QuadAlign(sizeof(V_NET_ROOT)); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_V_NET_ROOT_EXTENSION) { MRxNodeSize = QuadAlign(pMRxDispatch->MRxVNetRootSize); } } break; default: ASSERT(!"Invalid Node Type for allocation/Initialization"); return NULL; } pNode = RxAllocatePoolWithTag( NonPagedPool, (RdbssNodeSize + MRxNodeSize + NameLength), PoolTag); if (pNode != NULL) { ULONG NodeSize; PVOID *pContextPtr; PRX_PREFIX_ENTRY pPrefixEntry = NULL; NodeSize = RdbssNodeSize + MRxNodeSize; ZeroAndInitializeNodeType(pNode, NodeType, (NodeSize + NameLength)); switch (NodeType) { case RDBSS_NTC_SRVCALL: { PSRV_CALL pSrvCall = (PSRV_CALL)pNode; pContextPtr = &pSrvCall->Context; pPrefixEntry = &pSrvCall->PrefixEntry; // Set up the name pointer in the MRX_SRV_CALL structure .. pSrvCall->pSrvCallName = &pSrvCall->PrefixEntry.Prefix; } break; case RDBSS_NTC_NETROOT: { PNET_ROOT pNetRoot = (PNET_ROOT)pNode; pContextPtr = &pNetRoot->Context; pPrefixEntry = &pNetRoot->PrefixEntry; // Set up the net root name pointer in the MRX_NET_ROOT structure pNetRoot->pNetRootName = &pNetRoot->PrefixEntry.Prefix; } break; case RDBSS_NTC_V_NETROOT: { PV_NET_ROOT pVNetRoot = (PV_NET_ROOT)pNode; pContextPtr = &pVNetRoot->Context; pPrefixEntry = &pVNetRoot->PrefixEntry; } break; default: break; } if (pPrefixEntry != NULL) { ZeroAndInitializeNodeType( pPrefixEntry, RDBSS_NTC_PREFIX_ENTRY, sizeof( RX_PREFIX_ENTRY )); pPrefixEntry->Prefix.Buffer = (PWCH)((PCHAR)pNode + NodeSize); pPrefixEntry->Prefix.Length = (USHORT)NameLength; pPrefixEntry->Prefix.MaximumLength = (USHORT)NameLength; } if (MRxNodeSize > 0) { *pContextPtr = (PBYTE)pNode + RdbssNodeSize; } } return pNode; } VOID RxFreeObject(PVOID pObject) /*++ Routine Description: The routine frees a SRV_CALL/V_NET_ROOT/NET_ROOT instance Arguments: pObject - the instance to be freed Notes: --*/ { PAGED_CODE(); IF_DEBUG { switch (NodeType(pObject)) { case RDBSS_NTC_SRVCALL : { PSRV_CALL pSrvCall = (PSRV_CALL)pObject; if (pSrvCall->RxDeviceObject != NULL) { if (!(pSrvCall->RxDeviceObject->Dispatch->MRxFlags & RDBSS_MANAGE_SRV_CALL_EXTENSION)) { ASSERT(pSrvCall->Context == NULL); } ASSERT(pSrvCall->Context2 == NULL); pSrvCall->RxDeviceObject = NULL; } } break; case RDBSS_NTC_NETROOT : { PNET_ROOT pNetRoot = (PNET_ROOT)pObject; pNetRoot->SrvCall = NULL; //pNetRoot->Dispatch = NULL; pNetRoot->NodeTypeCode |= 0xf000; } break; case RDBSS_NTC_V_NETROOT : { } break; default: break; } } RxFreePool(pObject); } VOID RxFinalizeNetTable ( PRDBSS_DEVICE_OBJECT RxDeviceObject, BOOLEAN fForceFinalization ) /*++ Routine Description: This routine finalizes the net table. --*/ { BOOLEAN fMorePassesRequired = TRUE; PLIST_ENTRY pListEntry; NODE_TYPE_CODE DesiredNodeType; PRX_PREFIX_TABLE pRxNetNameTable = RxDeviceObject->pRxNetNameTable; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxForceNetTableFinalization at the TOP\n")); RxLog(("FINALNETT\n")); RxWmiLog(LOG, RxFinalizeNetTable_1, LOGPTR(RxDeviceObject)); RxAcquirePrefixTableLockExclusive( pRxNetNameTable, TRUE); //could be hosed if rogue! DesiredNodeType = RDBSS_NTC_V_NETROOT; RxAcquireScavengerMutex(); while (fMorePassesRequired) { for (pListEntry = pRxNetNameTable->MemberQueue.Flink; pListEntry != &(pRxNetNameTable->MemberQueue); ) { BOOLEAN NodeFinalized; PVOID pContainer; PRX_PREFIX_ENTRY PrefixEntry; PLIST_ENTRY pPrevEntry; PrefixEntry = CONTAINING_RECORD( pListEntry, RX_PREFIX_ENTRY, MemberQLinks ); ASSERT (NodeType(PrefixEntry) == RDBSS_NTC_PREFIX_ENTRY); pContainer = (PrefixEntry->ContainingRecord); RxDbgTrace(0, Dbg, ("RxForceNetTableFinalization ListEntry PrefixEntry Container" "=--> %08lx %08lx %08lx\n", pListEntry, PrefixEntry, pContainer)); RxLog(("FINALNETT: %lx %wZ\n", pContainer, &PrefixEntry->Prefix)); RxWmiLog(LOG, RxFinalizeNetTable_2, LOGPTR(pContainer) LOGUSTR(PrefixEntry->Prefix)); pListEntry = pListEntry->Flink; if (pContainer != NULL) { RxpUndoScavengerFinalizationMarking(pContainer); if (NodeType(pContainer) == DesiredNodeType) { switch (NodeType(pContainer)) { case RDBSS_NTC_SRVCALL : NodeFinalized = RxFinalizeSrvCall((PSRV_CALL)pContainer,TRUE,fForceFinalization); break; case RDBSS_NTC_NETROOT : NodeFinalized = RxFinalizeNetRoot((PNET_ROOT)pContainer,TRUE,fForceFinalization); break; case RDBSS_NTC_V_NETROOT : { PV_NET_ROOT pVNetRoot = (PV_NET_ROOT)pContainer; ULONG AdditionalReferenceTaken; AdditionalReferenceTaken = InterlockedCompareExchange( &pVNetRoot->AdditionalReferenceForDeleteFsctlTaken, 0, 1); if (AdditionalReferenceTaken) { RxDereferenceVNetRoot(pVNetRoot,LHS_ExclusiveLockHeld); NodeFinalized = TRUE; } else { NodeFinalized = RxFinalizeVNetRoot((PV_NET_ROOT)pContainer,TRUE,fForceFinalization); } } break; } } } } switch (DesiredNodeType) { case RDBSS_NTC_SRVCALL : fMorePassesRequired = FALSE; break; case RDBSS_NTC_NETROOT : DesiredNodeType = RDBSS_NTC_SRVCALL; break; case RDBSS_NTC_V_NETROOT : DesiredNodeType = RDBSS_NTC_NETROOT; break; } } RxDbgTrace(-1,Dbg,("RxFinalizeNetTable -- Done\n")); RxReleaseScavengerMutex(); RxReleasePrefixTableLock( pRxNetNameTable ); } NTSTATUS RxFinalizeConnection( IN OUT PNET_ROOT NetRoot, IN OUT PV_NET_ROOT VNetRoot, IN BOOLEAN Level ) /*++ Routine Description: The routine deletes a connection FROM THE USER's PERSPECTIVE. It doesn't disconnect but it does (with force) close open files. disconnecting is handled either by timeout or by srvcall finalization. Arguments: NetRoot - the NetRoot being finalized VNetRoot - the VNetRoot being finalized Level - This is a tri-state FALSE - fail if files or changenotifications are open TRUE - succeed no matter waht. orphan files and remove change notifies forcefully 0xff - take away extra reference on the vnetroot due to add_connection but otherwise act like FALSE Return Value: RxStatus(SUCCESS) if successful. --*/ { NTSTATUS Status = STATUS_SUCCESS; ULONG NumberOfOpenDirectories = 0; ULONG NumberOfOpenNonDirectories = 0; ULONG NumberOfFobxs = 0; LONG AdditionalReferenceForDeleteFsctlTaken = 0; PLIST_ENTRY ListEntry, NextListEntry; BOOLEAN PrefixTableLockAcquired,FcbTableLockAcquired; PRX_PREFIX_TABLE pRxNetNameTable; BOOLEAN ForceFilesClosed = FALSE; if (Level==TRUE) { ForceFilesClosed = TRUE; } PAGED_CODE(); ASSERT( NodeType(NetRoot) == RDBSS_NTC_NETROOT ); pRxNetNameTable = NetRoot->SrvCall->RxDeviceObject->pRxNetNameTable; Status = RxCancelNotifyChangeDirectoryRequestsForVNetRoot(VNetRoot, ForceFilesClosed); // either changenotifications were cancelled, or they weren't but we still want to // do go through in order to either forceclose the files or atleast deref the vnetroot // of the extra ref taken during ADD_CONNECTION if ((Status == STATUS_SUCCESS) || (Level != FALSE)) { // reset the status Status = STATUS_SUCCESS; PrefixTableLockAcquired = RxAcquirePrefixTableLockExclusive( pRxNetNameTable, TRUE); //don't let the netroot be finalized yet....... RxReferenceNetRoot(NetRoot); FcbTableLockAcquired = RxAcquireFcbTableLockExclusive( &NetRoot->FcbTable, TRUE); try { if ((Status == STATUS_SUCCESS) && (!VNetRoot->ConnectionFinalizationDone)) { USHORT BucketNumber; RxDbgTrace(+1, Dbg, ("RxFinalizeConnection<+> NR= %08lx VNR= %08lx %wZ\n", NetRoot,VNetRoot,&NetRoot->PrefixEntry.Prefix)); RxLog(("FINALCONN: %lx %wZ\n",NetRoot,&NetRoot->PrefixEntry.Prefix)); RxWmiLog(LOG, RxFinalizeConnection, LOGPTR(NetRoot) LOGUSTR(NetRoot->PrefixEntry.Prefix)); for (BucketNumber = 0; (BucketNumber < NetRoot->FcbTable.NumberOfBuckets); BucketNumber++) { PLIST_ENTRY ListHeader; ListHeader = &NetRoot->FcbTable.HashBuckets[BucketNumber]; for (ListEntry = ListHeader->Flink; ListEntry != ListHeader; ListEntry = NextListEntry ) { PFCB Fcb; PRX_FCB_TABLE_ENTRY FcbTableEntry; NextListEntry = ListEntry->Flink; FcbTableEntry = CONTAINING_RECORD( ListEntry, RX_FCB_TABLE_ENTRY, HashLinks ); Fcb = CONTAINING_RECORD( FcbTableEntry, FCB, FcbTableEntry); if (Fcb->VNetRoot != VNetRoot) { continue; } if (Fcb->UncleanCount>0 && !ForceFilesClosed) { Status = STATUS_CONNECTION_IN_USE; //this is changed later if (NodeType(Fcb)==RDBSS_NTC_STORAGE_TYPE_DIRECTORY) { NumberOfOpenDirectories++; } else { NumberOfOpenNonDirectories++; } continue; } ASSERT( NodeTypeIsFcb(Fcb)); RxDbgTrace( 0, Dbg, (" AcquiringFcbLock%c!!\n", '!')); Status = RxAcquireExclusiveFcb(NULL,Fcb); ASSERT(Status == STATUS_SUCCESS); RxDbgTrace( 0, Dbg, (" AcquiredFcbLock%c!!\n", '!')); // Ensure that no more file objects will be marked for a delayed close // on this FCB. ClearFlag(Fcb->FcbState,FCB_STATE_COLLAPSING_ENABLED); RxScavengeRelatedFobxs(Fcb); // a small complication here is that this fcb MAY have an open // section against it caused by our cacheing the file. if so, // we need to purge to get to the close RxPurgeFcb(Fcb); } } if (VNetRoot->NumberOfFobxs == 0) { VNetRoot->ConnectionFinalizationDone = TRUE; } } NumberOfFobxs = VNetRoot->NumberOfFobxs; AdditionalReferenceForDeleteFsctlTaken = VNetRoot->AdditionalReferenceForDeleteFsctlTaken; if (ForceFilesClosed) { RxFinalizeVNetRoot(VNetRoot,FALSE,TRUE); } } finally { if (FcbTableLockAcquired) { RxReleaseFcbTableLock( &NetRoot->FcbTable ); } // We should not delete the remote connection with the file opened. if (!ForceFilesClosed && (Status == STATUS_SUCCESS) && (NumberOfFobxs > 0)) { Status = STATUS_FILES_OPEN; } if (Status != STATUS_SUCCESS) { if (NumberOfOpenNonDirectories) { Status = STATUS_FILES_OPEN; } } if ((Status == STATUS_SUCCESS)||(Level==0xff)){ // the corresponding reference for this is in RxCreateTreeConnect... // please see the comment there... if (AdditionalReferenceForDeleteFsctlTaken != 0) { VNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0; RxDereferenceVNetRoot(VNetRoot,LHS_ExclusiveLockHeld); } } if (PrefixTableLockAcquired) { RxDereferenceNetRoot(NetRoot,LHS_ExclusiveLockHeld); RxReleasePrefixTableLock( pRxNetNameTable ); } } RxDbgTrace(-1, Dbg, ("RxFinalizeConnection<-> Status=%08lx\n", Status)); } return(Status); } NTSTATUS RxInitializeSrvCallParameters( PRX_CONTEXT RxContext, PSRV_CALL SrvCall) /*++ Routine Description: This routine initializes the server call parameters passed in through EA's Currently this routine initializes the Server principal name which is passed in by the DFS driver. Arguments: RxContext -- the associated context SrvCall -- the Srv Call Instance Return Value: RxStatus(SUCCESS) if successfull Notes: The current implementation maps out of memory situations into an error and passes it back. If the global strategy is to raise an exception this redundant step can be avoided. --*/ { NTSTATUS Status = (STATUS_SUCCESS); RxCaptureRequestPacket; RxCaptureParamBlock; ULONG EaInformationLength; PAGED_CODE(); SrvCall->pPrincipalName = NULL; if (RxContext->MajorFunction != IRP_MJ_CREATE) { return STATUS_SUCCESS; } EaInformationLength = RxContext->Create.EaLength; if (EaInformationLength > 0) { PFILE_FULL_EA_INFORMATION pEaEntry; pEaEntry = (PFILE_FULL_EA_INFORMATION)RxContext->Create.EaBuffer; ASSERT(pEaEntry != NULL); for(;;) { RxDbgTrace(0,Dbg,("RxExtractSrvCallParams: Processing EA name %s\n", pEaEntry->EaName)); if (strcmp(pEaEntry->EaName, EA_NAME_PRINCIPAL) == 0) { if (pEaEntry->EaValueLength > 0) { SrvCall->pPrincipalName = (PUNICODE_STRING) RxAllocatePoolWithTag( NonPagedPool, (sizeof(UNICODE_STRING) + pEaEntry->EaValueLength), RX_SRVCALL_PARAMS_POOLTAG); if (SrvCall->pPrincipalName != NULL) { SrvCall->pPrincipalName->Length = pEaEntry->EaValueLength; SrvCall->pPrincipalName->MaximumLength = pEaEntry->EaValueLength; SrvCall->pPrincipalName->Buffer = (PWCHAR)((PCHAR)SrvCall->pPrincipalName + sizeof(UNICODE_STRING)); RtlCopyMemory( SrvCall->pPrincipalName->Buffer, pEaEntry->EaName + pEaEntry->EaNameLength + 1, SrvCall->pPrincipalName->Length); } else { Status = (STATUS_INSUFFICIENT_RESOURCES); } } break; } if (pEaEntry->NextEntryOffset == 0) { break; } else { pEaEntry = (PFILE_FULL_EA_INFORMATION) ((PCHAR) pEaEntry + pEaEntry->NextEntryOffset); } } } return Status; } PSRV_CALL RxCreateSrvCall ( IN PRX_CONTEXT RxContext, IN PUNICODE_STRING Name, IN PUNICODE_STRING InnerNamePrefix OPTIONAL, IN PRX_CONNECTION_ID RxConnectionId ) /*++ Routine Description: The routine builds a node representing a server call context and inserts the name into the net name table. Optionally, it "co-allocates" a netroot structure as well. Appropriate alignment is respected for the enclosed netroot. The name(s) is(are) allocated at the end of the block. The reference count on the block is set to 1 (2 if enclosed netroot) on this create to account for ptr returned. Arguments: RxContext - the RDBSS context Name - the name to be inserted Dispatch - pointer to the minirdr dispatch table Return Value: Ptr to the created srvcall. --*/ { PSRV_CALL ThisSrvCall; PRX_PREFIX_ENTRY ThisEntry; ULONG NameSize,PrefixNameSize; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxSrvCallCreate--> Name = %wZ\n", Name)); ASSERT ( RxIsPrefixTableLockExclusive ( RxContext->RxDeviceObject->pRxNetNameTable ) ); NameSize = Name->Length + sizeof(WCHAR) * 2; if (InnerNamePrefix) { PrefixNameSize = InnerNamePrefix->Length; } else { PrefixNameSize = 0; } ThisSrvCall = RxAllocateObject(RDBSS_NTC_SRVCALL,NULL,(NameSize + PrefixNameSize)); if (ThisSrvCall != NULL) { ThisSrvCall->SerialNumberForEnum = SerialNumber++; ThisSrvCall->RxDeviceObject = RxContext->RxDeviceObject; RxInitializeBufferingManager(ThisSrvCall); InitializeListHead(&ThisSrvCall->ScavengerFinalizationList); InitializeListHead(&ThisSrvCall->TransitionWaitList); RxInitializePurgeSyncronizationContext( &ThisSrvCall->PurgeSyncronizationContext); #ifndef WIN9X RxInitializeSrvCallParameters(RxContext,ThisSrvCall); #endif RtlMoveMemory( ThisSrvCall->PrefixEntry.Prefix.Buffer, Name->Buffer, Name->Length); ThisEntry = &ThisSrvCall->PrefixEntry; ThisEntry->Prefix.MaximumLength = (USHORT)NameSize; ThisEntry->Prefix.Length = Name->Length; RxPrefixTableInsertName ( RxContext->RxDeviceObject->pRxNetNameTable, ThisEntry, (PVOID)ThisSrvCall, &ThisSrvCall->NodeReferenceCount, Name->Length, RxConnectionId); //make the whole srvcallname case insensitive RxDbgTrace(-1, Dbg, ("RxSrvCallCreate -> RefCount = %08lx\n", ThisSrvCall->NodeReferenceCount)); } return ThisSrvCall; } NTSTATUS RxSetSrvCallDomainName( PMRX_SRV_CALL pSrvCall, PUNICODE_STRING pDomainName) /*++ Routine Description: The routine sets the domain name associated with any given server. Arguments: pSrvCall - the SrvCall pDomainName - the DOMAIN to which the server belongs. Return Value: RxStatus(SUCCESS) if successful Notes: This is one of the callback routines provided in the wrapper for the mini redirectors. Since the Domain name is not often known at the beginning this mechanism has to be adopted once it is found. --*/ { NTSTATUS Status = (STATUS_SUCCESS); PAGED_CODE(); if (pSrvCall->pDomainName != NULL) { RxFreePool(pSrvCall->pDomainName); } if (pDomainName != NULL && pDomainName->Length > 0) { pSrvCall->pDomainName = (PUNICODE_STRING) RxAllocatePoolWithTag( NonPagedPool, sizeof(UNICODE_STRING) + pDomainName->Length + sizeof(WCHAR), RX_SRVCALL_PARAMS_POOLTAG); if (pSrvCall->pDomainName != NULL) { pSrvCall->pDomainName->Buffer = (PWCHAR)((PCHAR)pSrvCall->pDomainName + sizeof(UNICODE_STRING)); pSrvCall->pDomainName->Length = pDomainName->Length; pSrvCall->pDomainName->MaximumLength = pDomainName->Length; *pSrvCall->pDomainName->Buffer = 0; if (pSrvCall->pDomainName->Length > 0) { RtlCopyMemory( pSrvCall->pDomainName->Buffer, pDomainName->Buffer, pDomainName->Length); } } else { Status = (STATUS_INSUFFICIENT_RESOURCES); } } else { pSrvCall->pDomainName = NULL; } return Status; } VOID RxpDestroySrvCall( PSRV_CALL ThisSrvCall) /*++ Routine Description: This routine is used to tear down a SRV_CALL entry. This code is offloaded from the RxFinalizeCall routine to avoid having to hold the Name Table Lock for extended periods of time while the mini redirector is finalizing its data structures. Arguments: ThisSrvCall - the SrvCall being finalized Notes: there is no recursive part because i don't have a list of srvcalls and a list of netroots i only have a combined list. thus, recursive finalization of netroots is directly from the top level. however, all netroots should already have been done when i get here.. --*/ { NTSTATUS Status; BOOLEAN ForceFinalize; PRDBSS_DEVICE_OBJECT RxDeviceObject = ThisSrvCall->RxDeviceObject; PRX_PREFIX_TABLE pRxNetNameTable = RxDeviceObject->pRxNetNameTable; ASSERT(ThisSrvCall->UpperFinalizationDone); ForceFinalize = BooleanFlagOn( ThisSrvCall->Flags, SRVCALL_FLAG_FORCE_FINALIZED); //we have to protect this call since the srvcall may never have been claimed MINIRDR_CALL_THROUGH( Status, RxDeviceObject->Dispatch, MRxFinalizeSrvCall,((PMRX_SRV_CALL)ThisSrvCall,ForceFinalize) ); RxAcquirePrefixTableLockExclusive( pRxNetNameTable, TRUE); InterlockedDecrement(&ThisSrvCall->NodeReferenceCount); RxFinalizeSrvCall( ThisSrvCall, FALSE, ForceFinalize); RxReleasePrefixTableLock(pRxNetNameTable); } BOOLEAN RxFinalizeSrvCall ( OUT PSRV_CALL ThisSrvCall, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize ) /*++ Routine Description: The routine finalizes the given netroot. You should have exclusive on the netname tablelock. Arguments: ThisSrvCall - the SrvCall being finalized Return Value: BOOLEAN - tells whether finalization actually occured Notes: there is no recursive part because i don't have a list of srvcalls and a list of netroots i only have a combined list. thus, recursive finalization of netroots is directly from the top level. however, all netroots should already have been done when i get here.. --*/ { BOOLEAN NodeActuallyFinalized = FALSE; PRX_PREFIX_TABLE pRxNetNameTable; PAGED_CODE(); ASSERT( NodeType(ThisSrvCall) == RDBSS_NTC_SRVCALL ); pRxNetNameTable = ThisSrvCall->RxDeviceObject->pRxNetNameTable; ASSERT( RxIsPrefixTableLockExclusive( pRxNetNameTable )); RxDbgTrace(+1, Dbg, ("RxFinalizeSrvCall<+> %08lx %wZ RefC=%ld\n", ThisSrvCall,&ThisSrvCall->PrefixEntry.Prefix, ThisSrvCall->NodeReferenceCount)); if( ThisSrvCall->NodeReferenceCount == 1 || ForceFinalize ) { BOOLEAN DeferFinalizationToWorkerThread = FALSE; RxLog(("FINALSRVC: %lx %wZ\n",ThisSrvCall,&ThisSrvCall->PrefixEntry.Prefix)); RxWmiLog(LOG, RxFinalizeSrvCall, LOGPTR(ThisSrvCall) LOGUSTR(ThisSrvCall->PrefixEntry.Prefix)); if (!ThisSrvCall->UpperFinalizationDone) { NTSTATUS Status; RxRemovePrefixTableEntry ( pRxNetNameTable, &ThisSrvCall->PrefixEntry); if (ForceFinalize) { ThisSrvCall->Flags |= SRVCALL_FLAG_FORCE_FINALIZED; } ThisSrvCall->UpperFinalizationDone = TRUE; if (ThisSrvCall->NodeReferenceCount == 1) { NodeActuallyFinalized = TRUE; } if (ThisSrvCall->RxDeviceObject != NULL) { if (IoGetCurrentProcess() != RxGetRDBSSProcess()) { InterlockedIncrement(&ThisSrvCall->NodeReferenceCount); RxDispatchToWorkerThread( ThisSrvCall->RxDeviceObject, DelayedWorkQueue, RxpDestroySrvCall, ThisSrvCall); DeferFinalizationToWorkerThread = TRUE; } else { MINIRDR_CALL_THROUGH( Status, ThisSrvCall->RxDeviceObject->Dispatch, MRxFinalizeSrvCall,((PMRX_SRV_CALL)ThisSrvCall,ForceFinalize) ); } } } if (!DeferFinalizationToWorkerThread) { if( ThisSrvCall->NodeReferenceCount == 1 ) { if (ThisSrvCall->pDomainName != NULL) { RxFreePool(ThisSrvCall->pDomainName); } RxTearDownBufferingManager(ThisSrvCall); RxFreeObject(ThisSrvCall); NodeActuallyFinalized = TRUE; } } } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeSrvCall<-> %08lx\n", ThisSrvCall, NodeActuallyFinalized)); return NodeActuallyFinalized; } PNET_ROOT RxCreateNetRoot ( IN PSRV_CALL SrvCall, IN PUNICODE_STRING Name, IN ULONG NetRootFlags, IN PRX_CONNECTION_ID RxConnectionId ) /*++ Routine Description: The routine builds a node representing a netroot and inserts the name into the net name table. The name is allocated at the end of the block. The reference count on the block is set to 1 on this create.... Arguments: SrvCall - the associated server call context; may be NULL!! (but not right now.........) Dispatch - the minirdr dispatch table Name - the name to be inserted Return Value: Ptr to the created net root. --*/ { PNET_ROOT ThisNetRoot; PRX_PREFIX_TABLE pRxNetNameTable; ULONG NameSize,SrvCallNameSize; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxNetRootCreate--> Name = %wZ\n", Name)); ASSERT (SrvCall != NULL); pRxNetNameTable = SrvCall->RxDeviceObject->pRxNetNameTable; ASSERT ( RxIsPrefixTableLockExclusive ( pRxNetNameTable ) ); SrvCallNameSize = SrvCall->PrefixEntry.Prefix.Length; NameSize = Name->Length + SrvCallNameSize; ThisNetRoot = RxAllocateObject( RDBSS_NTC_NETROOT, SrvCall->RxDeviceObject->Dispatch, NameSize); if (ThisNetRoot != NULL) { USHORT CaseInsensitiveLength; RtlMoveMemory( (PCHAR)(ThisNetRoot->PrefixEntry.Prefix.Buffer) + SrvCallNameSize, Name->Buffer, Name->Length); if (SrvCallNameSize) { RtlMoveMemory( ThisNetRoot->PrefixEntry.Prefix.Buffer, SrvCall->PrefixEntry.Prefix.Buffer, SrvCallNameSize); } if (FlagOn(SrvCall->Flags,SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS)) { CaseInsensitiveLength = (USHORT)NameSize; } else { CaseInsensitiveLength = SrvCall->PrefixEntry.CaseInsensitiveLength; } RxPrefixTableInsertName ( pRxNetNameTable, &ThisNetRoot->PrefixEntry, (PVOID)ThisNetRoot, &ThisNetRoot->NodeReferenceCount, CaseInsensitiveLength, RxConnectionId); RxInitializeFcbTable(&ThisNetRoot->FcbTable, TRUE); InitializeListHead(&ThisNetRoot->VirtualNetRoots); InitializeListHead(&ThisNetRoot->TransitionWaitList); InitializeListHead(&ThisNetRoot->ScavengerFinalizationList); RxInitializePurgeSyncronizationContext( &ThisNetRoot->PurgeSyncronizationContext); ThisNetRoot->SerialNumberForEnum = SerialNumber++; ThisNetRoot->Flags |= NetRootFlags; ThisNetRoot->DiskParameters.ClusterSize = 1; ThisNetRoot->DiskParameters.ReadAheadGranularity = DEFAULT_READ_AHEAD_GRANULARITY; ThisNetRoot->SrvCall = SrvCall; //already have the lock RxReferenceSrvCall((PSRV_CALL)ThisNetRoot->SrvCall); } return ThisNetRoot; } BOOLEAN RxFinalizeNetRoot ( OUT PNET_ROOT ThisNetRoot, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize ) /*++ Routine Description: The routine finalizes the given netroot. You must be exclusive on the NetName tablelock. Arguments: ThisNetRoot - the NetRoot being dereferenced Return Value: BOOLEAN - tells whether finalization actually occured --*/ { NTSTATUS Status; BOOLEAN NodeActuallyFinalized = FALSE; PRX_PREFIX_TABLE pRxNetNameTable; PAGED_CODE(); ASSERT( NodeType(ThisNetRoot) == RDBSS_NTC_NETROOT ); pRxNetNameTable = ThisNetRoot->SrvCall->RxDeviceObject->pRxNetNameTable; ASSERT ( RxIsPrefixTableLockExclusive ( pRxNetNameTable ) ); if (ThisNetRoot->Flags & NETROOT_FLAG_FINALIZATION_IN_PROGRESS) { return FALSE; } // Since the table lock has been acquired exclusive the flags can be modified // without any further synchronization since the protection is against recursive // invocations. ThisNetRoot->Flags |= NETROOT_FLAG_FINALIZATION_IN_PROGRESS; RxDbgTrace(+1, Dbg, ("RxFinalizeNetRoot<+> %08lx %wZ RefC=%ld\n", ThisNetRoot,&ThisNetRoot->PrefixEntry.Prefix, ThisNetRoot->NodeReferenceCount)); if (RecursiveFinalize) { PLIST_ENTRY ListEntry; USHORT BucketNumber; RxAcquireFcbTableLockExclusive(&ThisNetRoot->FcbTable,TRUE); IF_DEBUG{ if ( FALSE && ThisNetRoot->NodeReferenceCount){ RxDbgTrace(0, Dbg, (" BAD!!!!!ReferenceCount = %08lx\n", ThisNetRoot->NodeReferenceCount)); } } for (BucketNumber = 0; (BucketNumber < ThisNetRoot->FcbTable.NumberOfBuckets); BucketNumber++) { PLIST_ENTRY ListHeader; ListHeader = &ThisNetRoot->FcbTable.HashBuckets[BucketNumber]; for (ListEntry = ListHeader->Flink; ListEntry != ListHeader; ) { PFCB Fcb; PRX_FCB_TABLE_ENTRY pFcbTableEntry; pFcbTableEntry = CONTAINING_RECORD( ListEntry, RX_FCB_TABLE_ENTRY, HashLinks); Fcb = CONTAINING_RECORD( pFcbTableEntry, FCB, FcbTableEntry); ListEntry = ListEntry->Flink; ASSERT( NodeTypeIsFcb(Fcb)); if (!FlagOn(Fcb->FcbState,FCB_STATE_ORPHANED)) { Status = RxAcquireExclusiveFcb(NULL,Fcb); ASSERT(Status == (STATUS_SUCCESS)); // a small complication here is that this fcb MAY have an open section against it caused // by our cacheing the file. if so, we need to purge to get to the close // wrong//if so, we have to get rid of it and then standoff to let // the close go thru. sigh............. RxPurgeFcb(Fcb); } } } RxReleaseFcbTableLock( &ThisNetRoot->FcbTable ); } if ( ThisNetRoot->NodeReferenceCount == 1 || ForceFinalize ){ RxLog(("FINALNETROOT: %lx %wZ\n",ThisNetRoot,&ThisNetRoot->PrefixEntry.Prefix)); RxWmiLog(LOG, RxFinalizeNetRoot, LOGPTR(ThisNetRoot) LOGUSTR(ThisNetRoot->PrefixEntry.Prefix)); //if (!ThisNetRoot->UpperFinalizationDone) { // NOTHING; // ThisNetRoot->UpperFinalizationDone = TRUE; //} if ( ThisNetRoot->NodeReferenceCount == 1 ){ PSRV_CALL SrvCall = (PSRV_CALL)ThisNetRoot->SrvCall; RxFinalizeFcbTable(&ThisNetRoot->FcbTable); if (!FlagOn(ThisNetRoot->Flags,NETROOT_FLAG_NAME_ALREADY_REMOVED)) { RxRemovePrefixTableEntry ( pRxNetNameTable, &ThisNetRoot->PrefixEntry); } RxFreeObject(ThisNetRoot); if (SrvCall != NULL) { RxDereferenceSrvCall(SrvCall,LHS_ExclusiveLockHeld); //already have the lock } NodeActuallyFinalized = TRUE; } } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeNetRoot<-> %08lx\n", ThisNetRoot, NodeActuallyFinalized)); return NodeActuallyFinalized; } VOID RxAddVirtualNetRootToNetRoot( PNET_ROOT pNetRoot, PV_NET_ROOT pVNetRoot) /*++ Routine Description: The routine adds a VNetRoot to the list of VNetRoot's associated with a NetRoot Arguments: pNetRoot - the NetRoot pVNetRoot - the new VNetRoot to be added to the list. Notes: The reference count associated with a NetRoot will be equal to the number of VNetRoot's associated with it plus 1. the last one being for the prefix name table. This ensures that a NetRoot cannot be finalized till all the VNetRoots associated with it have been finalized. --*/ { PAGED_CODE(); ASSERT(RxIsPrefixTableLockExclusive( pNetRoot->SrvCall->RxDeviceObject->pRxNetNameTable )); pVNetRoot->NetRoot = pNetRoot; pNetRoot->NumberOfVirtualNetRoots++; if (pNetRoot->DefaultVNetRoot == NULL) { //pNetRoot->DefaultVNetRoot = pVNetRoot; } InsertTailList(&pNetRoot->VirtualNetRoots,&pVNetRoot->NetRootListEntry); } VOID RxRemoveVirtualNetRootFromNetRoot( PNET_ROOT pNetRoot, PV_NET_ROOT pVNetRoot) /*++ Routine Description: The routine removes a VNetRoot to the list of VNetRoot's associated with a NetRoot Arguments: pNetRoot - the NetRoot pVNetRoot - the VNetRoot to be removed from the list. Notes: The reference count associated with a NetRoot will be equal to the number of VNetRoot's associated with it plus 1. the last one being for the prefix name table. This ensures that a NetRoot cannot be finalized till all the VNetRoots associated with it have been finalized. --*/ { PRX_PREFIX_TABLE pRxNetNameTable = pNetRoot->SrvCall->RxDeviceObject->pRxNetNameTable; PAGED_CODE(); ASSERT(RxIsPrefixTableLockExclusive( pRxNetNameTable )); pNetRoot->NumberOfVirtualNetRoots--; RemoveEntryList(&pVNetRoot->NetRootListEntry); if (pNetRoot->DefaultVNetRoot == pVNetRoot) { if (!IsListEmpty(&pNetRoot->VirtualNetRoots)) { // Traverse the list and pick another default net root. PV_NET_ROOT pTempVNetRoot; pTempVNetRoot = (PV_NET_ROOT) CONTAINING_RECORD( pNetRoot->VirtualNetRoots.Flink, V_NET_ROOT, NetRootListEntry); pNetRoot->DefaultVNetRoot = pTempVNetRoot; } else { pNetRoot->DefaultVNetRoot = NULL; } } if (IsListEmpty(&pNetRoot->VirtualNetRoots)) { NTSTATUS Status; if (!FlagOn(pNetRoot->Flags,NETROOT_FLAG_NAME_ALREADY_REMOVED)) { RxRemovePrefixTableEntry(pRxNetNameTable, &pNetRoot->PrefixEntry); SetFlag(pNetRoot->Flags,NETROOT_FLAG_NAME_ALREADY_REMOVED); } //ASSERT(pNetRoot->Dispatch != NULL); if ((pNetRoot->SrvCall != NULL) && (pNetRoot->SrvCall->RxDeviceObject!=NULL)) { MINIRDR_CALL_THROUGH( Status, pNetRoot->SrvCall->RxDeviceObject->Dispatch, MRxFinalizeNetRoot,((PMRX_NET_ROOT)pNetRoot,NULL) ); } } } NTSTATUS RxInitializeVNetRootParameters( PRX_CONTEXT RxContext, LUID *pLogonId, ULONG *pSessionId, PUNICODE_STRING *pUserNamePtr, PUNICODE_STRING *pUserDomainNamePtr, PUNICODE_STRING *pPasswordPtr, ULONG *pFlags ) /*++ Routine Description: This routine extracts the ea parameters specified Arguments: RxContext -- the RxContext pLogonId -- the logon Id. pUserNamePtr -- pointer to the User Name pUserDomainNamePtr -- pointer to the user domain name pPasswordPtr -- the password. Return Value: STATUS_SUCCESS -- successful, appropriate NTSTATUS code otherwise Notes: --*/ { NTSTATUS Status = STATUS_SUCCESS; RxCaptureRequestPacket; RxCaptureParamBlock; PIO_SECURITY_CONTEXT pSecurityContext; PACCESS_TOKEN pAccessToken; PAGED_CODE(); pSecurityContext = RxContext->Create.NtCreateParameters.SecurityContext; pAccessToken = SeQuerySubjectContextToken( &pSecurityContext->AccessState->SubjectSecurityContext); *pPasswordPtr = NULL; *pUserDomainNamePtr = NULL; *pUserNamePtr = NULL; *pFlags &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE; if (!SeTokenIsRestricted(pAccessToken)) { Status = SeQueryAuthenticationIdToken( pAccessToken, pLogonId); if (Status == STATUS_SUCCESS) { Status = SeQuerySessionIdToken( pAccessToken, pSessionId); } if ((Status == STATUS_SUCCESS) && (RxContext->Create.UserName.Buffer != NULL)) { PUNICODE_STRING pTargetString; pTargetString = RxAllocatePoolWithTag( NonPagedPool, (sizeof(UNICODE_STRING) + RxContext->Create.UserName.Length), RX_SRVCALL_PARAMS_POOLTAG); if (pTargetString != NULL) { pTargetString->Length = RxContext->Create.UserName.Length; pTargetString->MaximumLength = RxContext->Create.UserName.MaximumLength; if (pTargetString->Length > 0) { pTargetString->Buffer = (PWCHAR)((PCHAR)pTargetString + sizeof(UNICODE_STRING)); RtlCopyMemory( pTargetString->Buffer, RxContext->Create.UserName.Buffer, pTargetString->Length); } else { pTargetString->Buffer = NULL; } *pUserNamePtr = pTargetString; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if ((RxContext->Create.UserDomainName.Buffer != NULL) && (Status == STATUS_SUCCESS)) { PUNICODE_STRING pTargetString; pTargetString = RxAllocatePoolWithTag( NonPagedPool, (sizeof(UNICODE_STRING) + RxContext->Create.UserDomainName.Length + sizeof(WCHAR)), RX_SRVCALL_PARAMS_POOLTAG); if (pTargetString != NULL) { pTargetString->Length = RxContext->Create.UserDomainName.Length; pTargetString->MaximumLength = RxContext->Create.UserDomainName.MaximumLength; pTargetString->Buffer = (PWCHAR)((PCHAR)pTargetString + sizeof(UNICODE_STRING)); // in case of UPN name, domain name will be a NULL string *pTargetString->Buffer = 0; if (pTargetString->Length > 0) { RtlCopyMemory( pTargetString->Buffer, RxContext->Create.UserDomainName.Buffer, pTargetString->Length); } *pUserDomainNamePtr = pTargetString; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if ((RxContext->Create.Password.Buffer != NULL) && (Status == STATUS_SUCCESS)) { PUNICODE_STRING pTargetString; pTargetString = RxAllocatePoolWithTag( NonPagedPool, (sizeof(UNICODE_STRING) + RxContext->Create.Password.Length), RX_SRVCALL_PARAMS_POOLTAG); if (pTargetString != NULL) { pTargetString->Length = RxContext->Create.Password.Length; pTargetString->MaximumLength = RxContext->Create.Password.MaximumLength; if (pTargetString->Length > 0) { pTargetString->Buffer = (PWCHAR)((PCHAR)pTargetString + sizeof(UNICODE_STRING)); RtlCopyMemory( pTargetString->Buffer, RxContext->Create.Password.Buffer, pTargetString->Length); } else { pTargetString->Buffer = NULL; } *pPasswordPtr = pTargetString; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } if (Status == STATUS_SUCCESS) { if(RxIsThisACscAgentOpen(RxContext)) { *pFlags |= VNETROOT_FLAG_CSCAGENT_INSTANCE; } } if (Status != STATUS_SUCCESS) { if (*pUserNamePtr != NULL) { RxFreePool(*pUserNamePtr); *pUserNamePtr = NULL; } if (*pUserDomainNamePtr != NULL) { RxFreePool(*pUserDomainNamePtr); *pUserDomainNamePtr = NULL; } if (*pPasswordPtr != NULL) { RxFreePool(*pPasswordPtr); *pPasswordPtr = NULL; } } } else { Status = STATUS_ACCESS_DENIED; } return Status; } VOID RxUninitializeVNetRootParameters( PUNICODE_STRING pUserName, PUNICODE_STRING pUserDomainName, PUNICODE_STRING pPassword, ULONG *lpFlags ) /*++ Routine Description: This routine unintializes the parameters ( logon ) associated with a VNetRoot Arguments: pVNetRoot -- the VNetRoot --*/ { PAGED_CODE(); if (pUserName != NULL) { RxFreePool(pUserName); } if (pUserDomainName != NULL) { RxFreePool(pUserDomainName); } if (pPassword != NULL) { RxFreePool(pPassword); } if (lpFlags) { *lpFlags &= ~VNETROOT_FLAG_CSCAGENT_INSTANCE; } } PV_NET_ROOT RxCreateVNetRoot ( IN PRX_CONTEXT RxContext, IN PNET_ROOT NetRoot, IN PUNICODE_STRING CanonicalName, IN PUNICODE_STRING LocalNetRootName, IN PUNICODE_STRING FilePath, IN PRX_CONNECTION_ID RxConnectionId ) /*++ Routine Description: The routine builds a node representing a virtual netroot and inserts the name into the net name table. The name is allocated at the end of the block. The reference count on the block is set to 1 on this create.... Virtual netroots provide a mechanism for mapping "into" a share....i.e. having a user drive that points not at the root of the associated share point. The format of a name is either \server\share\d1\d2..... or \;m:\server\share\d1\d2..... depending on whether there is a local device ("m:") associated with this vnetroot. In the latter case is that \d1\d2.. gets prefixed onto each createfile that is opened on this vnetroot. vnetroot's are also used to supply alternate credentials. the point of the former kind of vnetroot is to propagate the credentials into the netroot as the default. for this to work, there must be no other references. You need to have the lock exclusive to call....see RxCreateSrvCall....... Arguments: RxContext - the RDBSS context NetRoot - the associated net root context Name - the name to be inserted NamePrefixOffsetInBytes - offset into the name where the prefix starts Return Value: Ptr to the created v net root. --*/ { NTSTATUS Status = STATUS_SUCCESS; PV_NET_ROOT ThisVNetRoot; UNICODE_STRING VNetRootName; PUNICODE_STRING ThisNamePrefix; ULONG NameSize; BOOLEAN fCscAgent = FALSE; PRX_PREFIX_ENTRY ThisEntry; PAGED_CODE(); ASSERT (RxIsPrefixTableLockExclusive( RxContext->RxDeviceObject->pRxNetNameTable )); NameSize = NetRoot->PrefixEntry.Prefix.Length + LocalNetRootName->Length; ThisVNetRoot = RxAllocateObject(RDBSS_NTC_V_NETROOT,NetRoot->SrvCall->RxDeviceObject->Dispatch,NameSize); if (ThisVNetRoot != NULL) { USHORT CaseInsensitiveLength; PMRX_SRV_CALL SrvCall; if (Status == STATUS_SUCCESS) { // Initialize the Create Parameters Status = RxInitializeVNetRootParameters( RxContext, &ThisVNetRoot->LogonId, &ThisVNetRoot->SessionId, &ThisVNetRoot->pUserName, &ThisVNetRoot->pUserDomainName, &ThisVNetRoot->pPassword, &ThisVNetRoot->Flags ); } if (Status == STATUS_SUCCESS) { VNetRootName = ThisVNetRoot->PrefixEntry.Prefix; RtlMoveMemory( VNetRootName.Buffer, CanonicalName->Buffer, VNetRootName.Length); ThisVNetRoot->PrefixOffsetInBytes = LocalNetRootName->Length + NetRoot->PrefixEntry.Prefix.Length; RxDbgTrace(+1, Dbg, ("RxVNetRootCreate--> Name = <%wZ>, offs=%08lx\n", CanonicalName, ThisVNetRoot->PrefixOffsetInBytes)); ThisNamePrefix = &ThisVNetRoot->NamePrefix; ThisNamePrefix->Buffer = (PWCH)((PCHAR)VNetRootName.Buffer + ThisVNetRoot->PrefixOffsetInBytes); ThisNamePrefix->Length = ThisNamePrefix->MaximumLength = VNetRootName.Length - (USHORT)ThisVNetRoot->PrefixOffsetInBytes; InitializeListHead(&ThisVNetRoot->TransitionWaitList); InitializeListHead(&ThisVNetRoot->ScavengerFinalizationList); // Now, insert into the netrootQ and the net name table ThisEntry = &ThisVNetRoot->PrefixEntry; SrvCall = NetRoot->pSrvCall; if (FlagOn(SrvCall->Flags,SRVCALL_FLAG_CASE_INSENSITIVE_FILENAMES)) { //here is insensitive length is the whole thing CaseInsensitiveLength = (USHORT)NameSize; } else { //here is insensitive length is determined by the netroot or srvcall //plus we have to account for the device, if present ULONG ComponentsToUpcase,wcLength,i; if (FlagOn(SrvCall->Flags,SRVCALL_FLAG_CASE_INSENSITIVE_NETROOTS)) { CaseInsensitiveLength = NetRoot->PrefixEntry.CaseInsensitiveLength; } else { CaseInsensitiveLength = ((PSRV_CALL)SrvCall)->PrefixEntry.CaseInsensitiveLength; } wcLength = CanonicalName->Length/sizeof(WCHAR); for (i=1;;i++) { //note: don't start at zero if (i>=wcLength) break; if (CanonicalName->Buffer[i]!=OBJ_NAME_PATH_SEPARATOR) break; } CaseInsensitiveLength += (USHORT)(i*sizeof(WCHAR)); } RxPrefixTableInsertName( RxContext->RxDeviceObject->pRxNetNameTable, ThisEntry, (PVOID)ThisVNetRoot, &ThisVNetRoot->NodeReferenceCount, CaseInsensitiveLength, RxConnectionId); RxReferenceNetRoot(NetRoot); RxAddVirtualNetRootToNetRoot(NetRoot,ThisVNetRoot); ThisVNetRoot->SerialNumberForEnum = SerialNumber++; ThisVNetRoot->UpperFinalizationDone = FALSE; ThisVNetRoot->ConnectionFinalizationDone = FALSE; ThisVNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0; RxDbgTrace(-1, Dbg, ("RxVNetRootCreate -> RefCount = %08lx\n", ThisVNetRoot->NodeReferenceCount)); } if (Status != STATUS_SUCCESS) { RxUninitializeVNetRootParameters( ThisVNetRoot->pUserName, ThisVNetRoot->pUserDomainName, ThisVNetRoot->pPassword, &ThisVNetRoot->Flags ); RxFreeObject(ThisVNetRoot); ThisVNetRoot = NULL; } } return ThisVNetRoot; } VOID RxOrphanSrvOpens( IN PV_NET_ROOT ThisVNetRoot ) /*++ Routine Description: The routine iterates through all the FCBs that belong to the netroot to which this VNetRoot belongs and orphans all SrvOpens that belong to the VNetRoot. The caller must have acquired the NetName tablelock. Arguments: ThisVNetRoot - the VNetRoot Return Value: None Notes: On Entry -- RxNetNameTable lock must be acquired exclusive. On Exit -- no change in lock ownership. --*/ { PLIST_ENTRY pListEntry; USHORT BucketNumber; PNET_ROOT NetRoot = (PNET_ROOT)(ThisVNetRoot->NetRoot); PRX_PREFIX_TABLE pRxNetNameTable = NetRoot->SrvCall->RxDeviceObject->pRxNetNameTable; PAGED_CODE(); // // MAILSLOT FCBs don't have SrvOpens // if(NetRoot->Type == NET_ROOT_MAILSLOT) return; ASSERT(RxIsPrefixTableLockExclusive(pRxNetNameTable)); RxAcquireFcbTableLockExclusive( &NetRoot->FcbTable, TRUE); try { for (BucketNumber = 0; (BucketNumber < NetRoot->FcbTable.NumberOfBuckets); BucketNumber++) { PLIST_ENTRY pListHeader; pListHeader = &NetRoot->FcbTable.HashBuckets[BucketNumber]; pListEntry = pListHeader->Flink; while (pListEntry != pListHeader) { PFCB pFcb; PRX_FCB_TABLE_ENTRY pFcbTableEntry; pFcbTableEntry = CONTAINING_RECORD( pListEntry, RX_FCB_TABLE_ENTRY, HashLinks ); pListEntry = pListEntry->Flink; pFcb = CONTAINING_RECORD( pFcbTableEntry, FCB, FcbTableEntry); ASSERT(NodeTypeIsFcb(pFcb)); RxOrphanSrvOpensForThisFcb(pFcb, ThisVNetRoot, FALSE); // don't force orphan the FCB // orphan only those srvopens // that belong to this VNetRoot } } if (NetRoot->FcbTable.pTableEntryForNull) { PFCB pFcb; pFcb = CONTAINING_RECORD( NetRoot->FcbTable.pTableEntryForNull, FCB, FcbTableEntry); ASSERT(NodeTypeIsFcb(pFcb)); RxOrphanSrvOpensForThisFcb(pFcb, ThisVNetRoot, FALSE); } } finally { RxReleaseFcbTableLock( &NetRoot->FcbTable ); } } BOOLEAN RxFinalizeVNetRoot( OUT PV_NET_ROOT ThisVNetRoot, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize ) /*++ Routine Description: The routine finalizes the given netroot. You must be exclusive on the NetName tablelock. Arguments: ThisVNetRoot - the VNetRoot being dereferenced Return Value: BOOLEAN - tells whether finalization actually occured --*/ { NTSTATUS Status = STATUS_SUCCESS; BOOLEAN NodeActuallyFinalized = FALSE; PRX_PREFIX_TABLE pRxNetNameTable; PAGED_CODE(); ASSERT( NodeType(ThisVNetRoot) == RDBSS_NTC_V_NETROOT ); pRxNetNameTable = ThisVNetRoot->NetRoot->SrvCall->RxDeviceObject->pRxNetNameTable; ASSERT ( RxIsPrefixTableLockExclusive ( pRxNetNameTable ) ); RxDbgTrace(+1, Dbg, ("RxFinalizeVNetRoot<+> %08lx %wZ RefC=%ld\n", ThisVNetRoot,&ThisVNetRoot->PrefixEntry.Prefix, ThisVNetRoot->NodeReferenceCount)); //The actual finalization is divided into two parts: // 1) if we're at the end (refcount==1) or being forced, we do the one-time only stuff // 2) if the refcount goes to zero, we actually do the free if( ThisVNetRoot->NodeReferenceCount == 1 || ForceFinalize ){ PNET_ROOT NetRoot = (PNET_ROOT)ThisVNetRoot->NetRoot; RxLog(("FINALVNETROOT: %lx %wZ\n",ThisVNetRoot,&ThisVNetRoot->PrefixEntry.Prefix)); RxWmiLog(LOG, RxFinalizeVNetRoot, LOGPTR(ThisVNetRoot) LOGUSTR(ThisVNetRoot->PrefixEntry.Prefix)); if (!ThisVNetRoot->UpperFinalizationDone) { ASSERT( NodeType(NetRoot) == RDBSS_NTC_NETROOT ); RxReferenceNetRoot(NetRoot); RxOrphanSrvOpens(ThisVNetRoot); RxRemoveVirtualNetRootFromNetRoot(NetRoot,ThisVNetRoot); RxDereferenceNetRoot(NetRoot,LHS_ExclusiveLockHeld); RxDbgTrace(0, Dbg, ("Mini Rdr VNetRoot finalization returned %lx\n", Status)); RxRemovePrefixTableEntry ( pRxNetNameTable, &ThisVNetRoot->PrefixEntry); ThisVNetRoot->UpperFinalizationDone = TRUE; } if (ThisVNetRoot->NodeReferenceCount == 1) { if (NetRoot->SrvCall->RxDeviceObject != NULL) { MINIRDR_CALL_THROUGH( Status, NetRoot->SrvCall->RxDeviceObject->Dispatch, MRxFinalizeVNetRoot,((PMRX_V_NET_ROOT)ThisVNetRoot,NULL) ); } RxUninitializeVNetRootParameters( ThisVNetRoot->pUserName, ThisVNetRoot->pUserDomainName, ThisVNetRoot->pPassword, &ThisVNetRoot->Flags ); RxDereferenceNetRoot(NetRoot,LHS_ExclusiveLockHeld); RxFreePool(ThisVNetRoot); NodeActuallyFinalized = TRUE; } } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeVNetRoot<-> %08lx\n", ThisVNetRoot, NodeActuallyFinalized)); return NodeActuallyFinalized; } PVOID RxAllocateFcbObject( PRDBSS_DEVICE_OBJECT RxDeviceObject, NODE_TYPE_CODE NodeType, POOL_TYPE PoolType, ULONG NameSize, PVOID pAlreadyAllocatedObject) /*++ Routine Description: The routine allocates and constructs the skeleton of a FCB/SRV_OPEN and FOBX instance Arguments: pMRxDispatch - the Mini redirector dispatch vector NodeType - the node type PoolType - the pool type to be used ( for paging file data structures NonPagedPool is used. NameLength - name size. Notes: The reasons as to why the allocation/freeing of these data structures have been centralized are as follows 1) The construction of these three data types have a lot in common with the exception of the initial computation of sizes. Therefore centralization minimizes the footprint. 2) It allows us to experiment with different clustering/allocation strategies. 3) It allows the incorporation of debug support in an easy way. --*/ { ULONG FcbSize,NonPagedFcbSize,SrvOpenSize,FobxSize; PMINIRDR_DISPATCH pMRxDispatch = RxDeviceObject->Dispatch; PVOID pObject; PNON_PAGED_FCB pNonPagedFcb = NULL; PFCB pFcb = NULL; PSRV_OPEN pSrvOpen = NULL; PFOBX pFobx = NULL; PWCH pName = NULL; PAGED_CODE(); FcbSize = SrvOpenSize = FobxSize = NonPagedFcbSize = 0; switch (NodeType) { default: { FcbSize = QuadAlign(sizeof(FCB)); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_FCB_EXTENSION) { FcbSize += QuadAlign(pMRxDispatch->MRxFcbSize); } if (PoolType == NonPagedPool) { NonPagedFcbSize = QuadAlign(sizeof(NON_PAGED_FCB)); } if (NodeType == RDBSS_NTC_OPENTARGETDIR_FCB) { break; } } // lack of break intentional case RDBSS_NTC_SRVOPEN : case RDBSS_NTC_INTERNAL_SRVOPEN: { SrvOpenSize = QuadAlign(sizeof(SRV_OPEN)); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_SRV_OPEN_EXTENSION) { SrvOpenSize += QuadAlign(pMRxDispatch->MRxSrvOpenSize); } } // lack of break intentional case RDBSS_NTC_FOBX : { FobxSize = QuadAlign(sizeof(FOBX)); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_FOBX_EXTENSION) { FobxSize += QuadAlign(pMRxDispatch->MRxFobxSize); } } } if (pAlreadyAllocatedObject == NULL) { pObject = RxAllocatePoolWithTag( PoolType, (FcbSize + SrvOpenSize + FobxSize + NonPagedFcbSize + NameSize), RX_FCB_POOLTAG); //ASSERT(pObject != NULL); if (pObject==NULL) { return(NULL); } } else { pObject = pAlreadyAllocatedObject; } switch (NodeType) { case RDBSS_NTC_FOBX: { pFobx = (PFOBX)pObject; } break; case RDBSS_NTC_SRVOPEN: { pSrvOpen = (PSRV_OPEN)pObject; pFobx = (PFOBX)((PBYTE)pSrvOpen + SrvOpenSize); } break; case RDBSS_NTC_INTERNAL_SRVOPEN: { pSrvOpen = (PSRV_OPEN)pObject; } break; default : { pFcb = (PFCB)pObject; if (NodeType != RDBSS_NTC_OPENTARGETDIR_FCB) { pSrvOpen = (PSRV_OPEN)((PBYTE)pFcb + FcbSize); pFobx = (PFOBX)((PBYTE)pSrvOpen + SrvOpenSize); } if (PoolType == NonPagedPool) { pNonPagedFcb = (PNON_PAGED_FCB)((PBYTE)pFobx + FobxSize); pName = (PWCH)((PBYTE)pNonPagedFcb + NonPagedFcbSize); } else { pName = (PWCH)((PBYTE)pFcb + FcbSize + SrvOpenSize + FobxSize); pNonPagedFcb = RxAllocatePoolWithTag( NonPagedPool, sizeof(NON_PAGED_FCB), RX_NONPAGEDFCB_POOLTAG); if (pNonPagedFcb == NULL) { RxFreePool(pFcb); return NULL; } } } break; } if (pFcb != NULL) { ZeroAndInitializeNodeType(pFcb, RDBSS_NTC_STORAGE_TYPE_UNKNOWN, (NODE_BYTE_SIZE) FcbSize); pFcb->NonPaged = pNonPagedFcb; ZeroAndInitializeNodeType(pFcb->NonPaged, RDBSS_NTC_NONPAGED_FCB, ((NODE_BYTE_SIZE) sizeof( NON_PAGED_FCB ))); IF_DEBUG { //make a copy of NonPaged so we can zap the real pointer and still find it DbgDoit(pFcb->CopyOfNonPaged = pNonPagedFcb); DbgDoit(pNonPagedFcb->FcbBackPointer = pFcb); } // Set up the pointers to the preallocated SRV_OPEN and FOBX if required pFcb->InternalSrvOpen = pSrvOpen; pFcb->InternalFobx = pFobx; pFcb->PrivateAlreadyPrefixedName.Buffer = pName; pFcb->PrivateAlreadyPrefixedName.Length = (USHORT)NameSize; pFcb->PrivateAlreadyPrefixedName.MaximumLength = pFcb->PrivateAlreadyPrefixedName.Length; if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_FCB_EXTENSION) { pFcb->Context = ((PBYTE)pFcb + QuadAlign(sizeof(FCB))); } ZeroAndInitializeNodeType( &pFcb->FcbTableEntry, RDBSS_NTC_FCB_TABLE_ENTRY, sizeof(RX_FCB_TABLE_ENTRY)); InterlockedIncrement(&RxNumberOfActiveFcbs); InterlockedIncrement(&RxDeviceObject->NumberOfActiveFcbs); // Initialize the Advanced FCB header ExInitializeFastMutex(&pNonPagedFcb->AdvancedFcbHeaderMutex); FsRtlSetupAdvancedHeader(&pFcb->Header,&pNonPagedFcb->AdvancedFcbHeaderMutex); } if (pSrvOpen != NULL) { ZeroAndInitializeNodeType( pSrvOpen, RDBSS_NTC_SRVOPEN, (NODE_BYTE_SIZE)SrvOpenSize); if ((NodeType != RDBSS_NTC_SRVOPEN) ) { //here the srvopen has no internal fobx....set the "used" flag SetFlag(pSrvOpen->Flags,SRVOPEN_FLAG_FOBX_USED); pSrvOpen->InternalFobx = NULL; } else { pSrvOpen->InternalFobx = pFobx; } if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_SRV_OPEN_EXTENSION) { pSrvOpen->Context = ((PBYTE)pSrvOpen + QuadAlign(sizeof(SRV_OPEN))); } InitializeListHead( &pSrvOpen->SrvOpenQLinks); } if (pFobx != NULL) { ZeroAndInitializeNodeType( pFobx, RDBSS_NTC_FOBX, (NODE_BYTE_SIZE)FobxSize); if (pMRxDispatch->MRxFlags & RDBSS_MANAGE_FOBX_EXTENSION) { pFobx->Context = ((PBYTE)pFobx + QuadAlign(sizeof(FOBX))); } } return pObject; } VOID RxFreeFcbObject(PVOID pObject) /*++ Routine Description: The routine frees a FCB/SRV_OPEN and FOBX instance Arguments: pObject - the instance to be freed Notes: --*/ { PAGED_CODE(); switch (NodeType(pObject)) { case RDBSS_NTC_FOBX: case RDBSS_NTC_SRVOPEN: { RxFreePool(pObject); } break; default: if (NodeTypeIsFcb(pObject)) { PFCB pFcb = (PFCB)pObject; PRDBSS_DEVICE_OBJECT RxDeviceObject = pFcb->RxDeviceObject; // Release any Filter Context structures associated with this structure FsRtlTeardownPerStreamContexts( &pFcb->Header ); DbgDoit((pFcb->Header.NodeTypeCode |= 0x1000)); if (pFcb->FcbState & FCB_STATE_PAGING_FILE) { RxFreePool(pFcb); } else { RxFreePool(pFcb->NonPaged); RxFreePool(pFcb); } InterlockedDecrement(&RxNumberOfActiveFcbs); InterlockedDecrement(&RxDeviceObject->NumberOfActiveFcbs); } else { //ASSERT(!"Valid Object Type for RxFreeFcbObject"); } } } PFCB RxCreateNetFcb ( OUT PRX_CONTEXT RxContext, IN PV_NET_ROOT VNetRoot, IN PUNICODE_STRING Name ) /*++ Routine Description: This routine allocates, initializes, and inserts a new Fcb record into the in memory data structures. The structure allocated has space for a srvopen and a fobx. The size for all these things comes from the net root; they have already been aligned. An additional complication is that i use the same routine to initialize a fake fcb for renames. in this case, i don't want it inserted into the tree. You get a fake FCB with IrpSp->Flags|SL_OPEN_TAGET_DIRECTORY. Arguments: RxContext - an RxContext describing a create............ NetRoot - the net root that this FCB is being opened on Name - The name of the FCB. the netroot MAY contain a nameprefix that is to be prepended here. Return Value: PFCB - Returns a pointer to the newly allocated FCB --*/ { PFCB Fcb; POOL_TYPE PoolType; NODE_TYPE_CODE NodeType; RxCaptureRequestPacket; RxCaptureParamBlock; BOOLEAN IsPagingFile; BOOLEAN FakeFcb; PNET_ROOT NetRoot; PRDBSS_DEVICE_OBJECT RxDeviceObject; PRX_FCB_TABLE_ENTRY ThisEntry; ULONG NameSize; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxCreateNetFcb\n", 0)); ASSERT( VNetRoot && (NodeType(VNetRoot) == RDBSS_NTC_V_NETROOT) ); NetRoot = (PNET_ROOT)VNetRoot->NetRoot; ASSERT( NodeType(NetRoot) == RDBSS_NTC_NETROOT ); ASSERT( ((PMRX_NET_ROOT)NetRoot) == RxContext->Create.pNetRoot ); RxDeviceObject = NetRoot->SrvCall->RxDeviceObject; ASSERT( RxDeviceObject == RxContext->RxDeviceObject); IsPagingFile = BooleanFlagOn( capPARAMS->Flags, SL_OPEN_PAGING_FILE ); FakeFcb = (BooleanFlagOn(capPARAMS->Flags,SL_OPEN_TARGET_DIRECTORY) && !BooleanFlagOn(NetRoot->Flags,NETROOT_FLAG_SUPPORTS_SYMBOLIC_LINKS)); ASSERT( FakeFcb || RxIsFcbTableLockExclusive ( &NetRoot->FcbTable ) ); NodeType = (FakeFcb) ? RDBSS_NTC_OPENTARGETDIR_FCB : RDBSS_NTC_STORAGE_TYPE_UNKNOWN; PoolType = (IsPagingFile) ? NonPagedPool : PagedPool; NameSize = Name->Length + NetRoot->InnerNamePrefix.Length; Fcb = RxAllocateFcbObject(RxDeviceObject, NodeType, PoolType, NameSize, NULL); if (Fcb != NULL) { Fcb->CachedNetRootType = NetRoot->Type; //Fcb->MRxDispatch = NetRoot->Dispatch; Fcb->RxDeviceObject = RxDeviceObject; Fcb->MRxDispatch = RxDeviceObject->Dispatch; Fcb->VNetRoot = VNetRoot; Fcb->pNetRoot = (PMRX_NET_ROOT)VNetRoot->pNetRoot; InitializeListHead(&Fcb->SrvOpenList); Fcb->SrvOpenListVersion = 0; Fcb->FcbTableEntry.Path.Buffer = (PWCH)((PCHAR)Fcb->PrivateAlreadyPrefixedName.Buffer + NetRoot->InnerNamePrefix.Length); Fcb->FcbTableEntry.Path.Length = Name->Length; Fcb->FcbTableEntry.Path.MaximumLength = Name->Length; // finally, copy in the name, including the netroot prefix ThisEntry = &Fcb->FcbTableEntry; RxDbgTrace(0, Dbg, ("RxCreateNetFcb name buffer/length %08lx/%08lx\n", ThisEntry->Path.Buffer, ThisEntry->Path.Length)); RxDbgTrace(0, Dbg, ("RxCreateNetFcb prefix/name %wZ/%wZ\n", &NetRoot->InnerNamePrefix, Name)); RtlMoveMemory( Fcb->PrivateAlreadyPrefixedName.Buffer, NetRoot->InnerNamePrefix.Buffer, NetRoot->InnerNamePrefix.Length); RtlMoveMemory( ThisEntry->Path.Buffer, Name->Buffer, Name->Length); RxDbgTrace(0, Dbg, ("RxCreateNetFcb apname %wZ\n", &Fcb->PrivateAlreadyPrefixedName)); RxDbgTrace(0, Dbg, ("RxCreateNetFcb finalname %wZ\n", &Fcb->FcbTableEntry.Path)); if (FlagOn(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_ADDEDBACKSLASH)) { SetFlag(Fcb->FcbState,FCB_STATE_ADDEDBACKSLASH); } InitializeListHead(&Fcb->NonPaged->TransitionWaitList); // Check to see if we need to set the Fcb state to indicate that this // is a paging file if (IsPagingFile) { Fcb->FcbState |= FCB_STATE_PAGING_FILE; } // Check to see whether this was marked for reparse if( (RxContext->MajorFunction == IRP_MJ_CREATE) && (RxContext->Create.Flags & RX_CONTEXT_CREATE_FLAG_SPECIAL_PATH) ) { Fcb->FcbState |= FCB_STATE_SPECIAL_PATH; } // The initial state, open count, and segment objects fields are already // zero so we can skip setting them // //Initialize the resources Fcb->Header.Resource = &Fcb->NonPaged->HeaderResource; ExInitializeResourceLite(Fcb->Header.Resource); Fcb->Header.PagingIoResource = &Fcb->NonPaged->PagingIoResource; ExInitializeResourceLite(Fcb->Header.PagingIoResource); //Initialize the filesize lock FILESIZE_LOCK_DISABLED( Fcb->Specific.Fcb.FileSizeLock = &Fcb->NonPaged->FileSizeLock; ExInitializeFastMutex(Fcb->Specific.Fcb.FileSizeLock); ) if (!FakeFcb) { // everything worked.... insert into netroot table RxFcbTableInsertFcb( &NetRoot->FcbTable, Fcb); } else { Fcb->FcbState |= FCB_STATE_FAKEFCB|FCB_STATE_NAME_ALREADY_REMOVED; InitializeListHead(&Fcb->FcbTableEntry.HashLinks); RxLog(("FakeFinally %lx\n",RxContext)); RxWmiLog(LOG, RxCreateNetFcb_1, LOGPTR(RxContext)); RxDbgTrace(0, Dbg, ("FakeFcb !!!!!!! Irpc=%08lx\n", RxContext)); } RxReferenceVNetRoot(VNetRoot); InterlockedIncrement(&Fcb->pNetRoot->NumberOfFcbs); Fcb->ulFileSizeVersion=0; #ifdef RDBSSLOG RxLog(("Fcb nm %lx %wZ",Fcb,&(Fcb->FcbTableEntry.Path))); RxWmiLog(LOG, RxCreateNetFcb_2, LOGPTR(Fcb) LOGUSTR(Fcb->FcbTableEntry.Path)); { char buffer[20]; ULONG len,remaining; UNICODE_STRING jPrefix,jSuffix; sprintf(buffer,"Fxx nm %p ",Fcb); len = strlen(buffer); remaining = MAX_RX_LOG_ENTRY_SIZE -1 - len; if (remainingFcbTableEntry.Path.Length) { jPrefix.Buffer = Fcb->FcbTableEntry.Path.Buffer; jPrefix.Length = (USHORT)(sizeof(WCHAR)*(remaining-17)); jSuffix.Buffer = Fcb->FcbTableEntry.Path.Buffer-15+(Fcb->FcbTableEntry.Path.Length/sizeof(WCHAR)); jSuffix.Length = sizeof(WCHAR)*15; RxLog(("%s%wZ..%wZ",buffer,&jPrefix,&jSuffix)); RxWmiLog(LOG, RxCreateNetFcb_3, LOGARSTR(buffer) LOGUSTR(jPrefix) LOGUSTR(jSuffix)); } } #endif RxLoudFcbMsg("Create: ",&(Fcb->FcbTableEntry.Path)); RxDbgTrace(0, Dbg, ("RxCreateNetFcb nm.iso.ifox %08lx %08lx %08lx\n", Fcb->FcbTableEntry.Path.Buffer, Fcb->InternalSrvOpen, Fcb->InternalFobx)); RxDbgTrace(-1, Dbg, ("RxCreateNetFcb %08lx %wZ\n", Fcb, &(Fcb->FcbTableEntry.Path))); } if (Fcb != NULL) { RxReferenceNetFcb(Fcb); #ifdef RX_WJ_DBG_SUPPORT RxdInitializeFcbWriteJournalDebugSupport(Fcb); #endif } return Fcb; } RX_FILE_TYPE RxInferFileType( IN PRX_CONTEXT RxContext ) /*++ Routine Description: This routine tries to infer the filetype from the createoptions. Arguments: RxContext - the context of the Open Return Value: the storagetype implied by the open. --*/ { ULONG CreateOptions = RxContext->Create.NtCreateParameters.CreateOptions; PAGED_CODE(); switch (CreateOptions & (FILE_DIRECTORY_FILE|FILE_NON_DIRECTORY_FILE)) { case FILE_DIRECTORY_FILE: return(FileTypeDirectory); case FILE_NON_DIRECTORY_FILE: return(FileTypeFile); default: case 0: return(FileTypeNotYetKnown); //0 => i don't know the storage type } } VOID RxFinishFcbInitialization( IN OUT PMRX_FCB MrxFcb, IN RDBSS_STORAGE_TYPE_CODES RdbssStorageType, IN PFCB_INIT_PACKET InitPacket OPTIONAL ) /*++ Routine Description: This routine is used to finish initializing an FCB after we find out what kind it is. Arguments: Fcb - the Fcb being initialzed StorageType - the type of entity that the FCB refers to InitPacket - extra data that is required depending on the type of entity Return Value: none. --*/ { PFCB Fcb = (PFCB)MrxFcb; USHORT OldStorageType; PAGED_CODE(); RxDbgTrace( 0, Dbg, ("RxFcbInit %x %08lx %wZ\n", RdbssStorageType, Fcb, &(Fcb->FcbTableEntry.Path))); OldStorageType = Fcb->Header.NodeTypeCode; Fcb->Header.NodeTypeCode = (CSHORT)RdbssStorageType; // only update the information in the Fcb if it's not already set if ( !FlagOn(Fcb->FcbState,FCB_STATE_TIME_AND_SIZE_ALREADY_SET) ) { if (InitPacket != NULL) { Fcb->Attributes = *(InitPacket->pAttributes); Fcb->NumberOfLinks = *(InitPacket->pNumLinks); Fcb->CreationTime = *(InitPacket-> pCreationTime); Fcb->LastAccessTime = *(InitPacket->pLastAccessTime); Fcb->LastWriteTime = *(InitPacket->pLastWriteTime); Fcb->LastChangeTime = *(InitPacket->pLastChangeTime); Fcb->ActualAllocationLength = InitPacket->pAllocationSize->QuadPart; Fcb->Header.AllocationSize = *(InitPacket->pAllocationSize); Fcb->Header.FileSize = *(InitPacket->pFileSize); Fcb->Header.ValidDataLength = *(InitPacket->pValidDataLength); //don't do this yet RxAdjustAllocationSizeforCC(Fcb); SetFlag(Fcb->FcbState,FCB_STATE_TIME_AND_SIZE_ALREADY_SET); } } else { if (RdbssStorageType == RDBSS_NTC_MAILSLOT){ Fcb->Attributes = 0; Fcb->NumberOfLinks = 0; Fcb->CreationTime.QuadPart = 0; Fcb->LastAccessTime.QuadPart = 0; Fcb->LastWriteTime.QuadPart = 0; Fcb->LastChangeTime.QuadPart = 0; Fcb->ActualAllocationLength = 0; Fcb->Header.AllocationSize.QuadPart = 0; Fcb->Header.FileSize.QuadPart = 0; Fcb->Header.ValidDataLength.QuadPart = 0; SetFlag(Fcb->FcbState,FCB_STATE_TIME_AND_SIZE_ALREADY_SET); } } switch (RdbssStorageType) { case RDBSS_NTC_MAILSLOT: case RDBSS_NTC_SPOOLFILE: break; case RDBSS_STORAGE_NTC(FileTypeDirectory): case RDBSS_STORAGE_NTC(FileTypeNotYetKnown): break; case RDBSS_STORAGE_NTC(FileTypeFile): if (OldStorageType == RDBSS_STORAGE_NTC(FileTypeFile)) break; RxInitializeLowIoPerFcbInfo(&Fcb->Specific.Fcb.LowIoPerFcbInfo); FsRtlInitializeFileLock( &Fcb->Specific.Fcb.FileLock, RxLockOperationCompletion, RxUnlockOperation ); // // Initialize the oplock structure. NOT YET IMPLEMENTED!!! //FsRtlInitializeOplock( &Fcb->Specific.Fcb.Oplock ); // // Indicate that we want to be consulted on whether Fast I/O is possible Fcb->Header.IsFastIoPossible = FastIoIsQuestionable; break; default: ASSERT(FALSE); break; } return; } VOID RxRemoveNameNetFcb ( OUT PFCB ThisFcb ) /*++ Routine Description: The routine removes the name from the table and sets a flag indicateing that it has done so. You must have already acquired the netroot tablelock and have the fcblock as well. Arguments: ThisFcb - the Fcb being dereferenced Return Value: none. --*/ { PNET_ROOT NetRoot; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxRemoveNameNetFcb<+> %08lx %wZ RefC=%ld\n", ThisFcb,&ThisFcb->FcbTableEntry.Path, ThisFcb->NodeReferenceCount)); ASSERT( NodeTypeIsFcb(ThisFcb)); NetRoot = (PNET_ROOT)ThisFcb->VNetRoot->NetRoot; ASSERT( RxIsFcbTableLockExclusive( &NetRoot->FcbTable )); ASSERT( RxIsFcbAcquiredExclusive( ThisFcb )); RxFcbTableRemoveFcb( &NetRoot->FcbTable, ThisFcb); RxLoudFcbMsg("RemoveName: ",&(ThisFcb->FcbTableEntry.Path)); SetFlag(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED); RxDbgTrace(-1, Dbg, ("RxRemoveNameNetFcb<-> %08lx\n", ThisFcb)); } VOID RxPurgeFcb( PFCB pFcb) /*++ Routine Description: The routine purges a given FCB instance. If the FCB has an open section against it caused by cacheing the file then we need to purge to get the close Arguments: pFcb - the Fcb being dereferenced Notes: On Entry to this routine the FCB must be accquired exclusive. On Exit the FCB resource will be released and the FCB finalized if possible --*/ { PAGED_CODE(); ASSERT(RxIsFcbAcquiredExclusive(pFcb)); //make sure that it doesn't disappear RxReferenceNetFcb(pFcb); if (pFcb->OpenCount) { RxPurgeFcbInSystemCache( pFcb, NULL, 0, TRUE, TRUE); } if (!RxDereferenceAndFinalizeNetFcb(pFcb,NULL,FALSE,FALSE)) { //if it remains, then release else, you can't!! RxReleaseFcb(NULL,pFcb); } } BOOLEAN RxFinalizeNetFcb ( OUT PFCB ThisFcb, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize, IN LONG ReferenceCount ) /*++ Routine Description: The routine finalizes the given Fcb. This routine needs the netroot tablelock; get it beforehand. Arguments: ThisFcb - the Fcb being dereferenced Return Value: BOOLEAN - tells whether finalization actually occured --*/ { BOOLEAN NodeActuallyFinalized = FALSE; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxFinalizeNetFcb<+> %08lx %wZ RefC=%ld\n", ThisFcb,&ThisFcb->FcbTableEntry.Path, ReferenceCount)); RxLoudFcbMsg("Finalize: ",&(ThisFcb->FcbTableEntry.Path)); ASSERT_CORRECT_FCB_STRUCTURE(ThisFcb); ASSERT(RxIsFcbAcquiredExclusive( ThisFcb )); ASSERT(!ForceFinalize); if (!RecursiveFinalize) { if ((ThisFcb->OpenCount != 0) || (ThisFcb->UncleanCount != 0)) { // The FCB cannot be finalized because there are outstanding refrences to it. ASSERT(ReferenceCount > 0); return NodeActuallyFinalized; } } else { PSRV_OPEN SrvOpen; PLIST_ENTRY ListEntry; IF_DEBUG{ if ( FALSE && ReferenceCount){ RxDbgTrace(0, Dbg, (" BAD!!!!!ReferenceCount = %08lx\n", ReferenceCount)); } } ListEntry = ThisFcb->SrvOpenList.Flink; while (ListEntry != &ThisFcb->SrvOpenList) { SrvOpen = CONTAINING_RECORD( ListEntry, SRV_OPEN, SrvOpenQLinks ); ListEntry = ListEntry->Flink; RxFinalizeSrvOpen(SrvOpen,TRUE,ForceFinalize); } } RxDbgTrace(0, Dbg, (" After Recursive Part, REfC=%lx\n", ReferenceCount)); // After the recursive finalization the reference count associated with the FCB // could be atmost 1 for further finalization to occur. This final reference count // belongs to the prefix name table of the NetRoot. //The actual finalization is divided into two parts: // 1) if we're at the end (refcount==1) or being forced, we do the one-time only stuff // 2) if the refcount goes to zero, we actually do the free ASSERT(ReferenceCount >= 1); if (ReferenceCount == 1 || ForceFinalize ) { PV_NET_ROOT VNetRoot = ThisFcb->VNetRoot; ASSERT(ForceFinalize || (ThisFcb->OpenCount == 0) && (ThisFcb->UncleanCount == 0)); RxLog(("FinalFcb %lx %lx %lx %lx", ThisFcb,ForceFinalize,ReferenceCount,ThisFcb->OpenCount)); RxWmiLog(LOG, RxFinalizeNetFcb, LOGPTR(ThisFcb) LOGUCHAR(ForceFinalize) LOGULONG(ReferenceCount) LOGULONG(ThisFcb->OpenCount)); RxDbgTrace(0, Dbg, (" Before Phase 1, REfC=%lx\n", ReferenceCount)); if (!ThisFcb->UpperFinalizationDone) { switch (NodeType(ThisFcb)) { case RDBSS_STORAGE_NTC(FileTypeFile): //FsRtlUninitializeOplock( &ThisFcb->Specific.Fcb.Oplock ); //NOT YET IMPLEMENTED FsRtlUninitializeFileLock( &ThisFcb->Specific.Fcb.FileLock ); break; default: break; } if (!FlagOn(ThisFcb->FcbState,FCB_STATE_ORPHANED)) { PNET_ROOT NetRoot = VNetRoot->NetRoot; ASSERT(RxIsFcbTableLockExclusive ( &NetRoot->FcbTable )); if (!FlagOn(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED)){ RxFcbTableRemoveFcb( &NetRoot->FcbTable, ThisFcb); } } RxDbgTrace(0, Dbg, (" EndOf Phase 1, REfC=%lx\n", ReferenceCount)); ThisFcb->UpperFinalizationDone = TRUE; } RxDbgTrace(0, Dbg, (" After Phase 1, REfC=%lx\n", ReferenceCount)); ASSERT(ReferenceCount >= 1); if (ReferenceCount==1) { if (ThisFcb->pBufferingStateChangeCompletedEvent != NULL) { RxFreePool(ThisFcb->pBufferingStateChangeCompletedEvent); } if (ThisFcb->MRxDispatch != NULL) { ThisFcb->MRxDispatch->MRxDeallocateForFcb((PMRX_FCB)ThisFcb); } DbgDoit(ThisFcb->NonPaged->NodeTypeCode &= ~0x4000); ExDeleteResourceLite(ThisFcb->Header.Resource); ExDeleteResourceLite(ThisFcb->Header.PagingIoResource); InterlockedDecrement(&ThisFcb->pNetRoot->NumberOfFcbs); RxDereferenceVNetRoot(VNetRoot,LHS_LockNotHeld); ASSERT(IsListEmpty(&ThisFcb->FcbTableEntry.HashLinks)); #ifdef RX_WJ_DBG_SUPPORT RxdTearDownFcbWriteJournalDebugSupport(ThisFcb); #endif NodeActuallyFinalized = TRUE; ASSERT(!(ThisFcb->fMiniInited)); RxFreeFcbObject(ThisFcb); } } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeNetFcb<-> %08lx\n", ThisFcb, NodeActuallyFinalized)); return NodeActuallyFinalized; } VOID RxSetFileSizeWithLock ( IN OUT PFCB Fcb, IN PLONGLONG FileSize ) /*++ Routine Description: This routine sets the filesize in the fcb header, taking a lock to ensure that the 64-bit value is set and read consistently. Arguments: Fcb - the associated fcb FileSize - ptr to the new filesize Return Value: none Notes: --*/ { PAGED_CODE(); FILESIZE_LOCK_DISABLED(RxAcquireFileSizeLock(Fcb);) Fcb->Header.FileSize.QuadPart = *FileSize; Fcb->ulFileSizeVersion++; FILESIZE_LOCK_DISABLED(RxReleaseFileSizeLock(Fcb);) } VOID RxGetFileSizeWithLock ( IN PFCB Fcb, OUT PLONGLONG FileSize ) /*++ Routine Description: This routine gets the filesize in the fcb header, taking a lock to ensure that the 64-bit value is set and read consistently. Arguments: Fcb - the associated fcb FileSize - ptr to the new filesize Return Value: none Notes: --*/ { PAGED_CODE(); FILESIZE_LOCK_DISABLED(RxAcquireFileSizeLock(Fcb);) *FileSize = Fcb->Header.FileSize.QuadPart; FILESIZE_LOCK_DISABLED(RxReleaseFileSizeLock(Fcb);) } PSRV_OPEN RxCreateSrvOpen ( IN PV_NET_ROOT pVNetRoot, IN OUT PFCB Fcb) /*++ Routine Description: This routine allocates, initializes, and inserts a new srv_open record into the in memory data structures. If a new structure has to be allocated, it has space for a fobx. This routine sets the refcount to 1 and leaves the srv_open in Condition_InTransition. Arguments: pVNetRoot - the V_NET_ROOT instance Fcb - the associated fcb Return Value: the new SRV_OPEN instance Notes: On Entry : The FCB associated with the SRV_OPEN must have been acquired exclusive On Exit : No change in resource ownership --*/ { PSRV_OPEN SrvOpen = NULL; PNET_ROOT NetRoot; POOL_TYPE PoolType; ULONG SrvOpenFlags; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxCreateNetSrvOpen\n", 0)); ASSERT ( NodeTypeIsFcb(Fcb) ); ASSERT ( RxIsFcbAcquiredExclusive ( Fcb ) ); NetRoot = (PNET_ROOT)Fcb->VNetRoot->NetRoot; try { PoolType = (Fcb->FcbState & FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool; SrvOpen = Fcb->InternalSrvOpen; if ((SrvOpen != NULL) && !(FlagOn(Fcb->FcbState,FCB_STATE_SRVOPEN_USED)) && !(FlagOn(SrvOpen->Flags,SRVOPEN_FLAG_ENCLOSED_ALLOCATED)) && IsListEmpty(&SrvOpen->SrvOpenQLinks)) { RxAllocateFcbObject( NetRoot->SrvCall->RxDeviceObject, RDBSS_NTC_INTERNAL_SRVOPEN, PoolType, 0, SrvOpen); //this just initializes SetFlag(Fcb->FcbState,FCB_STATE_SRVOPEN_USED); SrvOpenFlags = SRVOPEN_FLAG_FOBX_USED | SRVOPEN_FLAG_ENCLOSED_ALLOCATED; } else { SrvOpen = RxAllocateFcbObject(NetRoot->SrvCall->RxDeviceObject,RDBSS_NTC_SRVOPEN,PoolType,0, NULL); SrvOpenFlags = 0; } if (SrvOpen != NULL) { SrvOpen->Flags = SrvOpenFlags; SrvOpen->Fcb = Fcb; SrvOpen->pAlreadyPrefixedName = &Fcb->PrivateAlreadyPrefixedName; SrvOpen->pVNetRoot = (PMRX_V_NET_ROOT)pVNetRoot; RxReferenceVNetRoot(pVNetRoot); InterlockedIncrement(&pVNetRoot->pNetRoot->NumberOfSrvOpens); SrvOpen->NodeReferenceCount = 1; RxReferenceNetFcb(Fcb); //already have the lock InsertTailList(&Fcb->SrvOpenList,&SrvOpen->SrvOpenQLinks); Fcb->SrvOpenListVersion++; InitializeListHead(&SrvOpen->FobxList); InitializeListHead(&SrvOpen->TransitionWaitList); InitializeListHead(&SrvOpen->ScavengerFinalizationList); InitializeListHead(&SrvOpen->SrvOpenKeyList); } } finally { DebugUnwind( RxCreateFcb ); if (AbnormalTermination()) { // If this is an abnormal termination then undo our work; this is // one of those happy times when the existing code will work if (SrvOpen != NULL) { RxFinalizeSrvOpen( SrvOpen,TRUE,TRUE ); } } else { if (SrvOpen != NULL) { RxLog(("SrvOp %lx %lx\n",SrvOpen,SrvOpen->Fcb)); RxWmiLog(LOG, RxCreateSrvOpen, LOGPTR(SrvOpen) LOGPTR(SrvOpen->Fcb)); } } } RxDbgTrace(-1, Dbg, ("RxCreateNetSrvOpen -> %08lx\n", SrvOpen)); return SrvOpen; } BOOLEAN RxFinalizeSrvOpen ( OUT PSRV_OPEN ThisSrvOpen, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize ) /*++ Routine Description: The routine finalizes the given SrvOpen. Arguments: ThisSrvOpen - the SrvOpen being dereferenced Return Value: BOOLEAN - tells whether finalization actually occured Notes: On Entry : 1) The FCB associated with the SRV_OPEN must have been acquired exclusive 2) The tablelock associated with FCB's NET_ROOT instance must have been acquired shared(atleast) On Exit : No change in resource ownership --*/ { NTSTATUS Status; BOOLEAN NodeActuallyFinalized = FALSE; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxFinalizeSrvOpen<+> %08lx %wZ RefC=%ld\n", ThisSrvOpen,&ThisSrvOpen->Fcb->FcbTableEntry.Path, ThisSrvOpen->NodeReferenceCount)); ASSERT(NodeType(ThisSrvOpen) == RDBSS_NTC_SRVOPEN ); if (RecursiveFinalize) { PFOBX Fobx; PLIST_ENTRY ListEntry; IF_DEBUG{ if ( FALSE && ThisSrvOpen->NodeReferenceCount){ RxDbgTrace(0, Dbg, (" BAD!!!!!ReferenceCount = %08lx\n", ThisSrvOpen->NodeReferenceCount)); } } ListEntry = ThisSrvOpen->FobxList.Flink; while (ListEntry != &ThisSrvOpen->FobxList) { Fobx = CONTAINING_RECORD( ListEntry, FOBX, FobxQLinks ); ListEntry = ListEntry->Flink; RxFinalizeNetFobx(Fobx,TRUE,ForceFinalize); } } if( ThisSrvOpen->NodeReferenceCount == 0 || ForceFinalize ){ BOOLEAN FreeSrvOpen; PFCB Fcb; Fcb = ThisSrvOpen->Fcb; RxLog(("FinalSrvOp %lx %lx %lx", ThisSrvOpen,ForceFinalize,ThisSrvOpen->NodeReferenceCount )); RxWmiLog(LOG, RxFinalizeSrvOpen, LOGPTR(ThisSrvOpen) LOGUCHAR(ForceFinalize) LOGULONG(ThisSrvOpen->NodeReferenceCount)); FreeSrvOpen = !FlagOn(ThisSrvOpen->Flags,SRVOPEN_FLAG_ENCLOSED_ALLOCATED); if ((!ThisSrvOpen->UpperFinalizationDone) && ((ThisSrvOpen->Condition != Condition_Good) || (ThisSrvOpen->Flags & SRVOPEN_FLAG_CLOSED))) { ASSERT(NodeType(Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB ); ASSERT(RxIsFcbAcquiredExclusive ( Fcb ) ); RxPurgeChangeBufferingStateRequestsForSrvOpen(ThisSrvOpen); if (!FlagOn(Fcb->FcbState,FCB_STATE_ORPHANED)) { // close the file. MINIRDR_CALL_THROUGH(Status,Fcb->MRxDispatch,MRxForceClosed,((PMRX_SRV_OPEN)ThisSrvOpen)); } RemoveEntryList ( &ThisSrvOpen->SrvOpenQLinks); InitializeListHead( &ThisSrvOpen->SrvOpenQLinks); Fcb->SrvOpenListVersion++; if (ThisSrvOpen->pVNetRoot != NULL) { InterlockedDecrement(&ThisSrvOpen->pVNetRoot->pNetRoot->NumberOfSrvOpens); RxDereferenceVNetRoot( (PV_NET_ROOT)ThisSrvOpen->pVNetRoot, LHS_LockNotHeld); ThisSrvOpen->pVNetRoot = NULL; } ThisSrvOpen->UpperFinalizationDone = TRUE; } if (ThisSrvOpen->NodeReferenceCount == 0) { ASSERT(IsListEmpty(&ThisSrvOpen->SrvOpenKeyList)); if (!IsListEmpty(&ThisSrvOpen->SrvOpenQLinks)) { RemoveEntryList(&ThisSrvOpen->SrvOpenQLinks); InitializeListHead(&ThisSrvOpen->SrvOpenQLinks); } if (FreeSrvOpen ) { RxFreeFcbObject(ThisSrvOpen); } if (!FreeSrvOpen){ ClearFlag(Fcb->FcbState,FCB_STATE_SRVOPEN_USED); } RxDereferenceNetFcb(Fcb); } NodeActuallyFinalized = TRUE; } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeSrvOpen<-> %08lx\n", ThisSrvOpen, NodeActuallyFinalized)); return NodeActuallyFinalized; } ULONG RxPreviousFobxSerialNumber = 0; PMRX_FOBX RxCreateNetFobx ( OUT PRX_CONTEXT RxContext, IN PMRX_SRV_OPEN mrxSrvOpen ) /*++ Routine Description: This routine allocates, initializes, and inserts a new file object extension instance. Arguments: RxContext - an RxContext describing a create............ pSrvOpen - the associated SrvOpen Return Value: none Notes: On Entry : FCB associated with the FOBX instance have been acquired exclusive. On Exit : No change in resource ownership --*/ { PFCB pFcb; PFOBX pFobx; PSRV_OPEN pSrvOpen = (PSRV_OPEN)mrxSrvOpen; RxCaptureRequestPacket; RxCaptureParamBlock; ULONG FobxFlags; POOL_TYPE PoolType; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxCreateFobx<+>\n", 0)); ASSERT(NodeType(pSrvOpen) == RDBSS_NTC_SRVOPEN); ASSERT( NodeTypeIsFcb(pSrvOpen->Fcb) ); ASSERT(RxIsFcbAcquiredExclusive(pSrvOpen->Fcb)); pFcb = pSrvOpen->Fcb; PoolType = (pFcb->FcbState & FCB_STATE_PAGING_FILE) ? NonPagedPool : PagedPool; if (!(FlagOn(pFcb->FcbState,FCB_STATE_FOBX_USED)) && (pSrvOpen == pFcb->InternalSrvOpen)){ // Try and use the FOBX allocated as part of the FCB if it is available pFobx = pFcb->InternalFobx; RxAllocateFcbObject( pFcb->RxDeviceObject, RDBSS_NTC_FOBX,PoolType, 0, pFobx);//just initialize SetFlag(pFcb->FcbState,FCB_STATE_FOBX_USED); FobxFlags = FOBX_FLAG_ENCLOSED_ALLOCATED; } else if (!(FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_FOBX_USED))){ // Try and use the FOBX allocated as part of the SRV_OPEN if it is available pFobx = pSrvOpen->InternalFobx; RxAllocateFcbObject( pFcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, pFobx);//just initialize SetFlag(pSrvOpen->Flags,SRVOPEN_FLAG_FOBX_USED); FobxFlags = FOBX_FLAG_ENCLOSED_ALLOCATED; } else { pFobx = RxAllocateFcbObject( pFcb->RxDeviceObject, RDBSS_NTC_FOBX, PoolType, 0, NULL); FobxFlags = 0; } if (pFobx != NULL) { PMRX_NET_ROOT pNetRoot; pFobx->Flags = FobxFlags; if ((pNetRoot = RxContext->Create.pNetRoot) != NULL) { switch (pNetRoot->DeviceType) { case RxDeviceType(NAMED_PIPE): RxInitializeThrottlingState( &pFobx->Specific.NamedPipe.ThrottlingState, pNetRoot->NamedPipeParameters.PipeReadThrottlingParameters.Increment, pNetRoot->NamedPipeParameters.PipeReadThrottlingParameters.MaximumDelay ); break; case RxDeviceType(DISK): RxInitializeThrottlingState( &pFobx->Specific.DiskFile.LockThrottlingState, pNetRoot->DiskParameters.LockThrottlingParameters.Increment, pNetRoot->DiskParameters.LockThrottlingParameters.MaximumDelay ); break; } } if (FlagOn(RxContext->Create.Flags,RX_CONTEXT_CREATE_FLAG_UNC_NAME)){ SetFlag(pFobx->Flags,FOBX_FLAG_UNC_NAME); //DbgPrint("setting UNC flag in fobx\n"); } if (FlagOn(RxContext->Create.NtCreateParameters.CreateOptions,FILE_OPEN_FOR_BACKUP_INTENT)) { SetFlag(pFobx->Flags,FOBX_FLAG_BACKUP_INTENT); } pFobx->FobxSerialNumber = 0; pFobx->SrvOpen = pSrvOpen; pFobx->NodeReferenceCount = 1; pFobx->fOpenCountDecremented = FALSE; RxReferenceSrvOpen(pSrvOpen); InterlockedIncrement(&pSrvOpen->pVNetRoot->NumberOfFobxs); InsertTailList(&pSrvOpen->FobxList,&pFobx->FobxQLinks); InitializeListHead(&pFobx->ClosePendingList); InitializeListHead(&pFobx->ScavengerFinalizationList); RxLog(("Fobx %lx %lx %lx\n",pFobx,pFobx->SrvOpen,pFobx->SrvOpen->Fcb)); RxWmiLog(LOG, RxCreateNetFobx, LOGPTR(pFobx) LOGPTR(pFobx->SrvOpen) LOGPTR(pFobx->SrvOpen->Fcb)); } RxDbgTrace(-1, Dbg, ("RxCreateNetFobx<-> %08lx\n", pFobx)); return (PMRX_FOBX)pFobx; } BOOLEAN RxFinalizeNetFobx ( OUT PFOBX ThisFobx, IN BOOLEAN RecursiveFinalize, IN BOOLEAN ForceFinalize ) /*++ Routine Description: The routine finalizes the given Fobx. you need exclusive fcblock. Arguments: ThisFobx - the Fobx being dereferenced Return Value: BOOLEAN - tells whether finalization actually occured Notes: On Entry : FCB associated with the FOBX instance must have been acquired exclusive. On Exit : No change in resource ownership --*/ { BOOLEAN NodeActuallyFinalized = FALSE; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("RxFinalizeFobx<+> %08lx %wZ RefC=%ld\n", ThisFobx,&ThisFobx->SrvOpen->Fcb->FcbTableEntry.Path, ThisFobx->NodeReferenceCount)); ASSERT( NodeType(ThisFobx) == RDBSS_NTC_FOBX ); if( ThisFobx->NodeReferenceCount == 0 || ForceFinalize ){ NTSTATUS Status; PSRV_OPEN SrvOpen = ThisFobx->SrvOpen; PFCB Fcb = SrvOpen->Fcb; BOOLEAN FreeFobx = !FlagOn(ThisFobx->Flags,FOBX_FLAG_ENCLOSED_ALLOCATED); RxLog(("FinalFobx %lx %lx %lx", ThisFobx,ForceFinalize,ThisFobx->NodeReferenceCount )); RxWmiLog(LOG, RxFinalizeNetFobx_1, LOGPTR(ThisFobx) LOGUCHAR(ForceFinalize) LOGULONG(ThisFobx->NodeReferenceCount)); if (!ThisFobx->UpperFinalizationDone) { ASSERT(NodeType(ThisFobx->SrvOpen->Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB ); ASSERT( RxIsFcbAcquiredExclusive ( ThisFobx->SrvOpen->Fcb ) ); RemoveEntryList ( &ThisFobx->FobxQLinks); if (FlagOn(ThisFobx->Flags,FOBX_FLAG_FREE_UNICODE)){ RxFreePool(ThisFobx->UnicodeQueryTemplate.Buffer); } if ((Fcb->MRxDispatch != NULL) && (Fcb->MRxDispatch->MRxDeallocateForFobx != NULL)) { Fcb->MRxDispatch->MRxDeallocateForFobx((PMRX_FOBX)ThisFobx); } if (!FlagOn(ThisFobx->Flags,FOBX_FLAG_SRVOPEN_CLOSED)) { // DbgPrint("@@@@ CloseAssociatedSrvOpen FOBX(%lx) SrvOpen(%lx) %wZ\n",ThisFobx,SrvOpen,&SrvOpen->Fcb->FcbTableEntry.Path); Status = RxCloseAssociatedSrvOpen(ThisFobx,NULL); RxLog(("$$ScCl FOBX %lx SrvOp %lx %lx\n",ThisFobx,ThisFobx->SrvOpen,Status)); RxWmiLog(LOG, RxFinalizeNetFobx_2, LOGPTR(ThisFobx) LOGPTR(ThisFobx->SrvOpen) LOGULONG(Status)); } ThisFobx->UpperFinalizationDone = TRUE; } if (ThisFobx->NodeReferenceCount == 0){ ASSERT(IsListEmpty(&ThisFobx->ClosePendingList)); if (ThisFobx == Fcb->InternalFobx) { ClearFlag(Fcb->FcbState,FCB_STATE_FOBX_USED); } else if (ThisFobx == SrvOpen->InternalFobx) { ClearFlag(SrvOpen->Flags,SRVOPEN_FLAG_FOBX_USED); } if (SrvOpen != NULL) { ThisFobx->SrvOpen = NULL; InterlockedDecrement(&SrvOpen->pVNetRoot->NumberOfFobxs); RxDereferenceSrvOpen(SrvOpen,LHS_ExclusiveLockHeld); } if (FreeFobx){ RxFreeFcbObject(ThisFobx); } NodeActuallyFinalized = TRUE; } } else { RxDbgTrace(0, Dbg, (" NODE NOT ACTUALLY FINALIZED!!!%C\n", '!')); } RxDbgTrace(-1, Dbg, ("RxFinalizeFobx<-> %08lx\n", ThisFobx, NodeActuallyFinalized)); return NodeActuallyFinalized; } //#define RDBSS_ENABLELOUDFCBOPSBYDEFAULT #if DBG #ifdef RDBSS_ENABLELOUDFCBOPSBYDEFAULT BOOLEAN RxLoudFcbOpsOnExes = TRUE; #else BOOLEAN RxLoudFcbOpsOnExes = FALSE; #endif // RDBSS_ENABLELOUDFCBOPSBYDEFAULT BOOLEAN RxLoudFcbMsg( PUCHAR msg, PUNICODE_STRING Name ) { PWCHAR Buffer; ULONG Length; if (!RxLoudFcbOpsOnExes) { return FALSE; } Length = (Name->Length)/sizeof(WCHAR); Buffer = Name->Buffer + Length; if ( ( Length < 4 ) || ( (Buffer[-1]&'E') != 'E' ) || ( (Buffer[-2]&'X') != 'X' ) || ( (Buffer[-3]&'E') != 'E' ) || ( (Buffer[-4]&'.') != '.' ) ) { return FALSE; } DbgPrint("--->%s %wZ\n",msg,Name); return(TRUE); } #endif VOID RxCheckFcbStructuresForAlignment(void) { ULONG StructureId; PAGED_CODE(); if (FIELD_OFFSET(NET_ROOT,SrvCall) != FIELD_OFFSET(NET_ROOT,pSrvCall)) { StructureId = 'RN'; goto DO_A_BUGCHECK; } if (FIELD_OFFSET(V_NET_ROOT,NetRoot) != FIELD_OFFSET(V_NET_ROOT,pNetRoot)) { StructureId = 'RNV'; goto DO_A_BUGCHECK; } if (FIELD_OFFSET(FCB,VNetRoot) != FIELD_OFFSET(FCB,VNetRoot)) { StructureId = 'BCF'; goto DO_A_BUGCHECK; } if (FIELD_OFFSET(SRV_OPEN,Fcb) != FIELD_OFFSET(SRV_OPEN,pFcb)) { StructureId = 'NPOS'; goto DO_A_BUGCHECK; } if (FIELD_OFFSET(FOBX,SrvOpen) != FIELD_OFFSET(FOBX,pSrvOpen)) { StructureId = 'XBOF'; goto DO_A_BUGCHECK; } return; DO_A_BUGCHECK: RxBugCheck( StructureId, 0, 0 ); } BOOLEAN RxIsThisACscAgentOpen( IN PRX_CONTEXT RxContext ) /*++ Routine Description: This routine determines if the open was made by the user mode CSC agent. Arguments: RxContext - the RDBSS context Return Value: TRUE - if it is an agent open, FALSE otherwise Notes: The agent opens are always satisfied by going to the server. They are never satisfied from the cached copies. This enables reintegration using snapshots even when the files are being currently used. --*/ { BOOLEAN AgentOpen = FALSE; ULONG EaInformationLength; PDFS_NAME_CONTEXT pDfsNameContext; if (RxContext->Create.EaLength > 0) { PFILE_FULL_EA_INFORMATION pEaEntry; pEaEntry = (PFILE_FULL_EA_INFORMATION)RxContext->Create.EaBuffer; ASSERT(pEaEntry != NULL); for(;;) { if (strcmp(pEaEntry->EaName, EA_NAME_CSCAGENT) == 0) { AgentOpen = TRUE; break; } if (pEaEntry->NextEntryOffset == 0) { break; } else { pEaEntry = (PFILE_FULL_EA_INFORMATION) ((PCHAR) pEaEntry + pEaEntry->NextEntryOffset); } } } pDfsNameContext = RxContext->Create.NtCreateParameters.DfsNameContext; if ((pDfsNameContext != NULL) && (pDfsNameContext->NameContextType == DFS_CSCAGENT_NAME_CONTEXT)) { AgentOpen = TRUE; } return AgentOpen; } VOID RxOrphanThisFcb( PFCB pFcb ) /*++ Routine Description: This routine orphans an FCB Arguments: pFcb - the fcb to be orphaned Return Value: None Notes: --*/ { // force orphan all SrvOpens for this FCB and orphan the FCB itself RxOrphanSrvOpensForThisFcb(pFcb, NULL, TRUE); } VOID RxOrphanSrvOpensForThisFcb( PFCB pFcb, IN PV_NET_ROOT ThisVNetRoot, BOOLEAN fOrphanAll ) /*++ Routine Description: This routine orphans all srvopens for a file belonging to a particular VNetRoot. The SrvOpen collapsing routine elsewhere makes sure that srvopens for different vnetroots are not collapsed. Arguments: pFcb - the fcb whose srvopens need to be orphaned ThisVNetRoot - the VNetRoot for which the SrvOpens have to be orphaned fOrphanAll - Orphan all SrvOpens, ie ignore the ThisVNetRoot parameter Return Value: None Notes: --*/ { NTSTATUS Status; PLIST_ENTRY pListEntry; BOOLEAN fAllSrvOpensOrphaned = TRUE; // fOrphanAll = TRUE; // temporarily force the old behaviour Status = RxAcquireExclusiveFcb(NULL,pFcb); ASSERT(Status == STATUS_SUCCESS); RxReferenceNetFcb(pFcb); pListEntry = pFcb->SrvOpenList.Flink; while (pListEntry != &pFcb->SrvOpenList) { PSRV_OPEN pSrvOpen; pSrvOpen = (PSRV_OPEN) (CONTAINING_RECORD( pListEntry, SRV_OPEN, SrvOpenQLinks)); pListEntry = pSrvOpen->SrvOpenQLinks.Flink; if (!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_ORPHANED)) { // NB check fOrphanAll first as if it is TRUE, the ThisVNetRoot // parameter maybe NULL if (fOrphanAll || ((PV_NET_ROOT)(pSrvOpen->pVNetRoot) == ThisVNetRoot)) { PLIST_ENTRY pEntry; PFOBX pFobx; SetFlag(pSrvOpen->Flags, SRVOPEN_FLAG_ORPHANED); RxAcquireScavengerMutex(); pEntry = pSrvOpen->FobxList.Flink; while (pEntry != &pSrvOpen->FobxList) { pFobx = (PFOBX)CONTAINING_RECORD( pEntry, FOBX, FobxQLinks); if (!pFobx->fOpenCountDecremented) { InterlockedDecrement(&pFcb->OpenCount); pFobx->fOpenCountDecremented = TRUE; } pEntry = pEntry->Flink; } RxReleaseScavengerMutex(); if (!FlagOn(pSrvOpen->Flags,SRVOPEN_FLAG_CLOSED) && !IsListEmpty(&pSrvOpen->FobxList)) { PLIST_ENTRY pEntry; NTSTATUS Status; PFOBX pFobx; pEntry = pSrvOpen->FobxList.Flink; pFobx = (PFOBX)CONTAINING_RECORD( pEntry, FOBX, FobxQLinks); RxReferenceNetFobx(pFobx); RxPurgeChangeBufferingStateRequestsForSrvOpen(pSrvOpen); Status = RxCloseAssociatedSrvOpen(pFobx,NULL); RxDereferenceNetFobx(pFobx,LHS_ExclusiveLockHeld); pListEntry = pFcb->SrvOpenList.Flink; } } else { // we found atleast one SrvOpen which is a) Not Orphaned and // b) doesn't belong to this VNetRoot // hence we cannot orphan this FCB fAllSrvOpensOrphaned = FALSE; } } } // if all srvopens for this FCB are in orphaned state, orphan the FCB as well. if (fAllSrvOpensOrphaned) { // remove the FCB from the netname table // so that any new opens/creates for this file will create a new FCB. RxRemoveNameNetFcb(pFcb); SetFlag(pFcb->FcbState,FCB_STATE_ORPHANED); ClearFlag(pFcb->FcbState,FCB_STATE_WRITECACHEING_ENABLED); if (!RxDereferenceAndFinalizeNetFcb(pFcb,NULL,FALSE,FALSE)) { RxReleaseFcb(NULL,pFcb); } } else { // some srvopens are still active, just remove the refcount and release the FCB RxDereferenceNetFcb(pFcb); RxReleaseFcb(NULL,pFcb); } } VOID RxForceFinalizeAllVNetRoots( PNET_ROOT pNetRoot ) /*++ Routine Description: The routine foce finalizes all the vnetroots from the given netroot. You must be exclusive on the NetName tablelock. Arguments: pNetRoot - the NetRoot Return Value: VOID --*/ { PLIST_ENTRY pListEntry; pListEntry = pNetRoot->VirtualNetRoots.Flink; while (pListEntry != &pNetRoot->VirtualNetRoots) { PV_NET_ROOT pTempVNetRoot; pTempVNetRoot = (PV_NET_ROOT) CONTAINING_RECORD( pListEntry, V_NET_ROOT, NetRootListEntry); if (NodeType(pTempVNetRoot) == RDBSS_NTC_V_NETROOT) { RxFinalizeVNetRoot(pTempVNetRoot, TRUE, TRUE); } pListEntry = pListEntry->Flink; } }