[RXCE]
[reactos.git] / reactos / sdk / lib / drivers / rxce / rxce.c
index 2eb95ab..8f47f50 100644 (file)
@@ -75,6 +75,19 @@ RxpDispatchChangeBufferingStateRequests(
     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(
@@ -108,7 +121,14 @@ 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;
@@ -116,6 +136,13 @@ 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
@@ -147,6 +174,118 @@ BOOLEAN DumpDispatchRoutine = FALSE;
 
 /* 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
  */
@@ -385,6 +524,8 @@ RxAllocateFcbObject(
         FsRtlSetupAdvancedHeader(Fcb, &NonPagedFcb->AdvancedFcbHeaderMutex);
     }
 
+    DPRINT("Allocated %p\n", Buffer);
+
     return Buffer;
 }
 
@@ -570,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,
@@ -1468,8 +1752,12 @@ RxCreateRxContext(
         return NULL;
     }
 
-    /* And initialize it */
+    /* Zero it */
     RtlZeroMemory(Context, sizeof(RX_CONTEXT));
+
+    /* It was allocated on NP pool, keep track of it! */
+    SetFlag(Context->Flags, RX_CONTEXT_FLAG_FROM_POOL);
+    /* And initialize it */
     RxInitializeContext(Irp, RxDeviceObject, InitialContextFlags, Context);
     ASSERT((Context->MajorFunction != IRP_MJ_CREATE) || !BooleanFlagOn(Context->Flags, RX_CONTEXT_FLAG_MUST_SUCCEED_ALLOCATED));
 
@@ -1798,6 +2086,9 @@ RxCreateVNetRoot(
     return VNetRoot;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxDereference(
     IN OUT PVOID Instance,
@@ -1859,10 +2150,22 @@ 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();
 
@@ -2014,6 +2317,14 @@ RxDereferenceAndDeleteRxContext_Real(
     }
 }
 
+VOID
+NTAPI
+RxDispatchChangeBufferingStateRequests(
+    PVOID Context)
+{
+    UNIMPLEMENTED;
+}
+
 /*
  * @implemented
  */
@@ -2249,6 +2560,182 @@ RxFcbTableRemoveFcb(
     return STATUS_SUCCESS;
 }
 
+/*
+ * @implemented
+ */
+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();
+
+    ASSERT(NodeType(NetRoot) == RDBSS_NTC_NETROOT);
+
+    /* 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
  */
@@ -2328,7 +2815,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)
@@ -2391,7 +2878,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);
@@ -2544,7 +3031,7 @@ RxFinalizeNetRoot(
             HashBucket = &FcbTable->HashBuckets[Bucket];
             ListEntry = HashBucket->Flink;
             while (ListEntry != HashBucket)
-            {    
+            {
                 PFCB Fcb;
 
                 Fcb = CONTAINING_RECORD(ListEntry, FCB, FcbTableEntry.HashLinks);
@@ -2692,14 +3179,117 @@ RxFinalizeSrvCall(
     return TRUE;
 }
 
-BOOLEAN
+/*
+ * @implemented
+ */
+BOOLEAN
 RxFinalizeSrvOpen(
     OUT PSRV_OPEN ThisSrvOpen,
     IN BOOLEAN RecursiveFinalize,
     IN BOOLEAN ForceFinalize)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    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;
 }
 
 /*
@@ -3374,14 +3964,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)
     {
@@ -3400,19 +3990,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);
@@ -3605,11 +4199,49 @@ RxFlushFcbInSystemCache(
     return IoStatus.Status;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxFreeFcbObject(
     PVOID Object)
 {
-    UNIMPLEMENTED;
+    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);
+    }
 }
 
 /*
@@ -3654,6 +4286,128 @@ RxFreeObject(
     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;
+}
+
 /*
  * @implemented
  */
@@ -4105,6 +4859,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,
@@ -4454,6 +5229,9 @@ RxLockUserBuffer(
     _SEH2_END;
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 RxLowIoCompletionTail(
     IN PRX_CONTEXT RxContext)
@@ -4466,7 +5244,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;
@@ -4484,10 +5262,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
@@ -4762,48 +5545,177 @@ RxMapSystemBuffer(
     return Irp->AssociatedIrp.SystemBuffer;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxMarkFobxOnCleanup(
     PFOBX pFobx,
     PBOOLEAN NeedPurge)
 {
-    UNIMPLEMENTED;
-}
+    PFCB Fcb;
+    PFOBX ScavengerFobx;
+    LARGE_INTEGER TickCount;
+    PRDBSS_SCAVENGER Scavenger;
 
-VOID
-RxMarkFobxOnClose(
-    PFOBX Fobx)
-{
-    UNIMPLEMENTED;
+    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
  */
-PVOID
-RxNewMapUserBuffer(
-    PRX_CONTEXT RxContext)
+VOID
+RxMarkFobxOnClose(
+    PFOBX Fobx)
 {
-    PIRP Irp;
+    PFCB Fcb;
+    PRDBSS_SCAVENGER Scavenger;
 
     PAGED_CODE();
 
-    Irp = RxContext->CurrentIrp;
-    if (Irp->MdlAddress != NULL)
+    /* No FOBX, nothing to mark */
+    if (Fobx == NULL)
     {
-        return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+        return;
     }
 
-    return Irp->UserBuffer;
-}
+    Fcb = (PFCB)Fobx->pSrvOpen->pFcb;
+    ASSERT(NodeTypeIsFcb(Fcb));
 
-BOOLEAN
-NTAPI
-RxNoOpAcquire(
-    IN PVOID Fcb,
-    IN BOOLEAN Wait)
-{
-    UNIMPLEMENTED;
+    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();
+}
+
+/*
+ * @implemented
+ */
+PVOID
+RxNewMapUserBuffer(
+    PRX_CONTEXT RxContext)
+{
+    PIRP Irp;
+
+    PAGED_CODE();
+
+    Irp = RxContext->CurrentIrp;
+    if (Irp->MdlAddress != NULL)
+    {
+        return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
+    }
+
+    return Irp->UserBuffer;
+}
+
+BOOLEAN
+NTAPI
+RxNoOpAcquire(
+    IN PVOID Fcb,
+    IN BOOLEAN Wait)
+{
+    UNIMPLEMENTED;
     return FALSE;
 }
 
@@ -4825,6 +5737,67 @@ RxOrphanThisFcb(
 VOID
 RxOrphanSrvOpens(
     IN PV_NET_ROOT ThisVNetRoot)
+{
+    PFCB Fcb;
+    USHORT Bucket;
+    PNET_ROOT NetRoot;
+    PRX_FCB_TABLE FcbTable;
+    PRX_PREFIX_TABLE PrefixTable;
+
+    PAGED_CODE();
+
+    /* Mailslot won't have any SRV_OPEN (to orphan) */
+    NetRoot = (PNET_ROOT)ThisVNetRoot->pNetRoot;
+    if (NetRoot->Type == NET_ROOT_MAILSLOT)
+    {
+        return;
+    }
+
+    PrefixTable = NetRoot->pSrvCall->RxDeviceObject->pRxNetNameTable;
+    ASSERT(RxIsPrefixTableLockExclusive(PrefixTable));
+
+    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;
 }
@@ -4910,7 +5883,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;
                 }
@@ -4929,7 +5903,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);
     }
@@ -5005,20 +5979,338 @@ RxpDestroySrvCall(
     RxReleasePrefixTableLock(PrefixTable);
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxpDiscardChangeBufferingStateRequests(
     _Inout_ PLIST_ENTRY DiscardedRequests)
 {
-    UNIMPLEMENTED;
+    PLIST_ENTRY Entry;
+
+    PAGED_CODE();
+
+    /* No requests to discard */
+    if (IsListEmpty(DiscardedRequests))
+    {
+        return;
+    }
+
+    /* Free all the discarded requests */
+    Entry = DiscardedRequests->Flink;
+    while (Entry != DiscardedRequests)
+    {
+        PCHANGE_BUFFERING_STATE_REQUEST Request;
+
+        Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+        Entry = Entry->Flink;
+
+        DPRINT("Req %p for %p (%p) discarded\n", Request, Request->SrvOpenKey, Request->SrvOpen);
+
+        RxPrepareRequestForReuse(Request);
+        RxFreePool(Request);
+    }
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxpDispatchChangeBufferingStateRequests(
     PSRV_CALL SrvCall,
     PSRV_OPEN SrvOpen,
     PLIST_ENTRY DiscardedRequests)
 {
-    UNIMPLEMENTED;
+    KIRQL OldIrql;
+    NTSTATUS Status;
+    BOOLEAN StartDispatcher;
+    LIST_ENTRY AcceptedReqs;
+    LIST_ENTRY DispatcherList;
+    PRX_BUFFERING_MANAGER BufferingManager;
+
+    /* Initialize our lists */
+    InitializeListHead(&AcceptedReqs);
+    InitializeListHead(DiscardedRequests);
+
+    /* Transfer the requests to dispatch locally */
+    BufferingManager = &SrvCall->BufferingManager;
+    KeAcquireSpinLock(&BufferingManager->SpinLock, &OldIrql);
+    RxTransferList(&DispatcherList, &BufferingManager->DispatcherList);
+    KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
+
+    /* If there were requests */
+    if (!IsListEmpty(&DispatcherList))
+    {
+        PLIST_ENTRY Entry;
+
+        /* For each of the entries... */
+        Entry = DispatcherList.Flink;
+        while (Entry != &DispatcherList)
+        {
+            PCHANGE_BUFFERING_STATE_REQUEST Request;
+
+            Request = CONTAINING_RECORD(Entry, CHANGE_BUFFERING_STATE_REQUEST, ListEntry);
+            Entry = Entry->Flink;
+
+            /* 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);
+
+                    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
+    {
+        /* 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;
+        }
+    }
+
+    /* If there were accepted requests, move them to the buffering manager */
+    if (!IsListEmpty(&AcceptedReqs))
+    {
+        RxTransferList(&BufferingManager->HandlerList, &AcceptedReqs);
+    }
+    KeReleaseSpinLock(&BufferingManager->SpinLock, OldIrql);
+
+    /* 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
+ */
+NTSTATUS
+RxpLookupSrvOpenForRequestLite(
+    IN PSRV_CALL SrvCall,
+    IN OUT PCHANGE_BUFFERING_STATE_REQUEST Request)
+{
+    NTSTATUS Status;
+    PLIST_ENTRY Entry;
+    PSRV_OPEN SrvOpen;
+
+    PAGED_CODE();
+
+    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;
+            }
+        }
+    }
+
+    /* 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
+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();
+
+    /* If still referenced, don't mark it (broken caller) */
+    Node = (PNODE_TYPE_AND_SIZE)Instance;
+    if (Node->NodeReferenceCount > 1)
+    {
+        return;
+    }
+
+    DeviceObject = RxGetDeviceObjectOfInstance(Instance);
+    Scavenger = DeviceObject->pRdbssScavenger;
+
+    /* 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;
 }
 
 /*
@@ -5138,7 +6430,7 @@ RxPrefixTableLookupName(
     {
         NODE_TYPE_CODE Type;
 
-        Type = NodeType(Container);
+        Type = (NodeType(Container) & ~RX_SCAVENGER_MASK);
         switch (Type)
         {
             case RDBSS_NTC_SRVCALL:
@@ -5154,7 +6446,9 @@ RxPrefixTableLookupName(
                 break;
 
             default:
+                DPRINT1("Invalid node type: %x\n", Type);
                 ASSERT(FALSE);
+                RxReference(Container);
                 break;
         }
     }
@@ -5231,7 +6525,42 @@ RxPrepareContextForReuse(
  * @implemented
  */
 VOID
-NTAPI
+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)
 {
@@ -5240,6 +6569,39 @@ RxProcessChangeBufferingStateRequests(
     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)
@@ -5247,6 +6609,43 @@ RxProcessFcbChangeBufferingStateRequest(
     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,
@@ -5254,6 +6653,9 @@ RxpTrackDereference(
     _In_ ULONG Line,
     _In_ PVOID Instance)
 {
+    PCSTR InstanceType;
+    ULONG ReferenceCount;
+
     PAGED_CODE();
 
     if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
@@ -5261,7 +6663,53 @@ RxpTrackDereference(
         return TRUE;
     }
 
-    UNIMPLEMENTED;
+    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;
 }
 
@@ -5272,30 +6720,167 @@ RxpTrackReference(
     _In_ ULONG Line,
     _In_ PVOID Instance)
 {
+    PCSTR InstanceType;
+    ULONG ReferenceCount;
+
     if (!BooleanFlagOn(RdbssReferenceTracingValue, TraceType))
     {
         return;
     }
 
-    UNIMPLEMENTED;
+    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(Node->NodeTypeCode, RX_SCAVENGER_MASK))
+    if (!BooleanFlagOn(NodeType(Node), RX_SCAVENGER_MASK))
     {
         return;
     }
 
-    UNIMPLEMENTED;
+    /* 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);
+    }
 }
 
 /*
@@ -5363,18 +6948,249 @@ RxPurgeFcbInSystemCache(
     /* If purge failed, force section closing */
     if (!Purged)
     {
-        MmFlushImageSection(&Fcb->NonPaged->SectionObjectPointers, MmFlushForWrite);
+        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;
 
-        RxReleaseFcb(NULL, Fcb);
-        Purged = MmForceSectionClosed(&Fcb->NonPaged->SectionObjectPointers, TRUE);
-        RxAcquireExclusiveFcb(NULL, Fcb);
+        Entry = RemoveHeadList(&PurgeSyncCtx->ContextsAwaitingPurgeCompletion);
+        Context = CONTAINING_RECORD(Entry, RX_CONTEXT, RxContextSerializationQLinks);
+
+        RxSignalSynchronousWaiter(Context);
     }
 
-    /* Return appropriate status */
-    Status = (Purged ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
-    DPRINT("Purge for FCB %p returns %lx\n", Fcb, Status);
+    RxReleaseScavengerMutex();
 
-    return Status;
+    return (SuccessfullPurge > 0 ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
 }
 
 /*
@@ -5587,6 +7403,136 @@ RxReference(
     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
  */
@@ -5676,6 +7622,23 @@ RxRemoveVirtualNetRootFromNetRoot(
     }
 }
 
+VOID
+RxResumeBlockedOperations_ALL(
+    IN OUT PRX_CONTEXT RxContext)
+{
+    LIST_ENTRY BlockedOps;
+
+    PAGED_CODE();
+
+    /* Get the blocked operations */
+    RxTransferListWithMutex(&BlockedOps, &RxContext->BlockedOperations, RxContext->BlockedOpsMutex);
+
+    if (!IsListEmpty(&BlockedOps))
+    {
+        UNIMPLEMENTED;
+    }
+}
+
 VOID
 NTAPI
 RxResumeBlockedOperations_Serially(
@@ -5698,6 +7661,110 @@ RxResumeBlockedOperations_Serially(
     RxReleaseSerializationMutex();
 }
 
+/*
+ * @implemented
+ */
+VOID
+RxSetFileSizeWithLock(
+    IN OUT PFCB Fcb,
+    IN PLONGLONG FileSize)
+{
+    PAGED_CODE();
+
+    /* Set attribute and increase version */
+    Fcb->Header.FileSize.QuadPart = *FileSize;
+    ++Fcb->ulFileSizeVersion;
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxScavengeFobxsForNetRoot(
+    PNET_ROOT NetRoot,
+    PFCB PurgingFcb,
+    BOOLEAN SynchronizeWithScavenger)
+{
+    PRDBSS_SCAVENGER Scavenger;
+    PRDBSS_DEVICE_OBJECT RxDeviceObject;
+
+    PAGED_CODE();
+
+    RxDeviceObject = NetRoot->pSrvCall->RxDeviceObject;
+    Scavenger = RxDeviceObject->pRdbssScavenger;
+
+    /* Wait for the scavenger, if asked to */
+    if (SynchronizeWithScavenger)
+    {
+        KeWaitForSingleObject(&Scavenger->SyncEvent, Executive, KernelMode, FALSE, NULL);
+    }
+
+    RxAcquireScavengerMutex();
+
+    /* If there's nothing left to do... */
+    if (Scavenger->FobxsToBeFinalized <= 0)
+    {
+        RxReleaseScavengerMutex();
+    }
+    else
+    {
+        PLIST_ENTRY Entry;
+        LIST_ENTRY FobxToScavenge;
+
+        InitializeListHead(&FobxToScavenge);
+
+        /* Browse all the FOBXs to finalize */
+        Entry = Scavenger->FobxFinalizationList.Flink;
+        while (Entry != &Scavenger->FobxFinalizationList)
+        {
+            PFOBX Fobx;
+
+            Fobx = CONTAINING_RECORD(Entry, FOBX, ScavengerFinalizationList);
+            Entry = Entry->Flink;
+
+            if (Fobx->SrvOpen != NULL)
+            {
+                PFCB Fcb;
+
+                Fcb = (PFCB)Fobx->SrvOpen->pFcb;
+
+                /* If it matches our NET_ROOT */
+                if ((PNET_ROOT)Fcb->pNetRoot == NetRoot)
+                {
+                    NTSTATUS Status;
+
+                    /* Check whether it matches our FCB */
+                    Status = STATUS_MORE_PROCESSING_REQUIRED;
+                    if (PurgingFcb != NULL && PurgingFcb != Fcb)
+                    {
+                        MINIRDR_CALL_THROUGH(Status, RxDeviceObject->Dispatch, MRxAreFilesAliased, (Fcb, PurgingFcb));
+                    }
+
+                    /* If so, add it to the list of the FOBXs to scavenge */
+                    if (Status != STATUS_SUCCESS)
+                    {
+                        RxReferenceNetFobx(Fobx);
+                        ASSERT(NodeType(Fobx) == RDBSS_NTC_FOBX);
+
+                        RemoveEntryList(&Fobx->ScavengerFinalizationList);
+                        InsertTailList(&FobxToScavenge, &Fobx->ScavengerFinalizationList);
+                    }
+                }
+            }
+        }
+
+        RxReleaseScavengerMutex();
+
+        /* Now, scavenge all the extracted FOBX */
+        RxpScavengeFobxs(Scavenger, &FobxToScavenge);
+    }
+
+    if (SynchronizeWithScavenger)
+    {
+        KeSetEvent(&Scavenger->SyncEvent, IO_NO_INCREMENT, FALSE);
+    }
+}
+
 /*
  * @implemented
  */
@@ -5767,6 +7834,80 @@ RxScavengeRelatedFobxs(
     return TRUE;
 }
 
+VOID
+RxScavengerFinalizeEntries(
+    PRDBSS_DEVICE_OBJECT DeviceObject)
+{
+    UNIMPLEMENTED;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxScavengerTimerRoutine(
+    PVOID Context)
+{
+    BOOLEAN Requeue;
+    PRDBSS_DEVICE_OBJECT DeviceObject;
+    PRDBSS_SCAVENGER Scavenger;
+
+    PAGED_CODE();
+
+    DeviceObject = Context;
+    Scavenger = DeviceObject->pRdbssScavenger;
+
+    Requeue = FALSE;
+    RxAcquireScavengerMutex();
+    /* If the scavenger was dormant, wake it up! */
+    if (Scavenger->State == RDBSS_SCAVENGER_DORMANT)
+    {
+        /* Done */
+        Scavenger->State = RDBSS_SCAVENGER_ACTIVE;
+        KeResetEvent(&Scavenger->ScavengeEvent);
+
+        /* Scavenger the entries */
+        RxReleaseScavengerMutex();
+        RxScavengerFinalizeEntries(DeviceObject);
+        RxAcquireScavengerMutex();
+
+        /* 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;
+            }
+        }
+
+        RxReleaseScavengerMutex();
+
+        /* Requeue an execution */
+        if (Requeue)
+        {
+            RxPostOneShotTimerRequest(RxFileSystemDeviceObject, &Scavenger->WorkItem,
+                                      RxScavengerTimerRoutine, DeviceObject, Scavenger->TimeLimit);
+        }
+    }
+    else
+    {
+        RxReleaseScavengerMutex();
+    }
+
+    KeSetEvent(&Scavenger->ScavengeEvent, IO_NO_INCREMENT, FALSE);
+}
+
 BOOLEAN
 RxScavengeVNetRoots(
     PRDBSS_DEVICE_OBJECT RxDeviceObject)
@@ -6234,6 +8375,78 @@ RxTearDownBufferingManager(
     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
  */
@@ -6254,11 +8467,11 @@ RxTrackerUpdateHistory(
     {
         Case = RX_FCBTRACKER_CASE_NULLCONTEXT;
     }
-    else if ((ULONG_PTR)RxContext == -1)
+    else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT)
     {
         Case = RX_FCBTRACKER_CASE_CBS_CONTEXT;
     }
-    else if ((ULONG_PTR)RxContext == -2)
+    else if (RxContext == CHANGE_BUFFERING_STATE_CONTEXT_WAIT)
     {
         Case = RX_FCBTRACKER_CASE_CBS_WAIT_CONTEXT;
     }
@@ -6658,7 +8871,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;
     }
@@ -6676,25 +8889,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 */
@@ -6819,7 +9034,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
@@ -6866,7 +9081,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