[RXCE]
[reactos.git] / reactos / sdk / lib / drivers / rxce / rxce.c
index 9cffb08..0bc941a 100644 (file)
@@ -58,17 +58,59 @@ NTSTATUS
 RxInsertWorkQueueItem(
     PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
     WORK_QUEUE_TYPE WorkQueueType,
-    PRX_WORK_DISPATCH_ITEM DispatchItem);
+    PRX_WORK_QUEUE_ITEM WorkQueueItem);
 
 PVOID
 RxNewMapUserBuffer(
     PRX_CONTEXT RxContext);
 
+VOID
+NTAPI
+RxpDestroySrvCall(
+    IN PVOID Context);
+
+VOID
+RxpDispatchChangeBufferingStateRequests(
+    PSRV_CALL SrvCall,
+    PSRV_OPEN SrvOpen,
+    PLIST_ENTRY DiscardedRequests);
+
+VOID
+NTAPI
+RxScavengerTimerRoutine(
+    PVOID Context);
+
+VOID
+NTAPI
+RxTimerDispatch(
+    _In_ struct _KDPC *Dpc,
+    _In_opt_ PVOID DeferredContext,
+    _In_opt_ PVOID SystemArgument1,
+    _In_opt_ PVOID SystemArgument2);
+
 VOID
 NTAPI
 RxWorkItemDispatcher(
     PVOID Context);
 
+PVOID
+NTAPI
+_RxAllocatePoolWithTag(
+    _In_ POOL_TYPE PoolType,
+    _In_ SIZE_T NumberOfBytes,
+    _In_ ULONG Tag);
+
+VOID
+NTAPI
+_RxFreePool(
+    _In_ PVOID Buffer);
+
+VOID
+NTAPI
+_RxFreePoolWithTag(
+    _In_ PVOID Buffer,
+    _In_ ULONG Tag);
+
 extern ULONG ReadAheadGranularity;
 
 volatile LONG RxNumberOfActiveFcbs = 0;
@@ -79,19 +121,35 @@ BOOLEAN RxStopOnLoudCompletion = TRUE;
 BOOLEAN RxSrvCallConstructionDispatcherActive = FALSE;
 LIST_ENTRY RxSrvCalldownList;
 RX_SPIN_LOCK RxStrucSupSpinLock;
-ULONG RdbssReferenceTracingValue;
+#if 0
+ULONG RdbssReferenceTracingValue = (RDBSS_REF_TRACK_SRVCALL | RDBSS_REF_TRACK_NETROOT |
+                                    RDBSS_REF_TRACK_VNETROOT | RDBSS_REF_TRACK_NETFOBX |
+                                    RDBSS_REF_TRACK_NETFCB | RDBSS_REF_TRACK_SRVOPEN |
+                                    RX_PRINT_REF_TRACKING);
+#else
+ULONG RdbssReferenceTracingValue = 0;
+#endif
 LARGE_INTEGER RxWorkQueueWaitInterval[RxMaximumWorkQueue];
 LARGE_INTEGER RxSpinUpDispatcherWaitInterval;
 RX_DISPATCHER RxDispatcher;
 RX_WORK_QUEUE_DISPATCHER RxDispatcherWorkQueues;
 FAST_MUTEX RxLowIoPagingIoSyncMutex;
 BOOLEAN RxContinueFromAssert = TRUE;
+ULONG RxExplodePoolTags = 1;
+LARGE_INTEGER RxTimerInterval;
+RX_SPIN_LOCK RxTimerLock;
+LIST_ENTRY RxTimerQueueHead;
+LIST_ENTRY RxRecurrentWorkItemsList;
+KDPC RxTimerDpc;
+KTIMER RxTimer;
+ULONG RxTimerTickCount;
 #if DBG
 BOOLEAN DumpDispatchRoutine = TRUE;
 #else
 BOOLEAN DumpDispatchRoutine = FALSE;
 #endif
 
+#if RDBSS_ASSERTS
 #ifdef ASSERT
 #undef ASSERT
 #endif
@@ -101,9 +159,133 @@ BOOLEAN DumpDispatchRoutine = FALSE;
     {                                             \
         RxAssert(#exp, __FILE__, __LINE__, NULL); \
     }
+#endif
+
+#if RX_POOL_WRAPPER
+#undef RxAllocatePool
+#undef RxAllocatePoolWithTag
+#undef RxFreePool
+
+#define RxAllocatePool(P, S) _RxAllocatePoolWithTag(P, S, 0)
+#define RxAllocatePoolWithTag _RxAllocatePoolWithTag
+#define RxFreePool _RxFreePool
+#define RxFreePoolWithTag _RxFreePoolWithTag
+#endif
 
 /* FUNCTIONS ****************************************************************/
 
+NTSTATUS
+NTAPI
+RxAcquireExclusiveFcbResourceInMRx(
+    _Inout_ PMRX_FCB Fcb)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+RxAcquireFcbForLazyWrite(
+    PVOID Context,
+    BOOLEAN Wait)
+{
+    PFCB Fcb;
+    BOOLEAN Ret;
+
+    PAGED_CODE();
+
+    Fcb = Context;
+    /* The received context is a FCB */
+    ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
+    ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
+    ASSERT(Fcb->Specific.Fcb.LazyWriteThread == NULL);
+
+    /* Acquire the paging resource (shared) */
+    Ret = ExAcquireResourceSharedLite(Fcb->Header.PagingIoResource, Wait);
+    if (Ret)
+    {
+        /* Update tracker information */
+        Fcb->PagingIoResourceFile = __FILE__;
+        Fcb->PagingIoResourceLine = __LINE__;
+        /* Lazy writer thread is the current one */
+        Fcb->Specific.Fcb.LazyWriteThread = PsGetCurrentThread();
+
+        /* There is no top level IRP */
+        ASSERT(RxIsThisTheTopLevelIrp(NULL));
+        /* Now, there will be! */
+        Ret = RxTryToBecomeTheTopLevelIrp(NULL, (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP,
+                                          Fcb->RxDeviceObject, TRUE);
+        /* In case of failure, release the lock and reset everything */
+        if (!Ret)
+        {
+            Fcb->PagingIoResourceFile = NULL;
+            Fcb->PagingIoResourceLine = 0;
+            ExReleaseResourceLite(Fcb->Header.PagingIoResource);
+            Fcb->Specific.Fcb.LazyWriteThread = NULL;
+        }
+    }
+
+    return Ret;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+NTAPI
+RxAcquireFcbForReadAhead(
+    PVOID Context,
+    BOOLEAN Wait)
+{
+    PFCB Fcb;
+    BOOLEAN Ret;
+
+    PAGED_CODE();
+
+    Fcb = Context;
+    /* The received context is a FCB */
+    ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
+    ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
+
+    Ret = ExAcquireResourceSharedLite(Fcb->Header.Resource, Wait);
+    if (Ret)
+    {
+        /* There is no top level IRP */
+        ASSERT(RxIsThisTheTopLevelIrp(NULL));
+        /* Now, there will be! */
+        Ret = RxTryToBecomeTheTopLevelIrp(NULL, (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP,
+                                          Fcb->RxDeviceObject, TRUE);
+        /* In case of failure, release the lock and reset everything */
+        if (!Ret)
+        {
+            ExReleaseResourceLite(Fcb->Header.Resource);
+        }
+    }
+
+    return Ret;
+}
+
+VOID
+NTAPI
+RxAcquireFileForNtCreateSection(
+    PFILE_OBJECT FileObject)
+{
+    UNIMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+RxAcquireForCcFlush(
+    PFILE_OBJECT FileObject,
+    PDEVICE_OBJECT DeviceObject)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 /*
  * @implemented
  */
@@ -225,7 +407,7 @@ RxAllocateFcbObject(
     /* Otherwise, allocate it */
     else
     {
-        Buffer = ExAllocatePoolWithTag(PoolType, NameSize + FcbSize + SrvOpenSize + FobxSize + NonPagedSize, RX_FCB_POOLTAG);
+        Buffer = RxAllocatePoolWithTag(PoolType, NameSize + FcbSize + SrvOpenSize + FobxSize + NonPagedSize, RX_FCB_POOLTAG);
         if (Buffer == NULL)
         {
             return NULL;
@@ -260,10 +442,10 @@ RxAllocateFcbObject(
         /* If we were not allocated from non paged, allocate the NON_PAGED_FCB now */
         if (PoolType != NonPagedPool)
         {
-            NonPagedFcb = ExAllocatePoolWithTag(NonPagedPool, sizeof(NON_PAGED_FCB), RX_NONPAGEDFCB_POOLTAG);
+            NonPagedFcb = RxAllocatePoolWithTag(NonPagedPool, sizeof(NON_PAGED_FCB), RX_NONPAGEDFCB_POOLTAG);
             if (NonPagedFcb == NULL)
             {
-                ExFreePoolWithTag(Buffer, RX_FCB_POOLTAG);
+                RxFreePoolWithTag(Buffer, RX_FCB_POOLTAG);
                 return NULL;
             }
 
@@ -342,6 +524,8 @@ RxAllocateFcbObject(
         FsRtlSetupAdvancedHeader(Fcb, &NonPagedFcb->AdvancedFcbHeaderMutex);
     }
 
+    DPRINT("Allocated %p\n", Buffer);
+
     return Buffer;
 }
 
@@ -399,7 +583,7 @@ RxAllocateObject(
 
     /* Now, allocate the object */
     ObjectSize = ExtensionSize + StructSize + NameLength;
-    Object = ExAllocatePoolWithTag(NonPagedPool, ObjectSize, Tag);
+    Object = RxAllocatePoolWithTag(NonPagedPool, ObjectSize, Tag);
     if (Object == NULL)
     {
         return NULL;
@@ -527,6 +711,149 @@ RxBootstrapWorkerThreadDispatcher(
     RxpWorkerThreadDispatcher(RxWorkQueue, NULL);
 }
 
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RxChangeBufferingState(
+    PSRV_OPEN SrvOpen,
+    PVOID Context,
+    BOOLEAN ComputeNewState)
+{
+    PFCB Fcb;
+    NTSTATUS Status, MiniStatus;
+    ULONG NewBufferingState, OldBufferingState;
+
+    PAGED_CODE();
+
+    DPRINT("RxChangeBufferingState(%p, %p, %d)\n", SrvOpen, Context, ComputeNewState);
+
+    Fcb = (PFCB)SrvOpen->pFcb;
+    ASSERT(NodeTypeIsFcb(Fcb));
+    /* First of all, mark that buffering state is changing */
+    SetFlag(Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING);
+
+    /* Assume success */
+    Status = STATUS_SUCCESS;
+    _SEH2_TRY
+    {
+        /* If we're asked to compute a new state, ask the mini-rdr for it */
+        if (ComputeNewState)
+        {
+            MINIRDR_CALL_THROUGH(MiniStatus, Fcb->MRxDispatch, MRxComputeNewBufferingState,
+                                 ((PMRX_SRV_OPEN)SrvOpen, Context, &NewBufferingState));
+            if (MiniStatus != STATUS_SUCCESS)
+            {
+                NewBufferingState = 0;
+            }
+        }
+        else
+        {
+            /* If not, use SRV_OPEN state */
+            NewBufferingState = SrvOpen->BufferingFlags;
+        }
+
+        /* If no shared access, and if we're not asked to compute a new state, use maximum flags set */
+        if ((Fcb->ShareAccess.SharedRead + Fcb->ShareAccess.SharedWrite + Fcb->ShareAccess.SharedDelete) == 0 && !ComputeNewState)
+        {
+            SetFlag(NewBufferingState, FCB_STATE_BUFFERING_STATE_WITH_NO_SHARES);
+        }
+
+        /* If there's a lock operation to complete, clear that flag */
+        if (Fcb->OutstandingLockOperationsCount != 0)
+        {
+            ClearFlag(NewBufferingState, FCB_STATE_LOCK_BUFFERING_ENABLED);
+        }
+
+        /* Get the old state */
+        OldBufferingState = Fcb->FcbState & FCB_STATE_BUFFERING_STATE_MASK;
+        DPRINT("ChangeBufferingState %x -> %x (%x)\n", OldBufferingState, NewBufferingState, SrvOpen->BufferingFlags);
+
+        /* If we're dropping write cache, then flush the FCB */
+        if (BooleanFlagOn(OldBufferingState, FCB_STATE_WRITECACHING_ENABLED) &&
+            !BooleanFlagOn(NewBufferingState, FCB_STATE_WRITECACHING_ENABLED))
+        {
+            DPRINT("Flushing\n");
+
+            Status = RxFlushFcbInSystemCache(Fcb, TRUE);
+        }
+
+        /* If we're dropping read cache, then purge */
+        if (Fcb->UncleanCount == 0 ||
+            (BooleanFlagOn(OldBufferingState, FCB_STATE_READCACHING_ENABLED) &&
+             !BooleanFlagOn(NewBufferingState, FCB_STATE_READCACHING_ENABLED)) ||
+            BooleanFlagOn(NewBufferingState, FCB_STATE_DELETE_ON_CLOSE))
+        {
+            DPRINT("Purging\n");
+
+            if (!NT_SUCCESS(Status))
+            {
+                 DPRINT("Previous flush failed with status: %lx\n", Status);
+            }
+
+            CcPurgeCacheSection(&Fcb->NonPaged->SectionObjectPointers, NULL, 0, TRUE);
+        }
+
+        /* If there's already a change pending in SRV_OPEN */
+        if (ComputeNewState && BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING))
+        {
+            /* If there's a FOBX at least */
+            if (!IsListEmpty(&SrvOpen->FobxList))
+            {
+                PRX_CONTEXT RxContext;
+
+                /* Create a fake context to pass to the mini-rdr */
+                RxContext = RxCreateRxContext(NULL, Fcb->RxDeviceObject, RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING | RX_CONTEXT_FLAG_WAIT);
+                if (RxContext != NULL)
+                {
+                    PFOBX Fobx;
+
+                    RxContext->pFcb = RX_GET_MRX_FCB(Fcb);
+
+                    /* Give the first FOBX */
+                    Fobx = CONTAINING_RECORD(SrvOpen->FobxList.Flink, FOBX, FobxQLinks);
+                    RxContext->pFobx = (PMRX_FOBX)Fobx;
+                    RxContext->pRelevantSrvOpen = Fobx->pSrvOpen;
+
+                    /* If there was a delayed close, perform it */
+                    if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_CLOSE_DELAYED))
+                    {
+                        DPRINT("Oplock break close for %p\n", SrvOpen);
+
+                        RxCloseAssociatedSrvOpen(Fobx, RxContext);
+                    }
+                    /* Otherwise, inform the mini-rdr about completion */
+                    else
+                    {
+                        MINIRDR_CALL_THROUGH(MiniStatus, Fcb->MRxDispatch, MRxCompleteBufferingStateChangeRequest,
+                                             (RxContext, (PMRX_SRV_OPEN)SrvOpen, Context));
+                        (void)MiniStatus;
+                    }
+
+                    RxDereferenceAndDeleteRxContext(RxContext);
+                }
+            }
+        }
+
+        /* Set the new state */
+        Fcb->FcbState ^= (NewBufferingState ^ Fcb->FcbState) & FCB_STATE_BUFFERING_STATE_MASK;
+    }
+    _SEH2_FINALLY
+    {
+        /* Job done, clear the flag */
+        ClearFlag(Fcb->FcbState, FCB_STATE_BUFFERSTATE_CHANGING);
+
+        if (!BooleanFlagOn(NewBufferingState, FCB_STATE_FILETIMECACHEING_ENABLED))
+        {
+            ClearFlag(Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET);
+        }
+    }
+    _SEH2_END;
+
+    return Status;
+}
+
 NTSTATUS
 RxCheckVNetRootCredentials(
     PRX_CONTEXT RxContext,
@@ -698,11 +1025,71 @@ RxCompleteRequest_Real(
     }
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxCompleteSrvOpenKeyAssociation(
     IN OUT PSRV_OPEN SrvOpen)
 {
-    UNIMPLEMENTED;
+    PSRV_CALL SrvCall;
+
+    SrvCall = (PSRV_CALL)((PFCB)SrvOpen->pFcb)->VNetRoot->pNetRoot->pSrvCall;
+    /* Only handle requests if opening was a success */
+    if (SrvOpen->Condition == Condition_Good)
+    {
+        KIRQL OldIrql;
+        BOOLEAN ProcessChange;
+        LIST_ENTRY DiscardedRequests;
+
+        /* Initialize our discarded requests list */
+        InitializeListHead(&DiscardedRequests);
+
+        RxAcquireBufferingManagerMutex(&SrvCall->BufferingManager);
+
+        /* Transfer our requests in the SRV_CALL */
+        RxTransferList(&SrvCall->BufferingManager.SrvOpenLists[0], &SrvOpen->SrvOpenKeyList);
+
+        /* Was increased in RxInitiateSrvOpenKeyAssociation(), opening is done */
+        InterlockedDecrement(&SrvCall->BufferingManager.NumberOfOutstandingOpens);
+
+        /* Dispatch requests and get the discarded ones */
+        RxpDispatchChangeBufferingStateRequests(SrvCall, SrvOpen, &DiscardedRequests);
+
+        RxReleaseBufferingManagerMutex(&SrvCall->BufferingManager);
+
+        /* Is there still anything to process? */
+        KeAcquireSpinLock(&SrvCall->BufferingManager.SpinLock, &OldIrql);
+        if (IsListEmpty(&SrvCall->BufferingManager.HandlerList))
+        {
+            ProcessChange = FALSE;
+        }
+        else
+        {
+            ProcessChange = (SrvCall->BufferingManager.HandlerInactive == FALSE);
+            if (ProcessChange)
+            {
+                SrvCall->BufferingManager.HandlerInactive = TRUE;
+            }
+        }
+        KeReleaseSpinLock(&SrvCall->BufferingManager.SpinLock, OldIrql);
+
+        /* Yes? Go ahead! */
+        if (ProcessChange)
+        {
+            RxReferenceSrvCall(SrvCall);
+            RxPostToWorkerThread(RxFileSystemDeviceObject, HyperCriticalWorkQueue,
+                                 &SrvCall->BufferingManager.HandlerWorkItem,
+                                 RxProcessChangeBufferingStateRequests, SrvCall);
+        }
+
+        /* And discard left requests */
+        RxpDiscardChangeBufferingStateRequests(&DiscardedRequests);
+    }
+    else
+    {
+        InterlockedDecrement(&SrvCall->BufferingManager.NumberOfOutstandingOpens);
+    }
 }
 
 /*
@@ -731,7 +1118,7 @@ RxConstructNetRoot(
     ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
 
     /* Allocate the context */
-    Context = ExAllocatePoolWithTag(PagedPool, sizeof(MRX_CREATENETROOT_CONTEXT), RX_SRVCALL_POOLTAG);
+    Context = RxAllocatePoolWithTag(PagedPool, sizeof(MRX_CREATENETROOT_CONTEXT), RX_SRVCALL_POOLTAG);
     if (Context == NULL)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -797,7 +1184,7 @@ RxConstructNetRoot(
     RxTransitionVNetRoot(VirtualNetRoot, VRootCondition);
 
     /* Context is not longer needed */
-    ExFreePoolWithTag(Context, RX_SRVCALL_POOLTAG);
+    RxFreePoolWithTag(Context, RX_SRVCALL_POOLTAG);
 
     DPRINT("Status: %x\n", Status);
 
@@ -829,7 +1216,7 @@ RxConstructSrvCall(
     ASSERT(*LockHoldingState == LHS_ExclusiveLockHeld);
 
     /* Allocate the context for mini-rdr */
-    Calldown = ExAllocatePoolWithTag(NonPagedPool, sizeof(MRX_SRVCALLDOWN_STRUCTURE), RX_SRVCALL_POOLTAG);
+    Calldown = RxAllocatePoolWithTag(NonPagedPool, sizeof(MRX_SRVCALLDOWN_STRUCTURE), RX_SRVCALL_POOLTAG);
     if (Calldown == NULL)
     {
         SrvCall->Context = NULL;
@@ -1695,6 +2082,9 @@ RxCreateVNetRoot(
     return VNetRoot;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxDereference(
     IN OUT PVOID Instance,
@@ -1718,16 +2108,29 @@ RxDereference(
     RefCount = InterlockedDecrement((volatile long *)&Node->NodeReferenceCount);
     ASSERT(RefCount >= 0);
 
-    /* TODO: trace */
+    /* Trace refcount */
     switch (NodeType)
     {
         case RDBSS_NTC_SRVCALL:
+            PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
+            break;
+
         case RDBSS_NTC_NETROOT:
+            PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
+            break;
+
         case RDBSS_NTC_V_NETROOT:
+            PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
+            break;
+
         case RDBSS_NTC_SRVOPEN:
+            PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
+            break;
+
         case RDBSS_NTC_FOBX:
-            UNIMPLEMENTED;
+            PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
             break;
+
         default:
             ASSERT(FALSE);
             break;
@@ -1743,14 +2146,26 @@ RxDereference(
     /* We have to be locked exclusively */
     if (LockHoldingState != LHS_ExclusiveLockHeld)
     {
-        UNIMPLEMENTED;
+        if ((NodeType == RDBSS_NTC_FOBX && RefCount == 0) ||
+             (NodeType >= RDBSS_NTC_SRVCALL && NodeType <= RDBSS_NTC_V_NETROOT))
+        {
+            RxpMarkInstanceForScavengedFinalization(Instance);
+        }
+
         RxReleaseScavengerMutex();
         return;
     }
+    else
+    {
+        if (BooleanFlagOn(NodeType, RX_SCAVENGER_MASK))
+        {
+            RxpUndoScavengerFinalizationMarking(Instance);
+        }
+    }
 
     RxReleaseScavengerMutex();
 
-    /* TODO: Really deallocate stuff - we're leaking as hell! */
+    /* Now, deallocate the memory */
     switch (NodeType)
     {
         case RDBSS_NTC_SRVCALL:
@@ -1766,20 +2181,53 @@ RxDereference(
         }
 
         case RDBSS_NTC_NETROOT:
-            UNIMPLEMENTED;
+        {
+            PNET_ROOT NetRoot;
+
+            NetRoot = (PNET_ROOT)Instance;
+
+            ASSERT(NetRoot->pSrvCall->RxDeviceObject != NULL);
+            ASSERT(RxIsPrefixTableLockAcquired(NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable));
+            RxFinalizeNetRoot(NetRoot, TRUE, TRUE);
             break;
+        }
 
         case RDBSS_NTC_V_NETROOT:
-            UNIMPLEMENTED;
+        {
+            PV_NET_ROOT VNetRoot;
+
+            VNetRoot = (PV_NET_ROOT)Instance;
+
+            ASSERT(VNetRoot->pNetRoot->pSrvCall->RxDeviceObject != NULL);
+            ASSERT(RxIsPrefixTableLockAcquired(VNetRoot->pNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable));
+            RxFinalizeVNetRoot(VNetRoot, TRUE, TRUE);
             break;
+        }
 
         case RDBSS_NTC_SRVOPEN:
-            UNIMPLEMENTED;
+        {
+            PSRV_OPEN SrvOpen;
+
+            SrvOpen = (PSRV_OPEN)Instance;
+
+            ASSERT(RxIsFcbAcquired(SrvOpen->Fcb));
+            if (SrvOpen->OpenCount == 0)
+            {
+                RxFinalizeSrvOpen(SrvOpen, FALSE, FALSE);
+            }
             break;
+        }
 
         case RDBSS_NTC_FOBX:
-            UNIMPLEMENTED;
+        {
+            PFOBX Fobx;
+
+            Fobx = (PFOBX)Instance;
+
+            ASSERT(RxIsFcbAcquired(Fobx->SrvOpen->Fcb));
+            RxFinalizeNetFobx(Fobx, TRUE, FALSE);
             break;
+        }
     }
 }
 
@@ -1865,6 +2313,14 @@ RxDereferenceAndDeleteRxContext_Real(
     }
 }
 
+VOID
+NTAPI
+RxDispatchChangeBufferingStateRequests(
+    PVOID Context)
+{
+    UNIMPLEMENTED;
+}
+
 /*
  * @implemented
  */
@@ -1880,7 +2336,7 @@ RxDispatchToWorkerThread(
     PRX_WORK_DISPATCH_ITEM DispatchItem;
 
     /* Allocate a bit of context */
-    DispatchItem = ExAllocatePoolWithTag(PagedPool, sizeof(RX_WORK_DISPATCH_ITEM), RX_WORKQ_POOLTAG);
+    DispatchItem = RxAllocatePoolWithTag(PagedPool, sizeof(RX_WORK_DISPATCH_ITEM), RX_WORKQ_POOLTAG);
     if (DispatchItem == NULL)
     {
         return STATUS_INSUFFICIENT_RESOURCES;
@@ -1893,10 +2349,10 @@ RxDispatchToWorkerThread(
     DispatchItem->WorkQueueItem.Parameter = DispatchItem;
 
     /* Insert item */
-    Status = RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, DispatchItem);
+    Status = RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, &DispatchItem->WorkQueueItem);
     if (!NT_SUCCESS(Status))
     {
-        ExFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
+        RxFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
         DPRINT1("RxInsertWorkQueueItem failed! Queue: %ld, Routine: %p, Context: %p, Status: %lx\n", WorkQueueType, Routine, pContext, Status);
     }
 
@@ -2103,20 +2559,217 @@ RxFcbTableRemoveFcb(
 /*
  * @implemented
  */
-BOOLEAN
-RxFinalizeNetFcb(
-    OUT PFCB ThisFcb,
-    IN BOOLEAN RecursiveFinalize,
-    IN BOOLEAN ForceFinalize,
-    IN LONG ReferenceCount)
+NTSTATUS
+NTAPI
+RxFinalizeConnection(
+    IN OUT PNET_ROOT NetRoot,
+    IN OUT PV_NET_ROOT VNetRoot OPTIONAL,
+    IN LOGICAL ForceFilesClosed)
 {
+    NTSTATUS Status;
+    PRX_PREFIX_TABLE PrefixTable;
+    ULONG UncleanAny, UncleanDir;
+    LONG FilesOpen, AdditionalRef;
+    BOOLEAN PrefixLocked, FcbTableLocked, ForceClose;
+
     PAGED_CODE();
 
-    DPRINT("RxFinalizeNetFcb(%p, %d, %d, %d)\n", ThisFcb, RecursiveFinalize, ForceFinalize, ReferenceCount);
-    DPRINT("Finalize: %wZ\n", &ThisFcb->FcbTableEntry.Path);
+    ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
 
-    /* Make sure we have an exclusively acquired FCB */
-    ASSERT_CORRECT_FCB_STRUCTURE(ThisFcb);
+    /* Get a BOOLEAN out of LOGICAL
+     * -1 is like FALSE but also drops extra V_NET_ROOT reference in case of failure
+     */
+    ForceClose = (ForceFilesClosed == TRUE ? TRUE : FALSE);
+
+    /* First, delete any notification change */
+    Status = RxCancelNotifyChangeDirectoryRequestsForVNetRoot(VNetRoot, ForceClose);
+    /* If it failed, continue if forced */
+    if (Status != STATUS_SUCCESS && !ForceFilesClosed)
+    {
+        return Status;
+    }
+    /* Reset status, in case notification deletion failed */
+    Status = STATUS_SUCCESS;
+
+    PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+
+    PrefixLocked = FALSE;
+    FcbTableLocked = FALSE;
+    FilesOpen = 0;
+    AdditionalRef = 0;
+    UncleanAny = 0;
+    UncleanDir = 0;
+    _SEH2_TRY
+    {
+        RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
+        PrefixLocked = TRUE;
+
+        RxReferenceNetRoot(NetRoot);
+
+        RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
+        FcbTableLocked = TRUE;
+
+        /* If our V_NET_ROOT wasn't finalized yet, proceed! */
+        if (!VNetRoot->ConnectionFinalizationDone)
+        {
+            USHORT Bucket;
+            PRX_FCB_TABLE FcbTable;
+
+            DPRINT("Finalizing connection %p: %wZ\n", NetRoot, &NetRoot->PrefixEntry.Prefix);
+
+            /* We'll browse all its associated FCB to check whether they're open/orphaned */
+            FcbTable = &NetRoot->FcbTable;
+            for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
+            {
+                PLIST_ENTRY BucketList, Entry;
+
+                BucketList = &FcbTable->HashBuckets[Bucket];
+                Entry = BucketList->Flink;
+                while (Entry != BucketList)
+                {
+                    PFCB Fcb;
+
+                    Fcb = CONTAINING_RECORD(Entry, FCB, FcbTableEntry.HashLinks);
+                    Entry = Entry->Flink;
+
+                    /* FCB for this connection, go ahead */
+                    if (Fcb->VNetRoot == VNetRoot)
+                    {
+                        /* It's still open, and no force? Fail and keep track */
+                        if (Fcb->UncleanCount > 0 && !ForceClose)
+                        {
+                            Status = STATUS_CONNECTION_IN_USE;
+                            if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_DIRECTORY)
+                            {
+                                ++UncleanDir;
+                            }
+                            else
+                            {
+                                ++UncleanAny;
+                            }
+                        }
+                        else
+                        {
+                            /* Else, force purge */
+                            ASSERT(NodeTypeIsFcb(Fcb));
+
+                            Status = RxAcquireExclusiveFcb(NULL, Fcb);
+                            ASSERT(Status == STATUS_SUCCESS);
+
+                            ClearFlag(Fcb->FcbState, FCB_STATE_COLLAPSING_ENABLED);
+
+                            RxScavengeRelatedFobxs(Fcb);
+                            RxPurgeFcb(Fcb);
+
+                            /* We don't need to release FCB lock, FCB finalize will take care of it */
+                        }
+                    }
+                }
+            }
+
+            /* No files left, our V_NET_ROOT is finalized */
+            if (VNetRoot->NumberOfFobxs == 0)
+            {
+                VNetRoot->ConnectionFinalizationDone = TRUE;
+            }
+        }
+
+        /* Keep Number of open files and track of the extra reference */
+        FilesOpen = VNetRoot->NumberOfFobxs;
+        AdditionalRef = VNetRoot->AdditionalReferenceForDeleteFsctlTaken;
+        /* If force close, caller doesn't want to keep connection alive
+         * and wants it totally close, so drop the V_NET_ROOT too
+         */
+        if (ForceClose)
+        {
+            RxFinalizeVNetRoot(VNetRoot, FALSE, TRUE);
+        }
+    }
+    _SEH2_FINALLY
+    {
+        /* Release what was acquired */
+        if (FcbTableLocked)
+        {
+            RxReleaseFcbTableLock(&NetRoot->FcbTable);
+        }
+
+        /* If close is forced, only fix status if there are open files */
+        if (ForceClose)
+        {
+            if (Status != STATUS_SUCCESS && UncleanAny != 0)
+            {
+                Status = STATUS_FILES_OPEN;
+            }
+        }
+        /* Else, fix status and fail closing if there are open files */
+        else
+        {
+            if ((Status != STATUS_SUCCESS && UncleanAny != 0) || FilesOpen > 0)
+            {
+                Status = STATUS_FILES_OPEN;
+            }
+        }
+
+        DPRINT("UncleanAny: %ld, UncleanDir: %ld, FilesOpen: %ld\n", UncleanAny, UncleanDir, FilesOpen);
+
+        /* If we're are asked to remove the extra ref, or if closing was a success, do it;
+         * only if it was still referenced!
+         */
+        if ((ForceFilesClosed == 0xFF || Status == STATUS_SUCCESS) && AdditionalRef != 0)
+        {
+            VNetRoot->AdditionalReferenceForDeleteFsctlTaken = 0;
+            RxDereferenceVNetRoot(VNetRoot, LHS_ExclusiveLockHeld);
+        }
+
+        if (PrefixLocked)
+        {
+            RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
+            RxReleasePrefixTableLock(PrefixTable);
+        }
+    }
+    _SEH2_END;
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxFinalizeFcbTable(
+    IN OUT PRX_FCB_TABLE FcbTable)
+{
+    USHORT Bucket;
+
+    PAGED_CODE();
+
+    /* Just delete the lock */
+    ExDeleteResourceLite(&FcbTable->TableLock);
+
+    /* And make sure (checked) that the table is really empty... */
+    for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
+    {
+        ASSERT(IsListEmpty(&FcbTable->HashBuckets[Bucket]));
+    }
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxFinalizeNetFcb(
+    OUT PFCB ThisFcb,
+    IN BOOLEAN RecursiveFinalize,
+    IN BOOLEAN ForceFinalize,
+    IN LONG ReferenceCount)
+{
+    PAGED_CODE();
+
+    DPRINT("RxFinalizeNetFcb(%p, %d, %d, %d)\n", ThisFcb, RecursiveFinalize, ForceFinalize, ReferenceCount);
+    DPRINT("Finalize: %wZ\n", &ThisFcb->FcbTableEntry.Path);
+
+    /* Make sure we have an exclusively acquired FCB */
+    ASSERT_CORRECT_FCB_STRUCTURE(ThisFcb);
     ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
 
     /* We shouldn't force finalization... */
@@ -2158,7 +2811,7 @@ RxFinalizeNetFcb(
 
     ASSERT(ForceFinalize || ((ThisFcb->OpenCount == 0) && (ThisFcb->UncleanCount == 0)));
 
-    DPRINT("Finalizing FCB open: %d (%d)", ThisFcb->OpenCount, ForceFinalize);
+    DPRINT("Finalizing FCB open: %d (%d)\n", ThisFcb->OpenCount, ForceFinalize);
 
     /* If finalization was not already initiated, go ahead */
     if (!ThisFcb->UpperFinalizationDone)
@@ -2175,7 +2828,7 @@ RxFinalizeNetFcb(
                 Entry = ThisFcb->BufferedLocks.List;
                 ThisFcb->BufferedLocks.List = Entry->Next;
 
-                ExFreePool(Entry);
+                RxFreePool(Entry);
             }
         }
 
@@ -2208,7 +2861,7 @@ RxFinalizeNetFcb(
     /* Now, release everything */
     if (ThisFcb->pBufferingStateChangeCompletedEvent != NULL)
     {
-        ExFreePool(ThisFcb->pBufferingStateChangeCompletedEvent);
+        RxFreePool(ThisFcb->pBufferingStateChangeCompletedEvent);
     }
 
     if (ThisFcb->MRxDispatch != NULL)
@@ -2221,7 +2874,7 @@ RxFinalizeNetFcb(
     ExDeleteResourceLite(ThisFcb->Header.PagingIoResource);
 
     InterlockedDecrement((volatile long *)&ThisFcb->pNetRoot->NumberOfFcbs);
-    RxDereferenceNetRoot(ThisFcb->pNetRoot, LHS_LockNotHeld);
+    RxDereferenceVNetRoot(ThisFcb->VNetRoot, LHS_LockNotHeld);
 
     ASSERT(IsListEmpty(&ThisFcb->FcbTableEntry.HashLinks));
     ASSERT(!ThisFcb->fMiniInited);
@@ -2232,116 +2885,565 @@ RxFinalizeNetFcb(
     return TRUE;
 }
 
+/*
+ * @implemented
+ */
 BOOLEAN
-RxFinalizeNetRoot(
-    OUT PNET_ROOT ThisNetRoot,
-    IN BOOLEAN RecursiveFinalize,
-    IN BOOLEAN ForceFinalize
-    )
+RxFinalizeNetFobx(
+    _Out_ PFOBX ThisFobx,
+    _In_ BOOLEAN RecursiveFinalize,
+    _In_ BOOLEAN ForceFinalize)
 {
-    UNIMPLEMENTED;
-    return FALSE;
-}
+    PFCB Fcb;
+    PSRV_OPEN SrvOpen;
 
-BOOLEAN
-RxFinalizeSrvCall(
-    OUT PSRV_CALL ThisSrvCall,
-    IN BOOLEAN RecursiveFinalize,
-    IN BOOLEAN ForceFinalize)
-{
-    UNIMPLEMENTED;
-    return FALSE;
+    PAGED_CODE();
+
+    ASSERT(NodeType(ThisFobx) == RDBSS_NTC_FOBX);
+
+    /* Only finalize if forced or if there's no ref left */
+    if (ThisFobx->NodeReferenceCount != 0 &&
+        !ForceFinalize)
+    {
+        return FALSE;
+    }
+
+    DPRINT("Finalize Fobx: %p (with %d ref), forced: %d\n", ThisFobx, ThisFobx->NodeReferenceCount, ForceFinalize);
+
+    SrvOpen = ThisFobx->SrvOpen;
+    Fcb = SrvOpen->Fcb;
+    /* If it wasn't finalized yet, do it */
+    if (!ThisFobx->UpperFinalizationDone)
+    {
+        ASSERT(NodeType(SrvOpen->Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB);
+        ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
+
+        /* Remove it from the SRV_OPEN */
+        RemoveEntryList(&ThisFobx->FobxQLinks);
+
+        /* If we were used to browse a directory, free the query buffer */
+        if (BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_FREE_UNICODE))
+        {
+            RxFreePoolWithTag(ThisFobx->UnicodeQueryTemplate.Buffer, RX_DIRCTL_POOLTAG);
+        }
+
+        /* Notify the mini-rdr */
+        if (Fcb->MRxDispatch != NULL && Fcb->MRxDispatch->MRxDeallocateForFobx != NULL)
+        {
+            Fcb->MRxDispatch->MRxDeallocateForFobx((PMRX_FOBX)ThisFobx);
+        }
+
+        /* If the SRV_OPEN wasn't closed yet, do it */
+        if (!BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED))
+        {
+            NTSTATUS Status;
+
+            Status = RxCloseAssociatedSrvOpen(ThisFobx, FALSE);
+            DPRINT("Closing SRV_OPEN %p for %p: %x\n", SrvOpen, ThisFobx, Status);
+        }
+
+        /* Finalization done */
+        ThisFobx->UpperFinalizationDone = TRUE;
+    }
+
+    /* If we're still referenced, don't go any further! */
+    if (ThisFobx->NodeReferenceCount != 0)
+    {
+        return FALSE;
+    }
+
+    /* At that point, everything should be closed */
+    ASSERT(IsListEmpty(&ThisFobx->ClosePendingList));
+
+    /* Was the FOBX allocated with another object?
+     * If so, mark the buffer free in said object
+     */
+    if (ThisFobx == Fcb->InternalFobx)
+    {
+        ClearFlag(Fcb->FcbState, FCB_STATE_FOBX_USED);
+    }
+    else if (ThisFobx == SrvOpen->InternalFobx)
+    {
+        ClearFlag(SrvOpen->Flags, SRVOPEN_FLAG_FOBX_USED);
+    }
+
+    ThisFobx->pSrvOpen = NULL;
+
+    /* A FOBX less */
+    InterlockedDecrement((volatile long *)&SrvOpen->pVNetRoot->NumberOfFobxs);
+
+    RxDereferenceSrvOpen(SrvOpen, LHS_ExclusiveLockHeld);
+
+    /* If it wasn't allocated with another object, free the FOBX */
+    if (!BooleanFlagOn(ThisFobx->Flags, FOBX_FLAG_ENCLOSED_ALLOCATED))
+    {
+        RxFreeFcbObject(ThisFobx);
+    }
+
+    return TRUE;
 }
 
+/*
+ * @implemented
+ */
 BOOLEAN
-RxFinalizeSrvOpen(
-    OUT PSRV_OPEN ThisSrvOpen,
+RxFinalizeNetRoot(
+    OUT PNET_ROOT ThisNetRoot,
     IN BOOLEAN RecursiveFinalize,
     IN BOOLEAN ForceFinalize)
 {
-    UNIMPLEMENTED;
-    return FALSE;
-}
-
-NTSTATUS
-RxFindOrConstructVirtualNetRoot(
-    IN PRX_CONTEXT RxContext,
-    IN PUNICODE_STRING CanonicalName,
-    IN NET_ROOT_TYPE NetRootType,
-    IN PUNICODE_STRING RemainingName)
-{
-    ULONG Flags;
-    NTSTATUS Status;
-    PVOID Container;
-    BOOLEAN Construct;
-    PV_NET_ROOT VNetRoot;
-    RX_CONNECTION_ID ConnectionID;
-    PRDBSS_DEVICE_OBJECT RxDeviceObject;
-    LOCK_HOLDING_STATE LockHoldingState;
+    PSRV_CALL SrvCall;
+    PRX_FCB_TABLE FcbTable;
+    PRX_PREFIX_TABLE PrefixTable;
 
     PAGED_CODE();
 
-    RxDeviceObject = RxContext->RxDeviceObject;
-    ASSERT(RxDeviceObject->Dispatch != NULL);
-    ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
+    ASSERT(NodeType(ThisNetRoot) == RDBSS_NTC_NETROOT);
 
-    /* Ask the mini-rdr for connection ID */
-    ConnectionID.SessionID = 0;
-    if (RxDeviceObject->Dispatch->MRxGetConnectionId != NULL)
+    PrefixTable = ThisNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
+
+    /* If sme finalization is already ongoing, leave */
+    if (BooleanFlagOn(ThisNetRoot->Flags, NETROOT_FLAG_FINALIZATION_IN_PROGRESS))
     {
-        Status = RxDeviceObject->Dispatch->MRxGetConnectionId(RxContext, &ConnectionID);
-        if (!NT_SUCCESS(Status) && Status != STATUS_NOT_IMPLEMENTED)
-        {
-            /* mini-rdr is expected not to fail - unless it's not implemented */
-            DPRINT1("Failed to initialize connection ID\n");
-            ASSERT(FALSE);
-        }
+        return FALSE;
     }
 
-    RxContext->Create.NetNamePrefixEntry = NULL;
-
-    Status = STATUS_MORE_PROCESSING_REQUIRED;
-    RxAcquirePrefixTableLockShared(RxDeviceObject->pRxNetNameTable, TRUE);
-    LockHoldingState = LHS_SharedLockHeld;
-    Construct = TRUE;
-    Flags = 0;
+    /* Mark we're finalizing */
+    SetFlag(ThisNetRoot->Flags, NETROOT_FLAG_FINALIZATION_IN_PROGRESS);
 
-    /* We will try twice to find a matching VNetRoot: shared locked and then exlusively locked */
-    while (TRUE)
+    FcbTable = &ThisNetRoot->FcbTable;
+    /* Did caller asked us to finalize any associated FCB? */
+    if (RecursiveFinalize)
     {
-        PNET_ROOT NetRoot;
-        PV_NET_ROOT SavedVNetRoot;
+        USHORT Bucket;
 
-        /* Look in prefix table */
-        Container = RxPrefixTableLookupName(RxDeviceObject->pRxNetNameTable, CanonicalName, RemainingName, &ConnectionID);
-        if (Container != NULL)
+        /* Browse all the FCBs in our FCB table */
+        RxAcquireFcbTableLockExclusive(FcbTable, TRUE);
+        for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
         {
-            /* If that's not a VNetRoot, that's a SrvCall, not interesting, loop again */
-            if (NodeType(Container) != RDBSS_NTC_V_NETROOT)
-            {
-                ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
-                RxDereferenceSrvCall(Container, LockHoldingState);
-            }
-            else
+            PLIST_ENTRY HashBucket, ListEntry;
+
+            HashBucket = &FcbTable->HashBuckets[Bucket];
+            ListEntry = HashBucket->Flink;
+            while (ListEntry != HashBucket)
             {
-                VNetRoot = Container;
-                NetRoot = VNetRoot->NetRoot;
+                PFCB Fcb;
 
-                /* If the matching VNetRoot isn't in a good shape, there's something wrong - fail */
-                if ((NetRoot->Condition != Condition_InTransition && NetRoot->Condition != Condition_Good) ||
-                    NetRoot->SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
+                Fcb = CONTAINING_RECORD(ListEntry, FCB, FcbTableEntry.HashLinks);
+                ASSERT(NodeTypeIsFcb(Fcb));
+
+                ListEntry = ListEntry->Flink;
+
+                /* If the FCB isn't orphaned, then, it's time to purge it */
+                if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_ORPHANED))
                 {
-                    Status = STATUS_BAD_NETWORK_PATH;
-                    SavedVNetRoot = NULL;
+                    NTSTATUS Status;
+
+                    Status = RxAcquireExclusiveFcb(NULL, Fcb);
+                    ASSERT(Status == STATUS_SUCCESS);
+                    RxPurgeFcb(Fcb);
                 }
-                else
-                {
-                    LUID LogonId;
-                    ULONG SessionId;
-                    PUNICODE_STRING UserName, UserDomain, Password;
+            }
+        }
+        RxReleaseFcbTableLock(FcbTable);
+    }
 
-                    /* We can reuse if we use same credentials */
-                    Status = RxInitializeVNetRootParameters(RxContext, &LogonId,
+    /* Only finalize if forced or if there's a single ref left */
+    if (ThisNetRoot->NodeReferenceCount != 1 && !ForceFinalize)
+    {
+        return FALSE;
+    }
+
+    DPRINT("Finalizing NetRoot %p for %wZ\n", ThisNetRoot, &ThisNetRoot->PrefixEntry.Prefix);
+
+    /* If we're still referenced, don't go any further! */
+    if (ThisNetRoot->NodeReferenceCount != 1)
+    {
+        return FALSE;
+    }
+
+    /* Finalize the FCB table (and make sure it's empty!) */
+    RxFinalizeFcbTable(FcbTable);
+
+    /* If name wasn't remove already, do it now */
+    if (!BooleanFlagOn(ThisNetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED))
+    {
+        RxRemovePrefixTableEntry(PrefixTable, &ThisNetRoot->PrefixEntry);
+    }
+
+    /* Delete the object */
+    SrvCall = (PSRV_CALL)ThisNetRoot->pSrvCall;
+    RxFreeObject(ThisNetRoot);
+
+    /* And dereference the associated SRV_CALL */
+    if (SrvCall != NULL)
+    {
+        RxDereferenceSrvCall(SrvCall, LHS_ExclusiveLockHeld);
+    }
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxFinalizeSrvCall(
+    OUT PSRV_CALL ThisSrvCall,
+    IN BOOLEAN RecursiveFinalize,
+    IN BOOLEAN ForceFinalize)
+{
+    PRX_PREFIX_TABLE PrefixTable;
+
+    PAGED_CODE();
+
+    ASSERT(NodeType(ThisSrvCall) == RDBSS_NTC_SRVCALL);
+
+    PrefixTable = ThisSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
+
+    /* Only finalize if forced or if there's a single ref left */
+    if (ThisSrvCall->NodeReferenceCount != 1 &&
+        !ForceFinalize)
+    {
+        return FALSE;
+    }
+
+    DPRINT("Finalizing SrvCall %p for %wZ\n", ThisSrvCall, &ThisSrvCall->PrefixEntry.Prefix);
+
+    /* If it wasn't finalized yet, do it */
+    if (!ThisSrvCall->UpperFinalizationDone)
+    {
+        BOOLEAN WillFree;
+
+        /* Remove ourselves from prefix table */
+        RxRemovePrefixTableEntry(PrefixTable, &ThisSrvCall->PrefixEntry);
+
+        /* Remember our third arg, in case we get queued for later execution */
+        if (ForceFinalize)
+        {
+            SetFlag(ThisSrvCall->Flags, SRVCALL_FLAG_FORCE_FINALIZED);
+        }
+
+        /* And done */
+        ThisSrvCall->UpperFinalizationDone = TRUE;
+
+        /* Would defered execution free the object? */
+        WillFree = (ThisSrvCall->NodeReferenceCount == 1);
+
+        /* If we have a device object */
+        if (ThisSrvCall->RxDeviceObject != NULL)
+        {
+            NTSTATUS Status;
+
+            /* If we're not executing in the RDBSS thread, queue for execution within the thread */
+            if (RxGetRDBSSProcess() != IoGetCurrentProcess())
+            {
+                /* Extra ref, as usual */
+                InterlockedIncrement((volatile long *)&ThisSrvCall->NodeReferenceCount);
+                /* And dispatch */
+                RxDispatchToWorkerThread(ThisSrvCall->RxDeviceObject, DelayedWorkQueue, RxpDestroySrvCall, ThisSrvCall);
+
+                /* Return to the caller, in advance, whether we're freeing the object or not */
+                return WillFree;
+            }
+
+            /* If in the right thread already, call the mini-rdr */
+            MINIRDR_CALL_THROUGH(Status, ThisSrvCall->RxDeviceObject->Dispatch,
+                                 MRxFinalizeSrvCall, ((PMRX_SRV_CALL)ThisSrvCall, ForceFinalize));
+            (void)Status;
+        }
+    }
+
+    /* If we're still referenced, don't go any further! */
+    if (ThisSrvCall->NodeReferenceCount != 1)
+    {
+        return FALSE;
+    }
+
+    /* Don't leak */
+    if (ThisSrvCall->pDomainName != NULL)
+    {
+        RxFreePool(ThisSrvCall->pDomainName);
+    }
+
+    /* And free! */
+    RxTearDownBufferingManager(ThisSrvCall);
+    RxFreeObject(ThisSrvCall);
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxFinalizeSrvOpen(
+    OUT PSRV_OPEN ThisSrvOpen,
+    IN BOOLEAN RecursiveFinalize,
+    IN BOOLEAN ForceFinalize)
+{
+    PFCB Fcb;
+
+    PAGED_CODE();
+
+    /* We have to have a SRV_OPEN */
+    ASSERT(NodeType(ThisSrvOpen) == RDBSS_NTC_SRVOPEN);
+
+    /* If that's a recursive finalization, finalize any related FOBX */
+    if (RecursiveFinalize)
+    {
+        PLIST_ENTRY ListEntry;
+
+        ListEntry = ThisSrvOpen->FobxList.Flink;
+        while (ListEntry != &ThisSrvOpen->FobxList)
+        {
+            PFOBX Fobx;
+
+            Fobx = CONTAINING_RECORD(ListEntry, FOBX, FobxQLinks);
+            ListEntry = ListEntry->Flink;
+            RxFinalizeNetFobx(Fobx, TRUE, ForceFinalize);
+        }
+    }
+
+    /* If we have still references, don't finalize unless forced */
+    if (ThisSrvOpen->NodeReferenceCount != 0 &&
+        !ForceFinalize)
+    {
+        return FALSE;
+    }
+
+    DPRINT("Finalize SRV_OPEN: %p (with %d ref), forced: %d\n", ThisSrvOpen, ThisSrvOpen->NodeReferenceCount, ForceFinalize);
+
+    /* Only finalize if closed, or if it wasn't already done and SRV_OPEN is in a bad shape */
+    Fcb = (PFCB)ThisSrvOpen->pFcb;
+    if ((!ThisSrvOpen->UpperFinalizationDone && ThisSrvOpen->Condition != Condition_Good) ||
+        BooleanFlagOn(ThisSrvOpen->Flags, SRVOPEN_FLAG_CLOSED))
+    {
+        PV_NET_ROOT VNetRoot;
+
+        /* Associated FCB can't be fake one */
+        ASSERT(NodeType(Fcb) != RDBSS_NTC_OPENTARGETDIR_FCB);
+        ASSERT(RxIsFcbAcquiredExclusive (Fcb));
+
+        /* Purge any pending operation */
+        RxPurgeChangeBufferingStateRequestsForSrvOpen(ThisSrvOpen);
+
+        /* If the FCB wasn't orphaned, inform the mini-rdr about close */
+        if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_ORPHANED))
+        {
+            NTSTATUS Status;
+
+            MINIRDR_CALL_THROUGH(Status, Fcb->MRxDispatch, MRxForceClosed, ((PMRX_SRV_OPEN)ThisSrvOpen));
+            (void)Status;
+        }
+
+        /* Remove ourselves from the FCB */
+        RemoveEntryList(&ThisSrvOpen->SrvOpenQLinks);
+        InitializeListHead(&ThisSrvOpen->SrvOpenQLinks);
+        ++Fcb->SrvOpenListVersion;
+
+        /* If we have a V_NET_ROOT, dereference it */
+        VNetRoot = (PV_NET_ROOT)ThisSrvOpen->pVNetRoot;
+        if (VNetRoot != NULL)
+        {
+            InterlockedDecrement((volatile long *)&VNetRoot->pNetRoot->NumberOfSrvOpens);
+            RxDereferenceVNetRoot(VNetRoot, LHS_LockNotHeld);
+            ThisSrvOpen->pVNetRoot = NULL;
+        }
+
+        /* Finalization done */
+        ThisSrvOpen->UpperFinalizationDone = TRUE;
+    }
+
+    /* Don't free memory if still referenced */
+    if (ThisSrvOpen->NodeReferenceCount != 0)
+    {
+        return FALSE;
+    }
+
+    /* No key association left */
+    ASSERT(IsListEmpty(&ThisSrvOpen->SrvOpenKeyList));
+
+    /* If we're still in some FCB, remove us */
+    if (!IsListEmpty(&ThisSrvOpen->SrvOpenQLinks))
+    {
+        RemoveEntryList(&ThisSrvOpen->SrvOpenQLinks);
+    }
+
+    /* If enclosed allocation, mark the memory zone free */
+    if (BooleanFlagOn(ThisSrvOpen->Flags, SRVOPEN_FLAG_ENCLOSED_ALLOCATED))
+    {
+        ClearFlag(Fcb->FcbState, FCB_STATE_SRVOPEN_USED);
+    }
+    /* Otherwise, free the memory */
+    else
+    {
+        RxFreeFcbObject(ThisSrvOpen);
+    }
+
+    RxDereferenceNetFcb(Fcb);
+
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxFinalizeVNetRoot(
+    OUT PV_NET_ROOT ThisVNetRoot,
+    IN BOOLEAN RecursiveFinalize,
+    IN BOOLEAN ForceFinalize)
+{
+    PNET_ROOT NetRoot;
+    PRX_PREFIX_TABLE PrefixTable;
+
+    PAGED_CODE();
+
+    ASSERT(NodeType(ThisVNetRoot) == RDBSS_NTC_V_NETROOT);
+
+    PrefixTable = ThisVNetRoot->pNetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
+
+    /* Only finalize if forced or if there's a single ref left */
+    if (ThisVNetRoot->NodeReferenceCount != 1 &&
+        !ForceFinalize)
+    {
+        return FALSE;
+    }
+
+    DPRINT("Finalizing VNetRoot %p for %wZ\n", ThisVNetRoot, &ThisVNetRoot->PrefixEntry.Prefix);
+
+    NetRoot = (PNET_ROOT)ThisVNetRoot->pNetRoot;
+    /* If it wasn't finalized yet, do it */
+    if (!ThisVNetRoot->UpperFinalizationDone)
+    {
+        ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
+
+        /* Reference the NetRoot so that it doesn't disappear */
+        RxReferenceNetRoot(NetRoot);
+        RxOrphanSrvOpens(ThisVNetRoot);
+        /* Remove us from the available VNetRoot for NetRoot */
+        RxRemoveVirtualNetRootFromNetRoot(NetRoot, ThisVNetRoot);
+        /* Remove extra ref */
+        RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
+
+        /* Remove ourselves from prefix table */
+        RxRemovePrefixTableEntry(PrefixTable, &ThisVNetRoot->PrefixEntry);
+
+        /* Finalization done */
+        ThisVNetRoot->UpperFinalizationDone = TRUE;
+    }
+
+    /* If we're still referenced, don't go any further! */
+    if (ThisVNetRoot->NodeReferenceCount != 1)
+    {
+        return FALSE;
+    }
+
+    /* If there's an associated device, notify mini-rdr */
+    if (NetRoot->pSrvCall->RxDeviceObject != NULL)
+    {
+        NTSTATUS Status;
+
+        MINIRDR_CALL_THROUGH(Status, NetRoot->pSrvCall->RxDeviceObject->Dispatch,
+                             MRxFinalizeVNetRoot, ((PMRX_V_NET_ROOT)ThisVNetRoot, FALSE));
+        (void)Status;
+    }
+
+    /* Free parameters */
+    RxUninitializeVNetRootParameters(ThisVNetRoot->pUserName, ThisVNetRoot->pUserDomainName,
+                                     ThisVNetRoot->pPassword, &ThisVNetRoot->Flags);
+    /* Dereference our NetRoot, we won't reference it anymore */
+    RxDereferenceNetRoot(NetRoot, LHS_ExclusiveLockHeld);
+
+    /* And free the object! */
+    RxFreePoolWithTag(ThisVNetRoot, RX_V_NETROOT_POOLTAG);
+
+    return TRUE;
+}
+
+NTSTATUS
+RxFindOrConstructVirtualNetRoot(
+    IN PRX_CONTEXT RxContext,
+    IN PUNICODE_STRING CanonicalName,
+    IN NET_ROOT_TYPE NetRootType,
+    IN PUNICODE_STRING RemainingName)
+{
+    ULONG Flags;
+    NTSTATUS Status;
+    PVOID Container;
+    BOOLEAN Construct;
+    PV_NET_ROOT VNetRoot;
+    RX_CONNECTION_ID ConnectionID;
+    PRDBSS_DEVICE_OBJECT RxDeviceObject;
+    LOCK_HOLDING_STATE LockHoldingState;
+
+    PAGED_CODE();
+
+    RxDeviceObject = RxContext->RxDeviceObject;
+    ASSERT(RxDeviceObject->Dispatch != NULL);
+    ASSERT(NodeType(RxDeviceObject->Dispatch) == RDBSS_NTC_MINIRDR_DISPATCH);
+
+    /* Ask the mini-rdr for connection ID */
+    ConnectionID.SessionID = 0;
+    if (RxDeviceObject->Dispatch->MRxGetConnectionId != NULL)
+    {
+        Status = RxDeviceObject->Dispatch->MRxGetConnectionId(RxContext, &ConnectionID);
+        if (!NT_SUCCESS(Status) && Status != STATUS_NOT_IMPLEMENTED)
+        {
+            /* mini-rdr is expected not to fail - unless it's not implemented */
+            DPRINT1("Failed to initialize connection ID\n");
+            ASSERT(FALSE);
+        }
+    }
+
+    RxContext->Create.NetNamePrefixEntry = NULL;
+
+    Status = STATUS_MORE_PROCESSING_REQUIRED;
+    RxAcquirePrefixTableLockShared(RxDeviceObject->pRxNetNameTable, TRUE);
+    LockHoldingState = LHS_SharedLockHeld;
+    Construct = TRUE;
+    Flags = 0;
+
+    /* We will try twice to find a matching VNetRoot: shared locked and then exlusively locked */
+    while (TRUE)
+    {
+        PNET_ROOT NetRoot;
+        PV_NET_ROOT SavedVNetRoot;
+
+        /* Look in prefix table */
+        Container = RxPrefixTableLookupName(RxDeviceObject->pRxNetNameTable, CanonicalName, RemainingName, &ConnectionID);
+        if (Container != NULL)
+        {
+            /* If that's not a VNetRoot, that's a SrvCall, not interesting, loop again */
+            if (NodeType(Container) != RDBSS_NTC_V_NETROOT)
+            {
+                ASSERT(NodeType(Container) == RDBSS_NTC_SRVCALL);
+                RxDereferenceSrvCall(Container, LockHoldingState);
+            }
+            else
+            {
+                VNetRoot = Container;
+                NetRoot = VNetRoot->NetRoot;
+
+                /* If the matching VNetRoot isn't in a good shape, there's something wrong - fail */
+                if ((NetRoot->Condition != Condition_InTransition && NetRoot->Condition != Condition_Good) ||
+                    NetRoot->SrvCall->RxDeviceObject != RxContext->RxDeviceObject)
+                {
+                    Status = STATUS_BAD_NETWORK_PATH;
+                    SavedVNetRoot = NULL;
+                }
+                else
+                {
+                    LUID LogonId;
+                    ULONG SessionId;
+                    PUNICODE_STRING UserName, UserDomain, Password;
+
+                    /* We can reuse if we use same credentials */
+                    Status = RxInitializeVNetRootParameters(RxContext, &LogonId,
                                                             &SessionId, &UserName,
                                                             &UserDomain, &Password,
                                                             &Flags);
@@ -2858,14 +3960,14 @@ RxFinishFcbInitialization(
     IN RX_FILE_TYPE FileType,
     IN PFCB_INIT_PACKET InitPacket OPTIONAL)
 {
-    NODE_TYPE_CODE OldType;
+    RX_FILE_TYPE OldType;
 
     PAGED_CODE();
 
     DPRINT("RxFinishFcbInitialization(%p, %x, %p)\n", Fcb, FileType, InitPacket);
 
-    OldType = Fcb->Header.NodeTypeCode;
-    Fcb->Header.NodeTypeCode = FileType;
+    OldType = NodeType(Fcb);
+    NodeType(Fcb) = FileType;
     /* If mini-rdr already did the job for mailslot attributes, 0 the rest */
     if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_TIME_AND_SIZE_ALREADY_SET) && FileType == RDBSS_NTC_MAILSLOT)
     {
@@ -2884,19 +3986,23 @@ RxFinishFcbInitialization(
     if (FileType != RDBSS_NTC_STORAGE_TYPE_UNKNOWN &&
         FileType != RDBSS_NTC_STORAGE_TYPE_DIRECTORY)
     {
-        /* If our FCB newly points to a file, initiliaz everything related */
-        if (FileType == RDBSS_NTC_STORAGE_TYPE_FILE &&
-            !OldType != RDBSS_NTC_STORAGE_TYPE_FILE)
+        /* If our FCB newly points to a file, initiliaze everything related */
+        if (FileType == RDBSS_NTC_STORAGE_TYPE_FILE)
+
         {
-            RxInitializeLowIoPerFcbInfo(&((PFCB)Fcb)->Specific.Fcb.LowIoPerFcbInfo);
-            FsRtlInitializeFileLock(&((PFCB)Fcb)->Specific.Fcb.FileLock, &RxLockOperationCompletion,
-                                    &RxUnlockOperation);
+            if (OldType != RDBSS_NTC_STORAGE_TYPE_FILE)
+            {
+                RxInitializeLowIoPerFcbInfo(&((PFCB)Fcb)->Specific.Fcb.LowIoPerFcbInfo);
+                FsRtlInitializeFileLock(&((PFCB)Fcb)->Specific.Fcb.FileLock, RxLockOperationCompletion,
+                                        RxUnlockOperation);
 
-            ((PFCB)Fcb)->BufferedLocks.List = NULL;
-            ((PFCB)Fcb)->BufferedLocks.PendingLockOps = 0;
+                ((PFCB)Fcb)->BufferedLocks.List = NULL;
+                ((PFCB)Fcb)->BufferedLocks.PendingLockOps = 0;
 
-            Fcb->Header.IsFastIoPossible = FastIoIsQuestionable;
+                Fcb->Header.IsFastIoPossible = FastIoIsQuestionable;
+            }
         }
+        /* If not a file, validate type */
         else
         {
             ASSERT(FileType >= RDBSS_NTC_SPOOLFILE && FileType <= RDBSS_NTC_MAILSLOT);
@@ -2952,7 +4058,7 @@ RxFinishSrvCallConstruction(
 
     RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
     RxTransitionSrvCall(SrvCall, Condition);
-    ExFreePoolWithTag(Calldown, RX_SRVCALL_POOLTAG);
+    RxFreePoolWithTag(Calldown, RX_SRVCALL_POOLTAG);
 
     /* If async, finish it here, otherwise, caller has already finished the stuff */
     if (BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION))
@@ -2981,7 +4087,7 @@ RxFinishSrvCallConstruction(
             {
                 if (Context->Info.Buffer != NULL)
                 {
-                    ExFreePool(Context->Info.Buffer);
+                    RxFreePool(Context->Info.Buffer);
                     Context->Info.Buffer = NULL;
                 }
             }
@@ -3064,18 +4170,238 @@ RxFinishSrvCallConstructionDispatcher(
     }
 }
 
-VOID
-RxFreeFcbObject(
-    PVOID Object)
-{
-    UNIMPLEMENTED;
+/*
+ * @implemented
+ */
+NTSTATUS
+RxFlushFcbInSystemCache(
+    IN PFCB Fcb,
+    IN BOOLEAN SynchronizeWithLazyWriter)
+{
+    IO_STATUS_BLOCK IoStatus;
+
+    PAGED_CODE();
+
+    /* Deal with Cc */
+    CcFlushCache(&Fcb->NonPaged->SectionObjectPointers, NULL, 0, &IoStatus);
+    /* If we're asked to sync with LW, do it in case of success */
+    if (SynchronizeWithLazyWriter && NT_SUCCESS(IoStatus.Status))
+    {
+        RxAcquirePagingIoResource((PRX_CONTEXT)NULL, Fcb);
+        RxReleasePagingIoResource((PRX_CONTEXT)NULL, Fcb);
+    }
+
+    DPRINT("Flushing for FCB %p returns %lx\n", Fcb, IoStatus.Status);
+    return IoStatus.Status;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxFreeFcbObject(
+    PVOID Object)
+{
+    PAGED_CODE();
+
+    DPRINT("Freeing %p\n", Object);
+
+    /* If that's a FOBX/SRV_OPEN, nothing to do, just free it */
+    if (NodeType(Object) == RDBSS_NTC_FOBX || NodeType(Object) == RDBSS_NTC_SRVOPEN)
+    {
+        RxFreePoolWithTag(Object, RX_FCB_POOLTAG);
+    }
+    /* If that's a FCB... */
+    else if (NodeTypeIsFcb(Object))
+    {
+        PFCB Fcb;
+        PRDBSS_DEVICE_OBJECT DeviceObject;
+
+        Fcb = (PFCB)Object;
+        DeviceObject = Fcb->RxDeviceObject;
+
+        /* Delete per stream contexts */
+        FsRtlTeardownPerStreamContexts(&Fcb->Header);
+
+        SetFlag(Fcb->Header.Flags, FSRTL_FLAG_ACQUIRE_MAIN_RSRC_SH);
+
+        /* If there was a non-paged FCB allocated, free it */
+        if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_PAGING_FILE))
+        {
+            RxFreePoolWithTag(Fcb->NonPaged, RX_NONPAGEDFCB_POOLTAG);
+        }
+
+        /* Free the FCB */
+        RxFreePool(Fcb);
+
+        /* Update statistics */
+        InterlockedDecrement(&RxNumberOfActiveFcbs);
+        InterlockedDecrement((volatile long *)&DeviceObject->NumberOfActiveFcbs);
+    }
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxFreeObject(
     PVOID pObject)
 {
-    UNIMPLEMENTED;
+    PAGED_CODE();
+
+    /* First, perform a few sanity checks if we're dealing with a SRV_CALL or a NET_ROOT */
+    if (NodeType(pObject) == RDBSS_NTC_SRVCALL)
+    {
+        PSRV_CALL SrvCall;
+        PRDBSS_DEVICE_OBJECT DeviceObject;
+
+        SrvCall = (PSRV_CALL)pObject;
+        DeviceObject = SrvCall->RxDeviceObject;
+        if (DeviceObject != NULL)
+        {
+            if (!BooleanFlagOn(DeviceObject->Dispatch->MRxFlags, RDBSS_MANAGE_SRV_CALL_EXTENSION))
+            {
+                ASSERT(SrvCall->Context == NULL);
+            }
+
+            ASSERT(SrvCall->Context2 == NULL);
+
+            SrvCall->RxDeviceObject = NULL;
+        }
+    }
+    else if (NodeType(pObject) == RDBSS_NTC_NETROOT)
+    {
+        PNET_ROOT NetRoot;
+
+        NetRoot = (PNET_ROOT)pObject;
+        NetRoot->pSrvCall = NULL;
+        NetRoot->NodeTypeCode = NodeType(pObject) | 0xF000;
+    }
+
+    /* And just free the object */
+    RxFreePool(pObject);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxGatherRequestsForSrvOpen(
+    IN OUT PSRV_CALL SrvCall,
+    IN PSRV_OPEN SrvOpen,
+    IN OUT PLIST_ENTRY RequestsListHead)
+{
+    KIRQL OldIrql;
+    LIST_ENTRY Discarded, *Entry;
+    PCHANGE_BUFFERING_STATE_REQUEST Request;
+
+    /* Dispatch any pending operation first */
+    RxpDispatchChangeBufferingStateRequests(SrvCall, SrvOpen, &Discarded);
+
+    /* Then, get any entry related to our key and SRV_OPEN */
+    KeAcquireSpinLock(&SrvCall->BufferingManager.SpinLock, &OldIrql);
+    Entry = SrvCall->BufferingManager.HandlerList.Flink;
+    while (Entry != &SrvCall->BufferingManager.HandlerList)
+    {
+        Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+        Entry = Entry->Flink;
+        if (Request->SrvOpenKey == SrvOpen->Key && Request->SrvOpen == SrvOpen)
+        {
+            RemoveEntryList(&Request->ListEntry);
+            InsertTailList(RequestsListHead, &Request->ListEntry);
+        }
+    }
+    KeReleaseSpinLock(&SrvCall->BufferingManager.SpinLock, OldIrql);
+
+    /* Perform the same search in the last change list */
+    Entry = SrvCall->BufferingManager.LastChanceHandlerList.Flink;
+    while (Entry != &SrvCall->BufferingManager.LastChanceHandlerList)
+    {
+        Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+        Entry = Entry->Flink;
+        if (Request->SrvOpenKey == SrvOpen->Key && Request->SrvOpen == SrvOpen)
+        {
+            RemoveEntryList(&Request->ListEntry);
+            InsertTailList(RequestsListHead, &Request->ListEntry);
+        }
+    }
+
+    /* Discard the discarded requests */
+    RxpDiscardChangeBufferingStateRequests(&Discarded);
+}
+
+/*
+ * @implemented
+ */
+PRDBSS_DEVICE_OBJECT
+RxGetDeviceObjectOfInstance(
+    PVOID Instance)
+{
+    NODE_TYPE_CODE NodeType;
+    PRDBSS_DEVICE_OBJECT DeviceObject;
+
+    PAGED_CODE();
+
+    /* We only handle a few object types */
+    NodeType = NodeType(Instance);
+    ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
+           (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) || (NodeType == RDBSS_NTC_FOBX));
+
+    /* Get the device object depending on the object */
+    switch (NodeType)
+    {
+        case RDBSS_NTC_FOBX:
+        {
+            PFOBX Fobx;
+
+            Fobx = (PFOBX)Instance;
+            DeviceObject = Fobx->RxDeviceObject;
+            break;
+        }
+
+        case RDBSS_NTC_SRVCALL:
+        {
+            PSRV_CALL SrvCall;
+
+            SrvCall = (PSRV_CALL)Instance;
+            DeviceObject = SrvCall->RxDeviceObject;
+            break;
+        }
+
+        case RDBSS_NTC_NETROOT:
+        {
+            PNET_ROOT NetRoot;
+
+            NetRoot = (PNET_ROOT)Instance;
+            DeviceObject = NetRoot->pSrvCall->RxDeviceObject;
+            break;
+        }
+
+        case RDBSS_NTC_V_NETROOT:
+        {
+            PV_NET_ROOT VNetRoot;
+
+            VNetRoot = (PV_NET_ROOT)Instance;
+            DeviceObject = VNetRoot->pNetRoot->pSrvCall->RxDeviceObject;
+            break;
+        }
+
+        case RDBSS_NTC_SRVOPEN:
+        {
+            PSRV_OPEN SrvOpen;
+
+            SrvOpen = (PSRV_OPEN)Instance;
+            DeviceObject = ((PFCB)SrvOpen->pFcb)->RxDeviceObject;
+            break;
+        }
+
+        default:
+            DeviceObject = NULL;
+            break;
+    }
+
+    /* Job done */
+    return DeviceObject;
 }
 
 /*
@@ -3372,7 +4698,7 @@ RxInitializeLowIoContext(
     PAGED_CODE();
 
     RxContext = CONTAINING_RECORD(LowIoContext, RX_CONTEXT, LowIoContext);
-    ASSERT(LowIoContext = &RxContext->LowIoContext);
+    ASSERT(LowIoContext == &RxContext->LowIoContext);
 
     Stack = RxContext->CurrentIrpSp;
 
@@ -3529,6 +4855,27 @@ RxInitializeSrvCallParameters(
     return STATUS_NOT_IMPLEMENTED;
 }
 
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RxInitializeRxTimer(
+    VOID)
+{
+    PAGED_CODE();
+
+    RxTimerInterval.QuadPart = -550000;
+    KeInitializeSpinLock(&RxTimerLock);
+    InitializeListHead(&RxTimerQueueHead);
+    InitializeListHead(&RxRecurrentWorkItemsList);
+    KeInitializeDpc(&RxTimerDpc, RxTimerDispatch, NULL);
+    KeInitializeTimer(&RxTimer);
+    RxTimerTickCount = 0;
+
+    return STATUS_SUCCESS;
+}
+
 NTSTATUS
 RxInitializeVNetRootParameters(
    PRX_CONTEXT RxContext,
@@ -3704,12 +5051,24 @@ RxInitializeWorkQueueDispatcher(
     return Status;
 }
 
+/*
+ * @implemented
+ */
 VOID
-RxInitiateSrvOpenKeyAssociation (
-   IN OUT PSRV_OPEN SrvOpen
-   )
+RxInitiateSrvOpenKeyAssociation(
+   IN OUT PSRV_OPEN SrvOpen)
 {
-    UNIMPLEMENTED;
+    PRX_BUFFERING_MANAGER BufferingManager;
+
+    PAGED_CODE();
+
+    SrvOpen->Key = NULL;
+
+    /* Just keep track of the opening request */
+    BufferingManager = &((PSRV_CALL)((PFCB)SrvOpen->pFcb)->VNetRoot->pNetRoot->pSrvCall)->BufferingManager;
+    InterlockedIncrement(&BufferingManager->NumberOfOutstandingOpens);
+
+    InitializeListHead(&SrvOpen->SrvOpenKeyList);
 }
 
 /*
@@ -3719,7 +5078,7 @@ NTSTATUS
 RxInsertWorkQueueItem(
     PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
     WORK_QUEUE_TYPE WorkQueueType,
-    PRX_WORK_DISPATCH_ITEM DispatchItem)
+    PRX_WORK_QUEUE_ITEM WorkQueueItem)
 {
     KIRQL OldIrql;
     NTSTATUS Status;
@@ -3744,7 +5103,7 @@ RxInsertWorkQueueItem(
     else
     {
         SpinUpThreads = FALSE;
-        DispatchItem->WorkQueueItem.pDeviceObject = pMRxDeviceObject;
+        WorkQueueItem->pDeviceObject = pMRxDeviceObject;
         InterlockedIncrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
         WorkQueue->CumulativeQueueLength += WorkQueue->NumberOfWorkItemsToBeDispatched;
         InterlockedIncrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
@@ -3769,7 +5128,7 @@ RxInsertWorkQueueItem(
     }
 
     /* All fine, insert the item */
-    KeInsertQueue(&WorkQueue->Queue, &DispatchItem->WorkQueueItem.List);
+    KeInsertQueue(&WorkQueue->Queue, &WorkQueueItem->List);
 
     /* And start a new worker thread if needed */
     if (SpinUpThreads)
@@ -3788,7 +5147,7 @@ RxIsThisACscAgentOpen(
 
     CscAgent = FALSE;
 
-    /* Client Side Caching is DFS stuff - we don't support it */ 
+    /* Client Side Caching is DFS stuff - we don't support it */
     if (RxContext->Create.EaLength != 0)
     {
         UNIMPLEMENTED;
@@ -3866,6 +5225,9 @@ RxLockUserBuffer(
     _SEH2_END;
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 RxLowIoCompletionTail(
     IN PRX_CONTEXT RxContext)
@@ -3878,7 +5240,7 @@ RxLowIoCompletionTail(
     DPRINT("RxLowIoCompletionTail(%p)\n", RxContext);
 
     /* Only continue if we're at APC_LEVEL or lower */
-    if (KeGetCurrentIrql() >= DISPATCH_LEVEL &&
+    if (RxShouldPostCompletion() &&
         !BooleanFlagOn(RxContext->LowIoContext.Flags, LOWIO_CONTEXT_FLAG_CAN_COMPLETE_AT_DPC_LEVEL))
     {
         return STATUS_MORE_PROCESSING_REQUIRED;
@@ -3896,10 +5258,15 @@ RxLowIoCompletionTail(
     Operation = RxContext->LowIoContext.Operation;
     if (Operation == LOWIO_OP_READ || Operation == LOWIO_OP_WRITE)
     {
+        /* Remove ourselves from the list and resume operations */
         if (BooleanFlagOn(RxContext->LowIoContext.ParamsFor.ReadWrite.Flags, LOWIO_READWRITEFLAG_PAGING_IO))
         {
-            UNIMPLEMENTED;
-            Status = STATUS_NOT_IMPLEMENTED;
+            ExAcquireFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
+            RemoveEntryList(&RxContext->RxContextSerializationQLinks);
+            RxContext->RxContextSerializationQLinks.Flink = NULL;
+            RxContext->RxContextSerializationQLinks.Blink = NULL;
+            ExReleaseFastMutexUnsafe(&RxLowIoPagingIoSyncMutex);
+            RxResumeBlockedOperations_ALL(RxContext);
         }
     }
     else
@@ -4174,19 +5541,148 @@ RxMapSystemBuffer(
     return Irp->AssociatedIrp.SystemBuffer;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxMarkFobxOnCleanup(
     PFOBX pFobx,
     PBOOLEAN NeedPurge)
 {
-    UNIMPLEMENTED;
+    PFCB Fcb;
+    PFOBX ScavengerFobx;
+    LARGE_INTEGER TickCount;
+    PRDBSS_SCAVENGER Scavenger;
+
+    PAGED_CODE();
+
+    /* No FOBX, nothing to mark */
+    if (pFobx == NULL)
+    {
+        return;
+    }
+
+    /* Query time for close */
+    KeQueryTickCount(&TickCount);
+
+    Fcb = (PFCB)pFobx->pSrvOpen->pFcb;
+    ASSERT(NodeTypeIsFcb(Fcb));
+
+    Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
+    RxAcquireScavengerMutex();
+
+    ScavengerFobx = NULL;
+    /* If that's not a file, or even not a disk resource, just mark as dormant */
+    if (NodeType(Fcb) != RDBSS_NTC_STORAGE_TYPE_FILE || Fcb->VNetRoot->pNetRoot->DeviceType != FILE_DEVICE_DISK)
+    {
+        SetFlag(pFobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
+        InitializeListHead(&pFobx->ClosePendingList);
+        ++Scavenger->NumberOfDormantFiles;
+    }
+    else
+    {
+        ASSERT(Scavenger->NumberOfDormantFiles >= 0);
+        /* If we're about to reach the maximum dormant of FOBX */
+        if (Scavenger->NumberOfDormantFiles >= Scavenger->MaximumNumberOfDormantFiles)
+        {
+            /* This should never be wrong... */
+            if (!IsListEmpty(&Scavenger->ClosePendingFobxsList))
+            {
+                /* Then, take the first from the list (oldest) and save it for later purge */
+                ScavengerFobx = CONTAINING_RECORD(Scavenger->ClosePendingFobxsList.Flink, FOBX, ClosePendingList);
+                if (ScavengerFobx->pSrvOpen != NULL && ScavengerFobx->pSrvOpen->pFcb == RX_GET_MRX_FCB(Fcb))
+                {
+                    *NeedPurge = TRUE;
+                    ScavengerFobx = NULL;
+                }
+                else
+                {
+                    RxReferenceNetFobx(ScavengerFobx);
+                }
+            }
+        }
+
+        /* Mark ourselves as dormant */
+        SetFlag(pFobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
+        pFobx->CloseTime.QuadPart = TickCount.QuadPart;
+
+        /* And insert us in the list of dormant files */
+        InsertTailList(&Scavenger->ClosePendingFobxsList, &pFobx->ClosePendingList);
+        /* If scavenger was inactive, start it */
+        if (Scavenger->NumberOfDormantFiles++ == 0 && Scavenger->State == RDBSS_SCAVENGER_INACTIVE)
+        {
+            Scavenger->State = RDBSS_SCAVENGER_DORMANT;
+            RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem, RxScavengerTimerRoutine,
+                                      Fcb->RxDeviceObject, Scavenger->TimeLimit);
+        }
+    }
+
+    RxReleaseScavengerMutex();
+
+    /* If we had reached max */
+    if (ScavengerFobx != NULL)
+    {
+        NTSTATUS Status;
+
+        /* Purge the oldest FOBX */
+        Status = RxPurgeFobxFromCache(ScavengerFobx);
+        if (Status != STATUS_SUCCESS)
+        {
+            *NeedPurge = TRUE;
+        }
+    }
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxMarkFobxOnClose(
     PFOBX Fobx)
 {
-    UNIMPLEMENTED;
+    PFCB Fcb;
+    PRDBSS_SCAVENGER Scavenger;
+
+    PAGED_CODE();
+
+    /* No FOBX, nothing to mark */
+    if (Fobx == NULL)
+    {
+        return;
+    }
+
+    Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
+    ASSERT(NodeTypeIsFcb(Fcb));
+
+    Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
+
+    RxAcquireScavengerMutex();
+    /* Only mark it if it was already marked as dormant */
+    if (BooleanFlagOn(Fobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT))
+    {
+        /* If FCB wasn't already decrement, do it now */
+        if (!Fobx->fOpenCountDecremented)
+        {
+            Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
+            ASSERT(NodeTypeIsFcb(Fcb));
+            InterlockedDecrement((volatile long *)&Fcb->OpenCount);
+
+            Fobx->fOpenCountDecremented = TRUE;
+        }
+
+        /* We're no longer dormant */
+        InterlockedDecrement(&Scavenger->NumberOfDormantFiles);
+        ClearFlag(Fobx->Flags, FOBX_FLAG_MARKED_AS_DORMANT);
+    }
+
+    /* If we were inserted in the scavenger, drop ourselves out */
+    if (!IsListEmpty(&Fobx->ClosePendingList))
+    {
+        RemoveEntryList(&Fobx->ClosePendingList);
+        InitializeListHead(&Fobx->ClosePendingList);
+    }
+
+    RxReleaseScavengerMutex();
 }
 
 /*
@@ -4234,29 +5730,97 @@ RxOrphanThisFcb(
     UNIMPLEMENTED;
 }
 
-/*
- * @implemented
- */
-BOOLEAN
-RxpAcquirePrefixTableLockShared(
-   PRX_PREFIX_TABLE pTable,
-   BOOLEAN Wait,
-   BOOLEAN ProcessBufferingStateChangeRequests)
+VOID
+RxOrphanSrvOpens(
+    IN PV_NET_ROOT ThisVNetRoot)
 {
+    PFCB Fcb;
+    USHORT Bucket;
+    PNET_ROOT NetRoot;
+    PRX_FCB_TABLE FcbTable;
+    PRX_PREFIX_TABLE PrefixTable;
+
     PAGED_CODE();
 
-    DPRINT("RxpAcquirePrefixTableLockShared(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
-           pTable->TableLock.ActiveEntries);
+    /* Mailslot won't have any SRV_OPEN (to orphan) */
+    NetRoot = (PNET_ROOT)ThisVNetRoot->pNetRoot;
+    if (NetRoot->Type == NET_ROOT_MAILSLOT)
+    {
+        return;
+    }
 
-    return ExAcquireResourceSharedLite(&pTable->TableLock, Wait);
-}
+    PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockExclusive(PrefixTable));
 
-/*
- * @implemented
- */
-BOOLEAN
-RxpAcquirePrefixTableLockExclusive(
-   PRX_PREFIX_TABLE pTable,
+    FcbTable = &NetRoot->FcbTable;
+    RxAcquireFcbTableLockExclusive(FcbTable, TRUE);
+
+    _SEH2_TRY
+    {
+        /* Now, we'll browse all the FCBs attached, and orphan related SRV_OPENs */
+        for (Bucket = 0; Bucket < FcbTable->NumberOfBuckets; ++Bucket)
+        {
+            PLIST_ENTRY BucketList, Entry;
+
+            BucketList = &FcbTable->HashBuckets[Bucket];
+            Entry = BucketList->Flink;
+            while (Entry != BucketList)
+            {
+                Fcb = CONTAINING_RECORD(Entry, FCB, FcbTableEntry.HashLinks);
+                Entry = Entry->Flink;
+
+                ASSERT(NodeTypeIsFcb(Fcb));
+                RxOrphanSrvOpensForThisFcb(Fcb, ThisVNetRoot, FALSE);
+            }
+        }
+
+        /* Of course, don't forget about NULL-entry */
+        if (FcbTable->TableEntryForNull != NULL)
+        {
+            Fcb = CONTAINING_RECORD(FcbTable->TableEntryForNull, FCB, FcbTableEntry.HashLinks);
+            ASSERT(NodeTypeIsFcb(Fcb));
+            RxOrphanSrvOpensForThisFcb(Fcb, ThisVNetRoot, FALSE);
+        }
+    }
+    _SEH2_FINALLY
+    {
+        RxReleaseFcbTableLock(FcbTable);
+    }
+    _SEH2_END;
+}
+
+VOID
+RxOrphanSrvOpensForThisFcb(
+    IN PFCB Fcb,
+    IN PV_NET_ROOT ThisVNetRoot,
+    IN BOOLEAN OrphanAll)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxpAcquirePrefixTableLockShared(
+   PRX_PREFIX_TABLE pTable,
+   BOOLEAN Wait,
+   BOOLEAN ProcessBufferingStateChangeRequests)
+{
+    PAGED_CODE();
+
+    DPRINT("RxpAcquirePrefixTableLockShared(%p, %d, %d) -> %d\n", pTable, Wait, ProcessBufferingStateChangeRequests,
+           pTable->TableLock.ActiveEntries);
+
+    return ExAcquireResourceSharedLite(&pTable->TableLock, Wait);
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxpAcquirePrefixTableLockExclusive(
+   PRX_PREFIX_TABLE pTable,
    BOOLEAN Wait,
    BOOLEAN ProcessBufferingStateChangeRequests)
 {
@@ -4315,7 +5879,8 @@ RxpDereferenceAndFinalizeNetFcb(
 
             if (!RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, FALSE))
             {
-                if (RxContext != NULL && RxContext != (PVOID)-1 && RxContext != (PVOID)-2)
+                if (RxContext != NULL && RxContext != CHANGE_BUFFERING_STATE_CONTEXT &&
+                    RxContext != CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
                 {
                     RxContext->Flags |= RX_CONTEXT_FLAG_BYPASS_VALIDOP_CHECK;
                 }
@@ -4334,7 +5899,7 @@ RxpDereferenceAndFinalizeNetFcb(
     }
 
     /* If locking was OK (or not needed!), attempt finalization */
-    if (NT_SUCCESS(Status))
+    if (Status == STATUS_SUCCESS)
     {
         Freed = RxFinalizeNetFcb(ThisFcb, RecursiveFinalize, ForceFinalize, References);
     }
@@ -4354,451 +5919,1989 @@ RxpDereferenceAndFinalizeNetFcb(
     return Freed;
 }
 
+/*
+ * @implemented
+ */
 LONG
 RxpDereferenceNetFcb(
    PFCB Fcb)
 {
-    UNIMPLEMENTED;
-    return 0;
+    LONG NewCount;
+
+    PAGED_CODE();
+
+    ASSERT(NodeTypeIsFcb(Fcb));
+
+    NewCount = InterlockedDecrement((volatile long *)&Fcb->NodeReferenceCount);
+    ASSERT(NewCount >= 0);
+
+    PRINT_REF_COUNT(NETFCB, NewCount);
+
+    return NewCount;
 }
 
 /*
  * @implemented
  */
-PRX_PREFIX_ENTRY
-RxPrefixTableInsertName(
-    IN OUT PRX_PREFIX_TABLE ThisTable,
-    IN OUT PRX_PREFIX_ENTRY ThisEntry,
-    IN PVOID Container,
-    IN PULONG ContainerRefCount,
-    IN USHORT CaseInsensitiveLength,
-    IN PRX_CONNECTION_ID ConnectionId
-    )
+VOID
+NTAPI
+RxpDestroySrvCall(
+    IN PVOID Context)
 {
-    PAGED_CODE();
+    NTSTATUS Status;
+    PSRV_CALL SrvCall;
+    BOOLEAN ForceFinalize;
+    PRX_PREFIX_TABLE PrefixTable;
 
-    DPRINT("Insert: %wZ\n", &ThisEntry->Prefix);
+    SrvCall = (PSRV_CALL)Context;
+    /* At this step, RxFinalizeSrvCall already cleaned some fields */
+    ASSERT(SrvCall->UpperFinalizationDone);
 
-    ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
-    ASSERT(CaseInsensitiveLength <= ThisEntry->Prefix.Length);
+    PrefixTable = SrvCall->RxDeviceObject->pRxNetNameTable;
+    /* Were we called with ForceFinalize? */
+    ForceFinalize = BooleanFlagOn(SrvCall->Flags, SRVCALL_FLAG_FORCE_FINALIZED);
 
-    /* Copy parameters and compute hash */
-    ThisEntry->CaseInsensitiveLength = CaseInsensitiveLength;
-    ThisEntry->ContainingRecord = Container;
-    ThisEntry->ContainerRefCount = ContainerRefCount;
-    InterlockedIncrement((volatile long *)ContainerRefCount);
-    ThisEntry->SavedHashValue = RxTableComputeHashValue(&ThisEntry->Prefix);
-    DPRINT("Associated hash: %x\n", ThisEntry->SavedHashValue);
+    /* Notify mini-rdr */
+    MINIRDR_CALL_THROUGH(Status, SrvCall->RxDeviceObject->Dispatch,
+                         MRxFinalizeSrvCall, ((PMRX_SRV_CALL)SrvCall,
+                         ForceFinalize));
+    (void)Status;
 
-    /* If no path length: this is entry for null path */
-    if (ThisEntry->Prefix.Length == 0)
-    {
-        ThisTable->TableEntryForNull = ThisEntry;
-    }
-    /* Otherwise, insert in the appropriate bucket */
-    else
-    {
-        InsertTailList(HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue), &ThisEntry->HashLinks);
-    }
+    /* Dereference our extra reference (set before queueing) */
+    RxAcquirePrefixTableLockExclusive(PrefixTable, TRUE);
+    InterlockedDecrement((volatile long *)&SrvCall->NodeReferenceCount);
+    /* And finalize for real, with the right context */
+    RxFinalizeSrvCall(SrvCall, FALSE, ForceFinalize);
+    RxReleasePrefixTableLock(PrefixTable);
+}
 
-    /* If we had a connection ID, keep track of it */
-    if (ConnectionId != NULL)
+/*
+ * @implemented
+ */
+VOID
+RxpDiscardChangeBufferingStateRequests(
+    _Inout_ PLIST_ENTRY DiscardedRequests)
+{
+    PLIST_ENTRY Entry;
+
+    PAGED_CODE();
+
+    /* No requests to discard */
+    if (IsListEmpty(DiscardedRequests))
     {
-        ThisEntry->ConnectionId.Luid = ConnectionId->Luid;
+        return;
     }
-    else
+
+    /* Free all the discarded requests */
+    Entry = DiscardedRequests->Flink;
+    while (Entry != DiscardedRequests)
     {
-        ThisEntry->ConnectionId.Luid.LowPart = 0;
-        ThisEntry->ConnectionId.Luid.HighPart = 0;
-    }
+        PCHANGE_BUFFERING_STATE_REQUEST Request;
 
-    InsertTailList(&ThisTable->MemberQueue, &ThisEntry->MemberQLinks);
-    /* Reflect the changes */
-    ++ThisTable->Version;
+        Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+        Entry = Entry->Flink;
 
-    DPRINT("Inserted in bucket: %p\n", HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue));
+        DPRINT("Req %p for %p (%p) discarded\n", Request, Request->SrvOpenKey, Request->SrvOpen);
 
-    return ThisEntry;
+        RxPrepareRequestForReuse(Request);
+        RxFreePool(Request);
+    }
 }
 
 /*
  * @implemented
  */
-PVOID
-RxPrefixTableLookupName(
-    IN PRX_PREFIX_TABLE ThisTable,
-    IN PUNICODE_STRING CanonicalName,
-    OUT PUNICODE_STRING RemainingName,
-    IN PRX_CONNECTION_ID ConnectionId)
+VOID
+RxpDispatchChangeBufferingStateRequests(
+    PSRV_CALL SrvCall,
+    PSRV_OPEN SrvOpen,
+    PLIST_ENTRY DiscardedRequests)
 {
-    PVOID Container;
-
-    PAGED_CODE();
+    KIRQL OldIrql;
+    NTSTATUS Status;
+    BOOLEAN StartDispatcher;
+    LIST_ENTRY AcceptedReqs;
+    LIST_ENTRY DispatcherList;
+    PRX_BUFFERING_MANAGER BufferingManager;
 
-    ASSERT(RxIsPrefixTableLockAcquired(ThisTable));
-    ASSERT(CanonicalName->Length > 0);
+    /* Initialize our lists */
+    InitializeListHead(&AcceptedReqs);
+    InitializeListHead(DiscardedRequests);
 
-    /* Call the internal helper */
-    Container = RxTableLookupName(ThisTable, CanonicalName, RemainingName, ConnectionId);
-    if (Container == NULL)
-    {
-        return NULL;
-    }
+    /* Transfer the requests to dispatch locally */
+    BufferingManager = &SrvCall->BufferingManager;
+    KeAcquireSpinLock(&BufferingManager->SpinLock, &OldIrql);
+    RxTransferList(&DispatcherList, &BufferingManager->DispatcherList);
+    KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
 
-    /* Reference our container before returning it */
-    if (RdbssReferenceTracingValue != 0)
+    /* If there were requests */
+    if (!IsListEmpty(&DispatcherList))
     {
-        NODE_TYPE_CODE Type;
+        PLIST_ENTRY Entry;
 
-        Type = NodeType(Container);
-        switch (Type)
+        /* For each of the entries... */
+        Entry = DispatcherList.Flink;
+        while (Entry != &DispatcherList)
         {
-            case RDBSS_NTC_SRVCALL:
-                RxReferenceSrvCall(Container);
-                break;
+            PCHANGE_BUFFERING_STATE_REQUEST Request;
 
-            case RDBSS_NTC_NETROOT:
-                RxReferenceNetRoot(Container);
-                break;
+            Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+            Entry = Entry->Flink;
 
-            case RDBSS_NTC_V_NETROOT:
-                RxReferenceVNetRoot(Container);
-                break;
+            /* If we have been provided a SRV_OPEN, see whether it matches */
+            if (SrvOpen != NULL)
+            {
+                /* Match, the request is accepted */
+                if (Request->SrvOpenKey == SrvOpen->Key)
+                {
+                    Request->SrvOpen = SrvOpen;
+                    RxReferenceSrvOpen(SrvOpen);
 
-            default:
-                ASSERT(FALSE);
-                break;
+                    RemoveEntryList(&Request->ListEntry);
+                    InsertTailList(&AcceptedReqs, &Request->ListEntry);
+
+                    /* Move to the next entry */
+                    continue;
+                }
+                else
+                {
+                    Status = STATUS_PENDING;
+                }
+            }
+            else
+            {
+                /* No SRV_OPEN provided, try to find one */
+                Status = RxpLookupSrvOpenForRequestLite(SrvCall, Request);
+            }
+
+            /* We found a matching SRV_OPEN, accept the request */
+            if (Status == STATUS_SUCCESS)
+            {
+                RemoveEntryList(&Request->ListEntry);
+                InsertTailList(&AcceptedReqs, &Request->ListEntry);
+            }
+            /* Another run might help handling it, don't discard it */
+            else if (Status == STATUS_PENDING)
+            {
+                continue;
+            }
+            /* Otherwise, discard the request */
+            else
+            {
+                ASSERT(Status == STATUS_NOT_FOUND);
+
+                RemoveEntryList(&Request->ListEntry);
+                InsertTailList(DiscardedRequests, &Request->ListEntry);
+            }
         }
     }
+
+    KeAcquireSpinLock(&BufferingManager->SpinLock, &OldIrql);
+    /* Nothing to dispatch, no need to start dispatcher */
+    if (IsListEmpty(&DispatcherList))
+    {
+        StartDispatcher = FALSE;
+    }
     else
     {
-        RxReference(Container);
+        /* Transfer back the list of the not treated entries to the buffering manager */
+        RxTransferList(&BufferingManager->DispatcherList, &DispatcherList);
+        StartDispatcher = (BufferingManager->DispatcherActive == FALSE);
+        /* If the dispatcher isn't active, start it */
+        if (StartDispatcher)
+        {
+            BufferingManager->DispatcherActive = TRUE;
+        }
     }
 
-    return Container;
-}
+    /* If there were accepted requests, move them to the buffering manager */
+    if (!IsListEmpty(&AcceptedReqs))
+    {
+        RxTransferList(&BufferingManager->HandlerList, &AcceptedReqs);
+    }
+    KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
 
-LONG
-RxpReferenceNetFcb(
-   PFCB Fcb)
-{
-    UNIMPLEMENTED;
-    return 0;
+    /* If we're to start the dispatcher, do it */
+    if (StartDispatcher)
+    {
+        RxReferenceSrvCall(SrvCall);
+        DPRINT("Starting dispatcher\n");
+        RxPostToWorkerThread(RxFileSystemDeviceObject, HyperCriticalWorkQueue,
+                             &BufferingManager->DispatcherWorkItem,
+                             RxDispatchChangeBufferingStateRequests, SrvCall);
+    }
 }
 
 /*
  * @implemented
  */
-VOID
-RxpReleasePrefixTableLock(
-   PRX_PREFIX_TABLE pTable,
-   BOOLEAN ProcessBufferingStateChangeRequests)
+NTSTATUS
+RxpLookupSrvOpenForRequestLite(
+    IN PSRV_CALL SrvCall,
+    IN OUT PCHANGE_BUFFERING_STATE_REQUEST Request)
 {
+    NTSTATUS Status;
+    PLIST_ENTRY Entry;
+    PSRV_OPEN SrvOpen;
+
     PAGED_CODE();
 
-    DPRINT("RxpReleasePrefixTableLock(%p, %d) -> %d\n", pTable, ProcessBufferingStateChangeRequests,
-           pTable->TableLock.ActiveEntries);
+    Status = STATUS_SUCCESS;
+    /* Browse all our associated SRV_OPENs to find the one! */
+    for (Entry = SrvCall->BufferingManager.SrvOpenLists[0].Flink;
+         Entry != &SrvCall->BufferingManager.SrvOpenLists[0];
+         Entry = Entry->Flink)
+    {
+        /* Same key, not orphaned, this is ours */
+        SrvOpen = CONTAINING_RECORD(Entry, SRV_OPEN, SrvOpenKeyList);
+        if (SrvOpen->Key == Request->SrvOpenKey)
+        {
+            if (!BooleanFlagOn(SrvOpen->pFcb->FcbState, FCB_STATE_ORPHANED))
+            {
+                RxReferenceSrvOpen(SrvOpen);
+                break;
+            }
+        }
+    }
 
-    ExReleaseResourceLite(&pTable->TableLock);
+    /* We didn't manage to find a SRV_OPEN */
+    if (Entry == &SrvCall->BufferingManager.SrvOpenLists[0])
+    {
+        SrvOpen = NULL;
+
+        /* The coming open might help, mark as pending for later retry */
+        if (SrvCall->BufferingManager.NumberOfOutstandingOpens != 0)
+        {
+            Status = STATUS_PENDING;
+        }
+        /* Else, it's a complete failure */
+        else
+        {
+            Status = STATUS_NOT_FOUND;
+        }
+    }
+
+    /* Return the (not) found SRV_OPEN */
+    Request->SrvOpen = SrvOpen;
+
+    return Status;
 }
 
 /*
  * @implemented
  */
 VOID
-NTAPI
-RxPrepareContextForReuse(
-   IN OUT PRX_CONTEXT RxContext)
+RxpMarkInstanceForScavengedFinalization(
+   PVOID Instance)
 {
+    NODE_TYPE_CODE NodeType;
+    PNODE_TYPE_AND_SIZE Node;
+    PRDBSS_SCAVENGER Scavenger;
+    PRDBSS_DEVICE_OBJECT DeviceObject;
+    PLIST_ENTRY ScavengerHead, InstEntry;
+
     PAGED_CODE();
 
-    /* When we reach that point, make sure mandatory parts are null-ed */
-    if (RxContext->MajorFunction == IRP_MJ_CREATE)
-    {
-        ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
-        RxContext->Create.RdrFlags = 0;
-    }
-    else if (RxContext->MajorFunction == IRP_MJ_READ || RxContext->MajorFunction == IRP_MJ_WRITE)
+    /* If still referenced, don't mark it (broken caller) */
+    Node = (PNODE_TYPE_AND_SIZE)Instance;
+    if (Node->NodeReferenceCount > 1)
     {
-        ASSERT(RxContext->RxContextSerializationQLinks.Flink == NULL);
-        ASSERT(RxContext->RxContextSerializationQLinks.Blink == NULL);
+        return;
     }
 
-    RxContext->ReferenceCount = 0;
-}
+    DeviceObject = RxGetDeviceObjectOfInstance(Instance);
+    Scavenger = DeviceObject->pRdbssScavenger;
 
-VOID
-RxProcessFcbChangeBufferingStateRequest(
-    PFCB Fcb)
-{
-    UNIMPLEMENTED;
+    /* Mark the node */
+    NodeType = NodeType(Instance);
+    SetFlag(NodeType(Node), RX_SCAVENGER_MASK);
+    DPRINT("Node %p has now the scavenger mark!\n", Instance);
+
+    /* Increase the count in the scavenger, and queue it */
+    ScavengerHead = NULL;
+    switch (NodeType)
+    {
+        case RDBSS_NTC_FOBX:
+            ++Scavenger->FobxsToBeFinalized;
+            ScavengerHead = &Scavenger->FobxFinalizationList;
+            InstEntry = &((PFOBX)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_SRVCALL:
+            ++Scavenger->SrvCallsToBeFinalized;
+            ScavengerHead = &Scavenger->SrvCallFinalizationList;
+            InstEntry = &((PSRV_CALL)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_NETROOT:
+            ++Scavenger->NetRootsToBeFinalized;
+            ScavengerHead = &Scavenger->NetRootFinalizationList;
+            InstEntry = &((PNET_ROOT)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_V_NETROOT:
+            ++Scavenger->VNetRootsToBeFinalized;
+            ScavengerHead = &Scavenger->VNetRootFinalizationList;
+            InstEntry = &((PV_NET_ROOT)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_SRVOPEN:
+            ++Scavenger->SrvOpensToBeFinalized;
+            ScavengerHead = &Scavenger->SrvOpenFinalizationList;
+            InstEntry = &((PSRV_OPEN)Instance)->ScavengerFinalizationList;
+            break;
+    }
+
+    /* Extra ref for scavenger */
+    InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
+
+    /* If matching type */
+    if (ScavengerHead != NULL)
+    {
+        /* Insert in the scavenger list */
+        InsertTailList(ScavengerHead, InstEntry);
+
+        /* And if it wasn't started, start it */
+        if (Scavenger->State == RDBSS_SCAVENGER_INACTIVE)
+        {
+            Scavenger->State = RDBSS_SCAVENGER_DORMANT;
+            RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem,
+                                      RxScavengerTimerRoutine, DeviceObject, Scavenger->TimeLimit);
+        }
+    }
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RxPostOneShotTimerRequest(
+    IN PRDBSS_DEVICE_OBJECT pDeviceObject,
+    IN PRX_WORK_ITEM pWorkItem,
+    IN PRX_WORKERTHREAD_ROUTINE Routine,
+    IN PVOID pContext,
+    IN LARGE_INTEGER TimeInterval)
+{
+    KIRQL OldIrql;
+
+    ASSERT(pWorkItem != NULL);
+
+    /* Prepare the work item */
+    ExInitializeWorkItem(&pWorkItem->WorkQueueItem, Routine, pContext);
+    pWorkItem->WorkQueueItem.pDeviceObject = pDeviceObject;
+
+    /* Last tick can be computed with the number of times it was caller (timertickcount)
+     * and the interval between calls
+     */
+    KeAcquireSpinLock(&RxTimerLock, &OldIrql);
+    pWorkItem->LastTick = (TimeInterval.QuadPart / 550000) + RxTimerTickCount + 1;
+    /* Insert in work queue */
+    InsertTailList(&RxTimerQueueHead, &pWorkItem->WorkQueueItem.List);
+    KeReleaseSpinLock(&RxTimerLock, OldIrql);
+
+    /* If there are queued events, queue an execution */
+    if (IsListEmpty(&RxTimerQueueHead))
+    {
+        KeSetTimer(&RxTimer, RxTimerInterval, &RxTimerDpc);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RxPostToWorkerThread(
+    _In_ PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
+    _In_ WORK_QUEUE_TYPE WorkQueueType,
+    _In_ PRX_WORK_QUEUE_ITEM pWorkQueueItem,
+    _In_ PRX_WORKERTHREAD_ROUTINE Routine,
+    _In_ PVOID pContext)
+{
+    /* Initialize work queue item */
+    pWorkQueueItem->List.Flink = NULL;
+    pWorkQueueItem->WorkerRoutine = Routine;
+    pWorkQueueItem->Parameter = pContext;
+
+    /* And insert it in the work queue */
+    return RxInsertWorkQueueItem(pMRxDeviceObject, WorkQueueType, pWorkQueueItem);
+}
+
+VOID
+RxpProcessChangeBufferingStateRequests(
+    PSRV_CALL SrvCall,
+    BOOLEAN UpdateHandlerState)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+PRX_PREFIX_ENTRY
+RxPrefixTableInsertName(
+    IN OUT PRX_PREFIX_TABLE ThisTable,
+    IN OUT PRX_PREFIX_ENTRY ThisEntry,
+    IN PVOID Container,
+    IN PULONG ContainerRefCount,
+    IN USHORT CaseInsensitiveLength,
+    IN PRX_CONNECTION_ID ConnectionId
+    )
+{
+    PAGED_CODE();
+
+    DPRINT("Insert: %wZ\n", &ThisEntry->Prefix);
+
+    ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
+    ASSERT(CaseInsensitiveLength <= ThisEntry->Prefix.Length);
+
+    /* Copy parameters and compute hash */
+    ThisEntry->CaseInsensitiveLength = CaseInsensitiveLength;
+    ThisEntry->ContainingRecord = Container;
+    ThisEntry->ContainerRefCount = ContainerRefCount;
+    InterlockedIncrement((volatile long *)ContainerRefCount);
+    ThisEntry->SavedHashValue = RxTableComputeHashValue(&ThisEntry->Prefix);
+    DPRINT("Associated hash: %x\n", ThisEntry->SavedHashValue);
+
+    /* If no path length: this is entry for null path */
+    if (ThisEntry->Prefix.Length == 0)
+    {
+        ThisTable->TableEntryForNull = ThisEntry;
+    }
+    /* Otherwise, insert in the appropriate bucket */
+    else
+    {
+        InsertTailList(HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue), &ThisEntry->HashLinks);
+    }
+
+    /* If we had a connection ID, keep track of it */
+    if (ConnectionId != NULL)
+    {
+        ThisEntry->ConnectionId.Luid = ConnectionId->Luid;
+    }
+    else
+    {
+        ThisEntry->ConnectionId.Luid.LowPart = 0;
+        ThisEntry->ConnectionId.Luid.HighPart = 0;
+    }
+
+    InsertTailList(&ThisTable->MemberQueue, &ThisEntry->MemberQLinks);
+    /* Reflect the changes */
+    ++ThisTable->Version;
+
+    DPRINT("Inserted in bucket: %p\n", HASH_BUCKET(ThisTable, ThisEntry->SavedHashValue));
+
+    return ThisEntry;
+}
+
+/*
+ * @implemented
+ */
+PVOID
+RxPrefixTableLookupName(
+    IN PRX_PREFIX_TABLE ThisTable,
+    IN PUNICODE_STRING CanonicalName,
+    OUT PUNICODE_STRING RemainingName,
+    IN PRX_CONNECTION_ID ConnectionId)
+{
+    PVOID Container;
+
+    PAGED_CODE();
+
+    ASSERT(RxIsPrefixTableLockAcquired(ThisTable));
+    ASSERT(CanonicalName->Length > 0);
+
+    /* Call the internal helper */
+    Container = RxTableLookupName(ThisTable, CanonicalName, RemainingName, ConnectionId);
+    if (Container == NULL)
+    {
+        return NULL;
+    }
+
+    /* Reference our container before returning it */
+    if (RdbssReferenceTracingValue != 0)
+    {
+        NODE_TYPE_CODE Type;
+
+        Type = (NodeType(Container) & ~RX_SCAVENGER_MASK);
+        switch (Type)
+        {
+            case RDBSS_NTC_SRVCALL:
+                RxReferenceSrvCall(Container);
+                break;
+
+            case RDBSS_NTC_NETROOT:
+                RxReferenceNetRoot(Container);
+                break;
+
+            case RDBSS_NTC_V_NETROOT:
+                RxReferenceVNetRoot(Container);
+                break;
+
+            default:
+                DPRINT1("Invalid node type: %x\n", Type);
+                ASSERT(FALSE);
+                RxReference(Container);
+                break;
+        }
+    }
+    else
+    {
+        RxReference(Container);
+    }
+
+    return Container;
+}
+
+/*
+ * @implemented
+ */
+LONG
+RxpReferenceNetFcb(
+   PFCB Fcb)
+{
+    LONG NewCount;
+
+    PAGED_CODE();
+
+    ASSERT(NodeTypeIsFcb(Fcb));
+
+    NewCount = InterlockedIncrement((volatile long *)&Fcb->NodeReferenceCount);
+
+    PRINT_REF_COUNT(NETFCB, Fcb->NodeReferenceCount);
+
+    return NewCount;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxpReleasePrefixTableLock(
+   PRX_PREFIX_TABLE pTable,
+   BOOLEAN ProcessBufferingStateChangeRequests)
+{
+    PAGED_CODE();
+
+    DPRINT("RxpReleasePrefixTableLock(%p, %d) -> %d\n", pTable, ProcessBufferingStateChangeRequests,
+           pTable->TableLock.ActiveEntries);
+
+    ExReleaseResourceLite(&pTable->TableLock);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxPrepareContextForReuse(
+   IN OUT PRX_CONTEXT RxContext)
+{
+    PAGED_CODE();
+
+    /* When we reach that point, make sure mandatory parts are null-ed */
+    if (RxContext->MajorFunction == IRP_MJ_CREATE)
+    {
+        ASSERT(RxContext->Create.CanonicalNameBuffer == NULL);
+        RxContext->Create.RdrFlags = 0;
+    }
+    else if (RxContext->MajorFunction == IRP_MJ_READ || RxContext->MajorFunction == IRP_MJ_WRITE)
+    {
+        ASSERT(RxContext->RxContextSerializationQLinks.Flink == NULL);
+        ASSERT(RxContext->RxContextSerializationQLinks.Blink == NULL);
+    }
+
+    RxContext->ReferenceCount = 0;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxPrepareRequestForReuse(
+    PCHANGE_BUFFERING_STATE_REQUEST Request)
+{
+    PSRV_OPEN SrvOpen;
+
+    PAGED_CODE();
+
+    SrvOpen = Request->SrvOpen;
+
+    /* If the request was already prepared for service */
+    if (BooleanFlagOn(Request->Flags, RX_REQUEST_PREPARED_FOR_HANDLING))
+    {
+        /* We have to dereference the associated SRV_OPEN depending on the lock */
+        if (RxIsFcbAcquiredExclusive(SrvOpen->pFcb))
+        {
+            RxDereferenceSrvOpen(SrvOpen, LHS_ExclusiveLockHeld);
+        }
+        else
+        {
+            RxDereferenceSrvOpen(SrvOpen, LHS_LockNotHeld);
+        }
+    }
+    /* Otherwise, just dereference */
+    else if (SrvOpen != NULL)
+    {
+        RxDereferenceSrvOpen(SrvOpen, LHS_LockNotHeld);
+    }
+
+    Request->SrvOpen = NULL;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxProcessChangeBufferingStateRequests(
+    _In_ PVOID SrvCall)
+{
+    /* Call internal routine */
+    RxUndoScavengerFinalizationMarking(SrvCall);
+    RxpProcessChangeBufferingStateRequests(SrvCall, TRUE);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxProcessChangeBufferingStateRequestsForSrvOpen(
+    PSRV_OPEN SrvOpen)
+{
+    LONG NumberOfBufferingChangeRequests, LockedOldBufferingToken, OldBufferingToken;
+
+    /* Get the current number of change requests */
+    NumberOfBufferingChangeRequests = ((PSRV_CALL)SrvOpen->pVNetRoot->pNetRoot->pSrvCall)->BufferingManager.CumulativeNumberOfBufferingChangeRequests;
+    /* Get our old token */
+    OldBufferingToken = SrvOpen->BufferingToken;
+    LockedOldBufferingToken = InterlockedCompareExchange(&SrvOpen->BufferingToken,
+                                                         NumberOfBufferingChangeRequests,
+                                                         NumberOfBufferingChangeRequests);
+    /* If buffering state changed in between, process changes */
+    if (OldBufferingToken != LockedOldBufferingToken)
+    {
+        PFCB Fcb;
+        NTSTATUS Status;
+
+        /* Acquire the FCB and start processing */
+        Fcb = (PFCB)SrvOpen->pFcb;
+        Status = RxAcquireExclusiveFcb(NULL, Fcb);
+        if (Status == STATUS_SUCCESS)
+        {
+            RxProcessFcbChangeBufferingStateRequest(Fcb);
+            RxReleaseFcb(NULL, Fcb);
+        }
+    }
+}
+
+VOID
+RxProcessFcbChangeBufferingStateRequest(
+    PFCB Fcb)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxpScavengeFobxs(
+    PRDBSS_SCAVENGER Scavenger,
+    PLIST_ENTRY FobxToScavenge)
+{
+    /* Explore the whole list of FOBX to scavenge */
+    while (!IsListEmpty(FobxToScavenge))
+    {
+        PFCB Fcb;
+        PFOBX Fobx;
+        PLIST_ENTRY Entry;
+
+        Entry = RemoveHeadList(FobxToScavenge);
+        Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList);
+        Fcb = (PFCB)Fobx->SrvOpen->pFcb;
+
+        /* Try to acquire the lock exclusively to perform finalization */
+        if (RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS)
+        {
+            RxDereferenceNetRoot(Fobx, LHS_LockNotHeld);
+        }
+        else
+        {
+            RxReferenceNetFcb(Fcb);
+            RxDereferenceNetRoot(Fobx, LHS_ExclusiveLockHeld);
+
+            if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
+            {
+                RxReleaseFcb(NULL, Fcb);
+            }
+        }
+    }
+}
+
+BOOLEAN
+RxpTrackDereference(
+    _In_ ULONG TraceType,
+    _In_ PCSTR FileName,
+    _In_ ULONG Line,
+    _In_ PVOID Instance)
+{
+    PCSTR InstanceType;
+    ULONG ReferenceCount;
+
+    PAGED_CODE();
+
+    if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
+    {
+        return TRUE;
+    }
+
+    switch (TraceType)
+    {
+        case RDBSS_REF_TRACK_SRVCALL:
+            InstanceType = "SrvCall";
+            ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETROOT:
+            InstanceType = "NetRoot";
+            ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_VNETROOT:
+            InstanceType = "VNetRoot";
+            ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETFOBX:
+            InstanceType = "NetFobx";
+            ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETFCB:
+            InstanceType = "NetFcb";
+            ReferenceCount = ((PFCB)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_SRVOPEN:
+            InstanceType = "SrvOpen";
+            ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount;
+            break;
+
+        default:
+            DPRINT1("Invalid node type!\n");
+            return TRUE;
+    }
+
+    if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING))
+    {
+        UNIMPLEMENTED;
+    }
+
+    if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING))
+    {
+        DbgPrint("(%s:%d) %p (%s) dereferenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount);
+    }
+
+    return TRUE;
+}
+
+VOID
+RxpTrackReference(
+    _In_ ULONG TraceType,
+    _In_ PCSTR FileName,
+    _In_ ULONG Line,
+    _In_ PVOID Instance)
+{
+    PCSTR InstanceType;
+    ULONG ReferenceCount;
+
+    if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
+    {
+        return;
+    }
+
+    switch (TraceType)
+    {
+        case RDBSS_REF_TRACK_SRVCALL:
+            InstanceType = "SrvCall";
+            ReferenceCount = ((PSRV_CALL)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETROOT:
+            InstanceType = "NetRoot";
+            ReferenceCount = ((PNET_ROOT)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_VNETROOT:
+            InstanceType = "VNetRoot";
+            ReferenceCount = ((PV_NET_ROOT)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETFOBX:
+            InstanceType = "NetFobx";
+            ReferenceCount = ((PFOBX)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_NETFCB:
+            InstanceType = "NetFcb";
+            ReferenceCount = ((PFCB)Instance)->NodeReferenceCount;
+            break;
+
+        case RDBSS_REF_TRACK_SRVOPEN:
+            InstanceType = "SrvOpen";
+            ReferenceCount = ((PSRV_OPEN)Instance)->NodeReferenceCount;
+            break;
+
+        default:
+            DPRINT1("Invalid node type!\n");
+            return;
+    }
+
+    if (BooleanFlagOn(RdbssReferenceTracingValue, RX_LOG_REF_TRACKING))
+    {
+        UNIMPLEMENTED;
+    }
+
+    if (BooleanFlagOn(RdbssReferenceTracingValue, RX_PRINT_REF_TRACKING))
+    {
+        DbgPrint("(%s:%d) %p (%s) referenced from %d\n", FileName, Line, Instance, InstanceType, ReferenceCount);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxpUndoScavengerFinalizationMarking(
+   PVOID Instance)
+{
+    PLIST_ENTRY ListEntry;
+    PNODE_TYPE_AND_SIZE Node;
+    PRDBSS_SCAVENGER Scavenger;
+
+    PAGED_CODE();
+
+    Node = (PNODE_TYPE_AND_SIZE)Instance;
+    /* There's no marking - nothing to do */
+    if (!BooleanFlagOn(NodeType(Node), RX_SCAVENGER_MASK))
+    {
+        return;
+    }
+
+    /* First of all, remove the mark */
+    ClearFlag(NodeType(Node), RX_SCAVENGER_MASK);
+    DPRINT("Node %p no longer has the scavenger mark\n");
+
+    /* And now, remove from the scavenger */
+    Scavenger = RxGetDeviceObjectOfInstance(Instance)->pRdbssScavenger;
+    switch (NodeType(Node))
+    {
+        case RDBSS_NTC_FOBX:
+            --Scavenger->FobxsToBeFinalized;
+            ListEntry = &((PFOBX)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_SRVCALL:
+            --Scavenger->SrvCallsToBeFinalized;
+            ListEntry = &((PSRV_CALL)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_NETROOT:
+            --Scavenger->NetRootsToBeFinalized;
+            ListEntry = &((PNET_ROOT)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_V_NETROOT:
+            --Scavenger->VNetRootsToBeFinalized;
+            ListEntry = &((PV_NET_ROOT)Instance)->ScavengerFinalizationList;
+            break;
+
+        case RDBSS_NTC_SRVOPEN:
+            --Scavenger->SrvOpensToBeFinalized;
+            ListEntry = &((PSRV_OPEN)Instance)->ScavengerFinalizationList;
+            break;
+    }
+
+    /* Also, remove the extra ref from the scavenger */
+    RemoveEntryList(ListEntry);
+    InterlockedDecrement((volatile long *)&Node->NodeReferenceCount);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxPurgeChangeBufferingStateRequestsForSrvOpen(
+    PSRV_OPEN SrvOpen)
+{
+    PSRV_CALL SrvCall;
+    LIST_ENTRY Discarded;
+
+    PAGED_CODE();
+
+    ASSERT(RxIsFcbAcquiredExclusive(SrvOpen->Fcb));
+
+    /* Initialize our discarded list */
+    InitializeListHead(&Discarded);
+
+    SrvCall = (PSRV_CALL)SrvOpen->Fcb->VNetRoot->pNetRoot->pSrvCall;
+    RxAcquireBufferingManagerMutex(&SrvCall->BufferingManager);
+
+    /* Set the flag, and get the requests */
+    InitializeListHead(&SrvOpen->SrvOpenKeyList);
+    SetFlag(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_REQUESTS_PURGED);
+    RxGatherRequestsForSrvOpen(SrvCall, SrvOpen, &Discarded);
+
+    RxReleaseBufferingManagerMutex(&SrvCall->BufferingManager);
+
+    /* If there were discarded requests */
+    if (!IsListEmpty(&Discarded))
+    {
+        /* And a pending buffering state change */
+        if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING))
+        {
+            /* Clear the flag, and set the associated event - job done */
+            RxAcquireSerializationMutex();
+            ClearFlag(SrvOpen->Fcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
+            if (SrvOpen->Fcb->pBufferingStateChangeCompletedEvent != NULL)
+            {
+                KeSetEvent(SrvOpen->Fcb->pBufferingStateChangeCompletedEvent, IO_NETWORK_INCREMENT, FALSE);
+            }
+            RxReleaseSerializationMutex();
+        }
+
+        /* Drop the discarded requests */
+        RxpDiscardChangeBufferingStateRequests(&Discarded);
+    }
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxPurgeFcb(
+    IN  PFCB Fcb)
+{
+    PAGED_CODE();
+
+    ASSERT(RxIsFcbAcquiredExclusive(Fcb));
+
+    /* Reference our FCB so that it doesn't disappear */
+    RxReferenceNetFcb(Fcb);
+    /* Purge Cc if required */
+    if (Fcb->OpenCount != 0)
+    {
+        RxPurgeFcbInSystemCache(Fcb, NULL, 0, TRUE, TRUE);
+    }
+
+    /* If it wasn't freed, release the lock */
+    if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
+    {
+        RxReleaseFcb(NULL, Fcb);
+    }
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+RxPurgeFcbInSystemCache(
+    IN PFCB Fcb,
+    IN PLARGE_INTEGER FileOffset OPTIONAL,
+    IN ULONG Length,
+    IN BOOLEAN UninitializeCacheMaps,
+    IN BOOLEAN FlushFile)
+{
+    BOOLEAN Purged;
+    NTSTATUS Status;
+
+    PAGED_CODE();
+
+    ASSERT(RxIsFcbAcquiredExclusive(Fcb));
+
+    /* Try to flush first, if asked */
+    if (FlushFile)
+    {
+        /* If flushing failed, just make some noise */
+        Status = RxFlushFcbInSystemCache(Fcb, TRUE);
+        if (!NT_SUCCESS(Status))
+        {
+            PVOID CallersAddress, CallersCaller;
+
+            RtlGetCallersAddress(&CallersAddress, &CallersCaller);
+            DPRINT1("Flush failed with status %lx for FCB %p\n", Status, Fcb);
+            DPRINT1("Caller was %p %p\n", CallersAddress, CallersCaller);
+        }
+    }
+
+    /* Deal with Cc for purge */
+    Purged = CcPurgeCacheSection(&Fcb->NonPaged->SectionObjectPointers, FileOffset,
+                                 Length, UninitializeCacheMaps);
+    /* If purge failed, force section closing */
+    if (!Purged)
+    {
+        MmFlushImageSection(&Fcb->NonPaged->SectionObjectPointers, MmFlushForWrite);
+
+        RxReleaseFcb(NULL, Fcb);
+        Purged = MmForceSectionClosed(&Fcb->NonPaged->SectionObjectPointers, TRUE);
+        RxAcquireExclusiveFcb(NULL, Fcb);
+    }
+
+    /* Return appropriate status */
+    Status = (Purged ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
+    DPRINT("Purge for FCB %p returns %lx\n", Fcb, Status);
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+BOOLEAN
+RxPurgeFobx(
+    PFOBX pFobx)
+{
+    NTSTATUS Status;
+    PFCB FcbToBePurged;
+
+    PAGED_CODE();
+
+    /* Get the associated FCB */
+    FcbToBePurged = (PFCB)pFobx->pSrvOpen->pFcb;
+    Status = RxAcquireExclusiveFcb(NULL, FcbToBePurged);
+    ASSERT(Status == STATUS_SUCCESS);
+
+    /* Purge it */
+    Status = RxPurgeFcbInSystemCache(FcbToBePurged, NULL, 0, FALSE, TRUE);
+    if (Status != STATUS_SUCCESS)
+    {
+        DPRINT1("Purge failed for %p (%p)\n", FcbToBePurged, pFobx);
+        return FALSE;
+    }
+
+    /* And flush */
+    if (!MmFlushImageSection(&FcbToBePurged->NonPaged->SectionObjectPointers, MmFlushForWrite))
+    {
+        DPRINT1("Image section flush failed for %p (%p)\n", FcbToBePurged, pFobx);
+        return FALSE;
+    }
+
+    DPRINT("Purge OK for %p (%p)\n", FcbToBePurged, pFobx);
+    return TRUE;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+RxPurgeFobxFromCache(
+    PFOBX FobxToBePurged)
+{
+    NTSTATUS Status;
+    PFCB FcbToBePurged;
+
+    PAGED_CODE();
+
+    FcbToBePurged = (PFCB)FobxToBePurged->pSrvOpen->pFcb;
+    ASSERT(FcbToBePurged != NULL);
+
+    /* If we cannot have our FCB exclusively, give up */
+    Status = RxAcquireExclusiveFcb(NULL, FcbToBePurged);
+    if (Status != STATUS_SUCCESS)
+    {
+        RxDereferenceNetFobx(FobxToBePurged, LHS_LockNotHeld);
+        return Status;
+    }
+
+    /* Don't let the FCB disappear */
+    RxReferenceNetFcb(FcbToBePurged);
+
+    /* If the SRV_OPEN was already closed, or if there are unclean FOBX, give up */
+    if (BooleanFlagOn(FobxToBePurged->Flags, FOBX_FLAG_SRVOPEN_CLOSED) || FobxToBePurged->pSrvOpen->UncleanFobxCount != 0)
+    {
+        DPRINT("FCB purge skipped\n");
+    }
+    else
+    {
+        Status = RxPurgeFcbInSystemCache(FcbToBePurged, NULL, 0, FALSE, TRUE);
+    }
+
+    RxDereferenceNetFobx(FobxToBePurged, LHS_ExclusiveLockHeld);
+    /* Drop our extra reference */
+    if (!RxDereferenceAndFinalizeNetFcb(FcbToBePurged, NULL, FALSE, FALSE))
+    {
+        RxReleaseFcb(NULL, FcbToBePurged);
+    }
+
+    return Status;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+RxPurgeRelatedFobxs(
+    PNET_ROOT NetRoot,
+    PRX_CONTEXT RxContext,
+    BOOLEAN AttemptFinalization,
+    PFCB PurgingFcb)
+{
+    PLIST_ENTRY Entry;
+    ULONG SuccessfullPurge;
+    PRDBSS_SCAVENGER Scavenger;
+    PRDBSS_DEVICE_OBJECT RxDeviceObject;
+    PPURGE_SYNCHRONIZATION_CONTEXT PurgeSyncCtx;
+
+    PAGED_CODE();
+
+    RxDeviceObject = RxContext->RxDeviceObject;
+    Scavenger = RxDeviceObject->pRdbssScavenger;
+    PurgeSyncCtx = &NetRoot->PurgeSyncronizationContext;
+
+    RxAcquireScavengerMutex();
+
+    /* If there's already a purge in progress */
+    if (PurgeSyncCtx->PurgeInProgress)
+    {
+        /* Add our RX_CONTEXT to the current run */
+        InsertTailList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion,
+                       &RxContext->RxContextSerializationQLinks);
+
+        /* And wait until it's done */
+        RxReleaseScavengerMutex();
+        RxWaitSync(RxContext);
+        RxAcquireScavengerMutex();
+    }
+
+    /* Start the purge */
+    PurgeSyncCtx->PurgeInProgress = TRUE;
+
+    /* While the purge is still handling our NET_ROOT, do nothing but wait */
+    while (Scavenger->CurrentNetRootForClosePendingProcessing == NetRoot)
+    {
+        RxReleaseScavengerMutex();
+        KeWaitForSingleObject(&Scavenger->ClosePendingProcessingSyncEvent, Executive,
+                              KernelMode, TRUE, NULL);
+        RxAcquireScavengerMutex();
+    }
+
+    /* Now, for all the entries */
+    SuccessfullPurge = 0;
+    Entry = Scavenger->ClosePendingFobxsList.Flink;
+    while (Entry != &Scavenger->ClosePendingFobxsList)
+    {
+        PFCB Fcb;
+        PFOBX Fobx;
+        BOOLEAN Success;
+
+        Fobx = CONTAINING_RECORD(Entry, FOBX, ClosePendingList);
+        DPRINT("Dealing with FOBX: %p\n", Fobx);
+
+        Entry = Entry->Flink;
+
+        /* If it's not matching our NET_ROOT, ignore */
+        if (Fobx->pSrvOpen == NULL ||
+            Fobx->pSrvOpen->pFcb == NULL ||
+            ((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot == NULL ||
+            (PNET_ROOT)((PFCB)Fobx->pSrvOpen->pFcb)->VNetRoot->pNetRoot != NetRoot)
+        {
+            continue;
+        }
+
+        /* Determine if it matches our FCB */
+        Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
+        if (PurgingFcb != NULL && NodeType(PurgingFcb) != RDBSS_NTC_STORAGE_TYPE_DIRECTORY &&
+            PurgingFcb != Fcb)
+        {
+            NTSTATUS Status;
+
+            MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb));
+            if (Status == STATUS_SUCCESS)
+            {
+                continue;
+            }
+        }
+
+        /* Matching, we'll purge it */
+        RemoveEntryList(&Fobx->ClosePendingList);
+
+        /* Reference it so that it doesn't disappear */
+        RxReferenceNetFobx(Fobx);
+
+        RxReleaseScavengerMutex();
+
+        /* And purge */
+        Success = RxPurgeFobx(Fobx);
+        if (Success)
+        {
+            ++SuccessfullPurge;
+        }
+
+        /* If we don't have to finalize it (or if we cannot acquire lock exclusively
+         * Just normally dereference
+         */
+        if ((AttemptFinalization == DONT_ATTEMPT_FINALIZE_ON_PURGE) ||
+            RxAcquireExclusiveFcb(NULL, Fcb) != STATUS_SUCCESS)
+        {
+            RxDereferenceNetFobx(Fobx, LHS_LockNotHeld);
+        }
+        /* Otherwise, finalize */
+        else
+        {
+            RxReferenceNetFcb(Fcb);
+            RxDereferenceNetFobx(Fobx, LHS_ExclusiveLockHeld);
+            if (!RxDereferenceAndFinalizeNetFcb(Fcb, NULL, FALSE, FALSE))
+            {
+                RxReleaseFcb(NULL, Fcb);
+            }
+        }
+
+        if (!Success)
+        {
+            DPRINT1("Failed purging %p (%p)\n", Fcb, Fobx);
+        }
+
+        RxAcquireScavengerMutex();
+    }
+
+    /* If no contexts left, purge is not running */
+    if (IsListEmpty(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion))
+    {
+        PurgeSyncCtx->PurgeInProgress = FALSE;
+    }
+    /* Otherwise, notify a waiter it can start */
+    else
+    {
+        PRX_CONTEXT Context;
+
+        Entry = RemoveHeadList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion);
+        Context = CONTAINING_RECORD(Entry, RX_CONTEXT, RxContextSerializationQLinks);
+
+        RxSignalSynchronousWaiter(Context);
+    }
+
+    RxReleaseScavengerMutex();
+
+    return (SuccessfullPurge > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxpWorkerThreadDispatcher(
+   IN PRX_WORK_QUEUE WorkQueue,
+   IN PLARGE_INTEGER WaitInterval)
+{
+    NTSTATUS Status;
+    PVOID Parameter;
+    PETHREAD CurrentThread;
+    BOOLEAN KillThread, Dereference;
+    PRX_WORK_QUEUE_ITEM WorkQueueItem;
+    PWORKER_THREAD_ROUTINE WorkerRoutine;
+
+    InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
+
+    /* Reference ourselves */
+    CurrentThread = PsGetCurrentThread();
+    Status = ObReferenceObjectByPointer(CurrentThread, THREAD_ALL_ACCESS, *PsThreadType, KernelMode);
+    ASSERT(NT_SUCCESS(Status));
+
+    /* Infinite loop for worker */
+    KillThread = FALSE;
+    Dereference = FALSE;
+    do
+    {
+        KIRQL OldIrql;
+        PLIST_ENTRY ListEntry;
+
+        /* Remove an entry from the work queue */
+        ListEntry = KeRemoveQueue(&WorkQueue->Queue, KernelMode, WaitInterval);
+        if ((ULONG_PTR)ListEntry != STATUS_TIMEOUT)
+        {
+            PRDBSS_DEVICE_OBJECT DeviceObject;
+
+            WorkQueueItem = CONTAINING_RECORD(ListEntry, RX_WORK_QUEUE_ITEM, List);
+
+            InterlockedIncrement(&WorkQueue->NumberOfWorkItemsDispatched);
+            InterlockedDecrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
+            InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+
+            /* Get the parameters, and null-them in the struct */
+            WorkerRoutine = WorkQueueItem->WorkerRoutine;
+            Parameter = WorkQueueItem->Parameter;
+            DeviceObject = WorkQueueItem->pDeviceObject;
+
+            WorkQueueItem->List.Flink = NULL;
+            WorkQueueItem->WorkerRoutine = NULL;
+            WorkQueueItem->Parameter = NULL;
+            WorkQueueItem->pDeviceObject = NULL;
+
+            /* Call the routine */
+            DPRINT("Calling: %p(%p)\n", WorkerRoutine, Parameter);
+            WorkerRoutine(Parameter);
+
+            /* Are we going down now? */
+            if (InterlockedDecrement(&DeviceObject->DispatcherContext.NumberOfWorkerThreads) == 0)
+            {
+                PKEVENT TearDownEvent;
+
+                TearDownEvent = InterlockedExchangePointer((void * volatile*)&DeviceObject->DispatcherContext.pTearDownEvent, NULL);
+                if (TearDownEvent != NULL)
+                {
+                    KeSetEvent(TearDownEvent, IO_NO_INCREMENT, FALSE);
+                }
+            }
+
+            InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
+        }
+
+        /* Shall we shutdown... */
+        KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
+        switch (WorkQueue->State)
+        {
+            /* Our queue is active, kill it if we have no more items to dispatch
+             * and more threads than the required minimum
+             */
+            case RxWorkQueueActive:
+                if (WorkQueue->NumberOfWorkItemsToBeDispatched <= 0)
+                {
+                    ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
+                    if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
+                    {
+                        KillThread = TRUE;
+                        Dereference = TRUE;
+                        InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
+                    }
+
+                    if (KillThread)
+                    {
+                        InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+                    }
+                }
+                break;
+
+            /* The queue is inactive: kill it we have more threads than the required minimum */
+            case RxWorkQueueInactive:
+                ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
+                if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
+                {
+                    KillThread = TRUE;
+                    Dereference = TRUE;
+                    InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
+                }
+
+                if (KillThread)
+                {
+                    InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+                }
+                break;
+
+            /* Rundown in progress..., kill it for sure! */
+            case RxWorkQueueRundownInProgress:
+                {
+                    PRX_WORK_QUEUE_RUNDOWN_CONTEXT RundownContext;
+
+                    ASSERT(WorkQueue->pRundownContext != NULL);
+
+                    RundownContext = WorkQueue->pRundownContext;
+                    RundownContext->ThreadPointers[RundownContext->NumberOfThreadsSpunDown++] = CurrentThread;
+
+                    InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
+                    KillThread = TRUE;
+                    Dereference = FALSE;
+
+                    if (WorkQueue->NumberOfActiveWorkerThreads == 0)
+                    {
+                        KeSetEvent(&RundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
+                    }
+
+                    InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+                }
+                break;
+
+            default:
+                break;
+        }
+        KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
+    } while (!KillThread);
+
+    DPRINT("Killed worker thread\n");
+
+    /* Do we have to dereference ourselves? */
+    if (Dereference)
+    {
+        ObDereferenceObject(CurrentThread);
+    }
+
+    /* Dump last executed routine */
+    if (DumpDispatchRoutine)
+    {
+        DPRINT("Dispatch routine %p(%p) taken from %p\n", WorkerRoutine, Parameter, WorkQueueItem);
+    }
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
 }
 
-BOOLEAN
-RxpTrackDereference(
-    _In_ ULONG TraceType,
-    _In_ PCSTR FileName,
-    _In_ ULONG Line,
-    _In_ PVOID Instance)
+VOID
+RxReference(
+    IN OUT PVOID Instance)
+{
+    NODE_TYPE_CODE NodeType;
+    PNODE_TYPE_AND_SIZE Node;
+
+    PAGED_CODE();
+
+    RxAcquireScavengerMutex();
+
+    /* We can only reference a few structs */
+    NodeType = NodeType(Instance) & ~RX_SCAVENGER_MASK;
+    ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
+           (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
+           (NodeType == RDBSS_NTC_FOBX));
+
+    Node = (PNODE_TYPE_AND_SIZE)Instance;
+    InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
+
+    /* Trace refcount if asked */
+    switch (NodeType)
+    {
+        case RDBSS_NTC_SRVCALL:
+            PRINT_REF_COUNT(SRVCALL, Node->NodeReferenceCount);
+            break;
+
+        case RDBSS_NTC_NETROOT:
+            PRINT_REF_COUNT(NETROOT, Node->NodeReferenceCount);
+            break;
+
+        case RDBSS_NTC_V_NETROOT:
+            PRINT_REF_COUNT(VNETROOT, Node->NodeReferenceCount);
+            break;
+
+        case RDBSS_NTC_SRVOPEN:
+            PRINT_REF_COUNT(SRVOPEN, Node->NodeReferenceCount);
+            break;
+
+        case RDBSS_NTC_FOBX:
+            PRINT_REF_COUNT(NETFOBX, Node->NodeReferenceCount);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+    }
+
+    RxpUndoScavengerFinalizationMarking(Instance);
+    RxReleaseScavengerMutex();
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxReinitializeContext(
+   IN OUT PRX_CONTEXT RxContext)
+{
+    PIRP Irp;
+    PRDBSS_DEVICE_OBJECT RxDeviceObject;
+    ULONG InitialContextFlags, SavedFlags;
+
+    PAGED_CODE();
+
+    /* Backup a few flags */
+    Irp = RxContext->CurrentIrp;
+    RxDeviceObject = RxContext->RxDeviceObject;
+    SavedFlags = RxContext->Flags & RX_CONTEXT_PRESERVED_FLAGS;
+    InitialContextFlags = RxContext->Flags & RX_CONTEXT_INITIALIZATION_FLAGS;
+
+    /* Reset our context */
+    RxPrepareContextForReuse(RxContext);
+
+    /* Zero everything */
+    RtlZeroMemory(&RxContext->MajorFunction, sizeof(RX_CONTEXT) - FIELD_OFFSET(RX_CONTEXT, MajorFunction));
+
+    /* Restore saved flags */
+    RxContext->Flags = SavedFlags;
+    /* And reinit the context */
+    RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, RxContext);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxReleaseFcbFromLazyWrite(
+    PVOID Context)
+{
+    PFCB Fcb;
+
+    PAGED_CODE();
+
+    Fcb = Context;
+    /* The received context is a FCB */
+    ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
+    ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
+
+    /* Lazy writer is releasing lock, so forget about it */
+    Fcb->Specific.Fcb.LazyWriteThread = NULL;
+
+    /* If we were top level IRP, unwind */
+    if (RxGetTopIrpIfRdbssIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP)
+    {
+        RxUnwindTopLevelIrp(NULL);
+    }
+
+    /* And finally, release the lock */
+    Fcb->PagingIoResourceFile = NULL;
+    Fcb->PagingIoResourceLine = 0;
+    ExReleaseResourceLite(Fcb->Header.PagingIoResource);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxReleaseFcbFromReadAhead(
+    PVOID Context)
+{
+    PFCB Fcb;
+
+    PAGED_CODE();
+
+    Fcb = Context;
+    /* The received context is a FCB */
+    ASSERT(NodeType(Fcb) == RDBSS_NTC_FCB);
+    ASSERT_CORRECT_FCB_STRUCTURE(Fcb);
+
+    /* Top Level IRP is CC */
+    ASSERT(RxGetTopIrpIfRdbssIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP);
+    RxUnwindTopLevelIrp(NULL);
+
+    ExReleaseResourceLite(Fcb->Header.Resource);
+}
+
+VOID
+NTAPI
+RxReleaseFileForNtCreateSection(
+    PFILE_OBJECT FileObject)
+{
+    UNIMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+RxReleaseForCcFlush(
+    PFILE_OBJECT FileObject,
+    PDEVICE_OBJECT DeviceObject)
+{
+    UNIMPLEMENTED;
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxRemoveNameNetFcb(
+    OUT PFCB ThisFcb)
+{
+    PNET_ROOT NetRoot;
+
+    PAGED_CODE();
+
+    ASSERT(NodeTypeIsFcb(ThisFcb));
+
+    /* Just remove the entry from the FCB_TABLE */
+    NetRoot = (PNET_ROOT)ThisFcb->VNetRoot->pNetRoot;
+    ASSERT(RxIsFcbTableLockExclusive(&NetRoot->FcbTable));
+    ASSERT(RxIsFcbAcquiredExclusive(ThisFcb));
+
+    RxFcbTableRemoveFcb(&NetRoot->FcbTable, ThisFcb);
+    DPRINT("FCB (%p) %wZ removed\n", ThisFcb, &ThisFcb->FcbTableEntry.Path);
+    /* Mark, so that we don't try to do it twice */
+    SetFlag(ThisFcb->FcbState, FCB_STATE_NAME_ALREADY_REMOVED);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxRemovePrefixTableEntry(
+    IN OUT PRX_PREFIX_TABLE ThisTable,
+    IN OUT PRX_PREFIX_ENTRY Entry)
+{
+    PAGED_CODE();
+
+    ASSERT(NodeType(Entry) == RDBSS_NTC_PREFIX_ENTRY);
+    ASSERT(RxIsPrefixTableLockExclusive(ThisTable));
+
+    /* Check whether we're asked to remove null entry */
+    if (Entry->Prefix.Length == 0)
+    {
+        ThisTable->TableEntryForNull = NULL;
+    }
+    else
+    {
+        RemoveEntryList(&Entry->HashLinks);
+    }
+
+    Entry->ContainingRecord = NULL;
+
+    /* Also remove it from global list */
+    RemoveEntryList(&Entry->MemberQLinks);
+
+    ++ThisTable->Version;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxRemoveVirtualNetRootFromNetRoot(
+    PNET_ROOT NetRoot,
+    PV_NET_ROOT VNetRoot)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    PRX_PREFIX_TABLE PrefixTable;
+
+    PAGED_CODE();
+
+    PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockAcquired(PrefixTable));
+
+    /* Remove the VNetRoot from the list in the NetRoot */
+    --NetRoot->NumberOfVirtualNetRoots;
+    RemoveEntryList(&VNetRoot->NetRootListEntry);
+
+    /* Fix the NetRoot if we were the default VNetRoot */
+    if (NetRoot->DefaultVNetRoot == VNetRoot)
+    {
+        /* Put the first one available */
+        if (!IsListEmpty(&NetRoot->VirtualNetRoots))
+        {
+            NetRoot->DefaultVNetRoot = CONTAINING_RECORD(NetRoot->VirtualNetRoots.Flink, V_NET_ROOT, NetRootListEntry);
+        }
+        /* Otherwise, none */
+        else
+        {
+            NetRoot->DefaultVNetRoot = NULL;
+        }
+    }
+
+    /* If there are still other VNetRoot available, we're done */
+    if (!IsListEmpty(&NetRoot->VirtualNetRoots))
+    {
+        return;
+    }
+
+    /* Otherwise, initiate NetRoot finalization */
+    if (!BooleanFlagOn(NetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED))
+    {
+        RxRemovePrefixTableEntry(PrefixTable, &NetRoot->PrefixEntry);
+        SetFlag(NetRoot->Flags, NETROOT_FLAG_NAME_ALREADY_REMOVED);
+    }
+
+    /* Notify mini-rdr */
+    if (NetRoot->pSrvCall != NULL && NetRoot->pSrvCall->RxDeviceObject != NULL)
+    {
+        NTSTATUS Status;
+
+        MINIRDR_CALL_THROUGH(Status, NetRoot->pSrvCall->RxDeviceObject->Dispatch,
+                             MRxFinalizeNetRoot, ((PMRX_NET_ROOT)NetRoot, FALSE));
+        (void)Status;
+    }
 }
 
 VOID
-RxpTrackReference(
-    _In_ ULONG TraceType,
-    _In_ PCSTR FileName,
-    _In_ ULONG Line,
-    _In_ PVOID Instance)
+RxResumeBlockedOperations_ALL(
+    IN OUT PRX_CONTEXT RxContext)
 {
-    UNIMPLEMENTED;
+    LIST_ENTRY BlockedOps;
+
+    PAGED_CODE();
+
+    /* Get the blocked operations */
+    RxTransferListWithMutex(&BlockedOps, &RxContext->BlockedOperations, RxContext->BlockedOpsMutex);
+
+    if (!IsListEmpty(&BlockedOps))
+    {
+        UNIMPLEMENTED;
+    }
 }
 
 VOID
-RxpUndoScavengerFinalizationMarking(
-   PVOID Instance)
+NTAPI
+RxResumeBlockedOperations_Serially(
+    IN OUT PRX_CONTEXT RxContext,
+    IN OUT PLIST_ENTRY BlockingIoQ)
 {
+    PAGED_CODE();
+
+    RxAcquireSerializationMutex();
+
+    /* This can only happen on pipes */
+    if (!BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION))
+    {
+        RxReleaseSerializationMutex();
+        return;
+    }
+
     UNIMPLEMENTED;
+
+    RxReleaseSerializationMutex();
 }
 
-NTSTATUS
-RxPurgeFcbInSystemCache(
-    IN PFCB Fcb,
-    IN PLARGE_INTEGER FileOffset OPTIONAL,
-    IN ULONG Length,
-    IN BOOLEAN UninitializeCacheMaps,
-    IN BOOLEAN FlushFile)
+/*
+ * @implemented
+ */
+VOID
+RxSetFileSizeWithLock(
+    IN OUT PFCB Fcb,
+    IN PLONGLONG FileSize)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PAGED_CODE();
+
+    /* Set attribute and increase version */
+    Fcb->Header.FileSize.QuadPart = *FileSize;
+    ++Fcb->ulFileSizeVersion;
 }
 
 /*
  * @implemented
  */
 VOID
-RxpWorkerThreadDispatcher(
-   IN PRX_WORK_QUEUE WorkQueue,
-   IN PLARGE_INTEGER WaitInterval)
+RxScavengeFobxsForNetRoot(
+    PNET_ROOT NetRoot,
+    PFCB PurgingFcb,
+    BOOLEAN SynchronizeWithScavenger)
 {
-    NTSTATUS Status;
-    PVOID Parameter;
-    PETHREAD CurrentThread;
-    BOOLEAN KillThread, Dereference;
-    PRX_WORK_QUEUE_ITEM WorkQueueItem;
-    PWORKER_THREAD_ROUTINE WorkerRoutine;
+    PRDBSS_SCAVENGER Scavenger;
+    PRDBSS_DEVICE_OBJECT RxDeviceObject;
 
-    InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
+    PAGED_CODE();
 
-    /* Reference ourselves */
-    CurrentThread = PsGetCurrentThread();
-    Status = ObReferenceObjectByPointer(CurrentThread, THREAD_ALL_ACCESS, PsThreadType, KernelMode);
-    ASSERT(NT_SUCCESS(Status));
+    RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject;
+    Scavenger = RxDeviceObject->pRdbssScavenger;
 
-    /* Infinite loop for worker */
-    KillThread = FALSE;
-    Dereference = FALSE;
-    do
+    /* Wait for the scavenger, if asked to */
+    if (SynchronizeWithScavenger)
     {
-        KIRQL OldIrql;
-        PLIST_ENTRY ListEntry;
-
-        /* Remove an entry from the work queue */
-        ListEntry = KeRemoveQueue(&WorkQueue->Queue, KernelMode, WaitInterval);
-        if ((ULONG_PTR)ListEntry != STATUS_TIMEOUT)
-        {
-            PRDBSS_DEVICE_OBJECT DeviceObject;
+        KeWaitForSingleObject(&Scavenger->SyncEvent, Executive, KernelMode, FALSE, NULL);
+    }
 
-            WorkQueueItem = CONTAINING_RECORD(ListEntry, RX_WORK_QUEUE_ITEM, List);
+    RxAcquireScavengerMutex();
 
-            InterlockedIncrement(&WorkQueue->NumberOfWorkItemsDispatched);
-            InterlockedDecrement(&WorkQueue->NumberOfWorkItemsToBeDispatched);
-            InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+    /* If there's nothing left to do... */
+    if (Scavenger->FobxsToBeFinalized <= 0)
+    {
+        RxReleaseScavengerMutex();
+    }
+    else
+    {
+        PLIST_ENTRY Entry;
+        LIST_ENTRY FobxToScavenge;
 
-            /* Get the parameters, and null-them in the struct */
-            WorkerRoutine = WorkQueueItem->WorkerRoutine;
-            Parameter = WorkQueueItem->Parameter;
-            DeviceObject = WorkQueueItem->pDeviceObject;
+        InitializeListHead(&FobxToScavenge);
 
-            WorkQueueItem->List.Flink = NULL;
-            WorkQueueItem->WorkerRoutine = NULL;
-            WorkQueueItem->Parameter = NULL;
-            WorkQueueItem->pDeviceObject = NULL;
+        /* Browse all the FOBXs to finalize */
+        Entry = Scavenger->FobxFinalizationList.Flink;
+        while (Entry != &Scavenger->FobxFinalizationList)
+        {
+            PFOBX Fobx;
 
-            /* Call the routine */
-            DPRINT("Calling: %p(%p)\n", WorkerRoutine, Parameter);
-            WorkerRoutine(Parameter);
+            Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList);
+            Entry = Entry->Flink;
 
-            /* Are we going down now? */
-            if (InterlockedDecrement(&DeviceObject->DispatcherContext.NumberOfWorkerThreads) == 0)
+            if (Fobx->SrvOpen != NULL)
             {
-                PKEVENT TearDownEvent;
-
-                TearDownEvent = InterlockedExchangePointer((volatile PVOID)&DeviceObject->DispatcherContext.pTearDownEvent, NULL);
-                if (TearDownEvent != NULL)
-                {
-                    KeSetEvent(TearDownEvent, IO_NO_INCREMENT, FALSE);
-                }
-            }
+                PFCB Fcb;
 
-            InterlockedIncrement(&WorkQueue->NumberOfIdleWorkerThreads);
-        }
+                Fcb = (PFCB)Fobx->SrvOpen->pFcb;
 
-        /* Shall we shutdown... */
-        KeAcquireSpinLock(&WorkQueue->SpinLock, &OldIrql);
-        switch (WorkQueue->State)
-        {
-            /* Our queue is active, kill it if we have no more items to dispatch
-             * and more threads than the required minimum
-             */
-            case RxWorkQueueActive:
-                if (WorkQueue->NumberOfWorkItemsToBeDispatched <= 0)
+                /* If it matches our NET_ROOT */
+                if ((PNET_ROOT)Fcb->pNetRoot == NetRoot)
                 {
-                    ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
-                    if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
-                    {
-                        KillThread = TRUE;
-                        Dereference = TRUE;
-                        InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
-                    }
+                    NTSTATUS Status;
 
-                    if (KillThread)
+                    /* Check whether it matches our FCB */
+                    Status = STATUS_MORE_PROCESSING_REQUIRED;
+                    if (PurgingFcb != NULL && PurgingFcb != Fcb)
                     {
-                        InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+                        MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb));
                     }
-                }
-                break;
-
-            /* The queue is inactive: kill it we have more threads than the required minimum */
-            case RxWorkQueueInactive:
-                ASSERT(WorkQueue->NumberOfActiveWorkerThreads > 0);
-                if (WorkQueue->NumberOfActiveWorkerThreads > WorkQueue->MinimumNumberOfWorkerThreads)
-                {
-                    KillThread = TRUE;
-                    Dereference = TRUE;
-                    InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
-                }
-
-                if (KillThread)
-                {
-                    InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
-                }
-                break;
-
-            /* Rundown in progress..., kill it for sure! */
-            case RxWorkQueueRundownInProgress:
-                {
-                    PRX_WORK_QUEUE_RUNDOWN_CONTEXT RundownContext;
-
-                    ASSERT(WorkQueue->pRundownContext != NULL);
-
-                    RundownContext = WorkQueue->pRundownContext;
-                    RundownContext->ThreadPointers[RundownContext->NumberOfThreadsSpunDown++] = CurrentThread;
-
-                    InterlockedDecrement(&WorkQueue->NumberOfActiveWorkerThreads);
-                    KillThread = TRUE;
-                    Dereference = FALSE;
 
-                    if (WorkQueue->NumberOfActiveWorkerThreads == 0)
+                    /* If so, add it to the list of the FOBXs to scavenge */
+                    if (Status != STATUS_SUCCESS)
                     {
-                        KeSetEvent(&RundownContext->RundownCompletionEvent, IO_NO_INCREMENT, FALSE);
-                    }
+                        RxReferenceNetFobx(Fobx);
+                        ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX);
 
-                    InterlockedDecrement(&WorkQueue->NumberOfIdleWorkerThreads);
+                        RemoveEntryList(&Fobx->ScavengerFinalizationList);
+                        InsertTailList(&FobxToScavenge, &Fobx->ScavengerFinalizationList);
+                    }
                 }
-                break;
-
-            default:
-                break;
+            }
         }
-        KeReleaseSpinLock(&WorkQueue->SpinLock, OldIrql);
-    } while (!KillThread);
 
-    DPRINT("Killed worker thread\n");
+        RxReleaseScavengerMutex();
 
-    /* Do we have to dereference ourselves? */
-    if (Dereference)
-    {
-        ObDereferenceObject(CurrentThread);
+        /* Now, scavenge all the extracted FOBX */
+        RxpScavengeFobxs(Scavenger, &FobxToScavenge);
     }
 
-    /* Dump last executed routine */
-    if (DumpDispatchRoutine)
+    if (SynchronizeWithScavenger)
     {
-        DPRINT("Dispatch routine %p(%p) taken from %p\n", WorkerRoutine, Parameter, WorkQueueItem);
+        KeSetEvent(&Scavenger->SyncEvent, IO_NO_INCREMENT, FALSE);
     }
-
-    PsTerminateSystemThread(STATUS_SUCCESS);
 }
 
-VOID
-RxReference(
-    IN OUT PVOID Instance)
+/*
+ * @implemented
+ */
+BOOLEAN
+RxScavengeRelatedFobxs(
+    PFCB Fcb)
 {
-    NODE_TYPE_CODE NodeType;
-    PNODE_TYPE_AND_SIZE Node;
+    PFOBX Fobx;
+    LIST_ENTRY LocalList;
+    PLIST_ENTRY NextEntry;
+    PRDBSS_SCAVENGER Scavenger;
 
     PAGED_CODE();
 
+    /* First of all, check whether there are FOBX to scavenge */
+    Scavenger = Fcb->RxDeviceObject->pRdbssScavenger;
     RxAcquireScavengerMutex();
+    if (Scavenger->FobxsToBeFinalized <= 0)
+    {
+        RxReleaseScavengerMutex();
+        return FALSE;
+    }
 
-    /* We can only reference a few structs */
-    NodeType = NodeType(Instance) & ~RX_SCAVENGER_MASK;
-    ASSERT((NodeType == RDBSS_NTC_SRVCALL) || (NodeType == RDBSS_NTC_NETROOT) ||
-           (NodeType == RDBSS_NTC_V_NETROOT) || (NodeType == RDBSS_NTC_SRVOPEN) ||
-           (NodeType == RDBSS_NTC_FOBX));
+    /* Initialize our local list which will hold all the FOBX to scavenge so
+     * that we don't acquire the scavenger mutex too long
+     */
+    InitializeListHead(&LocalList);
 
-    Node = (PNODE_TYPE_AND_SIZE)Instance;
-    InterlockedIncrement((volatile long *)&Node->NodeReferenceCount);
+    /* Technically, that condition should all be true... */
+    if (!IsListEmpty(&Scavenger->FobxFinalizationList))
+    {
+        PLIST_ENTRY NextEntry, LastEntry;
+
+        /* Browse all the FCBs to find the matching ones */
+        NextEntry = Scavenger->FobxFinalizationList.Flink;
+        LastEntry = &Scavenger->FobxFinalizationList;
+        while (NextEntry != LastEntry)
+        {
+            Fobx = CONTAINING_RECORD(NextEntry, FOBX, ScavengerFinalizationList);
+            NextEntry = NextEntry->Flink;
+            /* Matching our FCB? Let's finalize it */
+            if (Fobx->pSrvOpen != NULL && Fobx->pSrvOpen->pFcb == RX_GET_MRX_FCB(Fcb))
+            {
+                RxpUndoScavengerFinalizationMarking(Fobx);
+                ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX);
+                InsertTailList(&LocalList, &Fobx->ScavengerFinalizationList);
+            }
+        }
+    }
+
+    RxReleaseScavengerMutex();
+
+    /* Nothing to scavenge? Quit */
+    if (IsListEmpty(&LocalList))
+    {
+        return FALSE;
+    }
 
-    /* And that's not implemented! (yay, we leak :-D) */
-    switch (NodeType)
+    /* Now, finalize all the extracted FOBX */
+    while (!IsListEmpty(&LocalList))
     {
-        case RDBSS_NTC_SRVCALL:
-        case RDBSS_NTC_NETROOT:
-        case RDBSS_NTC_V_NETROOT:
-        case RDBSS_NTC_SRVOPEN:
-        case RDBSS_NTC_FOBX:
-            UNIMPLEMENTED;
-            break;
-        default:
-            ASSERT(FALSE);
-            break;
+        NextEntry = RemoveHeadList(&LocalList);
+        Fobx = CONTAINING_RECORD(NextEntry, FOBX, ScavengerFinalizationList);
+        RxFinalizeNetFobx(Fobx, TRUE, TRUE);
     }
 
-    RxpUndoScavengerFinalizationMarking(Instance);
-    RxReleaseScavengerMutex();
+    return TRUE;
+}
+
+VOID
+RxScavengerFinalizeEntries(
+    PRDBSS_DEVICE_OBJECT DeviceObject)
+{
+    UNIMPLEMENTED;
 }
 
+/*
+ * @implemented
+ */
 VOID
 NTAPI
-RxResumeBlockedOperations_Serially(
-    IN OUT PRX_CONTEXT RxContext,
-    IN OUT PLIST_ENTRY BlockingIoQ)
+RxScavengerTimerRoutine(
+    PVOID Context)
 {
+    BOOLEAN Requeue;
+    PRDBSS_DEVICE_OBJECT DeviceObject;
+    PRDBSS_SCAVENGER Scavenger;
+
     PAGED_CODE();
 
-    RxAcquireSerializationMutex();
+    DeviceObject = Context;
+    Scavenger = DeviceObject->pRdbssScavenger;
 
-    /* This can only happen on pipes */
-    if (!BooleanFlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION))
+    Requeue = FALSE;
+    RxAcquireScavengerMutex();
+    /* If the scavenger was dormant, wake it up! */
+    if (Scavenger->State == RDBSS_SCAVENGER_DORMANT)
     {
-        RxReleaseSerializationMutex();
-        return;
-    }
+        /* Done */
+        Scavenger->State = RDBSS_SCAVENGER_ACTIVE;
+        KeResetEvent(&Scavenger->ScavengeEvent);
 
-    UNIMPLEMENTED;
+        /* Scavenger the entries */
+        RxReleaseScavengerMutex();
+        RxScavengerFinalizeEntries(DeviceObject);
+        RxAcquireScavengerMutex();
 
-    RxReleaseSerializationMutex();
-}
+        /* If we're still active (race) */
+        if (Scavenger->State == RDBSS_SCAVENGER_ACTIVE)
+        {
+            /* If there are new entries to scavenge, stay dormant and requeue a run */
+            if (Scavenger->NumberOfDormantFiles + Scavenger->SrvCallsToBeFinalized +
+                Scavenger->NetRootsToBeFinalized + Scavenger->VNetRootsToBeFinalized +
+                Scavenger->FcbsToBeFinalized + Scavenger->SrvOpensToBeFinalized +
+                Scavenger->FobxsToBeFinalized != 0)
+            {
+                Requeue = TRUE;
+                Scavenger->State = RDBSS_SCAVENGER_DORMANT;
+            }
+            /* Otherwise, we're inactive again */
+            else
+            {
+                Scavenger->State = RDBSS_SCAVENGER_INACTIVE;
+            }
+        }
 
-BOOLEAN
-RxScavengeRelatedFobxs(
-    PFCB Fcb)
-{
-    UNIMPLEMENTED;
-    return FALSE;
+        RxReleaseScavengerMutex();
+
+        /* Requeue an execution */
+        if (Requeue)
+        {
+            RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem,
+                                      RxScavengerTimerRoutine, DeviceObject, Scavenger->TimeLimit);
+        }
+    }
+    else
+    {
+        RxReleaseScavengerMutex();
+    }
+
+    KeSetEvent(&Scavenger->ScavengeEvent, IO_NO_INCREMENT, FALSE);
 }
 
 BOOLEAN
@@ -4820,7 +7923,7 @@ RxSpinUpRequestsDispatcher(
     NTSTATUS Status;
     PRX_DISPATCHER RxDispatcher;
 
-    Status = ObReferenceObjectByPointer(PsGetCurrentThread(), THREAD_ALL_ACCESS, PsThreadType, KernelMode);
+    Status = ObReferenceObjectByPointer(PsGetCurrentThread(), THREAD_ALL_ACCESS, *PsThreadType, KernelMode);
     if (!NT_SUCCESS(Status))
     {
         PsTerminateSystemThread(STATUS_SUCCESS);
@@ -4859,7 +7962,7 @@ RxSpinUpRequestsDispatcher(
 
             InterlockedDecrement(&WorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
 
-            DPRINT1("WORKQ:SR %lx %lx\n", WorkItem->WorkerRoutine, WorkItem->Parameter);
+            DPRINT("Workqueue: calling %p(%p)\n", WorkItem->WorkerRoutine, WorkItem->Parameter);
             WorkItem->WorkerRoutine(WorkItem->Parameter);
         }
     } while (RxDispatcher->State == RxDispatcherActive);
@@ -5023,6 +8126,9 @@ RxTableComputePathHashValue(
     return Hash;
 }
 
+/*
+ * @implemented
+ */
 PVOID
 RxTableLookupName(
     IN PRX_PREFIX_TABLE ThisTable,
@@ -5085,10 +8191,34 @@ RxTableLookupName(
         ASSERT(Entry->ContainingRecord != NULL);
         Container = Entry->ContainingRecord;
 
-        /* Need to handle NetRoot specific case... */
+        /* If we have a NET_ROOT, let's return a V_NET_ROOT */
         if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_NETROOT)
         {
-            UNIMPLEMENTED;
+            PNET_ROOT NetRoot;
+
+            NetRoot = (PNET_ROOT)Entry->ContainingRecord;
+            /* If there's a default one, perfect, that's a match */
+            if (NetRoot->DefaultVNetRoot != NULL)
+            {
+                Container = NetRoot->DefaultVNetRoot;
+            }
+            /* If none (that shouldn't happen!), try to find one */
+            else
+            {
+                /* Use the first one in the list */
+                if (!IsListEmpty(&NetRoot->VirtualNetRoots))
+                {
+                    Container = CONTAINING_RECORD(NetRoot->VirtualNetRoots.Flink, V_NET_ROOT, NetRootListEntry);
+                }
+                /* Really, really, shouldn't happen */
+                else
+                {
+                    ASSERT(FALSE);
+                    Entry = NULL;
+                    Container = NULL;
+                }
+            }
+
             break;
         }
         else if ((NodeType(Entry->ContainingRecord) & ~RX_SCAVENGER_MASK) == RDBSS_NTC_V_NETROOT)
@@ -5228,6 +8358,94 @@ RxTableLookupName_ExactLengthMatch(
     return NULL;
 }
 
+/*
+ * @implemented
+ */
+NTSTATUS
+RxTearDownBufferingManager(
+   PSRV_CALL SrvCall)
+{
+    PAGED_CODE();
+
+    /* Nothing to do */
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxTimerDispatch(
+    _In_ struct _KDPC *Dpc,
+    _In_opt_ PVOID DeferredContext,
+    _In_opt_ PVOID SystemArgument1,
+    _In_opt_ PVOID SystemArgument2)
+{
+    BOOLEAN Set;
+    LIST_ENTRY LocalList;
+    PLIST_ENTRY ListEntry;
+    PRX_WORK_ITEM WorkItem;
+
+    InitializeListHead(&LocalList);
+
+    KeAcquireSpinLockAtDpcLevel(&RxTimerLock);
+    ++RxTimerTickCount;
+
+    /* Find any entry matching */
+    if (!IsListEmpty(&RxTimerQueueHead))
+    {
+        ListEntry = RxTimerQueueHead.Flink;
+        do
+        {
+            WorkItem = CONTAINING_RECORD(ListEntry, RX_WORK_ITEM, WorkQueueItem.List);
+            if (WorkItem->LastTick == RxTimerTickCount)
+            {
+                ListEntry = ListEntry->Flink;
+
+                RemoveEntryList(&WorkItem->WorkQueueItem.List);
+                InsertTailList(&LocalList, &WorkItem->WorkQueueItem.List);
+            }
+            else
+            {
+                ListEntry = ListEntry->Flink;
+            }
+        } while (ListEntry != &RxTimerQueueHead);
+    }
+    /* Do we have to requeue a later execution? */
+    Set = !IsListEmpty(&RxTimerQueueHead);
+
+    KeReleaseSpinLockFromDpcLevel(&RxTimerLock);
+
+    /* Requeue if list wasn't empty */
+    if (Set)
+    {
+        KeSetTimer(&RxTimer, RxTimerInterval, &RxTimerDpc);
+    }
+
+    /* If we had matching entries */
+    if (!IsListEmpty(&LocalList))
+    {
+        /* Post them, one after another */
+        ListEntry = LocalList.Flink;
+        do
+        {
+            WorkItem = CONTAINING_RECORD(ListEntry, RX_WORK_ITEM, WorkQueueItem.List);
+            ListEntry = ListEntry->Flink;
+
+            WorkItem->WorkQueueItem.List.Flink = NULL;
+            WorkItem->WorkQueueItem.List.Blink = NULL;
+            RxPostToWorkerThread(WorkItem->WorkQueueItem.pDeviceObject, CriticalWorkQueue,
+                                 &WorkItem->WorkQueueItem, WorkItem->WorkQueueItem.WorkerRoutine,
+                                 WorkItem->WorkQueueItem.Parameter);
+        }
+        while (ListEntry != &LocalList);
+    }
+}
+
+/*
+ * @implemented
+ */
 VOID
 RxTrackerUpdateHistory(
     _Inout_opt_ PRX_CONTEXT RxContext,
@@ -5237,7 +8455,74 @@ RxTrackerUpdateHistory(
     _In_ PCSTR FileName,
     _In_ ULONG SerialNumber)
 {
-    UNIMPLEMENTED;
+    PFCB Fcb;
+    RX_FCBTRACKER_CASES Case;
+
+    /* Check for null or special context */
+    if (RxContext == NULL)
+    {
+        Case = RX_FCBTRACKER_CASE_NULLCONTEXT;
+    }
+    else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT)
+    {
+        Case = RX_FCBTRACKER_CASE_CBS_CONTEXT;
+    }
+    else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
+    {
+        Case = RX_FCBTRACKER_CASE_CBS_WAIT_CONTEXT;
+    }
+    else
+    {
+        ASSERT(NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT);
+        Case = RX_FCBTRACKER_CASE_NORMAL;
+    }
+
+    /* If caller provided a FCB, update its history */
+    if (MrxFcb != NULL)
+    {
+        Fcb = (PFCB)MrxFcb;
+        ASSERT(NodeTypeIsFcb(Fcb));
+
+        /* Only one acquire operation, so many release operations... */
+        if (Operation == TRACKER_ACQUIRE_FCB)
+        {
+            ++Fcb->FcbAcquires[Case];
+        }
+        else
+        {
+            ++Fcb->FcbReleases[Case];
+        }
+    }
+
+    /* If we have a normal context, update its history about this function calls */
+    if (Case == RX_FCBTRACKER_CASE_NORMAL)
+    {
+        ULONG TrackerHistoryPointer;
+
+        /* Only one acquire operation, so many release operations... */
+        if (Operation == TRACKER_ACQUIRE_FCB)
+        {
+            InterlockedIncrement(&RxContext->AcquireReleaseFcbTrackerX);
+        }
+        else
+        {
+            InterlockedDecrement(&RxContext->AcquireReleaseFcbTrackerX);
+        }
+
+        /* We only keep track of the 32 first calls */
+        TrackerHistoryPointer = InterlockedExchangeAdd((volatile long *)&RxContext->TrackerHistoryPointer, 1);
+        if (TrackerHistoryPointer < RDBSS_TRACKER_HISTORY_SIZE)
+        {
+            RxContext->TrackerHistory[TrackerHistoryPointer].AcquireRelease = Operation;
+            RxContext->TrackerHistory[TrackerHistoryPointer].LineNumber = LineNumber;
+            RxContext->TrackerHistory[TrackerHistoryPointer].FileName = (PSZ)FileName;
+            RxContext->TrackerHistory[TrackerHistoryPointer].SavedTrackerValue = RxContext->AcquireReleaseFcbTrackerX;
+            RxContext->TrackerHistory[TrackerHistoryPointer].Flags = RxContext->Flags;
+        }
+
+        /* If it's negative, then we released once more than we acquired it?! */
+        ASSERT(RxContext->AcquireReleaseFcbTrackerX >= 0);
+    }
 }
 
 VOID
@@ -5250,6 +8535,19 @@ RxTrackPagingIoResource(
     UNIMPLEMENTED;
 }
 
+/*
+ * @implemented
+ */
+VOID
+RxUndoScavengerFinalizationMarking(
+    PVOID Instance)
+{
+    /* Just call internal routine with mutex held */
+    RxAcquireScavengerMutex();
+    RxpUndoScavengerFinalizationMarking(Instance);
+    RxReleaseScavengerMutex();
+}
+
 /*
  * @implemented
  */
@@ -5265,17 +8563,17 @@ RxUninitializeVNetRootParameters(
     /* Only free what could have been allocated */
     if (UserName != NULL)
     {
-        ExFreePool(UserName);
+        RxFreePool(UserName);
     }
 
     if (UserDomainName != NULL)
     {
-        ExFreePool(UserDomainName);
+        RxFreePool(UserDomainName);
     }
 
     if (Password != NULL)
     {
-        ExFreePool(Password);
+        RxFreePool(Password);
     }
 
     /* And remove the possibly set CSC agent flag */
@@ -5329,11 +8627,112 @@ RxUpdateCondition(
     }
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxVerifyOperationIsLegal(
     IN PRX_CONTEXT RxContext)
 {
-    UNIMPLEMENTED;
+    PIRP Irp;
+    PMRX_FOBX Fobx;
+    BOOLEAN FlagSet;
+    PFILE_OBJECT FileObject;
+    PIO_STACK_LOCATION Stack;
+
+    PAGED_CODE();
+
+    Irp = RxContext->CurrentIrp;
+    Stack = RxContext->CurrentIrpSp;
+    FileObject = Stack->FileObject;
+
+    /* We'll only check stuff on opened files, this requires an IRP and a FO */
+    if (Irp == NULL || FileObject == NULL)
+    {
+        return;
+    }
+
+    /* Set no exception for breakpoint - remember whether is was already set */
+    FlagSet = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
+    SetFlag(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
+
+    /* If we have a CCB, perform a few checks on opened file */
+    Fobx = RxContext->pFobx;
+    if (Fobx != NULL)
+    {
+        PMRX_SRV_OPEN SrvOpen;
+
+        SrvOpen = Fobx->pSrvOpen;
+        if (SrvOpen != NULL)
+        {
+            UCHAR MajorFunction;
+
+            MajorFunction = RxContext->MajorFunction;
+            /* Only allow closing/cleanup operations on renamed files */
+            if (MajorFunction != IRP_MJ_CLEANUP && MajorFunction != IRP_MJ_CLOSE &&
+                BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_RENAMED))
+            {
+                RxContext->IoStatusBlock.Status = STATUS_FILE_RENAMED;
+                ExRaiseStatus(STATUS_FILE_RENAMED);
+            }
+
+            /* Only allow closing/cleanup operations on deleted files */
+            if (MajorFunction != IRP_MJ_CLEANUP && MajorFunction != IRP_MJ_CLOSE &&
+                BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_DELETED))
+            {
+                RxContext->IoStatusBlock.Status = STATUS_FILE_DELETED;
+                ExRaiseStatus(STATUS_FILE_DELETED);
+            }
+        }
+    }
+
+    /* If that's an open operation */
+    if (RxContext->MajorFunction == IRP_MJ_CREATE)
+    {
+        PFILE_OBJECT RelatedFileObject;
+
+        /* We won't allow an open operation relative to a file to be deleted */
+        RelatedFileObject = FileObject->RelatedFileObject;
+        if (RelatedFileObject != NULL)
+        {
+            PMRX_FCB Fcb;
+
+            Fcb = RelatedFileObject->FsContext;
+            if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE))
+            {
+                RxContext->IoStatusBlock.Status = STATUS_DELETE_PENDING;
+                ExRaiseStatus(STATUS_DELETE_PENDING);
+            }
+        }
+    }
+
+    /* If cleanup was completed */
+    if (BooleanFlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
+    {
+        if (!BooleanFlagOn(Irp->Flags, IRP_PAGING_IO))
+        {
+            UCHAR MajorFunction;
+
+            /* We only allow a subset of operations (see FatVerifyOperationIsLegal for instance) */
+            MajorFunction = Stack->MajorFunction;
+            if (MajorFunction != IRP_MJ_CLOSE && MajorFunction != IRP_MJ_QUERY_INFORMATION &&
+                MajorFunction != IRP_MJ_SET_INFORMATION)
+            {
+                if ((MajorFunction != IRP_MJ_READ && MajorFunction != IRP_MJ_WRITE) ||
+                    !BooleanFlagOn(Stack->MinorFunction, IRP_MN_COMPLETE))
+                {
+                    RxContext->IoStatusBlock.Status = STATUS_FILE_CLOSED;
+                    ExRaiseStatus(STATUS_FILE_CLOSED);
+                }
+            }
+        }
+    }
+
+    /* If flag was already set, don't clear it */
+    if (!FlagSet)
+    {
+        ClearFlag(RxContext->Flags, RX_CONTEXT_FLAG_NO_EXCEPTION_BREAKPOINT);
+    }
 }
 
 /*
@@ -5406,7 +8805,43 @@ RxWorkItemDispatcher(
 
     DispatchItem->DispatchRoutine(DispatchItem->DispatchRoutineParameter);
 
-    ExFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
+    RxFreePoolWithTag(DispatchItem, RX_WORKQ_POOLTAG);
+}
+
+/*
+ * @implemented
+ */
+PVOID
+NTAPI
+_RxAllocatePoolWithTag(
+    _In_ POOL_TYPE PoolType,
+    _In_ SIZE_T NumberOfBytes,
+    _In_ ULONG Tag)
+{
+    return ExAllocatePoolWithTagPriority(PoolType, NumberOfBytes, Tag, LowPoolPriority);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+_RxFreePool(
+    _In_ PVOID Buffer)
+{
+    ExFreePoolWithTag(Buffer, 0);
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+_RxFreePoolWithTag(
+    _In_ PVOID Buffer,
+    _In_ ULONG Tag)
+{
+    ExFreePoolWithTag(Buffer, Tag);
 }
 
 NTSTATUS
@@ -5432,7 +8867,7 @@ __RxAcquireFcb(
     SpecialContext = FALSE;
     ContextIsPresent = FALSE;
     /* Check for special context */
-    if ((ULONG_PTR)RxContext == -1 || (ULONG_PTR)RxContext == -2)
+    if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT || RxContext == CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
     {
         SpecialContext = TRUE;
     }
@@ -5450,25 +8885,27 @@ __RxAcquireFcb(
         UNIMPLEMENTED;
     }
 
-    /* Nor missing contexts... */
+    /* If we don't have a context, assume we can wait! */
     if (RxContext == NULL)
     {
-        UNIMPLEMENTED;
+        CanWait = TRUE;
     }
+    else
+    {
+        /* That said: we have a real context! */
+        ContextIsPresent = TRUE;
 
-    /* That said: we have a real context! */
-    ContextIsPresent = TRUE;
+        /* If we've been cancelled in between, give up */
+        Status = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED) ? STATUS_CANCELLED : STATUS_SUCCESS;
+        if (!NT_SUCCESS(Status))
+        {
+            return Status;
+        }
 
-    /* If we've been cancelled in between, give up */
-    Status = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_CANCELLED) ? STATUS_CANCELLED : STATUS_SUCCESS;
-    if (!NT_SUCCESS(Status))
-    {
-        return Status;
+        /* Can we wait? */
+        CanWait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
     }
 
-    /* Can we wait? */
-    CanWait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
-
     while (TRUE)
     {
         /* Assume we cannot lock */
@@ -5593,7 +9030,7 @@ __RxReleaseFcb(
     RxAcquireSerializationMutex();
 
     BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
-    IsExclusive = BooleanFlagOn(MrxFcb->Header.Resource->Flag, ResourceOwnedExclusive);
+    IsExclusive = !!RxIsResourceOwnershipStateExclusive(MrxFcb->Header.Resource);
 
     /* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
      * then just release the FCB
@@ -5640,7 +9077,7 @@ __RxReleaseFcbForThread(
     RxAcquireSerializationMutex();
 
     BufferingPending = BooleanFlagOn(MrxFcb->FcbState, FCB_STATE_BUFFERING_STATE_CHANGE_PENDING);
-    IsExclusive = BooleanFlagOn(MrxFcb->Header.Resource->Flag, ResourceOwnedExclusive);
+    IsExclusive = !!RxIsResourceOwnershipStateExclusive(MrxFcb->Header.Resource);
 
     /* If no buffering pending, or no exclusive lock (we can only handle with an exclusive lock),
      * then just release the FCB