[RDBSS]
authorPierre Schweitzer <pierre@reactos.org>
Sun, 2 Jul 2017 17:00:11 +0000 (17:00 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 2 Jul 2017 17:00:11 +0000 (17:00 +0000)
- Implement RxCloseAssociatedSrvOpen(), RxFastIoRead(), RxPurgeNetFcb(), RxRemoveShareAccess(), RxRemoveShareAccessPerSrvOpens()
- Continue implementation of RxCommonCleanup() to handle allocated SRV_OPEN
- Halfplement RxFastIoCheckIfPossible() so that it handles read operations
- Stub RxCancelNotifyChangeDirectoryRequestsForFobx()

[RXCE]
- Implement RxChangeBufferingState(), RxFinalizeSrvOpen(), RxFreeFcbObject(), RxGatherRequestsForSrvOpen(), RxGetDeviceObjectOfInstance(), RxInitializeRxTimer(), RxMarkFobxOnCleanup(), RxMarkFobxOnClose(), RxpDiscardChangeBufferingStateRequests(), RxpDispatchChangeBufferingStateRequests(), RxpLookupSrvOpenForRequestLite(), RxpMarkInstanceForScavengedFinalization(), RxPostOneShotTimerRequest(), RxPrepareRequestForReuse(), RxProcessChangeBufferingStateRequestsForSrvOpen(), RxpUndoScavengerFinalizationMarking(), RxPurgeChangeBufferingStateRequestsForSrvOpen(), RxPurgeFobxFromCache(), RxRemoveNameNetFcb(), RxScavengerTimerRoutine(), RxTimerDispatch()
- Finish implementation of RxDereference() to handle scavenger
- Finish implementation of RxLowIoCompletionTail() to handle blocked operations resume
- Fix a bug in RxFinalizeNetFcb() where it was dereferencing its NET_ROOT instead of its V_NET_ROOT
- Fix bugs in __RxAcquireFcb() where it improperly handled the lack of RX_CONTEXT
- Halfplement RxResumeBlockedOperations_ALL() to extract blocked operations from RX_CONTEXT (and drop them...)
- Stub RxDispatchChangeBufferingStateRequests(), RxScavengerFinalizeEntries()

[COPYSUP]
- Implement FsRtlCopyRead2()
This library is basically what you can find in FsRtl with an extended support of Top Level IRP. It is used by RDBSS for FastIO. Next to come in it will be FsRtlCopyWrite2().

This commit brings several improvements to current work on RBDSS/RXCE. First of all, both libraries will leak less (again!).
It also brings the scavenger infrastructure (not fully fonctionnal though). Our NFS driver doesn't make use of it though.
Finally, this brings support of FastIO (for read operations ;-)) to our NFS driver!

Regarding CORE-13484, with copy + FastIO I could copy a file without troubles. But that seems to be still problematic with xcopy without FastIO...

CORE-13484
CORE-11327

svn path=/trunk/; revision=75265

14 files changed:
reactos/drivers/filesystems/nfs/CMakeLists.txt
reactos/sdk/include/ddk/buffring.h
reactos/sdk/include/ddk/fcb.h
reactos/sdk/include/ddk/mrxfcb.h
reactos/sdk/include/ddk/nodetype.h
reactos/sdk/include/ddk/rxcontx.h
reactos/sdk/include/ddk/rxprocs.h
reactos/sdk/include/ddk/rxtimer.h
reactos/sdk/include/ddk/scavengr.h
reactos/sdk/lib/drivers/copysup/CMakeLists.txt [new file with mode: 0644]
reactos/sdk/lib/drivers/copysup/copysup.c [new file with mode: 0644]
reactos/sdk/lib/drivers/copysup/copysup.h [new file with mode: 0644]
reactos/sdk/lib/drivers/rdbsslib/rdbss.c
reactos/sdk/lib/drivers/rxce/rxce.c

index 7223d16..6bf139f 100644 (file)
@@ -11,7 +11,7 @@ add_definitions(-DRDBSS_TRACKER)
 
 add_library(nfs41_driver SHARED ${SOURCE} nfs.rc)
 set_module_type(nfs41_driver kernelmodedriver)
-target_link_libraries(nfs41_driver ntoskrnl_vista rdbsslib rxce memcmp ${PSEH_LIB})
+target_link_libraries(nfs41_driver ntoskrnl_vista rdbsslib rxce copysup memcmp ${PSEH_LIB})
 add_importlibs(nfs41_driver ntoskrnl hal)
 
 if((NOT MSVC) AND (NOT CMAKE_C_COMPILER_ID STREQUAL "Clang"))
index 399f23a..6389779 100644 (file)
@@ -1,6 +1,20 @@
 #ifndef __BUFFRING_H__
 #define __BUFFRING_H__
 
+#define RX_REQUEST_PREPARED_FOR_HANDLING 0x10000000
+
+typedef struct _CHANGE_BUFFERING_STATE_REQUEST_
+{   
+    LIST_ENTRY ListEntry;
+    ULONG Flags;
+#if (_WIN32_WINNT < 0x0600)
+    PSRV_CALL pSrvCall; 
+#endif
+    PSRV_OPEN SrvOpen;
+    PVOID SrvOpenKey;
+    PVOID MRxContext;
+} CHANGE_BUFFERING_STATE_REQUEST, *PCHANGE_BUFFERING_STATE_REQUEST;
+
 typedef struct _RX_BUFFERING_MANAGER_
 {
     BOOLEAN DispatcherActive;
@@ -49,6 +63,10 @@ VOID
 RxProcessFcbChangeBufferingStateRequest(
     _In_ PFCB Fcb);
 
+VOID
+RxPurgeChangeBufferingStateRequestsForSrvOpen(
+    _In_ PSRV_OPEN SrvOpen);
+
 VOID
 RxCompleteSrvOpenKeyAssociation(
     _Inout_ PSRV_OPEN SrvOpen);
index 27d1bc9..352748f 100644 (file)
@@ -211,10 +211,14 @@ typedef struct _FCB
 #define FCB_STATE_FILE_IS_BUF_COMPRESSED 0x00004000
 #define FCB_STATE_FILE_IS_DISK_COMPRESSED 0x00008000
 #define FCB_STATE_FILE_IS_SHADOWED 0x00010000
+#define FCB_STATE_BUFFERSTATE_CHANGING 0x00002000
 #define FCB_STATE_SPECIAL_PATH 0x00020000
 #define FCB_STATE_TIME_AND_SIZE_ALREADY_SET 0x00040000
+#define FCB_STATE_FILETIMECACHEING_ENABLED 0x00080000
 #define FCB_STATE_FILESIZECACHEING_ENABLED 0x00100000
+#define FCB_STATE_LOCK_BUFFERING_ENABLED 0x00200000
 #define FCB_STATE_COLLAPSING_ENABLED 0x00400000
+#define FCB_STATE_OPENSHARING_ENABLED 0x00800000
 #define FCB_STATE_READBUFFERING_ENABLED 0x01000000
 #define FCB_STATE_READCACHING_ENABLED 0x02000000
 #define FCB_STATE_WRITEBUFFERING_ENABLED 0x04000000
@@ -224,6 +228,17 @@ typedef struct _FCB
 #define FCB_STATE_FOBX_USED 0x40000000
 #define FCB_STATE_SRVOPEN_USED 0x80000000
 
+#define FCB_STATE_BUFFERING_STATE_MASK     \
+    ((FCB_STATE_WRITECACHING_ENABLED       \
+      | FCB_STATE_WRITEBUFFERING_ENABLED   \
+      | FCB_STATE_READCACHING_ENABLED      \
+      | FCB_STATE_READBUFFERING_ENABLED    \
+      | FCB_STATE_OPENSHARING_ENABLED      \
+      | FCB_STATE_COLLAPSING_ENABLED       \
+      | FCB_STATE_LOCK_BUFFERING_ENABLED   \
+      | FCB_STATE_FILESIZECACHEING_ENABLED \
+      | FCB_STATE_FILETIMECACHEING_ENABLED))
+
 typedef struct _FCB_INIT_PACKET
 {
     PULONG pAttributes;
@@ -276,6 +291,7 @@ typedef struct _SRV_OPEN
 #define FOBX_FLAG_SRVOPEN_CLOSED 0x1000000
 #define FOBX_FLAG_UNC_NAME 0x2000000
 #define FOBX_FLAG_ENCLOSED_ALLOCATED 0x4000000
+#define FOBX_FLAG_MARKED_AS_DORMANT 0x8000000
 
 typedef struct _FOBX
 {
@@ -354,53 +370,57 @@ RxpTrackDereference(
         DbgPrint("%ld\n", Count);                             \
     }
 
-#define RxReferenceSrvCall(SrvCall)                                         \
-   RxpTrackReference(RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall); \
-   RxReference(SrvCall)
+#define RxReferenceSrvCall(SrvCall)                                          \
+    RxpTrackReference(RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall); \
+    RxReference(SrvCall)
+
+#define RxDereferenceSrvCall(SrvCall, LockHoldingState)                        \
+    RxpTrackDereference(RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall); \
+    RxDereference(SrvCall, LockHoldingState)
 
-#define RxDereferenceSrvCall(SrvCall, LockHoldingState)                       \
-   RxpTrackDereference(RDBSS_REF_TRACK_SRVCALL, __FILE__, __LINE__, SrvCall); \
-   RxDereference(SrvCall, LockHoldingState)
+#define RxReferenceNetRoot(NetRoot)                                          \
+    RxpTrackReference(RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot); \
+    RxReference(NetRoot)
 
-#define RxReferenceNetRoot(NetRoot)                                         \
-   RxpTrackReference(RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot); \
-   RxReference(NetRoot)
+#define RxDereferenceNetRoot(NetRoot, LockHoldingState)                        \
+    RxpTrackDereference(RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot); \
+    RxDereference(NetRoot, LockHoldingState)
 
-#define RxDereferenceNetRoot(NetRoot, LockHoldingState)                       \
-   RxpTrackDereference(RDBSS_REF_TRACK_NETROOT, __FILE__, __LINE__, NetRoot); \
-   RxDereference(NetRoot, LockHoldingState)
+#define RxReferenceVNetRoot(VNetRoot)                                          \
+    RxpTrackReference(RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot); \
+    RxReference(VNetRoot)
 
-#define RxReferenceVNetRoot(VNetRoot)                                         \
-   RxpTrackReference(RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot); \
-   RxReference(VNetRoot)
+#define RxDereferenceVNetRoot(VNetRoot, LockHoldingState)                        \
+    RxpTrackDereference(RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot); \
+    RxDereference(VNetRoot, LockHoldingState)
 
-#define RxDereferenceVNetRoot(VNetRoot, LockHoldingState)                       \
-   RxpTrackDereference(RDBSS_REF_TRACK_VNETROOT, __FILE__, __LINE__, VNetRoot); \
-   RxDereference(VNetRoot, LockHoldingState)
+#define RxReferenceNetFobx(Fobx)                                          \
+    RxpTrackReference(RDBSS_REF_TRACK_NETFOBX, __FILE__, __LINE__, Fobx); \
+    RxReference(Fobx)
 
-#define RxDereferenceNetFobx(Fobx, LockHoldingState)                       \
-   RxpTrackDereference(RDBSS_REF_TRACK_NETFOBX, __FILE__, __LINE__, Fobx); \
-   RxDereference(Fobx, LockHoldingState)
+#define RxDereferenceNetFobx(Fobx, LockHoldingState)                        \
+    RxpTrackDereference(RDBSS_REF_TRACK_NETFOBX, __FILE__, __LINE__, Fobx); \
+    RxDereference(Fobx, LockHoldingState)
 
-#define RxReferenceSrvOpen(SrvOpen)                                         \
-   RxpTrackReference(RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen); \
-   RxReference(SrvOpen)
+#define RxReferenceSrvOpen(SrvOpen)                                          \
+    RxpTrackReference(RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen); \
+    RxReference(SrvOpen)
 
-#define RxDereferenceSrvOpen(SrvOpen, LockHoldingState)                       \
-   RxpTrackDereference(RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen); \
-   RxDereference(SrvOpen, LockHoldingState)
+#define RxDereferenceSrvOpen(SrvOpen, LockHoldingState)                        \
+    RxpTrackDereference(RDBSS_REF_TRACK_SRVOPEN, __FILE__, __LINE__, SrvOpen); \
+    RxDereference(SrvOpen, LockHoldingState)
 
-#define RxReferenceNetFcb(Fcb)                                         \
-  (RxpTrackReference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb), \
-   RxpReferenceNetFcb(Fcb))
+#define RxReferenceNetFcb(Fcb)                                           \
+    (RxpTrackReference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb), \
+     RxpReferenceNetFcb(Fcb))
 
-#define RxDereferenceNetFcb(Fcb)                                                \
-   ((LONG)RxpTrackDereference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb), \
-    RxpDereferenceNetFcb(Fcb))
+#define RxDereferenceNetFcb(Fcb)                                                 \
+    ((LONG)RxpTrackDereference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb), \
+     RxpDereferenceNetFcb(Fcb))
 
-#define RxDereferenceAndFinalizeNetFcb(Fcb, RxContext, RecursiveFinalize, ForceFinalize) \
-   (RxpTrackDereference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb),                \
-    RxpDereferenceAndFinalizeNetFcb(Fcb, RxContext, RecursiveFinalize, ForceFinalize))
+#define RxDereferenceAndFinalizeNetFcb(Fcb, RxContext, RecursiveFinalize, ForceFinalize)  \
+    (RxpTrackDereference(RDBSS_REF_TRACK_NETFCB, __FILE__, __LINE__, Fcb),                \
+     RxpDereferenceAndFinalizeNetFcb(Fcb, RxContext, RecursiveFinalize, ForceFinalize))
 
 PSRV_CALL
 RxCreateSrvCall(
@@ -533,6 +553,10 @@ RxFinishFcbInitialization(
 #define RxWaitForStableSrvOpen(S, R) RxWaitForStableCondition(&(S)->Condition, &(S)->TransitionWaitList, (R), NULL)
 #define RxTransitionSrvOpen(S, C) RxUpdateCondition((C), &(S)->Condition, &(S)->TransitionWaitList)
 
+VOID
+RxRemoveNameNetFcb(
+    _Out_ PFCB ThisFcb);
+
 LONG
 RxpReferenceNetFcb(
    _In_ PFCB Fcb);
@@ -616,6 +640,16 @@ RxFinalizeNetFobx(
 #define TRACKER_RELEASE_NON_EXCL_FCB_FOR_THRD_BUFF_PENDING 0x72727430
 #define TRACKER_RELEASE_EXCL_FCB_FOR_THRD_BUFF_PENDING 0x72727431
 #define TRACKER_FCB_FREE 0x72724372
+
+#define FCB_STATE_BUFFERING_STATE_WITH_NO_SHARES \
+    (( FCB_STATE_WRITECACHING_ENABLED            \
+    | FCB_STATE_WRITEBUFFERING_ENABLED           \
+    | FCB_STATE_READCACHING_ENABLED              \
+    | FCB_STATE_READBUFFERING_ENABLED            \
+    | FCB_STATE_LOCK_BUFFERING_ENABLED           \
+    | FCB_STATE_FILESIZECACHEING_ENABLED         \
+    | FCB_STATE_FILETIMECACHEING_ENABLED))
+
 #endif
 
 #endif
index dc605ca..e7244eb 100644 (file)
@@ -132,7 +132,9 @@ typedef struct _MRX_FCB_
 #define SRVOPEN_FLAG_CLOSE_DELAYED 0x8
 #define SRVOPEN_FLAG_FILE_RENAMED 0x10
 #define SRVOPEN_FLAG_FILE_DELETED 0x20
+#define SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING 0x40
 #define SRVOPEN_FLAG_COLLAPSING_DISABLED 0x80
+#define SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_REQUESTS_PURGED 0x100
 #define SRVOPEN_FLAG_NO_BUFFERING_STATE_CHANGE 0x200
 #define SRVOPEN_FLAG_ORPHANED 0x400
 
index 48f733f..932a33e 100644 (file)
@@ -42,6 +42,7 @@ typedef enum _RX_FILE_TYPE
 #define RDBSS_NTC_STORAGE_TYPE_DIRECTORY ((NODE_TYPE_CODE)0xec02)
 #define RDBSS_NTC_STORAGE_TYPE_FILE ((NODE_TYPE_CODE)0xec03)
 #define RDBSS_NTC_OPENTARGETDIR_FCB ((NODE_TYPE_CODE)0xecff)
+#define RDBSS_NTC_IPC_SHARE ((NODE_TYPE_CODE)0xecfe)
 #define RDBSS_NTC_MAILSLOT ((NODE_TYPE_CODE)0xecfd)
 #define RDBSS_NTC_SPOOLFILE ((NODE_TYPE_CODE)0xecfc)
 #define RDBSS_NTC_SRVCALL ((NODE_TYPE_CODE)0xeb10)
index d942fc5..82cb327 100644 (file)
@@ -428,6 +428,18 @@ RxRemoveFirstContextFromSerializationQueue(
        InitializeListHead((Source));                \
     }
 
+#define RxTransferListWithMutex(Destination, Source, Mutex) \
+    {                                                       \
+        ExAcquireFastMutex(Mutex);                          \
+        RxTransferList(Destination, Source);                \
+        ExReleaseFastMutex(Mutex);                          \
+    }
+
+VOID
+RxCancelNotifyChangeDirectoryRequestsForFobx(
+   PFOBX Fobx
+   );
+
 VOID
 NTAPI
 RxInitializeContext(
@@ -472,4 +484,8 @@ RxResumeBlockedOperations_Serially(
     _Inout_ PRX_CONTEXT RxContext,
     _Inout_ PLIST_ENTRY BlockingIoQ);
 
+VOID
+RxResumeBlockedOperations_ALL(
+    _Inout_ PRX_CONTEXT RxContext);
+
 #endif
index 14dfa26..fd3a49a 100644 (file)
@@ -469,6 +469,10 @@ VOID
 RxUpdateShareAccessPerSrvOpens(
     _In_ PSRV_OPEN SrvOpen);
 
+VOID
+RxRemoveShareAccessPerSrvOpens(
+    _Inout_ PSRV_OPEN SrvOpen);
+
 #if DBG
 NTSTATUS
 RxCheckShareAccess(
@@ -528,10 +532,33 @@ ULONG
 RxGetNetworkProviderPriority(
     _In_ PUNICODE_STRING DeviceName);
 
+VOID
+RxPrepareRequestForReuse(
+    PCHANGE_BUFFERING_STATE_REQUEST Request);
+
 VOID
 RxpDiscardChangeBufferingStateRequests(
     _Inout_ PLIST_ENTRY DiscardedRequests);
 
+VOID
+RxGatherRequestsForSrvOpen(
+    _Inout_ PSRV_CALL SrvCall,
+    _In_ PSRV_OPEN SrvOpen,
+    _Inout_ PLIST_ENTRY RequestsListHead);
+
+NTSTATUS
+RxpLookupSrvOpenForRequestLite(
+    _In_ PSRV_CALL SrvCall,
+    _Inout_ PCHANGE_BUFFERING_STATE_REQUEST Request);
+
+VOID
+RxProcessChangeBufferingStateRequestsForSrvOpen(
+    PSRV_OPEN SrvOpen);
+
+NTSTATUS
+RxPurgeFobxFromCache(
+    PFOBX FobxToBePurged);
+
 VOID
 RxUndoScavengerFinalizationMarking(
     PVOID Instance);
index 42b9767..ca97245 100644 (file)
@@ -8,6 +8,15 @@ typedef struct _RX_WORK_ITEM_
     ULONG Options;
 } RX_WORK_ITEM, *PRX_WORK_ITEM;
 
+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); 
+
 NTSTATUS
 NTAPI
 RxInitializeRxTimer(
index 04d1569..2929b96 100644 (file)
@@ -116,6 +116,10 @@ BOOLEAN
 RxScavengeRelatedFobxs(
     _In_ PFCB Fcb);
 
+VOID
+RxpMarkInstanceForScavengedFinalization(
+   PVOID Instance);
+
 VOID
 RxpUndoScavengerFinalizationMarking(
    _In_ PVOID Instance);
diff --git a/reactos/sdk/lib/drivers/copysup/CMakeLists.txt b/reactos/sdk/lib/drivers/copysup/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c84d41e
--- /dev/null
@@ -0,0 +1,7 @@
+add_definitions(-DUNICODE -D_UNICODE)
+
+list(APPEND SOURCE
+    copysup.c)
+
+add_library(copysup ${SOURCE})
+add_dependencies(copysup bugcodes xdk)
diff --git a/reactos/sdk/lib/drivers/copysup/copysup.c b/reactos/sdk/lib/drivers/copysup/copysup.c
new file mode 100644 (file)
index 0000000..05d2cc4
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  ReactOS kernel
+ *  Copyright (C) 2017 ReactOS Team
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+/*
+ * COPYRIGHT:        See COPYING in the top level directory
+ * PROJECT:          ReactOS kernel
+ * FILE:             sdk/lib/drivers/copysup/copysup.c
+ * PURPOSE:          CopySup library
+ * PROGRAMMER:       Pierre Schweitzer (pierre@reactos.org)
+ */
+
+/* INCLUDES *****************************************************************/
+
+#include "copysup.h"
+#include <pseh/pseh2.h>
+
+/* FUNCTIONS ****************************************************************/
+
+/*
+ * @implemented
+ */
+BOOLEAN
+FsRtlCopyRead2(
+    IN PFILE_OBJECT FileObject,
+    IN PLARGE_INTEGER FileOffset,
+    IN ULONG Length,
+    IN BOOLEAN Wait,
+    IN ULONG LockKey,
+    OUT PVOID Buffer,
+    OUT PIO_STATUS_BLOCK IoStatus,
+    IN PDEVICE_OBJECT DeviceObject,
+    IN PVOID TopLevelContext)
+{
+    BOOLEAN Ret;
+    ULONG PageCount;
+    LARGE_INTEGER FinalOffset;
+    PFSRTL_COMMON_FCB_HEADER Fcb;
+    PFAST_IO_DISPATCH FastIoDispatch;
+    PDEVICE_OBJECT RelatedDeviceObject;
+
+    PAGED_CODE();
+
+    Ret = TRUE;
+    PageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(FileOffset, Length);
+
+    /* Null-length read is always OK */
+    if (Length == 0)
+    {
+        IoStatus->Information = 0;
+        IoStatus->Status = STATUS_SUCCESS;
+
+        return TRUE;
+    }
+
+    /* Check we don't overflow */
+    FinalOffset.QuadPart = FileOffset->QuadPart + Length;
+    if (FinalOffset.QuadPart <= 0)
+    {
+        return FALSE;
+    }
+
+    /* Get the FCB (at least, its header) */
+    Fcb = FileObject->FsContext;
+
+    FsRtlEnterFileSystem();
+
+    /* Acquire its resource (shared) */
+    if (Wait)
+    {
+        ExAcquireResourceSharedLite(Fcb->Resource, TRUE);
+    }
+    else
+    {
+        if (!ExAcquireResourceSharedLite(Fcb->Resource, FALSE))
+        {
+            Ret = FALSE;
+            goto CriticalSection;
+        }
+    }
+
+    /* If cache wasn't initialized, or FastIO isn't possible, fail */
+    if (FileObject->PrivateCacheMap == NULL || Fcb->IsFastIoPossible == FastIoIsNotPossible)
+    {
+            Ret = FALSE;
+            goto Resource;
+    }
+
+    /* If FastIO is questionable, then, question! */
+    if (Fcb->IsFastIoPossible == FastIoIsQuestionable)
+    {
+        RelatedDeviceObject = IoGetRelatedDeviceObject(FileObject);
+        FastIoDispatch = RelatedDeviceObject->DriverObject->FastIoDispatch;
+        ASSERT(FastIoDispatch != NULL);
+        ASSERT(FastIoDispatch->FastIoCheckIfPossible != NULL);
+
+        /* If it's not possible, then fail */
+        if (!FastIoDispatch->FastIoCheckIfPossible(FileObject, FileOffset, Length,
+                                                   Wait, LockKey, TRUE, IoStatus, RelatedDeviceObject))
+        {
+            Ret = FALSE;
+            goto Resource;
+        }
+    }
+
+    /* If we get beyond file end... */
+    if (FinalOffset.QuadPart > Fcb->FileSize.QuadPart)
+    {
+        /* Fail if the offset was already beyond file end */
+        if (FileOffset->QuadPart >= Fcb->FileSize.QuadPart)
+        {
+            IoStatus->Information = 0;
+            IoStatus->Status = STATUS_END_OF_FILE;
+            goto Resource;
+        }
+
+        /* Otherwise, just fix read length */
+        Length = (ULONG)(Fcb->FileSize.QuadPart - FileOffset->QuadPart);
+    }
+
+    /* Set caller provided context as TLI */
+    IoSetTopLevelIrp(TopLevelContext);
+
+    _SEH2_TRY
+    {
+        /* If we cannot wait, or if file is bigger than 4GB */
+        if (!Wait || (FinalOffset.HighPart | Fcb->FileSize.HighPart) != 0)
+        {
+            /* Forward to Cc */
+            Ret = CcCopyRead(FileObject, FileOffset, Length, Wait, Buffer, IoStatus);
+            SetFlag(FileObject->Flags, FO_FILE_FAST_IO_READ);
+
+            /* Validate output */
+            ASSERT(!Ret || (IoStatus->Status == STATUS_END_OF_FILE) || (((ULONGLONG)FileOffset->QuadPart + IoStatus->Information) <= (ULONGLONG)Fcb->FileSize.QuadPart));
+        }
+        else
+        {
+            /* Forward to Cc */
+            CcFastCopyRead(FileObject, FileOffset->LowPart, Length, PageCount, Buffer, IoStatus);
+            SetFlag(FileObject->Flags, FO_FILE_FAST_IO_READ);
+
+            /* Validate output */
+            ASSERT((IoStatus->Status == STATUS_END_OF_FILE) || ((FileOffset->LowPart + IoStatus->Information) <= Fcb->FileSize.LowPart));
+        }
+
+        /* If read was successful, update the byte offset in the FO */
+        if (Ret)
+        {
+            FileObject->CurrentByteOffset.QuadPart = FileOffset->QuadPart + IoStatus->Information;
+        }
+    }
+    _SEH2_EXCEPT(FsRtlIsNtstatusExpected(_SEH2_GetExceptionCode()) ?
+                 EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+    {
+        Ret = FALSE;
+    }
+    _SEH2_END;
+
+    /* Reset TLI */
+    IoSetTopLevelIrp(NULL);
+
+Resource:
+    ExReleaseResourceLite(Fcb->Resource);
+CriticalSection:
+    FsRtlExitFileSystem();
+
+    return Ret;
+}
diff --git a/reactos/sdk/lib/drivers/copysup/copysup.h b/reactos/sdk/lib/drivers/copysup/copysup.h
new file mode 100644 (file)
index 0000000..40cf924
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _COPYSUP_H_
+#define _COPYSUP_H_
+
+#include <ntifs.h>
+
+BOOLEAN
+FsRtlCopyRead2(
+    _In_ PFILE_OBJECT FileObject,
+    _In_ PLARGE_INTEGER FileOffset,
+    _In_ ULONG Length,
+    _In_ BOOLEAN Wait,
+    _In_ ULONG LockKey,
+    _Out_ PVOID Buffer,
+    _Out_ PIO_STATUS_BLOCK IoStatus,
+    _In_ PDEVICE_OBJECT DeviceObject,
+    _In_ PVOID TopLevelContext);
+
+#endif
index cf02b87..348ae81 100644 (file)
@@ -30,6 +30,7 @@
 #include <pseh/pseh2.h>
 #include <limits.h>
 #include <dfs.h>
+#include <copysup.h>
 
 #define NDEBUG
 #include <debug.h>
@@ -357,6 +358,11 @@ RxpQueryInfoMiniRdr(
     FILE_INFORMATION_CLASS FileInfoClass,
     PVOID Buffer);
 
+VOID
+RxPurgeNetFcb(
+    PFCB Fcb,
+    PRX_CONTEXT LocalContext);
+
 NTSTATUS
 RxQueryAlternateNameInfo(
     PRX_CONTEXT RxContext,
@@ -779,6 +785,13 @@ RxAllocateCanonicalNameBuffer(
     return STATUS_SUCCESS;
 }
 
+VOID
+RxCancelNotifyChangeDirectoryRequestsForFobx(
+   PFOBX Fobx)
+{
+    UNIMPLEMENTED;
+}
+
 VOID
 NTAPI
 RxCancelRoutine(
@@ -971,17 +984,6 @@ RxCanonicalizeNameAndObtainNetRoot(
     return Status;
 }
 
-NTSTATUS
-NTAPI
-RxChangeBufferingState(
-    PSRV_OPEN SrvOpen,
-    PVOID Context,
-    BOOLEAN ComputeNewState)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
-
 VOID
 NTAPI
 RxCheckFcbStructuresForAlignment(
@@ -1058,13 +1060,202 @@ RxCheckShareAccessPerSrvOpens(
     return STATUS_SUCCESS;
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 RxCloseAssociatedSrvOpen(
     IN PFOBX Fobx,
     IN PRX_CONTEXT RxContext OPTIONAL)
 {
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    PFCB Fcb;
+    NTSTATUS Status;
+    PSRV_OPEN SrvOpen;
+    BOOLEAN CloseSrvOpen;
+    PRX_CONTEXT LocalContext;
+
+    PAGED_CODE();
+
+    /* Assume SRV_OPEN is already closed */
+    CloseSrvOpen = FALSE;
+    /* If we have a FOBX, we'll have to close it */
+    if (Fobx != NULL)
+    {
+        /* If the FOBX isn't closed yet */
+        if (!BooleanFlagOn(Fobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED))
+        {
+            SrvOpen = Fobx->SrvOpen;
+            Fcb = (PFCB)SrvOpen->pFcb;
+            /* Check whether we've to close SRV_OPEN first */
+            if (!BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_CLOSED))
+            {
+                CloseSrvOpen = TRUE;
+            }
+            else
+            {
+                ASSERT(RxIsFcbAcquiredExclusive(Fcb));
+
+                /* Not much to do */
+                SetFlag(Fobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED);
+
+                if (SrvOpen->OpenCount > 0)
+                {
+                    --SrvOpen->OpenCount;
+                }
+            }
+        }
+
+        /* No need to close SRV_OPEN, so close FOBX */
+        if (!CloseSrvOpen)
+        {
+            RxMarkFobxOnClose(Fobx);
+
+            return STATUS_SUCCESS;
+        }
+    }
+    else
+    {
+        /* No FOBX? No RX_CONTEXT, ok, job done! */
+        if (RxContext == NULL)
+        {
+            return STATUS_SUCCESS;
+        }
+
+        /* Get the FCB from RX_CONTEXT */
+        Fcb = (PFCB)RxContext->pFcb;
+        SrvOpen == NULL;
+    }
+
+    /* If we don't have RX_CONTEXT, allocte one, we'll need it */
+    if (RxContext == NULL)
+    {
+        ASSERT(Fobx != NULL);
+
+        LocalContext = RxCreateRxContext(NULL, Fcb->RxDeviceObject, RX_CONTEXT_FLAG_MUST_SUCCEED_NONBLOCKING | RX_CONTEXT_FLAG_WAIT);
+        if (LocalContext == NULL)
+        {
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        LocalContext->MajorFunction = 2;
+        LocalContext->pFcb = RX_GET_MRX_FCB(Fcb);
+        LocalContext->pFobx = (PMRX_FOBX)Fobx;
+        LocalContext->pRelevantSrvOpen = (PMRX_SRV_OPEN)Fobx->SrvOpen;
+    }
+    else
+    {
+        LocalContext = RxContext;
+    }
+
+    ASSERT(RxIsFcbAcquiredExclusive(Fcb));
+
+    /* Now, close the FOBX */
+    if (Fobx != NULL)
+    {
+        RxMarkFobxOnClose(Fobx);
+    }
+    else
+    {
+        InterlockedDecrement((volatile long *)&Fcb->OpenCount);
+    }
+
+    /* If not a "standard" file, SRV_OPEN can be null */
+    if (SrvOpen == NULL)
+    {
+        ASSERT((NodeType(Fcb) == RDBSS_NTC_OPENTARGETDIR_FCB) || (NodeType(Fcb) == RDBSS_NTC_IPC_SHARE) || (NodeType(Fcb) == RDBSS_NTC_MAILSLOT));
+        RxDereferenceNetFcb(Fcb);
+
+        if (LocalContext != RxContext)
+        {
+            RxDereferenceAndDeleteRxContext(LocalContext);
+        }
+
+        return STATUS_SUCCESS;
+    }
+
+    /* If SRV_OPEN isn't in a good condition, nothing to close */
+    if (SrvOpen->Condition != Condition_Good)
+    {
+        if (LocalContext != RxContext)
+        {
+            RxDereferenceAndDeleteRxContext(LocalContext);
+        }
+
+        return STATUS_SUCCESS;
+    }
+
+    /* Decrease open count */
+    if (SrvOpen->OpenCount > 0)
+    {
+        --SrvOpen->OpenCount;
+    }
+
+    /* If we're the only one left, is there a FOBX handled by Scavenger? */
+    if (SrvOpen->OpenCount == 1)
+    {
+        if (!IsListEmpty(&SrvOpen->FobxList))
+        {
+            if (!IsListEmpty(&CONTAINING_RECORD(SrvOpen->FobxList.Flink, FOBX, FobxQLinks)->ScavengerFinalizationList))
+            {
+                SetFlag(SrvOpen->Flags, SRVOPEN_FLAG_CLOSE_DELAYED);
+            }
+        }
+    }
+
+    /* Nothing left, purge FCB */
+    if (SrvOpen->OpenCount == 0 && RxContext == NULL)
+    {
+        RxPurgeNetFcb(Fcb, LocalContext);
+    }
+
+    /* Already closed? Job done! */
+    SrvOpen = Fobx->SrvOpen;
+    if (SrvOpen == NULL ||
+        (SrvOpen->OpenCount != 0 && !BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING)) ||
+        BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_CLOSED))
+    {
+        SetFlag(Fobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED);
+        if (LocalContext != RxContext)
+        {
+            RxDereferenceAndDeleteRxContext(LocalContext);
+        }
+
+        return STATUS_SUCCESS;        
+    }
+
+    ASSERT(RxIsFcbAcquiredExclusive(Fcb));
+
+    /* Inform mini-rdr about closing */
+    MINIRDR_CALL(Status, LocalContext, Fcb->MRxDispatch, MRxCloseSrvOpen, (LocalContext));
+    DPRINT("MRxCloseSrvOpen returned: %lx, called with RX_CONTEXT %p for FOBX %p (FCB %p, SRV_OPEN %p)\n ",
+           Status, RxContext, Fobx, Fcb, SrvOpen);
+
+    /* And mark as such */
+    SetFlag(SrvOpen->Flags, SRVOPEN_FLAG_CLOSED);
+    SrvOpen->Key = (PVOID)-1;
+
+    /* If we were delayed, we're not! */
+    if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_CLOSE_DELAYED))
+    {
+        InterlockedDecrement(&((PSRV_CALL)Fcb->pNetRoot->pSrvCall)->NumberOfCloseDelayedFiles);
+    }
+
+    /* Clear access */
+    RxRemoveShareAccessPerSrvOpens(SrvOpen);
+    RxPurgeChangeBufferingStateRequestsForSrvOpen(SrvOpen);
+
+    /* Dereference */
+    RxDereferenceSrvOpen(SrvOpen, LHS_ExclusiveLockHeld);
+
+    /* Mark the FOBX closed as well */
+    SetFlag(Fobx->Flags, FOBX_FLAG_SRVOPEN_CLOSED);
+
+    if (LocalContext != RxContext)
+    {
+        RxDereferenceAndDeleteRxContext(LocalContext);
+    }
+
+    return Status;
 }
 
 /*
@@ -1258,9 +1449,12 @@ RxCommonCleanup(
 #define BugCheckFileId RDBSS_BUG_CHECK_CLEANUP
     PFCB Fcb;
     PFOBX Fobx;
+    ULONG OpenCount;
     NTSTATUS Status;
-    BOOLEAN NeedPurge;
+    PNET_ROOT NetRoot;
     PFILE_OBJECT FileObject;
+    PLARGE_INTEGER TruncateSize;
+    BOOLEAN NeedPurge, FcbTableAcquired, OneLeft, IsFile, FcbAcquired;
 
     PAGED_CODE();
 
@@ -1300,6 +1494,8 @@ RxCommonCleanup(
         return Status;
     }
 
+    FcbAcquired = TRUE;
+
     Fobx->AssociatedFileObject = NULL;
 
     /* In case SRV_OPEN used is part of FCB */
@@ -1326,8 +1522,186 @@ RxCommonCleanup(
         return STATUS_SUCCESS;
     }
 
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    /* Report the fact that file could be set as delete on close */
+    if (BooleanFlagOn(Fobx->Flags, FOBX_FLAG_DELETE_ON_CLOSE))
+    {
+        SetFlag(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE);
+    }
+
+    /* Cancel any pending notification */
+    RxCancelNotifyChangeDirectoryRequestsForFobx(Fobx);
+
+    /* Backup open count before we start playing with it */
+    OpenCount = Fcb->ShareAccess.OpenCount;
+
+    NetRoot = (PNET_ROOT)Fcb->pNetRoot;
+    FcbTableAcquired = FALSE;
+    OneLeft = FALSE;
+
+    _SEH2_TRY
+    {
+        /* Unclean count and delete on close? Verify whether we're the one */
+        if (Fcb->UncleanCount == 1 && BooleanFlagOn(Fcb->FcbState, FCB_STATE_DELETE_ON_CLOSE))
+        {
+            if (RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, FALSE))
+            {
+                FcbTableAcquired = TRUE;
+            }
+            else
+            {
+                RxReleaseFcb(Context, Fcb);
+
+                RxAcquireFcbTableLockExclusive(&NetRoot->FcbTable, TRUE);
+
+                Status = RxAcquireExclusiveFcb(Context, Fcb);
+                if (Status != STATUS_SUCCESS)
+                {
+                    RxReleaseFcbTableLock(&NetRoot->FcbTable);
+                    return Status;
+                }
+
+                FcbTableAcquired = TRUE;
+            }
+
+            if (Fcb->UncleanCount == 1)
+            {
+                OneLeft = TRUE;
+            }
+            else
+            {
+                RxReleaseFcbTableLock(&NetRoot->FcbTable);
+                FcbTableAcquired = FALSE;
+            }
+        }
+
+        IsFile = FALSE;
+        TruncateSize = NULL;
+        /* Handle cleanup for pipes and printers */
+        if (NetRoot->Type == NET_ROOT_PIPE || NetRoot->Type == NET_ROOT_PRINT)
+        {
+            UNIMPLEMENTED;
+        }
+        /* Handle cleanup for files */
+        else if (NetRoot->Type == NET_ROOT_DISK || NetRoot->Type == NET_ROOT_WILD)
+        {
+            if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_FILE)
+            {
+                UNIMPLEMENTED;
+                IsFile = TRUE;
+            }
+        }
+
+        /* We have to still be there! */
+        ASSERT(Fcb->UncleanCount != 0);
+        InterlockedDecrement((volatile long *)&Fcb->UncleanCount);
+
+        if (BooleanFlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING))
+        {
+            --Fcb->UncachedUncleanCount;
+        }
+
+        /* Inform mini-rdr about ongoing cleanup */
+        MINIRDR_CALL(Status, Context, Fcb->MRxDispatch, MRxCleanupFobx, (Context));
+
+        ASSERT(Fobx->SrvOpen->UncleanFobxCount != 0);
+        --Fobx->SrvOpen->UncleanFobxCount;
+
+        /* Flush cache */
+        if (DisableFlushOnCleanup)
+        {
+            /* Only if we're the last standing */
+            if (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL &&
+                Fcb->UncleanCount == Fcb->UncachedUncleanCount)
+            {
+                DPRINT1("Flushing %p due to last cached handle cleanup\n", Context);
+                RxFlushFcbInSystemCache(Fcb, TRUE);
+            }
+        }
+        else
+        {
+            /* Always */
+            if (Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)
+            {
+                DPRINT1("Flushing %p on cleanup\n", Context);
+                RxFlushFcbInSystemCache(Fcb, TRUE);
+            }
+        }
+
+        /* If only remaining uncached & unclean, then flush and purge */
+        if (!BooleanFlagOn(FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING))
+        {
+            if (Fcb->UncachedUncleanCount != 0)
+            {
+                if (Fcb->UncachedUncleanCount == Fcb->UncleanCount &&
+                    Fcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)
+                {
+                    DPRINT1("Flushing FCB in system cache for %p\n", Context);
+                    RxPurgeFcbInSystemCache(Fcb, NULL, 0, FALSE, TRUE);
+                }
+            }
+        }
+
+        /* If purge required, flush */
+        if (!OneLeft && NeedPurge)
+        {
+            DPRINT1("Flushing FCB in system cache for %p\n", Context);
+            RxFlushFcbInSystemCache(Fcb, TRUE);
+        }
+
+        /* If it was a file, drop cache */
+        if (IsFile)
+        {
+            DPRINT1("Uninit cache map for file\n");
+            RxUninitializeCacheMap(Context, FileObject, TruncateSize);
+        }
+
+        /* If that's the one left, or if it needs purge, flush */
+        if (OneLeft || NeedPurge)
+        {
+            RxPurgeFcbInSystemCache(Fcb, NULL, 0, FALSE, !OneLeft);
+            /* Also remove from FCB table */
+            if (OneLeft)
+            {
+                RxRemoveNameNetFcb(Fcb);
+                RxReleaseFcbTableLock(&NetRoot->FcbTable);
+                FcbTableAcquired = FALSE;
+            }
+        }
+
+        /* Remove any share access */
+        if (OpenCount != 0 && NetRoot->Type == NET_ROOT_DISK)
+        {
+            RxRemoveShareAccess(FileObject, &Fcb->ShareAccess, "Cleanup the share access", "ClnUpShr");
+        }
+
+        /* In case there's caching, on a file, update file metadata */
+        if (NodeType(Fcb) == RDBSS_NTC_STORAGE_TYPE_FILE && BooleanFlagOn(Fobx->Flags, 0x20000000) &&
+            BooleanFlagOn(Fcb->FcbState, FCB_STATE_WRITECACHING_ENABLED) && !BooleanFlagOn(Fobx->pSrvOpen->Flags, SRVOPEN_FLAG_DONTUSE_WRITE_CACHING))
+        {
+            UNIMPLEMENTED;
+        }
+
+        /* We're clean! */
+        SetFlag(FileObject->Flags, FO_CLEANUP_COMPLETE);
+
+        FcbAcquired = FALSE;
+        RxReleaseFcb(Context, Fcb);
+    }
+    _SEH2_FINALLY
+    {
+        if (FcbAcquired)
+        {
+            RxReleaseFcb(Context, Fcb);
+        }
+
+        if (FcbTableAcquired)
+        {
+            RxReleaseFcbTableLock(&NetRoot->FcbTable);
+        }
+    }
+    _SEH2_END;
+
+    return Status;
 #undef BugCheckFileId
 }
 
@@ -3234,6 +3608,96 @@ RxFastIoCheckIfPossible(
     PIO_STATUS_BLOCK IoStatus,
     PDEVICE_OBJECT DeviceObject)
 {
+    PFCB Fcb;
+    PSRV_OPEN SrvOpen;
+
+    PAGED_CODE();
+
+    /* Get the FCB to validate it */
+    Fcb = FileObject->FsContext;
+    if (NodeType(Fcb) != RDBSS_NTC_STORAGE_TYPE_FILE)
+    {
+        DPRINT1("Not a file, FastIO not possible!\n");
+        return FALSE;
+    }
+
+    if (FileObject->DeletePending)
+    {
+        DPRINT1("File delete pending\n");
+        return FALSE;
+    }
+
+    /* If there's a pending write operation, deny fast operation */
+    if (Fcb->NonPaged->OutstandingAsyncWrites != 0)
+    {
+        DPRINT1("Write operations to be completed\n");
+        return FALSE;
+    }
+
+    /* Deny read on orphaned node */
+    SrvOpen = (PSRV_OPEN)((PFOBX)FileObject->FsContext2)->pSrvOpen;
+    if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_ORPHANED))
+    {
+        DPRINT1("SRV_OPEN orphaned\n");
+        return FALSE;
+    }
+
+    if (BooleanFlagOn(Fcb->FcbState, FCB_STATE_ORPHANED))
+    {
+        DPRINT1("FCB orphaned\n");
+        return FALSE;
+    }
+
+    /* If there's a buffering state change pending, deny fast operation (it might change
+     * cache status)
+     */
+    if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_BUFFERING_STATE_CHANGE_PENDING))
+    {
+        DPRINT1("Buffering change pending\n");
+        return FALSE;
+    }
+
+    /* File got renamed/deleted, deny operation */
+    if (BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_DELETED) ||
+        BooleanFlagOn(SrvOpen->Flags, SRVOPEN_FLAG_FILE_RENAMED))
+    {
+        DPRINT1("File renamed/deleted\n");
+        return FALSE;
+    }
+
+    /* Process pending change buffering state operations */
+    FsRtlEnterFileSystem();
+    RxProcessChangeBufferingStateRequestsForSrvOpen(SrvOpen);
+    FsRtlExitFileSystem();
+
+    /* If operation to come is a read operation */
+    if (CheckForReadOperation)
+    {
+        LARGE_INTEGER LargeLength;
+
+        /* Check that read cache is enabled */
+        if (!BooleanFlagOn(Fcb->FcbState, FCB_STATE_READCACHING_ENABLED))
+        {
+            DPRINT1("Read caching disabled\n");
+            return FALSE;
+        }
+
+        /* Check whether there's a lock conflict */
+        LargeLength.QuadPart = Length;
+        if (!FsRtlFastCheckLockForRead(&Fcb->Specific.Fcb.FileLock,
+                                       FileOffset,
+                                       &LargeLength,
+                                       LockKey,
+                                       FileObject,
+                                       PsGetCurrentProcess()))
+        {
+            DPRINT1("FsRtlFastCheckLockForRead failed\n");
+            return FALSE;
+        }
+
+        return TRUE;
+    }
+
     UNIMPLEMENTED;
     return FALSE;
 }
@@ -3263,6 +3727,9 @@ RxFastIoDeviceControl(
     }
 }
 
+/*
+ * @implemented
+ */
 BOOLEAN
 NTAPI
 RxFastIoRead(
@@ -3275,8 +3742,32 @@ RxFastIoRead(
     PIO_STATUS_BLOCK IoStatus,
     PDEVICE_OBJECT DeviceObject)
 {
-    UNIMPLEMENTED;
-    return FALSE;
+    BOOLEAN Ret;
+    RX_TOPLEVELIRP_CONTEXT TopLevelContext;
+
+    PAGED_CODE();
+
+    DPRINT("RxFastIoRead: %p (%p, %p)\n", FileObject, FileObject->FsContext,
+                                          FileObject->FsContext2);
+    DPRINT("Reading %ld at %I64x\n", Length, FileOffset->QuadPart);
+
+    /* Prepare a TLI context */
+    ASSERT(RxIsThisTheTopLevelIrp(NULL));
+    RxInitializeTopLevelIrpContext(&TopLevelContext, (PIRP)FSRTL_FAST_IO_TOP_LEVEL_IRP,
+                                   (PRDBSS_DEVICE_OBJECT)DeviceObject);
+
+    Ret = FsRtlCopyRead2(FileObject, FileOffset, Length, Wait, LockKey, Buffer,
+                         IoStatus, DeviceObject, &TopLevelContext);
+    if (Ret)
+    {
+        DPRINT("Read OK\n");
+    }
+    else
+    {
+        DPRINT1("Read failed!\n");
+    }
+
+    return Ret;
 }
 
 BOOLEAN
@@ -4399,15 +4890,6 @@ RxInitializeRegistrationStructures(
     return STATUS_SUCCESS;
 }
 
-NTSTATUS
-NTAPI
-RxInitializeRxTimer(
-    VOID)
-{
-    UNIMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
-}
-
 /*
  * @implemented
  */
@@ -5022,6 +5504,28 @@ RxpUnregisterMinirdr(
     UNIMPLEMENTED;
 }
 
+/*
+ * @implemented
+ */
+VOID
+RxPurgeNetFcb(
+    PFCB Fcb,
+    PRX_CONTEXT LocalContext)
+{
+    NTSTATUS Status;
+
+    PAGED_CODE();
+
+    /* First, flush */
+    MmFlushImageSection(&Fcb->NonPaged->SectionObjectPointers, MmFlushForWrite);
+
+    /* And force close */
+    RxReleaseFcb(NULL, Fcb);
+    MmForceSectionClosed(&Fcb->NonPaged->SectionObjectPointers, TRUE);
+    Status = RxAcquireExclusiveFcb(NULL, Fcb);
+    ASSERT(Status == STATUS_SUCCESS);
+}
+
 NTSTATUS
 RxQueryAlternateNameInfo(
     PRX_CONTEXT RxContext,
@@ -5610,6 +6114,9 @@ RxRemoveOverflowEntry(
     return Context;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxRemoveShareAccess(
     _Inout_ PFILE_OBJECT FileObject,
@@ -5617,7 +6124,58 @@ RxRemoveShareAccess(
     _In_ PSZ where,
     _In_ PSZ wherelogtag)
 {
-    UNIMPLEMENTED;
+    PAGED_CODE();
+
+    RxDumpCurrentAccess(where, "before", wherelogtag, ShareAccess);
+    IoRemoveShareAccess(FileObject, ShareAccess);
+    RxDumpCurrentAccess(where, "after", wherelogtag, ShareAccess);
+}
+
+/*
+ * @implemented
+ */
+VOID
+RxRemoveShareAccessPerSrvOpens(
+    IN OUT PSRV_OPEN SrvOpen)
+{
+    ACCESS_MASK DesiredAccess;
+    BOOLEAN ReadAccess;
+    BOOLEAN WriteAccess;
+    BOOLEAN DeleteAccess;
+
+    PAGED_CODE();
+
+    /* Get access that were granted to SRV_OPEN */
+    DesiredAccess = SrvOpen->DesiredAccess;
+    ReadAccess = (DesiredAccess & (FILE_READ_DATA | FILE_EXECUTE)) != 0;
+    WriteAccess = (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0;
+    DeleteAccess = (DesiredAccess & DELETE) != 0;
+
+    /* If any, drop them */
+    if ((ReadAccess) || (WriteAccess) || (DeleteAccess))
+    {
+        BOOLEAN SharedRead;
+        BOOLEAN SharedWrite;
+        BOOLEAN SharedDelete;
+        ULONG DesiredShareAccess;
+        PSHARE_ACCESS ShareAccess;
+
+        ShareAccess = &((PFCB)SrvOpen->pFcb)->ShareAccessPerSrvOpens;
+        DesiredShareAccess = SrvOpen->ShareAccess;
+
+        ShareAccess->Readers -= ReadAccess;
+        ShareAccess->Writers -= WriteAccess;
+        ShareAccess->Deleters -= DeleteAccess;
+
+        ShareAccess->OpenCount--;
+
+        SharedRead = (DesiredShareAccess & FILE_SHARE_READ) != 0;
+        SharedWrite = (DesiredShareAccess & FILE_SHARE_WRITE) != 0;
+        SharedDelete = (DesiredShareAccess & FILE_SHARE_DELETE) != 0;
+        ShareAccess->SharedRead -= SharedRead;
+        ShareAccess->SharedWrite -= SharedWrite;
+        ShareAccess->SharedDelete -= SharedDelete;
+    }
 }
 
 NTSTATUS
index 2eb95ab..2685567 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(
@@ -116,6 +129,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
@@ -570,6 +590,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,
@@ -1798,6 +1961,9 @@ RxCreateVNetRoot(
     return VNetRoot;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxDereference(
     IN OUT PVOID Instance,
@@ -1859,10 +2025,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 +2192,14 @@ RxDereferenceAndDeleteRxContext_Real(
     }
 }
 
+VOID
+NTAPI
+RxDispatchChangeBufferingStateRequests(
+    PVOID Context)
+{
+    UNIMPLEMENTED;
+}
+
 /*
  * @implemented
  */
@@ -2328,7 +2514,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 +2577,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);
@@ -2692,14 +2878,116 @@ RxFinalizeSrvCall(
     return TRUE;
 }
 
+/*
+ * @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;
+
+        for (ListEntry = ThisSrvOpen->FobxList.Flink;
+             ListEntry != &ThisSrvOpen->FobxList;
+             ListEntry = ListEntry->Flink)
+        {
+            PFOBX Fobx;
+
+            Fobx = CONTAINING_RECORD(ListEntry, FOBX, FobxQLinks);
+            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 and dereference FCB */
+    if (BooleanFlagOn(ThisSrvOpen->Flags, SRVOPEN_FLAG_ENCLOSED_ALLOCATED))
+    {
+        ClearFlag(Fcb->FcbState, FCB_STATE_SRVOPEN_USED);
+        RxDereferenceNetFcb(Fcb);
+    }
+    /* Otherwise, free the memory */
+    else
+    {
+        RxFreeFcbObject(ThisSrvOpen);
+    }
+
+    return TRUE;
 }
 
 /*
@@ -3605,11 +3893,47 @@ RxFlushFcbInSystemCache(
     return IoStatus.Status;
 }
 
+/*
+ * @implemented
+ */
 VOID
 RxFreeFcbObject(
     PVOID Object)
 {
-    UNIMPLEMENTED;
+    PAGED_CODE();
+
+    /* 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);
+    }
 }
 
 /*
@@ -3658,82 +3982,204 @@ RxFreeObject(
  * @implemented
  */
 VOID
-RxGetFileSizeWithLock(
-    IN PFCB Fcb,
-    OUT PLONGLONG FileSize)
+RxGatherRequestsForSrvOpen(
+    IN OUT PSRV_CALL SrvCall,
+    IN PSRV_OPEN SrvOpen,
+    IN OUT PLIST_ENTRY RequestsListHead)
 {
-    PAGED_CODE();
+    KIRQL OldIrql;
+    LIST_ENTRY Discarded, *Entry;
+    PCHANGE_BUFFERING_STATE_REQUEST Request;
 
-    *FileSize = Fcb->Header.FileSize.QuadPart;
-}
+    /* Dispatch any pending operation first */
+    RxpDispatchChangeBufferingStateRequests(SrvCall, SrvOpen, &Discarded);
 
-/*
- * @implemented
- */
-PEPROCESS
-NTAPI
-RxGetRDBSSProcess(
-    VOID)
-{
-    return RxData.OurProcess;
-}
+    /* 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);
 
-/*
- * @implemented
- */
-NTSTATUS
-RxInitializeBufferingManager(
-   PSRV_CALL SrvCall)
-{
-    KeInitializeSpinLock(&SrvCall->BufferingManager.SpinLock);
-    InitializeListHead(&SrvCall->BufferingManager.DispatcherList);
-    InitializeListHead(&SrvCall->BufferingManager.HandlerList);
-    InitializeListHead(&SrvCall->BufferingManager.LastChanceHandlerList);
-    SrvCall->BufferingManager.DispatcherActive = FALSE;
-    SrvCall->BufferingManager.HandlerInactive = FALSE;
-    SrvCall->BufferingManager.LastChanceHandlerActive = FALSE;
-    SrvCall->BufferingManager.NumberOfOutstandingOpens = 0;
-    InitializeListHead(&SrvCall->BufferingManager.SrvOpenLists[0]);
-    ExInitializeFastMutex(&SrvCall->BufferingManager.Mutex);
+    /* 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);
+        }
+    }
 
-    return STATUS_SUCCESS;
+    /* Discard the discarded requests */
+    RxpDiscardChangeBufferingStateRequests(&Discarded);
 }
 
 /*
  * @implemented
  */
-VOID
-NTAPI
-RxInitializeContext(
-    IN PIRP Irp,
-    IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
-    IN ULONG InitialContextFlags,
-    IN OUT PRX_CONTEXT RxContext)
+PRDBSS_DEVICE_OBJECT
+RxGetDeviceObjectOfInstance(
+    PVOID Instance)
 {
-    PIO_STACK_LOCATION Stack;
+    NODE_TYPE_CODE NodeType;
+    PRDBSS_DEVICE_OBJECT DeviceObject;
 
-    /* Initialize our various fields */
-    RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT;
-    RxContext->NodeByteSize = sizeof(RX_CONTEXT);
-    RxContext->ReferenceCount = 1;
-    RxContext->SerialNumber = InterlockedExchangeAdd((volatile LONG *)&RxContextSerialNumberCounter, 1);
-    RxContext->RxDeviceObject = RxDeviceObject;
-    KeInitializeEvent(&RxContext->SyncEvent, SynchronizationEvent, FALSE);
-    RxInitializeScavengerEntry(&RxContext->ScavengerEntry);
-    InitializeListHead(&RxContext->BlockedOperations);
-    RxContext->MRxCancelRoutine = NULL;
-    RxContext->ResumeRoutine = NULL;
-    RxContext->Flags |= InitialContextFlags;
-    RxContext->CurrentIrp = Irp;
-    RxContext->LastExecutionThread = PsGetCurrentThread();
-    RxContext->OriginalThread = RxContext->LastExecutionThread;
+    PAGED_CODE();
 
-    /* If've got no IRP, mark RX_CONTEXT */
-    if (Irp == NULL)
+    /* 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)
     {
-        RxContext->CurrentIrpSp = NULL;
-        RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1;
-        RxContext->MinorFunction = 0;
+        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
+ */
+VOID
+RxGetFileSizeWithLock(
+    IN PFCB Fcb,
+    OUT PLONGLONG FileSize)
+{
+    PAGED_CODE();
+
+    *FileSize = Fcb->Header.FileSize.QuadPart;
+}
+
+/*
+ * @implemented
+ */
+PEPROCESS
+NTAPI
+RxGetRDBSSProcess(
+    VOID)
+{
+    return RxData.OurProcess;
+}
+
+/*
+ * @implemented
+ */
+NTSTATUS
+RxInitializeBufferingManager(
+   PSRV_CALL SrvCall)
+{
+    KeInitializeSpinLock(&SrvCall->BufferingManager.SpinLock);
+    InitializeListHead(&SrvCall->BufferingManager.DispatcherList);
+    InitializeListHead(&SrvCall->BufferingManager.HandlerList);
+    InitializeListHead(&SrvCall->BufferingManager.LastChanceHandlerList);
+    SrvCall->BufferingManager.DispatcherActive = FALSE;
+    SrvCall->BufferingManager.HandlerInactive = FALSE;
+    SrvCall->BufferingManager.LastChanceHandlerActive = FALSE;
+    SrvCall->BufferingManager.NumberOfOutstandingOpens = 0;
+    InitializeListHead(&SrvCall->BufferingManager.SrvOpenLists[0]);
+    ExInitializeFastMutex(&SrvCall->BufferingManager.Mutex);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+ * @implemented
+ */
+VOID
+NTAPI
+RxInitializeContext(
+    IN PIRP Irp,
+    IN PRDBSS_DEVICE_OBJECT RxDeviceObject,
+    IN ULONG InitialContextFlags,
+    IN OUT PRX_CONTEXT RxContext)
+{
+    PIO_STACK_LOCATION Stack;
+
+    /* Initialize our various fields */
+    RxContext->NodeTypeCode = RDBSS_NTC_RX_CONTEXT;
+    RxContext->NodeByteSize = sizeof(RX_CONTEXT);
+    RxContext->ReferenceCount = 1;
+    RxContext->SerialNumber = InterlockedExchangeAdd((volatile LONG *)&RxContextSerialNumberCounter, 1);
+    RxContext->RxDeviceObject = RxDeviceObject;
+    KeInitializeEvent(&RxContext->SyncEvent, SynchronizationEvent, FALSE);
+    RxInitializeScavengerEntry(&RxContext->ScavengerEntry);
+    InitializeListHead(&RxContext->BlockedOperations);
+    RxContext->MRxCancelRoutine = NULL;
+    RxContext->ResumeRoutine = NULL;
+    RxContext->Flags |= InitialContextFlags;
+    RxContext->CurrentIrp = Irp;
+    RxContext->LastExecutionThread = PsGetCurrentThread();
+    RxContext->OriginalThread = RxContext->LastExecutionThread;
+
+    /* If've got no IRP, mark RX_CONTEXT */
+    if (Irp == NULL)
+    {
+        RxContext->CurrentIrpSp = NULL;
+        RxContext->MajorFunction = IRP_MJ_MAXIMUM_FUNCTION + 1;
+        RxContext->MinorFunction = 0;
     }
     else
     {
@@ -4105,6 +4551,28 @@ RxInitializeSrvCallParameters(
     return STATUS_NOT_IMPLEMENTED;
 }
 
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+RxInitializeRxTimer(
+    VOID)
+{
+    PAGED_CODE();
+
+    RxTimerInterval.HighPart = -1;
+    RxTimerInterval.LowPart = -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 +4922,9 @@ RxLockUserBuffer(
     _SEH2_END;
 }
 
+/*
+ * @implemented
+ */
 NTSTATUS
 RxLowIoCompletionTail(
     IN PRX_CONTEXT RxContext)
@@ -4484,10 +4955,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,19 +5238,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();
 }
 
 /*
@@ -4929,7 +5534,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 +5610,338 @@ RxpDestroySrvCall(
     RxReleasePrefixTableLock(PrefixTable);
 }
 
-VOID
-RxpDiscardChangeBufferingStateRequests(
-    _Inout_ PLIST_ENTRY DiscardedRequests)
+/*
+ * @implemented
+ */
+VOID
+RxpDiscardChangeBufferingStateRequests(
+    _Inout_ PLIST_ENTRY DiscardedRequests)
+{
+    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)
+{
+    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)
 {
-    UNIMPLEMENTED;
-}
+    KIRQL OldIrql;
 
-VOID
-RxpDispatchChangeBufferingStateRequests(
-    PSRV_CALL SrvCall,
-    PSRV_OPEN SrvOpen,
-    PLIST_ENTRY DiscardedRequests)
-{
-    UNIMPLEMENTED;
+    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;
 }
 
 /*
@@ -5227,6 +6150,41 @@ RxPrepareContextForReuse(
     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
  */
@@ -5240,6 +6198,37 @@ RxProcessChangeBufferingStateRequests(
     RxpProcessChangeBufferingStateRequests(SrvCall, TRUE);
 }
 
+/*
+ * @implemented
+ */
+VOID
+RxProcessChangeBufferingStateRequestsForSrvOpen(
+    PSRV_OPEN SrvOpen)
+{
+    LONG NumberOfBufferingChangeRequests, OldBufferingToken;
+
+    /* Get the current number of change requests */
+    NumberOfBufferingChangeRequests = ((PSRV_CALL)SrvOpen->pVNetRoot->pNetRoot->pSrvCall)->BufferingManager.CumulativeNumberOfBufferingChangeRequests;
+    /* Get our old token */
+    OldBufferingToken = InterlockedCompareExchange(&SrvOpen->BufferingToken,
+                                                   NumberOfBufferingChangeRequests, NumberOfBufferingChangeRequests);
+    /* Do we have stuff to process? */
+    if (OldBufferingToken != SrvOpen->BufferingToken)
+    {
+        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)
@@ -5280,22 +6269,111 @@ RxpTrackReference(
     UNIMPLEMENTED;
 }
 
+/*
+ * @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);
+    }
 }
 
 /*
@@ -5377,6 +6455,52 @@ RxPurgeFcbInSystemCache(
     return Status;
 }
 
+/*
+ * @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
  */
@@ -5587,6 +6711,30 @@ RxReference(
     RxReleaseScavengerMutex();
 }
 
+/*
+ * @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 +6824,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(
@@ -5767,6 +6932,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 +7473,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
  */
@@ -6676,25 +7987,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 */